blob: bd25e8fbed860ba8257eff143354be67405ca5a2 [file] [log] [blame]
// Copyright 2020 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::arch::x86_64::CpuidResult;
use std::arch::x86_64::__cpuid;
use std::arch::x86_64::__cpuid_count;
use std::collections::BTreeMap;
use std::sync::Arc;
use acpi_tables::aml;
use acpi_tables::facs::FACS;
use acpi_tables::rsdp::RSDP;
use acpi_tables::sdt::SDT;
use arch::CpuSet;
use arch::VcpuAffinity;
use base::error;
use base::warn;
use devices::ACPIPMResource;
use devices::PciAddress;
use devices::PciInterruptPin;
use devices::PciRoot;
use sync::Mutex;
use vm_memory::GuestAddress;
use vm_memory::GuestMemory;
use zerocopy::AsBytes;
use zerocopy::FromBytes;
use zerocopy::FromZeroes;
pub struct AcpiDevResource {
pub amls: Vec<u8>,
pub pm_iobase: u64,
pub pm: Arc<Mutex<ACPIPMResource>>,
/// Additional system descriptor tables.
pub sdts: Vec<SDT>,
}
#[repr(C, packed)]
#[derive(Clone, Copy, Default, FromZeroes, FromBytes, AsBytes)]
struct GenericAddress {
_space_id: u8,
_bit_width: u8,
_bit_offset: u8,
_access_width: u8,
_address: u64,
}
#[repr(C)]
#[derive(Clone, Copy, Default, AsBytes)]
struct LocalApic {
_type: u8,
_length: u8,
_processor_id: u8,
_apic_id: u8,
_flags: u32,
}
#[repr(C)]
#[derive(Clone, Copy, Default, FromZeroes, FromBytes, AsBytes)]
struct Ioapic {
_type: u8,
_length: u8,
_ioapic_id: u8,
_reserved: u8,
_apic_address: u32,
_gsi_base: u32,
}
#[repr(C, packed)]
#[derive(Clone, Copy, Default, FromZeroes, FromBytes, AsBytes)]
struct IoapicInterruptSourceOverride {
_type: u8,
_length: u8,
_bus: u8,
_source: u8,
_gsi: u32,
_flags: u16,
}
#[repr(C)]
#[derive(Clone, Copy, Default, FromZeroes, FromBytes, AsBytes)]
struct Localx2Apic {
_type: u8,
_length: u8,
_reserved: u16,
_x2apic_id: u32,
_flags: u32,
_processor_id: u32,
}
// Space ID for GenericAddress
const ADR_SPACE_SYSTEM_IO: u8 = 1;
const OEM_REVISION: u32 = 1;
//DSDT
const DSDT_REVISION: u8 = 6;
// FADT
const FADT_LEN: u32 = 276;
const FADT_REVISION: u8 = 6;
const FADT_MINOR_REVISION: u8 = 3;
// FADT flags
const FADT_POWER_BUTTON: u32 = 1 << 4;
const _FADT_SLEEP_BUTTON: u32 = 1 << 5;
const FADT_RESET_REGISTER: u32 = 1 << 10;
const FADT_LOW_POWER_S2IDLE: u32 = 1 << 21;
// FADT fields offset
const FADT_FIELD_FACS_ADDR32: usize = 36;
const FADT_FIELD_DSDT_ADDR32: usize = 40;
pub const FADT_FIELD_SCI_INTERRUPT: usize = 46;
const FADT_FIELD_SMI_COMMAND: usize = 48;
const FADT_FIELD_PM1A_EVENT_BLK_ADDR: usize = 56;
const FADT_FIELD_PM1B_EVENT_BLK_ADDR: usize = 60;
const FADT_FIELD_PM1A_CONTROL_BLK_ADDR: usize = 64;
const FADT_FIELD_PM1B_CONTROL_BLK_ADDR: usize = 68;
const FADT_FIELD_PM2_CONTROL_BLK_ADDR: usize = 72;
const FADT_FIELD_PM_TMR_BLK_ADDR: usize = 76;
const FADT_FIELD_GPE0_BLK_ADDR: usize = 80;
const FADT_FIELD_GPE1_BLK_ADDR: usize = 84;
const FADT_FIELD_PM1A_EVENT_BLK_LEN: usize = 88;
const FADT_FIELD_PM1A_CONTROL_BLK_LEN: usize = 89;
const FADT_FIELD_PM2_CONTROL_BLK_LEN: usize = 90;
const FADT_FIELD_PM_TMR_LEN: usize = 91;
const FADT_FIELD_GPE0_BLK_LEN: usize = 92;
const FADT_FIELD_GPE1_BLK_LEN: usize = 93;
const FADT_FIELD_GPE1_BASE: usize = 94;
const FADT_FIELD_RTC_DAY_ALARM: usize = 106;
const FADT_FIELD_RTC_MONTH_ALARM: usize = 107;
const FADT_FIELD_RTC_CENTURY: usize = 108;
const FADT_FIELD_FLAGS: usize = 112;
const FADT_FIELD_RESET_REGISTER: usize = 116;
const FADT_FIELD_RESET_VALUE: usize = 128;
const FADT_FIELD_MINOR_REVISION: usize = 131;
const FADT_FIELD_FACS_ADDR: usize = 132;
const FADT_FIELD_DSDT_ADDR: usize = 140;
const FADT_FIELD_X_PM1A_EVENT_BLK_ADDR: usize = 148;
const FADT_FIELD_X_PM1B_EVENT_BLK_ADDR: usize = 160;
const FADT_FIELD_X_PM1A_CONTROL_BLK_ADDR: usize = 172;
const FADT_FIELD_X_PM1B_CONTROL_BLK_ADDR: usize = 184;
const FADT_FIELD_X_PM2_CONTROL_BLK_ADDR: usize = 196;
const FADT_FIELD_X_PM_TMR_BLK_ADDR: usize = 208;
const FADT_FIELD_X_GPE0_BLK_ADDR: usize = 220;
const FADT_FIELD_X_GPE1_BLK_ADDR: usize = 232;
const FADT_FIELD_HYPERVISOR_ID: usize = 268;
// MADT
const MADT_LEN: u32 = 44;
const MADT_REVISION: u8 = 5;
// MADT fields offset
const MADT_FIELD_LAPIC_ADDR: usize = 36;
const MADT_FIELD_FLAGS: usize = 40;
// MADT flags
const MADT_FLAG_PCAT_COMPAT: u32 = 1 << 0;
// MADT structure offsets
const MADT_STRUCTURE_TYPE: usize = 0;
const MADT_STRUCTURE_LEN: usize = 1;
// MADT types
const MADT_TYPE_LOCAL_APIC: u8 = 0;
const MADT_TYPE_IO_APIC: u8 = 1;
const MADT_TYPE_INTERRUPT_SOURCE_OVERRIDE: u8 = 2;
const MADT_TYPE_LOCAL_X2APIC: u8 = 9;
// MADT flags
const MADT_ENABLED: u32 = 1;
const MADT_INT_POLARITY_ACTIVE_LOW: u16 = 0b11;
const MADT_INT_TRIGGER_LEVEL: u16 = 0b11 << 2;
// MADT compatibility
const MADT_MIN_LOCAL_APIC_ID: u32 = 255;
// XSDT
const XSDT_REVISION: u8 = 1;
const CPUID_LEAF0_EBX_CPUID_SHIFT: u32 = 24; // Offset of initial apic id.
// MCFG
const MCFG_LEN: u32 = 60;
const MCFG_REVISION: u8 = 1;
const MCFG_FIELD_BASE_ADDRESS: usize = 44;
const MCFG_FIELD_START_BUS_NUMBER: usize = 54;
const MCFG_FIELD_END_BUS_NUMBER: usize = 55;
const SSDT_REVISION: u8 = 2;
pub fn create_customize_ssdt(
pci_root: Arc<Mutex<PciRoot>>,
amls: BTreeMap<PciAddress, Vec<u8>>,
gpe_scope_amls: BTreeMap<PciAddress, Vec<u8>>,
) -> Option<SDT> {
if amls.is_empty() {
return None;
}
let mut ssdt = SDT::new(
*b"SSDT",
acpi_tables::HEADER_LEN,
SSDT_REVISION,
*b"CROSVM",
*b"CROSVMDT",
OEM_REVISION,
);
for (address, children) in amls {
if let Some(path) = pci_root.lock().acpi_path(&address) {
ssdt.append_slice(&aml::Scope::raw((*path).into(), children));
}
}
for children in gpe_scope_amls.values() {
ssdt.append_slice(children);
}
Some(ssdt)
}
fn create_dsdt_table(amls: &[u8]) -> SDT {
let mut dsdt = SDT::new(
*b"DSDT",
acpi_tables::HEADER_LEN,
DSDT_REVISION,
*b"CROSVM",
*b"CROSVMDT",
OEM_REVISION,
);
if !amls.is_empty() {
dsdt.append_slice(amls);
}
dsdt
}
fn create_facp_table(sci_irq: u16, force_s2idle: bool) -> SDT {
let mut facp = SDT::new(
*b"FACP",
FADT_LEN,
FADT_REVISION,
*b"CROSVM",
*b"CROSVMDT",
OEM_REVISION,
);
let mut fadt_flags: u32 = 0;
if force_s2idle {
fadt_flags |= FADT_LOW_POWER_S2IDLE;
}
facp.write(FADT_FIELD_FLAGS, fadt_flags);
// SCI Interrupt
facp.write(FADT_FIELD_SCI_INTERRUPT, sci_irq);
facp.write(FADT_FIELD_MINOR_REVISION, FADT_MINOR_REVISION); // FADT minor version
facp.write(FADT_FIELD_HYPERVISOR_ID, *b"CROSVM"); // Hypervisor Vendor Identity
facp.write::<u8>(FADT_FIELD_RTC_CENTURY, devices::cmos::RTC_REG_CENTURY);
facp.write::<u8>(FADT_FIELD_RTC_DAY_ALARM, devices::cmos::RTC_REG_ALARM_DAY);
facp.write::<u8>(
FADT_FIELD_RTC_MONTH_ALARM,
devices::cmos::RTC_REG_ALARM_MONTH,
);
facp
}
// Write virtualized FADT fields
fn write_facp_overrides(
facp: &mut SDT,
facs_offset: GuestAddress,
dsdt_offset: GuestAddress,
pm_iobase: u32,
reset_port: u32,
reset_value: u8,
) {
let fadt_flags: u32 = facp.read(FADT_FIELD_FLAGS);
// indicate we support FADT RESET_REG
facp.write(FADT_FIELD_FLAGS, fadt_flags | FADT_RESET_REGISTER);
facp.write(FADT_FIELD_SMI_COMMAND, 0u32);
facp.write(FADT_FIELD_FACS_ADDR32, 0u32);
facp.write(FADT_FIELD_DSDT_ADDR32, 0u32);
facp.write(FADT_FIELD_FACS_ADDR, facs_offset.0);
facp.write(FADT_FIELD_DSDT_ADDR, dsdt_offset.0);
// PM1A Event Block Address
facp.write(FADT_FIELD_PM1A_EVENT_BLK_ADDR, pm_iobase);
// PM1B Event Block Address (not supported)
facp.write(FADT_FIELD_PM1B_EVENT_BLK_ADDR, 0u32);
// PM1A Control Block Address
facp.write(
FADT_FIELD_PM1A_CONTROL_BLK_ADDR,
pm_iobase + devices::acpi::ACPIPM_RESOURCE_EVENTBLK_LEN as u32,
);
// PM1B Control Block Address (not supported)
facp.write(FADT_FIELD_PM1B_CONTROL_BLK_ADDR, 0u32);
// PM2 Control Block Address (not supported)
facp.write(FADT_FIELD_PM2_CONTROL_BLK_ADDR, 0u32);
// PM Timer Control Block Address (not supported)
facp.write(FADT_FIELD_PM_TMR_BLK_ADDR, 0u32);
// GPE0 Block Address
facp.write(
FADT_FIELD_GPE0_BLK_ADDR,
pm_iobase + devices::acpi::ACPIPM_RESOURCE_EVENTBLK_LEN as u32 + 4,
);
// GPE1 Block Address (not supported)
facp.write(FADT_FIELD_GPE1_BLK_ADDR, 0u32);
// PM1 Event Block Length
facp.write(
FADT_FIELD_PM1A_EVENT_BLK_LEN,
devices::acpi::ACPIPM_RESOURCE_EVENTBLK_LEN,
);
// PM1 Control Block Length
facp.write(
FADT_FIELD_PM1A_CONTROL_BLK_LEN,
devices::acpi::ACPIPM_RESOURCE_CONTROLBLK_LEN,
);
// PM2 Control Block Length (not supported)
facp.write(FADT_FIELD_PM2_CONTROL_BLK_LEN, 0u8);
// PM Timer Control Block Length (not supported)
facp.write(FADT_FIELD_PM_TMR_LEN, 0u8);
// GPE0 Block Length
facp.write(
FADT_FIELD_GPE0_BLK_LEN,
devices::acpi::ACPIPM_RESOURCE_GPE0_BLK_LEN,
);
// GPE1 Block Length (not supported)
facp.write(FADT_FIELD_GPE1_BLK_LEN, 0u8);
// GPE1 Base (not supported)
facp.write(FADT_FIELD_GPE1_BASE, 0u8);
// PM1A Extended Event Block Address (not supported)
facp.write(
FADT_FIELD_X_PM1A_EVENT_BLK_ADDR,
GenericAddress {
..Default::default()
},
);
// PM1B Extended Event Block Address (not supported)
facp.write(
FADT_FIELD_X_PM1B_EVENT_BLK_ADDR,
GenericAddress {
..Default::default()
},
);
// PM1A Extended Control Block Address (not supported)
facp.write(
FADT_FIELD_X_PM1A_CONTROL_BLK_ADDR,
GenericAddress {
..Default::default()
},
);
// PM1B Extended Control Block Address (not supported)
facp.write(
FADT_FIELD_X_PM1B_CONTROL_BLK_ADDR,
GenericAddress {
..Default::default()
},
);
// PM2 Extended Control Block Address (not supported)
facp.write(
FADT_FIELD_X_PM2_CONTROL_BLK_ADDR,
GenericAddress {
..Default::default()
},
);
// PM Timer Extended Control Block Address (not supported)
facp.write(
FADT_FIELD_X_PM_TMR_BLK_ADDR,
GenericAddress {
..Default::default()
},
);
// GPE0 Extended Address (not supported)
facp.write(
FADT_FIELD_X_GPE0_BLK_ADDR,
GenericAddress {
..Default::default()
},
);
// GPE1 Extended Address (not supported)
facp.write(
FADT_FIELD_X_GPE1_BLK_ADDR,
GenericAddress {
..Default::default()
},
);
// Reset register
facp.write(
FADT_FIELD_RESET_REGISTER,
GenericAddress {
_space_id: ADR_SPACE_SYSTEM_IO,
_bit_width: 8,
_bit_offset: 0,
_access_width: 1,
_address: reset_port.into(),
},
);
facp.write(FADT_FIELD_RESET_VALUE, reset_value);
}
fn next_offset(offset: GuestAddress, len: u64) -> Option<GuestAddress> {
// Enforce 64-byte allocation alignment.
match len % 64 {
0 => offset.checked_add(len),
x => offset.checked_add(len.checked_add(64 - x)?),
}
}
fn sync_acpi_id_from_cpuid(
madt: &mut SDT,
cpus: BTreeMap<usize, CpuSet>,
apic_ids: &mut Vec<usize>,
) -> base::Result<()> {
let cpu_set = match base::get_cpu_affinity() {
Err(e) => {
error!("Failed to get CPU affinity: {} when create MADT", e);
return Err(e);
}
Ok(c) => c,
};
for (vcpu, pcpu) in cpus {
let mut has_leafb = false;
let mut get_apic_id = false;
let mut apic_id: u8 = 0;
if let Err(e) = base::set_cpu_affinity(pcpu) {
error!("Failed to set CPU affinity: {} when create MADT", e);
return Err(e);
}
// SAFETY:
// Safe because we pass 0 and 0 for this call and the host supports the
// `cpuid` instruction
let mut cpuid_entry: CpuidResult = unsafe { __cpuid_count(0, 0) };
if cpuid_entry.eax >= 0xB {
// SAFETY:
// Safe because we pass 0xB and 0 for this call and the host supports the
// `cpuid` instruction
cpuid_entry = unsafe { __cpuid_count(0xB, 0) };
if cpuid_entry.ebx != 0 {
// MADT compatibility: (ACPI Spec v6.4) On some legacy OSes,
// Logical processors with APIC ID values less than 255 (whether in
// XAPIC or X2APIC mode) must use the Processor Local APIC structure.
if cpuid_entry.edx < MADT_MIN_LOCAL_APIC_ID {
apic_id = cpuid_entry.edx as u8;
get_apic_id = true;
} else {
// (ACPI Spec v6.4) When using the X2APIC, logical processors are
// required to have a processor device object in the DSDT and must
// convey the processor’s APIC information to OSPM using the Processor
// Local X2APIC structure.
// Now vCPUs use the DSDT passthrougt from host and the same APIC ID as
// the physical CPUs. Both of them should meet ACPI specifications on
// the host.
has_leafb = true;
let x2apic = Localx2Apic {
_type: MADT_TYPE_LOCAL_X2APIC,
_length: std::mem::size_of::<Localx2Apic>() as u8,
_x2apic_id: cpuid_entry.edx,
_flags: MADT_ENABLED,
_processor_id: (vcpu + 1) as u32,
..Default::default()
};
madt.append(x2apic);
apic_ids.push(cpuid_entry.edx as usize);
}
}
}
if !has_leafb {
if !get_apic_id {
// SAFETY:
// Safe because we pass 1 for this call and the host supports the
// `cpuid` instruction
cpuid_entry = unsafe { __cpuid(1) };
apic_id = (cpuid_entry.ebx >> CPUID_LEAF0_EBX_CPUID_SHIFT & 0xff) as u8;
}
let apic = LocalApic {
_type: MADT_TYPE_LOCAL_APIC,
_length: std::mem::size_of::<LocalApic>() as u8,
_processor_id: vcpu as u8,
_apic_id: apic_id,
_flags: MADT_ENABLED,
};
madt.append(apic);
apic_ids.push(apic_id as usize);
}
}
if let Err(e) = base::set_cpu_affinity(cpu_set) {
error!("Failed to reset CPU affinity: {} when create MADT", e);
return Err(e);
}
Ok(())
}
/// Create ACPI tables and return the RSDP.
/// The basic tables DSDT/FACP/MADT/XSDT are constructed in this function.
/// # Arguments
///
/// * `guest_mem` - The guest memory where the tables will be stored.
/// * `num_cpus` - Used to construct the MADT.
/// * `sci_irq` - Used to fill the FACP SCI_INTERRUPT field, which is going to be used by the ACPI
/// drivers to register sci handler.
/// * `acpi_dev_resource` - resouces needed by the ACPI devices for creating tables.
/// * `host_cpus` - The CPU affinity per CPU used to get corresponding CPUs' apic id and set these
/// apic id in MADT if `--host-cpu-topology` option is set.
/// * `apic_ids` - The apic id for vCPU will be sent to KVM by KVM_CREATE_VCPU ioctl.
/// * `pci_rqs` - PCI device to IRQ number assignments as returned by `arch::generate_pci_root()`
/// (device address, IRQ number, and PCI interrupt pin assignment).
/// * `pcie_cfg_mmio` - Base address for the pcie enhanced configuration access mechanism
/// * `max_bus` - Max bus number in MCFG table
pub fn create_acpi_tables(
guest_mem: &GuestMemory,
num_cpus: u8,
sci_irq: u32,
reset_port: u32,
reset_value: u8,
acpi_dev_resource: &AcpiDevResource,
host_cpus: Option<VcpuAffinity>,
apic_ids: &mut Vec<usize>,
pci_irqs: &[(PciAddress, u32, PciInterruptPin)],
pcie_cfg_mmio: u64,
max_bus: u8,
force_s2idle: bool,
) -> Option<GuestAddress> {
// RSDP is at the HI RSDP WINDOW
let rsdp_offset = GuestAddress(super::ACPI_HI_RSDP_WINDOW_BASE);
let facs_offset = next_offset(rsdp_offset, RSDP::len() as u64)?;
let mut offset = next_offset(facs_offset, FACS::len() as u64)?;
let mut dsdt_offset: Option<GuestAddress> = None;
let mut tables: Vec<u64> = Vec::new();
let mut facp: Option<SDT> = None;
let mut host_madt: Option<SDT> = None;
// User supplied System Description Tables, e.g. SSDT.
for sdt in acpi_dev_resource.sdts.iter() {
if sdt.is_signature(b"FACP") {
facp = Some(sdt.clone());
continue;
}
if sdt.is_signature(b"APIC") {
host_madt = Some(sdt.clone());
continue;
}
guest_mem.write_at_addr(sdt.as_slice(), offset).ok()?;
if sdt.is_signature(b"DSDT") {
dsdt_offset = Some(offset);
} else {
tables.push(offset.0);
}
offset = next_offset(offset, sdt.len() as u64)?;
}
// FACS
let facs = FACS::new();
guest_mem.write_at_addr(facs.as_bytes(), facs_offset).ok()?;
// DSDT
let dsdt_offset = match dsdt_offset {
Some(dsdt_offset) => dsdt_offset,
None => {
let dsdt_offset = offset;
let dsdt = create_dsdt_table(&acpi_dev_resource.amls);
guest_mem.write_at_addr(dsdt.as_slice(), offset).ok()?;
offset = next_offset(offset, dsdt.len() as u64)?;
dsdt_offset
}
};
// FACP aka FADT
let mut facp = facp.map_or_else(
|| create_facp_table(sci_irq as u16, force_s2idle),
|facp| {
let fadt_flags: u32 = facp.read(FADT_FIELD_FLAGS);
if fadt_flags & FADT_POWER_BUTTON != 0 {
warn!(
"Control Method Power Button is not supported. FADT flags = 0x{:x}",
fadt_flags
);
}
facp
},
);
write_facp_overrides(
&mut facp,
facs_offset,
dsdt_offset,
acpi_dev_resource.pm_iobase as u32,
reset_port,
reset_value,
);
guest_mem.write_at_addr(facp.as_slice(), offset).ok()?;
tables.push(offset.0);
offset = next_offset(offset, facp.len() as u64)?;
// MADT
let mut madt = SDT::new(
*b"APIC",
MADT_LEN,
MADT_REVISION,
*b"CROSVM",
*b"CROSVMDT",
OEM_REVISION,
);
madt.write(
MADT_FIELD_LAPIC_ADDR,
super::mptable::APIC_DEFAULT_PHYS_BASE,
);
// Our IrqChip implementations (the KVM in-kernel irqchip and the split irqchip) expose a pair
// of PC-compatible 8259 PICs.
madt.write(MADT_FIELD_FLAGS, MADT_FLAG_PCAT_COMPAT);
match host_cpus {
Some(VcpuAffinity::PerVcpu(cpus)) => {
sync_acpi_id_from_cpuid(&mut madt, cpus, apic_ids).ok()?;
}
_ => {
for cpu in 0..num_cpus {
let apic = LocalApic {
_type: MADT_TYPE_LOCAL_APIC,
_length: std::mem::size_of::<LocalApic>() as u8,
_processor_id: cpu,
_apic_id: cpu,
_flags: MADT_ENABLED,
};
madt.append(apic);
apic_ids.push(cpu as usize);
}
}
}
madt.append(Ioapic {
_type: MADT_TYPE_IO_APIC,
_length: std::mem::size_of::<Ioapic>() as u8,
_apic_address: super::mptable::IO_APIC_DEFAULT_PHYS_BASE,
..Default::default()
});
// Add interrupt overrides for the PCI IRQs so that they are reported as level triggered, as
// required by the PCI bus. The source and system GSI are identical, so this does not actually
// override the mapping; we just use it to set the level-triggered flag.
let mut unique_pci_irqs: Vec<u32> = pci_irqs.iter().map(|(_, irq_num, _)| *irq_num).collect();
unique_pci_irqs.sort_unstable();
unique_pci_irqs.dedup();
for irq_num in unique_pci_irqs {
madt.append(IoapicInterruptSourceOverride {
_type: MADT_TYPE_INTERRUPT_SOURCE_OVERRIDE,
_length: std::mem::size_of::<IoapicInterruptSourceOverride>() as u8,
_bus: 0, // ISA
_source: irq_num as u8,
_gsi: irq_num,
_flags: MADT_INT_POLARITY_ACTIVE_LOW | MADT_INT_TRIGGER_LEVEL,
});
}
if let Some(host_madt) = host_madt {
let mut idx = MADT_LEN as usize;
while idx + MADT_STRUCTURE_LEN < host_madt.len() {
let struct_type = host_madt.as_slice()[idx + MADT_STRUCTURE_TYPE];
let struct_len = host_madt.as_slice()[idx + MADT_STRUCTURE_LEN] as usize;
if struct_type == MADT_TYPE_INTERRUPT_SOURCE_OVERRIDE {
if idx + struct_len <= host_madt.len() {
madt.append_slice(&host_madt.as_slice()[idx..(idx + struct_len)]);
} else {
error!("Malformed host MADT");
}
}
idx += struct_len;
}
}
guest_mem.write_at_addr(madt.as_slice(), offset).ok()?;
tables.push(offset.0);
offset = next_offset(offset, madt.len() as u64)?;
// MCFG
let mut mcfg = SDT::new(
*b"MCFG",
MCFG_LEN,
MCFG_REVISION,
*b"CROSVM",
*b"CROSVMDT",
OEM_REVISION,
);
mcfg.write(MCFG_FIELD_BASE_ADDRESS, pcie_cfg_mmio);
mcfg.write(MCFG_FIELD_START_BUS_NUMBER, 0_u8);
mcfg.write(MCFG_FIELD_END_BUS_NUMBER, max_bus);
guest_mem.write_at_addr(mcfg.as_slice(), offset).ok()?;
tables.push(offset.0);
offset = next_offset(offset, madt.len() as u64)?;
// XSDT
let mut xsdt = SDT::new(
*b"XSDT",
acpi_tables::HEADER_LEN,
XSDT_REVISION,
*b"CROSVM",
*b"CROSVMDT",
OEM_REVISION,
);
for table in tables {
xsdt.append(table);
}
guest_mem.write_at_addr(xsdt.as_slice(), offset).ok()?;
// RSDP
let rsdp = RSDP::new(*b"CROSVM", offset.0);
guest_mem.write_at_addr(rsdp.as_bytes(), rsdp_offset).ok()?;
Some(rsdp_offset)
}
#[cfg(test)]
mod tests {
use crate::acpi::*;
#[test]
fn facp_table_creation() {
let sci_irq: u16 = 5;
let force_s2idle = true;
let facp = create_facp_table(sci_irq, force_s2idle);
assert_eq!(facp.read::<u32>(FADT_FIELD_FLAGS), FADT_LOW_POWER_S2IDLE);
assert_eq!(facp.read::<u16>(FADT_FIELD_SCI_INTERRUPT), sci_irq);
assert_eq!(
facp.read::<u8>(FADT_FIELD_MINOR_REVISION),
FADT_MINOR_REVISION
);
assert_eq!(facp.read::<[u8; 6]>(FADT_FIELD_HYPERVISOR_ID), *b"CROSVM");
assert_eq!(
facp.read::<u8>(FADT_FIELD_RTC_CENTURY),
devices::cmos::RTC_REG_CENTURY
);
assert_eq!(
facp.read::<u8>(FADT_FIELD_RTC_DAY_ALARM),
devices::cmos::RTC_REG_ALARM_DAY
);
assert_eq!(
facp.read::<u8>(FADT_FIELD_RTC_MONTH_ALARM),
devices::cmos::RTC_REG_ALARM_MONTH
);
}
}