1 // Copyright 2024, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #![cfg_attr(not(test), no_std)]
16 
17 use core::{cmp::min, ffi::c_uint, fmt::Write, mem::size_of};
18 
19 const ABR_MAGIC: &[u8; 4] = b"\0AB0";
20 const ABR_MAJOR_VERSION: u8 = 2;
21 const ABR_MINOR_VERSION: u8 = 2;
22 
23 // The following flags are harcoded as u8 instead of using the bitflag crate to avoid additional
24 // crate dependency and improve portability.
25 
26 /// One-shot recovery boot bit for the flag returned by `get_and_clear_one_shot_flag()`.
27 pub const ONE_SHOT_RECOVERY: u8 = 1 << 0;
28 /// One-shot bootloader boot bit for the flag returned by `get_and_clear_one_shot_flag()`.
29 pub const ONE_SHOT_BOOTLOADER: u8 = 1 << 1;
30 
31 const ABR_MAX_PRIORITY: u8 = 15;
32 const ABR_MAX_TRIES_REMAINING: u8 = 7;
33 
34 /// Error type for this library.
35 #[derive(Debug)]
36 pub enum Error {
37     BadMagic,
38     UnsupportedVersion,
39     BadChecksum,
40     InvalidData,
41     OpsError(Option<&'static str>),
42 }
43 
44 impl From<Option<&'static str>> for Error {
from(val: Option<&'static str>) -> Self45     fn from(val: Option<&'static str>) -> Self {
46         Error::OpsError(val)
47     }
48 }
49 
50 /// `Ops` provides the backend interfaces needed by A/B/R APIs.
51 pub trait Ops {
52     /// Reads exactly `out.len()` bytes into `out` from the persistent storage hosting the A/B/R
53     /// metadata.
read_abr_metadata(&mut self, out: &mut [u8]) -> Result<(), Option<&'static str>>54     fn read_abr_metadata(&mut self, out: &mut [u8]) -> Result<(), Option<&'static str>>;
55 
56     /// Writes exactly `data.len()` bytes from `data` to the persistent storage hosting the A/B/R
57     /// metadata.
write_abr_metadata(&mut self, data: &mut [u8]) -> Result<(), Option<&'static str>>58     fn write_abr_metadata(&mut self, data: &mut [u8]) -> Result<(), Option<&'static str>>;
59 
60     /// Returns an optional console writer for logging error messages.
console(&mut self) -> Option<&mut dyn Write>61     fn console(&mut self) -> Option<&mut dyn Write>;
62 }
63 
64 /// Helper macro for printing ABR log messages.
65 macro_rules! avb_print {
66     ( $abr_ops:expr, $( $x:expr ),* $(,)? ) => {
67             match $abr_ops.console() {
68                 Some(f) => write!(f, $($x,)*).unwrap(),
69                 _ => {}
70             }
71     };
72 }
73 
74 /// `SlotIndex` represents the A/B/R slot index.
75 #[derive(Copy, Clone, Eq, PartialEq, Debug)]
76 pub enum SlotIndex {
77     A,
78     B,
79     R,
80 }
81 
82 impl SlotIndex {
83     // Get the other counterpart of a A/B slot.
other(&self) -> Self84     fn other(&self) -> Self {
85         match self {
86             SlotIndex::A => SlotIndex::B,
87             SlotIndex::B => SlotIndex::A,
88             _ => panic!("Invalid slot index for `fn other()`"),
89         }
90     }
91 }
92 
93 // Implement conversion to c_uint for C interfaces
94 impl From<SlotIndex> for c_uint {
from(_val: SlotIndex) -> Self95     fn from(_val: SlotIndex) -> Self {
96         match _val {
97             SlotIndex::A => 0,
98             SlotIndex::B => 1,
99             SlotIndex::R => 2,
100         }
101     }
102 }
103 
104 // Implement conversion from c_uint for C interfaces.
105 impl TryFrom<c_uint> for SlotIndex {
106     type Error = Error;
107 
try_from(val: c_uint) -> core::result::Result<SlotIndex, Self::Error>108     fn try_from(val: c_uint) -> core::result::Result<SlotIndex, Self::Error> {
109         match val {
110             v if v == (SlotIndex::A).into() => Ok(SlotIndex::A),
111             v if v == (SlotIndex::B).into() => Ok(SlotIndex::B),
112             v if v == (SlotIndex::R).into() => Ok(SlotIndex::R),
113             _ => Err(Error::InvalidData),
114         }
115     }
116 }
117 
118 /// `SlotInfo` represents the current state of a A/B/R slot.
119 pub enum SlotState {
120     Successful,
121     Bootable(u8), // u8 = tries remaining
122     Unbootable,
123 }
124 
125 /// `SlotInfo` contains the current state and active status of a A/B/R slot.
126 pub struct SlotInfo {
127     pub state: SlotState,
128     pub is_active: bool,
129 }
130 
131 /// Error type for this library.
132 pub type AbrResult<T> = Result<T, Error>;
133 
134 /// `AbrSlotData` is the wire format metadata for A/B slot.
135 #[repr(C, packed)]
136 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
137 pub struct AbrSlotData {
138     pub priority: u8,
139     pub tries_remaining: u8,
140     pub successful_boot: u8,
141     pub reserved: u8,
142 }
143 
144 const ABR_SLOT_DATA_SIZE: usize = size_of::<AbrSlotData>();
145 
146 impl AbrSlotData {
147     /// Parses from bytes.
deserialize(bytes: &[u8; ABR_SLOT_DATA_SIZE]) -> Self148     pub fn deserialize(bytes: &[u8; ABR_SLOT_DATA_SIZE]) -> Self {
149         Self {
150             priority: bytes[0],
151             tries_remaining: bytes[1],
152             successful_boot: bytes[2],
153             reserved: bytes[3],
154         }
155     }
156 
157     /// Serializes to bytes.
serialize(&self) -> [u8; ABR_SLOT_DATA_SIZE]158     pub fn serialize(&self) -> [u8; ABR_SLOT_DATA_SIZE] {
159         [self.priority, self.tries_remaining, self.successful_boot, self.reserved]
160     }
161 
162     /// Returns if slot is bootable
is_slot_bootable(&self) -> bool163     fn is_slot_bootable(&self) -> bool {
164         self.priority > 0 && (self.successful_boot == 1 || self.tries_remaining > 0)
165     }
166 
set_slot_unbootable(&mut self)167     fn set_slot_unbootable(&mut self) {
168         self.tries_remaining = 0;
169         self.successful_boot = 0;
170     }
171 
172     /// Gets normalized priority.
get_normalized_priority(&self) -> u8173     fn get_normalized_priority(&self) -> u8 {
174         match self.is_slot_bootable() {
175             true => self.priority,
176             _ => 0,
177         }
178     }
179 
180     /// Ensures all unbootable or invalid states are marked as the canonical `unbootable` state.
181     /// That is priority=0, tries_remaining=0, and successful_boot=0.
slot_normalize(&mut self)182     fn slot_normalize(&mut self) {
183         if self.priority > 0 {
184             if self.tries_remaining == 0 && self.successful_boot == 0 {
185                 // All tries exhausted
186                 self.set_slot_unbootable();
187             }
188             if self.tries_remaining > 0 && self.successful_boot == 1 {
189                 // Illegal state. Reset to not successful state
190                 self.tries_remaining = ABR_MAX_TRIES_REMAINING;
191                 self.successful_boot = 0;
192             }
193             self.priority = min(self.priority, ABR_MAX_PRIORITY);
194             self.tries_remaining = min(self.tries_remaining, ABR_MAX_TRIES_REMAINING);
195         } else {
196             self.set_slot_unbootable();
197         }
198     }
199 }
200 
201 /// `AbrData` is the wire format of A/B/R metadata.
202 #[repr(C, packed)]
203 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
204 pub struct AbrData {
205     pub magic: [u8; 4],
206     pub version_major: u8,
207     pub version_minor: u8,
208     pub reserved: [u8; 2],
209     pub slot_data: [AbrSlotData; 2],
210     pub one_shot_flags: u8,
211     pub reserved2: [u8; 11],
212     pub crc32: u32,
213 }
214 
215 const ABR_DATA_SIZE: usize = size_of::<AbrData>();
216 
217 impl AbrData {
218     /// Returns the numeric index value for a `SlotIndex`. This is for indexing into
219     /// `Self::slot_data`.
slot_num_index(slot_index: SlotIndex) -> usize220     fn slot_num_index(slot_index: SlotIndex) -> usize {
221         match slot_index {
222             SlotIndex::A => 0,
223             SlotIndex::B => 1,
224             _ => panic!("Invalid slot index"),
225         }
226     }
227 
228     /// Returns a const reference to `Self::slot_data['slot_index']`
slot_data(&self, slot_index: SlotIndex) -> &AbrSlotData229     fn slot_data(&self, slot_index: SlotIndex) -> &AbrSlotData {
230         &self.slot_data[Self::slot_num_index(slot_index)]
231     }
232 
233     /// Returns a mutable reference to `Self::slot_data[`slot_index`]`
slot_data_mut(&mut self, slot_index: SlotIndex) -> &mut AbrSlotData234     fn slot_data_mut(&mut self, slot_index: SlotIndex) -> &mut AbrSlotData {
235         &mut self.slot_data[Self::slot_num_index(slot_index)]
236     }
237 
238     /// Reads, parses and checks metadata from persistent storage.
deserialize(abr_ops: &mut dyn Ops) -> AbrResult<Self>239     fn deserialize(abr_ops: &mut dyn Ops) -> AbrResult<Self> {
240         let mut bytes = [0u8; ABR_DATA_SIZE];
241         abr_ops.read_abr_metadata(&mut bytes[..])?;
242         // Usually, the parsing below should be done using the zerocopy crate. However, the Fuchsia
243         // source tree uses the unreleased alpha/beta version of zerocopy which can have
244         // drastically different usage and bound requirements. In order to minimize maintenance
245         // burden for Android and Fuchsia build, we manually copy and parse from the bytes directly
246         // to avoid zerocopy crate dependency.
247         let res = Self {
248             magic: bytes[..4].try_into().unwrap(),
249             version_major: bytes[4],
250             version_minor: bytes[5],
251             reserved: bytes[6..8].try_into().unwrap(),
252             slot_data: [
253                 AbrSlotData::deserialize(&bytes[8..12].try_into().unwrap()),
254                 AbrSlotData::deserialize(&bytes[12..16].try_into().unwrap()),
255             ],
256             one_shot_flags: bytes[16],
257             reserved2: bytes[17..28].try_into().unwrap(),
258             crc32: u32::from_be_bytes(bytes[28..ABR_DATA_SIZE].try_into().unwrap()),
259         };
260 
261         if res.magic != *ABR_MAGIC {
262             avb_print!(abr_ops, "Magic is incorrect.\n");
263             return Err(Error::BadMagic);
264         }
265         if res.crc32 != crc32(&bytes[..28]) {
266             avb_print!(abr_ops, "CRC32 does not match.\n");
267             return Err(Error::BadChecksum);
268         }
269         if res.version_major > ABR_MAJOR_VERSION {
270             avb_print!(abr_ops, "No support for given major version.\n");
271             return Err(Error::UnsupportedVersion);
272         }
273 
274         Ok(res)
275     }
276 
277     /// Updates CRC32 and writes metadata to persistent storage.
serialize(&mut self) -> [u8; ABR_DATA_SIZE]278     fn serialize(&mut self) -> [u8; ABR_DATA_SIZE] {
279         let mut res = [0u8; ABR_DATA_SIZE];
280         res[..4].clone_from_slice(&self.magic);
281         res[4] = self.version_major;
282         res[5] = self.version_minor;
283         res[6..8].clone_from_slice(&self.reserved);
284         res[8..12].clone_from_slice(&self.slot_data(SlotIndex::A).serialize());
285         res[12..16].clone_from_slice(&self.slot_data(SlotIndex::B).serialize());
286         res[16] = self.one_shot_flags;
287         res[17..28].clone_from_slice(&self.reserved2[..]);
288         self.crc32 = crc32(&res[..28]);
289         res[28..ABR_DATA_SIZE].clone_from_slice(&self.crc32.to_be_bytes());
290         res
291     }
292 
293     /// Returns an invalid instance.
null() -> Self294     fn null() -> Self {
295         Self { magic: [0u8; 4], ..Default::default() }
296     }
297 
298     /// Gets the active slot
get_active_slot(&self) -> SlotIndex299     fn get_active_slot(&self) -> SlotIndex {
300         let priority_a = self.slot_data(SlotIndex::A).get_normalized_priority();
301         let priority_b = self.slot_data(SlotIndex::B).get_normalized_priority();
302         if priority_b > priority_a {
303             return SlotIndex::B;
304         } else if priority_a > 0 {
305             return SlotIndex::A;
306         }
307         return SlotIndex::R;
308     }
309 
310     /// Is the given slot active.
is_slot_active(&self, slot_index: SlotIndex) -> bool311     fn is_slot_active(&self, slot_index: SlotIndex) -> bool {
312         self.get_active_slot() == slot_index
313     }
314 
315     /// Returns if one-shot recovery is set.
is_one_shot_recovery(&self) -> bool316     fn is_one_shot_recovery(&self) -> bool {
317         (self.one_shot_flags & ONE_SHOT_RECOVERY) != 0
318     }
319 
320     /// Sets one-shot recovery.
set_one_shot_recovery(&mut self, enable: bool)321     pub fn set_one_shot_recovery(&mut self, enable: bool) {
322         match enable {
323             true => self.one_shot_flags |= ONE_SHOT_RECOVERY,
324             _ => self.one_shot_flags &= !ONE_SHOT_RECOVERY,
325         }
326     }
327 
328     /// Sets one-shot bootloader
set_one_shot_bootloader(&mut self, enable: bool)329     pub fn set_one_shot_bootloader(&mut self, enable: bool) {
330         match enable {
331             true => self.one_shot_flags |= ONE_SHOT_BOOTLOADER,
332             _ => self.one_shot_flags &= !ONE_SHOT_BOOTLOADER,
333         }
334     }
335 }
336 
337 impl Default for AbrData {
default() -> Self338     fn default() -> Self {
339         Self {
340             magic: *ABR_MAGIC,
341             version_major: ABR_MAJOR_VERSION,
342             version_minor: ABR_MINOR_VERSION,
343             reserved: Default::default(),
344             slot_data: [
345                 AbrSlotData {
346                     priority: ABR_MAX_PRIORITY,
347                     tries_remaining: ABR_MAX_TRIES_REMAINING,
348                     successful_boot: 0,
349                     reserved: 0,
350                 },
351                 AbrSlotData {
352                     priority: ABR_MAX_PRIORITY - 1,
353                     tries_remaining: ABR_MAX_TRIES_REMAINING,
354                     successful_boot: 0,
355                     reserved: 0,
356                 },
357             ],
358             one_shot_flags: 0,
359             reserved2: Default::default(),
360             crc32: 0,
361         }
362     }
363 }
364 
365 /// Loads |abr_data| from persistent storage and normalizes it, initializing new data if necessary.
366 /// Changes as a result of normalization are not written back to persistent storage but a copy of
367 /// the exact original data from persistent storage is provided in |abr_data_orig| for future use
368 /// with save_metadata_if_changed().
369 ///
370 /// On success returns Ok((abr_data, abr_data_orig)). On failure an Error is returned.
load_metadata(abr_ops: &mut dyn Ops) -> AbrResult<(AbrData, AbrData)>371 fn load_metadata(abr_ops: &mut dyn Ops) -> AbrResult<(AbrData, AbrData)> {
372     let mut abr_data_orig = AbrData::null();
373     let mut abr_data = match AbrData::deserialize(abr_ops) {
374         Ok(v) => {
375             abr_data_orig = v;
376             v
377         }
378         Err(Error::OpsError(e)) => {
379             avb_print!(abr_ops, "read_abr_metadata error: {:?}\n", e);
380             return Err(e.into());
381         }
382         Err(Error::UnsupportedVersion) => {
383             // We don't want to clobber valid data in persistent storage, but we can't use this
384             // data, so bail out.
385             return Err(Error::UnsupportedVersion);
386         }
387         _ => Default::default(),
388     };
389     abr_data.slot_data_mut(SlotIndex::A).slot_normalize();
390     abr_data.slot_data_mut(SlotIndex::B).slot_normalize();
391 
392     Ok((abr_data, abr_data_orig))
393 }
394 
395 /// Serializes and saves metadata to persistent storage.
save_metadata(abr_ops: &mut dyn Ops, abr_data: &mut AbrData) -> AbrResult<()>396 fn save_metadata(abr_ops: &mut dyn Ops, abr_data: &mut AbrData) -> AbrResult<()> {
397     let mut bytes = abr_data.serialize();
398     abr_ops.write_abr_metadata(&mut bytes)?;
399     Ok(())
400 }
401 
402 /// Writes metadata to disk only if it has changed. `abr_data_orig` should be from load_metadata().
save_metadata_if_changed( abr_ops: &mut dyn Ops, abr_data: &mut AbrData, abr_data_orig: &AbrData, ) -> AbrResult<()>403 fn save_metadata_if_changed(
404     abr_ops: &mut dyn Ops,
405     abr_data: &mut AbrData,
406     abr_data_orig: &AbrData,
407 ) -> AbrResult<()> {
408     match abr_data == abr_data_orig {
409         true => Ok(()),
410         _ => save_metadata(abr_ops, abr_data),
411     }
412 }
413 
414 /// Equivalent to C API `AbrGetBootSlot()`.
415 ///
416 /// TODO(b/338243123): Detailed documentation is available in Fuchsia upstream header
417 /// "src/firmware/lib/abr/include/lib/abr/abr.h", which will migrate to the GBL repo.
get_boot_slot(abr_ops: &mut dyn Ops, update_metadata: bool) -> (SlotIndex, bool)418 pub fn get_boot_slot(abr_ops: &mut dyn Ops, update_metadata: bool) -> (SlotIndex, bool) {
419     let mut is_slot_marked_successful = false;
420     let (mut abr_data, abr_data_orig) = match load_metadata(abr_ops) {
421         Ok(v) => v,
422         Err(e) => {
423             avb_print!(
424                 abr_ops,
425                 "Failed to load metadata {:?}, falling back to recovery mode.\n",
426                 e
427             );
428             return (SlotIndex::R, is_slot_marked_successful);
429         }
430     };
431 
432     if abr_data.is_one_shot_recovery() && update_metadata {
433         abr_data.set_one_shot_recovery(false);
434         match save_metadata(abr_ops, &mut abr_data) {
435             Ok(()) => return (SlotIndex::R, is_slot_marked_successful),
436             Err(e) => {
437                 avb_print!(
438                     abr_ops,
439                     "Failed to update one-shot state {:?}. Ignoring one-shot request.\n",
440                     e
441                 );
442                 abr_data.set_one_shot_recovery(true);
443             }
444         }
445     }
446 
447     // Chooses the highest priority and bootable slot. Otherwise R slot.
448     let slot_to_boot = abr_data.get_active_slot();
449     match slot_to_boot {
450         SlotIndex::R => {}
451         v => {
452             is_slot_marked_successful = abr_data.slot_data(v).successful_boot == 1;
453         }
454     };
455 
456     if update_metadata {
457         // In addition to any changes that resulted from normalization, there are a couple changes
458         // to be made here. First is to decrement the tries remaining for a slot not yet marked as
459         // successful.
460         if slot_to_boot != SlotIndex::R && !is_slot_marked_successful {
461             let slot_data = abr_data.slot_data_mut(slot_to_boot);
462             slot_data.tries_remaining = slot_data.tries_remaining.checked_sub(1).unwrap();
463         }
464         // Second is to clear the successful_boot bit from any successfully-marked slots that
465         // aren't the slot we're booting. It's possible that booting from one slot will render the
466         // other slot unbootable (say, by migrating a config file format in a shared partiton).
467         // Clearing these bits minimizes the risk we'll have an unhealthy slot marked
468         // "successful_boot", which would prevent the system from automatically booting into
469         // recovery.
470         for slot in [SlotIndex::A, SlotIndex::B] {
471             if slot != slot_to_boot && abr_data.slot_data(slot).successful_boot == 1 {
472                 abr_data.slot_data_mut(slot).tries_remaining = ABR_MAX_TRIES_REMAINING;
473                 abr_data.slot_data_mut(slot).successful_boot = 0;
474             }
475         }
476         if let Err(e) = save_metadata_if_changed(abr_ops, &mut abr_data, &abr_data_orig) {
477             // We have no choice but to proceed without updating metadata.
478             avb_print!(abr_ops, "Failed to update metadata {:?}, proceeding anyways.\n", e);
479         }
480     }
481     (slot_to_boot, is_slot_marked_successful)
482 }
483 
484 /// Equivalent to C API `AbrMarkSlotActive()`.
485 ///
486 /// TODO(b/338243123): Detailed documentation is available in Fuchsia upstream header
487 /// "src/firmware/lib/abr/include/lib/abr/abr.h", which will migrate to the GBL repo.
mark_slot_active(abr_ops: &mut dyn Ops, slot_index: SlotIndex) -> AbrResult<()>488 pub fn mark_slot_active(abr_ops: &mut dyn Ops, slot_index: SlotIndex) -> AbrResult<()> {
489     if slot_index == SlotIndex::R {
490         avb_print!(abr_ops, "Invalid argument: Cannot mark slot R as active.\n");
491         return Err(Error::InvalidData);
492     }
493     let (mut abr_data, abr_data_orig) = load_metadata(abr_ops)?;
494     // Make requested slot top priority, unsuccessful, and with max tries.
495     abr_data.slot_data_mut(slot_index).priority = ABR_MAX_PRIORITY;
496     abr_data.slot_data_mut(slot_index).tries_remaining = ABR_MAX_TRIES_REMAINING;
497     abr_data.slot_data_mut(slot_index).successful_boot = 0;
498 
499     // Ensure other slot doesn't have as high a priority
500     let other = slot_index.other();
501     abr_data.slot_data_mut(other).priority =
502         min(abr_data.slot_data_mut(other).priority, ABR_MAX_PRIORITY - 1);
503 
504     save_metadata_if_changed(abr_ops, &mut abr_data, &abr_data_orig)
505 }
506 
507 /// Equivalent to C API `AbrGetSlotLastMarkedActive()`.
508 ///
509 /// TODO(b/338243123): Detailed documentation is available in Fuchsia upstream header
510 /// "src/firmware/lib/abr/include/lib/abr/abr.h", which will migrate to the GBL repo.
get_slot_last_marked_active(abr_ops: &mut dyn Ops) -> AbrResult<SlotIndex>511 pub fn get_slot_last_marked_active(abr_ops: &mut dyn Ops) -> AbrResult<SlotIndex> {
512     let (abr_data, _) = load_metadata(abr_ops)?;
513     Ok(
514         match abr_data.slot_data(SlotIndex::B).priority > abr_data.slot_data(SlotIndex::A).priority
515         {
516             true => SlotIndex::B,
517             false => SlotIndex::A,
518         },
519     )
520 }
521 
522 /// Equivalent to C API `AbrMarkSlotUnbootable()`.
523 ///
524 /// TODO(b/338243123): Detailed documentation is available in Fuchsia upstream header
525 /// "src/firmware/lib/abr/include/lib/abr/abr.h", which will migrate to the GBL repo.
mark_slot_unbootable(abr_ops: &mut dyn Ops, slot_index: SlotIndex) -> AbrResult<()>526 pub fn mark_slot_unbootable(abr_ops: &mut dyn Ops, slot_index: SlotIndex) -> AbrResult<()> {
527     if slot_index == SlotIndex::R {
528         avb_print!(abr_ops, "Invalid argument: Cannot mark slot R as unbootable.\n");
529         return Err(Error::InvalidData);
530     }
531     let (mut abr_data, abr_data_orig) = load_metadata(abr_ops)?;
532     abr_data.slot_data_mut(slot_index).set_slot_unbootable();
533     save_metadata_if_changed(abr_ops, &mut abr_data, &abr_data_orig)
534 }
535 
536 /// Equivalent to C API `AbrMarkSlotSuccessful()`.
537 ///
538 /// TODO(b/338243123): Detailed documentation is available in Fuchsia upstream header
539 /// "src/firmware/lib/abr/include/lib/abr/abr.h", which will migrate to the GBL repo.
mark_slot_successful(abr_ops: &mut dyn Ops, slot_index: SlotIndex) -> AbrResult<()>540 pub fn mark_slot_successful(abr_ops: &mut dyn Ops, slot_index: SlotIndex) -> AbrResult<()> {
541     if slot_index == SlotIndex::R {
542         avb_print!(abr_ops, "Invalid argument: Cannot mark slot R as successful.\n");
543         return Err(Error::InvalidData);
544     }
545     let (mut abr_data, abr_data_orig) = load_metadata(abr_ops)?;
546 
547     if !abr_data.slot_data(slot_index).is_slot_bootable() {
548         avb_print!(abr_ops, "Invalid argument: Cannot mark unbootable slot as successful.\n");
549         return Err(Error::InvalidData);
550     }
551 
552     abr_data.slot_data_mut(slot_index).tries_remaining = 0;
553     abr_data.slot_data_mut(slot_index).successful_boot = 1;
554 
555     // Proactively remove any success mark on the other slot
556     //
557     // This can theoretically be removed since get_boot_slot() clear successful bit on non-boot
558     // slots. However, legacy devices might still be using old versions of ABR implementation that
559     // don't clear it. Therefore, we keep this logic to be safe.
560     //
561     // Context: https://fxbug.dev/42142842, https://crbug.com/fuchsia/64057.
562     let other = slot_index.other();
563     if abr_data.slot_data(other).is_slot_bootable() {
564         abr_data.slot_data_mut(other).tries_remaining = ABR_MAX_TRIES_REMAINING;
565         abr_data.slot_data_mut(other).successful_boot = 0;
566     }
567     save_metadata_if_changed(abr_ops, &mut abr_data, &abr_data_orig)
568 }
569 
570 /// Equivalent to C API `AbrGetSlotInfo()`.
571 ///
572 /// TODO(b/338243123): Detailed documentation is available in Fuchsia upstream header
573 /// "src/firmware/lib/abr/include/lib/abr/abr.h", which will migrate to the GBL repo.
get_slot_info(abr_ops: &mut dyn Ops, slot_index: SlotIndex) -> AbrResult<SlotInfo>574 pub fn get_slot_info(abr_ops: &mut dyn Ops, slot_index: SlotIndex) -> AbrResult<SlotInfo> {
575     let (abr_data, _) = load_metadata(abr_ops)?;
576     Ok(match slot_index {
577         // Assume that R slot is always OK.
578         SlotIndex::R => SlotInfo {
579             state: SlotState::Successful,
580             is_active: abr_data.is_slot_active(SlotIndex::R),
581         },
582         _ => {
583             let slot_data = abr_data.slot_data(slot_index);
584             let state = match slot_data.successful_boot == 1 {
585                 true => SlotState::Successful,
586                 _ if slot_data.is_slot_bootable() => SlotState::Bootable(slot_data.tries_remaining),
587                 _ => SlotState::Unbootable,
588             };
589             SlotInfo { state, is_active: abr_data.is_slot_active(slot_index) }
590         }
591     })
592 }
593 
594 /// Equivalent to C API `AbrSetOneShotRecovery()`.
595 ///
596 /// TODO(b/338243123): Detailed documentation is available in Fuchsia upstream header
597 /// "src/firmware/lib/abr/include/lib/abr/abr.h", which will migrate to the GBL repo.
set_one_shot_recovery(abr_ops: &mut dyn Ops, enable: bool) -> AbrResult<()>598 pub fn set_one_shot_recovery(abr_ops: &mut dyn Ops, enable: bool) -> AbrResult<()> {
599     let (mut abr_data, abr_data_orig) = load_metadata(abr_ops)?;
600     abr_data.set_one_shot_recovery(enable);
601     save_metadata_if_changed(abr_ops, &mut abr_data, &abr_data_orig)
602 }
603 
604 /// Equivalent to C API `AbrSetOneShotBootloader()`.
605 ///
606 /// TODO(b/338243123): Detailed documentation is available in Fuchsia upstream header
607 /// "src/firmware/lib/abr/include/lib/abr/abr.h", which will migrate to the GBL repo.
set_one_shot_bootloader(abr_ops: &mut dyn Ops, enable: bool) -> AbrResult<()>608 pub fn set_one_shot_bootloader(abr_ops: &mut dyn Ops, enable: bool) -> AbrResult<()> {
609     let (mut abr_data, abr_data_orig) = load_metadata(abr_ops)?;
610     abr_data.set_one_shot_bootloader(enable);
611     save_metadata_if_changed(abr_ops, &mut abr_data, &abr_data_orig)
612 }
613 
614 /// Equivalent to C API `AbrGetAndClearOneShotFlags()`.
615 ///
616 /// TODO(b/338243123): Detailed documentation is available in Fuchsia upstream header
617 /// "src/firmware/lib/abr/include/lib/abr/abr.h", which will migrate to the GBL repo.
get_and_clear_one_shot_flag(abr_ops: &mut dyn Ops) -> AbrResult<u8>618 pub fn get_and_clear_one_shot_flag(abr_ops: &mut dyn Ops) -> AbrResult<u8> {
619     let (mut abr_data, abr_data_orig) = load_metadata(abr_ops)?;
620     let res = abr_data.one_shot_flags;
621     abr_data.one_shot_flags = 0;
622     save_metadata_if_changed(abr_ops, &mut abr_data, &abr_data_orig)?;
623     Ok(res)
624 }
625 
626 /// Reverses the bit of a byte.
reverse_byte(b: u8) -> u8627 fn reverse_byte(b: u8) -> u8 {
628     const LOOKUP_TABLE_4BIT_REVERSE: &[u8] =
629         &[0x0, 0x8, 0x4, 0xC, 0x2, 0xA, 0x6, 0xE, 0x1, 0x9, 0x5, 0xD, 0x3, 0xB, 0x7, 0xF];
630     LOOKUP_TABLE_4BIT_REVERSE[(b >> 4) as usize]
631         | (LOOKUP_TABLE_4BIT_REVERSE[(b & 0xf) as usize] << 4)
632 }
633 
634 // Reverses the bits of a u32;
reverse_u32(val: u32) -> u32635 fn reverse_u32(val: u32) -> u32 {
636     let mut bytes = val.to_le_bytes();
637     bytes.iter_mut().for_each(|v| *v = reverse_byte(*v));
638     u32::from_be_bytes(bytes)
639 }
640 
641 // Calculates the crc32 of the given bytes.
crc32(data: &[u8]) -> u32642 fn crc32(data: &[u8]) -> u32 {
643     let mut res: u32 = 0xffffffff;
644     for b in data {
645         res ^= (reverse_byte(*b) as u32) << 24;
646         for _ in 0..8 {
647             if (res & 0x80000000) != 0 {
648                 res = (res << 1) ^ 0x04C11DB7;
649             } else {
650                 res <<= 1;
651             }
652         }
653     }
654     reverse_u32(!res)
655 }
656 
657 #[cfg(test)]
658 mod test {
659     // Testing is currently done against the C interface tests in upstream Fuchsia:
660     // https://fuchsia.googlesource.com/fuchsia/+/96f7268b497f998ffcbeef73425b031bd7f4db65/src/firmware/lib/abr/test/libabr_test.cc
661     // These tests will be ported to here as rust tests in the future.
662 }
663