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