• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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         {
445             let newline = $ops.console_newline();
446             gbl_print!($ops, $($x,)*);
447             gbl_print!($ops, "{}", newline);
448         }
449     };
450 }
451 
452 /// Inherits everything from `ops` but override a few such as read boot_a from
453 /// bootimg_buffer, avb_write_rollback_index(), slot operation etc
454 pub(crate) struct RambootOps<'a, T> {
455     pub(crate) ops: &'a mut T,
456     pub(crate) preloaded_partitions: &'a [(&'a str, &'a [u8])],
457 }
458 
459 impl<'a, 'd, T: GblOps<'a, 'd>> GblOps<'a, 'd> for RambootOps<'_, T> {
console_out(&mut self) -> Option<&mut dyn Write>460     fn console_out(&mut self) -> Option<&mut dyn Write> {
461         self.ops.console_out()
462     }
463 
should_stop_in_fastboot(&mut self) -> Result<bool, Error>464     fn should_stop_in_fastboot(&mut self) -> Result<bool, Error> {
465         self.ops.should_stop_in_fastboot()
466     }
467 
reboot(&mut self)468     fn reboot(&mut self) {
469         self.ops.reboot()
470     }
471 
disks( &self, ) -> &'a [GblDisk< Disk<impl BlockIo + 'a, impl DerefMut<Target = [u8]> + 'a>, Gpt<impl DerefMut<Target = [u8]> + 'a>, >]472     fn disks(
473         &self,
474     ) -> &'a [GblDisk<
475         Disk<impl BlockIo + 'a, impl DerefMut<Target = [u8]> + 'a>,
476         Gpt<impl DerefMut<Target = [u8]> + 'a>,
477     >] {
478         self.ops.disks()
479     }
480 
expected_os(&mut self) -> Result<Option<Os>, Error>481     fn expected_os(&mut self) -> Result<Option<Os>, Error> {
482         self.ops.expected_os()
483     }
484 
zircon_add_device_zbi_items( &mut self, container: &mut ZbiContainer<&mut [u8]>, ) -> Result<(), Error>485     fn zircon_add_device_zbi_items(
486         &mut self,
487         container: &mut ZbiContainer<&mut [u8]>,
488     ) -> Result<(), Error> {
489         self.ops.zircon_add_device_zbi_items(container)
490     }
491 
get_zbi_bootloader_files_buffer(&mut self) -> Option<&mut [u8]>492     fn get_zbi_bootloader_files_buffer(&mut self) -> Option<&mut [u8]> {
493         self.ops.get_zbi_bootloader_files_buffer()
494     }
495 
load_slot_interface<'c>( &'c mut self, _fnmut: &'c mut dyn FnMut(&mut [u8]) -> Result<(), Error>, _boot_token: crate::BootToken, ) -> GblResult<slots::Cursor<'c>>496     fn load_slot_interface<'c>(
497         &'c mut self,
498         _fnmut: &'c mut dyn FnMut(&mut [u8]) -> Result<(), Error>,
499         _boot_token: crate::BootToken,
500     ) -> GblResult<slots::Cursor<'c>> {
501         self.ops.load_slot_interface(_fnmut, _boot_token)
502     }
503 
avb_read_is_device_unlocked(&mut self) -> AvbIoResult<bool>504     fn avb_read_is_device_unlocked(&mut self) -> AvbIoResult<bool> {
505         self.ops.avb_read_is_device_unlocked()
506     }
507 
avb_read_rollback_index(&mut self, _rollback_index_location: usize) -> AvbIoResult<u64>508     fn avb_read_rollback_index(&mut self, _rollback_index_location: usize) -> AvbIoResult<u64> {
509         self.ops.avb_read_rollback_index(_rollback_index_location)
510     }
511 
avb_write_rollback_index(&mut self, _: usize, _: u64) -> AvbIoResult<()>512     fn avb_write_rollback_index(&mut self, _: usize, _: u64) -> AvbIoResult<()> {
513         // We don't want to persist AVB related data such as updating antirollback indices.
514         Ok(())
515     }
516 
avb_read_persistent_value(&mut self, name: &CStr, value: &mut [u8]) -> AvbIoResult<usize>517     fn avb_read_persistent_value(&mut self, name: &CStr, value: &mut [u8]) -> AvbIoResult<usize> {
518         self.ops.avb_read_persistent_value(name, value)
519     }
520 
avb_write_persistent_value(&mut self, _: &CStr, _: &[u8]) -> AvbIoResult<()>521     fn avb_write_persistent_value(&mut self, _: &CStr, _: &[u8]) -> AvbIoResult<()> {
522         // We don't want to persist AVB related data such as updating current VBH.
523         Ok(())
524     }
525 
avb_erase_persistent_value(&mut self, _: &CStr) -> AvbIoResult<()>526     fn avb_erase_persistent_value(&mut self, _: &CStr) -> AvbIoResult<()> {
527         // We don't want to persist AVB related data such as updating current VBH.
528         Ok(())
529     }
530 
avb_cert_read_permanent_attributes( &mut self, attributes: &mut CertPermanentAttributes, ) -> AvbIoResult<()>531     fn avb_cert_read_permanent_attributes(
532         &mut self,
533         attributes: &mut CertPermanentAttributes,
534     ) -> AvbIoResult<()> {
535         self.ops.avb_cert_read_permanent_attributes(attributes)
536     }
537 
avb_cert_read_permanent_attributes_hash(&mut self) -> AvbIoResult<[u8; SHA256_DIGEST_SIZE]>538     fn avb_cert_read_permanent_attributes_hash(&mut self) -> AvbIoResult<[u8; SHA256_DIGEST_SIZE]> {
539         self.ops.avb_cert_read_permanent_attributes_hash()
540     }
541 
get_image_buffer( &mut self, image_name: &str, size: NonZeroUsize, ) -> GblResult<ImageBuffer<'d>>542     fn get_image_buffer(
543         &mut self,
544         image_name: &str,
545         size: NonZeroUsize,
546     ) -> GblResult<ImageBuffer<'d>> {
547         self.ops.get_image_buffer(image_name, size)
548     }
549 
get_custom_device_tree(&mut self) -> Option<&'a [u8]>550     fn get_custom_device_tree(&mut self) -> Option<&'a [u8]> {
551         self.ops.get_custom_device_tree()
552     }
553 
fixup_os_commandline<'c>( &mut self, commandline: &CStr, fixup_buffer: &'c mut [u8], ) -> Result<Option<&'c str>, Error>554     fn fixup_os_commandline<'c>(
555         &mut self,
556         commandline: &CStr,
557         fixup_buffer: &'c mut [u8],
558     ) -> Result<Option<&'c str>, Error> {
559         self.ops.fixup_os_commandline(commandline, fixup_buffer)
560     }
561 
fixup_bootconfig<'c>( &mut self, bootconfig: &[u8], fixup_buffer: &'c mut [u8], ) -> Result<Option<&'c [u8]>, Error>562     fn fixup_bootconfig<'c>(
563         &mut self,
564         bootconfig: &[u8],
565         fixup_buffer: &'c mut [u8],
566     ) -> Result<Option<&'c [u8]>, Error> {
567         self.ops.fixup_bootconfig(bootconfig, fixup_buffer)
568     }
569 
fixup_device_tree(&mut self, device_tree: &mut [u8]) -> Result<(), Error>570     fn fixup_device_tree(&mut self, device_tree: &mut [u8]) -> Result<(), Error> {
571         self.ops.fixup_device_tree(device_tree)
572     }
573 
select_device_trees( &mut self, components_registry: &mut device_tree::DeviceTreeComponentsRegistry, ) -> Result<(), Error>574     fn select_device_trees(
575         &mut self,
576         components_registry: &mut device_tree::DeviceTreeComponentsRegistry,
577     ) -> Result<(), Error> {
578         self.ops.select_device_trees(components_registry)
579     }
580 
read_from_partition_sync( &mut self, part: &str, off: u64, out: &mut (impl SliceMaybeUninit + ?Sized), ) -> Result<(), Error>581     fn read_from_partition_sync(
582         &mut self,
583         part: &str,
584         off: u64,
585         out: &mut (impl SliceMaybeUninit + ?Sized),
586     ) -> Result<(), Error> {
587         match self.preloaded_partitions.iter().find(|(name, _)| *name == part) {
588             Some((_, data)) => {
589                 let buf = data
590                     .get(off.try_into()?..)
591                     .and_then(|v| v.get(..out.len()))
592                     .ok_or(Error::OutOfRange)?;
593                 Ok(out.clone_from_slice(buf))
594             }
595             _ => self.ops.read_from_partition_sync(part, off, out),
596         }
597     }
598 
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<()>599     fn avb_handle_verification_result(
600         &mut self,
601         color: BootStateColor,
602         digest: Option<&CStr>,
603         boot_os_version: Option<&[u8]>,
604         boot_security_patch: Option<&[u8]>,
605         system_os_version: Option<&[u8]>,
606         system_security_patch: Option<&[u8]>,
607         vendor_os_version: Option<&[u8]>,
608         vendor_security_patch: Option<&[u8]>,
609     ) -> AvbIoResult<()> {
610         self.ops.avb_handle_verification_result(
611             color,
612             digest,
613             boot_os_version,
614             boot_security_patch,
615             system_os_version,
616             system_security_patch,
617             vendor_os_version,
618             vendor_security_patch,
619         )
620     }
621 
avb_validate_vbmeta_public_key( &self, public_key: &[u8], public_key_metadata: Option<&[u8]>, ) -> AvbIoResult<KeyValidationStatus>622     fn avb_validate_vbmeta_public_key(
623         &self,
624         public_key: &[u8],
625         public_key_metadata: Option<&[u8]>,
626     ) -> AvbIoResult<KeyValidationStatus> {
627         self.ops.avb_validate_vbmeta_public_key(public_key, public_key_metadata)
628     }
629 
slots_metadata(&mut self) -> Result<SlotsMetadata, Error>630     fn slots_metadata(&mut self) -> Result<SlotsMetadata, Error> {
631         // Ramboot is not suppose to call this interface.
632         unreachable!()
633     }
634 
get_current_slot(&mut self) -> Result<Slot, Error>635     fn get_current_slot(&mut self) -> Result<Slot, Error> {
636         // Ramboot is slotless
637         Err(Error::Unsupported)
638     }
639 
get_next_slot(&mut self, _: bool) -> Result<Slot, Error>640     fn get_next_slot(&mut self, _: bool) -> Result<Slot, Error> {
641         // Ramboot is not suppose to call this interface.
642         unreachable!()
643     }
644 
set_active_slot(&mut self, _: u8) -> Result<(), Error>645     fn set_active_slot(&mut self, _: u8) -> Result<(), Error> {
646         // Ramboot is not suppose to call this interface.
647         unreachable!()
648     }
649 
set_reboot_reason(&mut self, _: RebootReason) -> Result<(), Error>650     fn set_reboot_reason(&mut self, _: RebootReason) -> Result<(), Error> {
651         // Ramboot is not suppose to call this interface.
652         unreachable!()
653     }
654 
get_reboot_reason(&mut self) -> Result<RebootReason, Error>655     fn get_reboot_reason(&mut self) -> Result<RebootReason, Error> {
656         // Assumes that ramboot use normal boot mode. But we might consider supporting recovery
657         // if there is a usecase.
658         Ok(RebootReason::Normal)
659     }
660 
fastboot_variable<'arg>( &mut self, _: &CStr, _: impl Iterator<Item = &'arg CStr> + Clone, _: &mut [u8], ) -> Result<usize, Error>661     fn fastboot_variable<'arg>(
662         &mut self,
663         _: &CStr,
664         _: impl Iterator<Item = &'arg CStr> + Clone,
665         _: &mut [u8],
666     ) -> Result<usize, Error> {
667         // Ramboot should not need this.
668         unreachable!();
669     }
670 
fastboot_visit_all_variables( &mut self, _: impl FnMut(&[&CStr], &CStr), ) -> Result<(), Error>671     fn fastboot_visit_all_variables(
672         &mut self,
673         _: impl FnMut(&[&CStr], &CStr),
674     ) -> Result<(), Error> {
675         // Ramboot should not need this.
676         unreachable!();
677     }
678 }
679 
680 #[cfg(test)]
681 pub(crate) mod test {
682     use super::*;
683     use crate::error::IntegrationError;
684     use crate::partition::GblDisk;
685     use abr::{get_and_clear_one_shot_bootloader, get_boot_slot};
686     use avb::{CertOps, Ops};
687     use avb_test::TestOps as AvbTestOps;
688     use core::{
689         fmt::Write,
690         ops::{Deref, DerefMut},
691     };
692     use fdt::Fdt;
693     use gbl_async::block_on;
694     use gbl_storage::{new_gpt_max, Disk, GptMax, RamBlockIo};
695     use libutils::snprintf;
696     use std::{
697         collections::{HashMap, LinkedList},
698         ffi::CString,
699     };
700     use zbi::{ZbiFlags, ZbiType};
701 
702     /// Type of [GblDisk] in tests.
703     pub(crate) type TestGblDisk = GblDisk<Disk<RamBlockIo<Vec<u8>>, Vec<u8>>, GptMax>;
704 
705     /// Backing storage for [FakeGblOps].
706     ///
707     /// This needs to be a separate object because [GblOps] has designed its lifetimes to borrow
708     /// the [GblDisk] objects rather than own it, so that they can outlive the ops
709     /// object when necessary.
710     ///
711     /// # Example usage
712     /// ```
713     /// let storage = FakeGblOpsStorage::default();
714     /// storage.add_gpt_device(&gpt_disk_contents);
715     /// storage.add_raw_device(c"raw", &raw_disk_contents);
716     ///
717     /// let fake_ops = FakeGblOps(&storage);
718     /// ```
719     #[derive(Default)]
720     pub(crate) struct FakeGblOpsStorage(pub Vec<TestGblDisk>);
721 
722     impl FakeGblOpsStorage {
723         /// Adds a GPT disk.
add_gpt_device(&mut self, data: impl AsRef<[u8]>)724         pub(crate) fn add_gpt_device(&mut self, data: impl AsRef<[u8]>) {
725             // For test GPT images, all block sizes are 512.
726             self.0.push(TestGblDisk::new_gpt(
727                 Disk::new_ram_alloc(512, 512, data.as_ref().to_vec()).unwrap(),
728                 new_gpt_max(),
729             ));
730             let _ = block_on(self.0.last().unwrap().sync_gpt());
731         }
732 
733         /// Adds a raw partition disk.
add_raw_device(&mut self, name: &CStr, data: impl AsRef<[u8]>)734         pub(crate) fn add_raw_device(&mut self, name: &CStr, data: impl AsRef<[u8]>) {
735             // For raw partition, use block_size=alignment=1 for simplicity.
736             TestGblDisk::new_raw(Disk::new_ram_alloc(1, 1, data.as_ref().to_vec()).unwrap(), name)
737                 .and_then(|v| Ok(self.0.push(v)))
738                 .unwrap()
739         }
740     }
741 
742     impl Deref for FakeGblOpsStorage {
743         type Target = [TestGblDisk];
744 
deref(&self) -> &Self::Target745         fn deref(&self) -> &Self::Target {
746             &self.0[..]
747         }
748     }
749 
750     /// Fake [GblOps] implementation for testing.
751     #[derive(Default)]
752     pub(crate) struct FakeGblOps<'a, 'd> {
753         /// Partition data to expose.
754         pub partitions: &'a [TestGblDisk],
755 
756         /// Test fixture for [avb::Ops] and [avb::CertOps], provided by libavb.
757         ///
758         /// We don't use all the available functionality here, in particular the backing storage
759         /// is provided by `partitions` and our custom storage APIs rather than the [AvbTestOps]
760         /// fake storage, so that we can more accurately test our storage implementation.
761         pub avb_ops: AvbTestOps<'static>,
762 
763         /// Value returned by `should_stop_in_fastboot`.
764         pub stop_in_fastboot: Option<Result<bool, Error>>,
765 
766         /// For returned by `fn get_zbi_bootloader_files_buffer()`
767         pub zbi_bootloader_files_buffer: Vec<u8>,
768 
769         /// For checking that `Self::reboot` is called.
770         pub rebooted: bool,
771 
772         /// For return by `Self::expected_os()`
773         pub os: Option<Os>,
774 
775         /// For return by `Self::avb_validate_vbmeta_public_key`
776         pub avb_key_validation_status: Option<AvbIoResult<KeyValidationStatus>>,
777 
778         /// For return by `Self::get_image_buffer()`
779         pub image_buffers: HashMap<String, LinkedList<ImageBuffer<'d>>>,
780 
781         /// Custom device tree.
782         pub custom_device_tree: Option<&'a [u8]>,
783 
784         /// Custom handler for `avb_handle_verification_result`
785         pub avb_handle_verification_result: Option<
786             &'a mut dyn FnMut(
787                 BootStateColor,
788                 Option<&CStr>,
789                 Option<&[u8]>,
790                 Option<&[u8]>,
791                 Option<&[u8]>,
792                 Option<&[u8]>,
793                 Option<&[u8]>,
794                 Option<&[u8]>,
795             ) -> AvbIoResult<()>,
796         >,
797 
798         /// For returned by `get_current_slot`
799         //
800         // We wrap it in an `Option` so that if a test exercises code paths that use it but did not
801         // set it, it can panic with "unwrap()" which will give a clearer error and location
802         // message than a vague error such as `Error::Unimplemented`.
803         pub current_slot: Option<Result<Slot, Error>>,
804 
805         /// For returned by `get_next_slot`
806         pub next_slot: Option<Result<Slot, Error>>,
807 
808         /// Number of times `get_next_slot()` is called with `mark_boot_attempt` set to true.
809         pub mark_boot_attempt_called: usize,
810 
811         /// slot index last set active by `set_active()`,
812         pub last_set_active_slot: Option<u8>,
813 
814         /// For returned by `get_reboot_reason()`
815         pub reboot_reason: Option<Result<RebootReason, Error>>,
816     }
817 
818     /// Print `console_out` output, which can be useful for debugging.
819     impl<'a, 'd> Write for FakeGblOps<'a, 'd> {
write_str(&mut self, s: &str) -> Result<(), std::fmt::Error>820         fn write_str(&mut self, s: &str) -> Result<(), std::fmt::Error> {
821             Ok(print!("{s}"))
822         }
823     }
824 
825     impl<'a, 'd> FakeGblOps<'a, 'd> {
826         /// For now we've just hardcoded the `zircon_add_device_zbi_items()` callback to add a
827         /// single commandline ZBI item with these contents; if necessary we can generalize this
828         /// later and allow tests to configure the ZBI modifications.
829         pub const ADDED_ZBI_COMMANDLINE_CONTENTS: &'static [u8] = b"test_zbi_item";
830         pub const TEST_BOOTLOADER_FILE_1: &'static [u8] = b"\x06test_1foo";
831         pub const TEST_BOOTLOADER_FILE_2: &'static [u8] = b"\x06test_2bar";
832         pub const GBL_TEST_VAR: &'static str = "gbl-test-var";
833         pub const GBL_TEST_VAR_VAL: &'static str = "gbl-test-var-val";
834         pub const GBL_TEST_BOOTCONFIG: &'static str = "arg1=val1\x0aarg2=val2\x0a";
835 
new(partitions: &'a [TestGblDisk]) -> Self836         pub fn new(partitions: &'a [TestGblDisk]) -> Self {
837             let mut res = Self {
838                 partitions,
839                 zbi_bootloader_files_buffer: vec![0u8; 32 * 1024],
840                 ..Default::default()
841             };
842             let mut container =
843                 ZbiContainer::new(res.get_zbi_bootloader_files_buffer_aligned().unwrap()).unwrap();
844             for ele in [Self::TEST_BOOTLOADER_FILE_1, Self::TEST_BOOTLOADER_FILE_2] {
845                 container
846                     .create_entry_with_payload(ZbiType::BootloaderFile, 0, ZbiFlags::default(), ele)
847                     .unwrap();
848             }
849 
850             res
851         }
852 
853         /// Copies an entire partition contents into a vector.
854         ///
855         /// This is a common enough operation in tests that it's worth a small wrapper to provide
856         /// a more convenient API using [Vec].
857         ///
858         /// Panics if the given partition name doesn't exist.
copy_partition(&mut self, name: &str) -> Vec<u8>859         pub fn copy_partition(&mut self, name: &str) -> Vec<u8> {
860             let mut contents =
861                 vec![0u8; self.partition_size(name).unwrap().unwrap().try_into().unwrap()];
862             assert!(self.read_from_partition_sync(name, 0, &mut contents[..]).is_ok());
863             contents
864         }
865     }
866 
867     impl<'a, 'd> GblOps<'a, 'd> for FakeGblOps<'a, 'd> {
console_out(&mut self) -> Option<&mut dyn Write>868         fn console_out(&mut self) -> Option<&mut dyn Write> {
869             Some(self)
870         }
871 
should_stop_in_fastboot(&mut self) -> Result<bool, Error>872         fn should_stop_in_fastboot(&mut self) -> Result<bool, Error> {
873             self.stop_in_fastboot.unwrap_or(Ok(false))
874         }
875 
reboot(&mut self)876         fn reboot(&mut self) {
877             self.rebooted = true;
878         }
879 
disks( &self, ) -> &'a [GblDisk< Disk<impl BlockIo + 'a, impl DerefMut<Target = [u8]> + 'a>, Gpt<impl DerefMut<Target = [u8]> + 'a>, >]880         fn disks(
881             &self,
882         ) -> &'a [GblDisk<
883             Disk<impl BlockIo + 'a, impl DerefMut<Target = [u8]> + 'a>,
884             Gpt<impl DerefMut<Target = [u8]> + 'a>,
885         >] {
886             self.partitions
887         }
888 
expected_os(&mut self) -> Result<Option<Os>, Error>889         fn expected_os(&mut self) -> Result<Option<Os>, Error> {
890             Ok(self.os)
891         }
892 
zircon_add_device_zbi_items( &mut self, container: &mut ZbiContainer<&mut [u8]>, ) -> Result<(), Error>893         fn zircon_add_device_zbi_items(
894             &mut self,
895             container: &mut ZbiContainer<&mut [u8]>,
896         ) -> Result<(), Error> {
897             container
898                 .create_entry_with_payload(
899                     ZbiType::CmdLine,
900                     0,
901                     ZbiFlags::default(),
902                     Self::ADDED_ZBI_COMMANDLINE_CONTENTS,
903                 )
904                 .unwrap();
905             Ok(())
906         }
907 
get_zbi_bootloader_files_buffer(&mut self) -> Option<&mut [u8]>908         fn get_zbi_bootloader_files_buffer(&mut self) -> Option<&mut [u8]> {
909             Some(self.zbi_bootloader_files_buffer.as_mut_slice())
910         }
911 
load_slot_interface<'b>( &'b mut self, _: &'b mut dyn FnMut(&mut [u8]) -> Result<(), Error>, _: slots::BootToken, ) -> GblResult<slots::Cursor<'b>>912         fn load_slot_interface<'b>(
913             &'b mut self,
914             _: &'b mut dyn FnMut(&mut [u8]) -> Result<(), Error>,
915             _: slots::BootToken,
916         ) -> GblResult<slots::Cursor<'b>> {
917             unimplemented!();
918         }
919 
avb_read_is_device_unlocked(&mut self) -> AvbIoResult<bool>920         fn avb_read_is_device_unlocked(&mut self) -> AvbIoResult<bool> {
921             self.avb_ops.read_is_device_unlocked()
922         }
923 
avb_read_rollback_index(&mut self, rollback_index_location: usize) -> AvbIoResult<u64>924         fn avb_read_rollback_index(&mut self, rollback_index_location: usize) -> AvbIoResult<u64> {
925             self.avb_ops.read_rollback_index(rollback_index_location)
926         }
927 
avb_write_rollback_index( &mut self, rollback_index_location: usize, index: u64, ) -> AvbIoResult<()>928         fn avb_write_rollback_index(
929             &mut self,
930             rollback_index_location: usize,
931             index: u64,
932         ) -> AvbIoResult<()> {
933             self.avb_ops.write_rollback_index(rollback_index_location, index)
934         }
935 
avb_validate_vbmeta_public_key( &self, _public_key: &[u8], _public_key_metadata: Option<&[u8]>, ) -> AvbIoResult<KeyValidationStatus>936         fn avb_validate_vbmeta_public_key(
937             &self,
938             _public_key: &[u8],
939             _public_key_metadata: Option<&[u8]>,
940         ) -> AvbIoResult<KeyValidationStatus> {
941             self.avb_key_validation_status.clone().unwrap()
942         }
943 
avb_cert_read_permanent_attributes( &mut self, attributes: &mut CertPermanentAttributes, ) -> AvbIoResult<()>944         fn avb_cert_read_permanent_attributes(
945             &mut self,
946             attributes: &mut CertPermanentAttributes,
947         ) -> AvbIoResult<()> {
948             self.avb_ops.read_permanent_attributes(attributes)
949         }
950 
avb_cert_read_permanent_attributes_hash( &mut self, ) -> AvbIoResult<[u8; SHA256_DIGEST_SIZE]>951         fn avb_cert_read_permanent_attributes_hash(
952             &mut self,
953         ) -> AvbIoResult<[u8; SHA256_DIGEST_SIZE]> {
954             self.avb_ops.read_permanent_attributes_hash()
955         }
956 
avb_read_persistent_value( &mut self, name: &CStr, value: &mut [u8], ) -> AvbIoResult<usize>957         fn avb_read_persistent_value(
958             &mut self,
959             name: &CStr,
960             value: &mut [u8],
961         ) -> AvbIoResult<usize> {
962             self.avb_ops.read_persistent_value(name, value)
963         }
964 
avb_write_persistent_value(&mut self, name: &CStr, value: &[u8]) -> AvbIoResult<()>965         fn avb_write_persistent_value(&mut self, name: &CStr, value: &[u8]) -> AvbIoResult<()> {
966             self.avb_ops.write_persistent_value(name, value)
967         }
968 
avb_erase_persistent_value(&mut self, name: &CStr) -> AvbIoResult<()>969         fn avb_erase_persistent_value(&mut self, name: &CStr) -> AvbIoResult<()> {
970             self.avb_ops.erase_persistent_value(name)
971         }
972 
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<()>973         fn avb_handle_verification_result(
974             &mut self,
975             color: BootStateColor,
976             digest: Option<&CStr>,
977             boot_os_version: Option<&[u8]>,
978             boot_security_patch: Option<&[u8]>,
979             system_os_version: Option<&[u8]>,
980             system_security_patch: Option<&[u8]>,
981             vendor_os_version: Option<&[u8]>,
982             vendor_security_patch: Option<&[u8]>,
983         ) -> AvbIoResult<()> {
984             match self.avb_handle_verification_result.as_mut() {
985                 Some(f) => (*f)(
986                     color,
987                     digest,
988                     boot_os_version,
989                     boot_security_patch,
990                     system_os_version,
991                     system_security_patch,
992                     vendor_os_version,
993                     vendor_security_patch,
994                 ),
995                 _ => Ok(()),
996             }
997         }
998 
get_image_buffer( &mut self, image_name: &str, _size: NonZeroUsize, ) -> GblResult<ImageBuffer<'d>>999         fn get_image_buffer(
1000             &mut self,
1001             image_name: &str,
1002             _size: NonZeroUsize,
1003         ) -> GblResult<ImageBuffer<'d>> {
1004             if let Some(buf_list) = self.image_buffers.get_mut(image_name) {
1005                 if let Some(buf) = buf_list.pop_front() {
1006                     return Ok(buf);
1007                 };
1008             };
1009 
1010             gbl_println!(self, "FakeGblOps.get_image_buffer({image_name}) no buffer for the image");
1011             Err(IntegrationError::UnificationError(Error::Other(Some(
1012                 "No buffer provided. Add sufficient buffers to FakeGblOps.image_buffers",
1013             ))))
1014         }
1015 
get_custom_device_tree(&mut self) -> Option<&'a [u8]>1016         fn get_custom_device_tree(&mut self) -> Option<&'a [u8]> {
1017             self.custom_device_tree
1018         }
1019 
fixup_os_commandline<'c>( &mut self, _commandline: &CStr, _fixup_buffer: &'c mut [u8], ) -> Result<Option<&'c str>, Error>1020         fn fixup_os_commandline<'c>(
1021             &mut self,
1022             _commandline: &CStr,
1023             _fixup_buffer: &'c mut [u8],
1024         ) -> Result<Option<&'c str>, Error> {
1025             Ok(None)
1026         }
1027 
fixup_bootconfig<'c>( &mut self, _bootconfig: &[u8], fixup_buffer: &'c mut [u8], ) -> Result<Option<&'c [u8]>, Error>1028         fn fixup_bootconfig<'c>(
1029             &mut self,
1030             _bootconfig: &[u8],
1031             fixup_buffer: &'c mut [u8],
1032         ) -> Result<Option<&'c [u8]>, Error> {
1033             let (out, _) = fixup_buffer.split_at_mut(Self::GBL_TEST_BOOTCONFIG.len());
1034             out.clone_from_slice(Self::GBL_TEST_BOOTCONFIG.as_bytes());
1035             Ok(Some(out))
1036         }
1037 
fixup_device_tree(&mut self, fdt: &mut [u8]) -> Result<(), Error>1038         fn fixup_device_tree(&mut self, fdt: &mut [u8]) -> Result<(), Error> {
1039             Fdt::new_mut(fdt).unwrap().set_property("chosen", c"fixup", &[1])?;
1040             Ok(())
1041         }
1042 
select_device_trees( &mut self, device_tree: &mut device_tree::DeviceTreeComponentsRegistry, ) -> Result<(), Error>1043         fn select_device_trees(
1044             &mut self,
1045             device_tree: &mut device_tree::DeviceTreeComponentsRegistry,
1046         ) -> Result<(), Error> {
1047             // Select the first dtbo.
1048             match device_tree.components_mut().find(|v| !v.is_base_device_tree()) {
1049                 Some(v) => v.selected = true,
1050                 _ => {}
1051             }
1052             device_tree.autoselect()
1053         }
1054 
fastboot_variable<'arg>( &mut self, name: &CStr, mut args: impl Iterator<Item = &'arg CStr> + Clone, out: &mut [u8], ) -> Result<usize, Error>1055         fn fastboot_variable<'arg>(
1056             &mut self,
1057             name: &CStr,
1058             mut args: impl Iterator<Item = &'arg CStr> + Clone,
1059             out: &mut [u8],
1060         ) -> Result<usize, Error> {
1061             match name.to_str()? {
1062                 Self::GBL_TEST_VAR => {
1063                     Ok(snprintf!(out, "{}:{:?}", Self::GBL_TEST_VAR_VAL, args.next()).len())
1064                 }
1065                 _ => Err(Error::NotFound),
1066             }
1067         }
1068 
fastboot_visit_all_variables( &mut self, mut cb: impl FnMut(&[&CStr], &CStr), ) -> Result<(), Error>1069         fn fastboot_visit_all_variables(
1070             &mut self,
1071             mut cb: impl FnMut(&[&CStr], &CStr),
1072         ) -> Result<(), Error> {
1073             cb(
1074                 &[CString::new(Self::GBL_TEST_VAR).unwrap().as_c_str(), c"1"],
1075                 CString::new(format!("{}:1", Self::GBL_TEST_VAR_VAL)).unwrap().as_c_str(),
1076             );
1077             cb(
1078                 &[CString::new(Self::GBL_TEST_VAR).unwrap().as_c_str(), c"2"],
1079                 CString::new(format!("{}:2", Self::GBL_TEST_VAR_VAL)).unwrap().as_c_str(),
1080             );
1081             Ok(())
1082         }
1083 
slots_metadata(&mut self) -> Result<SlotsMetadata, Error>1084         fn slots_metadata(&mut self) -> Result<SlotsMetadata, Error> {
1085             unimplemented!();
1086         }
1087 
get_current_slot(&mut self) -> Result<Slot, Error>1088         fn get_current_slot(&mut self) -> Result<Slot, Error> {
1089             self.current_slot.unwrap()
1090         }
1091 
get_next_slot(&mut self, mark_boot_attempt: bool) -> Result<Slot, Error>1092         fn get_next_slot(&mut self, mark_boot_attempt: bool) -> Result<Slot, Error> {
1093             self.mark_boot_attempt_called += usize::from(mark_boot_attempt);
1094             self.next_slot.unwrap()
1095         }
1096 
set_active_slot(&mut self, slot: u8) -> Result<(), Error>1097         fn set_active_slot(&mut self, slot: u8) -> Result<(), Error> {
1098             self.last_set_active_slot = Some(slot);
1099             Ok(())
1100         }
1101 
set_reboot_reason(&mut self, _: RebootReason) -> Result<(), Error>1102         fn set_reboot_reason(&mut self, _: RebootReason) -> Result<(), Error> {
1103             unimplemented!()
1104         }
1105 
get_reboot_reason(&mut self) -> Result<RebootReason, Error>1106         fn get_reboot_reason(&mut self) -> Result<RebootReason, Error> {
1107             self.reboot_reason.unwrap()
1108         }
1109     }
1110 
1111     #[test]
test_fuchsia_reboot_bootloader()1112     fn test_fuchsia_reboot_bootloader() {
1113         let mut storage = FakeGblOpsStorage::default();
1114         storage.add_raw_device(c"durable_boot", [0x00u8; 4 * 1024]);
1115         let mut gbl_ops = FakeGblOps::new(&storage);
1116         gbl_ops.os = Some(Os::Fuchsia);
1117         (gbl_ops.reboot_bootloader().unwrap())();
1118         assert!(gbl_ops.rebooted);
1119         assert_eq!(get_and_clear_one_shot_bootloader(&mut GblAbrOps(&mut gbl_ops)), Ok(true));
1120     }
1121 
1122     #[test]
test_non_fuchsia_reboot_bootloader()1123     fn test_non_fuchsia_reboot_bootloader() {
1124         let mut storage = FakeGblOpsStorage::default();
1125         storage.add_raw_device(c"durable_boot", [0x00u8; 4 * 1024]);
1126         let mut gbl_ops = FakeGblOps::new(&storage);
1127         gbl_ops.os = Some(Os::Android);
1128         assert!(gbl_ops.reboot_bootloader().is_err_and(|e| e == Error::Unsupported));
1129         assert_eq!(get_and_clear_one_shot_bootloader(&mut GblAbrOps(&mut gbl_ops)), Ok(false));
1130     }
1131 
1132     #[test]
test_fuchsia_reboot_recovery()1133     fn test_fuchsia_reboot_recovery() {
1134         let mut storage = FakeGblOpsStorage::default();
1135         storage.add_raw_device(c"durable_boot", [0x00u8; 4 * 1024]);
1136         let mut gbl_ops = FakeGblOps::new(&storage);
1137         gbl_ops.os = Some(Os::Fuchsia);
1138         (gbl_ops.reboot_recovery().unwrap())();
1139         assert!(gbl_ops.rebooted);
1140         // One shot recovery is set.
1141         assert_eq!(get_boot_slot(&mut GblAbrOps(&mut gbl_ops), true), (SlotIndex::R, false));
1142         assert_eq!(get_boot_slot(&mut GblAbrOps(&mut gbl_ops), true), (SlotIndex::A, false));
1143     }
1144 
1145     #[test]
test_non_fuchsia_reboot_recovery()1146     fn test_non_fuchsia_reboot_recovery() {
1147         let mut storage = FakeGblOpsStorage::default();
1148         storage.add_raw_device(c"durable_boot", [0x00u8; 4 * 1024]);
1149         let mut gbl_ops = FakeGblOps::new(&storage);
1150         gbl_ops.os = Some(Os::Android);
1151         assert!(gbl_ops.reboot_recovery().is_err_and(|e| e == Error::Unsupported));
1152         // One shot recovery is not set.
1153         assert_eq!(get_boot_slot(&mut GblAbrOps(&mut gbl_ops), true), (SlotIndex::A, false));
1154     }
1155 
1156     /// Helper for creating a slot object.
slot(suffix: char) -> Slot1157     pub(crate) fn slot(suffix: char) -> Slot {
1158         Slot { suffix: suffix.into(), ..Default::default() }
1159     }
1160 }
1161