diff --git a/aarch64/src/fdt.rs b/aarch64/src/fdt.rs index 796cc4b811..aab2af04bc 100644 --- a/aarch64/src/fdt.rs +++ b/aarch64/src/fdt.rs @@ -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::("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, vm_generator: &impl Fn(&mut Fdt, &BTreeMap<&str, u32>) -> cros_fdt::Result<()>, dynamic_power_coefficient: BTreeMap, + device_tree_overlays: Vec, ) -> 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::(TEST_SYMBOL).unwrap(), TEST_PATH); + } } diff --git a/aarch64/src/lib.rs b/aarch64/src/lib.rs index a747fa154e..3b829d9d4c 100644 --- a/aarch64/src/lib.rs +++ b/aarch64/src/lib.rs @@ -397,7 +397,7 @@ impl arch::LinuxArch for AArch64 { #[cfg(any(target_os = "android", target_os = "linux"))] _guest_suspended_cvar: Option< Arc<(Mutex, Condvar)>, >, - _device_tree_overlays: Vec, + device_tree_overlays: Vec, ) -> std::result::Result, 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)?; diff --git a/arch/src/fdt.rs b/arch/src/fdt.rs index ab8b7b7cbf..69f7c1c35c 100644 --- a/arch/src/fdt.rs +++ b/arch/src/fdt.rs @@ -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) -> 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(()) +} diff --git a/arch/src/lib.rs b/arch/src/lib.rs index f2b64e7730..4161d203f4 100644 --- a/arch/src/lib.rs +++ b/arch/src/lib.rs @@ -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; diff --git a/riscv64/src/fdt.rs b/riscv64/src/fdt.rs index 5a163b2ce4..1765619144 100644 --- a/riscv64/src/fdt.rs +++ b/riscv64/src/fdt.rs @@ -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, ) -> 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(()) } diff --git a/riscv64/src/lib.rs b/riscv64/src/lib.rs index c4ff77713f..e801966a74 100644 --- a/riscv64/src/lib.rs +++ b/riscv64/src/lib.rs @@ -193,7 +193,7 @@ impl arch::LinuxArch for Riscv64 { #[cfg(any(target_os = "android", target_os = "linux"))] _guest_suspended_cvar: Option< Arc<(Mutex, Condvar)>, >, - _device_tree_overlays: Vec, + device_tree_overlays: Vec, ) -> std::result::Result, 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)?; diff --git a/x86_64/src/fdt.rs b/x86_64/src/fdt.rs index ba3ced0213..645e56157c 100644 --- a/x86_64/src/fdt.rs +++ b/x86_64/src/fdt.rs @@ -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, + device_tree_overlays: Vec, ) -> Result { 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 { diff --git a/x86_64/src/lib.rs b/x86_64/src/lib.rs index 7cd9b56d9b..fb58926ece 100644 --- a/x86_64/src/lib.rs +++ b/x86_64/src/lib.rs @@ -692,7 +692,7 @@ impl arch::LinuxArch for X8664arch { #[cfg(any(target_os = "android", target_os = "linux"))] guest_suspended_cvar: Option< Arc<(Mutex, Condvar)>, >, - _device_tree_overlays: Vec, + device_tree_overlays: Vec, ) -> std::result::Result, 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, + device_tree_overlays: Vec, ) -> 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::::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()); diff --git a/x86_64/tests/integration/main.rs b/x86_64/tests/integration/main.rs index 7a2971d47c..cb740c1b44 100644 --- a/x86_64/tests/integration/main.rs +++ b/x86_64/tests/integration/main.rs @@ -234,6 +234,7 @@ where kernel_end, params, None, + Vec::new(), ) .expect("failed to setup system_memory");