crosvm: Apply device tree overlays

This change propagates the overlay files to the `fdt::create_fdt`
function, load the file contents, and applies a series of device tree
overlays to the base device tree after it has been constructed.

Test: tools/run_tests
Bug: b/296796644
Change-Id: I01f4db604fba5b8ecd756f93d092432145357977
Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/4855968
Commit-Queue: Jakob Vukalović <jakobvukalovic@google.com>
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
This commit is contained in:
Jakob Vukalovic 2023-10-06 12:57:51 +01:00 committed by crosvm LUCI
parent d9bc6e99ff
commit d31b856040
9 changed files with 86 additions and 4 deletions

View file

@ -6,7 +6,9 @@ use std::collections::BTreeMap;
use std::fs::File;
use std::path::PathBuf;
use arch::apply_device_tree_overlays;
use arch::CpuSet;
use arch::DtbOverlay;
use arch::SERIAL_ADDR;
use cros_fdt::Error;
use cros_fdt::Fdt;
@ -207,6 +209,7 @@ fn create_gic_node(fdt: &mut Fdt, is_gicv3: bool, num_cpus: u64) -> Result<()> {
intc_node.set_prop("phandle", PHANDLE_GIC)?;
intc_node.set_prop("#address-cells", 2u32)?;
intc_node.set_prop("#size-cells", 2u32)?;
add_symbols_entry(fdt, "intc", "/intc")?;
Ok(())
}
@ -549,6 +552,22 @@ fn create_vmwdt_node(fdt: &mut Fdt, vmwdt_cfg: VmWdtConfig) -> Result<()> {
Ok(())
}
// Add a node path to __symbols__ node of the FDT, so it can be referenced by an overlay.
fn add_symbols_entry(fdt: &mut Fdt, symbol: &str, path: &str) -> Result<()> {
// Ensure the path points to a valid node with a defined phandle
let Some(target_node) = fdt.get_node(path) else {
return Err(Error::InvalidPath(format!("{path} does not exist")));
};
target_node
.get_prop::<u32>("phandle")
.or_else(|| target_node.get_prop("linux,phandle"))
.ok_or_else(|| Error::InvalidPath(format!("{path} must have a phandle")))?;
// Add the label -> path mapping.
let symbols_node = fdt.root_mut().subnode_mut("__symbols__")?;
symbols_node.set_prop(symbol, path)?;
Ok(())
}
/// Creates a flattened device tree containing all of the parameters for the
/// kernel and loads it into the guest memory at the specified offset.
///
@ -595,6 +614,7 @@ pub fn create_fdt(
dump_device_tree_blob: Option<PathBuf>,
vm_generator: &impl Fn(&mut Fdt, &BTreeMap<&str, u32>) -> cros_fdt::Result<()>,
dynamic_power_coefficient: BTreeMap<usize, u32>,
device_tree_overlays: Vec<DtbOverlay>,
) -> Result<()> {
let mut fdt = Fdt::new(&[]);
let mut phandles = BTreeMap::new();
@ -647,6 +667,9 @@ pub fn create_fdt(
create_virt_cpufreq_node(&mut fdt, num_cpus as u64)?;
}
// Done writing base FDT, now apply DT overlays
apply_device_tree_overlays(&mut fdt, device_tree_overlays)?;
let fdt_final = fdt.finish()?;
if let Some(file_path) = dump_device_tree_blob {
@ -714,4 +737,23 @@ mod tests {
vec!["arm,psci-1.0", "arm,psci-0.2"]
);
}
#[test]
fn symbols_entries() {
const TEST_SYMBOL: &str = "dev";
const TEST_PATH: &str = "/dev";
let mut fdt = Fdt::new(&[]);
add_symbols_entry(&mut fdt, TEST_SYMBOL, TEST_PATH).expect_err("missing node");
fdt.root_mut().subnode_mut(TEST_SYMBOL).unwrap();
add_symbols_entry(&mut fdt, TEST_SYMBOL, TEST_PATH).expect_err("missing phandle");
let intc_node = fdt.get_node_mut(TEST_PATH).unwrap();
intc_node.set_prop("phandle", 1u32).unwrap();
add_symbols_entry(&mut fdt, TEST_SYMBOL, TEST_PATH).expect("valid path");
let symbols = fdt.get_node("/__symbols__").unwrap();
assert_eq!(symbols.get_prop::<String>(TEST_SYMBOL).unwrap(), TEST_PATH);
}
}

View file

@ -397,7 +397,7 @@ impl arch::LinuxArch for AArch64 {
#[cfg(any(target_os = "android", target_os = "linux"))] _guest_suspended_cvar: Option<
Arc<(Mutex<bool>, Condvar)>,
>,
_device_tree_overlays: Vec<DtbOverlay>,
device_tree_overlays: Vec<DtbOverlay>,
) -> std::result::Result<RunnableLinuxVm<V, Vcpu>, Self::Error>
where
V: VmAArch64,
@ -751,6 +751,7 @@ impl arch::LinuxArch for AArch64 {
dump_device_tree_blob,
&|writer, phandles| vm.create_fdt(writer, phandles),
components.dynamic_power_coefficient,
device_tree_overlays,
)
.map_err(Error::CreateFdt)?;

View file

