mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-01-28 19:29:20 +00:00
aarch64: add generic PCI host controller
Implement the generic PCI host as supported by Linux and described in Documentation/devicetree/bindings/pci/host-generic-pci.txt in the kernel source tree. Also increase the ARM64 MMIO region size from 0x10000 to 0x100000 to allow enough space for several virtio PCI devices. Change-Id: I2f0a9b04356cce912874a06e62b4f3a59cbb08f1 Signed-off-by: Daniel Verkamp <dverkamp@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/1244398 Reviewed-by: Sonny Rao <sonnyrao@chromium.org>
This commit is contained in:
parent
8102525958
commit
948b5f7bc1
2 changed files with 97 additions and 6 deletions
|
@ -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<u8>) -> Result<(), Box<Error>> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn create_pci_nodes(
|
||||
fdt: &mut Vec<u8>,
|
||||
pci_irqs: Vec<(u32, PciInterruptPin)>,
|
||||
) -> Result<(), Box<Error>> {
|
||||
// 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<u32> = Vec::new();
|
||||
let mut masks: Vec<u32> = 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<u8>) -> Result<(), Box<Error>> {
|
||||
// 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<u8>) -> Result<(), Box<Error>> {
|
|||
///
|
||||
/// * `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<Error>> {
|
||||
|
@ -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)?;
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue