Patch the template DT
The original DT from crosvm is now overwritten by the template DT, which
is then patched.
add_dice_node is modified to patch_dice_node as google,open-dice node is
already in the template DT and therefore we don't need to (actually
can't) add new one.
Bug: 249054080
Test: TH
Change-Id: Ie1c93b56af6afd1a7844478f514bc42db3b2cff0
diff --git a/libs/libfdt/src/lib.rs b/libs/libfdt/src/lib.rs
index a1a740b..7ddf680 100644
--- a/libs/libfdt/src/lib.rs
+++ b/libs/libfdt/src/lib.rs
@@ -21,6 +21,7 @@
pub use iterators::{AddressRange, CellIterator, MemRegIterator, RangesIterator, Reg, RegIterator};
+use core::cmp::max;
use core::ffi::{c_int, c_void, CStr};
use core::fmt;
use core::mem;
@@ -623,6 +624,21 @@
mem::transmute::<&mut [u8], &mut Self>(fdt)
}
+ /// Update this FDT from a slice containing another FDT
+ pub fn copy_from_slice(&mut self, new_fdt: &[u8]) -> Result<()> {
+ if self.buffer.len() < new_fdt.len() {
+ Err(FdtError::NoSpace)
+ } else {
+ let totalsize = self.totalsize();
+ self.buffer[..new_fdt.len()].clone_from_slice(new_fdt);
+ // Zeroize the remaining part. We zeroize up to the size of the original DT because
+ // zeroizing the entire buffer (max 2MB) is not necessary and may increase the VM boot
+ // time.
+ self.buffer[new_fdt.len()..max(new_fdt.len(), totalsize)].fill(0_u8);
+ Ok(())
+ }
+ }
+
/// Make the whole slice containing the DT available to libfdt.
pub fn unpack(&mut self) -> Result<()> {
// SAFETY - "Opens" the DT in-place (supported use-case) by updating its header and
diff --git a/pvmfw/src/fdt.rs b/pvmfw/src/fdt.rs
index d014be9..fca4583 100644
--- a/pvmfw/src/fdt.rs
+++ b/pvmfw/src/fdt.rs
@@ -19,6 +19,7 @@
use crate::helpers::GUEST_PAGE_SIZE;
use crate::helpers::SIZE_4KB;
use crate::RebootReason;
+use alloc::ffi::CString;
use core::ffi::CStr;
use core::mem::size_of;
use core::ops::Range;
@@ -76,6 +77,24 @@
Ok(())
}
+fn read_bootargs_from(fdt: &Fdt) -> libfdt::Result<Option<CString>> {
+ if let Some(chosen) = fdt.chosen()? {
+ if let Some(bootargs) = chosen.getprop_str(cstr!("bootargs"))? {
+ // We need to copy the string to heap because the original fdt will be invalidated
+ // by the templated DT
+ let copy = CString::new(bootargs.to_bytes()).map_err(|_| FdtError::BadValue)?;
+ return Ok(Some(copy));
+ }
+ }
+ Ok(None)
+}
+
+fn patch_bootargs(fdt: &mut Fdt, bootargs: &CStr) -> libfdt::Result<()> {
+ let mut node = fdt.chosen_mut()?.ok_or(FdtError::NotFound)?;
+ // TODO(b/275306568) filter out dangerous options
+ node.setprop(cstr!("bootargs"), bootargs.to_bytes_with_nul())
+}
+
/// Read the first range in /memory node in DT
fn read_memory_range_from(fdt: &Fdt) -> libfdt::Result<Range<usize>> {
fdt.memory()?.ok_or(FdtError::NotFound)?.next().ok_or(FdtError::NotFound)
@@ -490,6 +509,7 @@
pub kernel_range: Option<Range<usize>>,
pub initrd_range: Option<Range<usize>>,
pub memory_range: Range<usize>,
+ bootargs: Option<CString>,
num_cpus: usize,
pci_info: PciInfo,
serial_info: SerialInfo,
@@ -505,7 +525,11 @@
let info = parse_device_tree(fdt)?;
debug!("Device tree info: {:?}", info);
- // TODO: replace fdt with the template DT
+ fdt.copy_from_slice(pvmfw_fdt_template::RAW).map_err(|e| {
+ error!("Failed to instantiate FDT from the template DT: {e}");
+ RebootReason::InvalidFdt
+ })?;
+
patch_device_tree(fdt, &info)?;
Ok(info)
}
@@ -527,6 +551,11 @@
})?;
validate_memory_range(&memory_range)?;
+ let bootargs = read_bootargs_from(fdt).map_err(|e| {
+ error!("Failed to read bootargs from DT: {e}");
+ RebootReason::InvalidFdt
+ })?;
+
let num_cpus = read_num_cpus_from(fdt).map_err(|e| {
error!("Failed to read num cpus from DT: {e}");
RebootReason::InvalidFdt
@@ -554,6 +583,7 @@
kernel_range,
initrd_range,
memory_range,
+ bootargs,
num_cpus,
pci_info,
serial_info,
@@ -562,6 +592,11 @@
}
fn patch_device_tree(fdt: &mut Fdt, info: &DeviceTreeInfo) -> Result<(), RebootReason> {
+ fdt.unpack().map_err(|e| {
+ error!("Failed to unpack DT for patching: {e}");
+ RebootReason::InvalidFdt
+ })?;
+
if let Some(initrd_range) = &info.initrd_range {
patch_initrd_range(fdt, initrd_range).map_err(|e| {
error!("Failed to patch initrd range to DT: {e}");
@@ -572,6 +607,12 @@
error!("Failed to patch memory range to DT: {e}");
RebootReason::InvalidFdt
})?;
+ if let Some(bootargs) = &info.bootargs {
+ patch_bootargs(fdt, bootargs.as_c_str()).map_err(|e| {
+ error!("Failed to patch bootargs to DT: {e}");
+ RebootReason::InvalidFdt
+ })?;
+ }
patch_num_cpus(fdt, info.num_cpus).map_err(|e| {
error!("Failed to patch cpus to DT: {e}");
RebootReason::InvalidFdt
@@ -596,6 +637,12 @@
error!("Failed to patch timer info to DT: {e}");
RebootReason::InvalidFdt
})?;
+
+ fdt.pack().map_err(|e| {
+ error!("Failed to pack DT after patching: {e}");
+ RebootReason::InvalidFdt
+ })?;
+
Ok(())
}
@@ -608,7 +655,7 @@
) -> libfdt::Result<()> {
fdt.unpack()?;
- add_dice_node(fdt, bcc.as_ptr() as usize, bcc.len())?;
+ patch_dice_node(fdt, bcc.as_ptr() as usize, bcc.len())?;
set_or_clear_chosen_flag(fdt, cstr!("avf,strict-boot"), strict_boot)?;
set_or_clear_chosen_flag(fdt, cstr!("avf,new-instance"), new_instance)?;
@@ -618,24 +665,17 @@
Ok(())
}
-/// Add a "google,open-dice"-compatible reserved-memory node to the tree.
-fn add_dice_node(fdt: &mut Fdt, addr: usize, size: usize) -> libfdt::Result<()> {
+/// Patch the "google,open-dice"-compatible reserved-memory node to point to the bcc range
+fn patch_dice_node(fdt: &mut Fdt, addr: usize, size: usize) -> libfdt::Result<()> {
// We reject DTs with missing reserved-memory node as validation should have checked that the
// "swiotlb" subnode (compatible = "restricted-dma-pool") was present.
- let mut reserved_memory =
- fdt.node_mut(cstr!("/reserved-memory"))?.ok_or(libfdt::FdtError::NotFound)?;
+ let node = fdt.node_mut(cstr!("/reserved-memory"))?.ok_or(libfdt::FdtError::NotFound)?;
- let mut dice = reserved_memory.add_subnode(cstr!("dice"))?;
+ let mut node = node.next_compatible(cstr!("google,open-dice"))?.ok_or(FdtError::NotFound)?;
- dice.appendprop(cstr!("compatible"), b"google,open-dice\0")?;
-
- dice.appendprop(cstr!("no-map"), &[])?;
-
- let addr = addr.try_into().unwrap();
- let size = size.try_into().unwrap();
- dice.appendprop_addrrange(cstr!("reg"), addr, size)?;
-
- Ok(())
+ let addr: u64 = addr.try_into().unwrap();
+ let size: u64 = size.try_into().unwrap();
+ node.setprop_inplace(cstr!("reg"), flatten(&[addr.to_be_bytes(), size.to_be_bytes()]))
}
fn set_or_clear_chosen_flag(fdt: &mut Fdt, flag: &CStr, value: bool) -> libfdt::Result<()> {