Skip to content

Commit 810bd49

Browse files
committed
acpi: add acpi_variant and constructors
Introduce the concept of ACPI variant, which can be use to version ACPI tables and standandardize table generation using configuration structs and `new` constructor methods.
1 parent da9fa2f commit 810bd49

18 files changed

Lines changed: 425 additions & 221 deletions

File tree

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use propolis::chardev::{self, BlockingSource, Source};
3232
use propolis::common::{DeviceMetadataMap, Lifecycle, GB, MB, PAGE_SIZE};
3333
use propolis::cpuid::TopoKind;
3434
use propolis::enlightenment::Enlightenment;
35-
use propolis::firmware::smbios;
35+
use propolis::firmware::{acpi, smbios};
3636
use propolis::hw::bhyve::BhyveHpet;
3737
use propolis::hw::chipset::{i440fx, Chipset};
3838
use propolis::hw::ibmpc;
@@ -1467,6 +1467,7 @@ impl MachineInitializer<'_> {
14671467

14681468
fn generate_acpi_tables(
14691469
&self,
1470+
acpi_variant: acpi::AcpiVariant,
14701471
cpus: u8,
14711472
) -> Result<fwcfg::formats::AcpiTables, MachineInitError> {
14721473
let (lowmem, _) = get_spec_guest_ram_limits(self.spec);
@@ -1490,6 +1491,7 @@ impl MachineInitializer<'_> {
14901491
)?;
14911492

