Merge "Test for checking persistence of encryptedstore"
diff --git a/compos/tests/AndroidTest.xml b/compos/tests/AndroidTest.xml
index f9e6837..4b414f1 100644
--- a/compos/tests/AndroidTest.xml
+++ b/compos/tests/AndroidTest.xml
@@ -16,6 +16,10 @@
<configuration description="Tests for CompOS">
<option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.art.apex" />
+ <!-- Only run tests if the device under test is SDK version 33 (Android 13) or above. -->
+ <object type="module_controller"
+ class="com.android.tradefed.testtype.suite.module.Sdk33ModuleController" />
+
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
<option name="force-root" value="true" />
</target_preparer>
diff --git a/pvmfw/Android.bp b/pvmfw/Android.bp
index f5e214e..ed3ef8d 100644
--- a/pvmfw/Android.bp
+++ b/pvmfw/Android.bp
@@ -18,7 +18,6 @@
"libfdtpci",
"liblibfdt",
"liblog_rust_nostd",
- "libpvmfw_avb_nostd",
"libpvmfw_embedded_key",
"libtinyvec_nostd",
"libvirtio_drivers",
diff --git a/pvmfw/avb/Android.bp b/pvmfw/avb/Android.bp
index 65259a5..3026d20 100644
--- a/pvmfw/avb/Android.bp
+++ b/pvmfw/avb/Android.bp
@@ -10,6 +10,9 @@
rustlibs: [
"libavb_bindgen",
],
+ whole_static_libs: [
+ "libavb",
+ ],
}
rust_library_rlib {
@@ -25,4 +28,31 @@
name: "libpvmfw_avb.test",
defaults: ["libpvmfw_avb_nostd_defaults"],
test_suites: ["general-tests"],
+ data: [
+ ":avb_testkey_rsa2048_pub_bin",
+ ":avb_testkey_rsa4096_pub_bin",
+ ":microdroid_kernel_signed",
+ ":unsigned_test_image",
+ ],
+ rustlibs: [
+ "libanyhow",
+ ],
+ enabled: false,
+ arch: {
+ // Microdroid kernel is only available in these architectures.
+ arm64: {
+ enabled: true,
+ },
+ x86_64: {
+ enabled: true,
+ },
+ },
+}
+
+// Generates a 16KB unsigned image for testing.
+genrule {
+ name: "unsigned_test_image",
+ tools: ["avbtool"],
+ out: ["unsigned_test.img"],
+ cmd: "$(location avbtool) generate_test_image --image_size 16384 --output $(out)",
}
diff --git a/pvmfw/avb/src/lib.rs b/pvmfw/avb/src/lib.rs
index eb1f918..1f39076 100644
--- a/pvmfw/avb/src/lib.rs
+++ b/pvmfw/avb/src/lib.rs
@@ -15,6 +15,8 @@
//! A library implementing the payload verification for pvmfw with libavb
#![cfg_attr(not(test), no_std)]
+// For usize.checked_add_signed(isize), available in Rust 1.66.0
+#![feature(mixed_integer_ops)]
mod verify;
diff --git a/pvmfw/avb/src/verify.rs b/pvmfw/avb/src/verify.rs
index 7f3ba3d..d5f7283 100644
--- a/pvmfw/avb/src/verify.rs
+++ b/pvmfw/avb/src/verify.rs
@@ -14,11 +14,19 @@
//! This module handles the pvmfw payload verification.
-use avb_bindgen::AvbSlotVerifyResult;
-use core::fmt;
+use avb_bindgen::{
+ avb_slot_verify, AvbHashtreeErrorMode, AvbIOResult, AvbOps, AvbSlotVerifyFlags,
+ AvbSlotVerifyResult,
+};
+use core::{
+ ffi::{c_char, c_void, CStr},
+ fmt,
+ ptr::{self, NonNull},
+ slice,
+};
/// Error code from AVB image verification.
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub enum AvbImageVerifyError {
/// AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT
InvalidArgument,
@@ -82,31 +90,383 @@
}
}
+enum AvbIOError {
+ /// AVB_IO_RESULT_ERROR_OOM,
+ #[allow(dead_code)]
+ Oom,
+ /// AVB_IO_RESULT_ERROR_IO,
+ #[allow(dead_code)]
+ Io,
+ /// AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION,
+ NoSuchPartition,
+ /// AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION,
+ RangeOutsidePartition,
+ /// AVB_IO_RESULT_ERROR_NO_SUCH_VALUE,
+ NoSuchValue,
+ /// AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE,
+ InvalidValueSize,
+ /// AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE,
+ #[allow(dead_code)]
+ InsufficientSpace,
+}
+
+impl From<AvbIOError> for AvbIOResult {
+ fn from(error: AvbIOError) -> Self {
+ match error {
+ AvbIOError::Oom => AvbIOResult::AVB_IO_RESULT_ERROR_OOM,
+ AvbIOError::Io => AvbIOResult::AVB_IO_RESULT_ERROR_IO,
+ AvbIOError::NoSuchPartition => AvbIOResult::AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION,
+ AvbIOError::RangeOutsidePartition => {
+ AvbIOResult::AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION
+ }
+ AvbIOError::NoSuchValue => AvbIOResult::AVB_IO_RESULT_ERROR_NO_SUCH_VALUE,
+ AvbIOError::InvalidValueSize => AvbIOResult::AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE,
+ AvbIOError::InsufficientSpace => AvbIOResult::AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE,
+ }
+ }
+}
+
+fn to_avb_io_result(result: Result<(), AvbIOError>) -> AvbIOResult {
+ result.map_or_else(|e| e.into(), |_| AvbIOResult::AVB_IO_RESULT_OK)
+}
+
+extern "C" fn read_is_device_unlocked(
+ _ops: *mut AvbOps,
+ out_is_unlocked: *mut bool,
+) -> AvbIOResult {
+ if let Err(e) = is_not_null(out_is_unlocked) {
+ return e.into();
+ }
+ // SAFETY: It is safe as the raw pointer `out_is_unlocked` is a valid pointer.
+ unsafe {
+ *out_is_unlocked = false;
+ }
+ AvbIOResult::AVB_IO_RESULT_OK
+}
+
+extern "C" fn read_from_partition(
+ ops: *mut AvbOps,
+ partition: *const c_char,
+ offset: i64,
+ num_bytes: usize,
+ buffer: *mut c_void,
+ out_num_read: *mut usize,
+) -> AvbIOResult {
+ to_avb_io_result(try_read_from_partition(
+ ops,
+ partition,
+ offset,
+ num_bytes,
+ buffer,
+ out_num_read,
+ ))
+}
+
+fn try_read_from_partition(
+ ops: *mut AvbOps,
+ partition: *const c_char,
+ offset: i64,
+ num_bytes: usize,
+ buffer: *mut c_void,
+ out_num_read: *mut usize,
+) -> Result<(), AvbIOError> {
+ let ops = as_avbops_ref(ops)?;
+ let partition = ops.as_ref().get_partition(partition)?;
+ let buffer = to_nonnull(buffer)?;
+ // SAFETY: It is safe to copy the requested number of bytes to `buffer` as `buffer`
+ // is created to point to the `num_bytes` of bytes in memory.
+ let buffer_slice = unsafe { slice::from_raw_parts_mut(buffer.as_ptr() as *mut u8, num_bytes) };
+ copy_data_to_dst(partition, offset, buffer_slice)?;
+ let out_num_read = to_nonnull(out_num_read)?;
+ // SAFETY: The raw pointer `out_num_read` was created to point to a valid a `usize`
+ // and we checked it is nonnull.
+ unsafe {
+ *out_num_read.as_ptr() = buffer_slice.len();
+ }
+ Ok(())
+}
+
+fn copy_data_to_dst(src: &[u8], offset: i64, dst: &mut [u8]) -> Result<(), AvbIOError> {
+ let start = to_copy_start(offset, src.len()).ok_or(AvbIOError::InvalidValueSize)?;
+ let end = start.checked_add(dst.len()).ok_or(AvbIOError::InvalidValueSize)?;
+ dst.copy_from_slice(src.get(start..end).ok_or(AvbIOError::RangeOutsidePartition)?);
+ Ok(())
+}
+
+fn to_copy_start(offset: i64, len: usize) -> Option<usize> {
+ usize::try_from(offset)
+ .ok()
+ .or_else(|| isize::try_from(offset).ok().and_then(|v| len.checked_add_signed(v)))
+}
+
+extern "C" fn get_size_of_partition(
+ ops: *mut AvbOps,
+ partition: *const c_char,
+ out_size_num_bytes: *mut u64,
+) -> AvbIOResult {
+ to_avb_io_result(try_get_size_of_partition(ops, partition, out_size_num_bytes))
+}
+
+fn try_get_size_of_partition(
+ ops: *mut AvbOps,
+ partition: *const c_char,
+ out_size_num_bytes: *mut u64,
+) -> Result<(), AvbIOError> {
+ let ops = as_avbops_ref(ops)?;
+ let partition = ops.as_ref().get_partition(partition)?;
+ let partition_size =
+ u64::try_from(partition.len()).map_err(|_| AvbIOError::InvalidValueSize)?;
+ let out_size_num_bytes = to_nonnull(out_size_num_bytes)?;
+ // SAFETY: The raw pointer `out_size_num_bytes` was created to point to a valid a `u64`
+ // and we checked it is nonnull.
+ unsafe {
+ *out_size_num_bytes.as_ptr() = partition_size;
+ }
+ Ok(())
+}
+
+extern "C" fn read_rollback_index(
+ _ops: *mut AvbOps,
+ _rollback_index_location: usize,
+ _out_rollback_index: *mut u64,
+) -> AvbIOResult {
+ // Rollback protection is not yet implemented, but
+ // this method is required by `avb_slot_verify()`.
+ AvbIOResult::AVB_IO_RESULT_OK
+}
+
+extern "C" fn get_unique_guid_for_partition(
+ _ops: *mut AvbOps,
+ _partition: *const c_char,
+ _guid_buf: *mut c_char,
+ _guid_buf_size: usize,
+) -> AvbIOResult {
+ // This method is required by `avb_slot_verify()`.
+ AvbIOResult::AVB_IO_RESULT_OK
+}
+
+extern "C" fn validate_public_key_for_partition(
+ ops: *mut AvbOps,
+ partition: *const c_char,
+ public_key_data: *const u8,
+ public_key_length: usize,
+ public_key_metadata: *const u8,
+ public_key_metadata_length: usize,
+ out_is_trusted: *mut bool,
+ out_rollback_index_location: *mut u32,
+) -> AvbIOResult {
+ to_avb_io_result(try_validate_public_key_for_partition(
+ ops,
+ partition,
+ public_key_data,
+ public_key_length,
+ public_key_metadata,
+ public_key_metadata_length,
+ out_is_trusted,
+ out_rollback_index_location,
+ ))
+}
+
+#[allow(clippy::too_many_arguments)]
+fn try_validate_public_key_for_partition(
+ ops: *mut AvbOps,
+ partition: *const c_char,
+ public_key_data: *const u8,
+ public_key_length: usize,
+ _public_key_metadata: *const u8,
+ _public_key_metadata_length: usize,
+ out_is_trusted: *mut bool,
+ _out_rollback_index_location: *mut u32,
+) -> Result<(), AvbIOError> {
+ is_not_null(public_key_data)?;
+ // SAFETY: It is safe to create a slice with the given pointer and length as
+ // `public_key_data` is a valid pointer and it points to an array of length
+ // `public_key_length`.
+ let public_key = unsafe { slice::from_raw_parts(public_key_data, public_key_length) };
+ let ops = as_avbops_ref(ops)?;
+ // Verifies the public key for the known partitions only.
+ ops.as_ref().get_partition(partition)?;
+ let trusted_public_key = ops.as_ref().trusted_public_key;
+ let out_is_trusted = to_nonnull(out_is_trusted)?;
+ // SAFETY: It is safe as the raw pointer `out_is_trusted` is a nonnull pointer.
+ unsafe {
+ *out_is_trusted.as_ptr() = public_key == trusted_public_key;
+ }
+ Ok(())
+}
+
+fn as_avbops_ref<'a>(ops: *mut AvbOps) -> Result<&'a AvbOps, AvbIOError> {
+ let ops = to_nonnull(ops)?;
+ // SAFETY: It is safe as the raw pointer `ops` is a nonnull pointer.
+ unsafe { Ok(ops.as_ref()) }
+}
+
+fn to_nonnull<T>(p: *mut T) -> Result<NonNull<T>, AvbIOError> {
+ NonNull::new(p).ok_or(AvbIOError::NoSuchValue)
+}
+
+fn is_not_null<T>(ptr: *const T) -> Result<(), AvbIOError> {
+ if ptr.is_null() {
+ Err(AvbIOError::NoSuchValue)
+ } else {
+ Ok(())
+ }
+}
+
+struct Payload<'a> {
+ kernel: &'a [u8],
+ trusted_public_key: &'a [u8],
+}
+
+impl<'a> AsRef<Payload<'a>> for AvbOps {
+ fn as_ref(&self) -> &Payload<'a> {
+ let payload = self.user_data as *const Payload;
+ // SAFETY: It is safe to cast the `AvbOps.user_data` to Payload as we have saved a
+ // pointer to a valid value of Payload in user_data when creating AvbOps, and
+ // assume that the Payload isn't used beyond the lifetime of the AvbOps that it
+ // belongs to.
+ unsafe { &*payload }
+ }
+}
+
+impl<'a> Payload<'a> {
+ const KERNEL_PARTITION_NAME: &[u8] = b"bootloader\0";
+
+ fn kernel_partition_name(&self) -> &CStr {
+ CStr::from_bytes_with_nul(Self::KERNEL_PARTITION_NAME).unwrap()
+ }
+
+ fn get_partition(&self, partition_name: *const c_char) -> Result<&[u8], AvbIOError> {
+ is_not_null(partition_name)?;
+ // SAFETY: It is safe as the raw pointer `partition_name` is a nonnull pointer.
+ let partition_name = unsafe { CStr::from_ptr(partition_name) };
+ match partition_name.to_bytes_with_nul() {
+ Self::KERNEL_PARTITION_NAME => Ok(self.kernel),
+ _ => Err(AvbIOError::NoSuchPartition),
+ }
+ }
+}
+
/// Verifies the payload (signed kernel + initrd) against the trusted public key.
-pub fn verify_payload(_public_key: &[u8]) -> Result<(), AvbImageVerifyError> {
- // TODO(b/256148034): Verify the kernel image with avb_slot_verify()
- // let result = unsafe {
- // avb_slot_verify(
- // &mut avb_ops,
- // requested_partitions.as_ptr(),
- // ab_suffix.as_ptr(),
- // flags,
- // hashtree_error_mode,
- // null_mut(),
- // )
- // };
- let result = AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_OK;
+pub fn verify_payload(kernel: &[u8], trusted_public_key: &[u8]) -> Result<(), AvbImageVerifyError> {
+ let mut payload = Payload { kernel, trusted_public_key };
+ let mut avb_ops = AvbOps {
+ user_data: &mut payload as *mut _ as *mut c_void,
+ ab_ops: ptr::null_mut(),
+ atx_ops: ptr::null_mut(),
+ read_from_partition: Some(read_from_partition),
+ get_preloaded_partition: None,
+ write_to_partition: None,
+ validate_vbmeta_public_key: None,
+ read_rollback_index: Some(read_rollback_index),
+ write_rollback_index: None,
+ read_is_device_unlocked: Some(read_is_device_unlocked),
+ get_unique_guid_for_partition: Some(get_unique_guid_for_partition),
+ get_size_of_partition: Some(get_size_of_partition),
+ read_persistent_value: None,
+ write_persistent_value: None,
+ validate_public_key_for_partition: Some(validate_public_key_for_partition),
+ };
+ // NULL is needed to mark the end of the array.
+ let requested_partitions: [*const c_char; 2] =
+ [payload.kernel_partition_name().as_ptr(), ptr::null()];
+ let ab_suffix = CStr::from_bytes_with_nul(b"\0").unwrap();
+
+ // SAFETY: It is safe to call `avb_slot_verify()` as the pointer arguments (`ops`,
+ // `requested_partitions` and `ab_suffix`) passed to the method are all valid and
+ // initialized. The last argument `out_data` is allowed to be null so that nothing
+ // will be written to it.
+ let result = unsafe {
+ avb_slot_verify(
+ &mut avb_ops,
+ requested_partitions.as_ptr(),
+ ab_suffix.as_ptr(),
+ AvbSlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION,
+ AvbHashtreeErrorMode::AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
+ /*out_data=*/ ptr::null_mut(),
+ )
+ };
to_avb_verify_result(result)
}
#[cfg(test)]
mod tests {
use super::*;
+ use anyhow::Result;
+ use std::fs;
- // TODO(b/256148034): Test verification succeeds with valid payload later.
+ const PUBLIC_KEY_RSA2048_PATH: &str = "data/testkey_rsa2048_pub.bin";
+ const PUBLIC_KEY_RSA4096_PATH: &str = "data/testkey_rsa4096_pub.bin";
+
+ /// This test uses the Microdroid payload compiled on the fly to check that
+ /// the latest payload can be verified successfully.
#[test]
- fn verification_succeeds_with_placeholder_input() {
- let fake_public_key = [0u8; 2];
- assert!(verify_payload(&fake_public_key).is_ok());
+ fn latest_valid_payload_is_verified_successfully() -> Result<()> {
+ let kernel = load_latest_signed_kernel()?;
+ let public_key = fs::read(PUBLIC_KEY_RSA4096_PATH)?;
+
+ assert_eq!(Ok(()), verify_payload(&kernel, &public_key));
+ Ok(())
+ }
+
+ #[test]
+ fn payload_with_empty_public_key_fails_verification() -> Result<()> {
+ assert_payload_verification_fails(
+ &load_latest_signed_kernel()?,
+ /*trusted_public_key=*/ &[0u8; 0],
+ AvbImageVerifyError::PublicKeyRejected,
+ )
+ }
+
+ #[test]
+ fn payload_with_an_invalid_public_key_fails_verification() -> Result<()> {
+ assert_payload_verification_fails(
+ &load_latest_signed_kernel()?,
+ /*trusted_public_key=*/ &[0u8; 512],
+ AvbImageVerifyError::PublicKeyRejected,
+ )
+ }
+
+ #[test]
+ fn payload_with_a_different_valid_public_key_fails_verification() -> Result<()> {
+ assert_payload_verification_fails(
+ &load_latest_signed_kernel()?,
+ &fs::read(PUBLIC_KEY_RSA2048_PATH)?,
+ AvbImageVerifyError::PublicKeyRejected,
+ )
+ }
+
+ #[test]
+ fn unsigned_kernel_fails_verification() -> Result<()> {
+ assert_payload_verification_fails(
+ &fs::read("unsigned_test.img")?,
+ &fs::read(PUBLIC_KEY_RSA4096_PATH)?,
+ AvbImageVerifyError::Io,
+ )
+ }
+
+ #[test]
+ fn tampered_kernel_fails_verification() -> Result<()> {
+ let mut kernel = load_latest_signed_kernel()?;
+ kernel[1] = !kernel[1]; // Flip the bits
+
+ assert_payload_verification_fails(
+ &kernel,
+ &fs::read(PUBLIC_KEY_RSA4096_PATH)?,
+ AvbImageVerifyError::Verification,
+ )
+ }
+
+ fn assert_payload_verification_fails(
+ kernel: &[u8],
+ trusted_public_key: &[u8],
+ expected_error: AvbImageVerifyError,
+ ) -> Result<()> {
+ assert_eq!(Err(expected_error), verify_payload(kernel, trusted_public_key));
+ Ok(())
+ }
+
+ fn load_latest_signed_kernel() -> Result<Vec<u8>> {
+ Ok(fs::read("microdroid_kernel")?)
}
}
diff --git a/pvmfw/src/entry.rs b/pvmfw/src/entry.rs
index 1b35c79..e979a95 100644
--- a/pvmfw/src/entry.rs
+++ b/pvmfw/src/entry.rs
@@ -47,6 +47,7 @@
/// The provided ramdisk was invalid.
InvalidRamdisk,
/// Failed to verify the payload.
+ #[allow(dead_code)]
PayloadVerificationError,
}
diff --git a/pvmfw/src/heap.rs b/pvmfw/src/heap.rs
index bfa8320..eab3bc4 100644
--- a/pvmfw/src/heap.rs
+++ b/pvmfw/src/heap.rs
@@ -14,6 +14,14 @@
//! Heap implementation.
+use core::alloc::GlobalAlloc as _;
+use core::alloc::Layout;
+use core::ffi::c_void;
+use core::mem;
+use core::num::NonZeroUsize;
+use core::ptr;
+use core::ptr::NonNull;
+
use buddy_system_allocator::LockedHeap;
#[global_allocator]
@@ -24,3 +32,32 @@
pub unsafe fn init() {
HEAP_ALLOCATOR.lock().init(HEAP.as_mut_ptr() as usize, HEAP.len());
}
+
+#[no_mangle]
+unsafe extern "C" fn malloc(size: usize) -> *mut c_void {
+ malloc_(size).map_or(ptr::null_mut(), |p| p.cast::<c_void>().as_ptr())
+}
+
+#[no_mangle]
+unsafe extern "C" fn free(ptr: *mut c_void) {
+ if let Some(ptr) = NonNull::new(ptr).map(|p| p.cast::<usize>().as_ptr().offset(-1)) {
+ if let Some(size) = NonZeroUsize::new(*ptr) {
+ if let Some(layout) = malloc_layout(size) {
+ HEAP_ALLOCATOR.dealloc(ptr as *mut u8, layout);
+ }
+ }
+ }
+}
+
+unsafe fn malloc_(size: usize) -> Option<NonNull<usize>> {
+ let size = NonZeroUsize::new(size)?.checked_add(mem::size_of::<usize>())?;
+ let ptr = HEAP_ALLOCATOR.alloc(malloc_layout(size)?);
+ let ptr = NonNull::new(ptr)?.cast::<usize>().as_ptr();
+ *ptr = size.get();
+ NonNull::new(ptr.offset(1))
+}
+
+fn malloc_layout(size: NonZeroUsize) -> Option<Layout> {
+ const ALIGN: usize = mem::size_of::<u64>();
+ Layout::from_size_align(size.get(), ALIGN).ok()
+}
diff --git a/pvmfw/src/main.rs b/pvmfw/src/main.rs
index e610e31..4d1ddfe 100644
--- a/pvmfw/src/main.rs
+++ b/pvmfw/src/main.rs
@@ -34,7 +34,7 @@
mod smccc;
use crate::{
- avb::PUBLIC_KEY,
+ avb::PUBLIC_KEY, // Keep the public key here otherwise the signing script will be broken.
entry::RebootReason,
memory::MemoryTracker,
pci::{find_virtio_devices, map_mmio},
@@ -43,7 +43,6 @@
use fdtpci::{PciError, PciInfo};
use libfdt::Fdt;
use log::{debug, error, info, trace};
-use pvmfw_avb::verify_payload;
fn main(
fdt: &Fdt,
@@ -55,6 +54,7 @@
info!("pVM firmware");
debug!("FDT: {:?}", fdt as *const libfdt::Fdt);
debug!("Signed kernel: {:?} ({:#x} bytes)", signed_kernel.as_ptr(), signed_kernel.len());
+ debug!("AVB public key: addr={:?}, size={:#x} ({1})", PUBLIC_KEY.as_ptr(), PUBLIC_KEY.len());
if let Some(rd) = ramdisk {
debug!("Ramdisk: {:?} ({:#x} bytes)", rd.as_ptr(), rd.len());
} else {
@@ -71,10 +71,6 @@
let mut pci_root = unsafe { pci_info.make_pci_root() };
find_virtio_devices(&mut pci_root).map_err(handle_pci_error)?;
- verify_payload(PUBLIC_KEY).map_err(|e| {
- error!("Failed to verify the payload: {e}");
- RebootReason::PayloadVerificationError
- })?;
info!("Starting payload...");
Ok(())
}
diff --git a/vmbase/src/bionic.rs b/vmbase/src/bionic.rs
index 8b3a076..b4a2f7b 100644
--- a/vmbase/src/bionic.rs
+++ b/vmbase/src/bionic.rs
@@ -14,6 +14,11 @@
//! Low-level compatibility layer between baremetal Rust and Bionic C functions.
+use core::ffi::c_char;
+use core::ffi::c_int;
+use core::ffi::CStr;
+
+use crate::eprintln;
use crate::linker;
/// Reference to __stack_chk_guard.
@@ -23,3 +28,37 @@
extern "C" fn __stack_chk_fail() -> ! {
panic!("stack guard check failed");
}
+
+/// Called from C to cause abnormal program termination.
+#[no_mangle]
+extern "C" fn abort() -> ! {
+ panic!("C code called abort()")
+}
+
+/// Error number set and read by C functions.
+pub static mut ERRNO: c_int = 0;
+
+#[no_mangle]
+unsafe extern "C" fn __errno() -> *mut c_int {
+ &mut ERRNO as *mut _
+}
+
+/// Reports a fatal error detected by Bionic.
+///
+/// # Safety
+///
+/// Input strings `prefix` and `format` must be properly NULL-terminated.
+///
+/// # Note
+///
+/// This Rust functions is missing the last argument of its C/C++ counterpart, a va_list.
+#[no_mangle]
+unsafe extern "C" fn async_safe_fatal_va_list(prefix: *const c_char, format: *const c_char) {
+ let prefix = CStr::from_ptr(prefix);
+ let format = CStr::from_ptr(format);
+
+ if let (Ok(prefix), Ok(format)) = (prefix.to_str(), format.to_str()) {
+ // We don't bother with printf formatting.
+ eprintln!("FATAL BIONIC ERROR: {prefix}: \"{format}\" (unformatted)");
+ }
+}