1*5225e6b1SAndroid Build Coastguard Worker // Copyright (C) 2024 Google LLC 2*5225e6b1SAndroid Build Coastguard Worker // 3*5225e6b1SAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); 4*5225e6b1SAndroid Build Coastguard Worker // you may not use this file except in compliance with the License. 5*5225e6b1SAndroid Build Coastguard Worker // You may obtain a copy of the License at 6*5225e6b1SAndroid Build Coastguard Worker // 7*5225e6b1SAndroid Build Coastguard Worker // http://www.apache.org/licenses/LICENSE-2.0 8*5225e6b1SAndroid Build Coastguard Worker // 9*5225e6b1SAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software 10*5225e6b1SAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, 11*5225e6b1SAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*5225e6b1SAndroid Build Coastguard Worker // See the License for the specific language governing permissions and 13*5225e6b1SAndroid Build Coastguard Worker // limitations under the License. 14*5225e6b1SAndroid Build Coastguard Worker 15*5225e6b1SAndroid Build Coastguard Worker use super::BootToken; 16*5225e6b1SAndroid Build Coastguard Worker use zerocopy::{AsBytes, ByteSlice, FromBytes, FromZeroes, Ref}; 17*5225e6b1SAndroid Build Coastguard Worker 18*5225e6b1SAndroid Build Coastguard Worker use liberror::Error; 19*5225e6b1SAndroid Build Coastguard Worker 20*5225e6b1SAndroid Build Coastguard Worker /// Tracks whether slot metadata differs from on-disk representation. 21*5225e6b1SAndroid Build Coastguard Worker #[derive(Copy, Clone, Debug, PartialEq, Eq)] 22*5225e6b1SAndroid Build Coastguard Worker pub enum CacheStatus { 23*5225e6b1SAndroid Build Coastguard Worker /// Slot metadata is the same as on disk 24*5225e6b1SAndroid Build Coastguard Worker Clean, 25*5225e6b1SAndroid Build Coastguard Worker /// Slot metadata has been modified 26*5225e6b1SAndroid Build Coastguard Worker Dirty, 27*5225e6b1SAndroid Build Coastguard Worker } 28*5225e6b1SAndroid Build Coastguard Worker 29*5225e6b1SAndroid Build Coastguard Worker /// Trait that describes the operations all slot metadata implementations must support 30*5225e6b1SAndroid Build Coastguard Worker /// to be used as the backing store in a SlotBlock. 31*5225e6b1SAndroid Build Coastguard Worker pub trait MetadataBytes: Copy + AsBytes + FromBytes + FromZeroes + Default { 32*5225e6b1SAndroid Build Coastguard Worker /// Returns a zerocopy reference to Self if buffer 33*5225e6b1SAndroid Build Coastguard Worker /// represents a valid serialization of Self. 34*5225e6b1SAndroid Build Coastguard Worker /// Implementors should check for invariants, 35*5225e6b1SAndroid Build Coastguard Worker /// e.g. checksums, magic numbers, and version numbers. 36*5225e6b1SAndroid Build Coastguard Worker /// 37*5225e6b1SAndroid Build Coastguard Worker /// Returns Err if the buffer does not represent a valid structure. validate<B: ByteSlice>(buffer: B) -> Result<Ref<B, Self>, Error>38*5225e6b1SAndroid Build Coastguard Worker fn validate<B: ByteSlice>(buffer: B) -> Result<Ref<B, Self>, Error>; 39*5225e6b1SAndroid Build Coastguard Worker 40*5225e6b1SAndroid Build Coastguard Worker /// Called right before writing metadata back to disk. 41*5225e6b1SAndroid Build Coastguard Worker /// Implementors should restore invariants, 42*5225e6b1SAndroid Build Coastguard Worker /// update checksums, or take other appropriate actions. prepare_for_sync(&mut self)43*5225e6b1SAndroid Build Coastguard Worker fn prepare_for_sync(&mut self); 44*5225e6b1SAndroid Build Coastguard Worker } 45*5225e6b1SAndroid Build Coastguard Worker 46*5225e6b1SAndroid Build Coastguard Worker /// Generalized description of a partition-backed ABR metadata structure. 47*5225e6b1SAndroid Build Coastguard Worker pub struct SlotBlock<MB: MetadataBytes> { 48*5225e6b1SAndroid Build Coastguard Worker // Internally tracked cache clean/dirty info 49*5225e6b1SAndroid Build Coastguard Worker cache_status: CacheStatus, 50*5225e6b1SAndroid Build Coastguard Worker // SlotBlock holds the boot token until mark_boot_attempt gets called. 51*5225e6b1SAndroid Build Coastguard Worker boot_token: Option<BootToken>, 52*5225e6b1SAndroid Build Coastguard Worker // Serialized slot metadata 53*5225e6b1SAndroid Build Coastguard Worker data: MB, 54*5225e6b1SAndroid Build Coastguard Worker } 55*5225e6b1SAndroid Build Coastguard Worker 56*5225e6b1SAndroid Build Coastguard Worker impl<'a, MB: MetadataBytes> SlotBlock<MB> { 57*5225e6b1SAndroid Build Coastguard Worker /// Note to those implementing Manager for SlotBlock<CustomType>: 58*5225e6b1SAndroid Build Coastguard Worker /// Be very, very careful with custody of the boot token. 59*5225e6b1SAndroid Build Coastguard Worker /// If you release it outside of the implementation of Manager::mark_boot_attempt, 60*5225e6b1SAndroid Build Coastguard Worker /// mark_boot_attempt will fail and the kernel may boot without tracking the attempt. 61*5225e6b1SAndroid Build Coastguard Worker /// If you lose the token, the only way to get it back is to reboot the device. take_boot_token(&mut self) -> Option<BootToken>62*5225e6b1SAndroid Build Coastguard Worker pub fn take_boot_token(&mut self) -> Option<BootToken> { 63*5225e6b1SAndroid Build Coastguard Worker self.boot_token.take() 64*5225e6b1SAndroid Build Coastguard Worker } 65*5225e6b1SAndroid Build Coastguard Worker 66*5225e6b1SAndroid Build Coastguard Worker /// Returns a mutable reference to the slot metadata and marks the cache as dirty. get_mut_data(&mut self) -> &mut MB67*5225e6b1SAndroid Build Coastguard Worker pub fn get_mut_data(&mut self) -> &mut MB { 68*5225e6b1SAndroid Build Coastguard Worker self.cache_status = CacheStatus::Dirty; 69*5225e6b1SAndroid Build Coastguard Worker &mut self.data 70*5225e6b1SAndroid Build Coastguard Worker } 71*5225e6b1SAndroid Build Coastguard Worker 72*5225e6b1SAndroid Build Coastguard Worker /// Returns an immutable reference to the slot metadata get_data(&self) -> &MB73*5225e6b1SAndroid Build Coastguard Worker pub fn get_data(&self) -> &MB { 74*5225e6b1SAndroid Build Coastguard Worker &self.data 75*5225e6b1SAndroid Build Coastguard Worker } 76*5225e6b1SAndroid Build Coastguard Worker 77*5225e6b1SAndroid Build Coastguard Worker #[cfg(test)] 78*5225e6b1SAndroid Build Coastguard Worker /// Returns the cache status cache_status(&self) -> CacheStatus79*5225e6b1SAndroid Build Coastguard Worker pub fn cache_status(&self) -> CacheStatus { 80*5225e6b1SAndroid Build Coastguard Worker self.cache_status 81*5225e6b1SAndroid Build Coastguard Worker } 82*5225e6b1SAndroid Build Coastguard Worker 83*5225e6b1SAndroid Build Coastguard Worker /// Attempt to deserialize a slot control block 84*5225e6b1SAndroid Build Coastguard Worker /// 85*5225e6b1SAndroid Build Coastguard Worker /// # Returns 86*5225e6b1SAndroid Build Coastguard Worker /// * `SlotBlock` - returns either the deserialized 87*5225e6b1SAndroid Build Coastguard Worker /// representation of the slot control block 88*5225e6b1SAndroid Build Coastguard Worker /// OR a fresh, default valued slot control block 89*5225e6b1SAndroid Build Coastguard Worker /// if there was an internal error. 90*5225e6b1SAndroid Build Coastguard Worker /// 91*5225e6b1SAndroid Build Coastguard Worker /// TODO(b/329116902): errors are logged deserialize<B: ByteSlice>(buffer: B, boot_token: BootToken) -> Self92*5225e6b1SAndroid Build Coastguard Worker pub fn deserialize<B: ByteSlice>(buffer: B, boot_token: BootToken) -> Self { 93*5225e6b1SAndroid Build Coastguard Worker // TODO(b/329116902): log failures 94*5225e6b1SAndroid Build Coastguard Worker // validate(buffer) 95*5225e6b1SAndroid Build Coastguard Worker // .inspect_err(|e| { 96*5225e6b1SAndroid Build Coastguard Worker // eprintln!("ABR metadata failed verification, using metadata defaults: {e}") 97*5225e6b1SAndroid Build Coastguard Worker // }) 98*5225e6b1SAndroid Build Coastguard Worker let (data, cache_status) = match MB::validate(buffer) { 99*5225e6b1SAndroid Build Coastguard Worker Ok(data) => (*data, CacheStatus::Clean), 100*5225e6b1SAndroid Build Coastguard Worker Err(_) => (Default::default(), CacheStatus::Dirty), 101*5225e6b1SAndroid Build Coastguard Worker }; 102*5225e6b1SAndroid Build Coastguard Worker 103*5225e6b1SAndroid Build Coastguard Worker SlotBlock { cache_status, boot_token: Some(boot_token), data } 104*5225e6b1SAndroid Build Coastguard Worker } 105*5225e6b1SAndroid Build Coastguard Worker 106*5225e6b1SAndroid Build Coastguard Worker /// Write back slot metadata to disk. 107*5225e6b1SAndroid Build Coastguard Worker /// 108*5225e6b1SAndroid Build Coastguard Worker /// The MetadataBytes type should reestablish any invariants when `prepare_for_sync` is called, 109*5225e6b1SAndroid Build Coastguard Worker /// e.g. recalculating checksums. 110*5225e6b1SAndroid Build Coastguard Worker /// 111*5225e6b1SAndroid Build Coastguard Worker /// Does NOT write back to disk if no changes have been made and the cache is clean. 112*5225e6b1SAndroid Build Coastguard Worker /// Panics if the write attempt fails. 113*5225e6b1SAndroid Build Coastguard Worker /// 114*5225e6b1SAndroid Build Coastguard Worker /// # Args 115*5225e6b1SAndroid Build Coastguard Worker /// 116*5225e6b1SAndroid Build Coastguard Worker /// * `persist`: A user provided closure for persisting a given slot metadata bytes to storage. sync_to_disk(&mut self, persist: &mut dyn FnMut(&mut [u8]) -> Result<(), Error>)117*5225e6b1SAndroid Build Coastguard Worker pub fn sync_to_disk(&mut self, persist: &mut dyn FnMut(&mut [u8]) -> Result<(), Error>) { 118*5225e6b1SAndroid Build Coastguard Worker if self.cache_status == CacheStatus::Clean { 119*5225e6b1SAndroid Build Coastguard Worker return; 120*5225e6b1SAndroid Build Coastguard Worker } 121*5225e6b1SAndroid Build Coastguard Worker 122*5225e6b1SAndroid Build Coastguard Worker self.data.prepare_for_sync(); 123*5225e6b1SAndroid Build Coastguard Worker 124*5225e6b1SAndroid Build Coastguard Worker match persist(self.get_mut_data().as_bytes_mut()) { 125*5225e6b1SAndroid Build Coastguard Worker Ok(_) => self.cache_status = CacheStatus::Clean, 126*5225e6b1SAndroid Build Coastguard Worker Err(e) => panic!("{}", e), 127*5225e6b1SAndroid Build Coastguard Worker }; 128*5225e6b1SAndroid Build Coastguard Worker } 129*5225e6b1SAndroid Build Coastguard Worker } 130*5225e6b1SAndroid Build Coastguard Worker 131*5225e6b1SAndroid Build Coastguard Worker #[cfg(test)] 132*5225e6b1SAndroid Build Coastguard Worker impl<MB: MetadataBytes> Default for SlotBlock<MB> { 133*5225e6b1SAndroid Build Coastguard Worker /// Returns a default valued SlotBlock. 134*5225e6b1SAndroid Build Coastguard Worker /// Only used in tests because BootToken cannot be constructed out of crate. default() -> Self135*5225e6b1SAndroid Build Coastguard Worker fn default() -> Self { 136*5225e6b1SAndroid Build Coastguard Worker Self { 137*5225e6b1SAndroid Build Coastguard Worker cache_status: CacheStatus::Clean, 138*5225e6b1SAndroid Build Coastguard Worker boot_token: Some(BootToken(())), 139*5225e6b1SAndroid Build Coastguard Worker data: Default::default(), 140*5225e6b1SAndroid Build Coastguard Worker } 141*5225e6b1SAndroid Build Coastguard Worker } 142*5225e6b1SAndroid Build Coastguard Worker } 143