diff --git a/Cargo.lock b/Cargo.lock index 2c68fcbebd..470de6e541 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -57,6 +57,7 @@ dependencies = [ "power_monitor", "resources", "sync", + "thiserror", "vm_control", "vm_memory", ] diff --git a/README.md b/README.md index d7cf6b8aa8..ba6e3d15e0 100644 --- a/README.md +++ b/README.md @@ -44,13 +44,13 @@ repo init -g crosvm -u https://chromium.googlesource.com/chromiumos/manifest.git repo sync ``` -A basic crosvm build links against `libcap` and `libfdt`. On a Debian-based system, -you can install `libcap-dev` and `libfdt-dev`. +A basic crosvm build links against `libcap`. On a Debian-based system, +you can install `libcap-dev`. Handy Debian one-liner for all build and runtime deps, particularly if you're running Crostini: ```sh -sudo apt install build-essential libcap-dev libfdt-dev libgbm-dev libvirglrenderer-dev libwayland-bin libwayland-dev pkg-config protobuf-compiler python wayland-protocols +sudo apt install build-essential libcap-dev libgbm-dev libvirglrenderer-dev libwayland-bin libwayland-dev pkg-config protobuf-compiler python wayland-protocols ``` Known issues: diff --git a/aarch64/src/fdt.rs b/aarch64/src/fdt.rs index 2269a164b1..4ae191d34e 100644 --- a/aarch64/src/fdt.rs +++ b/aarch64/src/fdt.rs @@ -2,15 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -use std::ffi::CStr; use std::fs::File; use std::io::Read; -use arch::fdt::{ - begin_node, end_node, finish_fdt, generate_prop32, generate_prop64, property, property_cstring, - property_null, property_string, property_string_list, property_u32, property_u64, start_fdt, - Error, Result, -}; +use arch::fdt::{Error, FdtWriter, Result}; use arch::SERIAL_ADDR; use devices::{PciAddress, PciInterruptPin}; use hypervisor::PsciVersion; @@ -61,63 +56,62 @@ const IRQ_TYPE_EDGE_RISING: u32 = 0x00000001; const IRQ_TYPE_LEVEL_HIGH: u32 = 0x00000004; const IRQ_TYPE_LEVEL_LOW: u32 = 0x00000008; -fn create_memory_node(fdt: &mut Vec, guest_mem: &GuestMemory) -> Result<()> { +fn create_memory_node(fdt: &mut FdtWriter, guest_mem: &GuestMemory) -> Result<()> { let mem_size = guest_mem.memory_size(); - let mem_reg_prop = generate_prop64(&[AARCH64_PHYS_MEM_START, mem_size]); + let mem_reg_prop = [AARCH64_PHYS_MEM_START, mem_size]; - begin_node(fdt, "memory")?; - property_string(fdt, "device_type", "memory")?; - property(fdt, "reg", &mem_reg_prop)?; - end_node(fdt)?; + let memory_node = fdt.begin_node("memory")?; + fdt.property_string("device_type", "memory")?; + fdt.property_array_u64("reg", &mem_reg_prop)?; + fdt.end_node(memory_node)?; Ok(()) } -fn create_cpu_nodes(fdt: &mut Vec, num_cpus: u32) -> Result<()> { - begin_node(fdt, "cpus")?; - property_u32(fdt, "#address-cells", 0x1)?; - property_u32(fdt, "#size-cells", 0x0)?; +fn create_cpu_nodes(fdt: &mut FdtWriter, num_cpus: u32) -> Result<()> { + let cpus_node = fdt.begin_node("cpus")?; + fdt.property_u32("#address-cells", 0x1)?; + fdt.property_u32("#size-cells", 0x0)?; for cpu_id in 0..num_cpus { let cpu_name = format!("cpu@{:x}", cpu_id); - begin_node(fdt, &cpu_name)?; - property_string(fdt, "device_type", "cpu")?; - property_string(fdt, "compatible", "arm,arm-v8")?; + let cpu_node = fdt.begin_node(&cpu_name)?; + fdt.property_string("device_type", "cpu")?; + fdt.property_string("compatible", "arm,arm-v8")?; if num_cpus > 1 { - property_string(fdt, "enable-method", "psci")?; + fdt.property_string("enable-method", "psci")?; } - property_u32(fdt, "reg", cpu_id)?; - end_node(fdt)?; + fdt.property_u32("reg", cpu_id)?; + fdt.end_node(cpu_node)?; } - end_node(fdt)?; + fdt.end_node(cpus_node)?; Ok(()) } -fn create_gic_node(fdt: &mut Vec, is_gicv3: bool, num_cpus: u64) -> Result<()> { +fn create_gic_node(fdt: &mut FdtWriter, is_gicv3: bool, num_cpus: u64) -> Result<()> { let mut gic_reg_prop = [AARCH64_GIC_DIST_BASE, AARCH64_GIC_DIST_SIZE, 0, 0]; - begin_node(fdt, "intc")?; + let intc_node = fdt.begin_node("intc")?; if is_gicv3 { - property_string(fdt, "compatible", "arm,gic-v3")?; + fdt.property_string("compatible", "arm,gic-v3")?; gic_reg_prop[2] = AARCH64_GIC_DIST_BASE - (AARCH64_GIC_REDIST_SIZE * num_cpus); gic_reg_prop[3] = AARCH64_GIC_REDIST_SIZE * num_cpus; } else { - property_string(fdt, "compatible", "arm,cortex-a15-gic")?; + fdt.property_string("compatible", "arm,cortex-a15-gic")?; gic_reg_prop[2] = AARCH64_GIC_CPUI_BASE; gic_reg_prop[3] = AARCH64_GIC_CPUI_SIZE; } - let gic_reg_prop = generate_prop64(&gic_reg_prop); - property_u32(fdt, "#interrupt-cells", GIC_FDT_IRQ_NUM_CELLS)?; - property_null(fdt, "interrupt-controller")?; - property(fdt, "reg", &gic_reg_prop)?; - property_u32(fdt, "phandle", PHANDLE_GIC)?; - property_u32(fdt, "#address-cells", 2)?; - property_u32(fdt, "#size-cells", 2)?; - end_node(fdt)?; + fdt.property_u32("#interrupt-cells", GIC_FDT_IRQ_NUM_CELLS)?; + fdt.property_null("interrupt-controller")?; + fdt.property_array_u64("reg", &gic_reg_prop)?; + fdt.property_u32("phandle", PHANDLE_GIC)?; + fdt.property_u32("#address-cells", 2)?; + fdt.property_u32("#size-cells", 2)?; + fdt.end_node(intc_node)?; Ok(()) } -fn create_timer_node(fdt: &mut Vec, num_cpus: u32) -> Result<()> { +fn create_timer_node(fdt: &mut FdtWriter, num_cpus: u32) -> Result<()> { // These are fixed interrupt numbers for the timer device. let irqs = [13, 14, 11, 10]; let compatible = "arm,armv8-timer"; @@ -130,49 +124,48 @@ fn create_timer_node(fdt: &mut Vec, num_cpus: u32) -> Result<()> { timer_reg_cells.push(irq); timer_reg_cells.push(cpu_mask | IRQ_TYPE_LEVEL_LOW); } - let timer_reg_prop = generate_prop32(timer_reg_cells.as_slice()); - begin_node(fdt, "timer")?; - property_string(fdt, "compatible", compatible)?; - property(fdt, "interrupts", &timer_reg_prop)?; - property_null(fdt, "always-on")?; - end_node(fdt)?; + let timer_node = fdt.begin_node("timer")?; + fdt.property_string("compatible", compatible)?; + fdt.property_array_u32("interrupts", &timer_reg_cells)?; + fdt.property_null("always-on")?; + fdt.end_node(timer_node)?; Ok(()) } -fn create_pmu_node(fdt: &mut Vec, num_cpus: u32) -> Result<()> { +fn create_pmu_node(fdt: &mut FdtWriter, num_cpus: u32) -> Result<()> { let compatible = "arm,armv8-pmuv3"; let cpu_mask: u32 = (((1 << num_cpus) - 1) << GIC_FDT_IRQ_PPI_CPU_SHIFT) & GIC_FDT_IRQ_PPI_CPU_MASK; - let irq = generate_prop32(&[ + let irq = [ GIC_FDT_IRQ_TYPE_PPI, AARCH64_PMU_IRQ, cpu_mask | IRQ_TYPE_LEVEL_HIGH, - ]); + ]; - begin_node(fdt, "pmu")?; - property_string(fdt, "compatible", compatible)?; - property(fdt, "interrupts", &irq)?; - end_node(fdt)?; + let pmu_node = fdt.begin_node("pmu")?; + fdt.property_string("compatible", compatible)?; + fdt.property_array_u32("interrupts", &irq)?; + fdt.end_node(pmu_node)?; Ok(()) } -fn create_serial_node(fdt: &mut Vec, addr: u64, irq: u32) -> Result<()> { - let serial_reg_prop = generate_prop64(&[addr, AARCH64_SERIAL_SIZE]); - let irq = generate_prop32(&[GIC_FDT_IRQ_TYPE_SPI, irq, IRQ_TYPE_EDGE_RISING]); +fn create_serial_node(fdt: &mut FdtWriter, addr: u64, irq: u32) -> Result<()> { + let serial_reg_prop = [addr, AARCH64_SERIAL_SIZE]; + let irq = [GIC_FDT_IRQ_TYPE_SPI, irq, IRQ_TYPE_EDGE_RISING]; - begin_node(fdt, &format!("U6_16550A@{:x}", addr))?; - property_string(fdt, "compatible", "ns16550a")?; - property(fdt, "reg", &serial_reg_prop)?; - property_u32(fdt, "clock-frequency", AARCH64_SERIAL_SPEED)?; - property(fdt, "interrupts", &irq)?; - end_node(fdt)?; + let serial_node = fdt.begin_node(&format!("U6_16550A@{:x}", addr))?; + fdt.property_string("compatible", "ns16550a")?; + fdt.property_array_u64("reg", &serial_reg_prop)?; + fdt.property_u32("clock-frequency", AARCH64_SERIAL_SPEED)?; + fdt.property_array_u32("interrupts", &irq)?; + fdt.end_node(serial_node)?; Ok(()) } -fn create_serial_nodes(fdt: &mut Vec) -> Result<()> { +fn create_serial_nodes(fdt: &mut FdtWriter) -> Result<()> { // Note that SERIAL_ADDR contains the I/O port addresses conventionally used // for serial ports on x86. This uses the same addresses (but on the MMIO bus) // to simplify the shared serial code. @@ -184,36 +177,32 @@ fn create_serial_nodes(fdt: &mut Vec) -> Result<()> { Ok(()) } -fn create_psci_node(fdt: &mut Vec, version: &PsciVersion) -> Result<()> { +fn create_psci_node(fdt: &mut FdtWriter, version: &PsciVersion) -> Result<()> { let mut compatible = vec![format!("arm,psci-{}.{}", version.major, version.minor)]; if version.major == 1 { // Put `psci-0.2` as well because PSCI 1.0 is compatible with PSCI 0.2. compatible.push(format!("arm,psci-0.2")) }; - begin_node(fdt, "psci")?; - property_string_list(fdt, "compatible", compatible)?; + let psci_node = fdt.begin_node("psci")?; + fdt.property_string_list("compatible", compatible)?; // Only support aarch64 guest - property_string(fdt, "method", "hvc")?; - end_node(fdt)?; + fdt.property_string("method", "hvc")?; + fdt.end_node(psci_node)?; Ok(()) } fn create_chosen_node( - fdt: &mut Vec, - cmdline: &CStr, + fdt: &mut FdtWriter, + cmdline: &str, initrd: Option<(GuestAddress, usize)>, ) -> Result<()> { - begin_node(fdt, "chosen")?; - property_u32(fdt, "linux,pci-probe-only", 1)?; - property_cstring(fdt, "bootargs", cmdline)?; + let chosen_node = fdt.begin_node("chosen")?; + fdt.property_u32("linux,pci-probe-only", 1)?; + fdt.property_string("bootargs", cmdline)?; // Used by android bootloader for boot console output - property_string( - fdt, - "stdout-path", - &format!("/U6_16550A@{:x}", SERIAL_ADDR[0]), - )?; + fdt.property_string("stdout-path", &format!("/U6_16550A@{:x}", SERIAL_ADDR[0]))?; let mut random_file = File::open("/dev/urandom").map_err(Error::FdtIoError)?; let mut kaslr_seed_bytes = [0u8; 8]; @@ -221,27 +210,27 @@ fn create_chosen_node( .read_exact(&mut kaslr_seed_bytes) .map_err(Error::FdtIoError)?; let kaslr_seed = u64::from_le_bytes(kaslr_seed_bytes); - property_u64(fdt, "kaslr-seed", kaslr_seed)?; + fdt.property_u64("kaslr-seed", kaslr_seed)?; let mut rng_seed_bytes = [0u8; 256]; random_file .read_exact(&mut rng_seed_bytes) .map_err(Error::FdtIoError)?; - property(fdt, "rng-seed", &rng_seed_bytes)?; + fdt.property("rng-seed", &rng_seed_bytes)?; if let Some((initrd_addr, initrd_size)) = initrd { let initrd_start = initrd_addr.offset() as u32; let initrd_end = initrd_start + initrd_size as u32; - property_u32(fdt, "linux,initrd-start", initrd_start)?; - property_u32(fdt, "linux,initrd-end", initrd_end)?; + fdt.property_u32("linux,initrd-start", initrd_start)?; + fdt.property_u32("linux,initrd-end", initrd_end)?; } - end_node(fdt)?; + fdt.end_node(chosen_node)?; Ok(()) } fn create_pci_nodes( - fdt: &mut Vec, + fdt: &mut FdtWriter, pci_irqs: Vec<(PciAddress, u32, PciInterruptPin)>, pci_device_base: u64, pci_device_size: u64, @@ -249,7 +238,7 @@ fn create_pci_nodes( // Add devicetree nodes describing a PCI generic host controller. // See Documentation/devicetree/bindings/pci/host-generic-pci.txt in the kernel // and "PCI Bus Binding to IEEE Std 1275-1994". - let ranges = generate_prop32(&[ + let ranges = [ // mmio addresses 0x3000000, // (ss = 11: 64-bit memory space) (AARCH64_MMIO_BASE >> 32) as u32, // PCI address @@ -266,9 +255,9 @@ fn create_pci_nodes( pci_device_base as u32, (pci_device_size >> 32) as u32, // size pci_device_size as u32, - ]); - let bus_range = generate_prop32(&[0, 0]); // Only bus 0 - let reg = generate_prop64(&[AARCH64_PCI_CFG_BASE, AARCH64_PCI_CFG_SIZE]); + ]; + let bus_range = [0, 0]; // Only bus 0 + let reg = [AARCH64_PCI_CFG_BASE, AARCH64_PCI_CFG_SIZE]; let mut interrupts: Vec = Vec::new(); let mut masks: Vec = Vec::new(); @@ -301,51 +290,48 @@ fn create_pci_nodes( masks.push(0x7); // allow INTA#-INTD# (1 | 2 | 3 | 4) } - let interrupt_map = generate_prop32(&interrupts); - let interrupt_map_mask = generate_prop32(&masks); - - begin_node(fdt, "pci")?; - property_string(fdt, "compatible", "pci-host-cam-generic")?; - property_string(fdt, "device_type", "pci")?; - property(fdt, "ranges", &ranges)?; - property(fdt, "bus-range", &bus_range)?; - property_u32(fdt, "#address-cells", 3)?; - property_u32(fdt, "#size-cells", 2)?; - property(fdt, "reg", ®)?; - property_u32(fdt, "#interrupt-cells", 1)?; - property(fdt, "interrupt-map", &interrupt_map)?; - property(fdt, "interrupt-map-mask", &interrupt_map_mask)?; - property_null(fdt, "dma-coherent")?; - end_node(fdt)?; + let pci_node = fdt.begin_node("pci")?; + fdt.property_string("compatible", "pci-host-cam-generic")?; + fdt.property_string("device_type", "pci")?; + fdt.property_array_u32("ranges", &ranges)?; + fdt.property_array_u32("bus-range", &bus_range)?; + fdt.property_u32("#address-cells", 3)?; + fdt.property_u32("#size-cells", 2)?; + fdt.property_array_u64("reg", ®)?; + fdt.property_u32("#interrupt-cells", 1)?; + fdt.property_array_u32("interrupt-map", &interrupts)?; + fdt.property_array_u32("interrupt-map-mask", &masks)?; + fdt.property_null("dma-coherent")?; + fdt.end_node(pci_node)?; Ok(()) } -fn create_rtc_node(fdt: &mut Vec) -> Result<()> { +fn create_rtc_node(fdt: &mut FdtWriter) -> Result<()> { // the kernel driver for pl030 really really wants a clock node // associated with an AMBA device or it will fail to probe, so we // need to make up a clock node to associate with the pl030 rtc // node and an associated handle with a unique phandle value. const CLK_PHANDLE: u32 = 24; - begin_node(fdt, "pclk@3M")?; - property_u32(fdt, "#clock-cells", 0)?; - property_string(fdt, "compatible", "fixed-clock")?; - property_u32(fdt, "clock-frequency", 3141592)?; - property_u32(fdt, "phandle", CLK_PHANDLE)?; - end_node(fdt)?; + let clock_node = fdt.begin_node("pclk@3M")?; + fdt.property_u32("#clock-cells", 0)?; + fdt.property_string("compatible", "fixed-clock")?; + fdt.property_u32("clock-frequency", 3141592)?; + fdt.property_u32("phandle", CLK_PHANDLE)?; + fdt.end_node(clock_node)?; let rtc_name = format!("rtc@{:x}", AARCH64_RTC_ADDR); - let reg = generate_prop64(&[AARCH64_RTC_ADDR, AARCH64_RTC_SIZE]); - let irq = generate_prop32(&[GIC_FDT_IRQ_TYPE_SPI, AARCH64_RTC_IRQ, IRQ_TYPE_LEVEL_HIGH]); + let reg = [AARCH64_RTC_ADDR, AARCH64_RTC_SIZE]; + let irq = [GIC_FDT_IRQ_TYPE_SPI, AARCH64_RTC_IRQ, IRQ_TYPE_LEVEL_HIGH]; - begin_node(fdt, &rtc_name)?; - property_string(fdt, "compatible", "arm,primecell")?; - property_u32(fdt, "arm,primecell-periphid", PL030_AMBA_ID)?; - property(fdt, "reg", ®)?; - property(fdt, "interrupts", &irq)?; - property_u32(fdt, "clocks", CLK_PHANDLE)?; - property_string(fdt, "clock-names", "apb_pclk")?; - end_node(fdt)?; + let rtc_node = fdt.begin_node(&rtc_name)?; + fdt.property_string("compatible", "arm,primecell")?; + fdt.property_u32("arm,primecell-periphid", PL030_AMBA_ID)?; + fdt.property_array_u64("reg", ®)?; + fdt.property_array_u32("interrupts", &irq)?; + fdt.property_u32("clocks", CLK_PHANDLE)?; + fdt.property_string("clock-names", "apb_pclk")?; + fdt.end_node(rtc_node)?; Ok(()) } @@ -374,22 +360,21 @@ pub fn create_fdt( fdt_load_offset: u64, pci_device_base: u64, pci_device_size: u64, - cmdline: &CStr, + cmdline: &str, initrd: Option<(GuestAddress, usize)>, android_fstab: Option, is_gicv3: bool, use_pmu: bool, psci_version: PsciVersion, ) -> Result<()> { - let mut fdt = vec![0; fdt_max_size]; - start_fdt(&mut fdt, fdt_max_size)?; + let mut fdt = FdtWriter::new(&[]); // The whole thing is put into one giant node with some top level properties - begin_node(&mut fdt, "")?; - property_u32(&mut fdt, "interrupt-parent", PHANDLE_GIC)?; - property_string(&mut fdt, "compatible", "linux,dummy-virt")?; - property_u32(&mut fdt, "#address-cells", 0x2)?; - property_u32(&mut fdt, "#size-cells", 0x2)?; + let root_node = fdt.begin_node("")?; + fdt.property_u32("interrupt-parent", PHANDLE_GIC)?; + fdt.property_string("compatible", "linux,dummy-virt")?; + fdt.property_u32("#address-cells", 0x2)?; + fdt.property_u32("#size-cells", 0x2)?; if let Some(android_fstab) = android_fstab { arch::android::create_android_fdt(&mut fdt, android_fstab)?; } @@ -406,11 +391,9 @@ pub fn create_fdt( create_pci_nodes(&mut fdt, pci_irqs, pci_device_base, pci_device_size)?; create_rtc_node(&mut fdt)?; // End giant node - end_node(&mut fdt)?; + fdt.end_node(root_node)?; - // Allocate another buffer so we can format and then write fdt to guest - let mut fdt_final = vec![0; fdt_max_size]; - finish_fdt(&mut fdt, &mut fdt_final, fdt_max_size)?; + let fdt_final = fdt.finish(fdt_max_size)?; let fdt_address = GuestAddress(AARCH64_PHYS_MEM_START + fdt_load_offset); let written = guest_mem diff --git a/aarch64/src/lib.rs b/aarch64/src/lib.rs index 1af668bd37..4acef3dc2b 100644 --- a/aarch64/src/lib.rs +++ b/aarch64/src/lib.rs @@ -385,7 +385,7 @@ impl arch::LinuxArch for AArch64 { fdt_offset(components.memory_size, has_bios), pci_device_base, pci_device_size, - &CString::new(cmdline).unwrap(), + cmdline.as_str(), initrd, components.android_fstab, irq_chip.get_vgic_version() == DeviceKind::ArmVgicV3, diff --git a/arch/Cargo.toml b/arch/Cargo.toml index 82cf8adbc7..a200065191 100644 --- a/arch/Cargo.toml +++ b/arch/Cargo.toml @@ -23,3 +23,4 @@ base = { path = "../base" } vm_control = { path = "../vm_control" } vm_memory = { path = "../vm_memory" } power_monitor = { path = "../power_monitor" } +thiserror = "1.0.20" diff --git a/arch/src/android.rs b/arch/src/android.rs index a3242ab288..2ddc8fdc02 100644 --- a/arch/src/android.rs +++ b/arch/src/android.rs @@ -6,7 +6,7 @@ use std::fs::File; use std::io::BufRead; use std::io::BufReader; -use crate::fdt::{begin_node, end_node, property_string, Error, Result}; +use crate::fdt::{Error, FdtWriter, Result}; fn parse_fstab_line(line: &str) -> Result> { let vec: Vec<&str> = line.split_whitespace().collect(); @@ -23,36 +23,36 @@ fn parse_fstab_line(line: &str) -> Result> { /// /// * `fdt` - The DTB to modify. The top-most node should be open. /// * `android-fstab` - A text file of Android fstab entries to add to the DTB -pub fn create_android_fdt(fdt: &mut Vec, fstab: File) -> Result<()> { +pub fn create_android_fdt(fdt: &mut FdtWriter, fstab: File) -> Result<()> { let vecs = BufReader::new(fstab) .lines() .map(|l| parse_fstab_line(&l.map_err(Error::FdtIoError)?)) .collect::>>>()?; - begin_node(fdt, "firmware")?; - begin_node(fdt, "android")?; - property_string(fdt, "compatible", "android,firmware")?; + let firmware_node = fdt.begin_node("firmware")?; + let android_node = fdt.begin_node("android")?; + fdt.property_string("compatible", "android,firmware")?; let (dtprop, fstab): (_, Vec<_>) = vecs.into_iter().partition(|x| x[0] == "#dt-vendor"); - begin_node(fdt, "vendor")?; + let vendor_node = fdt.begin_node("vendor")?; for vec in dtprop { let content = std::fs::read_to_string(&vec[2]).map_err(Error::FdtIoError)?; - property_string(fdt, &vec[1], &content)?; + fdt.property_string(&vec[1], &content)?; } - end_node(fdt)?; // vendor - begin_node(fdt, "fstab")?; - property_string(fdt, "compatible", "android,fstab")?; + fdt.end_node(vendor_node)?; + let fstab_node = fdt.begin_node("fstab")?; + fdt.property_string("compatible", "android,fstab")?; for vec in fstab { let partition = &vec[1][1..]; - begin_node(fdt, partition)?; - property_string(fdt, "compatible", &("android,".to_owned() + partition))?; - property_string(fdt, "dev", &vec[0])?; - property_string(fdt, "type", &vec[2])?; - property_string(fdt, "mnt_flags", &vec[3])?; - property_string(fdt, "fsmgr_flags", &vec[4])?; - end_node(fdt)?; + let partition_node = fdt.begin_node(partition)?; + fdt.property_string("compatible", &("android,".to_owned() + partition))?; + fdt.property_string("dev", &vec[0])?; + fdt.property_string("type", &vec[2])?; + fdt.property_string("mnt_flags", &vec[3])?; + fdt.property_string("fsmgr_flags", &vec[4])?; + fdt.end_node(partition_node)?; } - end_node(fdt)?; // fstab - end_node(fdt)?; // android - end_node(fdt)?; // firmware + fdt.end_node(fstab_node)?; + fdt.end_node(android_node)?; + fdt.end_node(firmware_node)?; Ok(()) } diff --git a/arch/src/fdt.rs b/arch/src/fdt.rs index 658777df54..f5c82ddd64 100644 --- a/arch/src/fdt.rs +++ b/arch/src/fdt.rs @@ -2,240 +2,746 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -use libc::{c_char, c_int, c_void}; -use std::ffi::{CStr, CString}; -use std::fmt::{self, Display}; +//! This module writes Flattened Devicetree blobs as defined here: +//! https://devicetree-specification.readthedocs.io/en/stable/flattened-format.html + +use std::collections::BTreeMap; +use std::convert::TryInto; +use std::ffi::CString; use std::io; -use std::ptr::null; +use std::mem::size_of; -// This links to libfdt which handles the creation of the binary blob -// flattened device tree (fdt) that is passed to the kernel and indicates -// the hardware configuration of the machine. -#[link(name = "fdt")] -extern "C" { - fn fdt_create(buf: *mut c_void, bufsize: c_int) -> c_int; - fn fdt_finish_reservemap(fdt: *mut c_void) -> c_int; - fn fdt_begin_node(fdt: *mut c_void, name: *const c_char) -> c_int; - fn fdt_property(fdt: *mut c_void, name: *const c_char, val: *const c_void, len: c_int) - -> c_int; - fn fdt_end_node(fdt: *mut c_void) -> c_int; - fn fdt_open_into(fdt: *const c_void, buf: *mut c_void, bufsize: c_int) -> c_int; - fn fdt_finish(fdt: *const c_void) -> c_int; - fn fdt_pack(fdt: *mut c_void) -> c_int; -} +use thiserror::Error as ThisError; -#[derive(Debug)] +#[derive(ThisError, Debug)] pub enum Error { - FdtCreateError(c_int), - FdtFinishReservemapError(c_int), - FdtBeginNodeError(c_int), - FdtPropertyError(c_int), - FdtEndNodeError(c_int), - FdtOpenIntoError(c_int), - FdtFinishError(c_int), - FdtPackError(c_int), + #[error("Properties may not be added after a node has been ended")] + PropertyAfterEndNode, + #[error("Property value size must fit in 32 bits")] + PropertyValueTooLarge, + #[error("Total size must fit in 32 bits")] + TotalSizeTooLarge, + #[error("Strings cannot contain NUL")] + InvalidString, + #[error("Attempted to end a node that was not the most recent")] + OutOfOrderEndNode, + #[error("Attempted to call finish without ending all nodes")] + UnclosedNode, + #[error("Error writing FDT to guest memory")] FdtGuestMemoryWriteError, + #[error("Parse error reading FDT parameters")] FdtFileParseError, + #[error("I/O error reading FDT parameters code={0}")] FdtIoError(io::Error), } -impl Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use self::Error::*; - - write!(f, "libfdt: ")?; - - match self { - FdtCreateError(ret) => write!(f, "error creating FDT, code={}", ret), - FdtFinishReservemapError(ret) => write!(f, "error finishing reserve map, code={}", ret), - FdtBeginNodeError(ret) => write!(f, "error beginning FDT node, code={}", ret), - FdtPropertyError(ret) => write!(f, "error adding FDT property, code={}", ret), - FdtEndNodeError(ret) => write!(f, "error ending FDT node, code={}", ret), - FdtOpenIntoError(ret) => write!(f, "error copying FDT to Guest, code={}", ret), - FdtFinishError(ret) => write!(f, "error performing FDT finish, code={}", ret), - FdtPackError(ret) => write!(f, "error packing FDT, code={}", ret), - FdtGuestMemoryWriteError => write!(f, "error writing FDT to guest memory"), - FdtFileParseError => write!(f, "parse error reading FDT parameters"), - FdtIoError(ret) => write!(f, "I/O error reading FDT parameters code={}", ret), - } - } -} - pub type Result = std::result::Result; -impl std::error::Error for Error {} +const FDT_HEADER_SIZE: usize = 40; +const FDT_VERSION: u32 = 17; +const FDT_LAST_COMP_VERSION: u32 = 16; -pub fn begin_node(fdt: &mut Vec, name: &str) -> Result<()> { - let cstr_name = CString::new(name).unwrap(); +const FDT_MAGIC: u32 = 0xd00dfeed; - // Safe because we allocated fdt and converted name to a CString - let fdt_ret = unsafe { fdt_begin_node(fdt.as_mut_ptr() as *mut c_void, cstr_name.as_ptr()) }; - if fdt_ret != 0 { - return Err(Error::FdtBeginNodeError(fdt_ret)); +const FDT_BEGIN_NODE: u32 = 0x00000001; +const FDT_END_NODE: u32 = 0x00000002; +const FDT_PROP: u32 = 0x00000003; +const FDT_END: u32 = 0x00000009; + +/// Interface for writing a Flattened Devicetree (FDT) and emitting a Devicetree Blob (FDT). +/// +/// # Example +/// +/// ```rust +/// use arch::fdt::FdtWriter; +/// +/// # fn main() -> arch::fdt::Result<()> { +/// let mut fdt = FdtWriter::new(&[]); +/// let root_node = fdt.begin_node("")?; +/// fdt.property_string("compatible", "linux,dummy-virt")?; +/// fdt.property_u32("#address-cells", 0x2)?; +/// fdt.property_u32("#size-cells", 0x2)?; +/// let chosen_node = fdt.begin_node("chosen")?; +/// fdt.property_u32("linux,pci-probe-only", 1)?; +/// fdt.property_string("bootargs", "panic=-1 console=hvc0 root=/dev/vda")?; +/// fdt.end_node(chosen_node)?; +/// fdt.end_node(root_node)?; +/// let dtb = fdt.finish(0x1000)?; +/// # Ok(()) +/// # } +/// ``` +pub struct FdtWriter { + data: Vec, + off_mem_rsvmap: u32, + off_dt_struct: u32, + strings: Vec, + string_offsets: BTreeMap, + node_depth: usize, + node_ended: bool, + boot_cpuid_phys: u32, +} + +/// Reserved physical memory region. +/// +/// This represents an area of physical memory reserved by the firmware and unusable by the OS. +/// For example, this could be used to preserve bootloader code or data used at runtime. +pub struct FdtReserveEntry { + /// Physical address of the beginning of the reserved region. + pub address: u64, + /// Size of the reserved region in bytes. + pub size: u64, +} + +/// Handle to an open node created by `FdtWriter::begin_node`. +/// +/// This must be passed back to `FdtWriter::end_node` to close the nodes. +/// Nodes must be closed in reverse order as they were opened, matching the nesting structure +/// of the devicetree. +#[derive(Debug)] +pub struct FdtWriterNode { + depth: usize, +} + +impl FdtWriter { + /// Create a new Flattened Devicetree writer instance. + /// + /// # Arguments + /// + /// `mem_reservations` - reserved physical memory regions to list in the FDT header. + pub fn new(mem_reservations: &[FdtReserveEntry]) -> Self { + let data = vec![0u8; FDT_HEADER_SIZE]; // Reserve space for header. + + let mut fdt = FdtWriter { + data, + off_mem_rsvmap: 0, + off_dt_struct: 0, + strings: Vec::new(), + string_offsets: BTreeMap::new(), + node_depth: 0, + node_ended: false, + boot_cpuid_phys: 0, + }; + + fdt.align(8); + fdt.off_mem_rsvmap = fdt.data.len() as u32; + fdt.write_mem_rsvmap(mem_reservations); + + fdt.align(4); + fdt.off_dt_struct = fdt.data.len() as u32; + + fdt } - Ok(()) -} -pub fn end_node(fdt: &mut Vec) -> Result<()> { - // Safe because we allocated fdt - let fdt_ret = unsafe { fdt_end_node(fdt.as_mut_ptr() as *mut c_void) }; - if fdt_ret != 0 { - return Err(Error::FdtEndNodeError(fdt_ret)); + fn write_mem_rsvmap(&mut self, mem_reservations: &[FdtReserveEntry]) { + for rsv in mem_reservations { + self.append_u64(rsv.address); + self.append_u64(rsv.size); + } + + self.append_u64(0); + self.append_u64(0); } - Ok(()) -} -pub fn property(fdt: &mut Vec, name: &str, val: &[u8]) -> Result<()> { - let cstr_name = CString::new(name).unwrap(); - let val_ptr = val.as_ptr() as *const c_void; - - // Safe because we allocated fdt and converted name to a CString - let fdt_ret = unsafe { - fdt_property( - fdt.as_mut_ptr() as *mut c_void, - cstr_name.as_ptr(), - val_ptr, - val.len() as i32, - ) - }; - if fdt_ret != 0 { - return Err(Error::FdtPropertyError(fdt_ret)); + /// Set the `boot_cpuid_phys` field of the devicetree header. + pub fn set_boot_cpuid_phys(&mut self, boot_cpuid_phys: u32) { + self.boot_cpuid_phys = boot_cpuid_phys; } - Ok(()) -} -fn cpu_to_fdt32(input: u32) -> [u8; 4] { - input.to_be_bytes() -} - -fn cpu_to_fdt64(input: u64) -> [u8; 8] { - input.to_be_bytes() -} - -pub fn property_u32(fdt: &mut Vec, name: &str, val: u32) -> Result<()> { - property(fdt, name, &cpu_to_fdt32(val)) -} - -pub fn property_u64(fdt: &mut Vec, name: &str, val: u64) -> Result<()> { - property(fdt, name, &cpu_to_fdt64(val)) -} - -// Helper to generate a properly formatted byte vector using 32-bit cells -pub fn generate_prop32(cells: &[u32]) -> Vec { - let mut ret: Vec = Vec::new(); - for &e in cells { - ret.extend(&cpu_to_fdt32(e)); + // Append `num_bytes` padding bytes (0x00). + fn pad(&mut self, num_bytes: usize) { + self.data.extend(std::iter::repeat(0).take(num_bytes)); } - ret -} -// Helper to generate a properly formatted byte vector using 64-bit cells -pub fn generate_prop64(cells: &[u64]) -> Vec { - let mut ret: Vec = Vec::new(); - for &e in cells { - ret.extend(&cpu_to_fdt64(e)); + // Append padding bytes (0x00) until the length of data is a multiple of `alignment`. + fn align(&mut self, alignment: usize) { + let offset = self.data.len() % alignment; + if offset != 0 { + self.pad(alignment - offset); + } } - ret -} -pub fn property_null(fdt: &mut Vec, name: &str) -> Result<()> { - let cstr_name = CString::new(name).unwrap(); - - // Safe because we allocated fdt, converted name to a CString - let fdt_ret = unsafe { - fdt_property( - fdt.as_mut_ptr() as *mut c_void, - cstr_name.as_ptr(), - null(), - 0, - ) - }; - if fdt_ret != 0 { - return Err(Error::FdtPropertyError(fdt_ret)); + // Rewrite the value of a big-endian u32 within data. + fn update_u32(&mut self, offset: usize, val: u32) { + let data_slice = &mut self.data[offset..offset + 4]; + data_slice.copy_from_slice(&val.to_be_bytes()); } - Ok(()) -} -pub fn property_bytes(fdt: &mut Vec, name: &str, bytes: &[u8]) -> Result<()> { - let cstr_name = CString::new(name).unwrap(); - - // Safe because we allocated fdt, converted name to CStrings - let fdt_ret = unsafe { - fdt_property( - fdt.as_mut_ptr() as *mut c_void, - cstr_name.as_ptr(), - bytes.as_ptr() as *mut c_void, - bytes.len() as i32, - ) - }; - if fdt_ret != 0 { - return Err(Error::FdtPropertyError(fdt_ret)); + fn append_u32(&mut self, val: u32) { + self.data.extend_from_slice(&val.to_be_bytes()); } - Ok(()) -} -pub fn property_cstring(fdt: &mut Vec, name: &str, cstr_value: &CStr) -> Result<()> { - let value_bytes = cstr_value.to_bytes_with_nul(); - property_bytes(fdt, name, value_bytes) -} + fn append_u64(&mut self, val: u64) { + self.data.extend_from_slice(&val.to_be_bytes()); + } -pub fn property_string(fdt: &mut Vec, name: &str, value: &str) -> Result<()> { - let cstr_value = CString::new(value).unwrap(); - property_cstring(fdt, name, &cstr_value) -} - -pub fn property_string_list(fdt: &mut Vec, name: &str, values: Vec) -> Result<()> { - let bytes = values - .into_iter() - .map(|s| { - let mut bytes = s.into_bytes(); - // Each value must be null-terminated. - bytes.push(0); - bytes + /// Open a new FDT node. + /// + /// The node must be closed using `end_node`. + /// + /// # Arguments + /// + /// `name` - name of the node; must not contain any NUL bytes. + pub fn begin_node(&mut self, name: &str) -> Result { + let name_cstr = CString::new(name).map_err(|_| Error::InvalidString)?; + self.append_u32(FDT_BEGIN_NODE); + self.data.extend(name_cstr.to_bytes_with_nul()); + self.align(4); + self.node_depth += 1; + self.node_ended = false; + Ok(FdtWriterNode { + depth: self.node_depth, }) - .flatten() - .collect::>(); - property_bytes(fdt, name, &bytes) + } + + /// Close a node previously opened with `begin_node`. + pub fn end_node(&mut self, node: FdtWriterNode) -> Result<()> { + if node.depth != self.node_depth { + return Err(Error::OutOfOrderEndNode); + } + + self.append_u32(FDT_END_NODE); + self.node_depth -= 1; + self.node_ended = true; + Ok(()) + } + + // Find an existing instance of a string `s`, or add it to the strings block. + // Returns the offset into the strings block. + fn intern_string(&mut self, s: CString) -> u32 { + if let Some(off) = self.string_offsets.get(&s) { + *off + } else { + let off = self.strings.len() as u32; + self.strings.extend_from_slice(s.to_bytes_with_nul()); + self.string_offsets.insert(s, off); + off + } + } + + /// Write a property. + /// + /// # Arguments + /// + /// `name` - name of the property; must not contain any NUL bytes. + /// `val` - value of the property (raw byte array). + pub fn property(&mut self, name: &str, val: &[u8]) -> Result<()> { + if self.node_ended { + return Err(Error::PropertyAfterEndNode); + } + + let name_cstr = CString::new(name).map_err(|_| Error::InvalidString)?; + + let len = val + .len() + .try_into() + .map_err(|_| Error::PropertyValueTooLarge)?; + + let nameoff = self.intern_string(name_cstr); + self.append_u32(FDT_PROP); + self.append_u32(len); + self.append_u32(nameoff); + self.data.extend_from_slice(val); + self.align(4); + Ok(()) + } + + /// Write an empty property. + pub fn property_null(&mut self, name: &str) -> Result<()> { + self.property(name, &[]) + } + + /// Write a string property. + pub fn property_string(&mut self, name: &str, val: &str) -> Result<()> { + let cstr_value = CString::new(val).map_err(|_| Error::InvalidString)?; + self.property(name, cstr_value.to_bytes_with_nul()) + } + + /// Write a stringlist property. + pub fn property_string_list(&mut self, name: &str, values: Vec) -> Result<()> { + let mut bytes = Vec::new(); + for s in values { + let cstr = CString::new(s).map_err(|_| Error::InvalidString)?; + bytes.extend_from_slice(&cstr.to_bytes_with_nul()); + } + self.property(name, &bytes) + } + + /// Write a 32-bit unsigned integer property. + pub fn property_u32(&mut self, name: &str, val: u32) -> Result<()> { + self.property(name, &val.to_be_bytes()) + } + + /// Write a 64-bit unsigned integer property. + pub fn property_u64(&mut self, name: &str, val: u64) -> Result<()> { + self.property(name, &val.to_be_bytes()) + } + + /// Write a property containing an array of 32-bit unsigned integers. + pub fn property_array_u32(&mut self, name: &str, cells: &[u32]) -> Result<()> { + let mut arr = Vec::with_capacity(cells.len() * size_of::()); + for &c in cells { + arr.extend(&c.to_be_bytes()); + } + self.property(name, &arr) + } + + /// Write a property containing an array of 64-bit unsigned integers. + pub fn property_array_u64(&mut self, name: &str, cells: &[u64]) -> Result<()> { + let mut arr = Vec::with_capacity(cells.len() * size_of::()); + for &c in cells { + arr.extend(&c.to_be_bytes()); + } + self.property(name, &arr) + } + + /// Finish writing the Devicetree Blob (DTB). + /// + /// Returns the DTB as a vector of bytes, consuming the `FdtWriter`. + /// The DTB is always padded up to `max_size` with zeroes, so the returned + /// value will either be exactly `max_size` bytes long, or an error will + /// be returned if the DTB does not fit in `max_size` bytes. + /// + /// # Arguments + /// + /// `max_size` - Maximum size of the finished DTB in bytes. + pub fn finish(mut self, max_size: usize) -> Result> { + if self.node_depth > 0 { + return Err(Error::UnclosedNode); + } + + self.append_u32(FDT_END); + let size_dt_struct = self.data.len() as u32 - self.off_dt_struct; + + let totalsize = self.data.len() + self.strings.len(); + + let totalsize = totalsize.try_into().map_err(|_| Error::TotalSizeTooLarge)?; + let off_dt_strings = self + .data + .len() + .try_into() + .map_err(|_| Error::TotalSizeTooLarge)?; + let size_dt_strings = self + .strings + .len() + .try_into() + .map_err(|_| Error::TotalSizeTooLarge)?; + + // Finalize the header. + self.update_u32(0, FDT_MAGIC); + self.update_u32(1 * 4, totalsize); + self.update_u32(2 * 4, self.off_dt_struct); + self.update_u32(3 * 4, off_dt_strings); + self.update_u32(4 * 4, self.off_mem_rsvmap); + self.update_u32(5 * 4, FDT_VERSION); + self.update_u32(6 * 4, FDT_LAST_COMP_VERSION); + self.update_u32(7 * 4, self.boot_cpuid_phys); + self.update_u32(8 * 4, size_dt_strings); + self.update_u32(9 * 4, size_dt_struct); + + // Add the strings block. + self.data.append(&mut self.strings); + + if self.data.len() > max_size { + Err(Error::TotalSizeTooLarge) + } else { + // Fill remaining data up to `max_size` with zeroes. + self.pad(max_size - self.data.len()); + Ok(self.data) + } + } } -pub fn start_fdt(fdt: &mut Vec, fdt_max_size: usize) -> Result<()> { - // Safe since we allocated this array with fdt_max_size - let mut fdt_ret = unsafe { fdt_create(fdt.as_mut_ptr() as *mut c_void, fdt_max_size as c_int) }; +#[cfg(test)] +mod tests { + use super::*; - if fdt_ret != 0 { - return Err(Error::FdtCreateError(fdt_ret)); + #[test] + fn minimal() { + let mut fdt = FdtWriter::new(&[]); + let root_node = fdt.begin_node("").unwrap(); + fdt.end_node(root_node).unwrap(); + assert_eq!( + fdt.finish(0x48).unwrap(), + [ + 0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed) + 0x00, 0x00, 0x00, 0x48, // 0004: totalsize (0x48) + 0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38) + 0x00, 0x00, 0x00, 0x48, // 000C: off_dt_strings (0x48) + 0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28) + 0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17) + 0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16) + 0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0) + 0x00, 0x00, 0x00, 0x00, // 0020: size_dt_strings (0) + 0x00, 0x00, 0x00, 0x10, // 0024: size_dt_struct (0x10) + 0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high) + 0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low) + 0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high) + 0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low) + 0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE + 0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding + 0x00, 0x00, 0x00, 0x02, // 0040: FDT_END_NODE + 0x00, 0x00, 0x00, 0x09, // 0044: FDT_END + ] + ); } - // Safe since we allocated this array - fdt_ret = unsafe { fdt_finish_reservemap(fdt.as_mut_ptr() as *mut c_void) }; - if fdt_ret != 0 { - return Err(Error::FdtFinishReservemapError(fdt_ret)); + + #[test] + fn reservemap() { + let mut fdt = FdtWriter::new(&[ + FdtReserveEntry { + address: 0x12345678AABBCCDD, + size: 0x1234, + }, + FdtReserveEntry { + address: 0x1020304050607080, + size: 0x5678, + }, + ]); + let root_node = fdt.begin_node("").unwrap(); + fdt.end_node(root_node).unwrap(); + assert_eq!( + fdt.finish(0x68).unwrap(), + [ + 0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed) + 0x00, 0x00, 0x00, 0x68, // 0004: totalsize (0x68) + 0x00, 0x00, 0x00, 0x58, // 0008: off_dt_struct (0x58) + 0x00, 0x00, 0x00, 0x68, // 000C: off_dt_strings (0x68) + 0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28) + 0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17) + 0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16) + 0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0) + 0x00, 0x00, 0x00, 0x00, // 0020: size_dt_strings (0) + 0x00, 0x00, 0x00, 0x10, // 0024: size_dt_struct (0x10) + 0x12, 0x34, 0x56, 0x78, // 0028: rsvmap entry 0 address high + 0xAA, 0xBB, 0xCC, 0xDD, // 002C: rsvmap entry 0 address low + 0x00, 0x00, 0x00, 0x00, // 0030: rsvmap entry 0 size high + 0x00, 0x00, 0x12, 0x34, // 0034: rsvmap entry 0 size low + 0x10, 0x20, 0x30, 0x40, // 0038: rsvmap entry 1 address high + 0x50, 0x60, 0x70, 0x80, // 003C: rsvmap entry 1 address low + 0x00, 0x00, 0x00, 0x00, // 0040: rsvmap entry 1 size high + 0x00, 0x00, 0x56, 0x78, // 0044: rsvmap entry 1 size low + 0x00, 0x00, 0x00, 0x00, // 0048: rsvmap terminator (address = 0 high) + 0x00, 0x00, 0x00, 0x00, // 004C: rsvmap terminator (address = 0 low) + 0x00, 0x00, 0x00, 0x00, // 0050: rsvmap terminator (size = 0 high) + 0x00, 0x00, 0x00, 0x00, // 0054: rsvmap terminator (size = 0 low) + 0x00, 0x00, 0x00, 0x01, // 0058: FDT_BEGIN_NODE + 0x00, 0x00, 0x00, 0x00, // 005C: node name ("") + padding + 0x00, 0x00, 0x00, 0x02, // 0060: FDT_END_NODE + 0x00, 0x00, 0x00, 0x09, // 0064: FDT_END + ] + ); + } + + #[test] + fn prop_null() { + let mut fdt = FdtWriter::new(&[]); + let root_node = fdt.begin_node("").unwrap(); + fdt.property_null("null").unwrap(); + fdt.end_node(root_node).unwrap(); + assert_eq!( + fdt.finish(0x59).unwrap(), + [ + 0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed) + 0x00, 0x00, 0x00, 0x59, // 0004: totalsize (0x59) + 0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38) + 0x00, 0x00, 0x00, 0x54, // 000C: off_dt_strings (0x54) + 0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28) + 0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17) + 0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16) + 0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0) + 0x00, 0x00, 0x00, 0x05, // 0020: size_dt_strings (0x05) + 0x00, 0x00, 0x00, 0x1c, // 0024: size_dt_struct (0x1C) + 0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high) + 0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low) + 0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high) + 0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low) + 0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE + 0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding + 0x00, 0x00, 0x00, 0x03, // 0040: FDT_PROP + 0x00, 0x00, 0x00, 0x00, // 0044: prop len (0) + 0x00, 0x00, 0x00, 0x00, // 0048: prop nameoff (0) + 0x00, 0x00, 0x00, 0x02, // 004C: FDT_END_NODE + 0x00, 0x00, 0x00, 0x09, // 0050: FDT_END + b'n', b'u', b'l', b'l', 0x00, // 0054: strings block + ] + ); + } + + #[test] + fn prop_u32() { + let mut fdt = FdtWriter::new(&[]); + let root_node = fdt.begin_node("").unwrap(); + fdt.property_u32("u32", 0x12345678).unwrap(); + fdt.end_node(root_node).unwrap(); + assert_eq!( + fdt.finish(0x5C).unwrap(), + [ + 0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed) + 0x00, 0x00, 0x00, 0x5c, // 0004: totalsize (0x5C) + 0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38) + 0x00, 0x00, 0x00, 0x58, // 000C: off_dt_strings (0x58) + 0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28) + 0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17) + 0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16) + 0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0) + 0x00, 0x00, 0x00, 0x04, // 0020: size_dt_strings (0x04) + 0x00, 0x00, 0x00, 0x20, // 0024: size_dt_struct (0x20) + 0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high) + 0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low) + 0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high) + 0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low) + 0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE + 0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding + 0x00, 0x00, 0x00, 0x03, // 0040: FDT_PROP + 0x00, 0x00, 0x00, 0x04, // 0044: prop len (4) + 0x00, 0x00, 0x00, 0x00, // 0048: prop nameoff (0) + 0x12, 0x34, 0x56, 0x78, // 004C: prop u32 value (0x12345678) + 0x00, 0x00, 0x00, 0x02, // 0050: FDT_END_NODE + 0x00, 0x00, 0x00, 0x09, // 0054: FDT_END + b'u', b'3', b'2', 0x00, // 0058: strings block + ] + ); + } + + #[test] + fn all_props() { + let mut fdt = FdtWriter::new(&[]); + let root_node = fdt.begin_node("").unwrap(); + fdt.property_null("null").unwrap(); + fdt.property_u32("u32", 0x12345678).unwrap(); + fdt.property_u64("u64", 0x1234567887654321).unwrap(); + fdt.property_string("str", "hello").unwrap(); + fdt.property_string_list("strlst", vec!["hi".into(), "bye".into()]) + .unwrap(); + fdt.property_array_u32("arru32", &[0x12345678, 0xAABBCCDD]) + .unwrap(); + fdt.property_array_u64("arru64", &[0x1234567887654321]) + .unwrap(); + fdt.end_node(root_node).unwrap(); + assert_eq!( + fdt.finish(0xEE).unwrap(), + [ + 0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed) + 0x00, 0x00, 0x00, 0xee, // 0004: totalsize (0xEE) + 0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38) + 0x00, 0x00, 0x00, 0xc8, // 000C: off_dt_strings (0xC8) + 0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28) + 0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17) + 0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16) + 0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0) + 0x00, 0x00, 0x00, 0x26, // 0020: size_dt_strings (0x26) + 0x00, 0x00, 0x00, 0x90, // 0024: size_dt_struct (0x90) + 0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high) + 0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low) + 0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high) + 0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low) + 0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE + 0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding + 0x00, 0x00, 0x00, 0x03, // 0040: FDT_PROP (null) + 0x00, 0x00, 0x00, 0x00, // 0044: prop len (0) + 0x00, 0x00, 0x00, 0x00, // 0048: prop nameoff (0) + 0x00, 0x00, 0x00, 0x03, // 004C: FDT_PROP (u32) + 0x00, 0x00, 0x00, 0x04, // 0050: prop len (4) + 0x00, 0x00, 0x00, 0x05, // 0054: prop nameoff (0x05) + 0x12, 0x34, 0x56, 0x78, // 0058: prop u32 value (0x12345678) + 0x00, 0x00, 0x00, 0x03, // 005C: FDT_PROP (u64) + 0x00, 0x00, 0x00, 0x08, // 0060: prop len (8) + 0x00, 0x00, 0x00, 0x09, // 0064: prop nameoff (0x09) + 0x12, 0x34, 0x56, 0x78, // 0068: prop u64 value high (0x12345678) + 0x87, 0x65, 0x43, 0x21, // 006C: prop u64 value low (0x87654321) + 0x00, 0x00, 0x00, 0x03, // 0070: FDT_PROP (string) + 0x00, 0x00, 0x00, 0x06, // 0074: prop len (6) + 0x00, 0x00, 0x00, 0x0D, // 0078: prop nameoff (0x0D) + b'h', b'e', b'l', b'l', // 007C: prop str value ("hello") + padding + b'o', 0x00, 0x00, 0x00, // 0080: "o\0" + padding + 0x00, 0x00, 0x00, 0x03, // 0084: FDT_PROP (string list) + 0x00, 0x00, 0x00, 0x07, // 0088: prop len (7) + 0x00, 0x00, 0x00, 0x11, // 008C: prop nameoff (0x11) + b'h', b'i', 0x00, b'b', // 0090: prop value ("hi", "bye") + b'y', b'e', 0x00, 0x00, // 0094: "ye\0" + padding + 0x00, 0x00, 0x00, 0x03, // 0098: FDT_PROP (u32 array) + 0x00, 0x00, 0x00, 0x08, // 009C: prop len (8) + 0x00, 0x00, 0x00, 0x18, // 00A0: prop nameoff (0x18) + 0x12, 0x34, 0x56, 0x78, // 00A4: prop value 0 + 0xAA, 0xBB, 0xCC, 0xDD, // 00A8: prop value 1 + 0x00, 0x00, 0x00, 0x03, // 00AC: FDT_PROP (u64 array) + 0x00, 0x00, 0x00, 0x08, // 00B0: prop len (8) + 0x00, 0x00, 0x00, 0x1f, // 00B4: prop nameoff (0x1F) + 0x12, 0x34, 0x56, 0x78, // 00B8: prop u64 value 0 high + 0x87, 0x65, 0x43, 0x21, // 00BC: prop u64 value 0 low + 0x00, 0x00, 0x00, 0x02, // 00C0: FDT_END_NODE + 0x00, 0x00, 0x00, 0x09, // 00C4: FDT_END + b'n', b'u', b'l', b'l', 0x00, // 00C8: strings + 0x00: "null"" + b'u', b'3', b'2', 0x00, // 00CD: strings + 0x05: "u32" + b'u', b'6', b'4', 0x00, // 00D1: strings + 0x09: "u64" + b's', b't', b'r', 0x00, // 00D5: strings + 0x0D: "str" + b's', b't', b'r', b'l', b's', b't', 0x00, // 00D9: strings + 0x11: "strlst" + b'a', b'r', b'r', b'u', b'3', b'2', 0x00, // 00E0: strings + 0x18: "arru32" + b'a', b'r', b'r', b'u', b'6', b'4', 0x00, // 00E7: strings + 0x1F: "arru64" + ] + ); + } + + #[test] + fn nested_nodes() { + let mut fdt = FdtWriter::new(&[]); + let root_node = fdt.begin_node("").unwrap(); + fdt.property_u32("abc", 0x13579024).unwrap(); + let nested_node = fdt.begin_node("nested").unwrap(); + fdt.property_u32("def", 0x12121212).unwrap(); + fdt.end_node(nested_node).unwrap(); + fdt.end_node(root_node).unwrap(); + assert_eq!( + fdt.finish(0x80).unwrap(), + [ + 0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed) + 0x00, 0x00, 0x00, 0x80, // 0004: totalsize (0x80) + 0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38) + 0x00, 0x00, 0x00, 0x78, // 000C: off_dt_strings (0x78) + 0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28) + 0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17) + 0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16) + 0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0) + 0x00, 0x00, 0x00, 0x08, // 0020: size_dt_strings (0x08) + 0x00, 0x00, 0x00, 0x40, // 0024: size_dt_struct (0x40) + 0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high) + 0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low) + 0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high) + 0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low) + 0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE + 0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding + 0x00, 0x00, 0x00, 0x03, // 0040: FDT_PROP + 0x00, 0x00, 0x00, 0x04, // 0044: prop len (4) + 0x00, 0x00, 0x00, 0x00, // 0048: prop nameoff (0x00) + 0x13, 0x57, 0x90, 0x24, // 004C: prop u32 value (0x13579024) + 0x00, 0x00, 0x00, 0x01, // 0050: FDT_BEGIN_NODE + b'n', b'e', b's', b't', // 0054: Node name ("nested") + b'e', b'd', 0x00, 0x00, // 0058: "ed\0" + pad + 0x00, 0x00, 0x00, 0x03, // 005C: FDT_PROP + 0x00, 0x00, 0x00, 0x04, // 0060: prop len (4) + 0x00, 0x00, 0x00, 0x04, // 0064: prop nameoff (0x04) + 0x12, 0x12, 0x12, 0x12, // 0068: prop u32 value (0x12121212) + 0x00, 0x00, 0x00, 0x02, // 006C: FDT_END_NODE ("nested") + 0x00, 0x00, 0x00, 0x02, // 0070: FDT_END_NODE ("") + 0x00, 0x00, 0x00, 0x09, // 0074: FDT_END + b'a', b'b', b'c', 0x00, // 0078: strings + 0x00: "abc" + b'd', b'e', b'f', 0x00, // 007C: strings + 0x04: "def" + ] + ); + } + + #[test] + fn prop_name_string_reuse() { + let mut fdt = FdtWriter::new(&[]); + let root_node = fdt.begin_node("").unwrap(); + fdt.property_u32("abc", 0x13579024).unwrap(); + let nested_node = fdt.begin_node("nested").unwrap(); + fdt.property_u32("def", 0x12121212).unwrap(); + fdt.property_u32("abc", 0x12121212).unwrap(); // This should reuse the "abc" string. + fdt.end_node(nested_node).unwrap(); + fdt.end_node(root_node).unwrap(); + assert_eq!( + fdt.finish(0x90).unwrap(), + [ + 0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed) + 0x00, 0x00, 0x00, 0x90, // 0004: totalsize (0x90) + 0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38) + 0x00, 0x00, 0x00, 0x88, // 000C: off_dt_strings (0x88) + 0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28) + 0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17) + 0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16) + 0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0) + 0x00, 0x00, 0x00, 0x08, // 0020: size_dt_strings (0x08) + 0x00, 0x00, 0x00, 0x50, // 0024: size_dt_struct (0x50) + 0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high) + 0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low) + 0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high) + 0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low) + 0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE + 0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding + 0x00, 0x00, 0x00, 0x03, // 0040: FDT_PROP + 0x00, 0x00, 0x00, 0x04, // 0044: prop len (4) + 0x00, 0x00, 0x00, 0x00, // 0048: prop nameoff (0x00) + 0x13, 0x57, 0x90, 0x24, // 004C: prop u32 value (0x13579024) + 0x00, 0x00, 0x00, 0x01, // 0050: FDT_BEGIN_NODE + b'n', b'e', b's', b't', // 0054: Node name ("nested") + b'e', b'd', 0x00, 0x00, // 0058: "ed\0" + pad + 0x00, 0x00, 0x00, 0x03, // 005C: FDT_PROP + 0x00, 0x00, 0x00, 0x04, // 0060: prop len (4) + 0x00, 0x00, 0x00, 0x04, // 0064: prop nameoff (0x04) + 0x12, 0x12, 0x12, 0x12, // 0068: prop u32 value (0x12121212) + 0x00, 0x00, 0x00, 0x03, // 006C: FDT_PROP + 0x00, 0x00, 0x00, 0x04, // 0070: prop len (4) + 0x00, 0x00, 0x00, 0x00, // 0074: prop nameoff (0x00 - reuse) + 0x12, 0x12, 0x12, 0x12, // 0078: prop u32 value (0x12121212) + 0x00, 0x00, 0x00, 0x02, // 007C: FDT_END_NODE ("nested") + 0x00, 0x00, 0x00, 0x02, // 0080: FDT_END_NODE ("") + 0x00, 0x00, 0x00, 0x09, // 0084: FDT_END + b'a', b'b', b'c', 0x00, // 0088: strings + 0x00: "abc" + b'd', b'e', b'f', 0x00, // 008C: strings + 0x04: "def" + ] + ); + } + + #[test] + fn invalid_node_name_nul() { + let mut fdt = FdtWriter::new(&[]); + fdt.begin_node("abc\0def") + .expect_err("node name with embedded NUL"); + } + + #[test] + fn invalid_prop_name_nul() { + let mut fdt = FdtWriter::new(&[]); + fdt.property_u32("abc\0def", 0) + .expect_err("property name with embedded NUL"); + } + + #[test] + fn invalid_prop_string_value_nul() { + let mut fdt = FdtWriter::new(&[]); + fdt.property_string("mystr", "abc\0def") + .expect_err("string property value with embedded NUL"); + } + + #[test] + fn invalid_prop_string_list_value_nul() { + let mut fdt = FdtWriter::new(&[]); + let strs = vec!["test".into(), "abc\0def".into()]; + fdt.property_string_list("mystr", strs) + .expect_err("stringlist property value with embedded NUL"); + } + + #[test] + fn invalid_prop_after_end_node() { + let mut fdt = FdtWriter::new(&[]); + let _root_node = fdt.begin_node("").unwrap(); + fdt.property_u32("ok_prop", 1234).unwrap(); + let nested_node = fdt.begin_node("mynode").unwrap(); + fdt.property_u32("ok_nested_prop", 5678).unwrap(); + fdt.end_node(nested_node).unwrap(); + fdt.property_u32("bad_prop_after_end_node", 1357) + .expect_err("property after end_node"); + } + + #[test] + fn invalid_end_node_out_of_order() { + let mut fdt = FdtWriter::new(&[]); + let root_node = fdt.begin_node("").unwrap(); + fdt.property_u32("ok_prop", 1234).unwrap(); + let _nested_node = fdt.begin_node("mynode").unwrap(); + fdt.end_node(root_node) + .expect_err("end node while nested node is open"); + } + + #[test] + fn invalid_finish_while_node_open() { + let mut fdt = FdtWriter::new(&[]); + let _root_node = fdt.begin_node("").unwrap(); + fdt.property_u32("ok_prop", 1234).unwrap(); + let _nested_node = fdt.begin_node("mynode").unwrap(); + fdt.property_u32("ok_nested_prop", 5678).unwrap(); + fdt.finish(0x100) + .expect_err("finish without ending all nodes"); } - Ok(()) -} - -pub fn finish_fdt(fdt: &mut Vec, fdt_final: &mut Vec, fdt_max_size: usize) -> Result<()> { - // Safe since we allocated fdt_final and previously passed in it's size - let mut fdt_ret = unsafe { fdt_finish(fdt.as_mut_ptr() as *mut c_void) }; - if fdt_ret != 0 { - return Err(Error::FdtFinishError(fdt_ret)); - } - - // Safe because we allocated both arrays with the correct size - fdt_ret = unsafe { - fdt_open_into( - fdt.as_mut_ptr() as *mut c_void, - fdt_final.as_mut_ptr() as *mut c_void, - fdt_max_size as i32, - ) - }; - if fdt_ret != 0 { - return Err(Error::FdtOpenIntoError(fdt_ret)); - } - - // Safe since we allocated fdt_final - fdt_ret = unsafe { fdt_pack(fdt_final.as_mut_ptr() as *mut c_void) }; - if fdt_ret != 0 { - return Err(Error::FdtPackError(fdt_ret)); - } - Ok(()) } diff --git a/ci/crosvm_aarch64_builder/Dockerfile b/ci/crosvm_aarch64_builder/Dockerfile index d3aa1d0a8a..e4e2000852 100644 --- a/ci/crosvm_aarch64_builder/Dockerfile +++ b/ci/crosvm_aarch64_builder/Dockerfile @@ -33,7 +33,6 @@ RUN apt-get install --yes --no-install-recommends -o APT::Immediate-Configure=fa libdbus-1-dev:arm64 \ libdrm-dev:arm64 \ libepoxy-dev:arm64 \ - libfdt-dev:arm64 \ libssl-dev:arm64 \ libwayland-dev:arm64 diff --git a/ci/crosvm_builder/Dockerfile b/ci/crosvm_builder/Dockerfile index e7fd5dec96..238ed0b6f8 100644 --- a/ci/crosvm_builder/Dockerfile +++ b/ci/crosvm_builder/Dockerfile @@ -21,7 +21,6 @@ RUN apt-get install --yes --no-install-recommends \ libdbus-1-dev \ libdrm-dev \ libepoxy-dev \ - libfdt-dev \ libssl-dev \ libwayland-dev \ qemu-system-x86 diff --git a/ci/crosvm_test_vm/build/cloud_init_data.yaml b/ci/crosvm_test_vm/build/cloud_init_data.yaml index cc75595709..5544d5f2e4 100644 --- a/ci/crosvm_test_vm/build/cloud_init_data.yaml +++ b/ci/crosvm_test_vm/build/cloud_init_data.yaml @@ -30,7 +30,6 @@ packages: - libdbus-1-3 - libdrm2 - libepoxy0 - - libfdt1 - libssl1.1 - libwayland-client0 - libx11-6 diff --git a/docker/Dockerfile b/docker/Dockerfile index 080043730b..b50084890b 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -17,7 +17,6 @@ RUN apt-get update && apt-get install -y \ libcap-dev \ libdbus-1-dev \ libegl1-mesa-dev \ - libfdt-dev \ libgl1-mesa-dev \ libgles2-mesa-dev \ libpciaccess-dev \ diff --git a/x86_64/src/fdt.rs b/x86_64/src/fdt.rs index d6b7ba6561..67fa497be2 100644 --- a/x86_64/src/fdt.rs +++ b/x86_64/src/fdt.rs @@ -3,7 +3,7 @@ // found in the LICENSE file. use arch::android::create_android_fdt; -use arch::fdt::{begin_node, end_node, finish_fdt, start_fdt, Error}; +use arch::fdt::{Error, FdtWriter}; use data_model::DataInit; use std::fs::File; use std::mem; @@ -42,17 +42,14 @@ pub fn create_fdt( // Reserve space for the setup_data let fdt_data_size = fdt_max_size - mem::size_of::(); - let mut fdt = vec![0; fdt_data_size]; - start_fdt(&mut fdt, fdt_data_size)?; + let mut fdt = FdtWriter::new(&[]); // The whole thing is put into one giant node with some top level properties - begin_node(&mut fdt, "")?; + let root_node = fdt.begin_node("")?; create_android_fdt(&mut fdt, android_fstab)?; - end_node(&mut fdt)?; + fdt.end_node(root_node)?; - // Allocate another buffer so we can format and then write fdt to guest - let mut fdt_final = vec![0; fdt_data_size]; - finish_fdt(&mut fdt, &mut fdt_final, fdt_data_size)?; + let fdt_final = fdt.finish(fdt_data_size)?; assert_eq!( mem::size_of::(),