14921493
let config = &fwcfg::formats::AcpiConfig {
1494+
acpi_variant,
14931495
num_cpus: cpus,
14941496
pci_window_32,
14951497
pci_window_64: fwcfg::formats::PciWindow::empty(),
@@ -1508,6 +1510,7 @@ impl MachineInitializer<'_> {
15081510
&mut self,
15091511
cpus: u8,
15101512
bootrom_version: &Option<String>,
1513+
acpi_variant: acpi::AcpiVariant,
15111514
) -> Result<Arc<ramfb::RamFb>, MachineInitError> {
15121515
let fwcfg = fwcfg::FwCfg::new();
15131516
fwcfg
@@ -1546,7 +1549,7 @@ impl MachineInitializer<'_> {
15461549
.insert_named("etc/e820", e820_entry)
15471550
.map_err(|e| MachineInitError::FwcfgInsertFailed("e820", e))?;
15481551

1549-
let acpi_entries = self.generate_acpi_tables(cpus)?;
1552+
let acpi_entries = self.generate_acpi_tables(acpi_variant, cpus)?;
15501553
fwcfg.insert_named("etc/acpi/tables", acpi_entries.tables).map_err(
15511554
|e| MachineInitError::FwcfgInsertFailed("acpi/tables", e),
15521555
)?;

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ impl SpecBuilder {
100100
memory_mb: board.memory_mb,
101101
chipset: board.chipset,
102102
guest_hv_interface: board.guest_hv_interface,
103+
..Default::default()
103104
},
104105
cpuid,
105106
..Default::default()
@@ -403,6 +404,7 @@ mod test {
403404
memory_mb: 512,
404405
chipset: Chipset::I440Fx(I440Fx { enable_pcie: false }),
405406
guest_hv_interface: GuestHypervisorInterface::Bhyve,
407+
..Default::default()
406408
};
407409

408410
SpecBuilder {

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ use thiserror::Error;
4242
#[cfg(feature = "failure-injection")]
4343
use propolis_api_types::instance_spec::components::devices::MigrationFailureInjector;
4444

45+
use propolis::firmware::acpi::AcpiVariant;
4546
#[cfg(feature = "falcon")]
4647
use propolis_api_types::instance_spec::components::{
4748
backends::DlpiNetworkBackend,
@@ -160,6 +161,9 @@ pub(crate) struct Board {
160161
pub memory_mb: u64,
161162
pub chipset: Chipset,
162163
pub guest_hv_interface: GuestHypervisorInterface,
164+
165+
// XXX: expose via the API once more variants are implemented.
166+
pub acpi_variant: AcpiVariant,
163167
}
164168

165169
impl Default for Board {
@@ -169,6 +173,7 @@ impl Default for Board {
169173
memory_mb: 0,
170174
chipset: Chipset::I440Fx(I440Fx { enable_pcie: false }),
171175
guest_hv_interface: GuestHypervisorInterface::Bhyve,
176+
acpi_variant: AcpiVariant::V0,
172177
}
173178
}
174179
}

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -581,8 +581,11 @@ async fn initialize_vm_objects(
581581
.initialize_storage_devices(&chipset, options.nexus_client.clone())
582582
.await?;
583583

584-
let ramfb =
585-
init.initialize_fwcfg(spec.board.cpus, &options.bootrom_version)?;
584+
let ramfb = init.initialize_fwcfg(
585+
spec.board.cpus,
586+
&options.bootrom_version,
587+
spec.board.acpi_variant,
588+
)?;
586589

587590
// If we have a VM RoT, that RoT needs to be able to collect some
588591
// information about the guest before it can be actually usable. It will do

bin/propolis-standalone/src/config.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use serde::{Deserialize, Serialize};
1818

1919
use cpuid_profile_config::*;
2020
use propolis::block;
21+
use propolis::firmware::acpi::AcpiVariant;
2122
use propolis::hw::pci::Bdf;
2223

2324
use crate::cidata::build_cidata_be;
@@ -71,6 +72,12 @@ pub struct Main {
7172

7273
/// Request bootrom override boot order using the devices specified
7374
pub boot_order: Option<Vec<String>>,
75+
76+
/// ACPI table variant to use for the VM
77+
///
78+
/// Default: V0
79+
#[serde(default)]
80+
pub acpi_variant: AcpiVariant,
7481
}
7582

7683
#[derive(Copy, Clone, Debug, Deserialize, Serialize)]

bin/propolis-standalone/src/main.rs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use tokio::runtime;
2424

2525
use propolis::chardev::{BlockingSource, Sink, Source, UDSock};
2626
use propolis::common::{DeviceMetadataMap, GB, MB};
27-
use propolis::firmware::smbios;
27+
use propolis::firmware::{acpi, smbios};
2828
use propolis::hw::chipset::{i440fx, Chipset};
2929
use propolis::hw::ps2::ctrl::PS2Ctrl;
3030
use propolis::hw::qemu::fwcfg;
@@ -1081,11 +1081,15 @@ fn generate_bootorder(
10811081
}
10821082

10831083
fn generate_acpi_tables(
1084-
cpus: u8,
1084+
acpi_variant: acpi::AcpiVariant,
1085+
num_cpus: u8,
10851086
lowmem: usize,
10861087
inventory: &Inventory,
10871088
device_metadata: &DeviceMetadataMap,
1089+
log: &slog::Logger,
10881090
) -> anyhow::Result<fwcfg::formats::AcpiTables> {
1091+
slog::info!(log, "Generating ACPI tables with variant {:?}", acpi_variant);
1092+
10891093
let generators: Vec<_> = inventory
10901094
.devs
10911095
.values()
@@ -1105,7 +1109,8 @@ fn generate_acpi_tables(
11051109
.context("invalid PCI window range")?;
11061110

11071111
let config = &fwcfg::formats::AcpiConfig {
1108-
num_cpus: cpus,
1112+
acpi_variant,
1113+
num_cpus,
11091114
pci_window_32,
11101115
pci_window_64: fwcfg::formats::PciWindow::empty(),
11111116
dsdt_generators: &generators,
@@ -1487,9 +1492,15 @@ fn setup_instance(
14871492
let e820_entry = generate_e820(machine, log).expect("can build E820 table");
14881493
fwcfg.insert_named("etc/e820", e820_entry).unwrap();
14891494

1490-
let acpi_entries =
1491-
generate_acpi_tables(cpus, lowmem, &guard.inventory, &device_metadata)
1492-
.expect("can build ACPI tables");
1495+
let acpi_entries = generate_acpi_tables(
1496+
config.main.acpi_variant,
1497+
cpus,
1498+
lowmem,
1499+
&guard.inventory,
1500+
&device_metadata,
1501+
log,
1502+
)
1503+
.expect("can build ACPI tables");
14931504
fwcfg
14941505
.insert_named("etc/acpi/tables", acpi_entries.tables)
14951506
.context("Failed to insert ACPI tables")?;

lib/propolis/src/firmware/acpi/aml.rs

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,22 @@
44

55
//! Collection of AML helpers and wrappers.
66
7-
use acpi_tables::aml;
7+
use super::AcpiVariant;
8+
use acpi_tables::{aml, Aml, AmlSink};
9+
use std::collections::HashSet;
10+
11+
// Flags used to defined ACPI methods concurrency control.
12+
//
13+
// See ACPI section 19.6.84 "Method (Declare Control Method)" for authoritative
14+
// information about these flags.
15+
16+
/// Declare the ASL method marked as "Serialized", meaning it is not safe for
17+
/// use by multiple concurrent threads.
18+
pub const SERIALIZED: bool = true;
19+
20+
/// Declare the ASL method marked as "NotSerialized", meaning it is safe for
21+
/// concurrent access (does not declare objects internally, etc)
22+
pub const NOT_SERIALIZED: bool = false;
823

924
/// Creates an IO port with a fixed port number.
1025
///
@@ -22,18 +37,30 @@ pub fn io_port(port: u16, alignment: u8, length: u8) -> aml::IO {
2237
aml::IO::new(port, port, alignment, length)
2338
}
2439

25-
// Flags used to defined ACPI methods concurrency control.
26-
//
27-
// See ACPI section 19.6.84 "Method (Declare Control Method)" for authoritative
28-
// information about these flags.
29-
30-
/// Declare the ASL method marked as "Serialized", meaning it is not safe for
31-
/// use by multiple concurrent threads.
32-
pub const SERIALIZED: bool = true;
33-
34-
/// Declare the ASL method marked as "NotSerialized", meaning it is safe for
35-
/// concurrent access (does not declare objects internally, etc)
36-
pub const NOT_SERIALIZED: bool = false;
40+
/// Wrapper for an `Aml` that only writes the AML bytecode to the sink if the
41+
/// target [`AcpiVariant`] is present in the filter.
42+
pub struct AcpiVariantFilter<'a> {
43+
target: AcpiVariant,
44+
filter: HashSet<AcpiVariant>,
45+
inner: &'a dyn Aml,
46+
}
47+
impl<'a> AcpiVariantFilter<'a> {
48+
pub fn new(
49+
target: AcpiVariant,
50+
filter: Vec<AcpiVariant>,
51+
inner: &'a dyn Aml,
52+
) -> Self {
53+
Self { target, filter: HashSet::from_iter(filter), inner }
54+
}
55+
}
56+
impl<'a> Aml for AcpiVariantFilter<'a> {
57+
fn to_aml_bytes(&self, sink: &mut dyn AmlSink) {
58+
if !self.filter.contains(&self.target) {
59+
return;
60+
}
61+
self.inner.to_aml_bytes(sink);
62+
}
63+
}
3764

3865
/// Constructors for ACPI paths defined in the ACPI specification.
3966
///

0 commit comments

Comments
 (0)