pvmfw: Add support for /avf/untrusted
Introduce a way for the (untrusted) host to pass flags to the guest
kernel using the device tree without pvmfw stripping them out of the DT.
For now, the validation policy will be to ignore any subnode (only props
are supported) and properties that could be used to trick the guest
kernel.
Test: m pvmfw
Bug: 324046698
Change-Id: Id3500e7b42139fa647a1653931158043be0ed6ff
diff --git a/pvmfw/src/fdt.rs b/pvmfw/src/fdt.rs
index 311f467..146d012 100644
--- a/pvmfw/src/fdt.rs
+++ b/pvmfw/src/fdt.rs
@@ -59,6 +59,8 @@
InvalidCpuCount(usize),
/// Invalid VCpufreq Range.
InvalidVcpufreq(u64, u64),
+ /// Forbidden /avf/untrusted property.
+ ForbiddenUntrustedProp(&'static CStr),
}
impl fmt::Display for FdtValidationError {
@@ -68,6 +70,9 @@
Self::InvalidVcpufreq(addr, size) => {
write!(f, "Invalid vcpufreq region: ({addr:#x}, {size:#x})")
}
+ Self::ForbiddenUntrustedProp(name) => {
+ write!(f, "Forbidden /avf/untrusted property '{name:?}'")
+ }
}
}
}
@@ -420,6 +425,24 @@
Ok(())
}
+/// Reads the /avf/untrusted DT node, which the host can use to pass properties (no subnodes) to
+/// the guest that don't require being validated by pvmfw.
+fn parse_untrusted_props(fdt: &Fdt) -> libfdt::Result<BTreeMap<CString, Vec<u8>>> {
+ let mut props = BTreeMap::new();
+ if let Some(node) = fdt.node(cstr!("/avf/untrusted"))? {
+ for property in node.properties()? {
+ let name = property.name()?;
+ let value = property.value()?;
+ props.insert(CString::from(name), value.to_vec());
+ }
+ if node.subnodes()?.next().is_some() {
+ warn!("Discarding unexpected /avf/untrusted subnodes.");
+ }
+ }
+
+ Ok(props)
+}
+
/// Read candidate properties' names from DT which could be overlaid
fn parse_vm_ref_dt(fdt: &Fdt) -> libfdt::Result<BTreeMap<CString, Vec<u8>>> {
let mut property_map = BTreeMap::new();
@@ -436,6 +459,19 @@
Ok(property_map)
}
+fn validate_untrusted_props(props: &BTreeMap<CString, Vec<u8>>) -> Result<(), FdtValidationError> {
+ const FORBIDDEN_PROPS: &[&CStr] =
+ &[cstr!("compatible"), cstr!("linux,phandle"), cstr!("phandle")];
+
+ for name in FORBIDDEN_PROPS {
+ if props.contains_key(*name) {
+ return Err(FdtValidationError::ForbiddenUntrustedProp(name));
+ }
+ }
+
+ Ok(())
+}
+
/// Overlay VM reference DT into VM DT based on the props_info. Property is overlaid in vm_dt only
/// when it exists both in vm_ref_dt and props_info. If the values mismatch, it returns error.
fn validate_vm_ref_dt(
@@ -837,6 +873,23 @@
node.setprop_inplace(cstr!("interrupts"), value.as_bytes())
}
+fn patch_untrusted_props(fdt: &mut Fdt, props: &BTreeMap<CString, Vec<u8>>) -> libfdt::Result<()> {
+ let avf_node = if let Some(node) = fdt.node_mut(cstr!("/avf"))? {
+ node
+ } else {
+ fdt.root_mut()?.add_subnode(cstr!("avf"))?
+ };
+
+ // The node shouldn't already be present; if it is, return the error.
+ let mut node = avf_node.add_subnode(cstr!("untrusted"))?;
+
+ for (name, value) in props {
+ node.setprop(name, value)?;
+ }
+
+ Ok(())
+}
+
#[derive(Debug)]
struct VcpufreqInfo {
addr: u64,
@@ -864,6 +917,7 @@
serial_info: SerialInfo,
pub swiotlb_info: SwiotlbInfo,
device_assignment: Option<DeviceAssignmentInfo>,
+ untrusted_props: BTreeMap<CString, Vec<u8>>,
vm_ref_dt_props_info: BTreeMap<CString, Vec<u8>>,
vcpufreq_info: Option<VcpufreqInfo>,
}
@@ -1023,6 +1077,15 @@
None => None,
};
+ let untrusted_props = parse_untrusted_props(fdt).map_err(|e| {
+ error!("Failed to read untrusted properties: {e}");
+ RebootReason::InvalidFdt
+ })?;
+ validate_untrusted_props(&untrusted_props).map_err(|e| {
+ error!("Failed to validate untrusted properties: {e}");
+ RebootReason::InvalidFdt
+ })?;
+
let vm_ref_dt_props_info = parse_vm_ref_dt(fdt).map_err(|e| {
error!("Failed to read names of properties under /avf from DT: {e}");
RebootReason::InvalidFdt
@@ -1039,6 +1102,7 @@
serial_info,
swiotlb_info,
device_assignment,
+ untrusted_props,
vm_ref_dt_props_info,
vcpufreq_info,
})
@@ -1097,6 +1161,10 @@
RebootReason::InvalidFdt
})?;
}
+ patch_untrusted_props(fdt, &info.untrusted_props).map_err(|e| {
+ error!("Failed to patch untrusted properties: {e}");
+ RebootReason::InvalidFdt
+ })?;
Ok(())
}