xref: /aosp_15_r20/bootable/libbootloader/gbl/libgbl/src/slots/android.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::partition::{MetadataBytes, SlotBlock};
16*5225e6b1SAndroid Build Coastguard Worker use super::{
17*5225e6b1SAndroid Build Coastguard Worker     BootTarget, BootToken, Bootability, Manager, OneShot, RecoveryTarget, Slot, SlotIterator,
18*5225e6b1SAndroid Build Coastguard Worker     Suffix, UnbootableReason,
19*5225e6b1SAndroid Build Coastguard Worker };
20*5225e6b1SAndroid Build Coastguard Worker 
21*5225e6b1SAndroid Build Coastguard Worker use core::convert::TryInto;
22*5225e6b1SAndroid Build Coastguard Worker use core::iter::zip;
23*5225e6b1SAndroid Build Coastguard Worker use core::mem::size_of;
24*5225e6b1SAndroid Build Coastguard Worker use core::ops::{BitAnd, BitOr, Not, Shl, Shr};
25*5225e6b1SAndroid Build Coastguard Worker use crc32fast::Hasher;
26*5225e6b1SAndroid Build Coastguard Worker use liberror::Error;
27*5225e6b1SAndroid Build Coastguard Worker use zerocopy::byteorder::little_endian::U32 as LittleEndianU32;
28*5225e6b1SAndroid Build Coastguard Worker use zerocopy::{AsBytes, ByteSlice, FromBytes, FromZeroes, Ref};
29*5225e6b1SAndroid Build Coastguard Worker 
30*5225e6b1SAndroid Build Coastguard Worker extern crate static_assertions;
31*5225e6b1SAndroid Build Coastguard Worker 
32*5225e6b1SAndroid Build Coastguard Worker const MAX_SLOTS: u8 = 4;
33*5225e6b1SAndroid Build Coastguard Worker 
34*5225e6b1SAndroid Build Coastguard Worker // TODO(b/332338968): remove the manual field definitions and use bindgen definitions.
35*5225e6b1SAndroid Build Coastguard Worker 
36*5225e6b1SAndroid Build Coastguard Worker // Helper function to extract values from bitfields.
37*5225e6b1SAndroid Build Coastguard Worker // Preconditions:
38*5225e6b1SAndroid Build Coastguard Worker // 1) All bits in a bitfield are consecutive.
39*5225e6b1SAndroid Build Coastguard Worker // 1a) No fields interleave their bits.
40*5225e6b1SAndroid Build Coastguard Worker // 2) `offset` defines the position of the least significant bit in the field.
41*5225e6b1SAndroid Build Coastguard Worker // 3) If a bit is set in `mask`, all bits of lower significance are set.
42*5225e6b1SAndroid Build Coastguard Worker // 4) If a bit is NOT set in `mask`, all bits of greater significanec are NOT set.
get_field<N, R>(base: N, offset: N, mask: N) -> R where N: Shr<Output = N> + BitAnd<Output = N>, R: Default + TryFrom<N>,43*5225e6b1SAndroid Build Coastguard Worker fn get_field<N, R>(base: N, offset: N, mask: N) -> R
44*5225e6b1SAndroid Build Coastguard Worker where
45*5225e6b1SAndroid Build Coastguard Worker     N: Shr<Output = N> + BitAnd<Output = N>,
46*5225e6b1SAndroid Build Coastguard Worker     R: Default + TryFrom<N>,
47*5225e6b1SAndroid Build Coastguard Worker {
48*5225e6b1SAndroid Build Coastguard Worker     ((base >> offset) & mask).try_into().unwrap_or_default()
49*5225e6b1SAndroid Build Coastguard Worker }
50*5225e6b1SAndroid Build Coastguard Worker 
51*5225e6b1SAndroid Build Coastguard Worker // Helper function to set values in bit fields.
52*5225e6b1SAndroid Build Coastguard Worker // All the preconditions for `get_field` apply.
53*5225e6b1SAndroid Build Coastguard Worker // Returns the modified field. It is the caller's responsibility
54*5225e6b1SAndroid Build Coastguard Worker // to assign the result appropriately.
set_field<N, R>(base: N, val: R, offset: N, mask: N) -> N where N: Copy + Shl<Output = N> + BitAnd<Output = N> + BitOr<Output = N> + Not<Output = N>, R: Into<N>,55*5225e6b1SAndroid Build Coastguard Worker fn set_field<N, R>(base: N, val: R, offset: N, mask: N) -> N
56*5225e6b1SAndroid Build Coastguard Worker where
57*5225e6b1SAndroid Build Coastguard Worker     N: Copy + Shl<Output = N> + BitAnd<Output = N> + BitOr<Output = N> + Not<Output = N>,
58*5225e6b1SAndroid Build Coastguard Worker     R: Into<N>,
59*5225e6b1SAndroid Build Coastguard Worker {
60*5225e6b1SAndroid Build Coastguard Worker     (base & !(mask << offset)) | ((val.into() & mask) << offset)
61*5225e6b1SAndroid Build Coastguard Worker }
62*5225e6b1SAndroid Build Coastguard Worker 
63*5225e6b1SAndroid Build Coastguard Worker const DEFAULT_PRIORITY: u8 = 7;
64*5225e6b1SAndroid Build Coastguard Worker const DEFAULT_RETRIES: u8 = 7;
65*5225e6b1SAndroid Build Coastguard Worker 
66*5225e6b1SAndroid Build Coastguard Worker /// Android reference implementation for slot-specific metadata.
67*5225e6b1SAndroid Build Coastguard Worker /// See `BootloaderControl` for more background information.
68*5225e6b1SAndroid Build Coastguard Worker ///
69*5225e6b1SAndroid Build Coastguard Worker /// Does NOT contain unbootable reason information.
70*5225e6b1SAndroid Build Coastguard Worker #[repr(C, packed)]
71*5225e6b1SAndroid Build Coastguard Worker #[derive(Copy, Clone, Debug, PartialEq, Eq, AsBytes, FromBytes, FromZeroes)]
72*5225e6b1SAndroid Build Coastguard Worker struct SlotMetaData(u16);
73*5225e6b1SAndroid Build Coastguard Worker 
74*5225e6b1SAndroid Build Coastguard Worker #[allow(dead_code)]
75*5225e6b1SAndroid Build Coastguard Worker #[allow(missing_docs)]
76*5225e6b1SAndroid Build Coastguard Worker impl SlotMetaData {
77*5225e6b1SAndroid Build Coastguard Worker     const PRIORITY_MASK: u16 = 0b1111;
78*5225e6b1SAndroid Build Coastguard Worker     const PRIORITY_OFFSET: u16 = 0;
79*5225e6b1SAndroid Build Coastguard Worker 
80*5225e6b1SAndroid Build Coastguard Worker     const TRIES_MASK: u16 = 0b111;
81*5225e6b1SAndroid Build Coastguard Worker     const TRIES_OFFSET: u16 = 4;
82*5225e6b1SAndroid Build Coastguard Worker 
83*5225e6b1SAndroid Build Coastguard Worker     const SUCCESSFUL_MASK: u16 = 0b1;
84*5225e6b1SAndroid Build Coastguard Worker     const SUCCESSFUL_OFFSET: u16 = 7;
85*5225e6b1SAndroid Build Coastguard Worker 
86*5225e6b1SAndroid Build Coastguard Worker     const VERITY_CORRUPTED_MASK: u16 = 0b1;
87*5225e6b1SAndroid Build Coastguard Worker     const VERITY_CORRUPTED_OFFSET: u16 = 8;
88*5225e6b1SAndroid Build Coastguard Worker 
priority(&self) -> u889*5225e6b1SAndroid Build Coastguard Worker     fn priority(&self) -> u8 {
90*5225e6b1SAndroid Build Coastguard Worker         get_field(self.0, Self::PRIORITY_OFFSET, Self::PRIORITY_MASK)
91*5225e6b1SAndroid Build Coastguard Worker     }
set_priority(&mut self, priority: u8)92*5225e6b1SAndroid Build Coastguard Worker     fn set_priority(&mut self, priority: u8) {
93*5225e6b1SAndroid Build Coastguard Worker         self.0 = set_field(self.0, priority, Self::PRIORITY_OFFSET, Self::PRIORITY_MASK)
94*5225e6b1SAndroid Build Coastguard Worker     }
95*5225e6b1SAndroid Build Coastguard Worker 
tries(&self) -> u896*5225e6b1SAndroid Build Coastguard Worker     fn tries(&self) -> u8 {
97*5225e6b1SAndroid Build Coastguard Worker         get_field(self.0, Self::TRIES_OFFSET, Self::TRIES_MASK)
98*5225e6b1SAndroid Build Coastguard Worker     }
set_tries(&mut self, tries: u8)99*5225e6b1SAndroid Build Coastguard Worker     fn set_tries(&mut self, tries: u8) {
100*5225e6b1SAndroid Build Coastguard Worker         self.0 = set_field(self.0, tries, Self::TRIES_OFFSET, Self::TRIES_MASK)
101*5225e6b1SAndroid Build Coastguard Worker     }
102*5225e6b1SAndroid Build Coastguard Worker 
successful(&self) -> bool103*5225e6b1SAndroid Build Coastguard Worker     fn successful(&self) -> bool {
104*5225e6b1SAndroid Build Coastguard Worker         get_field::<_, u8>(self.0, Self::SUCCESSFUL_OFFSET, Self::SUCCESSFUL_MASK) != 0
105*5225e6b1SAndroid Build Coastguard Worker     }
set_successful(&mut self, successful: bool)106*5225e6b1SAndroid Build Coastguard Worker     fn set_successful(&mut self, successful: bool) {
107*5225e6b1SAndroid Build Coastguard Worker         self.0 = set_field(self.0, successful, Self::SUCCESSFUL_OFFSET, Self::SUCCESSFUL_MASK);
108*5225e6b1SAndroid Build Coastguard Worker     }
109*5225e6b1SAndroid Build Coastguard Worker 
verity_corrupted(&self) -> bool110*5225e6b1SAndroid Build Coastguard Worker     fn verity_corrupted(&self) -> bool {
111*5225e6b1SAndroid Build Coastguard Worker         get_field::<_, u8>(self.0, Self::VERITY_CORRUPTED_OFFSET, Self::VERITY_CORRUPTED_MASK) != 0
112*5225e6b1SAndroid Build Coastguard Worker     }
set_verity_corrupted(&mut self, verity_corrupted: bool)113*5225e6b1SAndroid Build Coastguard Worker     fn set_verity_corrupted(&mut self, verity_corrupted: bool) {
114*5225e6b1SAndroid Build Coastguard Worker         self.0 = set_field(
115*5225e6b1SAndroid Build Coastguard Worker             self.0,
116*5225e6b1SAndroid Build Coastguard Worker             verity_corrupted,
117*5225e6b1SAndroid Build Coastguard Worker             Self::VERITY_CORRUPTED_OFFSET,
118*5225e6b1SAndroid Build Coastguard Worker             Self::VERITY_CORRUPTED_MASK,
119*5225e6b1SAndroid Build Coastguard Worker         );
120*5225e6b1SAndroid Build Coastguard Worker     }
121*5225e6b1SAndroid Build Coastguard Worker }
122*5225e6b1SAndroid Build Coastguard Worker static_assertions::const_assert_eq!(
123*5225e6b1SAndroid Build Coastguard Worker     core::mem::size_of::<SlotMetaData>(),
124*5225e6b1SAndroid Build Coastguard Worker     core::mem::size_of::<u16>()
125*5225e6b1SAndroid Build Coastguard Worker );
126*5225e6b1SAndroid Build Coastguard Worker 
127*5225e6b1SAndroid Build Coastguard Worker impl Default for SlotMetaData {
default() -> Self128*5225e6b1SAndroid Build Coastguard Worker     fn default() -> Self {
129*5225e6b1SAndroid Build Coastguard Worker         let mut val = Self(0);
130*5225e6b1SAndroid Build Coastguard Worker         val.set_priority(DEFAULT_PRIORITY);
131*5225e6b1SAndroid Build Coastguard Worker         val.set_tries(DEFAULT_RETRIES);
132*5225e6b1SAndroid Build Coastguard Worker 
133*5225e6b1SAndroid Build Coastguard Worker         val
134*5225e6b1SAndroid Build Coastguard Worker     }
135*5225e6b1SAndroid Build Coastguard Worker }
136*5225e6b1SAndroid Build Coastguard Worker 
137*5225e6b1SAndroid Build Coastguard Worker #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, AsBytes, FromBytes, FromZeroes)]
138*5225e6b1SAndroid Build Coastguard Worker #[repr(C, packed)]
139*5225e6b1SAndroid Build Coastguard Worker struct ControlBits(u16);
140*5225e6b1SAndroid Build Coastguard Worker 
141*5225e6b1SAndroid Build Coastguard Worker #[allow(dead_code)]
142*5225e6b1SAndroid Build Coastguard Worker #[allow(missing_docs)]
143*5225e6b1SAndroid Build Coastguard Worker impl ControlBits {
144*5225e6b1SAndroid Build Coastguard Worker     const NB_SLOT_MASK: u16 = 0b111;
145*5225e6b1SAndroid Build Coastguard Worker     const NB_SLOT_OFFSET: u16 = 0;
146*5225e6b1SAndroid Build Coastguard Worker 
147*5225e6b1SAndroid Build Coastguard Worker     const RECOVERY_TRIES_MASK: u16 = 0b111;
148*5225e6b1SAndroid Build Coastguard Worker     const RECOVERY_TRIES_OFFSET: u16 = 3;
149*5225e6b1SAndroid Build Coastguard Worker 
150*5225e6b1SAndroid Build Coastguard Worker     const MERGE_STATUS_MASK: u16 = 0b111;
151*5225e6b1SAndroid Build Coastguard Worker     const MERGE_STATUS_OFFSET: u16 = 6;
152*5225e6b1SAndroid Build Coastguard Worker 
nb_slots(&self) -> u8153*5225e6b1SAndroid Build Coastguard Worker     fn nb_slots(&self) -> u8 {
154*5225e6b1SAndroid Build Coastguard Worker         core::cmp::min(get_field(self.0, Self::NB_SLOT_OFFSET, Self::NB_SLOT_MASK), MAX_SLOTS)
155*5225e6b1SAndroid Build Coastguard Worker     }
set_nb_slots(&mut self, nb_slots: u8)156*5225e6b1SAndroid Build Coastguard Worker     fn set_nb_slots(&mut self, nb_slots: u8) {
157*5225e6b1SAndroid Build Coastguard Worker         self.0 = set_field(
158*5225e6b1SAndroid Build Coastguard Worker             self.0,
159*5225e6b1SAndroid Build Coastguard Worker             core::cmp::min(nb_slots, MAX_SLOTS),
160*5225e6b1SAndroid Build Coastguard Worker             Self::NB_SLOT_OFFSET,
161*5225e6b1SAndroid Build Coastguard Worker             Self::NB_SLOT_MASK,
162*5225e6b1SAndroid Build Coastguard Worker         );
163*5225e6b1SAndroid Build Coastguard Worker     }
164*5225e6b1SAndroid Build Coastguard Worker 
recovery_tries(&self) -> u8165*5225e6b1SAndroid Build Coastguard Worker     fn recovery_tries(&self) -> u8 {
166*5225e6b1SAndroid Build Coastguard Worker         get_field(self.0, Self::RECOVERY_TRIES_OFFSET, Self::RECOVERY_TRIES_MASK)
167*5225e6b1SAndroid Build Coastguard Worker     }
set_recovery_tries(&mut self, recovery_tries: u8)168*5225e6b1SAndroid Build Coastguard Worker     fn set_recovery_tries(&mut self, recovery_tries: u8) {
169*5225e6b1SAndroid Build Coastguard Worker         self.0 = set_field(
170*5225e6b1SAndroid Build Coastguard Worker             self.0,
171*5225e6b1SAndroid Build Coastguard Worker             recovery_tries,
172*5225e6b1SAndroid Build Coastguard Worker             Self::RECOVERY_TRIES_OFFSET,
173*5225e6b1SAndroid Build Coastguard Worker             Self::RECOVERY_TRIES_MASK,
174*5225e6b1SAndroid Build Coastguard Worker         );
175*5225e6b1SAndroid Build Coastguard Worker     }
176*5225e6b1SAndroid Build Coastguard Worker 
merge_status(&self) -> u8177*5225e6b1SAndroid Build Coastguard Worker     fn merge_status(&self) -> u8 {
178*5225e6b1SAndroid Build Coastguard Worker         get_field(self.0, Self::MERGE_STATUS_OFFSET, Self::MERGE_STATUS_MASK)
179*5225e6b1SAndroid Build Coastguard Worker     }
set_merge_status(&mut self, merge_status: u8)180*5225e6b1SAndroid Build Coastguard Worker     fn set_merge_status(&mut self, merge_status: u8) {
181*5225e6b1SAndroid Build Coastguard Worker         self.0 =
182*5225e6b1SAndroid Build Coastguard Worker             set_field(self.0, merge_status, Self::MERGE_STATUS_OFFSET, Self::MERGE_STATUS_MASK);
183*5225e6b1SAndroid Build Coastguard Worker     }
184*5225e6b1SAndroid Build Coastguard Worker }
185*5225e6b1SAndroid Build Coastguard Worker 
186*5225e6b1SAndroid Build Coastguard Worker const BOOT_CTRL_MAGIC: u32 = 0x42414342;
187*5225e6b1SAndroid Build Coastguard Worker const BOOT_CTRL_VERSION: u8 = 1;
188*5225e6b1SAndroid Build Coastguard Worker 
189*5225e6b1SAndroid Build Coastguard Worker /// The reference implementation for Android A/B bootloader message structures.
190*5225e6b1SAndroid Build Coastguard Worker /// It is designed to be put in the `slot_suffix` field of the `bootloader_message`
191*5225e6b1SAndroid Build Coastguard Worker /// structure described bootloader_message.h.
192*5225e6b1SAndroid Build Coastguard Worker ///
193*5225e6b1SAndroid Build Coastguard Worker /// See //hardware/interfaces/boot/1.1/default/boot_control/libboot_control.cpp
194*5225e6b1SAndroid Build Coastguard Worker /// and //hardware/interfaces/boot/1.1/default/boot_control/include/private/boot_control_definition.h
195*5225e6b1SAndroid Build Coastguard Worker /// for structure definition and semantics.
196*5225e6b1SAndroid Build Coastguard Worker ///
197*5225e6b1SAndroid Build Coastguard Worker /// Does NOT support oneshots
198*5225e6b1SAndroid Build Coastguard Worker #[repr(C, packed)]
199*5225e6b1SAndroid Build Coastguard Worker #[derive(Copy, Clone, Debug, PartialEq, Eq, AsBytes, FromBytes, FromZeroes)]
200*5225e6b1SAndroid Build Coastguard Worker struct BootloaderControl {
201*5225e6b1SAndroid Build Coastguard Worker     slot_suffix: [u8; 4],
202*5225e6b1SAndroid Build Coastguard Worker     magic: u32,
203*5225e6b1SAndroid Build Coastguard Worker     version: u8,
204*5225e6b1SAndroid Build Coastguard Worker     control_bits: ControlBits,
205*5225e6b1SAndroid Build Coastguard Worker     reserved0: [u8; 1],
206*5225e6b1SAndroid Build Coastguard Worker     slot_metadata: [SlotMetaData; MAX_SLOTS as usize],
207*5225e6b1SAndroid Build Coastguard Worker     reserved1: [u8; 8],
208*5225e6b1SAndroid Build Coastguard Worker     crc32: LittleEndianU32,
209*5225e6b1SAndroid Build Coastguard Worker }
210*5225e6b1SAndroid Build Coastguard Worker static_assertions::const_assert_eq!(core::mem::size_of::<BootloaderControl>(), 32);
211*5225e6b1SAndroid Build Coastguard Worker 
212*5225e6b1SAndroid Build Coastguard Worker impl BootloaderControl {
calculate_crc32(&self) -> u32213*5225e6b1SAndroid Build Coastguard Worker     fn calculate_crc32(&self) -> u32 {
214*5225e6b1SAndroid Build Coastguard Worker         let mut hasher = Hasher::new();
215*5225e6b1SAndroid Build Coastguard Worker         hasher.update(&self.as_bytes()[..(size_of::<Self>() - size_of::<LittleEndianU32>())]);
216*5225e6b1SAndroid Build Coastguard Worker         hasher.finalize()
217*5225e6b1SAndroid Build Coastguard Worker     }
218*5225e6b1SAndroid Build Coastguard Worker }
219*5225e6b1SAndroid Build Coastguard Worker 
220*5225e6b1SAndroid Build Coastguard Worker impl Default for BootloaderControl {
default() -> Self221*5225e6b1SAndroid Build Coastguard Worker     fn default() -> Self {
222*5225e6b1SAndroid Build Coastguard Worker         let mut data = Self {
223*5225e6b1SAndroid Build Coastguard Worker             slot_suffix: Default::default(),
224*5225e6b1SAndroid Build Coastguard Worker             magic: BOOT_CTRL_MAGIC,
225*5225e6b1SAndroid Build Coastguard Worker             version: BOOT_CTRL_VERSION,
226*5225e6b1SAndroid Build Coastguard Worker             control_bits: Default::default(),
227*5225e6b1SAndroid Build Coastguard Worker             reserved0: Default::default(),
228*5225e6b1SAndroid Build Coastguard Worker             slot_metadata: Default::default(),
229*5225e6b1SAndroid Build Coastguard Worker             reserved1: Default::default(),
230*5225e6b1SAndroid Build Coastguard Worker             crc32: LittleEndianU32::ZERO,
231*5225e6b1SAndroid Build Coastguard Worker         };
232*5225e6b1SAndroid Build Coastguard Worker         // The slot suffix field stores the current active slot,
233*5225e6b1SAndroid Build Coastguard Worker         // which starts as the first one.
234*5225e6b1SAndroid Build Coastguard Worker         // Notice that it stores the entire suffix,
235*5225e6b1SAndroid Build Coastguard Worker         // including the leading underscore.
236*5225e6b1SAndroid Build Coastguard Worker         '_'.encode_utf8(&mut data.slot_suffix[0..]);
237*5225e6b1SAndroid Build Coastguard Worker         'a'.encode_utf8(&mut data.slot_suffix[1..]);
238*5225e6b1SAndroid Build Coastguard Worker         data.control_bits.set_nb_slots(4);
239*5225e6b1SAndroid Build Coastguard Worker         data.crc32.set(data.calculate_crc32());
240*5225e6b1SAndroid Build Coastguard Worker         data
241*5225e6b1SAndroid Build Coastguard Worker     }
242*5225e6b1SAndroid Build Coastguard Worker }
243*5225e6b1SAndroid Build Coastguard Worker 
244*5225e6b1SAndroid Build Coastguard Worker impl MetadataBytes for BootloaderControl {
validate<B: ByteSlice>(buffer: B) -> Result<Ref<B, Self>, Error>245*5225e6b1SAndroid Build Coastguard Worker     fn validate<B: ByteSlice>(buffer: B) -> Result<Ref<B, Self>, Error> {
246*5225e6b1SAndroid Build Coastguard Worker         let boot_control_data = Ref::<B, Self>::new_from_prefix(buffer)
247*5225e6b1SAndroid Build Coastguard Worker             .ok_or(Error::BufferTooSmall(Some(size_of::<BootloaderControl>())))?
248*5225e6b1SAndroid Build Coastguard Worker             .0;
249*5225e6b1SAndroid Build Coastguard Worker 
250*5225e6b1SAndroid Build Coastguard Worker         if boot_control_data.magic != BOOT_CTRL_MAGIC {
251*5225e6b1SAndroid Build Coastguard Worker             return Err(Error::BadMagic);
252*5225e6b1SAndroid Build Coastguard Worker         }
253*5225e6b1SAndroid Build Coastguard Worker         if boot_control_data.version > BOOT_CTRL_VERSION {
254*5225e6b1SAndroid Build Coastguard Worker             return Err(Error::UnsupportedVersion);
255*5225e6b1SAndroid Build Coastguard Worker         }
256*5225e6b1SAndroid Build Coastguard Worker         if boot_control_data.crc32.get() != boot_control_data.calculate_crc32() {
257*5225e6b1SAndroid Build Coastguard Worker             return Err(Error::BadChecksum);
258*5225e6b1SAndroid Build Coastguard Worker         }
259*5225e6b1SAndroid Build Coastguard Worker 
260*5225e6b1SAndroid Build Coastguard Worker         Ok(boot_control_data)
261*5225e6b1SAndroid Build Coastguard Worker     }
262*5225e6b1SAndroid Build Coastguard Worker 
prepare_for_sync(&mut self)263*5225e6b1SAndroid Build Coastguard Worker     fn prepare_for_sync(&mut self) {
264*5225e6b1SAndroid Build Coastguard Worker         self.crc32 = self.calculate_crc32().into();
265*5225e6b1SAndroid Build Coastguard Worker     }
266*5225e6b1SAndroid Build Coastguard Worker }
267*5225e6b1SAndroid Build Coastguard Worker 
268*5225e6b1SAndroid Build Coastguard Worker impl super::private::SlotGet for SlotBlock<BootloaderControl> {
get_slot_by_number(&self, number: usize) -> Result<Slot, Error>269*5225e6b1SAndroid Build Coastguard Worker     fn get_slot_by_number(&self, number: usize) -> Result<Slot, Error> {
270*5225e6b1SAndroid Build Coastguard Worker         let lower_ascii_suffixes = ('a'..='z').map(Suffix);
271*5225e6b1SAndroid Build Coastguard Worker         let control = self.get_data();
272*5225e6b1SAndroid Build Coastguard Worker         let (suffix, &slot_data) = zip(lower_ascii_suffixes, control.slot_metadata.iter())
273*5225e6b1SAndroid Build Coastguard Worker             // Note: there may be fewer slots than the maximum possible
274*5225e6b1SAndroid Build Coastguard Worker             .take(control.control_bits.nb_slots().into())
275*5225e6b1SAndroid Build Coastguard Worker             .nth(number)
276*5225e6b1SAndroid Build Coastguard Worker             .ok_or(Error::BadIndex(number))?;
277*5225e6b1SAndroid Build Coastguard Worker 
278*5225e6b1SAndroid Build Coastguard Worker         let bootability = match (slot_data.successful(), slot_data.tries()) {
279*5225e6b1SAndroid Build Coastguard Worker             (true, _) => Bootability::Successful,
280*5225e6b1SAndroid Build Coastguard Worker             (false, t) if t > 0 => Bootability::Retriable(t.into()),
281*5225e6b1SAndroid Build Coastguard Worker             (_, _) => Bootability::Unbootable(UnbootableReason::Unknown),
282*5225e6b1SAndroid Build Coastguard Worker         };
283*5225e6b1SAndroid Build Coastguard Worker 
284*5225e6b1SAndroid Build Coastguard Worker         Ok(Slot { suffix, priority: slot_data.priority().into(), bootability })
285*5225e6b1SAndroid Build Coastguard Worker     }
286*5225e6b1SAndroid Build Coastguard Worker }
287*5225e6b1SAndroid Build Coastguard Worker 
288*5225e6b1SAndroid Build Coastguard Worker impl Manager for SlotBlock<BootloaderControl> {
slots_iter(&self) -> SlotIterator289*5225e6b1SAndroid Build Coastguard Worker     fn slots_iter(&self) -> SlotIterator {
290*5225e6b1SAndroid Build Coastguard Worker         SlotIterator::new(self)
291*5225e6b1SAndroid Build Coastguard Worker     }
292*5225e6b1SAndroid Build Coastguard Worker 
get_boot_target(&self) -> Result<BootTarget, Error>293*5225e6b1SAndroid Build Coastguard Worker     fn get_boot_target(&self) -> Result<BootTarget, Error> {
294*5225e6b1SAndroid Build Coastguard Worker         Ok(self
295*5225e6b1SAndroid Build Coastguard Worker             .slots_iter()
296*5225e6b1SAndroid Build Coastguard Worker             .filter(Slot::is_bootable)
297*5225e6b1SAndroid Build Coastguard Worker             .max_by_key(|slot| (slot.priority, slot.suffix.rank()))
298*5225e6b1SAndroid Build Coastguard Worker             .map_or(
299*5225e6b1SAndroid Build Coastguard Worker                 // TODO(b/326253270): how is the recovery slot actually determined?
300*5225e6b1SAndroid Build Coastguard Worker                 BootTarget::Recovery(RecoveryTarget::Slotted(self.get_slot_last_set_active()?)),
301*5225e6b1SAndroid Build Coastguard Worker                 BootTarget::NormalBoot,
302*5225e6b1SAndroid Build Coastguard Worker             ))
303*5225e6b1SAndroid Build Coastguard Worker     }
304*5225e6b1SAndroid Build Coastguard Worker 
set_slot_unbootable( &mut self, slot_suffix: Suffix, reason: UnbootableReason, ) -> Result<(), Error>305*5225e6b1SAndroid Build Coastguard Worker     fn set_slot_unbootable(
306*5225e6b1SAndroid Build Coastguard Worker         &mut self,
307*5225e6b1SAndroid Build Coastguard Worker         slot_suffix: Suffix,
308*5225e6b1SAndroid Build Coastguard Worker         reason: UnbootableReason,
309*5225e6b1SAndroid Build Coastguard Worker     ) -> Result<(), Error> {
310*5225e6b1SAndroid Build Coastguard Worker         let (idx, slot) = self
311*5225e6b1SAndroid Build Coastguard Worker             .slots_iter()
312*5225e6b1SAndroid Build Coastguard Worker             .enumerate()
313*5225e6b1SAndroid Build Coastguard Worker             .find(|(_, slot)| slot.suffix == slot_suffix)
314*5225e6b1SAndroid Build Coastguard Worker             .ok_or(Error::InvalidInput)?;
315*5225e6b1SAndroid Build Coastguard Worker         if slot.bootability == Bootability::Unbootable(reason) {
316*5225e6b1SAndroid Build Coastguard Worker             return Ok(());
317*5225e6b1SAndroid Build Coastguard Worker         }
318*5225e6b1SAndroid Build Coastguard Worker 
319*5225e6b1SAndroid Build Coastguard Worker         let slot_data = &mut self.get_mut_data().slot_metadata[idx];
320*5225e6b1SAndroid Build Coastguard Worker         slot_data.set_tries(0);
321*5225e6b1SAndroid Build Coastguard Worker         slot_data.set_successful(false);
322*5225e6b1SAndroid Build Coastguard Worker 
323*5225e6b1SAndroid Build Coastguard Worker         Ok(())
324*5225e6b1SAndroid Build Coastguard Worker     }
325*5225e6b1SAndroid Build Coastguard Worker 
mark_boot_attempt(&mut self) -> Result<BootToken, Error>326*5225e6b1SAndroid Build Coastguard Worker     fn mark_boot_attempt(&mut self) -> Result<BootToken, Error> {
327*5225e6b1SAndroid Build Coastguard Worker         let target_slot = match self.get_boot_target()? {
328*5225e6b1SAndroid Build Coastguard Worker             BootTarget::NormalBoot(slot) => slot,
329*5225e6b1SAndroid Build Coastguard Worker             BootTarget::Recovery(RecoveryTarget::Dedicated) => Err(Error::OperationProhibited)?,
330*5225e6b1SAndroid Build Coastguard Worker             BootTarget::Recovery(RecoveryTarget::Slotted(slot)) => {
331*5225e6b1SAndroid Build Coastguard Worker                 self.slots_iter().find(|s| s.suffix == slot.suffix).ok_or(Error::InvalidInput)?;
332*5225e6b1SAndroid Build Coastguard Worker                 return self.take_boot_token().ok_or(Error::OperationProhibited);
333*5225e6b1SAndroid Build Coastguard Worker             }
334*5225e6b1SAndroid Build Coastguard Worker         };
335*5225e6b1SAndroid Build Coastguard Worker 
336*5225e6b1SAndroid Build Coastguard Worker         let (idx, slot) = self
337*5225e6b1SAndroid Build Coastguard Worker             .slots_iter()
338*5225e6b1SAndroid Build Coastguard Worker             .enumerate()
339*5225e6b1SAndroid Build Coastguard Worker             .find(|(_, slot)| slot.suffix == target_slot.suffix)
340*5225e6b1SAndroid Build Coastguard Worker             .ok_or(Error::InvalidInput)?;
341*5225e6b1SAndroid Build Coastguard Worker         match slot.bootability {
342*5225e6b1SAndroid Build Coastguard Worker             Bootability::Unbootable(_) => Err(Error::OperationProhibited),
343*5225e6b1SAndroid Build Coastguard Worker             Bootability::Retriable(_) => {
344*5225e6b1SAndroid Build Coastguard Worker                 let metadata = &mut self.get_mut_data().slot_metadata[idx];
345*5225e6b1SAndroid Build Coastguard Worker                 metadata.set_tries(metadata.tries() - 1);
346*5225e6b1SAndroid Build Coastguard Worker                 let token = self.take_boot_token().ok_or(Error::OperationProhibited)?;
347*5225e6b1SAndroid Build Coastguard Worker                 Ok(token)
348*5225e6b1SAndroid Build Coastguard Worker             }
349*5225e6b1SAndroid Build Coastguard Worker             Bootability::Successful => {
350*5225e6b1SAndroid Build Coastguard Worker                 let token = self.take_boot_token().ok_or(Error::OperationProhibited)?;
351*5225e6b1SAndroid Build Coastguard Worker                 Ok(token)
352*5225e6b1SAndroid Build Coastguard Worker             }
353*5225e6b1SAndroid Build Coastguard Worker         }
354*5225e6b1SAndroid Build Coastguard Worker     }
355*5225e6b1SAndroid Build Coastguard Worker 
set_active_slot(&mut self, slot_suffix: Suffix) -> Result<(), Error>356*5225e6b1SAndroid Build Coastguard Worker     fn set_active_slot(&mut self, slot_suffix: Suffix) -> Result<(), Error> {
357*5225e6b1SAndroid Build Coastguard Worker         let idx =
358*5225e6b1SAndroid Build Coastguard Worker             self.slots_iter().position(|s| s.suffix == slot_suffix).ok_or(Error::InvalidInput)?;
359*5225e6b1SAndroid Build Coastguard Worker 
360*5225e6b1SAndroid Build Coastguard Worker         let data = self.get_mut_data();
361*5225e6b1SAndroid Build Coastguard Worker         for (i, slot) in data.slot_metadata.iter_mut().enumerate() {
362*5225e6b1SAndroid Build Coastguard Worker             if i == idx {
363*5225e6b1SAndroid Build Coastguard Worker                 *slot = Default::default();
364*5225e6b1SAndroid Build Coastguard Worker             } else {
365*5225e6b1SAndroid Build Coastguard Worker                 slot.set_priority(DEFAULT_PRIORITY - 1);
366*5225e6b1SAndroid Build Coastguard Worker             }
367*5225e6b1SAndroid Build Coastguard Worker         }
368*5225e6b1SAndroid Build Coastguard Worker 
369*5225e6b1SAndroid Build Coastguard Worker         // Note: we know this is safe because the slot suffix is an ASCII char,
370*5225e6b1SAndroid Build Coastguard Worker         // which is only 1 byte long in utf8.
371*5225e6b1SAndroid Build Coastguard Worker         // The 0th element of self.data.slot_suffix is an underscore character.
372*5225e6b1SAndroid Build Coastguard Worker         slot_suffix.0.encode_utf8(&mut self.get_mut_data().slot_suffix[1..]);
373*5225e6b1SAndroid Build Coastguard Worker 
374*5225e6b1SAndroid Build Coastguard Worker         Ok(())
375*5225e6b1SAndroid Build Coastguard Worker     }
376*5225e6b1SAndroid Build Coastguard Worker 
set_oneshot_status(&mut self, _: OneShot) -> Result<(), Error>377*5225e6b1SAndroid Build Coastguard Worker     fn set_oneshot_status(&mut self, _: OneShot) -> Result<(), Error> {
378*5225e6b1SAndroid Build Coastguard Worker         Err(Error::OperationProhibited)
379*5225e6b1SAndroid Build Coastguard Worker     }
380*5225e6b1SAndroid Build Coastguard Worker 
clear_oneshot_status(&mut self)381*5225e6b1SAndroid Build Coastguard Worker     fn clear_oneshot_status(&mut self) {}
382*5225e6b1SAndroid Build Coastguard Worker 
write_back(&mut self, persist: &mut dyn FnMut(&mut [u8]) -> Result<(), Error>)383*5225e6b1SAndroid Build Coastguard Worker     fn write_back(&mut self, persist: &mut dyn FnMut(&mut [u8]) -> Result<(), Error>) {
384*5225e6b1SAndroid Build Coastguard Worker         self.sync_to_disk(persist)
385*5225e6b1SAndroid Build Coastguard Worker     }
386*5225e6b1SAndroid Build Coastguard Worker }
387*5225e6b1SAndroid Build Coastguard Worker 
388*5225e6b1SAndroid Build Coastguard Worker #[cfg(test)]
389*5225e6b1SAndroid Build Coastguard Worker mod test {
390*5225e6b1SAndroid Build Coastguard Worker     use super::*;
391*5225e6b1SAndroid Build Coastguard Worker     use crate::slots::{android::BootloaderControl, partition::MetadataBytes};
392*5225e6b1SAndroid Build Coastguard Worker 
393*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_slot_block_defaults()394*5225e6b1SAndroid Build Coastguard Worker     fn test_slot_block_defaults() {
395*5225e6b1SAndroid Build Coastguard Worker         let sb: SlotBlock<BootloaderControl> = Default::default();
396*5225e6b1SAndroid Build Coastguard Worker         let expected: Vec<Slot> = ('a'..='d')
397*5225e6b1SAndroid Build Coastguard Worker             .map(|c| Slot {
398*5225e6b1SAndroid Build Coastguard Worker                 suffix: c.into(),
399*5225e6b1SAndroid Build Coastguard Worker                 priority: DEFAULT_PRIORITY.into(),
400*5225e6b1SAndroid Build Coastguard Worker                 bootability: Bootability::Retriable(sb.get_max_retries().unwrap()),
401*5225e6b1SAndroid Build Coastguard Worker             })
402*5225e6b1SAndroid Build Coastguard Worker             .collect();
403*5225e6b1SAndroid Build Coastguard Worker         let actual: Vec<Slot> = sb.slots_iter().collect();
404*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(actual, expected);
405*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.get_oneshot_status(), None);
406*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.get_boot_target().unwrap(), BootTarget::NormalBoot(expected[0]));
407*5225e6b1SAndroid Build Coastguard Worker         // Include the explicit null bytes for safety.
408*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.get_data().slot_suffix.as_slice(), "_a\0\0".as_bytes());
409*5225e6b1SAndroid Build Coastguard Worker     }
410*5225e6b1SAndroid Build Coastguard Worker 
411*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_slot_block_fewer_slots()412*5225e6b1SAndroid Build Coastguard Worker     fn test_slot_block_fewer_slots() {
413*5225e6b1SAndroid Build Coastguard Worker         let mut sb: SlotBlock<BootloaderControl> = Default::default();
414*5225e6b1SAndroid Build Coastguard Worker         sb.get_mut_data().control_bits.set_nb_slots(2);
415*5225e6b1SAndroid Build Coastguard Worker 
416*5225e6b1SAndroid Build Coastguard Worker         let expected: Vec<Slot> = ('a'..='b')
417*5225e6b1SAndroid Build Coastguard Worker             .map(|c| Slot {
418*5225e6b1SAndroid Build Coastguard Worker                 suffix: c.into(),
419*5225e6b1SAndroid Build Coastguard Worker                 priority: DEFAULT_PRIORITY.into(),
420*5225e6b1SAndroid Build Coastguard Worker                 bootability: Bootability::Retriable(sb.get_max_retries().unwrap()),
421*5225e6b1SAndroid Build Coastguard Worker             })
422*5225e6b1SAndroid Build Coastguard Worker             .collect();
423*5225e6b1SAndroid Build Coastguard Worker         let actual: Vec<Slot> = sb.slots_iter().collect();
424*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(actual, expected);
425*5225e6b1SAndroid Build Coastguard Worker     }
426*5225e6b1SAndroid Build Coastguard Worker 
427*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_slot_block_slot_count_saturates()428*5225e6b1SAndroid Build Coastguard Worker     fn test_slot_block_slot_count_saturates() {
429*5225e6b1SAndroid Build Coastguard Worker         let mut ctrl: BootloaderControl = Default::default();
430*5225e6b1SAndroid Build Coastguard Worker         ctrl.control_bits.set_nb_slots(255);
431*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(ctrl.control_bits.nb_slots(), MAX_SLOTS);
432*5225e6b1SAndroid Build Coastguard Worker 
433*5225e6b1SAndroid Build Coastguard Worker         let mut sb: SlotBlock<BootloaderControl> = Default::default();
434*5225e6b1SAndroid Build Coastguard Worker         sb.get_mut_data().control_bits.set_nb_slots(255);
435*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.slots_iter().count(), MAX_SLOTS.into());
436*5225e6b1SAndroid Build Coastguard Worker     }
437*5225e6b1SAndroid Build Coastguard Worker 
438*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_slot_block_parse()439*5225e6b1SAndroid Build Coastguard Worker     fn test_slot_block_parse() {
440*5225e6b1SAndroid Build Coastguard Worker         let boot_ctrl: BootloaderControl = Default::default();
441*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(
442*5225e6b1SAndroid Build Coastguard Worker             BootloaderControl::validate(boot_ctrl.as_bytes()),
443*5225e6b1SAndroid Build Coastguard Worker             Ok(Ref::new(boot_ctrl.as_bytes()).unwrap())
444*5225e6b1SAndroid Build Coastguard Worker         );
445*5225e6b1SAndroid Build Coastguard Worker     }
446*5225e6b1SAndroid Build Coastguard Worker 
447*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_slot_block_parse_buffer_too_small()448*5225e6b1SAndroid Build Coastguard Worker     fn test_slot_block_parse_buffer_too_small() {
449*5225e6b1SAndroid Build Coastguard Worker         let buffer: [u8; 0] = Default::default();
450*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(
451*5225e6b1SAndroid Build Coastguard Worker             BootloaderControl::validate(buffer.as_slice()),
452*5225e6b1SAndroid Build Coastguard Worker             Err(Error::BufferTooSmall(Some(size_of::<BootloaderControl>())))
453*5225e6b1SAndroid Build Coastguard Worker         );
454*5225e6b1SAndroid Build Coastguard Worker     }
455*5225e6b1SAndroid Build Coastguard Worker 
456*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_slot_block_parse_bad_magic()457*5225e6b1SAndroid Build Coastguard Worker     fn test_slot_block_parse_bad_magic() {
458*5225e6b1SAndroid Build Coastguard Worker         let mut boot_ctrl: BootloaderControl = Default::default();
459*5225e6b1SAndroid Build Coastguard Worker         boot_ctrl.magic += 1;
460*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(BootloaderControl::validate(boot_ctrl.as_bytes()), Err(Error::BadMagic));
461*5225e6b1SAndroid Build Coastguard Worker     }
462*5225e6b1SAndroid Build Coastguard Worker 
463*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_slot_block_parse_bad_version()464*5225e6b1SAndroid Build Coastguard Worker     fn test_slot_block_parse_bad_version() {
465*5225e6b1SAndroid Build Coastguard Worker         let mut boot_ctrl: BootloaderControl = Default::default();
466*5225e6b1SAndroid Build Coastguard Worker         boot_ctrl.version = 15;
467*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(
468*5225e6b1SAndroid Build Coastguard Worker             BootloaderControl::validate(boot_ctrl.as_bytes()),
469*5225e6b1SAndroid Build Coastguard Worker             Err(Error::UnsupportedVersion)
470*5225e6b1SAndroid Build Coastguard Worker         );
471*5225e6b1SAndroid Build Coastguard Worker     }
472*5225e6b1SAndroid Build Coastguard Worker 
473*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_slot_block_parse_bad_crc()474*5225e6b1SAndroid Build Coastguard Worker     fn test_slot_block_parse_bad_crc() {
475*5225e6b1SAndroid Build Coastguard Worker         let mut boot_ctrl: BootloaderControl = Default::default();
476*5225e6b1SAndroid Build Coastguard Worker         let bad_crc = boot_ctrl.crc32.get() ^ LittleEndianU32::MAX_VALUE.get();
477*5225e6b1SAndroid Build Coastguard Worker         boot_ctrl.crc32 = bad_crc.into();
478*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(BootloaderControl::validate(boot_ctrl.as_bytes()), Err(Error::BadChecksum));
479*5225e6b1SAndroid Build Coastguard Worker     }
480*5225e6b1SAndroid Build Coastguard Worker 
481*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_get_boot_target_recovery()482*5225e6b1SAndroid Build Coastguard Worker     fn test_get_boot_target_recovery() {
483*5225e6b1SAndroid Build Coastguard Worker         let mut sb: SlotBlock<BootloaderControl> = Default::default();
484*5225e6b1SAndroid Build Coastguard Worker         sb.get_mut_data().slot_metadata.iter_mut().for_each(|bits| bits.set_tries(0));
485*5225e6b1SAndroid Build Coastguard Worker         let a_slot = sb.slots_iter().next().unwrap();
486*5225e6b1SAndroid Build Coastguard Worker 
487*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(
488*5225e6b1SAndroid Build Coastguard Worker             sb.get_boot_target().unwrap(),
489*5225e6b1SAndroid Build Coastguard Worker             BootTarget::Recovery(RecoveryTarget::Slotted(a_slot))
490*5225e6b1SAndroid Build Coastguard Worker         );
491*5225e6b1SAndroid Build Coastguard Worker     }
492*5225e6b1SAndroid Build Coastguard Worker 
493*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_get_boot_target_recovery_nondefault_recovery_slot()494*5225e6b1SAndroid Build Coastguard Worker     fn test_get_boot_target_recovery_nondefault_recovery_slot() {
495*5225e6b1SAndroid Build Coastguard Worker         let mut sb: SlotBlock<BootloaderControl> = Default::default();
496*5225e6b1SAndroid Build Coastguard Worker         let b_suffix: Suffix = 'b'.into();
497*5225e6b1SAndroid Build Coastguard Worker         assert!(sb.set_active_slot(b_suffix).is_ok());
498*5225e6b1SAndroid Build Coastguard Worker         sb.get_mut_data().slot_metadata.iter_mut().for_each(|bits| bits.set_tries(0));
499*5225e6b1SAndroid Build Coastguard Worker         let b_slot = sb.slots_iter().find(|s| s.suffix == b_suffix).unwrap();
500*5225e6b1SAndroid Build Coastguard Worker 
501*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(
502*5225e6b1SAndroid Build Coastguard Worker             sb.get_boot_target().unwrap(),
503*5225e6b1SAndroid Build Coastguard Worker             BootTarget::Recovery(RecoveryTarget::Slotted(b_slot))
504*5225e6b1SAndroid Build Coastguard Worker         );
505*5225e6b1SAndroid Build Coastguard Worker     }
506*5225e6b1SAndroid Build Coastguard Worker 
507*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_get_slot_last_set_active()508*5225e6b1SAndroid Build Coastguard Worker     fn test_get_slot_last_set_active() {
509*5225e6b1SAndroid Build Coastguard Worker         let mut sb: SlotBlock<BootloaderControl> = Default::default();
510*5225e6b1SAndroid Build Coastguard Worker         let v: Vec<Slot> = sb.slots_iter().collect();
511*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.set_active_slot(v[1].suffix), Ok(()));
512*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.get_slot_last_set_active().unwrap(), v[1]);
513*5225e6b1SAndroid Build Coastguard Worker         for slot in v.iter() {
514*5225e6b1SAndroid Build Coastguard Worker             assert_eq!(sb.set_slot_unbootable(slot.suffix, UnbootableReason::NoMoreTries), Ok(()));
515*5225e6b1SAndroid Build Coastguard Worker         }
516*5225e6b1SAndroid Build Coastguard Worker 
517*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.get_slot_last_set_active().unwrap(), sb.slots_iter().nth(1).unwrap());
518*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.get_data().slot_suffix.as_slice(), "_b\0\0".as_bytes());
519*5225e6b1SAndroid Build Coastguard Worker     }
520*5225e6b1SAndroid Build Coastguard Worker 
521*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_slot_mark_boot_attempt()522*5225e6b1SAndroid Build Coastguard Worker     fn test_slot_mark_boot_attempt() {
523*5225e6b1SAndroid Build Coastguard Worker         let mut sb: SlotBlock<BootloaderControl> = Default::default();
524*5225e6b1SAndroid Build Coastguard Worker         let slot = Slot { suffix: 'a'.into(), ..Default::default() };
525*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.mark_boot_attempt(), Ok(BootToken(())));
526*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(
527*5225e6b1SAndroid Build Coastguard Worker             sb.slots_iter().next().unwrap(),
528*5225e6b1SAndroid Build Coastguard Worker             Slot {
529*5225e6b1SAndroid Build Coastguard Worker                 suffix: slot.suffix,
530*5225e6b1SAndroid Build Coastguard Worker                 priority: DEFAULT_PRIORITY.into(),
531*5225e6b1SAndroid Build Coastguard Worker                 bootability: Bootability::Retriable((DEFAULT_RETRIES - 1).into())
532*5225e6b1SAndroid Build Coastguard Worker             }
533*5225e6b1SAndroid Build Coastguard Worker         );
534*5225e6b1SAndroid Build Coastguard Worker 
535*5225e6b1SAndroid Build Coastguard Worker         // Make sure we can call exactly once
536*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.mark_boot_attempt(), Err(Error::OperationProhibited));
537*5225e6b1SAndroid Build Coastguard Worker     }
538*5225e6b1SAndroid Build Coastguard Worker 
539*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_slot_mark_boot_attempt_no_more_tries()540*5225e6b1SAndroid Build Coastguard Worker     fn test_slot_mark_boot_attempt_no_more_tries() {
541*5225e6b1SAndroid Build Coastguard Worker         let mut sb: SlotBlock<BootloaderControl> = Default::default();
542*5225e6b1SAndroid Build Coastguard Worker         sb.get_mut_data().slot_metadata[0].set_tries(1);
543*5225e6b1SAndroid Build Coastguard Worker         let slot = Slot { suffix: 'a'.into(), ..Default::default() };
544*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.mark_boot_attempt(), Ok(BootToken(())));
545*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(
546*5225e6b1SAndroid Build Coastguard Worker             sb.slots_iter().next().unwrap(),
547*5225e6b1SAndroid Build Coastguard Worker             Slot {
548*5225e6b1SAndroid Build Coastguard Worker                 suffix: slot.suffix,
549*5225e6b1SAndroid Build Coastguard Worker                 priority: DEFAULT_PRIORITY.into(),
550*5225e6b1SAndroid Build Coastguard Worker                 // Default implementation does not track unbootable reasons
551*5225e6b1SAndroid Build Coastguard Worker                 bootability: Bootability::Unbootable(UnbootableReason::Unknown)
552*5225e6b1SAndroid Build Coastguard Worker             }
553*5225e6b1SAndroid Build Coastguard Worker         );
554*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.get_data().slot_metadata[0].tries(), 0);
555*5225e6b1SAndroid Build Coastguard Worker     }
556*5225e6b1SAndroid Build Coastguard Worker 
557*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_slot_mark_boot_attempt_successful()558*5225e6b1SAndroid Build Coastguard Worker     fn test_slot_mark_boot_attempt_successful() {
559*5225e6b1SAndroid Build Coastguard Worker         let mut sb: SlotBlock<BootloaderControl> = Default::default();
560*5225e6b1SAndroid Build Coastguard Worker         let initial_tries;
561*5225e6b1SAndroid Build Coastguard Worker         {
562*5225e6b1SAndroid Build Coastguard Worker             let metadata = &mut sb.get_mut_data().slot_metadata[0];
563*5225e6b1SAndroid Build Coastguard Worker             initial_tries = metadata.tries();
564*5225e6b1SAndroid Build Coastguard Worker             metadata.set_successful(true);
565*5225e6b1SAndroid Build Coastguard Worker         }
566*5225e6b1SAndroid Build Coastguard Worker         let target = BootTarget::NormalBoot(Slot {
567*5225e6b1SAndroid Build Coastguard Worker             suffix: 'a'.into(),
568*5225e6b1SAndroid Build Coastguard Worker             priority: DEFAULT_PRIORITY.into(),
569*5225e6b1SAndroid Build Coastguard Worker             bootability: Bootability::Successful,
570*5225e6b1SAndroid Build Coastguard Worker         });
571*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.mark_boot_attempt(), Ok(BootToken(())));
572*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(BootTarget::NormalBoot(sb.slots_iter().next().unwrap()), target);
573*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.get_data().slot_metadata[0].tries(), initial_tries);
574*5225e6b1SAndroid Build Coastguard Worker     }
575*5225e6b1SAndroid Build Coastguard Worker 
576*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_mark_slot_tried_slotted_recovery()577*5225e6b1SAndroid Build Coastguard Worker     fn test_mark_slot_tried_slotted_recovery() {
578*5225e6b1SAndroid Build Coastguard Worker         let mut sb: SlotBlock<BootloaderControl> = Default::default();
579*5225e6b1SAndroid Build Coastguard Worker         assert!(sb.set_slot_unbootable('a'.into(), UnbootableReason::UserRequested).is_ok());
580*5225e6b1SAndroid Build Coastguard Worker         assert!(sb.set_slot_unbootable('b'.into(), UnbootableReason::UserRequested).is_ok());
581*5225e6b1SAndroid Build Coastguard Worker         assert_eq!(sb.mark_boot_attempt(), Ok(BootToken(())));
582*5225e6b1SAndroid Build Coastguard Worker     }
583*5225e6b1SAndroid Build Coastguard Worker 
584*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_set_oneshot_status_unsupported()585*5225e6b1SAndroid Build Coastguard Worker     fn test_set_oneshot_status_unsupported() {
586*5225e6b1SAndroid Build Coastguard Worker         let mut sb: SlotBlock<BootloaderControl> = Default::default();
587*5225e6b1SAndroid Build Coastguard Worker         let oneshots = [
588*5225e6b1SAndroid Build Coastguard Worker             OneShot::Bootloader,
589*5225e6b1SAndroid Build Coastguard Worker             OneShot::Continue(RecoveryTarget::Dedicated),
590*5225e6b1SAndroid Build Coastguard Worker             OneShot::Continue(RecoveryTarget::Slotted(sb.get_slot_last_set_active().unwrap())),
591*5225e6b1SAndroid Build Coastguard Worker         ];
592*5225e6b1SAndroid Build Coastguard Worker 
593*5225e6b1SAndroid Build Coastguard Worker         for oneshot in oneshots {
594*5225e6b1SAndroid Build Coastguard Worker             assert_eq!(sb.set_oneshot_status(oneshot), Err(Error::OperationProhibited));
595*5225e6b1SAndroid Build Coastguard Worker         }
596*5225e6b1SAndroid Build Coastguard Worker     }
597*5225e6b1SAndroid Build Coastguard Worker }
598