diff --git a/aarch64/src/fdt.rs b/aarch64/src/fdt.rs index ef125cba60..358287cf8a 100644 --- a/aarch64/src/fdt.rs +++ b/aarch64/src/fdt.rs @@ -9,6 +9,7 @@ use std::fmt; use std::ffi::{CString, CStr}; use std::ptr::null; +use devices::PciInterruptPin; use sys_util::{GuestAddress, GuestMemory}; // This is the start of DRAM in the physical address space. @@ -32,7 +33,10 @@ use AARCH64_SERIAL_SIZE; use AARCH64_SERIAL_SPEED; // These are related to guest virtio devices. +use AARCH64_PCI_CFG_BASE; +use AARCH64_PCI_CFG_SIZE; use AARCH64_MMIO_BASE; +use AARCH64_MMIO_SIZE; use AARCH64_MMIO_LEN; use AARCH64_IRQ_BASE; @@ -373,6 +377,73 @@ fn create_io_nodes(fdt: &mut Vec) -> Result<(), Box> { Ok(()) } +fn create_pci_nodes( + fdt: &mut Vec, + pci_irqs: Vec<(u32, PciInterruptPin)>, +) -> Result<(), Box> { + // 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(&[ + // bus address (ss = 01: 32-bit memory space) + 0x2000000, (AARCH64_MMIO_BASE >> 32) as u32, AARCH64_MMIO_BASE as u32, + // CPU address + (AARCH64_MMIO_BASE >> 32) as u32, AARCH64_MMIO_BASE as u32, + // size + (AARCH64_MMIO_SIZE >> 32) as u32, AARCH64_MMIO_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 (i, pci_irq) in pci_irqs.iter().enumerate() { + // PCI_DEVICE(3) + interrupts.push((pci_irq.0 + 1) << 11); + interrupts.push(0); + interrupts.push(0); + + // INT#(1) + interrupts.push(pci_irq.1.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(AARCH64_IRQ_BASE + i as u32); + interrupts.push(IRQ_TYPE_EDGE_RISING); + + // 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)?; + end_node(fdt)?; + + Ok(()) +} + fn create_rtc_node(fdt: &mut Vec) -> Result<(), Box> { // the kernel driver for pl030 really really wants a clock node // associated with an AMBA device or it will fail to probe, so we @@ -409,11 +480,13 @@ fn create_rtc_node(fdt: &mut Vec) -> Result<(), Box> { /// /// * `fdt_max_size` - The amount of space reserved for the device tree /// * `guest_mem` - The guest memory object +/// * `pci_irqs` - List of PCI device number to PCI interrupt pin mappings /// * `num_cpus` - Number of virtual CPUs the guest will have /// * `fdt_load_offset` - The offset into physical memory for the device tree /// * `cmdline` - The kernel commandline pub fn create_fdt(fdt_max_size: usize, guest_mem: &GuestMemory, + pci_irqs: Vec<(u32, PciInterruptPin)>, num_cpus: u32, fdt_load_offset: u64, cmdline: &CStr) -> Result<(), Box> { @@ -446,7 +519,12 @@ pub fn create_fdt(fdt_max_size: usize, create_timer_node(&mut fdt, num_cpus)?; create_serial_node(&mut fdt)?; create_psci_node(&mut fdt)?; - create_io_nodes(&mut fdt)?; + // TODO(dverkamp): remove create_io_nodes() once the PCI conversion is complete + if !pci_irqs.is_empty() { + create_pci_nodes(&mut fdt, pci_irqs)?; + } else { + create_io_nodes(&mut fdt)?; + } create_rtc_node(&mut fdt)?; // End giant node end_node(&mut fdt)?; diff --git a/aarch64/src/lib.rs b/aarch64/src/lib.rs index 88522b1852..8d71d4489a 100644 --- a/aarch64/src/lib.rs +++ b/aarch64/src/lib.rs @@ -23,7 +23,7 @@ use std::os::unix::io::FromRawFd; use std::os::unix::net::UnixDatagram; use arch::{RunnableLinuxVm, VirtioDeviceStub, VmComponents}; -use devices::{Bus, PciInterruptPin}; +use devices::{Bus, BusError, PciConfigMmio, PciInterruptPin}; use sys_util::{EventFd, GuestAddress, GuestMemory}; use resources::{AddressRanges, SystemAllocator}; @@ -98,8 +98,14 @@ const AARCH64_RTC_SIZE: u64 = 0x1000; // Which gets mapped to the first SPI interrupt (physical 32). const AARCH64_RTC_IRQ: u32 = 0; +// PCI MMIO configuration region base address. +const AARCH64_PCI_CFG_BASE: u64 = 0x10000; +// PCI MMIO configuration region size. +const AARCH64_PCI_CFG_SIZE: u64 = 0x1000000; // This is the base address of MMIO devices. -const AARCH64_MMIO_BASE: u64 = 0x10000; +const AARCH64_MMIO_BASE: u64 = 0x1010000; +// Size of the whole MMIO region. +const AARCH64_MMIO_SIZE: u64 = 0x100000; // Each MMIO device gets a 4k page. const AARCH64_MMIO_LEN: u64 = 0x1000; // Virtio devices start at SPI interrupt number 1 @@ -127,6 +133,8 @@ pub enum Error { KernelLoadFailure, /// Failure to Create GIC CreateGICFailure(sys_util::Error), + /// Couldn't register PCI bus. + RegisterPci(BusError), /// Couldn't register virtio socket. RegisterVsock(arch::DeviceRegistrationError), /// VCPU Init failed @@ -151,6 +159,7 @@ impl error::Error for Error { "Kernel cound not be loaded", &Error::CreateGICFailure(_) => "Failure to create GIC", + &Error::RegisterPci(_) => "error registering PCI bus", &Error::RegisterVsock(_) => "error registering virtual socket device", &Error::VCPUInitFailure => "Failed to initialize VCPU", @@ -212,6 +221,7 @@ impl arch::LinuxArch for AArch64 { &mut resources, &mut vm) .map_err(Error::CreatePciRoot)?; + let pci_bus = Arc::new(Mutex::new(PciConfigMmio::new(pci))); let exit_evt = EventFd::new().map_err(Error::CreateEventFd)?; let (io_bus, stdio_serial) = Self::setup_io_bus()?; @@ -227,6 +237,9 @@ impl arch::LinuxArch for AArch64 { .map_err(Error::RegisterVsock)?; } + mmio_bus.insert(pci_bus.clone(), AARCH64_PCI_CFG_BASE, AARCH64_PCI_CFG_SIZE, false) + .map_err(Error::RegisterPci)?; + for param in components.extra_kernel_params { cmdline.insert_str(¶m).map_err(Error::Cmdline)?; } @@ -271,10 +284,10 @@ impl AArch64 { mem_size: u64, vcpu_count: u32, cmdline: &CStr, - _pci_irqs: Vec<(u32, PciInterruptPin)>) -> Result<()> { - // TODO(dgreid) set up PCI in the device tree. + pci_irqs: Vec<(u32, PciInterruptPin)>) -> Result<()> { fdt::create_fdt(AARCH64_FDT_MAX_SIZE as usize, mem, + pci_irqs, vcpu_count, fdt_offset(mem_size), cmdline)?; @@ -309,7 +322,7 @@ impl AArch64 { let device_addr_start = Self::get_base_dev_pfn(mem_size) * sys_util::pagesize() as u64; AddressRanges::new() .add_device_addresses(device_addr_start, u64::max_value() - device_addr_start) - .add_mmio_addresses(AARCH64_MMIO_BASE, 0x10000) + .add_mmio_addresses(AARCH64_MMIO_BASE, AARCH64_MMIO_SIZE) .create_allocator(AARCH64_IRQ_BASE, gpu_allocation).unwrap() }