Skip to content

Commit d05da54

Browse files
committed
Wire up ACPI table generation via fw_cfg
Integrate the new ACPI table generation into propolis-standalone and propolis-server. Also replace hardcoded memory region addresses with constants that align with ACPI table definitions. The PCIe ECAM base is kept same as before at 0xe000_0000 (3.5GB) to match existing i440fx chipset ECAM placement. ECAM is no longer added to the E820 map as reserved memory since it is MMIO space properly described in the MCFG ACPI table. Guest physical memory map: 0x0000_0000 - 0xbfff_ffff Low RAM (up to 3 GiB) 0xc000_0000 - 0xffff_ffff PCI hole (1 GiB MMIO region) 0xc000_0000 - 0xdfff_ffff 32-bit PCI MMIO 0xe000_0000 - 0xefff_ffff PCIe ECAM (256 MiB, 256 buses) 0xfec0_0000 IOAPIC 0xfed0_0000 HPET 0xffe0_0000 - 0xffff_ffff Bootrom (2 MiB) 0x1_0000_0000+ High RAM + 64-bit PCI MMIO e820 map as seen by guest: 0x0000_0000 - 0x0009_ffff Usable (640 KiB low memory) 0x0010_0000 - 0xbeaf_ffff Usable (~3 GiB main RAM) 0xbeb0_0000 - 0xbfb6_cfff Reserved (UEFI runtime/data) 0xbfb6_d000 - 0xbfbf_efff ACPI Tables + NVS 0xbfbf_f000 - 0xbffd_ffff Usable (top of low memory) 0xbffe_0000 - 0xffff_ffff Reserved (PCI hole) 0x1_0000_0000 - highmem Usable (high RAM above 4 GiB) To stay on safe side only enable using new ACPI tables for newly launched VMs. Old VMs using OVMF tables would keep using the same OVMF tables throughout multiple migrations. To verify this add the phd test as well for new VM launched with native tables, native tables preserved through migration and VM launched from old propolis without native tables stays with OVMF through multiple future migrations. Signed-off-by: Amey Narkhede <[email protected]>
1 parent 838a9a9 commit d05da54

File tree

11 files changed

+268
-14
lines changed

11 files changed

+268
-14
lines changed

bin/propolis-cli/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ impl VmConfig {
340340
} else {
341341
Default::default()
342342
},
343+
native_acpi_tables: Some(true),
343344
},
344345
components: Default::default(),
345346
smbios: None,

bin/propolis-server/src/lib/initializer.rs

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,12 @@ pub enum MachineInitError {
111111
/// Arbitrary ROM limit for now
112112
const MAX_ROM_SIZE: usize = 0x20_0000;
113113

114+
const PCIE_ECAM_BASE: usize = 0xe000_0000;
115+
const PCIE_ECAM_SIZE: usize = 0x1000_0000;
116+
const MEM_32BIT_DEVICES_START: usize = 0xc000_0000;
117+
const MEM_32BIT_DEVICES_END: usize = 0xfc00_0000;
118+
const HIGHMEM_START: usize = 0x1_0000_0000;
119+
114120
fn get_spec_guest_ram_limits(spec: &Spec) -> (usize, usize) {
115121
let memsize = spec.board.memory_mb as usize * MB;
116122
let lowmem = memsize.min(3 * GB);
@@ -141,19 +147,22 @@ pub fn build_instance(
141147
.context("failed to add low memory region")?
142148
.add_rom_region(0x1_0000_0000 - MAX_ROM_SIZE, MAX_ROM_SIZE, "bootrom")
143149
.context("failed to add bootrom region")?
144-
.add_mmio_region(0xc000_0000_usize, 0x2000_0000_usize, "dev32")
150+
.add_mmio_region(lowmem, PCIE_ECAM_BASE - lowmem, "dev32")
145151
.context("failed to add low device MMIO region")?
146-
.add_mmio_region(0xe000_0000_usize, 0x1000_0000_usize, "pcicfg")
152+
.add_mmio_region(
153+
PCIE_ECAM_BASE,
154+
MEM_32BIT_DEVICES_END - PCIE_ECAM_BASE,
155+
"pcicfg",
156+
)
147157
.context("failed to add PCI config region")?;
148158

149-
let highmem_start = 0x1_0000_0000;
150159
if highmem > 0 {
151160
builder = builder
152-
.add_mem_region(highmem_start, highmem, "highmem")
161+
.add_mem_region(HIGHMEM_START, highmem, "highmem")
153162
.context("failed to add high memory region")?;
154163
}
155164

156-
let dev64_start = highmem_start + highmem;
165+
let dev64_start = HIGHMEM_START + highmem;
157166
builder = builder
158167
.add_mmio_region(dev64_start, vmm::MAX_PHYSMEM - dev64_start, "dev64")
159168
.context("failed to add high device MMIO region")?;
@@ -1156,9 +1165,17 @@ impl MachineInitializer<'_> {
11561165
propolis::vmm::MapType::Dram => {
11571166
e820_table.add_mem(addr, len);
11581167
}
1159-
_ => {
1168+
propolis::vmm::MapType::Rom => {
11601169
e820_table.add_reserved(addr, len);
11611170
}
1171+
propolis::vmm::MapType::Mmio => {
1172+
// With native ACPI tables, MMIO is described in the DSDT
1173+
// _CRS and should not appear in E820. Without native
1174+
// tables, preserve original E820 layout.
1175+
if !self.spec.board.native_acpi_tables {
1176+
e820_table.add_reserved(addr, len);
1177+
}
1178+
}
11621179
}
11631180
}
11641181

@@ -1270,6 +1287,49 @@ impl MachineInitializer<'_> {
12701287
.insert_named("etc/e820", e820_entry)
12711288
.map_err(|e| MachineInitError::FwcfgInsertFailed("e820", e))?;
12721289

1290+
if self.spec.board.native_acpi_tables {
1291+
let (_, highmem) = get_spec_guest_ram_limits(self.spec);
1292+
let dev64_start = HIGHMEM_START + highmem;
1293+
1294+
let acpi_tables = fwcfg::formats::build_acpi_tables(
1295+
&fwcfg::formats::AcpiConfig {
1296+
num_cpus: cpus,
1297+
pcie_ecam_base: PCIE_ECAM_BASE as u64,
1298+
pcie_ecam_size: PCIE_ECAM_SIZE as u64,
1299+
pcie_mmio32_base: MEM_32BIT_DEVICES_START as u64,
1300+
pcie_mmio32_limit: (MEM_32BIT_DEVICES_END - 1) as u64,
1301+
pcie_mmio64_base: dev64_start as u64,
1302+
pcie_mmio64_limit: vmm::MAX_PHYSMEM as u64 - 1,
1303+
com_ports: vec![
1304+
(ibmpc::PORT_COM1, ibmpc::IRQ_COM1),
1305+
(ibmpc::PORT_COM2, ibmpc::IRQ_COM2),
1306+
],
1307+
..Default::default()
1308+
},
1309+
);
1310+
fwcfg
1311+
.insert_named(
1312+
"etc/acpi/tables",
1313+
Entry::Bytes(acpi_tables.tables),
1314+
)
1315+
.map_err(|e| {
1316+
MachineInitError::FwcfgInsertFailed("acpi/tables", e)
1317+
})?;
1318+
fwcfg
1319+
.insert_named("etc/acpi/rsdp", Entry::Bytes(acpi_tables.rsdp))
1320+
.map_err(|e| {
1321+
MachineInitError::FwcfgInsertFailed("acpi/rsdp", e)
1322+
})?;
1323+
fwcfg
1324+
.insert_named(
1325+
"etc/table-loader",
1326+
Entry::Bytes(acpi_tables.loader),
1327+
)
1328+
.map_err(|e| {
1329+
MachineInitError::FwcfgInsertFailed("table-loader", e)
1330+
})?;
1331+
}
1332+
12731333
let ramfb = ramfb::RamFb::create(
12741334
self.log.new(slog::o!("component" => "ramfb")),
12751335
);

bin/propolis-server/src/lib/server.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,10 @@ impl PropolisServerApi for PropolisServerImpl {
248248
let vm_init = match init {
249249
InstanceInitializationMethod::Spec { spec } => spec
250250
.try_into()
251-
.map(|s| VmInitializationMethod::Spec(Box::new(s)))
251+
.map(|mut s: crate::spec::Spec| {
252+
s.board.native_acpi_tables = true;
253+
VmInitializationMethod::Spec(Box::new(s))
254+
})
252255
.map_err(|e| {
253256
if let Some(s) = e.source() {
254257
format!("{e}: {s}")
@@ -337,7 +340,10 @@ impl PropolisServerApi for PropolisServerImpl {
337340
let vm_init = match init {
338341
InstanceInitializationMethodV0::Spec { spec } => spec
339342
.try_into()
340-
.map(|s| VmInitializationMethod::Spec(Box::new(s)))
343+
.map(|mut s: crate::spec::Spec| {
344+
s.board.native_acpi_tables = true;
345+
VmInitializationMethod::Spec(Box::new(s))
346+
})
341347
.map_err(|e| {
342348
if let Some(s) = e.source() {
343349
format!("{e}: {s}")

bin/propolis-server/src/lib/spec/api_spec_v0.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ impl From<Spec> for InstanceSpecV0 {
101101
chipset: board.chipset,
102102
guest_hv_interface: board.guest_hv_interface,
103103
cpuid: Some(cpuid.into_instance_spec_cpuid()),
104+
native_acpi_tables: Some(board.native_acpi_tables),
104105
};
105106
let mut spec = InstanceSpecV0 { board, components: Default::default() };
106107

bin/propolis-server/src/lib/spec/builder.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ impl SpecBuilder {
9696
memory_mb: board.memory_mb,
9797
chipset: board.chipset,
9898
guest_hv_interface: board.guest_hv_interface,
99+
native_acpi_tables: board
100+
.native_acpi_tables
101+
.unwrap_or(false),
99102
},
100103
cpuid,
101104
..Default::default()
@@ -380,6 +383,7 @@ mod test {
380383
memory_mb: 512,
381384
chipset: Chipset::I440Fx(I440Fx { enable_pcie: false }),
382385
guest_hv_interface: GuestHypervisorInterface::Bhyve,
386+
native_acpi_tables: false,
383387
};
384388

385389
SpecBuilder {

bin/propolis-server/src/lib/spec/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ pub(crate) struct Board {
129129
pub memory_mb: u64,
130130
pub chipset: Chipset,
131131
pub guest_hv_interface: GuestHypervisorInterface,
132+
pub native_acpi_tables: bool,
132133
}
133134

134135
impl Default for Board {
@@ -138,6 +139,7 @@ impl Default for Board {
138139
memory_mb: 0,
139140
chipset: Chipset::I440Fx(I440Fx { enable_pcie: false }),
140141
guest_hv_interface: GuestHypervisorInterface::Bhyve,
142+
native_acpi_tables: false,
141143
}
142144
}
143145
}

bin/propolis-standalone/src/main.rs

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ const PAGE_OFFSET: u64 = 0xfff;
4343
// Arbitrary ROM limit for now
4444
const MAX_ROM_SIZE: usize = 0x20_0000;
4545

46+
const PCIE_ECAM_BASE: usize = 0xe000_0000;
47+
const PCIE_ECAM_SIZE: usize = 0x1000_0000;
48+
const MEM_32BIT_DEVICES_START: usize = 0xc000_0000;
49+
const MEM_32BIT_DEVICES_END: usize = 0xfc00_0000;
50+
const HIGHMEM_START: usize = 0x1_0000_0000;
51+
4652
const MIN_RT_THREADS: usize = 8;
4753
const BASE_RT_THREADS: usize = 4;
4854

@@ -738,15 +744,18 @@ fn build_machine(
738744
.max_cpus(max_cpu)?
739745
.add_mem_region(0, lowmem, "lowmem")?
740746
.add_rom_region(0x1_0000_0000 - MAX_ROM_SIZE, MAX_ROM_SIZE, "bootrom")?
741-
.add_mmio_region(0xc000_0000, 0x2000_0000, "dev32")?
742-
.add_mmio_region(0xe000_0000, 0x1000_0000, "pcicfg")?;
747+
.add_mmio_region(lowmem, PCIE_ECAM_BASE - lowmem, "dev32")?
748+
.add_mmio_region(
749+
PCIE_ECAM_BASE,
750+
MEM_32BIT_DEVICES_END - PCIE_ECAM_BASE,
751+
"pcicfg",
752+
)?;
743753

744-
let highmem_start = 0x1_0000_0000;
745754
if highmem > 0 {
746-
builder = builder.add_mem_region(highmem_start, highmem, "highmem")?;
755+
builder = builder.add_mem_region(HIGHMEM_START, highmem, "highmem")?;
747756
}
748757

749-
let dev64_start = highmem_start + highmem;
758+
let dev64_start = HIGHMEM_START + highmem;
750759
builder = builder.add_mmio_region(
751760
dev64_start,
752761
vmm::MAX_PHYSMEM - dev64_start,
@@ -974,9 +983,13 @@ fn generate_e820(
974983
MapType::Dram => {
975984
e820_table.add_mem(addr, len);
976985
}
977-
_ => {
986+
MapType::Rom => {
978987
e820_table.add_reserved(addr, len);
979988
}
989+
MapType::Mmio => {
990+
// MMIO is described in the DSDT _CRS and should not
991+
// appear in E820.
992+
}
980993
}
981994
}
982995

@@ -1375,6 +1388,37 @@ fn setup_instance(
13751388
let e820_entry = generate_e820(machine, log).expect("can build E820 table");
13761389
fwcfg.insert_named("etc/e820", e820_entry).unwrap();
13771390

1391+
let acpi_tables =
1392+
fwcfg::formats::build_acpi_tables(&fwcfg::formats::AcpiConfig {
1393+
num_cpus: cpus,
1394+
pcie_ecam_base: PCIE_ECAM_BASE as u64,
1395+
pcie_ecam_size: PCIE_ECAM_SIZE as u64,
1396+
pcie_mmio32_base: MEM_32BIT_DEVICES_START as u64,
1397+
pcie_mmio32_limit: (MEM_32BIT_DEVICES_END - 1) as u64,
1398+
pcie_mmio64_base: (HIGHMEM_START + highmem) as u64,
1399+
pcie_mmio64_limit: vmm::MAX_PHYSMEM as u64 - 1,
1400+
com_ports: vec![
1401+
(ibmpc::PORT_COM1, ibmpc::IRQ_COM1),
1402+
(ibmpc::PORT_COM2, ibmpc::IRQ_COM2),
1403+
],
1404+
..Default::default()
1405+
});
1406+
fwcfg
1407+
.insert_named(
1408+
"etc/acpi/tables",
1409+
fwcfg::Entry::Bytes(acpi_tables.tables),
1410+
)
1411+
.context("failed to insert ACPI tables")?;
1412+
fwcfg
1413+
.insert_named("etc/acpi/rsdp", fwcfg::Entry::Bytes(acpi_tables.rsdp))
1414+
.context("failed to insert ACPI RSDP")?;
1415+
fwcfg
1416+
.insert_named(
1417+
"etc/table-loader",
1418+
fwcfg::Entry::Bytes(acpi_tables.loader),
1419+
)
1420+
.context("failed to insert ACPI table-loader")?;
1421+
13781422
fwcfg.attach(pio, &machine.acc_mem);
13791423

13801424
guard.inventory.register(&fwcfg);

crates/propolis-api-types/src/instance_spec/components/board.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,5 +178,14 @@ pub struct Board {
178178
/// default values from the host's CPUID values.
179179
#[serde(default, skip_serializing_if = "Option::is_none")]
180180
pub cpuid: Option<Cpuid>,
181+
182+
/// Use native ACPI tables (via fw_cfg) instead of OVMF provided tables.
183+
///
184+
/// VMs created before propolis supported ACPI table generation will not
185+
/// have this field. For backwards compatibility with live migration,
186+
/// `None` is treated as `false` (use OVMF tables). New VMs should set
187+
/// this to `true`.
188+
#[serde(default, skip_serializing_if = "Option::is_none")]
189+
pub native_acpi_tables: Option<bool>,
181190
// TODO: Processor and NUMA topology.
182191
}

phd-tests/framework/src/test_vm/config.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ pub struct VmConfig<'dr> {
5656
disks: Vec<DiskRequest<'dr>>,
5757
migration_failure: Option<MigrationFailureInjector>,
5858
guest_hv_interface: Option<GuestHypervisorInterface>,
59+
native_acpi_tables: bool,
5960
}
6061

6162
impl<'dr> VmConfig<'dr> {
@@ -76,6 +77,7 @@ impl<'dr> VmConfig<'dr> {
7677
disks: Vec::new(),
7778
migration_failure: None,
7879
guest_hv_interface: None,
80+
native_acpi_tables: true,
7981
};
8082

8183
config.boot_disk(
@@ -151,6 +153,11 @@ impl<'dr> VmConfig<'dr> {
151153
self
152154
}
153155

156+
pub fn native_acpi_tables(&mut self, enabled: bool) -> &mut Self {
157+
self.native_acpi_tables = enabled;
158+
self
159+
}
160+
154161
/// Add a new disk to the VM config, and add it to the front of the VM's
155162
/// boot order.
156163
///
@@ -221,6 +228,7 @@ impl<'dr> VmConfig<'dr> {
221228
disks,
222229
migration_failure,
223230
guest_hv_interface,
231+
native_acpi_tables,
224232
} = self;
225233

226234
let bootrom_path = framework
@@ -302,6 +310,7 @@ impl<'dr> VmConfig<'dr> {
302310
.as_ref()
303311
.cloned()
304312
.unwrap_or_default(),
313+
native_acpi_tables: Some(*native_acpi_tables),
305314
},
306315
components: Default::default(),
307316
smbios: None,

0 commit comments

Comments
 (0)