Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/acpi-tables/src/mcfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ impl Mcfg {
oem_table_id: [u8; 8],
oem_revision: u32,
pci_mmio_config_addr: u64,
end_bus: u8,
) -> Self {
let header = SdtHeader::new(
*b"MCFG",
Expand All @@ -53,7 +54,7 @@ impl Mcfg {
base_address: pci_mmio_config_addr,
segment: 0,
start: 0,
end: 0,
end: end_bus,
..Default::default()
},
..Default::default()
Expand Down
13 changes: 12 additions & 1 deletion src/vmm/src/acpi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::acpi::x86_64::{
use crate::arch::x86_64::layout;
use crate::device_manager::DeviceManager;
use crate::logger::{debug, error};
use crate::pci::bus::MAX_PCI_BUSES;
use crate::vstate::memory::{GuestAddress, GuestMemoryMmap};
use crate::vstate::resources::ResourceAllocator;

Expand Down Expand Up @@ -157,7 +158,17 @@ impl AcpiTableWriter<'_> {
resource_allocator: &mut ResourceAllocator,
pci_mmio_config_addr: u64,
) -> Result<u64, AcpiError> {
let mut mcfg = Mcfg::new(OEM_ID, *b"FCMVMCFG", OEM_REVISION, pci_mmio_config_addr);
// The ECAM aperture is sized for every bus a segment can possibly
// have. Which of those buses actually exist is what the host bridge's
// _CRS bus range describes in PciSegment::append_aml_bytes().
let end_bus = MAX_PCI_BUSES - 1;
let mut mcfg = Mcfg::new(
OEM_ID,
*b"FCMVMCFG",
OEM_REVISION,
pci_mmio_config_addr,
end_bus,
);
self.write_acpi_table(resource_allocator, &mut mcfg)
}

Expand Down
6 changes: 4 additions & 2 deletions src/vmm/src/arch/aarch64/fdt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ use super::cache_info::{CacheEntry, read_cache_config};
use super::gic::GICDevice;
use crate::arch::{
MEM_32BIT_DEVICES_SIZE, MEM_32BIT_DEVICES_START, MEM_64BIT_DEVICES_SIZE,
MEM_64BIT_DEVICES_START, PCI_MMIO_CONFIG_SIZE_PER_SEGMENT,
MEM_64BIT_DEVICES_START,
};
use crate::device_manager::DeviceManager;
use crate::device_manager::mmio::MMIODeviceInfo;
use crate::devices::acpi::vmclock::{VMCLOCK_SIZE, VmClock};
use crate::devices::acpi::vmgenid::{VMGENID_MEM_SIZE, VmGenId};
use crate::devices::pci::PciSegment;
use crate::initrd::InitrdConfig;
use crate::pci::bus::PCI_MMIO_CONFIG_SIZE_PER_SEGMENT;
use crate::vstate::memory::{Address, GuestMemoryMmap, GuestRegionType};

// This is a value for uniquely identifying the FDT node declaring the interrupt controller.
Expand Down Expand Up @@ -531,11 +532,12 @@ fn create_pci_nodes(fdt: &mut FdtWriter, segment: &PciSegment) -> Result<(), Fdt
];

let pci_node = fdt.begin_node(&pci_node_name)?;
let last_bus = u32::from(segment.pci_buses.num_buses() - 1);

fdt.property_string("compatible", "pci-host-ecam-generic")?;
fdt.property_string("device_type", "pci")?;
fdt.property_array_u32("ranges", &ranges)?;
fdt.property_array_u32("bus-range", &[0, 0])?;
fdt.property_array_u32("bus-range", &[0, last_bus])?;
fdt.property_u32("linux,pci-domain", segment.id.into())?;
fdt.property_u32("#address-cells", 3)?;
fdt.property_u32("#size-cells", 2)?;
Expand Down
2 changes: 0 additions & 2 deletions src/vmm/src/arch/aarch64/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,6 @@ pub const MMIO32_MEM_SIZE: u64 = DRAM_MEM_START - MMIO32_MEM_START;
pub const PCI_MMCONFIG_SIZE: u64 = 256 << 20;
/// Start of MMIO region for PCIe configuration accesses.
pub const PCI_MMCONFIG_START: u64 = DRAM_MEM_START - PCI_MMCONFIG_SIZE;
/// MMIO space per PCIe segment
pub const PCI_MMIO_CONFIG_SIZE_PER_SEGMENT: u64 = 4096 * 256;

// We reserve 768 MiB for devices at the beginning of the MMIO region. This includes space both for
// pure MMIO and PCIe devices.
Expand Down
2 changes: 0 additions & 2 deletions src/vmm/src/arch/x86_64/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,6 @@ pub const MMIO32_MEM_START: u64 = FIRST_ADDR_PAST_32BITS - MMIO32_MEM_SIZE;
pub const PCI_MMCONFIG_SIZE: u64 = 256 << 20;
/// Start of MMIO region for PCIe configuration accesses.
pub const PCI_MMCONFIG_START: u64 = IOAPIC_ADDR as u64 - PCI_MMCONFIG_SIZE;
/// MMIO space per PCIe segment
pub const PCI_MMIO_CONFIG_SIZE_PER_SEGMENT: u64 = 4096 * 256;

// We reserve 768 MiB for devices at the beginning of the MMIO region. This includes space both for
// pure MMIO and PCIe devices.
Expand Down
6 changes: 4 additions & 2 deletions src/vmm/src/device_manager/pci_mngr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ impl PciDevices {
.insert((device_type, id), virtio_device.clone());

self.pci_segment
.pci_bus
.pci_buses
.root_bus()
.lock()
.expect("Poisoned lock")
.add_device(sbdf.device(), virtio_device.clone())?;
Expand Down Expand Up @@ -182,7 +183,8 @@ impl PciDevices {
// with the PCI bus lock held and can relocate the BAR, so afterwards
// the BAR address of the device can no longer change under us.
self.pci_segment
.pci_bus
.pci_buses
.root_bus()
.lock()
.expect("Poisoned lock")
.remove_device(sbdf_device);
Expand Down
89 changes: 74 additions & 15 deletions src/vmm/src/devices/pci/pci_segment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,20 @@ use acpi_tables::{Aml, aml};
#[cfg(target_arch = "x86_64")]
use uuid::Uuid;

use crate::arch::{PCI_MMCONFIG_START, PCI_MMIO_CONFIG_SIZE_PER_SEGMENT};
use crate::arch::PCI_MMCONFIG_START;
use crate::logger::info;
use crate::pci::PciSBDF;
#[cfg(target_arch = "x86_64")]
use crate::pci::bus::{PCI_CONFIG_IO_PORT, PCI_CONFIG_IO_PORT_SIZE, PciConfigIo};
use crate::pci::bus::{PciBus, PciBusError, PciConfigMmio, PciHostBridge};
use crate::pci::bus::{
PCI_MMIO_CONFIG_SIZE_PER_SEGMENT, PciBusError, PciBuses, PciConfigMmio, PciHostBridge,
};
use crate::vstate::bus::BusError;
use crate::vstate::vm::KvmVm;

pub struct PciSegment {
pub(crate) id: u16,
pub(crate) pci_bus: Arc<Mutex<PciBus>>,
pub(crate) pci_buses: Arc<PciBuses>,
// The MMIO bus only holds a weak reference to the device, so we need to keep
// the strong reference here alive for as long as the segment exists.
pub(crate) _pci_config_mmio: Arc<Mutex<PciConfigMmio>>,
Expand Down Expand Up @@ -65,10 +67,15 @@ impl std::fmt::Debug for PciSegment {

impl PciSegment {
fn build(id: u16, vm: &Arc<KvmVm>, pci_irq_slots: &[u8; 32]) -> Result<PciSegment, BusError> {
let host_bridge = PciHostBridge::new(None);
let pci_bus = Arc::new(Mutex::new(PciBus::new(host_bridge)));

let pci_config_mmio = Arc::new(Mutex::new(PciConfigMmio::new(Arc::clone(&pci_bus))));
let pci_buses = Arc::new(PciBuses::new(0));
pci_buses
.root_bus()
.lock()
.expect("Poisoned lock")
.add_device(0, Arc::new(Mutex::new(PciHostBridge::new(None))))
.expect("Slot 0 of the root bus is free");

let pci_config_mmio = Arc::new(Mutex::new(PciConfigMmio::new(pci_buses.clone())));
let mmio_config_address = PCI_MMCONFIG_START + PCI_MMIO_CONFIG_SIZE_PER_SEGMENT * id as u64;

vm.common.mmio_bus.insert(
Expand All @@ -87,7 +94,7 @@ impl PciSegment {

let segment = PciSegment {
id,
pci_bus,
pci_buses,
_pci_config_mmio: pci_config_mmio,
mmio_config_address,
proximity_domain: 0,
Expand All @@ -110,7 +117,7 @@ impl PciSegment {
pci_irq_slots: &[u8; 32],
) -> Result<PciSegment, BusError> {
let mut segment = Self::build(id, vm, pci_irq_slots)?;
let pci_config_io = Arc::new(Mutex::new(PciConfigIo::new(Arc::clone(&segment.pci_bus))));
let pci_config_io = Arc::new(Mutex::new(PciConfigIo::new(segment.pci_buses.clone())));

vm.pio_bus.insert(
pci_config_io.clone(),
Expand Down Expand Up @@ -160,7 +167,7 @@ impl PciSegment {
Ok(PciSBDF::new(
self.id,
0,
self.pci_bus.lock().unwrap().next_device_id()?,
self.pci_buses.root_bus().lock().unwrap().next_device_id()?,
0,
))
}
Expand Down Expand Up @@ -233,6 +240,29 @@ impl Aml for PciDsmMethod {
}
}

#[cfg(target_arch = "x86_64")]
struct PciOscMethod {}

#[cfg(target_arch = "x86_64")]
impl Aml for PciOscMethod {
fn append_aml_bytes(&self, v: &mut Vec<u8>) -> Result<(), aml::AmlError> {
// _OSC (Operating System Capabilities), such as PCIeHotplug.
//
// Grant whatever the OS asks for by returning the capabilities buffer
// (Arg3) unchanged, so the control field it gets back is the one it
// requested. Granting control of features Firecracker does not
// implement is harmless: the OS only drives a feature if it also finds
// the corresponding PCI capability.
aml::Method::new(
"_OSC".try_into()?,
4,
false,
vec![&aml::Return::new(&aml::Arg(3))],
)
.append_aml_bytes(v)
}
}

#[cfg(target_arch = "x86_64")]
impl Aml for PciSegment {
fn append_aml_bytes(&self, v: &mut Vec<u8>) -> Result<(), aml::AmlError> {
Expand Down Expand Up @@ -260,12 +290,17 @@ impl Aml for PciSegment {
let pci_dsm = PciDsmMethod {};
pci_dsdt_inner_data.push(&pci_dsm);

let pci_osc = PciOscMethod {};
pci_dsdt_inner_data.push(&pci_osc);

let last_bus = u16::from(self.pci_buses.num_buses() - 1);

#[allow(clippy::if_same_then_else)]
let crs = if self.id == 0 {
aml::Name::new(
"_CRS".try_into()?,
&aml::ResourceTemplate::new(vec![
&aml::AddressSpace::new_bus_number(0x0u16, 0x0u16)?,
&aml::AddressSpace::new_bus_number(0x0u16, last_bus)?,
&aml::Io::new(0xcf8, 0xcf8, 1, 0x8),
&aml::Memory32Fixed::new(
true,
Expand All @@ -292,7 +327,7 @@ impl Aml for PciSegment {
aml::Name::new(
"_CRS".try_into()?,
&aml::ResourceTemplate::new(vec![
&aml::AddressSpace::new_bus_number(0x0u16, 0x0u16)?,
&aml::AddressSpace::new_bus_number(0x0u16, last_bus)?,
&aml::Memory32Fixed::new(
true,
self.mmio_config_address.try_into().unwrap(),
Expand Down Expand Up @@ -352,6 +387,7 @@ mod tests {
use super::*;
use crate::arch;
use crate::builder::tests::default_vmm;
#[cfg(target_arch = "x86_64")]
use crate::utils::u64_to_usize;

#[test]
Expand Down Expand Up @@ -407,13 +443,32 @@ mod tests {
let pci_irq_slots = &[0u8; 32];
let pci_segment = PciSegment::new(0, &kvm_vm, pci_irq_slots).unwrap();

let mut data = [0u8; u64_to_usize(PCI_MMIO_CONFIG_SIZE_PER_SEGMENT)];
let mut data = [0u8; 4];

// Test that we can access the start and end of the ECAM region.
kvm_vm
.common
.mmio_bus
.read(pci_segment.mmio_config_address, &mut data)
.unwrap();
kvm_vm
.common
.mmio_bus
.read(
pci_segment.mmio_config_address + PCI_MMIO_CONFIG_SIZE_PER_SEGMENT
- data.len() as u64,
&mut data,
)
.unwrap();
// Test that accesses outside the ECAM region fail.
kvm_vm
.common
.mmio_bus
.read(
pci_segment.mmio_config_address - data.len() as u64,
&mut data,
)
.unwrap_err();
kvm_vm
.common
.mmio_bus
Expand Down Expand Up @@ -470,8 +525,12 @@ mod tests {
// a single bus with id 0. Also, each device of ours has a
// single function.
assert_eq!(sbdf, PciSBDF::new(0, 0, dev_id, 0));
let mut segment = pci_segment.pci_bus.lock().unwrap();
segment.add_device(dev_id, mock_dev()).unwrap();
let root_bus = pci_segment.pci_buses.root_bus();
root_bus
.lock()
.unwrap()
.add_device(dev_id, mock_dev())
.unwrap();
}

// We can only have 32 devices on a segment
Expand Down
Loading
Loading