@ -3,6 +3,25 @@
// found in the LICENSE file.
use std::fs::File;
use std::io::Read;
use cros_fdt::apply_overlay;
use cros_fdt::Error;
use cros_fdt::Fdt;
use cros_fdt::Result;
/// Device tree overlay file
pub struct DtbOverlay(pub File);
/// Apply multiple device tree overlays to the base FDT.
pub fn apply_device_tree_overlays(fdt: &mut Fdt, overlays: Vec<DtbOverlay>) -> Result<()> {
for DtbOverlay(mut overlay_file) in overlays {
let mut buffer = Vec::new();
overlay_file
.read_to_end(&mut buffer)
.map_err(Error::FdtIoError)?;
let overlay = Fdt::from_blob(buffer.as_slice())?;
apply_overlay::<&str>(fdt, overlay, [])?;
}
Ok(())
}

View file

@ -56,6 +56,7 @@ use devices::ProxyDevice;
use devices::SerialHardware;
use devices::SerialParameters;
use devices::VirtioMmioDevice;
pub use fdt::apply_device_tree_overlays;
pub use fdt::DtbOverlay;
#[cfg(feature = "gdb")]
use gdbstub::arch::Arch;

View file

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use arch::apply_device_tree_overlays;
use arch::DtbOverlay;
use cros_fdt::Error;
use cros_fdt::Fdt;
use cros_fdt::Result;
@ -302,6 +304,7 @@ pub fn create_fdt(
cmdline: &str,
initrd: Option<(GuestAddress, usize)>,
timebase_frequency: u32,
device_tree_overlays: Vec<DtbOverlay>,
) -> Result<()> {
let mut fdt = Fdt::new(&[]);
@ -316,6 +319,9 @@ pub fn create_fdt(
create_aia_node(&mut fdt, num_cpus as usize, aia_num_ids, aia_num_sources)?;
create_pci_nodes(&mut fdt, pci_irqs, pci_cfg, pci_ranges)?;
// Done writing base FDT, now apply DT overlays
apply_device_tree_overlays(&mut fdt, device_tree_overlays)?;
let fdt_final = fdt.finish()?;
if fdt_final.len() > fdt_max_size {
return Err(Error::TotalSizeTooLarge);
@ -328,5 +334,6 @@ pub fn create_fdt(
if written < fdt_final.len() {
return Err(Error::FdtGuestMemoryWriteError);
}
Ok(())
}

View file

@ -193,7 +193,7 @@ impl arch::LinuxArch for Riscv64 {
#[cfg(any(target_os = "android", target_os = "linux"))] _guest_suspended_cvar: Option<
Arc<(Mutex<bool>, Condvar)>,
>,
_device_tree_overlays: Vec<DtbOverlay>,
device_tree_overlays: Vec<DtbOverlay>,
) -> std::result::Result<RunnableLinuxVm<V, Vcpu>, Self::Error>
where
V: VmRiscv64,
@ -383,6 +383,7 @@ impl arch::LinuxArch for Riscv64 {
cmdline.as_str(),
initrd,
timebase_freq,
device_tree_overlays,
)
.map_err(Error::CreateFdt)?;

View file

@ -6,6 +6,8 @@ use std::fs::File;
use std::path::PathBuf;
use arch::android::create_android_fdt;
use arch::apply_device_tree_overlays;
use arch::DtbOverlay;
use cros_fdt::Error;
use cros_fdt::Fdt;
@ -21,10 +23,15 @@ use crate::SetupDataType;
pub fn create_fdt(
android_fstab: File,
dump_device_tree_blob: Option<PathBuf>,
device_tree_overlays: Vec<DtbOverlay>,
) -> Result<SetupData, Error> {
let mut fdt = Fdt::new(&[]);
// The whole thing is put into one giant node with some top level properties
create_android_fdt(&mut fdt, android_fstab)?;
// Done writing base FDT, now apply DT overlays
apply_device_tree_overlays(&mut fdt, device_tree_overlays)?;
let fdt_final = fdt.finish()?;
if let Some(file_path) = dump_device_tree_blob {

View file

@ -692,7 +692,7 @@ impl arch::LinuxArch for X8664arch {
#[cfg(any(target_os = "android", target_os = "linux"))] guest_suspended_cvar: Option<
Arc<(Mutex<bool>, Condvar)>,
>,
_device_tree_overlays: Vec<DtbOverlay>,
device_tree_overlays: Vec<DtbOverlay>,
) -> std::result::Result<RunnableLinuxVm<V, Vcpu>, Self::Error>
where
V: VmX86_64,
@ -996,6 +996,7 @@ impl arch::LinuxArch for X8664arch {
kernel_end,
params,
dump_device_tree_blob,
device_tree_overlays,
)?;
// Configure the bootstrap VCPU for the Linux/x86 64-bit boot protocol.
@ -1615,6 +1616,7 @@ impl X8664arch {
kernel_end: u64,
params: boot_params,
dump_device_tree_blob: Option<PathBuf>,
device_tree_overlays: Vec<DtbOverlay>,
) -> Result<()> {
kernel_loader::load_cmdline(mem, GuestAddress(CMDLINE_OFFSET), cmdline)
.map_err(Error::LoadCmdline)?;
@ -1622,7 +1624,8 @@ impl X8664arch {
let mut setup_data = Vec::<SetupData>::new();
if let Some(android_fstab) = android_fstab {
setup_data.push(
fdt::create_fdt(android_fstab, dump_device_tree_blob).map_err(Error::CreateFdt)?,
fdt::create_fdt(android_fstab, dump_device_tree_blob, device_tree_overlays)
.map_err(Error::CreateFdt)?,
);
}
setup_data.push(setup_data_rng_seed());

View file

@ -234,6 +234,7 @@ where
kernel_end,
params,
None,
Vec::new(),
)
.expect("failed to setup system_memory");