Merge "Sync condition of microdroid_vbmeta with microdroid_super" into main
diff --git a/libs/bssl/src/err.rs b/libs/bssl/src/err.rs
index 60bab98..a53ac8c 100644
--- a/libs/bssl/src/err.rs
+++ b/libs/bssl/src/err.rs
@@ -14,27 +14,90 @@
//! Wrappers of the error handling functions in BoringSSL err.h.
+use alloc::string::{String, ToString};
use bssl_avf_error::{CipherError, EcError, EcdsaError, GlobalError, ReasonCode};
-use bssl_sys::{self, ERR_get_error, ERR_GET_LIB_RUST, ERR_GET_REASON_RUST};
+use bssl_sys::{
+ self, ERR_get_error_line, ERR_lib_error_string, ERR_reason_error_string, ERR_GET_LIB_RUST,
+ ERR_GET_REASON_RUST,
+};
+use core::ffi::{c_char, CStr};
+use core::ptr;
+use log::{error, info};
const NO_ERROR_REASON_CODE: i32 = 0;
-/// Returns the reason code for the least recent error and removes that
-/// error from the error queue.
-pub(crate) fn get_error_reason_code() -> ReasonCode {
- let packed_error = get_packed_error();
+/// Processes the error queue till it is empty, logs the information for all the errors in
+/// the queue from the least recent to the most recent, and returns the reason code for the
+/// most recent error.
+pub(crate) fn process_error_queue() -> ReasonCode {
+ let mut reason_code = ReasonCode::NoError;
+ loop {
+ let code = process_least_recent_error();
+ if code == ReasonCode::NoError {
+ break;
+ }
+ reason_code = code;
+ }
+ reason_code
+}
+
+/// Removes the least recent error in the error queue and logs the error information.
+///
+/// Returns the reason code for the least recent error.
+fn process_least_recent_error() -> ReasonCode {
+ let mut file = ptr::null();
+ let mut line = 0;
+ // SAFETY: This function only reads the error queue and writes to the given
+ // pointers. It doesn't retain any references to the pointers.
+ let packed_error = unsafe { ERR_get_error_line(&mut file, &mut line) };
let reason = get_reason(packed_error);
+ if reason == NO_ERROR_REASON_CODE {
+ info!("No error in the BoringSSL error queue");
+ return ReasonCode::NoError;
+ }
+
+ // SAFETY: Any non-null result is expected to point to a global const C string.
+ let file = unsafe { cstr_to_string(file, "<unknown file>") };
+ error!(
+ "BoringSSL error: {}:{}: lib = {}, reason = {}",
+ file,
+ line,
+ lib_error_string(packed_error),
+ reason_error_string(packed_error),
+ );
+
let lib = get_lib(packed_error);
map_to_reason_code(reason, lib)
}
-/// Returns the packed error code for the least recent error and removes that
-/// error from the error queue.
+fn lib_error_string(packed_error: u32) -> String {
+ // SAFETY: This function only reads the given error code and returns a
+ // pointer to a static string.
+ let p = unsafe { ERR_lib_error_string(packed_error) };
+ // SAFETY: Any non-null result is expected to point to a global const C string.
+ unsafe { cstr_to_string(p, "<unknown library>") }
+}
+
+fn reason_error_string(packed_error: u32) -> String {
+ // SAFETY: This function only reads the given error code and returns a
+ // pointer to a static string.
+ let p = unsafe { ERR_reason_error_string(packed_error) };
+ // SAFETY: Any non-null result is expected to point to a global const C string.
+ unsafe { cstr_to_string(p, "<unknown reason>") }
+}
+
+/// Converts a C string pointer to a Rust string.
///
-/// Returns 0 if there are no errors in the queue.
-fn get_packed_error() -> u32 {
- // SAFETY: This function only reads the error queue.
- unsafe { ERR_get_error() }
+/// # Safety
+///
+/// The caller needs to ensure that the pointer is null or points to a valid C string.
+unsafe fn cstr_to_string(p: *const c_char, default: &str) -> String {
+ if p.is_null() {
+ return default.to_string();
+ }
+ // Safety: Safe given the requirements of this function.
+ let s = unsafe { CStr::from_ptr(p) };
+ s.to_str().unwrap_or(default).to_string()
}
fn get_reason(packed_error: u32) -> i32 {
diff --git a/libs/bssl/src/util.rs b/libs/bssl/src/util.rs
index 880c85b..ddb6c6b 100644
--- a/libs/bssl/src/util.rs
+++ b/libs/bssl/src/util.rs
@@ -14,14 +14,14 @@
//! Utility functions.
-use crate::err::get_error_reason_code;
+use crate::err::process_error_queue;
use bssl_avf_error::{ApiName, Error, Result};
use log::error;
pub(crate) fn check_int_result(ret: i32, api_name: ApiName) -> Result<()> {
match ret {
1 => Ok(()),
- 0 => Err(Error::CallFailed(api_name, get_error_reason_code())),
+ 0 => Err(Error::CallFailed(api_name, process_error_queue())),
_ => {
error!(
"Received a return value ({}) other than 0 or 1 from the BoringSSL API: {:?}",
@@ -33,5 +33,5 @@
}
pub(crate) fn to_call_failed_error(api_name: ApiName) -> Error {
- Error::CallFailed(api_name, get_error_reason_code())
+ Error::CallFailed(api_name, process_error_queue())
}
diff --git a/libs/cborutil/src/lib.rs b/libs/cborutil/src/lib.rs
index 6e834f1..4d308c1 100644
--- a/libs/cborutil/src/lib.rs
+++ b/libs/cborutil/src/lib.rs
@@ -21,7 +21,7 @@
use alloc::string::String;
use alloc::vec::Vec;
use ciborium::value::{Integer, Value};
-use coset::{CoseError, CoseKey, Label, Result};
+use coset::{CborSerializable, CoseError, CoseKey, Label, Result};
use log::error;
use serde::{de::DeserializeOwned, Serialize};
@@ -43,6 +43,11 @@
}
}
+/// Parses the given CBOR-encoded byte slice as a value array.
+pub fn parse_value_array(data: &[u8], context: &'static str) -> Result<Vec<Value>> {
+ value_to_array(Value::from_slice(data)?, context)
+}
+
/// Converts the provided value `v` to a value array.
pub fn value_to_array(v: Value, context: &'static str) -> Result<Vec<Value>> {
v.into_array().map_err(|e| to_unexpected_item_error(&e, "array", context))
diff --git a/libs/libfdt/src/iterators.rs b/libs/libfdt/src/iterators.rs
index a524655..7406164 100644
--- a/libs/libfdt/src/iterators.rs
+++ b/libs/libfdt/src/iterators.rs
@@ -330,7 +330,7 @@
}
impl<'a> DescendantsIterator<'a> {
- pub(crate) fn new(node: &'a FdtNode) -> Self {
+ pub(crate) fn new(node: &FdtNode<'a>) -> Self {
Self { node: Some((*node, 0)) }
}
}
diff --git a/libs/libfdt/src/lib.rs b/libs/libfdt/src/lib.rs
index 7eb08b2..d90f5f0 100644
--- a/libs/libfdt/src/lib.rs
+++ b/libs/libfdt/src/lib.rs
@@ -487,7 +487,7 @@
}
/// Returns an iterator of descendants
- pub fn descendants(&'a self) -> DescendantsIterator<'a> {
+ pub fn descendants(&self) -> DescendantsIterator<'a> {
DescendantsIterator::new(self)
}
@@ -811,6 +811,41 @@
Ok(next_offset.map(|offset| Self { fdt: self.fdt, offset }))
}
+ fn next_node_offset(&self, depth: usize) -> Result<Option<(c_int, usize)>> {
+ let mut next_depth: c_int = depth.try_into().or(Err(FdtError::BadValue))?;
+ // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
+ let ret = unsafe {
+ libfdt_bindgen::fdt_next_node(self.fdt.as_ptr(), self.offset, &mut next_depth)
+ };
+ let Ok(next_depth) = usize::try_from(next_depth) else {
+ return Ok(None);
+ };
+ Ok(fdt_err_or_option(ret)?.map(|offset| (offset, next_depth)))
+ }
+
+ /// Returns the next node
+ pub fn next_node(self, depth: usize) -> Result<Option<(Self, usize)>> {
+ Ok(self
+ .next_node_offset(depth)?
+ .map(|(offset, next_depth)| (FdtNodeMut { fdt: self.fdt, offset }, next_depth)))
+ }
+
+ /// Deletes this and returns the next node
+ pub fn delete_and_next_node(mut self, depth: usize) -> Result<Option<(Self, usize)>> {
+ // Skip all would-be-removed descendants.
+ let mut iter = self.next_node_offset(depth)?;
+ while let Some((descendant_offset, descendant_depth)) = iter {
+ if descendant_depth <= depth {
+ break;
+ }
+ let descendant = FdtNodeMut { fdt: self.fdt, offset: descendant_offset };
+ iter = descendant.next_node_offset(descendant_depth)?;
+ }
+ // SAFETY: This consumes self, so invalid node wouldn't be used any further
+ unsafe { self.nop_self()? };
+ Ok(iter.map(|(offset, next_depth)| (FdtNodeMut { fdt: self.fdt, offset }, next_depth)))
+ }
+
fn parent(&'a self) -> Result<FdtNode<'a>> {
// SAFETY: Accesses (read-only) are constrained to the DT totalsize.
let ret = unsafe { libfdt_bindgen::fdt_parent_offset(self.fdt.as_ptr(), self.offset) };
diff --git a/libs/libfdt/tests/api_test.rs b/libs/libfdt/tests/api_test.rs
index e68557f..08fb8a5 100644
--- a/libs/libfdt/tests/api_test.rs
+++ b/libs/libfdt/tests/api_test.rs
@@ -399,3 +399,94 @@
assert_eq!(expected_names, subnode_names);
}
+
+#[test]
+fn node_mut_delete_and_next_node() {
+ let mut data = fs::read(TEST_TREE_PHANDLE_PATH).unwrap();
+ let fdt = Fdt::from_mut_slice(&mut data).unwrap();
+
+ let expected_nodes = vec![
+ (Ok(cstr!("node_b")), 1),
+ (Ok(cstr!("node_c")), 1),
+ (Ok(cstr!("node_z")), 1),
+ (Ok(cstr!("node_za")), 2),
+ (Ok(cstr!("node_zb")), 2),
+ (Ok(cstr!("__symbols__")), 1),
+ ];
+
+ let mut expected_nodes_iter = expected_nodes.iter();
+ let mut iter = fdt.root_mut().unwrap().next_node(0).unwrap();
+ while let Some((node, depth)) = iter {
+ let node_name = node.as_node().name();
+ if node_name == Ok(cstr!("node_a")) || node_name == Ok(cstr!("node_zz")) {
+ iter = node.delete_and_next_node(depth).unwrap();
+ } else {
+ // Note: Checking name here is easier than collecting names and assert_eq!(),
+ // because we can't keep name references while iterating with FdtNodeMut.
+ let expected_node = expected_nodes_iter.next();
+ assert_eq!(expected_node, Some(&(node_name, depth)));
+ iter = node.next_node(depth).unwrap();
+ }
+ }
+ assert_eq!(None, expected_nodes_iter.next());
+
+ let root = fdt.root().unwrap();
+ let all_descendants: Vec<_> =
+ root.descendants().map(|(node, depth)| (node.name(), depth)).collect();
+ assert_eq!(expected_nodes, all_descendants);
+}
+
+#[test]
+#[ignore] // Borrow checker test. Compilation success is sufficient.
+fn node_name_lifetime() {
+ let data = fs::read(TEST_TREE_PHANDLE_PATH).unwrap();
+ let fdt = Fdt::from_slice(&data).unwrap();
+
+ let name = {
+ let root = fdt.root().unwrap();
+ root.name()
+ // Make root to be dropped
+ };
+ assert_eq!(Ok(cstr!("")), name);
+}
+
+#[test]
+#[ignore] // Borrow checker test. Compilation success is sufficient.
+fn node_subnode_lifetime() {
+ let data = fs::read(TEST_TREE_PHANDLE_PATH).unwrap();
+ let fdt = Fdt::from_slice(&data).unwrap();
+
+ let name = {
+ let node_a = {
+ let root = fdt.root().unwrap();
+ root.subnode(cstr!("node_a")).unwrap()
+ // Make root to be dropped
+ };
+ assert_ne!(None, node_a);
+ node_a.unwrap().name()
+ // Make node_a to be dropped
+ };
+ assert_eq!(Ok(cstr!("node_a")), name);
+}
+
+#[test]
+#[ignore] // Borrow checker test. Compilation success is sufficient.
+fn node_descendants_lifetime() {
+ let data = fs::read(TEST_TREE_PHANDLE_PATH).unwrap();
+ let fdt = Fdt::from_slice(&data).unwrap();
+
+ let first_descendant_name = {
+ let (first_descendant, _) = {
+ let mut descendants_iter = {
+ let root = fdt.root().unwrap();
+ root.descendants()
+ // Make root to be dropped
+ };
+ descendants_iter.next().unwrap()
+ // Make descendants_iter to be dropped
+ };
+ first_descendant.name()
+ // Make first_descendant to be dropped
+ };
+ assert_eq!(Ok(cstr!("node_a")), first_descendant_name);
+}
diff --git a/pvmfw/src/config.rs b/pvmfw/src/config.rs
index 3f78a88..7b548ce 100644
--- a/pvmfw/src/config.rs
+++ b/pvmfw/src/config.rs
@@ -84,6 +84,7 @@
const MAGIC: u32 = u32::from_ne_bytes(*b"pvmf");
const VERSION_1_0: Version = Version { major: 1, minor: 0 };
const VERSION_1_1: Version = Version { major: 1, minor: 1 };
+ const VERSION_1_2: Version = Version { major: 1, minor: 2 };
pub fn total_size(&self) -> usize {
self.total_size as usize
@@ -105,8 +106,9 @@
let last_entry = match self.version {
Self::VERSION_1_0 => Entry::DebugPolicy,
Self::VERSION_1_1 => Entry::VmDtbo,
+ Self::VERSION_1_2 => Entry::VmBaseDtbo,
v @ Version { major: 1, .. } => {
- const LATEST: Version = Header::VERSION_1_1;
+ const LATEST: Version = Header::VERSION_1_2;
warn!("Parsing unknown config data version {v} as version {LATEST}");
return Ok(Entry::COUNT);
}
@@ -122,6 +124,7 @@
Bcc,
DebugPolicy,
VmDtbo,
+ VmBaseDtbo,
#[allow(non_camel_case_types)] // TODO: Use mem::variant_count once stable.
_VARIANT_COUNT,
}
@@ -129,7 +132,8 @@
impl Entry {
const COUNT: usize = Self::_VARIANT_COUNT as usize;
- const ALL_ENTRIES: [Entry; Self::COUNT] = [Self::Bcc, Self::DebugPolicy, Self::VmDtbo];
+ const ALL_ENTRIES: [Entry; Self::COUNT] =
+ [Self::Bcc, Self::DebugPolicy, Self::VmDtbo, Self::VmBaseDtbo];
}
#[derive(Default)]
@@ -137,6 +141,7 @@
pub bcc: &'a mut [u8],
pub debug_policy: Option<&'a [u8]>,
pub vm_dtbo: Option<&'a mut [u8]>,
+ pub vm_base_dtbo: Option<&'a [u8]>,
}
#[repr(packed)]
@@ -285,13 +290,15 @@
entries[i] = Some(chunk);
}
}
- let [bcc, debug_policy, vm_dtbo] = entries;
+ let [bcc, debug_policy, vm_dtbo, vm_base_dtbo] = entries;
// The platform BCC has always been required.
let bcc = bcc.unwrap();
// We have no reason to mutate so drop the `mut`.
let debug_policy = debug_policy.map(|x| &*x);
- Entries { bcc, debug_policy, vm_dtbo }
+ let vm_base_dtbo = vm_base_dtbo.map(|x| &*x);
+
+ Entries { bcc, debug_policy, vm_dtbo, vm_base_dtbo }
}
}
diff --git a/pvmfw/src/entry.rs b/pvmfw/src/entry.rs
index 8c4396d..8eca7a1 100644
--- a/pvmfw/src/entry.rs
+++ b/pvmfw/src/entry.rs
@@ -88,6 +88,7 @@
kernel: usize,
kernel_size: usize,
vm_dtbo: Option<&mut [u8]>,
+ vm_base_dtbo: Option<&[u8]>,
) -> Result<Self, RebootReason> {
let fdt_size = NonZeroUsize::new(crosvm::FDT_MAX_SIZE).unwrap();
// TODO - Only map the FDT as read-only, until we modify it right before jump_to_payload()
@@ -101,7 +102,7 @@
// SAFETY: The tracker validated the range to be in main memory, mapped, and not overlap.
let fdt = unsafe { slice::from_raw_parts_mut(range.start as *mut u8, range.len()) };
- let info = fdt::sanitize_device_tree(fdt, vm_dtbo)?;
+ let info = fdt::sanitize_device_tree(fdt, vm_dtbo, vm_base_dtbo)?;
let fdt = libfdt::Fdt::from_mut_slice(fdt).map_err(|e| {
error!("Failed to load sanitized FDT: {e}");
RebootReason::InvalidFdt
@@ -227,7 +228,13 @@
Some(memory::appended_payload_range()),
));
- let slices = MemorySlices::new(fdt, payload, payload_size, config_entries.vm_dtbo)?;
+ let slices = MemorySlices::new(
+ fdt,
+ payload,
+ payload_size,
+ config_entries.vm_dtbo,
+ config_entries.vm_base_dtbo,
+ )?;
// This wrapper allows main() to be blissfully ignorant of platform details.
let next_bcc = crate::main(
diff --git a/pvmfw/src/fdt.rs b/pvmfw/src/fdt.rs
index b53e452..33a5055 100644
--- a/pvmfw/src/fdt.rs
+++ b/pvmfw/src/fdt.rs
@@ -19,6 +19,7 @@
use crate::helpers::GUEST_PAGE_SIZE;
use crate::Box;
use crate::RebootReason;
+use alloc::collections::BTreeMap;
use alloc::ffi::CString;
use alloc::vec::Vec;
use core::cmp::max;
@@ -200,27 +201,47 @@
Ok(())
}
-fn read_vendor_hashtree_descriptor_root_digest_from(fdt: &Fdt) -> libfdt::Result<Option<Vec<u8>>> {
+/// Read candidate properties' names from DT which could be overlaid
+fn parse_vm_base_dtbo(fdt: &Fdt) -> libfdt::Result<BTreeMap<CString, Vec<u8>>> {
+ let mut property_map = BTreeMap::new();
if let Some(avf_node) = fdt.node(cstr!("/avf"))? {
- if let Some(vendor_hashtree_descriptor_root_digest) =
- avf_node.getprop(cstr!("vendor_hashtree_descriptor_root_digest"))?
- {
- return Ok(Some(vendor_hashtree_descriptor_root_digest.to_vec()));
+ for property in avf_node.properties()? {
+ let name = property.name()?;
+ let value = property.value()?;
+ property_map.insert(
+ CString::new(name.to_bytes()).map_err(|_| FdtError::BadValue)?,
+ value.to_vec(),
+ );
}
}
- Ok(None)
+ Ok(property_map)
}
-fn patch_vendor_hashtree_descriptor_root_digest(
- fdt: &mut Fdt,
- vendor_hashtree_descriptor_root_digest: &[u8],
+/// Overlay VM base DTBO into VM DT based on the props_info. Property is overlaid in vm_dt only
+/// when it exists both in vm_base_dtbo and props_info. If the values mismatch, it returns error.
+fn apply_vm_base_dtbo(
+ vm_dt: &mut Fdt,
+ vm_base_dtbo: &Fdt,
+ props_info: &BTreeMap<CString, Vec<u8>>,
) -> libfdt::Result<()> {
- let mut root_node = fdt.root_mut()?;
- let mut avf_node = root_node.add_subnode(cstr!("/avf"))?;
- avf_node.setprop(
- cstr!("vendor_hashtree_descriptor_root_digest"),
- vendor_hashtree_descriptor_root_digest,
- )?;
+ let mut root_vm_dt = vm_dt.root_mut()?;
+ let mut avf_vm_dt = root_vm_dt.add_subnode(cstr!("avf"))?;
+ // TODO(b/318431677): Validate nodes beyond /fragment@0/__overlay__/avf and use apply_overlay.
+ let avf_vm_base_dtbo =
+ vm_base_dtbo.node(cstr!("/fragment@0/__overlay__/avf"))?.ok_or(FdtError::NotFound)?;
+ for (name, value) in props_info.iter() {
+ if let Some(value_in_vm_base_dtbo) = avf_vm_base_dtbo.getprop(name)? {
+ if value != value_in_vm_base_dtbo {
+ error!(
+ "Property mismatches while applying overlay VM base DTBO. \
+ Name:{:?}, Value from host as hex:{:x?}, Value from VM base DTBO as hex:{:x?}",
+ name, value, value_in_vm_base_dtbo
+ );
+ return Err(FdtError::BadValue);
+ }
+ avf_vm_dt.setprop(name, value_in_vm_base_dtbo)?;
+ }
+ }
Ok(())
}
@@ -616,7 +637,7 @@
serial_info: SerialInfo,
pub swiotlb_info: SwiotlbInfo,
device_assignment: Option<DeviceAssignmentInfo>,
- vendor_hashtree_descriptor_root_digest: Option<Vec<u8>>,
+ vm_base_dtbo_props_info: BTreeMap<CString, Vec<u8>>,
}
impl DeviceTreeInfo {
@@ -630,6 +651,7 @@
pub fn sanitize_device_tree(
fdt: &mut [u8],
vm_dtbo: Option<&mut [u8]>,
+ vm_base_dtbo: Option<&[u8]>,
) -> Result<DeviceTreeInfo, RebootReason> {
let fdt = Fdt::from_mut_slice(fdt).map_err(|e| {
error!("Failed to load FDT: {e}");
@@ -673,6 +695,18 @@
}
}
+ if let Some(vm_base_dtbo) = vm_base_dtbo {
+ let vm_base_dtbo = Fdt::from_slice(vm_base_dtbo).map_err(|e| {
+ error!("Failed to load VM base DTBO: {e}");
+ RebootReason::InvalidFdt
+ })?;
+
+ apply_vm_base_dtbo(fdt, vm_base_dtbo, &info.vm_base_dtbo_props_info).map_err(|e| {
+ error!("Failed to apply VM base DTBO: {e}");
+ RebootReason::InvalidFdt
+ })?;
+ }
+
patch_device_tree(fdt, &info)?;
// TODO(b/317201360): Ensure no overlapping in <reg> among devices
@@ -746,19 +780,10 @@
None => None,
};
- // TODO(b/285854379) : A temporary solution lives. This is for enabling
- // microdroid vendor partition for non-protected VM as well. When passing
- // DT path containing vendor_hashtree_descriptor_root_digest via fstab, init
- // stage will check if vendor_hashtree_descriptor_root_digest exists in the
- // init stage, regardless the protection. Adding this temporary solution
- // will prevent fatal in init stage for protected VM. However, this data is
- // not trustable without validating root digest of vendor hashtree
- // descriptor comes from ABL.
- let vendor_hashtree_descriptor_root_digest =
- read_vendor_hashtree_descriptor_root_digest_from(fdt).map_err(|e| {
- error!("Failed to read vendor_hashtree_descriptor_root_digest from DT: {e}");
- RebootReason::InvalidFdt
- })?;
+ let vm_base_dtbo_props_info = parse_vm_base_dtbo(fdt).map_err(|e| {
+ error!("Failed to read names of properties under /avf from DT: {e}");
+ RebootReason::InvalidFdt
+ })?;
Ok(DeviceTreeInfo {
kernel_range,
@@ -770,7 +795,7 @@
serial_info,
swiotlb_info,
device_assignment,
- vendor_hashtree_descriptor_root_digest,
+ vm_base_dtbo_props_info,
})
}
@@ -823,15 +848,6 @@
RebootReason::InvalidFdt
})?;
}
- if let Some(vendor_hashtree_descriptor_root_digest) =
- &info.vendor_hashtree_descriptor_root_digest
- {
- patch_vendor_hashtree_descriptor_root_digest(fdt, vendor_hashtree_descriptor_root_digest)
- .map_err(|e| {
- error!("Failed to patch vendor_hashtree_descriptor_root_digest to DT: {e}");
- RebootReason::InvalidFdt
- })?;
- }
Ok(())
}
diff --git a/service_vm/requests/src/client_vm.rs b/service_vm/requests/src/client_vm.rs
index 6ebed50..d4474cf 100644
--- a/service_vm/requests/src/client_vm.rs
+++ b/service_vm/requests/src/client_vm.rs
@@ -22,7 +22,7 @@
use crate::keyblob::decrypt_private_key;
use alloc::vec::Vec;
use bssl_avf::{rand_bytes, sha256, Digester, EcKey, PKey};
-use cbor_util::value_to_array;
+use cbor_util::parse_value_array;
use ciborium::value::Value;
use core::result;
use coset::{AsCborValue, CborSerializable, CoseSign, CoseSign1};
@@ -53,10 +53,8 @@
// Validates the prefix of the Client VM DICE chain in the CSR.
let service_vm_dice_chain =
dice_artifacts.bcc().ok_or(RequestProcessingError::MissingDiceChain)?;
- let service_vm_dice_chain =
- value_to_array(Value::from_slice(service_vm_dice_chain)?, "service_vm_dice_chain")?;
- let client_vm_dice_chain =
- value_to_array(Value::from_slice(&csr.dice_cert_chain)?, "client_vm_dice_chain")?;
+ let service_vm_dice_chain = parse_value_array(service_vm_dice_chain, "service_vm_dice_chain")?;
+ let client_vm_dice_chain = parse_value_array(&csr.dice_cert_chain, "client_vm_dice_chain")?;
validate_client_vm_dice_chain_prefix_match(&client_vm_dice_chain, &service_vm_dice_chain)?;
// Validates the signatures in the Client VM DICE chain and extracts the partially decoded
// DiceChainEntryPayloads.
diff --git a/tests/benchmark/assets/vm_config_gki-android14-6.1.json b/tests/benchmark/assets/vm_config_gki-android14-6.1.json
new file mode 100644
index 0000000..c4fdc6e
--- /dev/null
+++ b/tests/benchmark/assets/vm_config_gki-android14-6.1.json
@@ -0,0 +1,10 @@
+{
+ "os": {
+ "name": "microdroid_gki-android14-6.1"
+ },
+ "task": {
+ "type": "microdroid_launcher",
+ "command": "MicrodroidIdleNativeLib.so"
+ },
+ "export_tombstones": true
+}
diff --git a/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java b/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
index 5d3ce9e..e31a55d 100644
--- a/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
+++ b/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
@@ -248,6 +248,16 @@
}
@Test
+ public void testMicrodroidGkiBootTime()
+ throws VirtualMachineException, InterruptedException, IOException {
+ runBootTimeTest(
+ "test_vm_boot_time",
+ "assets/vm_config_gki-android14-6.1.json",
+ /* reportDetailed */ false,
+ (builder) -> builder.setCpuTopology(CPU_TOPOLOGY_ONE_CPU));
+ }
+
+ @Test
public void testMicrodroidHostCpuTopologyBootTime()
throws VirtualMachineException, InterruptedException, IOException {
runBootTimeTest(
@@ -258,6 +268,16 @@
}
@Test
+ public void testMicrodroidGkiHostCpuTopologyBootTime()
+ throws VirtualMachineException, InterruptedException, IOException {
+ runBootTimeTest(
+ "test_vm_boot_time_host_topology",
+ "assets/vm_config_gki-android14-6.1.json",
+ /* reportDetailed */ false,
+ (builder) -> builder.setCpuTopology(CPU_TOPOLOGY_MATCH_HOST));
+ }
+
+ @Test
public void testMicrodroidDebugBootTime()
throws VirtualMachineException, InterruptedException, IOException {
runBootTimeTest(
@@ -268,6 +288,16 @@
}
@Test
+ public void testMicrodroidGkiDebugBootTime()
+ throws VirtualMachineException, InterruptedException, IOException {
+ runBootTimeTest(
+ "test_vm_boot_time_debug",
+ "assets/vm_config_gki-android14-6.1.json",
+ /* reportDetailed */ true,
+ (builder) -> builder);
+ }
+
+ @Test
public void testMicrodroidDebugBootTime_withVendorPartition() throws Exception {
assume().withMessage(
"Cuttlefish doesn't support device tree under"
diff --git a/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java b/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
index f5656e2..b176cfc 100644
--- a/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
+++ b/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
@@ -45,7 +45,6 @@
import org.junit.runner.RunWith;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
@@ -413,11 +412,16 @@
for (int round = 0; round < ROUND_COUNT; ++round) {
reInstallApex(REINSTALL_APEX_TIMEOUT_SEC);
- if (isWithCompos) {
- compileStagedApex(COMPILE_STAGED_APEX_TIMEOUT_SEC);
+ try {
+ if (isWithCompos) {
+ compileStagedApex(COMPILE_STAGED_APEX_TIMEOUT_SEC);
+ }
+ } finally {
+ // If compilation fails, we still have a staged APEX, and we need to reboot to
+ // clean that up for further tests.
+ getDevice().nonBlockingReboot();
+ waitForBootCompleted();
}
- getDevice().nonBlockingReboot();
- waitForBootCompleted();
double elapsedSec = getDmesgBootTime();
bootDmesgTime.add(elapsedSec);
@@ -458,8 +462,9 @@
try {
CommandRunner android = new CommandRunner(getDevice());
- String result = android.run(
- COMPOSD_CMD_BIN + " staged-apex-compile");
+ String result =
+ android.runWithTimeout(
+ 3 * 60 * 1000, COMPOSD_CMD_BIN + " staged-apex-compile");
assertWithMessage("Failed to compile staged APEX. Reason: " + result)
.that(result).ignoringCase().contains("all ok");
diff --git a/tests/hostside/helper/java/com/android/microdroid/test/host/CommandRunner.java b/tests/hostside/helper/java/com/android/microdroid/test/host/CommandRunner.java
index 846531d..242dbde 100644
--- a/tests/hostside/helper/java/com/android/microdroid/test/host/CommandRunner.java
+++ b/tests/hostside/helper/java/com/android/microdroid/test/host/CommandRunner.java
@@ -66,9 +66,7 @@
public String runWithTimeout(long timeoutMillis, String... cmd)
throws DeviceNotAvailableException {
- CommandResult result =
- mDevice.executeShellV2Command(
- join(cmd), timeoutMillis, java.util.concurrent.TimeUnit.MILLISECONDS);
+ CommandResult result = runForResultWithTimeout(timeoutMillis, cmd);
if (result.getStatus() != CommandStatus.SUCCESS) {
fail(join(cmd) + " has failed: " + result);
}
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index a54a22a..1fa0976 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -435,7 +435,7 @@
@Test
@CddTest(requirements = {"9.17/C-2-1", "9.17/C-2-2", "9.17/C-2-6"})
- public void protectedVmWithImageSignedWithDifferentKeyRunsPvmfw() throws Exception {
+ public void protectedVmWithImageSignedWithDifferentKeyFailsToVerifyPayload() throws Exception {
// Arrange
assumeProtectedVm();
File key = findTestFile("test.com.android.virt.pem");
@@ -452,8 +452,9 @@
vmInfo.mProcess.waitFor(5L, TimeUnit.SECONDS);
String consoleLog = getDevice().pullFileContents(CONSOLE_PATH);
assertWithMessage("pvmfw should start").that(consoleLog).contains("pVM firmware");
- // TODO(b/256148034): Asserts that pvmfw run fails when this verification is implemented.
- // Also rename the test.
+ assertWithMessage("pvmfw should fail to verify the payload")
+ .that(consoleLog)
+ .contains("Failed to verify the payload");
vmInfo.mProcess.destroy();
}