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