// Copyright 2018 The Chromium OS Authors. All rights reserved. // 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_u32, property_u64, start_fdt, Error, Result, }; use arch::SERIAL_ADDR; use devices::{PciAddress, PciInterruptPin}; use sys_util::{GuestAddress, GuestMemory}; // This is the start of DRAM in the physical address space. use crate::AARCH64_PHYS_MEM_START; // These are GIC address-space location constants. use crate::AARCH64_GIC_CPUI_BASE; use crate::AARCH64_GIC_CPUI_SIZE; use crate::AARCH64_GIC_DIST_BASE; use crate::AARCH64_GIC_DIST_SIZE; use crate::AARCH64_GIC_REDIST_SIZE; // These are RTC related constants use crate::AARCH64_RTC_ADDR; use crate::AARCH64_RTC_IRQ; use crate::AARCH64_RTC_SIZE; use devices::pl030::PL030_AMBA_ID; // These are serial device related constants. use crate::AARCH64_SERIAL_1_3_IRQ; use crate::AARCH64_SERIAL_2_4_IRQ; use crate::AARCH64_SERIAL_SIZE; use crate::AARCH64_SERIAL_SPEED; // These are related to guest virtio devices. use crate::AARCH64_MMIO_BASE; use crate::AARCH64_MMIO_SIZE; use crate::AARCH64_PCI_CFG_BASE; use crate::AARCH64_PCI_CFG_SIZE; use crate::AARCH64_PMU_IRQ; // This is an arbitrary number to specify the node for the GIC. // If we had a more complex interrupt architecture, then we'd need an enum for // these. const PHANDLE_GIC: u32 = 1; // These are specified by the Linux GIC bindings const GIC_FDT_IRQ_NUM_CELLS: u32 = 3; const GIC_FDT_IRQ_TYPE_SPI: u32 = 0; const GIC_FDT_IRQ_TYPE_PPI: u32 = 1; const GIC_FDT_IRQ_PPI_CPU_SHIFT: u32 = 8; const GIC_FDT_IRQ_PPI_CPU_MASK: u32 = (0xff << GIC_FDT_IRQ_PPI_CPU_SHIFT); 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<()> { let mem_size = guest_mem.memory_size(); let mem_reg_prop = generate_prop64(&[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)?; 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)?; 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")?; if num_cpus > 1 { property_string(fdt, "enable-method", "psci")?; } property_u32(fdt, "reg", cpu_id)?; end_node(fdt)?; } end_node(fdt)?; Ok(()) } fn create_gic_node(fdt: &mut Vec, 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")?; if is_gicv3 { property_string(fdt, "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")?; 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)?; Ok(()) } fn create_timer_node(fdt: &mut Vec, num_cpus: u32) -> Result<()> { // These are fixed interrupt numbers for the timer device. let irqs = [13, 14, 11, 10]; let compatible = "arm,armv8-timer"; let cpu_mask: u32 = (((1 << num_cpus) - 1) << GIC_FDT_IRQ_PPI_CPU_SHIFT) & GIC_FDT_IRQ_PPI_CPU_MASK; let mut timer_reg_cells = Vec::new(); for &irq in &irqs { timer_reg_cells.push(GIC_FDT_IRQ_TYPE_PPI); 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)?; Ok(()) } fn create_pmu_node(fdt: &mut Vec, 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(&[ 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)?; 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]); 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)?; Ok(()) } fn create_serial_nodes(fdt: &mut Vec) -> 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. create_serial_node(fdt, SERIAL_ADDR[0], AARCH64_SERIAL_1_3_IRQ)?; create_serial_node(fdt, SERIAL_ADDR[1], AARCH64_SERIAL_2_4_IRQ)?; create_serial_node(fdt, SERIAL_ADDR[2], AARCH64_SERIAL_1_3_IRQ)?; create_serial_node(fdt, SERIAL_ADDR[3], AARCH64_SERIAL_2_4_IRQ)?; Ok(()) } // TODO(sonnyrao) -- check to see if host kernel supports PSCI 0_2 fn create_psci_node(fdt: &mut Vec) -> Result<()> { let compatible = "arm,psci-0.2"; begin_node(fdt, "psci")?; property_string(fdt, "compatible", compatible)?; // Only support aarch64 guest property_string(fdt, "method", "hvc")?; // These constants are from PSCI property_u32(fdt, "cpu_suspend", 0xc4000001)?; property_u32(fdt, "cpu_off", 0x84000002)?; property_u32(fdt, "cpu_on", 0xc4000003)?; property_u32(fdt, "migrate", 0xc4000005)?; end_node(fdt)?; Ok(()) } fn create_chosen_node( fdt: &mut Vec, cmdline: &CStr, initrd: Option<(GuestAddress, usize)>, ) -> Result<()> { begin_node(fdt, "chosen")?; property_u32(fdt, "linux,pci-probe-only", 1)?; property_cstring(fdt, "bootargs", cmdline)?; let mut random_file = File::open("/dev/urandom").map_err(Error::FdtIoError)?; let mut kaslr_seed_bytes = [0u8; 8]; random_file .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)?; 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)?; 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)?; } end_node(fdt)?; Ok(()) } fn create_pci_nodes( fdt: &mut Vec, pci_irqs: Vec<(PciAddress, u32, PciInterruptPin)>, pci_device_base: u64, pci_device_size: u64, ) -> Result<()> { // 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(&[ // mmio addresses 0x3000000, // (ss = 11: 64-bit memory space) (AARCH64_MMIO_BASE >> 32) as u32, // PCI address AARCH64_MMIO_BASE as u32, (AARCH64_MMIO_BASE >> 32) as u32, // CPU address AARCH64_MMIO_BASE as u32, (AARCH64_MMIO_SIZE >> 32) as u32, // size AARCH64_MMIO_SIZE as u32, // device addresses 0x3000000, // (ss = 11: 64-bit memory space) (pci_device_base >> 32) as u32, // PCI address pci_device_base as u32, (pci_device_base >> 32) as u32, // CPU address 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 mut interrupts: Vec = Vec::new(); let mut masks: Vec = Vec::new(); for (address, irq_num, irq_pin) in pci_irqs.iter() { // PCI_DEVICE(3) interrupts.push(address.to_config_address(0)); interrupts.push(0); interrupts.push(0); // INT#(1) interrupts.push(irq_pin.to_mask() + 1); // CONTROLLER(PHANDLE) interrupts.push(PHANDLE_GIC); interrupts.push(0); interrupts.push(0); // CONTROLLER_DATA(3) interrupts.push(GIC_FDT_IRQ_TYPE_SPI); interrupts.push(*irq_num); interrupts.push(IRQ_TYPE_LEVEL_HIGH); // PCI_DEVICE(3) masks.push(0xf800); // bits 11..15 (device) masks.push(0); masks.push(0); // INT#(1) 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)?; Ok(()) } fn create_rtc_node(fdt: &mut Vec) -> 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 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]); 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)?; 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. /// /// # Arguments /// /// * `fdt_max_size` - The amount of space reserved for the device tree /// * `guest_mem` - The guest memory object /// * `pci_irqs` - List of PCI device address to PCI interrupt number and pin mappings /// * `num_cpus` - Number of virtual CPUs the guest will have /// * `fdt_load_offset` - The offset into physical memory for the device tree /// * `pci_device_base` - The offset into physical memory for PCI device memory /// * `pci_device_size` - The size of PCI device memory /// * `cmdline` - The kernel commandline /// * `initrd` - An optional tuple of initrd guest physical address and size /// * `android_fstab` - An optional file holding Android fstab entries /// * `is_gicv3` - True if gicv3, false if v2 pub fn create_fdt( fdt_max_size: usize, guest_mem: &GuestMemory, pci_irqs: Vec<(PciAddress, u32, PciInterruptPin)>, num_cpus: u32, fdt_load_offset: u64, pci_device_base: u64, pci_device_size: u64, cmdline: &CStr, initrd: Option<(GuestAddress, usize)>, android_fstab: Option, is_gicv3: bool, use_pmu: bool, ) -> Result<()> { let mut fdt = vec![0; fdt_max_size]; start_fdt(&mut fdt, fdt_max_size)?; // 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)?; if let Some(android_fstab) = android_fstab { arch::android::create_android_fdt(&mut fdt, android_fstab)?; } create_chosen_node(&mut fdt, cmdline, initrd)?; create_memory_node(&mut fdt, guest_mem)?; create_cpu_nodes(&mut fdt, num_cpus)?; create_gic_node(&mut fdt, is_gicv3, num_cpus as u64)?; create_timer_node(&mut fdt, num_cpus)?; if use_pmu { create_pmu_node(&mut fdt, num_cpus)?; } create_serial_nodes(&mut fdt)?; create_psci_node(&mut 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)?; // 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_address = GuestAddress(AARCH64_PHYS_MEM_START + fdt_load_offset); let written = guest_mem .write_at_addr(fdt_final.as_slice(), fdt_address) .map_err(|_| Error::FdtGuestMemoryWriteError)?; if written < fdt_max_size { return Err(Error::FdtGuestMemoryWriteError); } Ok(()) }