xref: /aosp_15_r20/bootable/libbootloader/gbl/libgbl/src/slots/fuchsia.rs (revision 5225e6b173e52d2efc6bcf950c27374fd72adabc)
1*5225e6b1SAndroid Build Coastguard Worker // Copyright 2023, The Android Open Source Project
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 extern crate bitflags;
16*5225e6b1SAndroid Build Coastguard Worker extern crate crc32fast;
17*5225e6b1SAndroid Build Coastguard Worker extern crate zerocopy;
18*5225e6b1SAndroid Build Coastguard Worker 
19*5225e6b1SAndroid Build Coastguard Worker use super::partition::{MetadataBytes, SlotBlock};
20*5225e6b1SAndroid Build Coastguard Worker use super::{
21*5225e6b1SAndroid Build Coastguard Worker     BootTarget, BootToken, Bootability, Manager, OneShot, RecoveryTarget, Slot, SlotIterator,
22*5225e6b1SAndroid Build Coastguard Worker     Suffix, UnbootableReason,
23*5225e6b1SAndroid Build Coastguard Worker };
24*5225e6b1SAndroid Build Coastguard Worker use bitflags::bitflags;
25*5225e6b1SAndroid Build Coastguard Worker use core::iter::zip;
26*5225e6b1SAndroid Build Coastguard Worker use core::mem::size_of;
27*5225e6b1SAndroid Build Coastguard Worker use crc32fast::Hasher;
28*5225e6b1SAndroid Build Coastguard Worker use liberror::Error;
29*5225e6b1SAndroid Build Coastguard Worker use zerocopy::byteorder::big_endian::U32 as BigEndianU32;
30*5225e6b1SAndroid Build Coastguard Worker use zerocopy::{AsBytes, ByteSlice, FromBytes, FromZeroes, Ref};
31*5225e6b1SAndroid Build Coastguard Worker 
32*5225e6b1SAndroid Build Coastguard Worker const DEFAULT_PRIORITY: u8 = 15;
33*5225e6b1SAndroid Build Coastguard Worker const DEFAULT_RETRIES: u8 = 7;
34*5225e6b1SAndroid Build Coastguard Worker 
35*5225e6b1SAndroid Build Coastguard Worker #[repr(C, packed)]
36*5225e6b1SAndroid Build Coastguard Worker #[derive(Copy, Clone, Debug, PartialEq, Eq, AsBytes, FromBytes, FromZeroes)]
37*5225e6b1SAndroid Build Coastguard Worker struct AbrSlotData {
38*5225e6b1SAndroid Build Coastguard Worker     priority: u8,
39*5225e6b1SAndroid Build Coastguard Worker     tries: u8,
40*5225e6b1SAndroid Build Coastguard Worker     successful: u8,
41*5225e6b1SAndroid Build Coastguard Worker     unbootable_reason: u8,
42*5225e6b1SAndroid Build Coastguard Worker }
43*5225e6b1SAndroid Build Coastguard Worker 
44*5225e6b1SAndroid Build Coastguard Worker impl Default for AbrSlotData {
default() -> Self45*5225e6b1SAndroid Build Coastguard Worker     fn default() -> Self {
46*5225e6b1SAndroid Build Coastguard Worker         Self {
47*5225e6b1SAndroid Build Coastguard Worker             priority: DEFAULT_PRIORITY,
48*5225e6b1SAndroid Build Coastguard Worker             tries: DEFAULT_RETRIES,
49*5225e6b1SAndroid Build Coastguard Worker             successful: 0,
50*5225e6b1SAndroid Build Coastguard Worker             unbootable_reason: 0,
51*5225e6b1SAndroid Build Coastguard Worker         }
52*5225e6b1SAndroid Build Coastguard Worker     }
53*5225e6b1SAndroid Build Coastguard Worker }
54*5225e6b1SAndroid Build Coastguard Worker 
55*5225e6b1SAndroid Build Coastguard Worker #[repr(C, packed)]
56*5225e6b1SAndroid Build Coastguard Worker #[derive(Copy, Clone, Debug, PartialEq, Eq, AsBytes, FromBytes, FromZeroes)]
57*5225e6b1SAndroid Build Coastguard Worker struct OneShotFlags(u8);
58*5225e6b1SAndroid Build Coastguard Worker 
59*5225e6b1SAndroid Build Coastguard Worker bitflags! {
60*5225e6b1SAndroid Build Coastguard Worker     impl OneShotFlags: u8 {
61*5225e6b1SAndroid Build Coastguard Worker         /// No oneshot specified
62*5225e6b1SAndroid Build Coastguard Worker         const NONE = 0;
63*5225e6b1SAndroid Build Coastguard Worker         /// Oneshot boot to recovery mode
64*5225e6b1SAndroid Build Coastguard Worker         const RECOVERY = 1 << 0;
65*5225e6b1SAndroid Build Coastguard Worker         /// Oneshot boot to fastboot
66*5225e6b1SAndroid Build Coastguard Worker         const BOOTLOADER = 1 << 1;
67*5225e6b1SAndroid Build Coastguard Worker     }
68*5225e6b1SAndroid Build Coastguard Worker }
69*5225e6b1SAndroid Build Coastguard Worker 
70*5225e6b1SAndroid Build Coastguard Worker impl From<OneShotFlags> for Option<OneShot> {
from(flags: OneShotFlags) -> Self71*5225e6b1SAndroid Build Coastguard Worker     fn from(flags: OneShotFlags) -> Self {
72*5225e6b1SAndroid Build Coastguard Worker         match flags {
73*5225e6b1SAndroid Build Coastguard Worker             OneShotFlags::RECOVERY => Some(OneShot::Continue(RecoveryTarget::Dedicated)),
74*5225e6b1SAndroid Build Coastguard Worker             OneShotFlags::BOOTLOADER => Some(OneShot::Bootloader),
75*5225e6b1SAndroid Build Coastguard Worker             _ => None,
76*5225e6b1SAndroid Build Coastguard Worker         }
77*5225e6b1SAndroid Build Coastguard Worker     }
78*5225e6b1SAndroid Build Coastguard Worker }
79*5225e6b1SAndroid Build Coastguard Worker 
80*5225e6b1SAndroid Build Coastguard Worker impl From<Option<OneShot>> for OneShotFlags {
from(oneshot: Option<OneShot>) -> Self81*5225e6b1SAndroid Build Coastguard Worker     fn from(oneshot: Option<OneShot>) -> Self {
82*5225e6b1SAndroid Build Coastguard Worker         if let Some(target) = oneshot {
83*5225e6b1SAndroid Build Coastguard Worker             match target {
84*5225e6b1SAndroid Build Coastguard Worker                 OneShot::Bootloader => Self::BOOTLOADER,
85*5225e6b1SAndroid Build Coastguard Worker                 OneShot::Continue(RecoveryTarget::Dedicated) => Self::RECOVERY,
86*5225e6b1SAndroid Build Coastguard Worker                 _ => Self::NONE,
87*5225e6b1SAndroid Build Coastguard Worker             }
88*5225e6b1SAndroid Build Coastguard Worker         } else {
89*5225e6b1SAndroid Build Coastguard Worker             Self::NONE
90*5225e6b1SAndroid Build Coastguard Worker         }
91*5225e6b1SAndroid Build Coastguard Worker     }
92*5225e6b1SAndroid Build Coastguard Worker }
93*5225e6b1SAndroid Build Coastguard Worker 
94*5225e6b1SAndroid Build Coastguard Worker const ABR_MAGIC: &[u8; 4] = b"\0AB0";
95*5225e6b1SAndroid Build Coastguard Worker const ABR_VERSION_MAJOR: u8 = 2;
96*5225e6b1SAndroid Build Coastguard Worker const ABR_VERSION_MINOR: u8 = 3;
97*5225e6b1SAndroid Build Coastguard Worker 
98*5225e6b1SAndroid Build Coastguard Worker #[repr(C, packed)]
99*5225e6b1SAndroid Build Coastguard Worker #[derive(Copy, Clone, Debug, PartialEq, Eq, AsBytes, FromBytes, FromZeroes)]
100*5225e6b1SAndroid Build Coastguard Worker struct AbrData {
101*5225e6b1SAndroid Build Coastguard Worker     magic: [u8; 4],
102*5225e6b1SAndroid Build Coastguard Worker     version_major: u8,
103*5225e6b1SAndroid Build Coastguard Worker     version_minor: u8,
104*5225e6b1SAndroid Build Coastguard Worker     reserved: [u8; 2],
105*5225e6b1SAndroid Build Coastguard Worker     slot_data: [AbrSlotData; 2],
106*5225e6b1SAndroid Build Coastguard Worker     oneshot_flag: OneShotFlags,
107*5225e6b1SAndroid Build Coastguard Worker     reserved2: [u8; 11],
108*5225e6b1SAndroid Build Coastguard Worker     crc32: BigEndianU32,
109*5225e6b1SAndroid Build Coastguard Worker }
110*5225e6b1SAndroid Build Coastguard Worker 
111*5225e6b1SAndroid Build Coastguard Worker impl AbrData {
calculate_crc32(&self) -> u32112*5225e6b1SAndroid Build Coastguard Worker     fn calculate_crc32(&self) -> u32 {
113*5225e6b1SAndroid Build Coastguard Worker         let mut hasher = Hasher::new();
114*5225e6b1SAndroid Build Coastguard Worker         // Note: core::offset_of isn't stable yet,
115*5225e6b1SAndroid Build Coastguard Worker         // and size_of_val isn't permitted on unaligned structs.
116*5225e6b1SAndroid Build Coastguard Worker         hasher.update(&self.as_bytes()[..(size_of::<Self>() - size_of::<BigEndianU32>())]);
117*5225e6b1SAndroid Build Coastguard Worker         hasher.finalize()
118*5225e6b1SAndroid Build Coastguard Worker     }
119*5225e6b1SAndroid Build Coastguard Worker }
120*5225e6b1SAndroid Build Coastguard Worker 
121*5225e6b1SAndroid Build Coastguard Worker impl MetadataBytes for AbrData {
validate<B: ByteSlice>(buffer: B) -> Result<Ref<B, AbrData>, Error>122*5225e6b1SAndroid Build Coastguard Worker     fn validate<B: ByteSlice>(buffer: B) -> Result<Ref<B, AbrData>, Error> {
123*5225e6b1SAndroid Build Coastguard Worker         let abr_data = Ref::<B, AbrData>::new_from_prefix(buffer)
124*5225e6b1SAndroid Build Coastguard Worker             .ok_or(Error::BufferTooSmall(Some(size_of::<AbrData>())))?
125*5225e6b1SAndroid Build Coastguard Worker             .0;
126*5225e6b1SAndroid Build Coastguard Worker 
127*5225e6b1SAndroid Build Coastguard Worker         if abr_data.magic != *ABR_MAGIC {
128*5225e6b1SAndroid Build Coastguard Worker             return Err(Error::BadMagic);
129*5225e6b1SAndroid Build Coastguard Worker         }
130*5225e6b1SAndroid Build Coastguard Worker         if abr_data.version_major > ABR_VERSION_MAJOR {
131*5225e6b1SAndroid Build Coastguard Worker             return Err(Error::UnsupportedVersion);
132*5225e6b1SAndroid Build Coastguard Worker         }
133*5225e6b1SAndroid Build Coastguard Worker         if abr_data.crc32.get() != abr_data.calculate_crc32() {
134*5225e6b1SAndroid Build Coastguard Worker             return Err(Error::BadChecksum);
135*5225e6b1SAndroid Build Coastguard Worker         }
136*5225e6b1SAndroid Build Coastguard Worker 
137*5225e6b1SAndroid Build Coastguard Worker         Ok(abr_data)
138*5225e6b1SAndroid Build Coastguard Worker     }
139*5225e6b1SAndroid Build Coastguard Worker 
prepare_for_sync(&mut self)140*5225e6b1SAndroid Build Coastguard Worker     fn prepare_for_sync(&mut self) {
141*5225e6b1SAndroid Build Coastguard Worker         self.version_minor = ABR_VERSION_MINOR;
142*5225e6b1SAndroid Build Coastguard Worker         self.crc32 = self.calculate_crc32().into();
143*5225e6b1SAndroid Build Coastguard Worker     }
144*5225e6b1SAndroid Build Coastguard Worker }
145*5225e6b1SAndroid Build Coastguard Worker 
146*5225e6b1SAndroid Build Coastguard Worker impl Default for AbrData {
default() -> Self147*5225e6b1SAndroid Build Coastguard Worker     fn default() -> Self {
148*5225e6b1SAndroid Build Coastguard Worker         let mut data = Self {
149*5225e6b1SAndroid Build Coastguard Worker             magic: *ABR_MAGIC,
150*5225e6b1SAndroid Build Coastguard Worker             version_major: ABR_VERSION_MAJOR,
151*5225e6b1SAndroid Build Coastguard Worker             version_minor: ABR_VERSION_MINOR,
152*5225e6b1SAndroid Build Coastguard Worker             reserved: Default::default(),
153*5225e6b1SAndroid Build Coastguard Worker             slot_data: Default::default(),
154*5225e6b1SAndroid Build Coastguard Worker             oneshot_flag: OneShotFlags::NONE,
155*5225e6b1SAndroid Build Coastguard Worker             reserved2: Default::default(),
156*5225e6b1SAndroid Build Coastguard Worker             crc32: BigEndianU32::ZERO,
157*5225e6b1SAndroid Build Coastguard Worker         };
158*5225e6b1SAndroid Build Coastguard Worker         data.crc32.set(data.calculate_crc32());
159*5225e6b1SAndroid Build Coastguard Worker         data
160*5225e6b1SAndroid Build Coastguard Worker     }
161*5225e6b1SAndroid Build Coastguard Worker }
162*5225e6b1SAndroid Build Coastguard Worker 
163*5225e6b1SAndroid Build Coastguard Worker impl super::private::SlotGet for SlotBlock<AbrData> {
get_slot_by_number(&self, number: usize) -> Result<Slot, Error>164*5225e6b1SAndroid Build Coastguard Worker     fn get_slot_by_number(&self, number: usize) -> Result<Slot, Error> {
165*5225e6b1SAndroid Build Coastguard Worker         let lower_ascii_suffixes = ('a'..='z').map(Suffix);
166*5225e6b1SAndroid Build Coastguard Worker         let (suffix, &abr_slot) = zip(lower_ascii_suffixes, self.get_data().slot_data.iter())
167*5225e6b1SAndroid Build Coastguard Worker             .nth(number)
168*5225e6b1SAndroid Build Coastguard Worker             .ok_or(Error::BadIndex(number))?;
169*5225e6b1SAndroid Build Coastguard Worker 
170*5225e6b1SAndroid Build Coastguard Worker         let bootability = match (abr_slot.successful, abr_slot.tries) {
171*5225e6b1SAndroid Build Coastguard Worker             (s, _) if s != 0 => Bootability::Successful,
172*5225e6b1SAndroid Build Coastguard Worker             (0, t) if t > 0 => Bootability::Retriable(t.into()),
173*5225e6b1SAndroid Build Coastguard Worker             (_, _) => Bootability::Unbootable(abr_slot.unbootable_reason.into()),
174*5225e6b1SAndroid Build Coastguard Worker         };
175*5225e6b1SAndroid Build Coastguard Worker 
176*5225e6b1SAndroid Build Coastguard Worker         Ok(Slot { suffix, priority: abr_slot.priority.into(), bootability })
177*5225e6b1SAndroid Build Coastguard Worker     }
178*5225e6b1SAndroid Build Coastguard Worker }
179*5225e6b1SAndroid Build Coastguard Worker 
180*5225e6b1SAndroid Build Coastguard Worker impl Manager for SlotBlock<AbrData> {
get_boot_target(&self) -> Result<BootTarget, Error>181*5225e6b1SAndroid Build Coastguard Worker     fn get_boot_target(&self) -> Result<BootTarget, Error> {
182*5225e6b1SAndroid Build Coastguard Worker         Ok(self
183*5225e6b1SAndroid Build Coastguard Worker             .slots_iter()
184*5225e6b1SAndroid Build Coastguard Worker             .filter(Slot::is_bootable)
185*5225e6b1SAndroid Build Coastguard Worker             .max_by_key(|slot| (slot.priority, slot.suffix.rank()))
186*5225e6b1SAndroid Build Coastguard Worker             .map_or(BootTarget::Recovery(RecoveryTarget::Dedicated), BootTarget::NormalBoot))
187*5225e6b1SAndroid Build Coastguard Worker     }
188*5225e6b1SAndroid Build Coastguard Worker 
slots_iter(&self) -> SlotIterator189*5225e6b1SAndroid Build Coastguard Worker     fn slots_iter(&self) -> SlotIterator {
190*5225e6b1SAndroid Build Coastguard Worker         SlotIterator::new(self)
191*5225e6b1SAndroid Build Coastguard Worker     }
192*5225e6b1SAndroid Build Coastguard Worker 
set_slot_unbootable( &mut self, slot_suffix: Suffix, reason: UnbootableReason, ) -> Result<(), Error>193*5225e6b1SAndroid Build Coastguard Worker     fn set_slot_unbootable(
194*5225e6b1SAndroid Build Coastguard Worker         &mut self,
195*5225e6b1SAndroid Build Coastguard Worker         slot_suffix: Suffix,
196*5225e6b1SAndroid Build Coastguard Worker         reason: UnbootableReason,
197*5225e6b1SAndroid Build Coastguard Worker     ) -> Result<(), Error> {
198*5225e6b1SAndroid Build Coastguard Worker         let (idx, slot) = self.get_index_and_slot_with_suffix(slot_suffix)?;
199*5225e6b1SAndroid Build Coastguard Worker         if slot.bootability == Bootability::Unbootable(reason) {
200*5225e6b1SAndroid Build Coastguard Worker             return Ok(());
201*5225e6b1SAndroid Build Coastguard Worker         }
202*5225e6b1SAndroid Build Coastguard Worker 
203*5225e6b1SAndroid Build Coastguard Worker         let abr_data = self.get_mut_data();
204*5225e6b1SAndroid Build Coastguard Worker         let slot_data = &mut abr_data.slot_data[idx];
205*5225e6b1SAndroid Build Coastguard Worker         slot_data.unbootable_reason = reason.into();
206*5225e6b1SAndroid Build Coastguard Worker         slot_data.tries = 0;
207*5225e6b1SAndroid Build Coastguard Worker         slot_data.successful = 0;
208*5225e6b1SAndroid Build Coastguard Worker 
209*5225e6b1SAndroid Build Coastguard Worker         Ok(())
210*5225e6b1SAndroid Build Coastguard Worker     }
211*5225e6b1SAndroid Build Coastguard Worker 
mark_boot_attempt(&mut self) -> Result<BootToken, Error>212*5225e6b1SAndroid Build Coastguard Worker     fn mark_boot_attempt(&mut self) -> Result<BootToken, Error> {
213*5225e6b1SAndroid Build Coastguard Worker         let target = if let Some(OneShot::Continue(r)) = self.get_oneshot_status() {
214*5225e6b1SAndroid Build Coastguard Worker             BootTarget::Recovery(r)
215*5225e6b1SAndroid Build Coastguard Worker         } else {
216*5225e6b1SAndroid Build Coastguard Worker             self.get_boot_target()?
217*5225e6b1SAndroid Build Coastguard Worker         };
218*5225e6b1SAndroid Build Coastguard Worker         let target_slot = match target {
219*5225e6b1SAndroid Build Coastguard Worker             BootTarget::NormalBoot(slot) => slot,
220*5225e6b1SAndroid Build Coastguard Worker             BootTarget::Recovery(RecoveryTarget::Slotted(_)) => Err(Error::OperationProhibited)?,
221*5225e6b1SAndroid Build Coastguard Worker             BootTarget::Recovery(RecoveryTarget::Dedicated) => {
222*5225e6b1SAndroid Build Coastguard Worker                 // Even though boot to recovery does not cause a metadata update,
223*5225e6b1SAndroid Build Coastguard Worker                 // we still need to gate access to the boot token.
224*5225e6b1SAndroid Build Coastguard Worker                 return self.take_boot_token().ok_or(Error::OperationProhibited);
225*5225e6b1SAndroid Build Coastguard Worker             }
226*5225e6b1SAndroid Build Coastguard Worker         };
227*5225e6b1SAndroid Build Coastguard Worker 
228*5225e6b1SAndroid Build Coastguard Worker         let (idx, slot) = self.get_index_and_slot_with_suffix(target_slot.suffix)?;
229*5225e6b1SAndroid Build Coastguard Worker 
230*5225e6b1SAndroid Build Coastguard Worker         match slot.bootability {
231*5225e6b1SAndroid Build Coastguard Worker             Bootability::Unbootable(_) => Err(Error::OperationProhibited),
232*5225e6b1SAndroid Build Coastguard Worker             Bootability::Retriable(_) => {
233*5225e6b1SAndroid Build Coastguard Worker                 let abr_slot = &mut self.get_mut_data().slot_data[idx];
234*5225e6b1SAndroid Build Coastguard Worker                 abr_slot.tries -= 1;
235*5225e6b1SAndroid Build Coastguard Worker                 if abr_slot.tries == 0 {
236*5225e6b1SAndroid Build Coastguard Worker                     abr_slot.unbootable_reason = UnbootableReason::NoMoreTries.into();
237*5225e6b1SAndroid Build Coastguard Worker                 }
238*5225e6b1SAndroid Build Coastguard Worker                 let token = self.take_boot_token().ok_or(Error::OperationProhibited)?;
239*5225e6b1SAndroid Build Coastguard Worker                 Ok(token)
240*5225e6b1SAndroid Build Coastguard Worker             }
241*5225e6b1SAndroid Build Coastguard Worker             Bootability::Successful => {
242*5225e6b1SAndroid Build Coastguard Worker                 let token = self.take_boot_token().ok_or(Error::OperationProhibited)?;
243*5225e6b1SAndroid Build Coastguard Worker                 Ok(token)
244*5225e6b1SAndroid Build Coastguard Worker             }
245*5225e6b1SAndroid Build Coastguard Worker         }
246*5225e6b1SAndroid Build Coastguard Worker     }
247*5225e6b1SAndroid Build Coastguard Worker 
set_active_slot(&mut self, slot_suffix: Suffix) -> Result<(), Error>248*5225e6b1SAndroid Build Coastguard Worker     fn set_active_slot(&mut self, slot_suffix: Suffix) -> Result<(), Error> {
249*5225e6b1SAndroid Build Coastguard Worker         let (idx, _) = self.get_index_and_slot_with_suffix(slot_suffix)?;
250*5225e6b1SAndroid Build Coastguard Worker 
251*5225e6b1SAndroid Build Coastguard Worker         let abr_data = self.get_mut_data();
252*5225e6b1SAndroid Build Coastguard Worker         for (i, slot) in abr_data.slot_data.iter_mut().enumerate() {
253*5225e6b1SAndroid Build Coastguard Worker             if i == idx {
254*5225e6b1SAndroid Build Coastguard Worker                 *slot = Default::default();
255*5225e6b1SAndroid Build Coastguard Worker             } else {
256*5225e6b1SAndroid Build Coastguard Worker                 slot.priority = DEFAULT_PRIORITY - 1;
257*5225e6b1SAndroid Build Coastguard Worker             }
258*5225e6b1SAndroid Build Coastguard Worker         }
259*5225e6b1SAndroid Build Coastguard Worker         Ok(())
260*5225e6b1SAndroid Build Coastguard Worker     }
261*5225e6b1SAndroid Build Coastguard Worker 
get_oneshot_status(&self) -> Option<OneShot>262*5225e6b1SAndroid Build Coastguard Worker     fn get_oneshot_status(&self) -> Option<OneShot> {
263*5225e6b1SAndroid Build Coastguard Worker         self.get_data().oneshot_flag.into()
264*5225e6b1SAndroid Build Coastguard Worker     }
265*5225e6b1SAndroid Build Coastguard Worker 
set_oneshot_status(&mut self, oneshot: OneShot) -> Result<(), Error>266*5225e6b1SAndroid Build Coastguard Worker     fn set_oneshot_status(&mut self, oneshot: OneShot) -> Result<(), Error> {
267*5225e6b1SAndroid Build Coastguard Worker         if Some(oneshot) == self.get_oneshot_status() {
268*5225e6b1SAndroid Build Coastguard Worker             return Ok(());
269*5225e6b1SAndroid Build Coastguard Worker         }
270*5225e6b1SAndroid Build Coastguard Worker 
271*5225e6b1SAndroid Build Coastguard Worker         let oneshot_flag = OneShotFlags::from(Some(oneshot));
272*5225e6b1SAndroid Build Coastguard Worker         if oneshot_flag == OneShotFlags::NONE {
273*5225e6b1SAndroid Build Coastguard Worker             Err(Error::OperationProhibited)
274*5225e6b1SAndroid Build Coastguard Worker         } else {
275*5225e6b1SAndroid Build Coastguard Worker             self.get_mut_data().oneshot_flag = oneshot_flag;
276*5225e6b1SAndroid Build Coastguard Worker             Ok(())
277*5225e6b1SAndroid Build Coastguard Worker         }
278*5225e6b1SAndroid Build Coastguard Worker     }
279*5225e6b1SAndroid Build Coastguard Worker 
clear_oneshot_status(&mut self)280*5225e6b1SAndroid Build Coastguard Worker     fn clear_oneshot_status(&mut self) {
281*5225e6b1SAndroid Build Coastguard Worker         if self.get_oneshot_status().is_some() {
282*5225e6b1SAndroid Build Coastguard Worker             self.get_mut_data().oneshot_flag = OneShotFlags::NONE;
283*5225e6b1SAndroid Build Coastguard Worker         }
284*5225e6b1SAndroid Build Coastguard Worker     }
285*5225e6b1SAndroid Build Coastguard Worker 
write_back(&mut self, persist: &mut dyn FnMut(&mut [u8]) -> Result<(), Error>)286*5225e6b1SAndroid Build Coastguard Worker     fn write_back(&mut self, persist: &mut dyn FnMut(&mut [u8]) -> Result<(), Error>) {
287*5225e6b1SAndroid Build Coastguard Worker         self.sync_to_disk(persist);
288*5225e6b1SAndroid Build Coastguard Worker     }
289*5225e6b1SAndroid Build Coastguard Worker }
290*5225e6b1SAndroid Build Coastguard Worker 
291*5225e6b1SAndroid Build Coastguard Worker impl<'a> SlotBlock<AbrData> {
get_index_and_slot_with_suffix(&self, slot_suffix: Suffix) -> Result<(usize, Slot), Error>292*5225e6b1SAndroid Build Coastguard Worker     fn get_index_and_slot_with_suffix(&self, slot_suffix: Suffix) -> Result<(usize, Slot), Error> {
293*5225e6b1SAndroid Build Coastguard Worker         self.slots_iter()
294*5225e6b1SAndroid Build Coastguard Worker             .enumerate()
295*5225e6b1SAndroid Build Coastguard Worker             .find(|(_, s)| s.suffix == slot_suffix)
296*5225e6b1SAndroid Build Coastguard Worker             .ok_or(Error::InvalidInput)
297*5225e6b1SAndroid Build Coastguard Worker     }
298*5225e6b1SAndroid Build Coastguard Worker }
299*5225e6b1SAndroid Build Coastguard Worker 
300*5225e6b1SAndroid Build Coastguard Worker #[cfg(test)]
301*5225e6b1SAndroid Build Coastguard Worker mod test {
302*5225e6b1SAndroid Build Coastguard Worker     use super::*;
303*5225e6b1SAndroid Build Coastguard Worker     use crate::slots::{partition::CacheStatus, Cursor};
304*5225e6b1SAndroid Build Coastguard Worker     use gbl_async::block_on;
305*5225e6b1SAndroid Build Coastguard Worker     use gbl_storage::{new_gpt_max, Disk, RamBlockIo};
306*5225e6b1SAndroid Build Coastguard Worker 
307*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_slot_block_defaults()308*5225e6b1SAndroid Build Coastguard Worker     fn test_slot_block_defaults() {
309*5225e6b1SAndroid Build Coastguard Worker         let sb: SlotBlock<AbrData> = Default::default();
310*5225e6b1SAndroid Build Coastguard Worker         let expected: Vec<Slot> = vec![
311*5225e6b1SAndroid Build Coastguard Worker             Slot {
312*5225e6b1SAndroid Build Coastguard Worker                 suffix: 'a'.into(),
313*5225e6b1SAndroid Build Coastguard Worker                 priority: DEFAULT_PRIORITY.into(),
314*5225e6b1SAndroid Build Coastguard Worker                 bootability: Bootability::Retriable(sb.get_max_retries().unwrap()),
315*5225e6b1SAndroid Build Coastguard Worker             },
316*5225e6b1SAndroid Build Coastguard Worker             Slot {
317*5225e6b1SAndroid Build Coastguard Worker                 suffix: 'b'.into(),
318*5225e6b1SAndroid Build Coastguard Worker                 priority: DEFAULT_PRIORITY.into(),
319*5225e6b1SAndroid Build Coastguard Worker                 bootability: Bootability::Retriable(sb.get_max_retries().unwrap()),
320*5225e6b1SAndroid Build Coastguard Worker             },
321*5225e6b1SAndroid Build Coastguard Worker         ];
322*5225e6b1SAndroid Build Coastguard Worker         let actual: Vec<Slot> = sb.slots_iter().collect();
323*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(actual, expected);
324*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.get_oneshot_status(), None);
325*5225e6b1SAndroid Build Coastguard Worker     }
326*5225e6b1SAndroid Build Coastguard Worker 
327*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_suffix()328*5225e6b1SAndroid Build Coastguard Worker     fn test_suffix() {
329*5225e6b1SAndroid Build Coastguard Worker         let slot = Slot { suffix: 'a'.into(), ..Default::default() };
330*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(BootTarget::Recovery(RecoveryTarget::Dedicated).suffix(), 'r'.into());
331*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(BootTarget::Recovery(RecoveryTarget::Slotted(slot)).suffix(), slot.suffix);
332*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(BootTarget::NormalBoot(slot).suffix(), slot.suffix);
333*5225e6b1SAndroid Build Coastguard Worker     }
334*5225e6b1SAndroid Build Coastguard Worker 
335*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_slot_block_parse()336*5225e6b1SAndroid Build Coastguard Worker     fn test_slot_block_parse() {
337*5225e6b1SAndroid Build Coastguard Worker         let abr: AbrData = Default::default();
338*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(AbrData::validate(abr.as_bytes()), Ok(Ref::new(abr.as_bytes()).unwrap()));
339*5225e6b1SAndroid Build Coastguard Worker     }
340*5225e6b1SAndroid Build Coastguard Worker 
341*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_slot_block_parse_buffer_too_small()342*5225e6b1SAndroid Build Coastguard Worker     fn test_slot_block_parse_buffer_too_small() {
343*5225e6b1SAndroid Build Coastguard Worker         let buffer: [u8; 0] = Default::default();
344*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(
345*5225e6b1SAndroid Build Coastguard Worker             AbrData::validate(&buffer[..]),
346*5225e6b1SAndroid Build Coastguard Worker             Err(Error::BufferTooSmall(Some(size_of::<AbrData>()))),
347*5225e6b1SAndroid Build Coastguard Worker         );
348*5225e6b1SAndroid Build Coastguard Worker     }
349*5225e6b1SAndroid Build Coastguard Worker 
350*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_slot_block_parse_bad_magic()351*5225e6b1SAndroid Build Coastguard Worker     fn test_slot_block_parse_bad_magic() {
352*5225e6b1SAndroid Build Coastguard Worker         let mut abr: AbrData = Default::default();
353*5225e6b1SAndroid Build Coastguard Worker         abr.magic[0] += 1;
354*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(AbrData::validate(abr.as_bytes()), Err(Error::BadMagic));
355*5225e6b1SAndroid Build Coastguard Worker     }
356*5225e6b1SAndroid Build Coastguard Worker 
357*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_slot_block_parse_bad_version_major()358*5225e6b1SAndroid Build Coastguard Worker     fn test_slot_block_parse_bad_version_major() {
359*5225e6b1SAndroid Build Coastguard Worker         let mut abr: AbrData = Default::default();
360*5225e6b1SAndroid Build Coastguard Worker         abr.version_major = 15;
361*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(AbrData::validate(abr.as_bytes()), Err(Error::UnsupportedVersion));
362*5225e6b1SAndroid Build Coastguard Worker     }
363*5225e6b1SAndroid Build Coastguard Worker 
364*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_slot_block_parse_bad_crc()365*5225e6b1SAndroid Build Coastguard Worker     fn test_slot_block_parse_bad_crc() {
366*5225e6b1SAndroid Build Coastguard Worker         let mut abr: AbrData = Default::default();
367*5225e6b1SAndroid Build Coastguard Worker         let bad_crc = abr.crc32.get() ^ BigEndianU32::MAX_VALUE.get();
368*5225e6b1SAndroid Build Coastguard Worker         abr.crc32 = bad_crc.into();
369*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(AbrData::validate(abr.as_bytes()), Err(Error::BadChecksum));
370*5225e6b1SAndroid Build Coastguard Worker     }
371*5225e6b1SAndroid Build Coastguard Worker 
372*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_slot_mark_boot_attempt()373*5225e6b1SAndroid Build Coastguard Worker     fn test_slot_mark_boot_attempt() {
374*5225e6b1SAndroid Build Coastguard Worker         let mut sb: SlotBlock<AbrData> = Default::default();
375*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.mark_boot_attempt(), Ok(BootToken(())));
376*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(
377*5225e6b1SAndroid Build Coastguard Worker             sb.slots_iter().next().unwrap(),
378*5225e6b1SAndroid Build Coastguard Worker             Slot {
379*5225e6b1SAndroid Build Coastguard Worker                 suffix: 'a'.into(),
380*5225e6b1SAndroid Build Coastguard Worker                 priority: DEFAULT_PRIORITY.into(),
381*5225e6b1SAndroid Build Coastguard Worker                 bootability: Bootability::Retriable((DEFAULT_RETRIES - 1).into())
382*5225e6b1SAndroid Build Coastguard Worker             }
383*5225e6b1SAndroid Build Coastguard Worker         );
384*5225e6b1SAndroid Build Coastguard Worker 
385*5225e6b1SAndroid Build Coastguard Worker         // Make sure we can call exactly once
386*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.mark_boot_attempt(), Err(Error::OperationProhibited));
387*5225e6b1SAndroid Build Coastguard Worker     }
388*5225e6b1SAndroid Build Coastguard Worker 
389*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_slot_mark_boot_attempt_tracks_active()390*5225e6b1SAndroid Build Coastguard Worker     fn test_slot_mark_boot_attempt_tracks_active() {
391*5225e6b1SAndroid Build Coastguard Worker         let mut sb: SlotBlock<AbrData> = Default::default();
392*5225e6b1SAndroid Build Coastguard Worker         assert!(sb.set_active_slot('b'.into()).is_ok());
393*5225e6b1SAndroid Build Coastguard Worker 
394*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.mark_boot_attempt(), Ok(BootToken(())));
395*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(
396*5225e6b1SAndroid Build Coastguard Worker             sb.get_boot_target().unwrap(),
397*5225e6b1SAndroid Build Coastguard Worker             BootTarget::NormalBoot(Slot {
398*5225e6b1SAndroid Build Coastguard Worker                 suffix: 'b'.into(),
399*5225e6b1SAndroid Build Coastguard Worker                 priority: DEFAULT_PRIORITY.into(),
400*5225e6b1SAndroid Build Coastguard Worker                 bootability: Bootability::Retriable((DEFAULT_RETRIES - 1).into())
401*5225e6b1SAndroid Build Coastguard Worker             })
402*5225e6b1SAndroid Build Coastguard Worker         );
403*5225e6b1SAndroid Build Coastguard Worker     }
404*5225e6b1SAndroid Build Coastguard Worker 
405*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_slot_mark_boot_attempt_no_more_tries()406*5225e6b1SAndroid Build Coastguard Worker     fn test_slot_mark_boot_attempt_no_more_tries() {
407*5225e6b1SAndroid Build Coastguard Worker         let mut sb: SlotBlock<AbrData> = Default::default();
408*5225e6b1SAndroid Build Coastguard Worker         sb.get_mut_data().slot_data[0].tries = 1;
409*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.mark_boot_attempt(), Ok(BootToken(())));
410*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(
411*5225e6b1SAndroid Build Coastguard Worker             sb.slots_iter().next().unwrap(),
412*5225e6b1SAndroid Build Coastguard Worker             Slot {
413*5225e6b1SAndroid Build Coastguard Worker                 suffix: 'a'.into(),
414*5225e6b1SAndroid Build Coastguard Worker                 priority: DEFAULT_PRIORITY.into(),
415*5225e6b1SAndroid Build Coastguard Worker                 bootability: Bootability::Unbootable(UnbootableReason::NoMoreTries)
416*5225e6b1SAndroid Build Coastguard Worker             }
417*5225e6b1SAndroid Build Coastguard Worker         );
418*5225e6b1SAndroid Build Coastguard Worker     }
419*5225e6b1SAndroid Build Coastguard Worker 
420*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_slot_mark_boot_attempt_successful()421*5225e6b1SAndroid Build Coastguard Worker     fn test_slot_mark_boot_attempt_successful() {
422*5225e6b1SAndroid Build Coastguard Worker         let mut sb: SlotBlock<AbrData> = Default::default();
423*5225e6b1SAndroid Build Coastguard Worker         sb.get_mut_data().slot_data[0].successful = 1;
424*5225e6b1SAndroid Build Coastguard Worker         let target = BootTarget::NormalBoot(Slot {
425*5225e6b1SAndroid Build Coastguard Worker             suffix: 'a'.into(),
426*5225e6b1SAndroid Build Coastguard Worker             priority: DEFAULT_PRIORITY.into(),
427*5225e6b1SAndroid Build Coastguard Worker             bootability: Bootability::Successful,
428*5225e6b1SAndroid Build Coastguard Worker         });
429*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.mark_boot_attempt(), Ok(BootToken(())));
430*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.get_boot_target().unwrap(), target);
431*5225e6b1SAndroid Build Coastguard Worker     }
432*5225e6b1SAndroid Build Coastguard Worker 
433*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_slot_mark_tried_recovery()434*5225e6b1SAndroid Build Coastguard Worker     fn test_slot_mark_tried_recovery() {
435*5225e6b1SAndroid Build Coastguard Worker         let mut sb: SlotBlock<AbrData> = Default::default();
436*5225e6b1SAndroid Build Coastguard Worker         assert!(sb.set_slot_unbootable('a'.into(), UnbootableReason::UserRequested).is_ok());
437*5225e6b1SAndroid Build Coastguard Worker         assert!(sb.set_slot_unbootable('b'.into(), UnbootableReason::UserRequested).is_ok());
438*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.mark_boot_attempt(), Ok(BootToken(())));
439*5225e6b1SAndroid Build Coastguard Worker 
440*5225e6b1SAndroid Build Coastguard Worker         // Make sure a second attempt fails due to the moved boot token
441*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.mark_boot_attempt(), Err(Error::OperationProhibited));
442*5225e6b1SAndroid Build Coastguard Worker     }
443*5225e6b1SAndroid Build Coastguard Worker 
444*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_slot_mark_tried_recovery_oneshot()445*5225e6b1SAndroid Build Coastguard Worker     fn test_slot_mark_tried_recovery_oneshot() {
446*5225e6b1SAndroid Build Coastguard Worker         let mut sb: SlotBlock<AbrData> = Default::default();
447*5225e6b1SAndroid Build Coastguard Worker         let tgt = sb.get_boot_target();
448*5225e6b1SAndroid Build Coastguard Worker         assert!(sb.set_oneshot_status(OneShot::Continue(RecoveryTarget::Dedicated)).is_ok());
449*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.mark_boot_attempt(), Ok(BootToken(())));
450*5225e6b1SAndroid Build Coastguard Worker 
451*5225e6b1SAndroid Build Coastguard Worker         // Verify that tries weren't decremented
452*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.get_boot_target(), tgt);
453*5225e6b1SAndroid Build Coastguard Worker     }
454*5225e6b1SAndroid Build Coastguard Worker 
455*5225e6b1SAndroid Build Coastguard Worker     macro_rules! set_unbootable_tests {
456*5225e6b1SAndroid Build Coastguard Worker                 ($($name:ident: $value:expr,)*) => {
457*5225e6b1SAndroid Build Coastguard Worker                     $(
458*5225e6b1SAndroid Build Coastguard Worker                         #[test]
459*5225e6b1SAndroid Build Coastguard Worker                         fn $name() {
460*5225e6b1SAndroid Build Coastguard Worker                             let mut sb: SlotBlock<AbrData> = Default::default();
461*5225e6b1SAndroid Build Coastguard Worker                             let suffix: Suffix = 'a'.into();
462*5225e6b1SAndroid Build Coastguard Worker                             assert_eq!(sb.set_slot_unbootable(suffix, $value), Ok(()));
463*5225e6b1SAndroid Build Coastguard Worker                             assert_eq!(sb.slots_iter()
464*5225e6b1SAndroid Build Coastguard Worker                                        .find(|s| s.suffix == suffix)
465*5225e6b1SAndroid Build Coastguard Worker                                        .unwrap()
466*5225e6b1SAndroid Build Coastguard Worker                                        .bootability,
467*5225e6b1SAndroid Build Coastguard Worker                                        Bootability::Unbootable($value)
468*5225e6b1SAndroid Build Coastguard Worker                             );
469*5225e6b1SAndroid Build Coastguard Worker                         }
470*5225e6b1SAndroid Build Coastguard Worker                     )*
471*5225e6b1SAndroid Build Coastguard Worker                 }
472*5225e6b1SAndroid Build Coastguard Worker             }
473*5225e6b1SAndroid Build Coastguard Worker 
474*5225e6b1SAndroid Build Coastguard Worker     use UnbootableReason::*;
475*5225e6b1SAndroid Build Coastguard Worker     set_unbootable_tests! {
476*5225e6b1SAndroid Build Coastguard Worker         test_set_unbootable_no_more_tries: NoMoreTries,
477*5225e6b1SAndroid Build Coastguard Worker         test_set_unbootable_system_update: SystemUpdate,
478*5225e6b1SAndroid Build Coastguard Worker         test_set_unbootable_user_requested: UserRequested,
479*5225e6b1SAndroid Build Coastguard Worker         test_set_unbootable_verification_failure: VerificationFailure,
480*5225e6b1SAndroid Build Coastguard Worker         test_set_unbootable_unknown: Unknown,
481*5225e6b1SAndroid Build Coastguard Worker     }
482*5225e6b1SAndroid Build Coastguard Worker 
483*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_no_bootable_slots_boot_recovery()484*5225e6b1SAndroid Build Coastguard Worker     fn test_no_bootable_slots_boot_recovery() {
485*5225e6b1SAndroid Build Coastguard Worker         let mut sb: SlotBlock<AbrData> = Default::default();
486*5225e6b1SAndroid Build Coastguard Worker         let v: Vec<Slot> = sb.slots_iter().collect();
487*5225e6b1SAndroid Build Coastguard Worker         for slot in v {
488*5225e6b1SAndroid Build Coastguard Worker             assert_eq!(
489*5225e6b1SAndroid Build Coastguard Worker                 sb.set_slot_unbootable(slot.suffix, UnbootableReason::UserRequested),
490*5225e6b1SAndroid Build Coastguard Worker                 Ok(())
491*5225e6b1SAndroid Build Coastguard Worker             );
492*5225e6b1SAndroid Build Coastguard Worker         }
493*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.get_boot_target().unwrap(), BootTarget::Recovery(RecoveryTarget::Dedicated));
494*5225e6b1SAndroid Build Coastguard Worker     }
495*5225e6b1SAndroid Build Coastguard Worker 
496*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_set_active_slot()497*5225e6b1SAndroid Build Coastguard Worker     fn test_set_active_slot() {
498*5225e6b1SAndroid Build Coastguard Worker         let mut sb: SlotBlock<AbrData> = Default::default();
499*5225e6b1SAndroid Build Coastguard Worker         let v: Vec<Slot> = sb.slots_iter().collect();
500*5225e6b1SAndroid Build Coastguard Worker 
501*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.get_boot_target().unwrap(), BootTarget::NormalBoot(v[0]));
502*5225e6b1SAndroid Build Coastguard Worker         for slot in v.iter() {
503*5225e6b1SAndroid Build Coastguard Worker             assert_eq!(sb.set_active_slot(slot.suffix), Ok(()));
504*5225e6b1SAndroid Build Coastguard Worker             assert_eq!(sb.get_boot_target().unwrap(), BootTarget::NormalBoot(*slot));
505*5225e6b1SAndroid Build Coastguard Worker         }
506*5225e6b1SAndroid Build Coastguard Worker     }
507*5225e6b1SAndroid Build Coastguard Worker 
508*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_set_active_slot_no_such_slot()509*5225e6b1SAndroid Build Coastguard Worker     fn test_set_active_slot_no_such_slot() {
510*5225e6b1SAndroid Build Coastguard Worker         let mut sb: SlotBlock<AbrData> = Default::default();
511*5225e6b1SAndroid Build Coastguard Worker         let bad_suffix: Suffix = '$'.into();
512*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.set_active_slot(bad_suffix), Err(Error::InvalidInput));
513*5225e6b1SAndroid Build Coastguard Worker     }
514*5225e6b1SAndroid Build Coastguard Worker 
515*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_get_slot_last_set_active()516*5225e6b1SAndroid Build Coastguard Worker     fn test_get_slot_last_set_active() {
517*5225e6b1SAndroid Build Coastguard Worker         let mut sb: SlotBlock<AbrData> = Default::default();
518*5225e6b1SAndroid Build Coastguard Worker         let v: Vec<Slot> = sb.slots_iter().collect();
519*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.set_active_slot(v[0].suffix), Ok(()));
520*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.get_slot_last_set_active().unwrap(), v[0]);
521*5225e6b1SAndroid Build Coastguard Worker         for slot in v.iter() {
522*5225e6b1SAndroid Build Coastguard Worker             assert_eq!(sb.set_slot_unbootable(slot.suffix, NoMoreTries), Ok(()));
523*5225e6b1SAndroid Build Coastguard Worker         }
524*5225e6b1SAndroid Build Coastguard Worker 
525*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.get_slot_last_set_active().unwrap(), sb.slots_iter().next().unwrap());
526*5225e6b1SAndroid Build Coastguard Worker     }
527*5225e6b1SAndroid Build Coastguard Worker 
528*5225e6b1SAndroid Build Coastguard Worker     macro_rules! set_oneshot_tests {
529*5225e6b1SAndroid Build Coastguard Worker                 ($($name:ident: $value:expr,)*) => {
530*5225e6b1SAndroid Build Coastguard Worker                     $(
531*5225e6b1SAndroid Build Coastguard Worker                         #[test]
532*5225e6b1SAndroid Build Coastguard Worker                         fn $name(){
533*5225e6b1SAndroid Build Coastguard Worker                             let mut sb: SlotBlock<AbrData> = Default::default();
534*5225e6b1SAndroid Build Coastguard Worker                             assert_eq!(sb.set_oneshot_status($value), Ok(()));
535*5225e6b1SAndroid Build Coastguard Worker                             assert_eq!(sb.get_oneshot_status(), Some($value));
536*5225e6b1SAndroid Build Coastguard Worker 
537*5225e6b1SAndroid Build Coastguard Worker                             assert_eq!(sb.get_boot_target().unwrap(),
538*5225e6b1SAndroid Build Coastguard Worker                                        BootTarget::NormalBoot(
539*5225e6b1SAndroid Build Coastguard Worker                                            Slot{
540*5225e6b1SAndroid Build Coastguard Worker                                                suffix: 'a'.into(),
541*5225e6b1SAndroid Build Coastguard Worker                                                priority: DEFAULT_PRIORITY.into(),
542*5225e6b1SAndroid Build Coastguard Worker                                                bootability: Bootability::Retriable(sb.get_max_retries().unwrap()),
543*5225e6b1SAndroid Build Coastguard Worker                                            },
544*5225e6b1SAndroid Build Coastguard Worker                                        ));
545*5225e6b1SAndroid Build Coastguard Worker                         }
546*5225e6b1SAndroid Build Coastguard Worker                     )*
547*5225e6b1SAndroid Build Coastguard Worker                 }
548*5225e6b1SAndroid Build Coastguard Worker             }
549*5225e6b1SAndroid Build Coastguard Worker 
550*5225e6b1SAndroid Build Coastguard Worker     set_oneshot_tests! {
551*5225e6b1SAndroid Build Coastguard Worker         test_set_oneshot_bootloader: OneShot::Bootloader,
552*5225e6b1SAndroid Build Coastguard Worker         test_set_oneshot_recovery: OneShot::Continue(RecoveryTarget::Dedicated),
553*5225e6b1SAndroid Build Coastguard Worker     }
554*5225e6b1SAndroid Build Coastguard Worker 
555*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_clear_oneshot_status()556*5225e6b1SAndroid Build Coastguard Worker     fn test_clear_oneshot_status() {
557*5225e6b1SAndroid Build Coastguard Worker         let mut sb: SlotBlock<AbrData> = Default::default();
558*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.set_oneshot_status(OneShot::Bootloader), Ok(()));
559*5225e6b1SAndroid Build Coastguard Worker         sb.clear_oneshot_status();
560*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.get_oneshot_status(), None);
561*5225e6b1SAndroid Build Coastguard Worker     }
562*5225e6b1SAndroid Build Coastguard Worker 
563*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_set_oneshot_mistaken_recovery_slotted()564*5225e6b1SAndroid Build Coastguard Worker     fn test_set_oneshot_mistaken_recovery_slotted() {
565*5225e6b1SAndroid Build Coastguard Worker         let mut sb: SlotBlock<AbrData> = Default::default();
566*5225e6b1SAndroid Build Coastguard Worker         let slot = sb.slots_iter().next().unwrap();
567*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(
568*5225e6b1SAndroid Build Coastguard Worker             sb.set_oneshot_status(OneShot::Continue(RecoveryTarget::Slotted(slot))),
569*5225e6b1SAndroid Build Coastguard Worker             Err(Error::OperationProhibited)
570*5225e6b1SAndroid Build Coastguard Worker         );
571*5225e6b1SAndroid Build Coastguard Worker     }
572*5225e6b1SAndroid Build Coastguard Worker 
573*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_deserialize_default_to_dirty_cache()574*5225e6b1SAndroid Build Coastguard Worker     fn test_deserialize_default_to_dirty_cache() {
575*5225e6b1SAndroid Build Coastguard Worker         let mut abr_data: AbrData = Default::default();
576*5225e6b1SAndroid Build Coastguard Worker         // Changing the success both invalidates the crc
577*5225e6b1SAndroid Build Coastguard Worker         // and lets us verify that the deserialized slot block
578*5225e6b1SAndroid Build Coastguard Worker         // uses defaulted backing bytes instead of the provided bytes.
579*5225e6b1SAndroid Build Coastguard Worker         abr_data.slot_data[0].successful = 1;
580*5225e6b1SAndroid Build Coastguard Worker         let sb = SlotBlock::<AbrData>::deserialize(abr_data.as_bytes(), BootToken(()));
581*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.cache_status(), CacheStatus::Dirty);
582*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(
583*5225e6b1SAndroid Build Coastguard Worker             sb.slots_iter().next().unwrap().bootability,
584*5225e6b1SAndroid Build Coastguard Worker             Bootability::Retriable(DEFAULT_RETRIES.into())
585*5225e6b1SAndroid Build Coastguard Worker         );
586*5225e6b1SAndroid Build Coastguard Worker     }
587*5225e6b1SAndroid Build Coastguard Worker 
588*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_deserialize_modified_to_clean_cache()589*5225e6b1SAndroid Build Coastguard Worker     fn test_deserialize_modified_to_clean_cache() {
590*5225e6b1SAndroid Build Coastguard Worker         let mut abr_data: AbrData = Default::default();
591*5225e6b1SAndroid Build Coastguard Worker         abr_data.slot_data[0].successful = 1;
592*5225e6b1SAndroid Build Coastguard Worker         // If we recalculate the crc,
593*5225e6b1SAndroid Build Coastguard Worker         // that just means we have a metadata block that stores
594*5225e6b1SAndroid Build Coastguard Worker         // relevant, non-default information.
595*5225e6b1SAndroid Build Coastguard Worker         abr_data.crc32.set(abr_data.calculate_crc32());
596*5225e6b1SAndroid Build Coastguard Worker         let sb = SlotBlock::<AbrData>::deserialize(abr_data.as_bytes(), BootToken(()));
597*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.cache_status(), CacheStatus::Clean);
598*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.slots_iter().next().unwrap().bootability, Bootability::Successful);
599*5225e6b1SAndroid Build Coastguard Worker     }
600*5225e6b1SAndroid Build Coastguard Worker 
601*5225e6b1SAndroid Build Coastguard Worker     type TestDisk = Disk<RamBlockIo<Vec<u8>>, Vec<u8>>;
602*5225e6b1SAndroid Build Coastguard Worker 
603*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_writeback()604*5225e6b1SAndroid Build Coastguard Worker     fn test_writeback() {
605*5225e6b1SAndroid Build Coastguard Worker         const PARTITION: &str = "test_partition";
606*5225e6b1SAndroid Build Coastguard Worker         const OFFSET: u64 = 2112; // Deliberately wrong to test propagation of parameter.
607*5225e6b1SAndroid Build Coastguard Worker         let disk = include_bytes!("../../testdata/writeback_test_disk.bin").to_vec();
608*5225e6b1SAndroid Build Coastguard Worker         let mut blk = TestDisk::new_ram_alloc(512, 512, disk).unwrap();
609*5225e6b1SAndroid Build Coastguard Worker         let mut gpt = new_gpt_max();
610*5225e6b1SAndroid Build Coastguard Worker         block_on(blk.sync_gpt(&mut gpt)).unwrap();
611*5225e6b1SAndroid Build Coastguard Worker         let mut sb: SlotBlock<AbrData> = Default::default();
612*5225e6b1SAndroid Build Coastguard Worker         let mut read_buffer: [u8; size_of::<AbrData>()] = Default::default();
613*5225e6b1SAndroid Build Coastguard Worker 
614*5225e6b1SAndroid Build Coastguard Worker         // Clean cache, write_back is a no-op
615*5225e6b1SAndroid Build Coastguard Worker         sb.write_back(&mut |data: &mut [u8]| {
616*5225e6b1SAndroid Build Coastguard Worker             Ok(block_on(blk.write_gpt_partition(&mut gpt, PARTITION, OFFSET, data))?)
617*5225e6b1SAndroid Build Coastguard Worker         });
618*5225e6b1SAndroid Build Coastguard Worker         let res =
619*5225e6b1SAndroid Build Coastguard Worker             block_on(blk.read_gpt_partition(&mut gpt, PARTITION, OFFSET, &mut read_buffer[..]));
620*5225e6b1SAndroid Build Coastguard Worker         assert!(res.is_ok());
621*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(read_buffer, [0; std::mem::size_of::<AbrData>()]);
622*5225e6b1SAndroid Build Coastguard Worker 
623*5225e6b1SAndroid Build Coastguard Worker         // Make a change, write_back writes back to the defined partition
624*5225e6b1SAndroid Build Coastguard Worker         // at the defined offset.
625*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.set_oneshot_status(OneShot::Bootloader), Ok(()));
626*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.cache_status(), CacheStatus::Dirty);
627*5225e6b1SAndroid Build Coastguard Worker 
628*5225e6b1SAndroid Build Coastguard Worker         sb.write_back(&mut |data: &mut [u8]| {
629*5225e6b1SAndroid Build Coastguard Worker             Ok(block_on(blk.write_gpt_partition(&mut gpt, PARTITION, OFFSET, data))?)
630*5225e6b1SAndroid Build Coastguard Worker         });
631*5225e6b1SAndroid Build Coastguard Worker         let res =
632*5225e6b1SAndroid Build Coastguard Worker             block_on(blk.read_gpt_partition(&mut gpt, PARTITION, OFFSET, &mut read_buffer[..]));
633*5225e6b1SAndroid Build Coastguard Worker         assert!(res.is_ok());
634*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(read_buffer, sb.get_data().as_bytes());
635*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.cache_status(), CacheStatus::Clean);
636*5225e6b1SAndroid Build Coastguard Worker     }
637*5225e6b1SAndroid Build Coastguard Worker 
638*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_writeback_with_cursor()639*5225e6b1SAndroid Build Coastguard Worker     fn test_writeback_with_cursor() {
640*5225e6b1SAndroid Build Coastguard Worker         const PARTITION: &str = "test_partition";
641*5225e6b1SAndroid Build Coastguard Worker         const OFFSET: u64 = 2112; // Deliberately wrong to test propagation of parameter.
642*5225e6b1SAndroid Build Coastguard Worker         let disk = include_bytes!("../../testdata/writeback_test_disk.bin").to_vec();
643*5225e6b1SAndroid Build Coastguard Worker         let mut blk = TestDisk::new_ram_alloc(512, 512, disk).unwrap();
644*5225e6b1SAndroid Build Coastguard Worker         let mut gpt = new_gpt_max();
645*5225e6b1SAndroid Build Coastguard Worker         block_on(blk.sync_gpt(&mut gpt)).unwrap();
646*5225e6b1SAndroid Build Coastguard Worker         let mut read_buffer: [u8; size_of::<AbrData>()] = Default::default();
647*5225e6b1SAndroid Build Coastguard Worker 
648*5225e6b1SAndroid Build Coastguard Worker         let mut sb: SlotBlock<AbrData> = Default::default();
649*5225e6b1SAndroid Build Coastguard Worker 
650*5225e6b1SAndroid Build Coastguard Worker         // New block to trigger drop on the cursor.
651*5225e6b1SAndroid Build Coastguard Worker         {
652*5225e6b1SAndroid Build Coastguard Worker             let mut persist = |data: &mut [u8]| {
653*5225e6b1SAndroid Build Coastguard Worker                 Ok(block_on(blk.write_gpt_partition(&mut gpt, PARTITION, OFFSET, data))?)
654*5225e6b1SAndroid Build Coastguard Worker             };
655*5225e6b1SAndroid Build Coastguard Worker             let cursor = Cursor { ctx: &mut sb, persist: &mut persist };
656*5225e6b1SAndroid Build Coastguard Worker             assert!(cursor.ctx.set_active_slot('b'.into()).is_ok());
657*5225e6b1SAndroid Build Coastguard Worker         }
658*5225e6b1SAndroid Build Coastguard Worker 
659*5225e6b1SAndroid Build Coastguard Worker         let res =
660*5225e6b1SAndroid Build Coastguard Worker             block_on(blk.read_gpt_partition(&mut gpt, PARTITION, OFFSET, &mut read_buffer[..]));
661*5225e6b1SAndroid Build Coastguard Worker         assert!(res.is_ok());
662*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(read_buffer, sb.get_data().as_bytes());
663*5225e6b1SAndroid Build Coastguard Worker     }
664*5225e6b1SAndroid Build Coastguard Worker }
665