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