Introduce VM base DTBO as pvmfw config v1.2
VM base DTBO will be constructed in ABL and passed into pvmfw via config
entry from v1.2. This change is required to control pvmfw config v1.2.
Properties in VM base DTBO will be sent to VM DT only when FDT come from
the host contains same property name.
Bug: 318431695
Test: adb shell /apex/com.android.virt/bin/vm run-microdroid --protected
Test: adb shell /apex/com.android.virt/bin/vm run-microdroid --protected --vendor /vendor/etc/avf/microdroid/microdroid_vendor.img
Test: adb -s <microdroid device> shell ls /sys/firmware/devicetree/base/avf
Test: adb -s <microdroid device> shell cat /sys/firmware/devicetree/base/avf/vendor_hashtree_descriptor_root_digest
Change-Id: Ia4889f61eb8975ead8dfe8c6d38abd94ba35f0ab
diff --git a/pvmfw/src/fdt.rs b/pvmfw/src/fdt.rs
index b53e452..44e55dd 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<&mut [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_mut_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(())
}