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