xref: /aosp_15_r20/bootable/libbootloader/gbl/libgbl/src/slots/partition.rs (revision 5225e6b173e52d2efc6bcf950c27374fd72adabc)
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