blob: fc619e0be152e05388e7b4a5a61b2dcd34d43d1e [file] [log] [blame]
// Copyright 2023, The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! # Generic Boot Loader (gbl) Library
//!
//! TODO: b/312610098 - add documentation.
//!
//! The intended users of this library are firmware, bootloader, and bring-up teams at OEMs and SOC
//! Vendors
//!
//! # Features
//! * `sw_digest` - enables software implementation of digests: [SwDigest], [SwContext]
//! * `alloc` - enables AVB ops related logic that relies on allocation and depends on allocation.
// This code is intended for use in bootloaders that typically will not support
// the Rust standard library
#![cfg_attr(not(any(test, android_dylib)), no_std)]
// TODO: b/312610985 - return warning for unused partitions
#![allow(unused_variables, dead_code)]
// TODO: b/312608163 - Adding ZBI library usage to check dependencies
extern crate avb;
extern crate core;
extern crate cstr;
extern crate gbl_storage;
extern crate spin;
extern crate zbi;
use avb::{HashtreeErrorMode, SlotVerifyData, SlotVerifyError, SlotVerifyFlags, SlotVerifyResult};
use core::ffi::CStr;
use core::fmt::Debug;
use cstr::cstr;
use gbl_storage::AsMultiBlockDevices;
use spin::Mutex;
pub mod boot_mode;
pub mod boot_reason;
pub mod digest;
pub mod error;
pub mod fastboot;
pub mod ops;
/// The 'slots' module, containing types and traits for
/// querying and modifying slotted boot behavior.
pub mod slots;
use slots::{BootTarget, BootToken, Cursor, Manager, OneShot, SuffixBytes, UnbootableReason};
#[cfg(feature = "sw_digest")]
pub mod sw_digest;
pub use avb::Descriptor;
pub use boot_mode::BootMode;
pub use boot_reason::KnownBootReason;
pub use digest::{Context, Digest};
pub use error::{Error, IntegrationError, Result};
pub use ops::{
AndroidBootImages, BootImages, DefaultGblOps, FuchsiaBootImages, GblOps, GblOpsError,
};
#[cfg(feature = "sw_digest")]
pub use sw_digest::{SwContext, SwDigest};
use ops::GblUtils;
// TODO: b/312607649 - Replace placeholders with actual structures: https://r.android.com/2721974, etc
/// TODO: b/312607649 - placeholder type
pub struct Partition {}
/// TODO: b/312607649 - placeholder type
pub struct InfoStruct {}
/// TODO: b/312607649 - placeholder type
pub struct AvbVerificationFlags(u32); // AvbVBMetaImageFlags from
// external/avb/libavb/avb_vbmeta_image.h
/// Data structure holding verified slot data.
#[derive(Debug)]
pub struct VerifiedData<'a>(SlotVerifyData<'a>);
/// Structure representing partition and optional address it is required to be loaded.
/// If no address is provided GBL will use default one.
pub struct PartitionRamMap<'b, 'c> {
/// Partition details
pub partition: &'b Partition,
/// Optional memory region to load partitions.
/// If it's not provided default values will be used.
pub address: Option<&'c mut [u8]>,
loaded: bool,
verified: bool,
}
/// Boot Image in memory
pub struct BootImage<'a>(&'a mut [u8]);
/// Vendor Boot Image in memory
pub struct VendorBootImage<'a>(&'a mut [u8]);
/// Init Boot Image in memory
pub struct InitBootImage<'a>(&'a mut [u8]);
/// Kernel Image in memory
pub struct KernelImage<'a>(&'a mut [u8]);
/// Ramdisk in memory
pub struct Ramdisk<'a>(&'a mut [u8]);
/// Bootconfig in memory
pub struct Bootconfig<'a>(&'a mut [u8]);
/// DTB in memory
pub struct Dtb<'a>(&'a mut [u8]);
/// Create Boot Image from corresponding partition for `partitions_ram_map` and `avb_descriptors`
/// lists
pub fn get_boot_image<'a: 'b, 'b: 'c, 'c, 'd>(
verified_data: &mut VerifiedData<'d>,
partitions_ram_map: &'a mut [PartitionRamMap<'b, 'c>],
) -> (Option<BootImage<'c>>, &'a mut [PartitionRamMap<'b, 'c>]) {
match partitions_ram_map.len() {
0 => (None, partitions_ram_map),
_ => {
let (partition_map, tail) = partitions_ram_map.split_first_mut().unwrap();
(partition_map.address.take().map(BootImage), tail)
}
}
}
/// Create Vendor Boot Image from corresponding partition for `partitions_ram_map` and
/// `avb_descriptors` lists
pub fn get_vendor_boot_image<'a: 'b, 'b: 'c, 'c, 'd>(
verified_data: &mut VerifiedData<'d>,
partitions_ram_map: &'a mut [PartitionRamMap<'b, 'c>],
) -> (Option<VendorBootImage<'c>>, &'a mut [PartitionRamMap<'b, 'c>]) {
match partitions_ram_map.len() {
0 => (None, partitions_ram_map),
_ => {
let (partition_map, tail) = partitions_ram_map.split_first_mut().unwrap();
(partition_map.address.take().map(VendorBootImage), tail)
}
}
}
/// Create Init Boot Image from corresponding partition for `partitions` and `avb_descriptors` lists
pub fn get_init_boot_image<'a: 'b, 'b: 'c, 'c, 'd>(
verified_data: &mut VerifiedData<'d>,
partitions_ram_map: &'a mut [PartitionRamMap<'b, 'c>],
) -> (Option<InitBootImage<'c>>, &'a mut [PartitionRamMap<'b, 'c>]) {
match partitions_ram_map.len() {
0 => (None, partitions_ram_map),
_ => {
let (partition_map, tail) = partitions_ram_map.split_first_mut().unwrap();
(partition_map.address.take().map(InitBootImage), tail)
}
}
}
/// Create separate image types from [avb::Descriptor]
pub fn get_images<'a: 'b, 'b: 'c, 'c, 'd>(
verified_data: &mut VerifiedData<'d>,
partitions_ram_map: &'a mut [PartitionRamMap<'b, 'c>],
) -> (
Option<BootImage<'c>>,
Option<InitBootImage<'c>>,
Option<VendorBootImage<'c>>,
&'a mut [PartitionRamMap<'b, 'c>],
) {
let (boot_image, partitions_ram_map) = get_boot_image(verified_data, partitions_ram_map);
let (init_boot_image, partitions_ram_map) =
get_init_boot_image(verified_data, partitions_ram_map);
let (vendor_boot_image, partitions_ram_map) =
get_vendor_boot_image(verified_data, partitions_ram_map);
(boot_image, init_boot_image, vendor_boot_image, partitions_ram_map)
}
static BOOT_TOKEN: Mutex<Option<BootToken>> = Mutex::new(Some(BootToken(())));
type AvbVerifySlot = for<'b> fn(
ops: &mut dyn avb::Ops<'b>,
requested_partitions: &[&CStr],
ab_suffix: Option<&CStr>,
flags: SlotVerifyFlags,
hashtree_error_mode: HashtreeErrorMode,
) -> SlotVerifyResult<'b, SlotVerifyData<'b>>;
/// GBL object that provides implementation of helpers for boot process.
///
/// To create this object use [GblBuilder].
pub struct Gbl<'a, G>
where
G: GblOps,
{
ops: &'a mut G,
image_verification: bool,
verify_slot: AvbVerifySlot,
}
impl<'a, G> Gbl<'a, G>
where
G: GblOps,
{
/// Verify + Load Image Into memory
///
/// Load from disk, validate with AVB
///
/// # Arguments
/// * `avb_ops` - implementation for `avb::Ops` that would be borrowed in result to prevent
/// changes to partitions until it is out of scope.
/// * `partitions_ram_map` - Partitions to verify with optional address to load image to.
/// * `avb_verification_flags` - AVB verification flags/options
/// * `boot_target` - [Optional] Boot Target
///
/// # Returns
///
/// * `Ok(&[avb_descriptor])` - Array of AVB Descriptors - AVB return codes, partition name,
/// image load address, image size, AVB Footer contents (version details, etc.)
/// * `Err(Error)` - on failure
pub fn load_and_verify_image<'b>(
&mut self,
avb_ops: &mut impl avb::Ops<'b>,
partitions_ram_map: &mut [PartitionRamMap],
avb_verification_flags: AvbVerificationFlags,
boot_target: Option<BootTarget>,
) -> Result<VerifiedData<'b>> {
let bytes: SuffixBytes =
if let Some(tgt) = boot_target { tgt.suffix().into() } else { Default::default() };
let requested_partitions = [cstr!("")];
let avb_suffix = CStr::from_bytes_until_nul(&bytes)?;
let verified_data = VerifiedData(
(self.verify_slot)(
avb_ops,
&requested_partitions,
Some(avb_suffix),
SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_NONE,
HashtreeErrorMode::AVB_HASHTREE_ERROR_MODE_EIO,
)
.map_err(|v| v.without_verify_data())?,
);
Ok(verified_data)
}
/// Load Slot Manager Interface
///
/// The default implementation loads from the `durable_boot` partition
/// and writes changes back on the destruction of the cursor.
///
/// # Returns
///
/// * `Ok(Cursor)` - Cursor object that manages a Manager
/// * `Err(Error)` - on failure
pub fn load_slot_interface<'b, B: gbl_storage::AsBlockDevice, M: Manager>(
&mut self,
block_device: &'b mut B,
) -> Result<Cursor<'b, B, M>> {
let boot_token = BOOT_TOKEN.lock().take().ok_or(Error::OperationProhibited)?;
self.ops
.load_slot_interface::<B, M>(block_device, boot_token)
.map_err(|_| Error::OperationProhibited.into())
}
/// Info Load
///
/// Unpack boot image in RAM
///
/// # Arguments
/// * `boot_image_buffer` - Buffer that contains (Optionally Verified) Boot Image
/// * `boot_mode` - Boot Mode
/// * `boot_target` - [Optional] Boot Target
///
/// # Returns
///
/// * `Ok(InfoStruct)` - Info Struct (Concatenated kernel commandline - includes slot,
/// bootconfig selection, normal_mode, Concatenated bootconfig) on success
/// * `Err(Error)` - on failure
pub fn unpack_boot_image(
&self,
boot_image_buffer: &BootImage,
boot_target: Option<BootTarget>,
) -> Result<InfoStruct> {
unimplemented!();
}
/// Kernel Load
///
/// Prepare kernel in RAM for booting
///
/// # Arguments
/// * `info` - Info Struct from Info Load
/// * `image_buffer` - Buffer that contains (Verified) Boot Image
/// * `load_buffer` - Kernel Load buffer
///
/// # Returns
///
/// * `Ok(())` - on success
/// * `Err(Error)` - on failure
pub fn kernel_load<'b>(
&self,
info: &InfoStruct,
image_buffer: BootImage,
load_buffer: &'b mut [u8],
) -> Result<KernelImage<'b>> {
unimplemented!();
}
/// Ramdisk + Bootconfig Load
///
/// Kernel Load
/// (Could break this into a RD and Bootconfig specific function each, TBD)
/// Prepare ramdisk/bootconfig in RAM for booting
///
/// # Arguments
/// * `info` - Info Struct from Info Load
/// * `vendor_boot_image` - Buffer that contains (Verified) Vendor Boot Image
/// * `init_boot_image` - Buffer that contains (Verified) Init Boot Image
/// * `ramdisk_load_buffer` - Ramdisk Load buffer (not compressed). It will be filled with
/// a concatenation of `vendor_boot_image`, `init_boot_image` and bootconfig at the end.
///
/// # Returns
///
/// * `Ok(&str)` - on success returns Kernel command line
/// * `Err(Error)` - on failure
pub fn ramdisk_bootconfig_load(
&self,
info: &InfoStruct,
vendor_boot_image: &VendorBootImage,
init_boot_image: &InitBootImage,
ramdisk: &mut Ramdisk,
) -> Result<&'static str> {
unimplemented!();
}
/// DTB Update And Load
///
/// Prepare DTB in RAM for booting
///
/// # Arguments
/// * `info` - Info Struct from Info Load
/// * `vendor_boot_image_buffer` - Buffer that contains (Verified) Vendor Boot Image
///
/// # Returns
///
/// * `Ok()` - on success
/// * `Err(Error)` - on failure
pub fn dtb_update_and_load(
&self,
info: &InfoStruct,
vendor_boot_image_buffer: VendorBootImage,
) -> Result<Dtb> {
unimplemented!();
}
/// Kernel Jump
///
///
/// # Arguments
/// * `kernel_load_buffer` - Kernel Load buffer
/// * `ramdisk_bootconfi_load_buffer` - Concatenated Ramdisk, (Bootconfig if present) Load
/// buffer
/// * `dtb_load_buffer` - DTB Load buffer
/// * `boot_token` - Consumable boot token
///
/// # Returns
///
/// * doesn't return on success
/// * `Err(Error)` - on failure
// Nevertype could be used here when it is stable https://github.com/serde-rs/serde/issues/812
pub fn kernel_jump(
&self,
kernel_load_buffer: KernelImage,
ramdisk_load_buffer: Ramdisk,
dtb_load_buffer: Dtb,
boot_token: BootToken,
) -> Result<()> {
unimplemented!();
}
/// Load, verify, and boot
///
/// Wrapper around the above functions for devices that don't need custom behavior between each
/// step
///
/// Warning: If the call to load_verify_boot fails, the device MUST
/// be restarted in order to make forward boot progress.
/// Callers MAY log the error, enter an interactive mode,
/// or take other actions before rebooting.
///
///
/// # Arguments
/// * `avb_ops` - implementation for `avb::Ops` that would be borrowed in result to prevent
/// changes to partitions until it is out of scope.
/// * `partitions_ram_map` - Partitions to verify and optional address for them to be loaded.
/// * `avb_verification_flags` - AVB verification flags/options
/// * `slot_cursor` - Cursor object that manages interactions with boot slot management
/// * `kernel_load_buffer` - Buffer for loading the kernel.
/// * `ramdisk_load_buffer` - Buffer for loading the ramdisk.
/// * `fdt` - Buffer containing a flattened device tree blob.
///
/// # Returns
///
/// * doesn't return on success
/// * `Err(Error)` - on failure
// Nevertype could be used here when it is stable https://github.com/serde-rs/serde/issues/812
#[allow(clippy::too_many_arguments)]
pub fn load_verify_boot<'b: 'c, 'c, 'd: 'b, B: gbl_storage::AsBlockDevice>(
&mut self,
avb_ops: &mut impl avb::Ops<'b>,
partitions_ram_map: &'d mut [PartitionRamMap<'b, 'c>],
avb_verification_flags: AvbVerificationFlags,
slot_cursor: Cursor<B, impl Manager>,
kernel_load_buffer: &mut [u8],
ramdisk_load_buffer: &mut [u8],
fdt: &mut [u8],
) -> Result<()> {
let dtb = Dtb(&mut fdt[..]);
let mut ramdisk = Ramdisk(ramdisk_load_buffer);
// Call the inner method which consumes the cursor
// in order to properly manager cursor lifetime
// and cleanup.
let (kernel_image, token) = self.lvb_inner(
avb_ops,
&mut ramdisk,
kernel_load_buffer,
partitions_ram_map,
avb_verification_flags,
slot_cursor,
)?;
self.kernel_jump(kernel_image, ramdisk, dtb, token)
}
fn is_unrecoverable_error(error: &IntegrationError) -> bool {
// Note: these ifs are nested instead of chained because multiple
// expressions in an if-let is an unstable features
if let IntegrationError::AvbSlotVerifyError(ref avb_error) = error {
// These are the AVB errors that are not recoverable on a subsequent attempt.
// If necessary in the future, this helper function can be moved to the GblOps trait
// and customized for platform specific behavior.
if matches!(
avb_error,
SlotVerifyError::Verification(_)
| SlotVerifyError::PublicKeyRejected
| SlotVerifyError::RollbackIndex
) {
return true;
}
}
false
}
fn lvb_inner<'b: 'c, 'c, 'd: 'b, 'e, B: gbl_storage::AsBlockDevice>(
&mut self,
avb_ops: &mut impl avb::Ops<'b>,
ramdisk: &mut Ramdisk,
kernel_load_buffer: &'e mut [u8],
partitions_ram_map: &'d mut [PartitionRamMap<'b, 'c>],
avb_verification_flags: AvbVerificationFlags,
mut slot_cursor: Cursor<B, impl Manager>,
) -> Result<(KernelImage<'e>, BootToken)> {
let mut oneshot_status = slot_cursor.ctx.get_oneshot_status();
slot_cursor.ctx.clear_oneshot_status();
if oneshot_status == Some(OneShot::Bootloader) {
match self.ops.do_fastboot(&mut slot_cursor) {
Ok(_) => oneshot_status = slot_cursor.ctx.get_oneshot_status(),
Err(IntegrationError::GblNativeError(Error::NotImplemented)) => (),
Err(e) => return Err(e),
}
}
let boot_target = match oneshot_status {
None | Some(OneShot::Bootloader) => slot_cursor.ctx.get_boot_target(),
Some(OneShot::Continue(recovery)) => BootTarget::Recovery(recovery),
};
let mut verify_data = self
.load_and_verify_image(
avb_ops,
partitions_ram_map,
AvbVerificationFlags(0),
Some(boot_target),
)
.map_err(|e: IntegrationError| {
if let BootTarget::NormalBoot(slot) = boot_target {
if Self::is_unrecoverable_error(&e) {
let _ = slot_cursor.ctx.set_slot_unbootable(
slot.suffix,
UnbootableReason::VerificationFailure,
);
} else {
// Note: the call to mark_boot_attempt will fail if any of the following occur:
// * the target was already Unbootable before the call to load_and_verify_image
// * policy, I/O, or other errors in mark_boot_attempt
//
// We don't really care about those circumstances.
// The call here is a best effort attempt to decrement tries remaining.
let _ = slot_cursor.ctx.mark_boot_attempt(boot_target);
}
}
e
})?;
let (boot_image, init_boot_image, vendor_boot_image, _) =
get_images(&mut verify_data, partitions_ram_map);
let boot_image = boot_image.ok_or(Error::MissingImage)?;
let vendor_boot_image = vendor_boot_image.ok_or(Error::MissingImage)?;
let init_boot_image = init_boot_image.ok_or(Error::MissingImage)?;
let info_struct = self.unpack_boot_image(&boot_image, Some(boot_target))?;
let kernel_image = self.kernel_load(&info_struct, boot_image, kernel_load_buffer)?;
let cmd_line = self.ramdisk_bootconfig_load(
&info_struct,
&vendor_boot_image,
&init_boot_image,
ramdisk,
)?;
self.dtb_update_and_load(&info_struct, vendor_boot_image)?;
let token = slot_cursor
.ctx
.mark_boot_attempt(boot_target)
.map_err(|_| Error::OperationProhibited)?;
Ok((kernel_image, token))
}
/// Loads and boots a Zircon kernel according to ABR + AVB.
pub fn zircon_load_and_boot(&mut self, load_buffer: &mut [u8]) -> Result<()> {
let (mut block_devices, load_buffer) = GblUtils::new(self.ops, load_buffer)?;
block_devices.sync_gpt_all(&mut |_, _, _| {});
// TODO(b/334962583): Implement zircon ABR + AVB.
// The following are place holder for test of invocation in the integration test only.
let ptn_size = block_devices
.find_partition("zircon_a")?
.size()
.map_err(|e: gbl_storage::StorageError| IntegrationError::StorageError(e))?
.try_into()
.or(Err(Error::ArithmeticOverflow))?;
let (kernel, remains) = load_buffer.split_at_mut(ptn_size);
block_devices.read_gpt_partition("zircon_a", 0, kernel)?;
self.ops.boot(BootImages::Fuchsia(FuchsiaBootImages {
zbi_kernel: kernel,
zbi_items: &mut [],
}))?;
Err(Error::BootFailed.into())
}
}
#[cfg(feature = "sw_digest")]
impl<'a> Default for Gbl<'a, DefaultGblOps> {
fn default() -> Self {
GblBuilder::new(DefaultGblOps::new()).build()
}
}
/// Builder for GBL object
#[derive(Debug)]
pub struct GblBuilder<'a, G>
where
G: GblOps,
{
ops: &'a mut G,
image_verification: bool,
verify_slot: AvbVerifySlot,
}
impl<'a, G> GblBuilder<'a, G>
where
G: GblOps,
{
/// Start Gbl object creation, with default GblOps implementation
pub fn new(ops: &'a mut G) -> Self {
GblBuilder { ops, image_verification: true, verify_slot: avb::slot_verify }
}
/// Disable image verification
pub fn no_image_verification(mut self) -> Self {
self.image_verification = false;
self
}
// Override [avb::slot_verify] for testing only
#[cfg(test)]
fn verify_slot(mut self, verify_slot: AvbVerifySlot) -> Self {
self.verify_slot = verify_slot;
self
}
/// Finish Gbl object construction and return it as the result
pub fn build(self) -> Gbl<'a, G> {
Gbl {
ops: self.ops,
image_verification: self.image_verification,
verify_slot: self.verify_slot,
}
}
}
#[cfg(test)]
mod tests {
extern crate avb_test;
use super::*;
use avb::IoError;
use avb::IoResult as AvbIoResult;
use avb::PublicKeyForPartitionInfo;
#[cfg(feature = "sw_digest")]
use avb_test::TestOps;
#[cfg(feature = "sw_digest")]
use std::fs;
struct AvbOpsUnimplemented {}
impl avb::Ops<'_> for AvbOpsUnimplemented {
fn validate_vbmeta_public_key(&mut self, _: &[u8], _: Option<&[u8]>) -> AvbIoResult<bool> {
Err(IoError::NotImplemented)
}
fn read_from_partition(&mut self, _: &CStr, _: i64, _: &mut [u8]) -> AvbIoResult<usize> {
Err(IoError::NotImplemented)
}
fn read_rollback_index(&mut self, _: usize) -> AvbIoResult<u64> {
Err(IoError::NotImplemented)
}
fn write_rollback_index(&mut self, _: usize, _: u64) -> AvbIoResult<()> {
Err(IoError::NotImplemented)
}
fn read_is_device_unlocked(&mut self) -> AvbIoResult<bool> {
Err(IoError::NotImplemented)
}
#[cfg(feature = "uuid")]
fn get_unique_guid_for_partition(&mut self, partition: &CStr) -> AvbIoResult<uuid::Uuid> {
Err(IoError::NotImplemented)
}
fn get_size_of_partition(&mut self, partition: &CStr) -> AvbIoResult<u64> {
Err(IoError::NotImplemented)
}
fn read_persistent_value(&mut self, name: &CStr, value: &mut [u8]) -> AvbIoResult<usize> {
Err(IoError::NotImplemented)
}
fn write_persistent_value(&mut self, name: &CStr, value: &[u8]) -> AvbIoResult<()> {
Err(IoError::NotImplemented)
}
fn erase_persistent_value(&mut self, name: &CStr) -> AvbIoResult<()> {
Err(IoError::NotImplemented)
}
fn validate_public_key_for_partition(
&mut self,
partition: &CStr,
public_key: &[u8],
public_key_metadata: Option<&[u8]>,
) -> AvbIoResult<PublicKeyForPartitionInfo> {
Err(IoError::NotImplemented)
}
}
#[cfg(feature = "sw_digest")]
#[test]
fn test_load_and_verify_image_avb_io_error() {
let mut gbl = GblBuilder::new(DefaultGblOps::new()).build();
let mut avb_ops = AvbOpsUnimplemented {};
let mut partitions_ram_map: [PartitionRamMap; 0] = [];
let avb_verification_flags = AvbVerificationFlags(0);
let res = gbl.load_and_verify_image(
&mut avb_ops,
&mut partitions_ram_map,
avb_verification_flags,
None,
);
assert_eq!(res.unwrap_err(), Error::AvbSlotVerifyError(SlotVerifyError::Io));
}
const TEST_PARTITION_NAME: &str = "test_part";
const TEST_IMAGE_PATH: &str = "testdata/test_image.img";
const TEST_VBMETA_PATH: &str = "testdata/test_vbmeta.img";
const TEST_PUBLIC_KEY_PATH: &str = "testdata/testkey_rsa4096_pub.bin";
const TEST_VBMETA_ROLLBACK_LOCATION: usize = 0; // Default value, we don't explicitly set this.
#[cfg(feature = "sw_digest")]
#[test]
fn test_load_and_verify_image_stub() {
let mut gbl = GblBuilder::new(DefaultGblOps::new()).build();
let mut avb_ops = TestOps::default();
avb_ops.add_partition(TEST_PARTITION_NAME, fs::read(TEST_IMAGE_PATH).unwrap());
avb_ops.add_partition("vbmeta", fs::read(TEST_VBMETA_PATH).unwrap());
avb_ops.add_vbmeta_key(fs::read(TEST_PUBLIC_KEY_PATH).unwrap(), None, true);
avb_ops.rollbacks.insert(TEST_VBMETA_ROLLBACK_LOCATION, 0);
avb_ops.unlock_state = Ok(false);
let mut partitions_ram_map: [PartitionRamMap; 0] = [];
let avb_verification_flags = AvbVerificationFlags(0);
let res = gbl.load_and_verify_image(
&mut avb_ops,
&mut partitions_ram_map,
avb_verification_flags,
None,
);
assert!(res.is_ok());
}
#[cfg(feature = "sw_digest")]
#[test]
fn test_load_and_verify_image_avb_error() {
const TEST_ERROR: SlotVerifyError<'static> = SlotVerifyError::Verification(None);
let expected_error = SlotVerifyError::Verification(None);
let mut gbl = GblBuilder::new(DefaultGblOps::new())
.verify_slot(|_, _, _, _, _| Err(TEST_ERROR))
.build();
let mut avb_ops = AvbOpsUnimplemented {};
let mut partitions_ram_map: [PartitionRamMap; 0] = [];
let avb_verification_flags = AvbVerificationFlags(0);
let res = gbl.load_and_verify_image(
&mut avb_ops,
&mut partitions_ram_map,
avb_verification_flags,
None,
);
assert_eq!(res.unwrap_err(), Error::AvbSlotVerifyError(TEST_ERROR));
}
}