xref: /aosp_15_r20/bootable/libbootloader/gbl/libgbl/src/ops.rs (revision 5225e6b173e52d2efc6bcf950c27374fd72adabc)
1 // Copyright 2023, 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 //! GblOps trait that defines GBL callbacks.
16 
17 pub use crate::image_buffer::ImageBuffer;
18 use crate::{
19     error::Result as GblResult,
20     fuchsia_boot::GblAbrOps,
21     gbl_avb::state::{BootStateColor, KeyValidationStatus},
22     partition::{check_part_unique, read_unique_partition, write_unique_partition, GblDisk},
23 };
24 pub use abr::{set_one_shot_bootloader, set_one_shot_recovery, SlotIndex};
25 use core::{ffi::CStr, fmt::Write, num::NonZeroUsize, ops::DerefMut, result::Result};
26 use gbl_async::block_on;
27 use gbl_storage::SliceMaybeUninit;
28 use libutils::aligned_subslice;
29 
30 // Re-exports of types from other dependencies that appear in the APIs of this library.
31 pub use avb::{
32     CertPermanentAttributes, IoError as AvbIoError, IoResult as AvbIoResult, SHA256_DIGEST_SIZE,
33 };
34 pub use gbl_storage::{BlockIo, Disk, Gpt};
35 use liberror::Error;
36 pub use slots::{Slot, SlotsMetadata};
37 pub use zbi::{ZbiContainer, ZBI_ALIGNMENT_USIZE};
38 
39 use super::device_tree;
40 use super::slots;
41 
42 /// Target Type of OS to boot.
43 #[derive(PartialEq, Debug, Copy, Clone)]
44 pub enum Os {
45     /// Android
46     Android,
47     /// Fuchsia
48     Fuchsia,
49 }
50 
51 /// Contains reboot reasons for instructing GBL to boot to different modes.
52 #[derive(PartialEq, Debug, Copy, Clone)]
53 pub enum RebootReason {
54     /// Normal boot.
55     Normal,
56     /// Bootloader Fastboot mode.
57     Bootloader,
58     /// Userspace Fastboot mode.
59     FastbootD,
60     /// Recovery mode.
61     Recovery,
62 }
63 
64 // https://stackoverflow.com/questions/41081240/idiomatic-callbacks-in-rust
65 // should we use traits for this? or optional/box FnMut?
66 //
67 /* TODO: b/312612203 - needed callbacks:
68 missing:
69 - key management => atx extension in callback =>  atx_ops: ptr::null_mut(), // support optional ATX.
70 */
71 /// Trait that defines callbacks that can be provided to Gbl.
72 pub trait GblOps<'a, 'd> {
73     /// Gets a console for logging messages.
console_out(&mut self) -> Option<&mut dyn Write>74     fn console_out(&mut self) -> Option<&mut dyn Write>;
75 
76     /// The string to use for console line termination with [gbl_println!].
77     ///
78     /// Defaults to "\n" if not overridden.
console_newline(&self) -> &'static str79     fn console_newline(&self) -> &'static str {
80         "\n"
81     }
82 
83     /// This method can be used to implement platform specific mechanism for deciding whether boot
84     /// should abort and enter Fastboot mode.
should_stop_in_fastboot(&mut self) -> Result<bool, Error>85     fn should_stop_in_fastboot(&mut self) -> Result<bool, Error>;
86 
87     /// Reboots the system into the last set boot mode.
88     ///
89     /// The method is not expected to return. Errors should be handled internally by the
90     /// implementation. In most cases, implementation should continue to reset even in the presence
91     /// of errors (users can force power cycle anyway). If there are error cases where reboot
92     /// absolutely can't be taken, implementation should hang and notify platform user in its own
93     /// way.
reboot(&mut self)94     fn reboot(&mut self);
95 
96     /// Reboots into recovery mode
97     ///
98     /// On success, returns a closure that performs the reboot.
reboot_recovery(&mut self) -> Result<impl FnOnce() + '_, Error>99     fn reboot_recovery(&mut self) -> Result<impl FnOnce() + '_, Error> {
100         if self.expected_os_is_fuchsia()? {
101             // TODO(b/363075013): Checks and prioritizes platform specific `set_boot_reason()`.
102             set_one_shot_recovery(&mut GblAbrOps(self), true)?;
103             return Ok(|| self.reboot());
104         }
105         Err(Error::Unsupported)
106     }
107 
108     /// Reboots into bootloader fastboot mode
109     ///
110     /// On success, returns a closure that performs the reboot.
reboot_bootloader(&mut self) -> Result<impl FnOnce() + '_, Error>111     fn reboot_bootloader(&mut self) -> Result<impl FnOnce() + '_, Error> {
112         if self.expected_os_is_fuchsia()? {
113             // TODO(b/363075013): Checks and prioritizes platform specific `set_boot_reason()`.
114             set_one_shot_bootloader(&mut GblAbrOps(self), true)?;
115             return Ok(|| self.reboot());
116         }
117         Err(Error::Unsupported)
118     }
119 
120     /// Returns the list of disk devices on this platform.
121     ///
122     /// Notes that the return slice doesn't capture the life time of `&self`, meaning that the slice
123     /// reference must be producible without borrowing `Self`. This is intended and necessary to
124     /// make disk IO and the rest of GblOps methods independent and parallelizable, which is
125     /// required for features such as parallell fastboot flash, download and other commands. For
126     /// implementation, this typically means that the `GblOps` object should hold a reference of the
127     /// array instead of owning it.
disks( &self, ) -> &'a [GblDisk< Disk<impl BlockIo + 'a, impl DerefMut<Target = [u8]> + 'a>, Gpt<impl DerefMut<Target = [u8]> + 'a>, >]128     fn disks(
129         &self,
130     ) -> &'a [GblDisk<
131         Disk<impl BlockIo + 'a, impl DerefMut<Target = [u8]> + 'a>,
132         Gpt<impl DerefMut<Target = [u8]> + 'a>,
133     >];
134 
135     /// Reads data from a partition.
read_from_partition( &mut self, part: &str, off: u64, out: &mut (impl SliceMaybeUninit + ?Sized), ) -> Result<(), Error>136     async fn read_from_partition(
137         &mut self,
138         part: &str,
139         off: u64,
140         out: &mut (impl SliceMaybeUninit + ?Sized),
141     ) -> Result<(), Error> {
142         read_unique_partition(self.disks(), part, off, out).await
143     }
144 
145     /// Reads data from a partition synchronously.
read_from_partition_sync( &mut self, part: &str, off: u64, out: &mut (impl SliceMaybeUninit + ?Sized), ) -> Result<(), Error>146     fn read_from_partition_sync(
147         &mut self,
148         part: &str,
149         off: u64,
150         out: &mut (impl SliceMaybeUninit + ?Sized),
151     ) -> Result<(), Error> {
152         block_on(self.read_from_partition(part, off, out))
153     }
154 
155     /// Writes data to a partition.
write_to_partition( &mut self, part: &str, off: u64, data: &mut [u8], ) -> Result<(), Error>156     async fn write_to_partition(
157         &mut self,
158         part: &str,
159         off: u64,
160         data: &mut [u8],
161     ) -> Result<(), Error> {
162         write_unique_partition(self.disks(), part, off, data).await
163     }
164 
165     /// Writes data to a partition synchronously.
write_to_partition_sync( &mut self, part: &str, off: u64, data: &mut [u8], ) -> Result<(), Error>166     fn write_to_partition_sync(
167         &mut self,
168         part: &str,
169         off: u64,
170         data: &mut [u8],
171     ) -> Result<(), Error> {
172         block_on(self.write_to_partition(part, off, data))
173     }
174 
175     /// Returns the size of a partiiton. Returns Ok(None) if partition doesn't exist.
partition_size(&mut self, part: &str) -> Result<Option<u64>, Error>176     fn partition_size(&mut self, part: &str) -> Result<Option<u64>, Error> {
177         match check_part_unique(self.disks(), part) {
178             Ok((_, p)) => Ok(Some(p.size()?)),
179             Err(Error::NotFound) => Ok(None),
180             Err(e) => Err(e),
181         }
182     }
183 
184     /// Returns which OS to load, or `None` to try to auto-detect based on disk layout & contents.
expected_os(&mut self) -> Result<Option<Os>, Error>185     fn expected_os(&mut self) -> Result<Option<Os>, Error>;
186 
187     /// Returns if the expected_os is fuchsia
expected_os_is_fuchsia(&mut self) -> Result<bool, Error>188     fn expected_os_is_fuchsia(&mut self) -> Result<bool, Error> {
189         // TODO(b/374776896): Implement auto detection.
190         Ok(self.expected_os()?.map(|v| v == Os::Fuchsia).unwrap_or(false))
191     }
192 
193     /// Adds device specific ZBI items to the given `container`
zircon_add_device_zbi_items( &mut self, container: &mut ZbiContainer<&mut [u8]>, ) -> Result<(), Error>194     fn zircon_add_device_zbi_items(
195         &mut self,
196         container: &mut ZbiContainer<&mut [u8]>,
197     ) -> Result<(), Error>;
198 
199     /// Gets a buffer for staging bootloader file from fastboot.
200     ///
201     /// Fuchsia uses bootloader file for staging SSH key in development flow.
202     ///
203     /// Returns `None` if the platform does not intend to support it.
get_zbi_bootloader_files_buffer(&mut self) -> Option<&mut [u8]>204     fn get_zbi_bootloader_files_buffer(&mut self) -> Option<&mut [u8]>;
205 
206     /// Gets the aligned part of buffer returned by `get_zbi_bootloader_files_buffer()` according to
207     /// ZBI alignment requirement.
get_zbi_bootloader_files_buffer_aligned(&mut self) -> Option<&mut [u8]>208     fn get_zbi_bootloader_files_buffer_aligned(&mut self) -> Option<&mut [u8]> {
209         aligned_subslice(self.get_zbi_bootloader_files_buffer()?, ZBI_ALIGNMENT_USIZE).ok()
210     }
211 
212     // TODO(b/334962570): figure out how to plumb ops-provided hash implementations into
213     // libavb. The tricky part is that libavb hashing APIs are global with no way to directly
214     // correlate the implementation to a particular [GblOps] object, so we'll probably have to
215     // create a [Context] ahead of time and store it globally for the hashing APIs to access.
216     // However this would mean that [Context] must be a standalone object and cannot hold a
217     // reference to [GblOps], which may restrict implementations.
218     // fn new_digest(&self) -> Option<Self::Context>;
219 
220     /// Load and initialize a slot manager and return a cursor over the manager on success.
221     ///
222     /// # Args
223     ///
224     /// * `persist`: A user provided closure for persisting a given slot metadata bytes to storage.
225     /// * `boot_token`: A [slots::BootToken].
load_slot_interface<'b>( &'b mut self, persist: &'b mut dyn FnMut(&mut [u8]) -> Result<(), Error>, boot_token: slots::BootToken, ) -> GblResult<slots::Cursor<'b>>226     fn load_slot_interface<'b>(
227         &'b mut self,
228         persist: &'b mut dyn FnMut(&mut [u8]) -> Result<(), Error>,
229         boot_token: slots::BootToken,
230     ) -> GblResult<slots::Cursor<'b>>;
231 
232     // The following is a selective subset of the interfaces in `avb::Ops` and `avb::CertOps` needed
233     // by GBL's usage of AVB. The rest of the APIs are either not relevant to or are implemented and
234     // managed by GBL APIs.
235 
236     /// Returns if device is in an unlocked state.
237     ///
238     /// The interface has the same requirement as `avb::Ops::read_is_device_unlocked`.
avb_read_is_device_unlocked(&mut self) -> AvbIoResult<bool>239     fn avb_read_is_device_unlocked(&mut self) -> AvbIoResult<bool>;
240 
241     /// Reads the AVB rollback index at the given location
242     ///
243     /// The interface has the same requirement as `avb::Ops::read_rollback_index`.
avb_read_rollback_index(&mut self, rollback_index_location: usize) -> AvbIoResult<u64>244     fn avb_read_rollback_index(&mut self, rollback_index_location: usize) -> AvbIoResult<u64>;
245 
246     /// Writes the AVB rollback index at the given location.
247     ///
248     /// The interface has the same requirement as `avb::Ops::write_rollback_index`.
avb_write_rollback_index( &mut self, rollback_index_location: usize, index: u64, ) -> AvbIoResult<()>249     fn avb_write_rollback_index(
250         &mut self,
251         rollback_index_location: usize,
252         index: u64,
253     ) -> AvbIoResult<()>;
254 
255     /// Reads the AVB persistent value for the given name.
256     ///
257     /// The interface has the same requirement as `avb::Ops::read_persistent_value`.
avb_read_persistent_value(&mut self, name: &CStr, value: &mut [u8]) -> AvbIoResult<usize>258     fn avb_read_persistent_value(&mut self, name: &CStr, value: &mut [u8]) -> AvbIoResult<usize>;
259 
260     /// Writes the AVB persistent value for the given name.
261     ///
262     /// The interface has the same requirement as `avb::Ops::write_persistent_value`.
avb_write_persistent_value(&mut self, name: &CStr, value: &[u8]) -> AvbIoResult<()>263     fn avb_write_persistent_value(&mut self, name: &CStr, value: &[u8]) -> AvbIoResult<()>;
264 
265     /// Erases the AVB persistent value for the given name.
266     ///
267     /// The interface has the same requirement as `avb::Ops::erase_persistent_value`.
avb_erase_persistent_value(&mut self, name: &CStr) -> AvbIoResult<()>268     fn avb_erase_persistent_value(&mut self, name: &CStr) -> AvbIoResult<()>;
269 
270     /// Validate public key used to execute AVB.
271     ///
272     /// Used by `avb::CertOps::read_permanent_attributes_hash` so have similar requirements.
avb_validate_vbmeta_public_key( &self, public_key: &[u8], public_key_metadata: Option<&[u8]>, ) -> AvbIoResult<KeyValidationStatus>273     fn avb_validate_vbmeta_public_key(
274         &self,
275         public_key: &[u8],
276         public_key_metadata: Option<&[u8]>,
277     ) -> AvbIoResult<KeyValidationStatus>;
278 
279     /// Reads AVB certificate extension permanent attributes.
280     ///
281     /// The interface has the same requirement as `avb::CertOps::read_permanent_attributes`.
avb_cert_read_permanent_attributes( &mut self, attributes: &mut CertPermanentAttributes, ) -> AvbIoResult<()>282     fn avb_cert_read_permanent_attributes(
283         &mut self,
284         attributes: &mut CertPermanentAttributes,
285     ) -> AvbIoResult<()>;
286 
287     /// Reads AVB certificate extension permanent attributes hash.
288     ///
289     /// The interface has the same requirement as `avb::CertOps::read_permanent_attributes_hash`.
avb_cert_read_permanent_attributes_hash(&mut self) -> AvbIoResult<[u8; SHA256_DIGEST_SIZE]>290     fn avb_cert_read_permanent_attributes_hash(&mut self) -> AvbIoResult<[u8; SHA256_DIGEST_SIZE]>;
291 
292     /// Handle AVB result.
293     ///
294     /// Set device state (rot / version binding), show UI, etc.
avb_handle_verification_result( &mut self, color: BootStateColor, digest: Option<&CStr>, boot_os_version: Option<&[u8]>, boot_security_patch: Option<&[u8]>, system_os_version: Option<&[u8]>, system_security_patch: Option<&[u8]>, vendor_os_version: Option<&[u8]>, vendor_security_patch: Option<&[u8]>, ) -> AvbIoResult<()>295     fn avb_handle_verification_result(
296         &mut self,
297         color: BootStateColor,
298         digest: Option<&CStr>,
299         boot_os_version: Option<&[u8]>,
300         boot_security_patch: Option<&[u8]>,
301         system_os_version: Option<&[u8]>,
302         system_security_patch: Option<&[u8]>,
303         vendor_os_version: Option<&[u8]>,
304         vendor_security_patch: Option<&[u8]>,
305     ) -> AvbIoResult<()>;
306 
307     /// Get buffer for specific image of requested size.
get_image_buffer( &mut self, image_name: &str, size: NonZeroUsize, ) -> GblResult<ImageBuffer<'d>>308     fn get_image_buffer(
309         &mut self,
310         image_name: &str,
311         size: NonZeroUsize,
312     ) -> GblResult<ImageBuffer<'d>>;
313 
314     /// Returns the custom device tree to use, if any.
315     ///
316     /// If this returns a device tree, it will be used instead of any on-disk contents. This is
317     /// currently needed for Cuttlefish, but should not be used in production devices because this
318     /// data cannot be verified with libavb.
get_custom_device_tree(&mut self) -> Option<&'a [u8]>319     fn get_custom_device_tree(&mut self) -> Option<&'a [u8]>;
320 
321     /// Requests an OS command line to be used alongside the one built by GBL.
322     ///
323     /// The returned command line will be verified and appended on top of the command line
324     /// built by GBL. Refer to the behavior specified for the corresponding UEFI interface:
325     /// https://cs.android.com/android/platform/superproject/main/+/main:bootable/libbootloader/gbl/docs/gbl_os_configuration_protocol.md
fixup_os_commandline<'c>( &mut self, commandline: &CStr, fixup_buffer: &'c mut [u8], ) -> Result<Option<&'c str>, Error>326     fn fixup_os_commandline<'c>(
327         &mut self,
328         commandline: &CStr,
329         fixup_buffer: &'c mut [u8],
330     ) -> Result<Option<&'c str>, Error>;
331 
332     /// Requests an OS bootconfig to be used alongside the one built by GBL.
333     ///
334     /// The returned bootconfig will be verified and appended on top of the bootconfig
335     /// built by GBL. Refer to the behavior specified for the corresponding UEFI interface:
336     /// https://cs.android.com/android/platform/superproject/main/+/main:bootable/libbootloader/gbl/docs/gbl_os_configuration_protocol.md
fixup_bootconfig<'c>( &mut self, bootconfig: &[u8], fixup_buffer: &'c mut [u8], ) -> Result<Option<&'c [u8]>, Error>337     fn fixup_bootconfig<'c>(
338         &mut self,
339         bootconfig: &[u8],
340         fixup_buffer: &'c mut [u8],
341     ) -> Result<Option<&'c [u8]>, Error>;
342 
343     /// Selects from device tree components to build the final one.
344     ///
345     /// Provided components registry must be used to select one device tree (none is not allowed),
346     /// and any number of overlays. Refer to the behavior specified for the corresponding UEFI
347     /// interface:
348     /// https://cs.android.com/android/platform/superproject/main/+/main:bootable/libbootloader/gbl/docs/gbl_os_configuration_protocol.md
select_device_trees( &mut self, components: &mut device_tree::DeviceTreeComponentsRegistry, ) -> Result<(), Error>349     fn select_device_trees(
350         &mut self,
351         components: &mut device_tree::DeviceTreeComponentsRegistry,
352     ) -> Result<(), Error>;
353 
354     /// Provide writtable buffer of the device tree built by GBL.
355     ///
356     /// Modified device tree will be verified and used to boot a device. Refer to the behavior
357     /// specified for the corresponding UEFI interface:
358     /// https://cs.android.com/android/platform/superproject/main/+/main:bootable/libbootloader/gbl/docs/efi_protocols.md
359     /// https://github.com/U-Boot-EFI/EFI_DT_FIXUP_PROTOCOL
fixup_device_tree(&mut self, device_tree: &mut [u8]) -> Result<(), Error>360     fn fixup_device_tree(&mut self, device_tree: &mut [u8]) -> Result<(), Error>;
361 
362     /// Gets platform-specific fastboot variable.
363     ///
364     /// # Args
365     ///
366     /// * `name`: Varaiable name.
367     /// * `args`: Additional arguments.
368     /// * `out`: The output buffer for the value of the variable. Must be a ASCII string.
369     ///
370     /// # Returns
371     ///
372     /// * Returns the number of bytes written in `out` on success.
fastboot_variable<'arg>( &mut self, name: &CStr, args: impl Iterator<Item = &'arg CStr> + Clone, out: &mut [u8], ) -> Result<usize, Error>373     fn fastboot_variable<'arg>(
374         &mut self,
375         name: &CStr,
376         args: impl Iterator<Item = &'arg CStr> + Clone,
377         out: &mut [u8],
378     ) -> Result<usize, Error>;
379 
380     /// Iterates all fastboot variables, arguments and values.
381     ///
382     /// # Args
383     ///
384     /// * `cb`: A closure that takes 1) an array of CStr that contains the variable name followed by
385     ///   any additional arguments and 2) a CStr representing the value.
fastboot_visit_all_variables( &mut self, cb: impl FnMut(&[&CStr], &CStr), ) -> Result<(), Error>386     fn fastboot_visit_all_variables(
387         &mut self,
388         cb: impl FnMut(&[&CStr], &CStr),
389     ) -> Result<(), Error>;
390 
391     /// Returns a [SlotsMetadata] for the platform.
slots_metadata(&mut self) -> Result<SlotsMetadata, Error>392     fn slots_metadata(&mut self) -> Result<SlotsMetadata, Error>;
393 
394     /// Gets the currently booted bootloader slot.
395     ///
396     /// # Returns
397     ///
398     /// * Returns Ok(Some(slot index)) if bootloader is slotted.
399     /// * Returns Ok(Errorr::Unsupported) if bootloader is not slotted.
400     /// * Returns Err() on error.
get_current_slot(&mut self) -> Result<Slot, Error>401     fn get_current_slot(&mut self) -> Result<Slot, Error>;
402 
403     /// Gets the slot for the next A/B decision.
404     ///
405     /// # Args
406     ///
407     /// * `mark_boot_attempt`: Passes true if the caller attempts to boot the returned slot and
408     ///   would like implementation to perform necessary update to the state of slot such as retry
409     ///   counter. Passes false if the caller only wants to query the slot decision and not cause
410     ///   any state change.
get_next_slot(&mut self, _mark_boot_attempt: bool) -> Result<Slot, Error>411     fn get_next_slot(&mut self, _mark_boot_attempt: bool) -> Result<Slot, Error>;
412 
413     /// Sets the active slot for the next A/B decision.
414     ///
415     /// # Args
416     ///
417     /// * `slot`: The numeric index of the slot.
set_active_slot(&mut self, _slot: u8) -> Result<(), Error>418     fn set_active_slot(&mut self, _slot: u8) -> Result<(), Error>;
419 
420     /// Sets the reboot reason for the next reboot.
set_reboot_reason(&mut self, _reason: RebootReason) -> Result<(), Error>421     fn set_reboot_reason(&mut self, _reason: RebootReason) -> Result<(), Error>;
422 
423     /// Gets the reboot reason for this boot.
get_reboot_reason(&mut self) -> Result<RebootReason, Error>424     fn get_reboot_reason(&mut self) -> Result<RebootReason, Error>;
425 }
426 
427 /// Prints with `GblOps::console_out()`.
428 #[macro_export]
429 macro_rules! gbl_print {
430     ( $ops:expr, $( $x:expr ),* $(,)? ) => {
431         {
432             match $ops.console_out() {
433                 Some(v) => write!(v, $($x,)*).unwrap(),
434                 _ => {}
435             }
436         }
437     };
438 }
439 
440 /// Prints the given text plus a newline termination with `GblOps::console_out()`.
441 #[macro_export]
442 macro_rules! gbl_println {
443     ( $ops:expr, $( $x:expr ),* $(,)? ) => {
444         let newline = $ops.console_newline();
445         gbl_print!($ops, $($x,)*);
446         gbl_print!($ops, "{}", newline);
447     };
448 }
449 
450 #[cfg(test)]
451 pub(crate) mod test {
452     use super::*;
453     use crate::error::IntegrationError;
454     use crate::partition::GblDisk;
455     use abr::{get_and_clear_one_shot_bootloader, get_boot_slot};
456     use avb::{CertOps, Ops};
457     use avb_test::TestOps as AvbTestOps;
458     use core::{
459         fmt::Write,
460         ops::{Deref, DerefMut},
461         str::from_utf8,
462     };
463     use fastboot::{snprintf, FormattedBytes};
464     use gbl_async::block_on;
465     use gbl_storage::{new_gpt_max, Disk, GptMax, RamBlockIo};
466     use std::{
467         collections::{HashMap, LinkedList},
468         ffi::CString,
469     };
470     use zbi::{ZbiFlags, ZbiType};
471 
472     /// Type of [GblDisk] in tests.
473     pub(crate) type TestGblDisk = GblDisk<Disk<RamBlockIo<Vec<u8>>, Vec<u8>>, GptMax>;
474 
475     /// Backing storage for [FakeGblOps].
476     ///
477     /// This needs to be a separate object because [GblOps] has designed its lifetimes to borrow
478     /// the [GblDisk] objects rather than own it, so that they can outlive the ops
479     /// object when necessary.
480     ///
481     /// # Example usage
482     /// ```
483     /// let storage = FakeGblOpsStorage::default();
484     /// storage.add_gpt_device(&gpt_disk_contents);
485     /// storage.add_raw_device(c"raw", &raw_disk_contents);
486     ///
487     /// let fake_ops = FakeGblOps(&storage);
488     /// ```
489     #[derive(Default)]
490     pub(crate) struct FakeGblOpsStorage(pub Vec<TestGblDisk>);
491 
492     impl FakeGblOpsStorage {
493         /// Adds a GPT disk.
add_gpt_device(&mut self, data: impl AsRef<[u8]>)494         pub(crate) fn add_gpt_device(&mut self, data: impl AsRef<[u8]>) {
495             // For test GPT images, all block sizes are 512.
496             self.0.push(TestGblDisk::new_gpt(
497                 Disk::new_ram_alloc(512, 512, data.as_ref().to_vec()).unwrap(),
498                 new_gpt_max(),
499             ));
500             let _ = block_on(self.0.last().unwrap().sync_gpt());
501         }
502 
503         /// Adds a raw partition disk.
add_raw_device(&mut self, name: &CStr, data: impl AsRef<[u8]>)504         pub(crate) fn add_raw_device(&mut self, name: &CStr, data: impl AsRef<[u8]>) {
505             // For raw partition, use block_size=alignment=1 for simplicity.
506             TestGblDisk::new_raw(Disk::new_ram_alloc(1, 1, data.as_ref().to_vec()).unwrap(), name)
507                 .and_then(|v| Ok(self.0.push(v)))
508                 .unwrap()
509         }
510     }
511 
512     impl Deref for FakeGblOpsStorage {
513         type Target = [TestGblDisk];
514 
deref(&self) -> &Self::Target515         fn deref(&self) -> &Self::Target {
516             &self.0[..]
517         }
518     }
519 
520     /// Fake [GblOps] implementation for testing.
521     #[derive(Default)]
522     pub(crate) struct FakeGblOps<'a, 'd> {
523         /// Partition data to expose.
524         pub partitions: &'a [TestGblDisk],
525 
526         /// Test fixture for [avb::Ops] and [avb::CertOps], provided by libavb.
527         ///
528         /// We don't use all the available functionality here, in particular the backing storage
529         /// is provided by `partitions` and our custom storage APIs rather than the [AvbTestOps]
530         /// fake storage, so that we can more accurately test our storage implementation.
531         pub avb_ops: AvbTestOps<'static>,
532 
533         /// Value returned by `should_stop_in_fastboot`.
534         pub stop_in_fastboot: Option<Result<bool, Error>>,
535 
536         /// For returned by `fn get_zbi_bootloader_files_buffer()`
537         pub zbi_bootloader_files_buffer: Vec<u8>,
538 
539         /// For checking that `Self::reboot` is called.
540         pub rebooted: bool,
541 
542         /// For return by `Self::expected_os()`
543         pub os: Option<Os>,
544 
545         /// For return by `Self::avb_validate_vbmeta_public_key`
546         pub avb_key_validation_status: Option<AvbIoResult<KeyValidationStatus>>,
547 
548         /// For return by `Self::get_image_buffer()`
549         pub image_buffers: HashMap<String, LinkedList<ImageBuffer<'d>>>,
550     }
551 
552     /// Print `console_out` output, which can be useful for debugging.
553     impl<'a, 'd> Write for FakeGblOps<'a, 'd> {
write_str(&mut self, s: &str) -> Result<(), std::fmt::Error>554         fn write_str(&mut self, s: &str) -> Result<(), std::fmt::Error> {
555             Ok(print!("{s}"))
556         }
557     }
558 
559     impl<'a, 'd> FakeGblOps<'a, 'd> {
560         /// For now we've just hardcoded the `zircon_add_device_zbi_items()` callback to add a
561         /// single commandline ZBI item with these contents; if necessary we can generalize this
562         /// later and allow tests to configure the ZBI modifications.
563         pub const ADDED_ZBI_COMMANDLINE_CONTENTS: &'static [u8] = b"test_zbi_item";
564         pub const TEST_BOOTLOADER_FILE_1: &'static [u8] = b"\x06test_1foo";
565         pub const TEST_BOOTLOADER_FILE_2: &'static [u8] = b"\x06test_2bar";
566         pub const GBL_TEST_VAR: &'static str = "gbl-test-var";
567         pub const GBL_TEST_VAR_VAL: &'static str = "gbl-test-var-val";
568 
new(partitions: &'a [TestGblDisk]) -> Self569         pub fn new(partitions: &'a [TestGblDisk]) -> Self {
570             let mut res = Self {
571                 partitions,
572                 zbi_bootloader_files_buffer: vec![0u8; 32 * 1024],
573                 ..Default::default()
574             };
575             let mut container =
576                 ZbiContainer::new(res.get_zbi_bootloader_files_buffer_aligned().unwrap()).unwrap();
577             for ele in [Self::TEST_BOOTLOADER_FILE_1, Self::TEST_BOOTLOADER_FILE_2] {
578                 container
579                     .create_entry_with_payload(ZbiType::BootloaderFile, 0, ZbiFlags::default(), ele)
580                     .unwrap();
581             }
582 
583             res
584         }
585 
586         /// Copies an entire partition contents into a vector.
587         ///
588         /// This is a common enough operation in tests that it's worth a small wrapper to provide
589         /// a more convenient API using [Vec].
590         ///
591         /// Panics if the given partition name doesn't exist.
copy_partition(&mut self, name: &str) -> Vec<u8>592         pub fn copy_partition(&mut self, name: &str) -> Vec<u8> {
593             let mut contents =
594                 vec![0u8; self.partition_size(name).unwrap().unwrap().try_into().unwrap()];
595             assert!(self.read_from_partition_sync(name, 0, &mut contents[..]).is_ok());
596             contents
597         }
598     }
599 
600     impl<'a, 'd> GblOps<'a, 'd> for FakeGblOps<'a, 'd> {
console_out(&mut self) -> Option<&mut dyn Write>601         fn console_out(&mut self) -> Option<&mut dyn Write> {
602             Some(self)
603         }
604 
should_stop_in_fastboot(&mut self) -> Result<bool, Error>605         fn should_stop_in_fastboot(&mut self) -> Result<bool, Error> {
606             self.stop_in_fastboot.unwrap_or(Ok(false))
607         }
608 
reboot(&mut self)609         fn reboot(&mut self) {
610             self.rebooted = true;
611         }
612 
disks( &self, ) -> &'a [GblDisk< Disk<impl BlockIo + 'a, impl DerefMut<Target = [u8]> + 'a>, Gpt<impl DerefMut<Target = [u8]> + 'a>, >]613         fn disks(
614             &self,
615         ) -> &'a [GblDisk<
616             Disk<impl BlockIo + 'a, impl DerefMut<Target = [u8]> + 'a>,
617             Gpt<impl DerefMut<Target = [u8]> + 'a>,
618         >] {
619             self.partitions
620         }
621 
expected_os(&mut self) -> Result<Option<Os>, Error>622         fn expected_os(&mut self) -> Result<Option<Os>, Error> {
623             Ok(self.os)
624         }
625 
zircon_add_device_zbi_items( &mut self, container: &mut ZbiContainer<&mut [u8]>, ) -> Result<(), Error>626         fn zircon_add_device_zbi_items(
627             &mut self,
628             container: &mut ZbiContainer<&mut [u8]>,
629         ) -> Result<(), Error> {
630             container
631                 .create_entry_with_payload(
632                     ZbiType::CmdLine,
633                     0,
634                     ZbiFlags::default(),
635                     Self::ADDED_ZBI_COMMANDLINE_CONTENTS,
636                 )
637                 .unwrap();
638             Ok(())
639         }
640 
get_zbi_bootloader_files_buffer(&mut self) -> Option<&mut [u8]>641         fn get_zbi_bootloader_files_buffer(&mut self) -> Option<&mut [u8]> {
642             Some(self.zbi_bootloader_files_buffer.as_mut_slice())
643         }
644 
load_slot_interface<'b>( &'b mut self, _: &'b mut dyn FnMut(&mut [u8]) -> Result<(), Error>, _: slots::BootToken, ) -> GblResult<slots::Cursor<'b>>645         fn load_slot_interface<'b>(
646             &'b mut self,
647             _: &'b mut dyn FnMut(&mut [u8]) -> Result<(), Error>,
648             _: slots::BootToken,
649         ) -> GblResult<slots::Cursor<'b>> {
650             unimplemented!();
651         }
652 
avb_read_is_device_unlocked(&mut self) -> AvbIoResult<bool>653         fn avb_read_is_device_unlocked(&mut self) -> AvbIoResult<bool> {
654             self.avb_ops.read_is_device_unlocked()
655         }
656 
avb_read_rollback_index(&mut self, rollback_index_location: usize) -> AvbIoResult<u64>657         fn avb_read_rollback_index(&mut self, rollback_index_location: usize) -> AvbIoResult<u64> {
658             self.avb_ops.read_rollback_index(rollback_index_location)
659         }
660 
avb_write_rollback_index( &mut self, rollback_index_location: usize, index: u64, ) -> AvbIoResult<()>661         fn avb_write_rollback_index(
662             &mut self,
663             rollback_index_location: usize,
664             index: u64,
665         ) -> AvbIoResult<()> {
666             self.avb_ops.write_rollback_index(rollback_index_location, index)
667         }
668 
avb_validate_vbmeta_public_key( &self, _public_key: &[u8], _public_key_metadata: Option<&[u8]>, ) -> AvbIoResult<KeyValidationStatus>669         fn avb_validate_vbmeta_public_key(
670             &self,
671             _public_key: &[u8],
672             _public_key_metadata: Option<&[u8]>,
673         ) -> AvbIoResult<KeyValidationStatus> {
674             self.avb_key_validation_status.clone().unwrap()
675         }
676 
avb_cert_read_permanent_attributes( &mut self, attributes: &mut CertPermanentAttributes, ) -> AvbIoResult<()>677         fn avb_cert_read_permanent_attributes(
678             &mut self,
679             attributes: &mut CertPermanentAttributes,
680         ) -> AvbIoResult<()> {
681             self.avb_ops.read_permanent_attributes(attributes)
682         }
683 
avb_cert_read_permanent_attributes_hash( &mut self, ) -> AvbIoResult<[u8; SHA256_DIGEST_SIZE]>684         fn avb_cert_read_permanent_attributes_hash(
685             &mut self,
686         ) -> AvbIoResult<[u8; SHA256_DIGEST_SIZE]> {
687             self.avb_ops.read_permanent_attributes_hash()
688         }
689 
avb_read_persistent_value( &mut self, name: &CStr, value: &mut [u8], ) -> AvbIoResult<usize>690         fn avb_read_persistent_value(
691             &mut self,
692             name: &CStr,
693             value: &mut [u8],
694         ) -> AvbIoResult<usize> {
695             self.avb_ops.read_persistent_value(name, value)
696         }
697 
avb_write_persistent_value(&mut self, name: &CStr, value: &[u8]) -> AvbIoResult<()>698         fn avb_write_persistent_value(&mut self, name: &CStr, value: &[u8]) -> AvbIoResult<()> {
699             self.avb_ops.write_persistent_value(name, value)
700         }
701 
avb_erase_persistent_value(&mut self, name: &CStr) -> AvbIoResult<()>702         fn avb_erase_persistent_value(&mut self, name: &CStr) -> AvbIoResult<()> {
703             self.avb_ops.erase_persistent_value(name)
704         }
705 
avb_handle_verification_result( &mut self, _color: BootStateColor, _digest: Option<&CStr>, _boot_os_version: Option<&[u8]>, _boot_security_patch: Option<&[u8]>, _system_os_version: Option<&[u8]>, _system_security_patch: Option<&[u8]>, _vendor_os_version: Option<&[u8]>, _vendor_security_patch: Option<&[u8]>, ) -> AvbIoResult<()>706         fn avb_handle_verification_result(
707             &mut self,
708             _color: BootStateColor,
709             _digest: Option<&CStr>,
710             _boot_os_version: Option<&[u8]>,
711             _boot_security_patch: Option<&[u8]>,
712             _system_os_version: Option<&[u8]>,
713             _system_security_patch: Option<&[u8]>,
714             _vendor_os_version: Option<&[u8]>,
715             _vendor_security_patch: Option<&[u8]>,
716         ) -> AvbIoResult<()> {
717             unimplemented!();
718         }
719 
get_image_buffer( &mut self, image_name: &str, _size: NonZeroUsize, ) -> GblResult<ImageBuffer<'d>>720         fn get_image_buffer(
721             &mut self,
722             image_name: &str,
723             _size: NonZeroUsize,
724         ) -> GblResult<ImageBuffer<'d>> {
725             if let Some(buf_list) = self.image_buffers.get_mut(image_name) {
726                 if let Some(buf) = buf_list.pop_front() {
727                     return Ok(buf);
728                 };
729             };
730 
731             gbl_println!(self, "FakeGblOps.get_image_buffer({image_name}) no buffer for the image");
732             Err(IntegrationError::UnificationError(Error::Other(Some(
733                 "No buffer provided. Add sufficient buffers to FakeGblOps.image_buffers",
734             ))))
735         }
736 
get_custom_device_tree(&mut self) -> Option<&'static [u8]>737         fn get_custom_device_tree(&mut self) -> Option<&'static [u8]> {
738             None
739         }
740 
fixup_os_commandline<'c>( &mut self, _commandline: &CStr, _fixup_buffer: &'c mut [u8], ) -> Result<Option<&'c str>, Error>741         fn fixup_os_commandline<'c>(
742             &mut self,
743             _commandline: &CStr,
744             _fixup_buffer: &'c mut [u8],
745         ) -> Result<Option<&'c str>, Error> {
746             unimplemented!();
747         }
748 
fixup_bootconfig<'c>( &mut self, _bootconfig: &[u8], _fixup_buffer: &'c mut [u8], ) -> Result<Option<&'c [u8]>, Error>749         fn fixup_bootconfig<'c>(
750             &mut self,
751             _bootconfig: &[u8],
752             _fixup_buffer: &'c mut [u8],
753         ) -> Result<Option<&'c [u8]>, Error> {
754             unimplemented!();
755         }
756 
fixup_device_tree(&mut self, _: &mut [u8]) -> Result<(), Error>757         fn fixup_device_tree(&mut self, _: &mut [u8]) -> Result<(), Error> {
758             unimplemented!();
759         }
760 
select_device_trees( &mut self, _: &mut device_tree::DeviceTreeComponentsRegistry, ) -> Result<(), Error>761         fn select_device_trees(
762             &mut self,
763             _: &mut device_tree::DeviceTreeComponentsRegistry,
764         ) -> Result<(), Error> {
765             unimplemented!();
766         }
767 
fastboot_variable<'arg>( &mut self, name: &CStr, mut args: impl Iterator<Item = &'arg CStr> + Clone, out: &mut [u8], ) -> Result<usize, Error>768         fn fastboot_variable<'arg>(
769             &mut self,
770             name: &CStr,
771             mut args: impl Iterator<Item = &'arg CStr> + Clone,
772             out: &mut [u8],
773         ) -> Result<usize, Error> {
774             match name.to_str()? {
775                 Self::GBL_TEST_VAR => {
776                     Ok(snprintf!(out, "{}:{:?}", Self::GBL_TEST_VAR_VAL, args.next()).len())
777                 }
778                 _ => Err(Error::NotFound),
779             }
780         }
781 
fastboot_visit_all_variables( &mut self, mut cb: impl FnMut(&[&CStr], &CStr), ) -> Result<(), Error>782         fn fastboot_visit_all_variables(
783             &mut self,
784             mut cb: impl FnMut(&[&CStr], &CStr),
785         ) -> Result<(), Error> {
786             cb(
787                 &[CString::new(Self::GBL_TEST_VAR).unwrap().as_c_str(), c"1"],
788                 CString::new(format!("{}:1", Self::GBL_TEST_VAR_VAL)).unwrap().as_c_str(),
789             );
790             cb(
791                 &[CString::new(Self::GBL_TEST_VAR).unwrap().as_c_str(), c"2"],
792                 CString::new(format!("{}:2", Self::GBL_TEST_VAR_VAL)).unwrap().as_c_str(),
793             );
794             Ok(())
795         }
796 
slots_metadata(&mut self) -> Result<SlotsMetadata, Error>797         fn slots_metadata(&mut self) -> Result<SlotsMetadata, Error> {
798             unimplemented!();
799         }
800 
get_current_slot(&mut self) -> Result<Slot, Error>801         fn get_current_slot(&mut self) -> Result<Slot, Error> {
802             unimplemented!()
803         }
804 
get_next_slot(&mut self, _: bool) -> Result<Slot, Error>805         fn get_next_slot(&mut self, _: bool) -> Result<Slot, Error> {
806             unimplemented!()
807         }
808 
set_active_slot(&mut self, _: u8) -> Result<(), Error>809         fn set_active_slot(&mut self, _: u8) -> Result<(), Error> {
810             unimplemented!()
811         }
812 
set_reboot_reason(&mut self, _: RebootReason) -> Result<(), Error>813         fn set_reboot_reason(&mut self, _: RebootReason) -> Result<(), Error> {
814             unimplemented!()
815         }
816 
get_reboot_reason(&mut self) -> Result<RebootReason, Error>817         fn get_reboot_reason(&mut self) -> Result<RebootReason, Error> {
818             unimplemented!()
819         }
820     }
821 
822     #[test]
test_fuchsia_reboot_bootloader()823     fn test_fuchsia_reboot_bootloader() {
824         let mut storage = FakeGblOpsStorage::default();
825         storage.add_raw_device(c"durable_boot", [0x00u8; 4 * 1024]);
826         let mut gbl_ops = FakeGblOps::new(&storage);
827         gbl_ops.os = Some(Os::Fuchsia);
828         (gbl_ops.reboot_bootloader().unwrap())();
829         assert!(gbl_ops.rebooted);
830         assert_eq!(get_and_clear_one_shot_bootloader(&mut GblAbrOps(&mut gbl_ops)), Ok(true));
831     }
832 
833     #[test]
test_non_fuchsia_reboot_bootloader()834     fn test_non_fuchsia_reboot_bootloader() {
835         let mut storage = FakeGblOpsStorage::default();
836         storage.add_raw_device(c"durable_boot", [0x00u8; 4 * 1024]);
837         let mut gbl_ops = FakeGblOps::new(&storage);
838         gbl_ops.os = Some(Os::Android);
839         assert!(gbl_ops.reboot_bootloader().is_err_and(|e| e == Error::Unsupported));
840         assert_eq!(get_and_clear_one_shot_bootloader(&mut GblAbrOps(&mut gbl_ops)), Ok(false));
841     }
842 
843     #[test]
test_fuchsia_reboot_recovery()844     fn test_fuchsia_reboot_recovery() {
845         let mut storage = FakeGblOpsStorage::default();
846         storage.add_raw_device(c"durable_boot", [0x00u8; 4 * 1024]);
847         let mut gbl_ops = FakeGblOps::new(&storage);
848         gbl_ops.os = Some(Os::Fuchsia);
849         (gbl_ops.reboot_recovery().unwrap())();
850         assert!(gbl_ops.rebooted);
851         // One shot recovery is set.
852         assert_eq!(get_boot_slot(&mut GblAbrOps(&mut gbl_ops), true), (SlotIndex::R, false));
853         assert_eq!(get_boot_slot(&mut GblAbrOps(&mut gbl_ops), true), (SlotIndex::A, false));
854     }
855 
856     #[test]
test_non_fuchsia_reboot_recovery()857     fn test_non_fuchsia_reboot_recovery() {
858         let mut storage = FakeGblOpsStorage::default();
859         storage.add_raw_device(c"durable_boot", [0x00u8; 4 * 1024]);
860         let mut gbl_ops = FakeGblOps::new(&storage);
861         gbl_ops.os = Some(Os::Android);
862         assert!(gbl_ops.reboot_recovery().is_err_and(|e| e == Error::Unsupported));
863         // One shot recovery is not set.
864         assert_eq!(get_boot_slot(&mut GblAbrOps(&mut gbl_ops), true), (SlotIndex::A, false));
865     }
866 }
867