xref: /aosp_15_r20/bootable/libbootloader/gbl/libgbl/src/lib.rs (revision 5225e6b173e52d2efc6bcf950c27374fd72adabc)
1 // Copyright 2023, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! # Generic Boot Loader (gbl) Library
16 //!
17 //! TODO: b/312610098 - add documentation.
18 //!
19 //! The intended users of this library are firmware, bootloader, and bring-up teams at OEMs and SOC
20 //! Vendors
21 //!
22 //! This library is `no_std` as it is intended for use in bootloaders that typically will not
23 //! support the Rust standard library. However, it does require `alloc` with a global allocator,
24 //! currently used for:
25 //! * libavb
26 //! * kernel decompression
27 
28 #![cfg_attr(not(any(test, android_dylib)), no_std)]
29 // TODO: b/312610985 - return warning for unused partitions
30 #![allow(async_fn_in_trait)]
31 // Needed for MaybeUninit::fill() experimental API
32 #![feature(maybe_uninit_fill)]
33 extern crate avb;
34 extern crate core;
35 extern crate gbl_storage;
36 extern crate spin;
37 extern crate zbi;
38 
39 use avb::{HashtreeErrorMode, SlotVerifyData, SlotVerifyError, SlotVerifyFlags};
40 use core::ffi::CStr;
41 use core::marker::PhantomData;
42 
43 pub mod android_boot;
44 pub mod boot_mode;
45 pub mod boot_reason;
46 pub mod constants;
47 pub mod decompress;
48 pub mod device_tree;
49 pub mod error;
50 pub mod fastboot;
51 pub mod fuchsia_boot;
52 pub mod gbl_avb;
53 pub mod ops;
54 pub mod partition;
55 
56 /// The 'slots' module, containing types and traits for
57 /// querying and modifying slotted boot behavior.
58 pub mod slots;
59 
60 mod image_buffer;
61 mod overlap;
62 
63 use slots::{BootTarget, BootToken, Cursor, OneShot, SuffixBytes, UnbootableReason};
64 
65 pub use avb::Descriptor;
66 pub use boot_mode::BootMode;
67 pub use boot_reason::KnownBootReason;
68 pub use error::{IntegrationError, Result};
69 use liberror::Error;
70 pub use ops::{GblOps, Os};
71 
72 use overlap::is_overlap;
73 
74 // TODO: b/312607649 - Replace placeholders with actual structures: https://r.android.com/2721974, etc
75 /// TODO: b/312607649 - placeholder type
76 pub struct Partition {}
77 /// TODO: b/312607649 - placeholder type
78 pub struct InfoStruct {}
79 
80 /// Structure representing partition and optional address it is required to be loaded.
81 /// If no address is provided GBL will use default one.
82 pub struct PartitionRamMap<'b, 'c> {
83     /// Partition details
84     pub partition: &'b Partition,
85 
86     /// Optional memory region to load partitions.
87     /// If it's not provided default values will be used.
88     pub address: Option<&'c mut [u8]>,
89 }
90 
91 /// Boot Image in memory
92 #[allow(dead_code)]
93 pub struct BootImage<'a>(&'a mut [u8]);
94 
95 /// Vendor Boot Image in memory
96 pub struct VendorBootImage<'a>(&'a mut [u8]);
97 
98 /// Init Boot Image in memory
99 pub struct InitBootImage<'a>(&'a mut [u8]);
100 
101 /// Kernel Image in memory
102 #[allow(dead_code)]
103 pub struct KernelImage<'a>(&'a mut [u8]);
104 
105 /// Ramdisk in memory
106 pub struct Ramdisk<'a>(&'a mut [u8]);
107 /// Bootconfig in memory
108 #[allow(dead_code)]
109 pub struct Bootconfig<'a>(&'a mut [u8]);
110 /// DTB in memory
111 #[allow(dead_code)]
112 pub struct Dtb<'a>(&'a mut [u8]);
113 
114 /// Create Boot Image from corresponding partition for `partitions_ram_map` and `avb_descriptors`
115 /// lists
get_boot_image<'a: 'b, 'b: 'c, 'c, 'd>( partitions_ram_map: &'a mut [PartitionRamMap<'b, 'c>], ) -> (Option<BootImage<'c>>, &'a mut [PartitionRamMap<'b, 'c>])116 pub fn get_boot_image<'a: 'b, 'b: 'c, 'c, 'd>(
117     partitions_ram_map: &'a mut [PartitionRamMap<'b, 'c>],
118 ) -> (Option<BootImage<'c>>, &'a mut [PartitionRamMap<'b, 'c>]) {
119     match partitions_ram_map.len() {
120         0 => (None, partitions_ram_map),
121         _ => {
122             let (partition_map, tail) = partitions_ram_map.split_first_mut().unwrap();
123             (partition_map.address.take().map(BootImage), tail)
124         }
125     }
126 }
127 
128 /// Create Vendor Boot Image from corresponding partition for `partitions_ram_map` and
129 /// `avb_descriptors` lists
get_vendor_boot_image<'a: 'b, 'b: 'c, 'c, 'd>( partitions_ram_map: &'a mut [PartitionRamMap<'b, 'c>], ) -> (Option<VendorBootImage<'c>>, &'a mut [PartitionRamMap<'b, 'c>])130 pub fn get_vendor_boot_image<'a: 'b, 'b: 'c, 'c, 'd>(
131     partitions_ram_map: &'a mut [PartitionRamMap<'b, 'c>],
132 ) -> (Option<VendorBootImage<'c>>, &'a mut [PartitionRamMap<'b, 'c>]) {
133     match partitions_ram_map.len() {
134         0 => (None, partitions_ram_map),
135         _ => {
136             let (partition_map, tail) = partitions_ram_map.split_first_mut().unwrap();
137             (partition_map.address.take().map(VendorBootImage), tail)
138         }
139     }
140 }
141 
142 /// Create Init Boot Image from corresponding partition for `partitions` and `avb_descriptors` lists
get_init_boot_image<'a: 'b, 'b: 'c, 'c, 'd>( partitions_ram_map: &'a mut [PartitionRamMap<'b, 'c>], ) -> (Option<InitBootImage<'c>>, &'a mut [PartitionRamMap<'b, 'c>])143 pub fn get_init_boot_image<'a: 'b, 'b: 'c, 'c, 'd>(
144     partitions_ram_map: &'a mut [PartitionRamMap<'b, 'c>],
145 ) -> (Option<InitBootImage<'c>>, &'a mut [PartitionRamMap<'b, 'c>]) {
146     match partitions_ram_map.len() {
147         0 => (None, partitions_ram_map),
148         _ => {
149             let (partition_map, tail) = partitions_ram_map.split_first_mut().unwrap();
150             (partition_map.address.take().map(InitBootImage), tail)
151         }
152     }
153 }
154 
155 /// Create separate image types from [avb::Descriptor]
get_images<'a: 'b, 'b: 'c, 'c, 'd>( partitions_ram_map: &'a mut [PartitionRamMap<'b, 'c>], ) -> ( Option<BootImage<'c>>, Option<InitBootImage<'c>>, Option<VendorBootImage<'c>>, &'a mut [PartitionRamMap<'b, 'c>], )156 pub fn get_images<'a: 'b, 'b: 'c, 'c, 'd>(
157     partitions_ram_map: &'a mut [PartitionRamMap<'b, 'c>],
158 ) -> (
159     Option<BootImage<'c>>,
160     Option<InitBootImage<'c>>,
161     Option<VendorBootImage<'c>>,
162     &'a mut [PartitionRamMap<'b, 'c>],
163 ) {
164     let (boot_image, partitions_ram_map) = get_boot_image(partitions_ram_map);
165     let (init_boot_image, partitions_ram_map) = get_init_boot_image(partitions_ram_map);
166     let (vendor_boot_image, partitions_ram_map) = get_vendor_boot_image(partitions_ram_map);
167     (boot_image, init_boot_image, vendor_boot_image, partitions_ram_map)
168 }
169 
170 /// GBL object that provides implementation of helpers for boot process.
171 pub struct Gbl<'a, 'd, G>
172 where
173     G: GblOps<'a, 'd>,
174 {
175     ops: &'a mut G,
176     boot_token: Option<BootToken>,
177     _get_image_buffer_lifetime: PhantomData<&'d ()>,
178 }
179 
180 // TODO(b/312610985): Investigate whether to deprecate this and remove this allow.
181 #[allow(unused_variables)]
182 impl<'a, 'f, G> Gbl<'a, 'f, G>
183 where
184     G: GblOps<'a, 'f>,
185 {
186     /// Returns a new [Gbl] object.
187     ///
188     /// # Arguments
189     /// * `ops` - the [GblOps] callbacks to use
new(ops: &'a mut G) -> Self190     pub fn new(ops: &'a mut G) -> Self {
191         Self { ops, boot_token: Some(BootToken(())), _get_image_buffer_lifetime: PhantomData }
192     }
193 
194     /// Verify + Load Image Into memory
195     ///
196     /// Load from disk, validate with AVB
197     ///
198     /// # Arguments
199     /// * `avb_ops` - implementation for `avb::Ops`
200     /// * `partitions_to_verify` - names of all the partitions to verify with libavb.
201     /// * `slot_verify_flags` - AVB slot verification flags
202     /// * `boot_target` - [Optional] Boot Target
203     ///
204     /// # Returns
205     /// * `Ok(SlotVerifyData)` - avb verification data
206     /// * `Err(Error)` - on failure
load_and_verify_image<'b>( &mut self, avb_ops: &mut impl avb::Ops<'b>, partitions_to_verify: &[&CStr], slot_verify_flags: SlotVerifyFlags, boot_target: Option<BootTarget>, ) -> Result<SlotVerifyData<'b>>207     pub fn load_and_verify_image<'b>(
208         &mut self,
209         avb_ops: &mut impl avb::Ops<'b>,
210         partitions_to_verify: &[&CStr],
211         slot_verify_flags: SlotVerifyFlags,
212         boot_target: Option<BootTarget>,
213     ) -> Result<SlotVerifyData<'b>> {
214         let bytes: SuffixBytes =
215             if let Some(tgt) = boot_target { tgt.suffix().into() } else { Default::default() };
216 
217         let avb_suffix = CStr::from_bytes_until_nul(&bytes).map_err(Error::from)?;
218 
219         Ok(avb::slot_verify(
220             avb_ops,
221             partitions_to_verify,
222             Some(avb_suffix),
223             slot_verify_flags,
224             HashtreeErrorMode::AVB_HASHTREE_ERROR_MODE_EIO,
225         )
226         .map_err(|v| v.without_verify_data())?)
227     }
228 
229     /// Load Slot Manager Interface
230     ///
231     /// The default implementation loads from the `durable_boot` partition
232     /// and writes changes back on the destruction of the cursor.
233     ///
234     /// # Returns
235     ///
236     /// * `Ok(Cursor)` - Cursor object that manages a Manager
237     /// * `Err(Error)` - on failure
load_slot_interface( &'a mut self, persist: &'a mut dyn FnMut(&mut [u8]) -> core::result::Result<(), Error>, ) -> Result<Cursor<'a>>238     pub fn load_slot_interface(
239         &'a mut self,
240         persist: &'a mut dyn FnMut(&mut [u8]) -> core::result::Result<(), Error>,
241     ) -> Result<Cursor<'a>> {
242         let boot_token = self.boot_token.take().ok_or(Error::OperationProhibited)?;
243         self.ops.load_slot_interface(persist, boot_token)
244     }
245 
246     /// Info Load
247     ///
248     /// Unpack boot image in RAM
249     ///
250     /// # Arguments
251     ///   * `boot_image_buffer` - Buffer that contains (Optionally Verified) Boot Image
252     ///   * `boot_mode` - Boot Mode
253     ///   * `boot_target` - [Optional] Boot Target
254     ///
255     /// # Returns
256     ///
257     /// * `Ok(InfoStruct)` - Info Struct (Concatenated kernel commandline - includes slot,
258     /// bootconfig selection, normal_mode, Concatenated bootconfig) on success
259     /// * `Err(Error)` - on failure
unpack_boot_image( &self, boot_image_buffer: &BootImage, boot_target: Option<BootTarget>, ) -> Result<InfoStruct>260     pub fn unpack_boot_image(
261         &self,
262         boot_image_buffer: &BootImage,
263         boot_target: Option<BootTarget>,
264     ) -> Result<InfoStruct> {
265         unimplemented!();
266     }
267 
268     /// Kernel Load
269     ///
270     /// Prepare kernel in RAM for booting
271     ///
272     /// # Arguments
273     ///   * `info` - Info Struct from Info Load
274     ///   * `image_buffer` - Buffer that contains (Verified) Boot Image
275     ///   * `load_buffer` - Kernel Load buffer
276     ///
277     /// # Returns
278     ///
279     /// * `Ok(())` - on success
280     /// * `Err(Error)` - on failure
kernel_load<'b>( &self, info: &InfoStruct, image_buffer: BootImage, load_buffer: &'b mut [u8], ) -> Result<KernelImage<'b>>281     pub fn kernel_load<'b>(
282         &self,
283         info: &InfoStruct,
284         image_buffer: BootImage,
285         load_buffer: &'b mut [u8],
286     ) -> Result<KernelImage<'b>> {
287         unimplemented!();
288     }
289 
290     /// Ramdisk + Bootconfig Load
291     ///
292     /// Kernel Load
293     /// (Could break this into a RD and Bootconfig specific function each, TBD)
294     /// Prepare ramdisk/bootconfig in RAM for booting
295     ///
296     /// # Arguments
297     ///   * `info` - Info Struct from Info Load
298     ///   * `vendor_boot_image` - Buffer that contains (Verified) Vendor Boot Image
299     ///   * `init_boot_image` - Buffer that contains (Verified) Init Boot Image
300     ///   * `ramdisk_load_buffer` - Ramdisk Load buffer (not compressed). It will be filled with
301     ///     a concatenation of `vendor_boot_image`, `init_boot_image` and bootconfig at the end.
302     ///
303     /// # Returns
304     ///
305     /// * `Ok(&str)` - on success returns Kernel command line
306     /// * `Err(Error)` - on failure
ramdisk_bootconfig_load( &self, info: &InfoStruct, vendor_boot_image: &VendorBootImage, init_boot_image: &InitBootImage, ramdisk: &mut Ramdisk, ) -> Result<&'static str>307     pub fn ramdisk_bootconfig_load(
308         &self,
309         info: &InfoStruct,
310         vendor_boot_image: &VendorBootImage,
311         init_boot_image: &InitBootImage,
312         ramdisk: &mut Ramdisk,
313     ) -> Result<&'static str> {
314         unimplemented!();
315     }
316 
317     /// DTB Update And Load
318     ///
319     /// Prepare DTB in RAM for booting
320     ///
321     /// # Arguments
322     ///   * `info` - Info Struct from Info Load
323     ///   * `vendor_boot_image_buffer` - Buffer that contains (Verified) Vendor Boot Image
324     ///
325     /// # Returns
326     ///
327     /// * `Ok()` - on success
328     /// * `Err(Error)` - on failure
dtb_update_and_load( &self, info: &InfoStruct, vendor_boot_image_buffer: VendorBootImage, ) -> Result<Dtb>329     pub fn dtb_update_and_load(
330         &self,
331         info: &InfoStruct,
332         vendor_boot_image_buffer: VendorBootImage,
333     ) -> Result<Dtb> {
334         unimplemented!();
335     }
336 
337     /// Kernel Jump
338     ///
339     ///
340     /// # Arguments
341     ///   * `kernel_load_buffer` - Kernel Load buffer
342     ///   * `ramdisk_bootconfi_load_buffer` - Concatenated Ramdisk, (Bootconfig if present) Load
343     ///   buffer
344     ///   * `dtb_load_buffer` - DTB Load buffer
345     ///   * `boot_token` - Consumable boot token
346     ///
347     /// # Returns
348     ///
349     /// * doesn't return on success
350     /// * `Err(Error)` - on failure
351     // Nevertype could be used here when it is stable https://github.com/serde-rs/serde/issues/812
kernel_jump( &self, kernel_load_buffer: KernelImage, ramdisk_load_buffer: Ramdisk, dtb_load_buffer: Dtb, boot_token: BootToken, ) -> Result<()>352     pub fn kernel_jump(
353         &self,
354         kernel_load_buffer: KernelImage,
355         ramdisk_load_buffer: Ramdisk,
356         dtb_load_buffer: Dtb,
357         boot_token: BootToken,
358     ) -> Result<()> {
359         unimplemented!();
360     }
361 
362     /// Load, verify, and boot
363     ///
364     /// Wrapper around the above functions for devices that don't need custom behavior between each
365     /// step
366     ///
367     /// Warning: If the call to load_verify_boot fails, the device MUST
368     ///          be restarted in order to make forward boot progress.
369     ///          Callers MAY log the error, enter an interactive mode,
370     ///          or take other actions before rebooting.
371     ///
372     /// # Arguments
373     /// * `avb_ops` - implementation for `avb::Ops` that would be borrowed in result to prevent
374     ///   changes to partitions until it is out of scope.
375     /// * `partitions_to_verify` - names of all the partitions to verify with libavb.
376     /// * `partitions_ram_map` - Partitions to verify and optional address for them to be loaded.
377     /// * `slot_verify_flags` - AVB slot verification flags
378     /// * `slot_cursor` - Cursor object that manages interactions with boot slot management
379     /// * `kernel_load_buffer` - Buffer for loading the kernel.
380     /// * `ramdisk_load_buffer` - Buffer for loading the ramdisk.
381     /// * `fdt` - Buffer containing a flattened device tree blob.
382     ///
383     /// # Returns
384     /// * doesn't return on success
385     /// * `Err(Error)` - on failure
386     // Nevertype could be used here when it is stable https://github.com/serde-rs/serde/issues/812
387     #[allow(clippy::too_many_arguments)]
load_verify_boot<'b: 'c, 'c, 'd: 'b>( &mut self, avb_ops: &mut impl avb::Ops<'b>, partitions_to_verify: &[&CStr], partitions_ram_map: &'d mut [PartitionRamMap<'b, 'c>], slot_verify_flags: SlotVerifyFlags, slot_cursor: Cursor, kernel_load_buffer: &mut [u8], ramdisk_load_buffer: &mut [u8], fdt: &mut [u8], ) -> Result<()>388     pub fn load_verify_boot<'b: 'c, 'c, 'd: 'b>(
389         &mut self,
390         avb_ops: &mut impl avb::Ops<'b>,
391         partitions_to_verify: &[&CStr],
392         partitions_ram_map: &'d mut [PartitionRamMap<'b, 'c>],
393         slot_verify_flags: SlotVerifyFlags,
394         slot_cursor: Cursor,
395         kernel_load_buffer: &mut [u8],
396         ramdisk_load_buffer: &mut [u8],
397         fdt: &mut [u8],
398     ) -> Result<()> {
399         let dtb = Dtb(&mut fdt[..]);
400         let mut ramdisk = Ramdisk(ramdisk_load_buffer);
401 
402         // Call the inner method which consumes the cursor
403         // in order to properly manager cursor lifetime
404         // and cleanup.
405         let (kernel_image, token) = self.lvb_inner(
406             avb_ops,
407             &mut ramdisk,
408             kernel_load_buffer,
409             partitions_to_verify,
410             partitions_ram_map,
411             slot_verify_flags,
412             slot_cursor,
413         )?;
414 
415         self.kernel_jump(kernel_image, ramdisk, dtb, token)
416     }
417 
is_unrecoverable_error(error: &IntegrationError) -> bool418     fn is_unrecoverable_error(error: &IntegrationError) -> bool {
419         // Note: these ifs are nested instead of chained because multiple
420         //       expressions in an if-let is an unstable features
421         if let IntegrationError::AvbSlotVerifyError(ref avb_error) = error {
422             // These are the AVB errors that are not recoverable on a subsequent attempt.
423             // If necessary in the future, this helper function can be moved to the GblOps trait
424             // and customized for platform specific behavior.
425             if matches!(
426                 avb_error,
427                 SlotVerifyError::Verification(_)
428                     | SlotVerifyError::PublicKeyRejected
429                     | SlotVerifyError::RollbackIndex
430             ) {
431                 return true;
432             }
433         }
434         false
435     }
436 
lvb_inner<'b: 'c, 'c, 'd: 'b, 'e>( &mut self, avb_ops: &mut impl avb::Ops<'b>, ramdisk: &mut Ramdisk, kernel_load_buffer: &'e mut [u8], partitions_to_verify: &[&CStr], partitions_ram_map: &'d mut [PartitionRamMap<'b, 'c>], slot_verify_flags: SlotVerifyFlags, slot_cursor: Cursor, ) -> Result<(KernelImage<'e>, BootToken)>437     fn lvb_inner<'b: 'c, 'c, 'd: 'b, 'e>(
438         &mut self,
439         avb_ops: &mut impl avb::Ops<'b>,
440         ramdisk: &mut Ramdisk,
441         kernel_load_buffer: &'e mut [u8],
442         partitions_to_verify: &[&CStr],
443         partitions_ram_map: &'d mut [PartitionRamMap<'b, 'c>],
444         slot_verify_flags: SlotVerifyFlags,
445         slot_cursor: Cursor,
446     ) -> Result<(KernelImage<'e>, BootToken)> {
447         let oneshot_status = slot_cursor.ctx.get_oneshot_status();
448         slot_cursor.ctx.clear_oneshot_status();
449 
450         let boot_target = match oneshot_status {
451             None | Some(OneShot::Bootloader) => slot_cursor.ctx.get_boot_target()?,
452             Some(OneShot::Continue(recovery)) => BootTarget::Recovery(recovery),
453         };
454 
455         let verify_data = self
456             .load_and_verify_image(
457                 avb_ops,
458                 partitions_to_verify,
459                 slot_verify_flags,
460                 Some(boot_target),
461             )
462             .map_err(|e: IntegrationError| {
463                 if let BootTarget::NormalBoot(slot) = boot_target {
464                     if Self::is_unrecoverable_error(&e) {
465                         let _ = slot_cursor.ctx.set_slot_unbootable(
466                             slot.suffix,
467                             UnbootableReason::VerificationFailure,
468                         );
469                     } else {
470                         // Note: the call to mark_boot_attempt will fail if any of the following occur:
471                         // * the target was already Unbootable before the call to load_and_verify_image
472                         // * policy, I/O, or other errors in mark_boot_attempt
473                         //
474                         // We don't really care about those circumstances.
475                         // The call here is a best effort attempt to decrement tries remaining.
476                         let _ = slot_cursor.ctx.mark_boot_attempt();
477                     }
478                 }
479                 e
480             })?;
481 
482         let (boot_image, init_boot_image, vendor_boot_image, _) = get_images(partitions_ram_map);
483         let boot_image = boot_image.ok_or(Error::MissingImage)?;
484         let vendor_boot_image = vendor_boot_image.ok_or(Error::MissingImage)?;
485         let init_boot_image = init_boot_image.ok_or(Error::MissingImage)?;
486 
487         if is_overlap(&[
488             boot_image.0,
489             vendor_boot_image.0,
490             init_boot_image.0,
491             &ramdisk.0,
492             kernel_load_buffer,
493         ]) {
494             return Err(IntegrationError::UnificationError(Error::BufferOverlap));
495         }
496 
497         let info_struct = self.unpack_boot_image(&boot_image, Some(boot_target))?;
498 
499         let kernel_image = self.kernel_load(&info_struct, boot_image, kernel_load_buffer)?;
500 
501         let cmd_line = self.ramdisk_bootconfig_load(
502             &info_struct,
503             &vendor_boot_image,
504             &init_boot_image,
505             ramdisk,
506         )?;
507 
508         self.dtb_update_and_load(&info_struct, vendor_boot_image)?;
509 
510         let token = slot_cursor.ctx.mark_boot_attempt().map_err(|_| Error::OperationProhibited)?;
511 
512         Ok((kernel_image, token))
513     }
514 }
515 
516 #[cfg(test)]
517 mod tests {
518     extern crate avb_sysdeps;
519     extern crate avb_test;
520     use super::*;
521     use crate::ops::test::FakeGblOps;
522     use avb::{CertPermanentAttributes, SlotVerifyError};
523     use avb_test::{FakeVbmetaKey, TestOps};
524     use std::{fs, path::Path};
525     use zerocopy::FromBytes;
526 
527     const TEST_ZIRCON_PARTITION_NAME: &str = "zircon_a";
528     const TEST_ZIRCON_PARTITION_NAME_CSTR: &CStr = c"zircon_a";
529     const TEST_ZIRCON_IMAGE_PATH: &str = "zircon_a.zbi";
530     const TEST_ZIRCON_VBMETA_PATH: &str = "zircon_a.vbmeta";
531     const TEST_ZIRCON_VBMETA_CERT_PATH: &str = "zircon_a.vbmeta.cert";
532     const TEST_PUBLIC_KEY_PATH: &str = "testkey_rsa4096_pub.bin";
533     const TEST_PERMANENT_ATTRIBUTES_PATH: &str = "cert_permanent_attributes.bin";
534     const TEST_PERMANENT_ATTRIBUTES_HASH_PATH: &str = "cert_permanent_attributes.hash";
535     const TEST_BAD_PERMANENT_ATTRIBUTES_PATH: &str = "cert_permanent_attributes.bad.bin";
536     const TEST_BAD_PERMANENT_ATTRIBUTES_HASH_PATH: &str = "cert_permanent_attributes.bad.hash";
537     const TEST_VBMETA_ROLLBACK_LOCATION: usize = 0; // Default value, we don't explicitly set this.
538     pub const TEST_CERT_PIK_VERSION: u64 = 42;
539     pub const TEST_CERT_PSK_VERSION: u64 = 42;
540 
541     /// Returns the contents of a test data file.
542     ///
543     /// Panicks if the requested file cannot be read.
544     ///
545     /// # Arguments
546     /// * `path`: file path relative to libgbl's `testdata/` directory.
testdata(path: &str) -> Vec<u8>547     fn testdata(path: &str) -> Vec<u8> {
548         let full_path = Path::new("external/gbl/libgbl/testdata").join(path);
549         fs::read(full_path).unwrap()
550     }
551 
552     /// Creates and returns a configured avb `TestOps`.
553     ///
554     /// The initial state will verify successfully with:
555     /// * a valid vbmeta image in the `vbmeta` partition, containing a hash descriptor for the
556     ///   `TEST_ZIRCON_PARTITION_NAME` partition
557     /// * an image in the `TEST_ZIRCON_PARTITION_NAME` partition matching the vbmeta hash
558     /// * no preloaded partition data
559     /// * a public key matching the vbmeta image
560     /// * a valid vbmeta rollback index
561     /// * a locked bootloader state
562     ///
563     /// The caller can modify any of this state as needed for their particular test.
test_avb_ops() -> TestOps<'static>564     fn test_avb_ops() -> TestOps<'static> {
565         let mut avb_ops = TestOps::default();
566 
567         avb_ops.add_partition(TEST_ZIRCON_PARTITION_NAME, testdata(TEST_ZIRCON_IMAGE_PATH));
568         avb_ops.add_partition("vbmeta", testdata(TEST_ZIRCON_VBMETA_PATH));
569         avb_ops.default_vbmeta_key = Some(FakeVbmetaKey::Avb {
570             public_key: testdata(TEST_PUBLIC_KEY_PATH),
571             public_key_metadata: None,
572         });
573         avb_ops.rollbacks.insert(TEST_VBMETA_ROLLBACK_LOCATION, Ok(0));
574         avb_ops.unlock_state = Ok(false);
575 
576         avb_ops
577     }
578 
579     /// Similar to `test_avb_ops()`, but with the avb_cert extension enabled.
test_avb_cert_ops() -> TestOps<'static>580     fn test_avb_cert_ops() -> TestOps<'static> {
581         let mut avb_ops = test_avb_ops();
582 
583         // Replace vbmeta with the cert-signed version.
584         avb_ops.add_partition("vbmeta", testdata(TEST_ZIRCON_VBMETA_CERT_PATH));
585 
586         // Tell `avb_ops` to use cert APIs and to route the default key through cert validation.
587         avb_ops.use_cert = true;
588         avb_ops.default_vbmeta_key = Some(FakeVbmetaKey::Cert);
589 
590         // Add the permanent attributes.
591         let perm_attr_bytes = testdata(TEST_PERMANENT_ATTRIBUTES_PATH);
592         let perm_attr_hash = testdata(TEST_PERMANENT_ATTRIBUTES_HASH_PATH);
593         avb_ops.cert_permanent_attributes =
594             Some(CertPermanentAttributes::read_from(&perm_attr_bytes[..]).unwrap());
595         avb_ops.cert_permanent_attributes_hash = Some(perm_attr_hash.try_into().unwrap());
596 
597         // Add the rollbacks for the cert keys.
598         avb_ops.rollbacks.insert(avb::CERT_PIK_VERSION_LOCATION, Ok(TEST_CERT_PIK_VERSION));
599         avb_ops.rollbacks.insert(avb::CERT_PSK_VERSION_LOCATION, Ok(TEST_CERT_PSK_VERSION));
600 
601         avb_ops
602     }
603 
604     #[test]
test_load_and_verify_image_success()605     fn test_load_and_verify_image_success() {
606         let mut gbl_ops = FakeGblOps::default();
607         let mut gbl = Gbl::new(&mut gbl_ops);
608         let mut avb_ops = test_avb_ops();
609 
610         let res = gbl.load_and_verify_image(
611             &mut avb_ops,
612             &mut [&TEST_ZIRCON_PARTITION_NAME_CSTR],
613             SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_NONE,
614             None,
615         );
616         assert!(res.is_ok());
617     }
618 
619     #[test]
test_load_and_verify_image_verification_error()620     fn test_load_and_verify_image_verification_error() {
621         let mut gbl_ops = FakeGblOps::default();
622         let mut gbl = Gbl::new(&mut gbl_ops);
623         let mut avb_ops = test_avb_ops();
624 
625         // Modify the kernel image, it should now fail to validate against the vbmeta image.
626         avb_ops.partitions.get_mut(TEST_ZIRCON_PARTITION_NAME).unwrap().contents.as_mut_vec()[0] ^=
627             0x01;
628 
629         let res = gbl.load_and_verify_image(
630             &mut avb_ops,
631             &mut [&TEST_ZIRCON_PARTITION_NAME_CSTR],
632             SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_NONE,
633             None,
634         );
635         assert_eq!(
636             res.unwrap_err(),
637             IntegrationError::AvbSlotVerifyError(SlotVerifyError::Verification(None))
638         );
639     }
640 
641     #[test]
test_load_and_verify_image_io_error()642     fn test_load_and_verify_image_io_error() {
643         let mut gbl_ops = FakeGblOps::default();
644         let mut gbl = Gbl::new(&mut gbl_ops);
645         let mut avb_ops = test_avb_ops();
646 
647         // Erase the fake rollbacks, which will result in an I/O error when attempting to access.
648         avb_ops.rollbacks.clear();
649 
650         let res = gbl.load_and_verify_image(
651             &mut avb_ops,
652             &mut [&TEST_ZIRCON_PARTITION_NAME_CSTR],
653             SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_NONE,
654             None,
655         );
656         assert_eq!(res.unwrap_err(), IntegrationError::AvbSlotVerifyError(SlotVerifyError::Io));
657     }
658 
659     #[test]
test_load_and_verify_image_with_cert_success()660     fn test_load_and_verify_image_with_cert_success() {
661         let mut gbl_ops = FakeGblOps::default();
662         let mut gbl = Gbl::new(&mut gbl_ops);
663         let mut avb_ops = test_avb_cert_ops();
664 
665         let res = gbl.load_and_verify_image(
666             &mut avb_ops,
667             &mut [&TEST_ZIRCON_PARTITION_NAME_CSTR],
668             SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_NONE,
669             None,
670         );
671         assert!(res.is_ok());
672     }
673 
674     #[test]
test_load_and_verify_image_with_cert_permanent_attribute_mismatch_error()675     fn test_load_and_verify_image_with_cert_permanent_attribute_mismatch_error() {
676         let mut gbl_ops = FakeGblOps::default();
677         let mut gbl = Gbl::new(&mut gbl_ops);
678         let mut avb_ops = test_avb_cert_ops();
679 
680         // Swap in the corrupted permanent attributes, which should cause the vbmeta image to fail
681         // validation due to key mismatch.
682         let perm_attr_bytes = testdata(TEST_BAD_PERMANENT_ATTRIBUTES_PATH);
683         let perm_attr_hash = testdata(TEST_BAD_PERMANENT_ATTRIBUTES_HASH_PATH);
684         avb_ops.cert_permanent_attributes =
685             Some(CertPermanentAttributes::read_from(&perm_attr_bytes[..]).unwrap());
686         avb_ops.cert_permanent_attributes_hash = Some(perm_attr_hash.try_into().unwrap());
687 
688         let res = gbl.load_and_verify_image(
689             &mut avb_ops,
690             &mut [&TEST_ZIRCON_PARTITION_NAME_CSTR],
691             SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_NONE,
692             None,
693         );
694         assert_eq!(
695             res.unwrap_err(),
696             IntegrationError::AvbSlotVerifyError(SlotVerifyError::PublicKeyRejected)
697         );
698     }
699 }
700