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 //! This file provides C interface wrappers of libabr APIs.
16 
17 #![cfg_attr(not(test), no_std)]
18 
19 use abr::{
20     get_and_clear_one_shot_flag, get_boot_slot, get_slot_info, get_slot_last_marked_active,
21     mark_slot_active, mark_slot_successful, mark_slot_unbootable, set_one_shot_bootloader,
22     set_one_shot_recovery, AbrResult, Error, Ops, SlotIndex, SlotInfo as AbrSlotInfo, SlotState,
23 };
24 use core::{
25     ffi::{c_char, c_uint, c_void},
26     fmt::Write,
27 };
28 
29 pub mod utils;
30 
31 pub const ABR_RESULT_OK: c_uint = 0;
32 pub const ABR_RESULT_ERR_IO: c_uint = 1;
33 pub const ABR_RESULT_ERR_INVALID_DATA: c_uint = 2;
34 pub const ABR_RESULT_ERR_UNSUPPORTED_VERSION: c_uint = 3;
35 
36 // ABR system dependencies.
37 //
38 // These correspond to the definitions in Fuchsia upstream header
39 // "src/firmware/lib/abr/include/lib/abr/sysdeps.h", which will eventually migrate over.
40 extern "C" {
41     /// Prints out a NULL-terminated string.
AbrPrint(message: *const c_char)42     pub fn AbrPrint(message: *const c_char);
43 
44     /// Aborts the program or reboots the device if |abort| is not implemented.
AbrAbort()45     pub fn AbrAbort();
46 }
47 
48 /// A helper to print an ASCII character via `AbrPrint()`.
abr_print_ascii_char(ch: u8)49 fn abr_print_ascii_char(ch: u8) {
50     let s = [ch, 0];
51     // SAFETY:
52     // * `s` is a valid buffer
53     // * `s` is for input only and will not be retained by the function.
54     unsafe { AbrPrint(s.as_ptr() as _) }
55 }
56 
57 /// A helper structure that implements formatted write using `AbrPrint()`.
58 struct AbrPrintSysdeps {}
59 
60 impl Write for AbrPrintSysdeps {
write_str(&mut self, s: &str) -> core::fmt::Result61     fn write_str(&mut self, s: &str) -> core::fmt::Result {
62         if s.is_ascii() {
63             s.as_bytes().iter().for_each(|v| abr_print_ascii_char(*v));
64         }
65         Ok(())
66     }
67 }
68 
69 /// A panic handler is needed when building as a static library. We simply call into
70 /// the AbrAbort() system dependency.
71 #[cfg(not(test))]
72 #[panic_handler]
panic(panic: &core::panic::PanicInfo<'_>) -> !73 fn panic(panic: &core::panic::PanicInfo<'_>) -> ! {
74     write!(AbrPrintSysdeps {}, "libabr panics! {}", panic).unwrap();
75     // SAFETY: Call to external C function. The function simply aborts/reboots the system.
76     unsafe { AbrAbort() };
77     unreachable!()
78 }
79 
80 /// This corresponds to the `AbrOps` C definition in Fuchsia upstream header
81 /// "src/firmware/lib/abr/include/lib/abr/ops.h", which will eventually migrate over.
82 ///
83 /// typedef struct AbrOps {
84 ///     void* context;
85 ///     bool (*read_abr_metadata)(void* context, size_t size, uint8_t* buffer);
86 ///     bool (*write_abr_metadata)(void* context, const uint8_t* buffer, size_t size);
87 /// } AbrOps;
88 #[repr(C)]
89 #[derive(Debug, Copy, Clone)]
90 pub struct AbrOps {
91     pub context: *mut c_void,
92     pub read_abr_metadata:
93         Option<unsafe extern "C" fn(context: *mut c_void, size: usize, buffer: *mut u8) -> bool>,
94     pub write_abr_metadata:
95         Option<unsafe extern "C" fn(context: *mut c_void, buffer: *const u8, size: usize) -> bool>,
96 }
97 
98 /// `AbrOpsSafe` wraps a reference to `AbrOps` and is created by an unsafe constructor that
99 /// establishes necessary safety invariants on `AbrOps`.
100 struct AbrOpsSafe<'a> {
101     ops: &'a AbrOps,
102     log: AbrPrintSysdeps,
103 }
104 
105 impl<'a> AbrOpsSafe<'a> {
106     /// Creates a new instance from a reference to `AbrOps`.
107     ///
108     /// # Safety
109     ///
110     /// * Caller must make sure that `ops.context` is either not used, or points to a valid and
111     ///   correct type of value needed by `ops.read_abr_metadata` and `ops.write_abr_metadata`.
new(ops: &'a AbrOps) -> Self112     unsafe fn new(ops: &'a AbrOps) -> Self {
113         Self { ops, log: AbrPrintSysdeps {} }
114     }
115 }
116 
117 type AbrSlotIndex = c_uint;
118 
119 impl Ops for AbrOpsSafe<'_> {
read_abr_metadata(&mut self, out: &mut [u8]) -> Result<(), Option<&'static str>>120     fn read_abr_metadata(&mut self, out: &mut [u8]) -> Result<(), Option<&'static str>> {
121         let read_abr_metadata =
122             self.ops.read_abr_metadata.ok_or(Some("Missing read_abr_metadata() method"))?;
123         // SAFETY:
124         // * By safety requirement of `AbrOpsSafe::new()`, `self.ops.context` is either unused, or
125         //   a valid pointer to a correct type of object used by `self.ops.read_abr_metadata`.
126         // * `out` is a valid buffer
127         // * `out` is for reading data only and will not be retained by the function.
128         match unsafe { read_abr_metadata(self.ops.context, out.len(), out.as_mut_ptr() as _) } {
129             false => Err(Some("read_abr_metadata() failed")),
130             _ => Ok(()),
131         }
132     }
133 
write_abr_metadata(&mut self, data: &mut [u8]) -> Result<(), Option<&'static str>>134     fn write_abr_metadata(&mut self, data: &mut [u8]) -> Result<(), Option<&'static str>> {
135         let write_abr_metadata =
136             self.ops.write_abr_metadata.ok_or(Some("Missing write_abr_metadata() method"))?;
137         // SAFETY:
138         // * By safety requirement of `AbrOpsSafe::new()`, `self.ops.context` is either unused, or
139         //   a valid pointer to a correct type of object used by `self.ops.write_abr_metadata`.
140         // * `data` is a valid buffer.
141         // * `data` is for input only and will not be retained by the function.
142         match unsafe { write_abr_metadata(self.ops.context, data.as_ptr() as _, data.len()) } {
143             false => Err(Some("write_abr_metadata() failed")),
144             _ => Ok(()),
145         }
146     }
147 
console(&mut self) -> Option<&mut dyn core::fmt::Write>148     fn console(&mut self) -> Option<&mut dyn core::fmt::Write> {
149         Some(&mut self.log)
150     }
151 }
152 
153 /// A helper that extracts the return value and maps the result to an integer A/B/R result code.
unpack_result<T: Into<O>, O>(res: AbrResult<T>, val: &mut O) -> c_uint154 fn unpack_result<T: Into<O>, O>(res: AbrResult<T>, val: &mut O) -> c_uint {
155     match res {
156         Err(e) => match e {
157             Error::BadMagic | Error::BadChecksum | Error::InvalidData => {
158                 ABR_RESULT_ERR_INVALID_DATA
159             }
160             Error::UnsupportedVersion => ABR_RESULT_ERR_UNSUPPORTED_VERSION,
161             Error::OpsError(_) => ABR_RESULT_ERR_IO,
162         },
163         Ok(v) => {
164             *val = v.into();
165             ABR_RESULT_OK
166         }
167     }
168 }
169 
170 /// C interface wrapper of `abr::get_boot_slot()`
171 ///
172 /// # Safety
173 ///
174 /// * Caller must make sure to pass a valid pointer for `abr_ops`.
175 /// * Caller must make sure that `ops.context` is either not used, or points to a valid and correct
176 ///   type of value needed by `ops.read_abr_metadata` and `ops.write_abr_metadata`.
177 /// * Caller must make sure to pass either a NULL or valid pointer for `is_slot_marked_successful`.
178 #[no_mangle]
179 #[allow(non_snake_case)]
AbrGetBootSlot( abr_ops: *const AbrOps, update_metadata: bool, is_slot_marked_successful: *mut bool, ) -> AbrSlotIndex180 pub unsafe extern "C" fn AbrGetBootSlot(
181     abr_ops: *const AbrOps,
182     update_metadata: bool,
183     is_slot_marked_successful: *mut bool,
184 ) -> AbrSlotIndex {
185     let mut abr_ops = AbrOpsSafe::new(abr_ops.as_ref().unwrap());
186     let (slot_index, successful) = get_boot_slot(&mut abr_ops, update_metadata);
187     match is_slot_marked_successful.as_mut() {
188         Some(v) => *v = successful,
189         _ => {}
190     };
191     slot_index.into()
192 }
193 
194 // NULL terminated strings for slot suffixes.
195 const SLOT_A_SUFFIX: &[u8] = b"_a\0";
196 const SLOT_B_SUFFIX: &[u8] = b"_b\0";
197 const SLOT_R_SUFFIX: &[u8] = b"_r\0";
198 const SLOT_SUFFIX_INVALID: &[u8] = b"\0";
199 
200 /// C interface for getting slot suffix.
201 #[no_mangle]
202 #[allow(non_snake_case)]
AbrGetSlotSuffix(slot_index: AbrSlotIndex) -> *const c_char203 pub extern "C" fn AbrGetSlotSuffix(slot_index: AbrSlotIndex) -> *const c_char {
204     match slot_index.try_into() {
205         Ok(SlotIndex::A) => &SLOT_A_SUFFIX,
206         Ok(SlotIndex::B) => &SLOT_B_SUFFIX,
207         Ok(SlotIndex::R) => &SLOT_R_SUFFIX,
208         Err(_) => &SLOT_SUFFIX_INVALID,
209     }
210     .as_ptr() as _
211 }
212 
213 /// C interface wrapper of `abr::mark_slot_active()`
214 ///
215 /// # Safety
216 ///
217 /// * Caller must make sure to pass a valid pointer for `abr_ops`.
218 /// * Caller must make sure that `ops.context` is either not used, or points to a valid and correct
219 ///   type of value needed by `ops.read_abr_metadata` and `ops.write_abr_metadata`.
220 #[no_mangle]
221 #[allow(non_snake_case)]
AbrMarkSlotActive( abr_ops: *const AbrOps, slot_index: AbrSlotIndex, ) -> c_uint222 pub unsafe extern "C" fn AbrMarkSlotActive(
223     abr_ops: *const AbrOps,
224     slot_index: AbrSlotIndex,
225 ) -> c_uint {
226     let slot_index = match slot_index.try_into() {
227         Ok(v) => v,
228         Err(_) => return ABR_RESULT_ERR_INVALID_DATA,
229     };
230     unpack_result(
231         mark_slot_active(&mut AbrOpsSafe::new(abr_ops.as_ref().unwrap()), slot_index),
232         &mut (),
233     )
234 }
235 
236 /// C interface wrapper of `abr::get_slot_last_marked_active()`
237 ///
238 /// # Safety
239 ///
240 /// * Caller must make sure to pass a valid pointer for `abr_ops` and `out_slot`.
241 /// * Caller must make sure that `ops.context` is either not used, or points to a valid and correct
242 ///   type of value needed by `ops.read_abr_metadata` and `ops.write_abr_metadata`.
243 #[no_mangle]
244 #[allow(non_snake_case)]
AbrGetSlotLastMarkedActive( abr_ops: *const AbrOps, out_slot: *mut AbrSlotIndex, ) -> c_uint245 pub unsafe extern "C" fn AbrGetSlotLastMarkedActive(
246     abr_ops: *const AbrOps,
247     out_slot: *mut AbrSlotIndex,
248 ) -> c_uint {
249     unpack_result(
250         get_slot_last_marked_active(&mut AbrOpsSafe::new(abr_ops.as_ref().unwrap())),
251         out_slot.as_mut().unwrap(),
252     )
253 }
254 
255 /// C interface wrapper of `abr::mark_slot_unbootable()`
256 ///
257 /// # Safety
258 ///
259 /// * Caller must make sure to pass a valid pointer for `abr_ops`.
260 /// * Caller must make sure that `ops.context` is either not used, or points to a valid and correct
261 ///   type of value needed by `ops.read_abr_metadata` and `ops.write_abr_metadata`.
262 #[no_mangle]
263 #[allow(non_snake_case)]
AbrMarkSlotUnbootable( abr_ops: *const AbrOps, slot_index: AbrSlotIndex, ) -> c_uint264 pub unsafe extern "C" fn AbrMarkSlotUnbootable(
265     abr_ops: *const AbrOps,
266     slot_index: AbrSlotIndex,
267 ) -> c_uint {
268     let slot_index = match slot_index.try_into() {
269         Ok(v) => v,
270         Err(_) => return ABR_RESULT_ERR_INVALID_DATA,
271     };
272     unpack_result(
273         mark_slot_unbootable(&mut AbrOpsSafe::new(abr_ops.as_ref().unwrap()), slot_index),
274         &mut (),
275     )
276 }
277 
278 /// C interface wrapper of `abr::mark_slot_successful()`
279 ///
280 /// # Safety
281 ///
282 /// * Caller must make sure to pass a valid pointer for `abr_ops`.
283 /// * Caller must make sure that `ops.context` is either not used, or points to a valid and correct
284 ///   type of value needed by `ops.read_abr_metadata` and `ops.write_abr_metadata`.
285 #[no_mangle]
286 #[allow(non_snake_case)]
AbrMarkSlotSuccessful( abr_ops: *const AbrOps, slot_index: AbrSlotIndex, ) -> c_uint287 pub unsafe extern "C" fn AbrMarkSlotSuccessful(
288     abr_ops: *const AbrOps,
289     slot_index: AbrSlotIndex,
290 ) -> c_uint {
291     let slot_index = match slot_index.try_into() {
292         Ok(v) => v,
293         Err(_) => return ABR_RESULT_ERR_INVALID_DATA,
294     };
295     unpack_result(
296         mark_slot_successful(&mut AbrOpsSafe::new(abr_ops.as_ref().unwrap()), slot_index),
297         &mut (),
298     )
299 }
300 
301 /// `SlotInfo` contains the current state of a A/B/R slot.
302 ///
303 /// TODO(b/338243123): Detailed documentation is available in Fuchsia upstream header
304 /// "src/firmware/lib/abr/include/lib/abr/abr.h", which will migrate to the GBL repo.
305 #[repr(C)]
306 #[derive(Debug, Copy, Clone)]
307 pub struct SlotInfo {
308     /// Whether the slot is expected to be bootable.
309     pub is_bootable: bool,
310     /// Whether the slot is the highest priority A/B slot.
311     pub is_active: bool,
312     /// Whether the slot is currently marked successful.
313     pub is_marked_successful: bool,
314     /// If not marked successful, this represents the number of attempts left for booting this slot.
315     pub num_tries_remaining: u8,
316 }
317 
318 impl From<AbrSlotInfo> for SlotInfo {
from(val: abr::SlotInfo) -> Self319     fn from(val: abr::SlotInfo) -> Self {
320         let is_marked_successful = matches!(val.state, SlotState::Successful);
321         let num_tries_remaining = match val.state {
322             SlotState::Bootable(v) => v,
323             _ => 0,
324         };
325         Self {
326             is_bootable: is_marked_successful || num_tries_remaining > 0,
327             is_active: val.is_active,
328             is_marked_successful,
329             num_tries_remaining,
330         }
331     }
332 }
333 
334 /// C interface wrapper of `abr::get_slot_info()`
335 ///
336 /// # Safety
337 ///
338 /// * Caller must make sure to pass a valid pointer for `abr_ops` and 'info'.
339 /// * Caller must make sure that `ops.context` is either not used, or points to a valid and correct
340 ///   type of value needed by `ops.read_abr_metadata` and `ops.write_abr_metadata`.
341 #[no_mangle]
342 #[allow(non_snake_case)]
AbrGetSlotInfo( abr_ops: *const AbrOps, slot_index: AbrSlotIndex, info: *mut SlotInfo, ) -> c_uint343 pub unsafe extern "C" fn AbrGetSlotInfo(
344     abr_ops: *const AbrOps,
345     slot_index: AbrSlotIndex,
346     info: *mut SlotInfo,
347 ) -> c_uint {
348     let slot_index = match slot_index.try_into() {
349         Ok(v) => v,
350         Err(_) => return ABR_RESULT_ERR_INVALID_DATA,
351     };
352     unpack_result(
353         get_slot_info(&mut AbrOpsSafe::new(abr_ops.as_ref().unwrap()), slot_index)
354             .map(|v| SlotInfo::from(v)),
355         info.as_mut().unwrap(),
356     )
357 }
358 
359 /// C interface wrapper of `abr::set_one_shot_recovery()`
360 ///
361 /// # Safety
362 ///
363 /// * Caller must make sure to pass a valid pointer for `abr_ops`.
364 /// * Caller must make sure that `ops.context` is either not used, or points to a valid and correct
365 ///   type of value needed by `ops.read_abr_metadata` and `ops.write_abr_metadata`.
366 #[no_mangle]
367 #[allow(non_snake_case)]
AbrSetOneShotRecovery(abr_ops: *const AbrOps, enable: bool) -> c_uint368 pub unsafe extern "C" fn AbrSetOneShotRecovery(abr_ops: *const AbrOps, enable: bool) -> c_uint {
369     unpack_result(
370         set_one_shot_recovery(&mut AbrOpsSafe::new(abr_ops.as_ref().unwrap()), enable),
371         &mut (),
372     )
373 }
374 
375 /// C interface wrapper of `abr::set_one_shot_bootloader()`
376 ///
377 /// # Safety
378 ///
379 /// * Caller must make sure to pass a valid pointer for `abr_ops`.
380 /// * Caller must make sure that `ops.context` is either not used, or points to a valid and correct
381 ///   type of value needed by `ops.read_abr_metadata` and `ops.write_abr_metadata`.
382 #[no_mangle]
383 #[allow(non_snake_case)]
AbrSetOneShotBootloader(abr_ops: *const AbrOps, enable: bool) -> c_uint384 pub unsafe extern "C" fn AbrSetOneShotBootloader(abr_ops: *const AbrOps, enable: bool) -> c_uint {
385     unpack_result(
386         set_one_shot_bootloader(&mut AbrOpsSafe::new(abr_ops.as_ref().unwrap()), enable),
387         &mut (),
388     )
389 }
390 
391 /// Gets and clears the one shot flag.
392 ///
393 /// # Safety
394 ///
395 /// * Caller must make sure to pass a valid pointer for `abr_ops` and `flags`.
396 /// * Caller must make sure that `ops.context` is either not used, or points to a valid and correct
397 ///   type of value needed by `ops.read_abr_metadata` and `ops.write_abr_metadata`.
398 #[no_mangle]
399 #[allow(non_snake_case)]
AbrGetAndClearOneShotFlags( abr_ops: *const AbrOps, flags: *mut c_uint, ) -> c_uint400 pub unsafe extern "C" fn AbrGetAndClearOneShotFlags(
401     abr_ops: *const AbrOps,
402     flags: *mut c_uint,
403 ) -> c_uint {
404     unpack_result(
405         get_and_clear_one_shot_flag(&mut AbrOpsSafe::new(abr_ops.as_ref().unwrap())),
406         flags.as_mut().unwrap(),
407     )
408 }
409 
410 // Needed because of no-std environment in static lib build.
411 #[cfg(not(test))]
412 #[no_mangle]
rust_eh_personality()413 pub extern "C" fn rust_eh_personality() {}
414