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
1 change: 1 addition & 0 deletions bin/propolis-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ impl VmConfig {
} else {
Default::default()
},
native_acpi_tables: Some(true),
},
components: Default::default(),
smbios: None,
Expand Down
121 changes: 106 additions & 15 deletions bin/propolis-server/src/lib/initializer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,12 @@ pub enum MachineInitError {
/// Arbitrary ROM limit for now
const MAX_ROM_SIZE: usize = 0x20_0000;

const PCIE_ECAM_BASE: usize = 0xe000_0000;
const PCIE_ECAM_SIZE: usize = 0x1000_0000;
const MEM_32BIT_DEVICES_START: usize = 0xc000_0000;
const MEM_32BIT_DEVICES_END: usize = 0xfc00_0000;
const HIGHMEM_START: usize = 0x1_0000_0000;

fn get_spec_guest_ram_limits(spec: &Spec) -> (usize, usize) {
let memsize = spec.board.memory_mb as usize * MB;
let lowmem = memsize.min(3 * GB);
Expand Down Expand Up @@ -141,19 +147,22 @@ pub fn build_instance(
.context("failed to add low memory region")?
.add_rom_region(0x1_0000_0000 - MAX_ROM_SIZE, MAX_ROM_SIZE, "bootrom")
.context("failed to add bootrom region")?
.add_mmio_region(0xc000_0000_usize, 0x2000_0000_usize, "dev32")
.add_mmio_region(lowmem, PCIE_ECAM_BASE - lowmem, "dev32")
.context("failed to add low device MMIO region")?
.add_mmio_region(0xe000_0000_usize, 0x1000_0000_usize, "pcicfg")
.add_mmio_region(
PCIE_ECAM_BASE,
MEM_32BIT_DEVICES_END - PCIE_ECAM_BASE,
"pcicfg",
)
.context("failed to add PCI config region")?;

let highmem_start = 0x1_0000_0000;
if highmem > 0 {
builder = builder
.add_mem_region(highmem_start, highmem, "highmem")
.add_mem_region(HIGHMEM_START, highmem, "highmem")
.context("failed to add high memory region")?;
}

let dev64_start = highmem_start + highmem;
let dev64_start = HIGHMEM_START + highmem;
builder = builder
.add_mmio_region(dev64_start, vmm::MAX_PHYSMEM - dev64_start, "dev64")
.context("failed to add high device MMIO region")?;
Expand Down Expand Up @@ -393,16 +402,25 @@ impl MachineInitializer<'_> {
continue;
}

let (irq, port) = match desc.num {
SerialPortNumber::Com1 => (ibmpc::IRQ_COM1, ibmpc::PORT_COM1),
SerialPortNumber::Com2 => (ibmpc::IRQ_COM2, ibmpc::PORT_COM2),
SerialPortNumber::Com3 => (ibmpc::IRQ_COM3, ibmpc::PORT_COM3),
SerialPortNumber::Com4 => (ibmpc::IRQ_COM4, ibmpc::PORT_COM4),
let (irq, port, uart_name) = match desc.num {
SerialPortNumber::Com1 => {
(ibmpc::IRQ_COM1, ibmpc::PORT_COM1, "COM1")
}
SerialPortNumber::Com2 => {
(ibmpc::IRQ_COM2, ibmpc::PORT_COM2, "COM2")
}
SerialPortNumber::Com3 => {
(ibmpc::IRQ_COM3, ibmpc::PORT_COM3, "COM3")
}
SerialPortNumber::Com4 => {
(ibmpc::IRQ_COM4, ibmpc::PORT_COM4, "COM4")
}
};

let dev = LpcUart::new(chipset.irq_pin(irq).unwrap());
let dev =
LpcUart::new(chipset.irq_pin(irq).unwrap(), port, irq, uart_name);
dev.set_autodiscard(true);
LpcUart::attach(&dev, &self.machine.bus_pio, port);
dev.attach(&self.machine.bus_pio);
self.devices.insert(name.to_owned(), dev.clone());
if desc.num == SerialPortNumber::Com1 {
assert!(com1.is_none());
Expand Down Expand Up @@ -899,9 +917,14 @@ impl MachineInitializer<'_> {
// Set up an LPC uart for ASIC management comms from the guest.
//
// NOTE: SoftNpu squats on com4.
let uart = LpcUart::new(chipset.irq_pin(ibmpc::IRQ_COM4).unwrap());
let uart = LpcUart::new(
chipset.irq_pin(ibmpc::IRQ_COM4).unwrap(),
ibmpc::PORT_COM4,
ibmpc::IRQ_COM4,
"COM4",
);
uart.set_autodiscard(true);
LpcUart::attach(&uart, &self.machine.bus_pio, ibmpc::PORT_COM4);
uart.attach(&self.machine.bus_pio);
self.devices
.insert(SpecKey::Name("softnpu-uart".to_string()), uart.clone());

Expand Down Expand Up @@ -1156,9 +1179,17 @@ impl MachineInitializer<'_> {
propolis::vmm::MapType::Dram => {
e820_table.add_mem(addr, len);
}
_ => {
propolis::vmm::MapType::Rom => {
e820_table.add_reserved(addr, len);
}
propolis::vmm::MapType::Mmio => {
// With native ACPI tables, MMIO is described in the DSDT
// _CRS and should not appear in E820. Without native
// tables, preserve original E820 layout.
if self.spec.board.native_acpi_tables != Some(true) {
e820_table.add_reserved(addr, len);
}
}
}
}

Expand Down Expand Up @@ -1270,6 +1301,66 @@ impl MachineInitializer<'_> {
.insert_named("etc/e820", e820_entry)
.map_err(|e| MachineInitError::FwcfgInsertFailed("e820", e))?;

if self.spec.board.native_acpi_tables == Some(true) {
let (_, highmem) = get_spec_guest_ram_limits(self.spec);
let dev64_start = HIGHMEM_START + highmem;

// Collect DSDT generators from devices that implement the trait
let generators: Vec<_> = self
.devices
.values()
.filter_map(|dev| dev.as_dsdt_generator())
.collect();

// Get the physical address width from CPUID leaf 0x8000_0008.
// EAX[7:0] contains the physical address bits supported by the CPU.
// The 64-bit MMIO limit must not exceed what the CPU can address.
let phys_addr_bits = self
.spec
.cpuid
.get(CpuidIdent::leaf(0x8000_0008))
.map(|v| v.eax & 0xff)
.unwrap_or(48) as u64;
let max_phys_addr = (1u64 << phys_addr_bits) - 1;
let mmio64_limit =
max_phys_addr.min(vmm::MAX_PHYSMEM as u64 - 1);

let acpi_tables = fwcfg::formats::build_acpi_tables(
&fwcfg::formats::AcpiConfig {
num_cpus: cpus,
pcie_ecam_base: PCIE_ECAM_BASE as u64,
pcie_ecam_size: PCIE_ECAM_SIZE as u64,
pcie_mmio32_base: MEM_32BIT_DEVICES_START as u64,
pcie_mmio32_limit: (MEM_32BIT_DEVICES_END - 1) as u64,
pcie_mmio64_base: dev64_start as u64,
pcie_mmio64_limit: mmio64_limit,
..Default::default()
},
&generators,
);
fwcfg
.insert_named(
"etc/acpi/tables",
Entry::Bytes(acpi_tables.tables),
)
.map_err(|e| {
MachineInitError::FwcfgInsertFailed("acpi/tables", e)
})?;
fwcfg
.insert_named("etc/acpi/rsdp", Entry::Bytes(acpi_tables.rsdp))
.map_err(|e| {
MachineInitError::FwcfgInsertFailed("acpi/rsdp", e)
})?;
fwcfg
.insert_named(
"etc/table-loader",
Entry::Bytes(acpi_tables.loader),
)
.map_err(|e| {
MachineInitError::FwcfgInsertFailed("table-loader", e)
})?;
}

let ramfb = ramfb::RamFb::create(
self.log.new(slog::o!("component" => "ramfb")),
);
Expand Down
18 changes: 16 additions & 2 deletions bin/propolis-server/src/lib/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,14 @@ impl PropolisServerApi for PropolisServerImpl {
let vm_init = match init {
InstanceInitializationMethod::Spec { spec } => spec
.try_into()
.map(|s| VmInitializationMethod::Spec(Box::new(s)))
.map(|mut s: crate::spec::Spec| {
// Default to native ACPI tables for new VMs but respect
// explicit client preference if provided.
if s.board.native_acpi_tables.is_none() {
s.board.native_acpi_tables = Some(true);
}
VmInitializationMethod::Spec(Box::new(s))
})
.map_err(|e| {
if let Some(s) = e.source() {
format!("{e}: {s}")
Expand Down Expand Up @@ -337,7 +344,14 @@ impl PropolisServerApi for PropolisServerImpl {
let vm_init = match init {
InstanceInitializationMethodV0::Spec { spec } => spec
.try_into()
.map(|s| VmInitializationMethod::Spec(Box::new(s)))
.map(|mut s: crate::spec::Spec| {
// Default to native ACPI tables for new VMs, but respect
// explicit client preference if provided.
if s.board.native_acpi_tables.is_none() {
s.board.native_acpi_tables = Some(true);
}
VmInitializationMethod::Spec(Box::new(s))
})
.map_err(|e| {
if let Some(s) = e.source() {
format!("{e}: {s}")
Expand Down
1 change: 1 addition & 0 deletions bin/propolis-server/src/lib/spec/api_spec_v0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ impl From<Spec> for InstanceSpecV0 {
chipset: board.chipset,
guest_hv_interface: board.guest_hv_interface,
cpuid: Some(cpuid.into_instance_spec_cpuid()),
native_acpi_tables: board.native_acpi_tables,
};
let mut spec = InstanceSpecV0 { board, components: Default::default() };

Expand Down
2 changes: 2 additions & 0 deletions bin/propolis-server/src/lib/spec/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ impl SpecBuilder {
memory_mb: board.memory_mb,
chipset: board.chipset,
guest_hv_interface: board.guest_hv_interface,
native_acpi_tables: board.native_acpi_tables,
},
cpuid,
..Default::default()
Expand Down Expand Up @@ -380,6 +381,7 @@ mod test {
memory_mb: 512,
chipset: Chipset::I440Fx(I440Fx { enable_pcie: false }),
guest_hv_interface: GuestHypervisorInterface::Bhyve,
native_acpi_tables: Some(false),
};

SpecBuilder {
Expand Down
7 changes: 7 additions & 0 deletions bin/propolis-server/src/lib/spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,12 @@ pub(crate) struct Board {
pub memory_mb: u64,
pub chipset: Chipset,
pub guest_hv_interface: GuestHypervisorInterface,
/// Use native ACPI tables instead of OVMF-provided tables.
///
/// `None` indicates a VM created before native ACPI table support existed.
/// For migration compatibility, `None` is preserved through round-trips
/// and treated the same as `Some(false)` at runtime.
pub native_acpi_tables: Option<bool>,
}

impl Default for Board {
Expand All @@ -138,6 +144,7 @@ impl Default for Board {
memory_mb: 0,
chipset: Chipset::I440Fx(I440Fx { enable_pcie: false }),
guest_hv_interface: GuestHypervisorInterface::Bhyve,
native_acpi_tables: None,
}
}
}
Expand Down
Loading