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