blob: 36effbb3cb6e5ab11bfad994c2a7606553faeaa3 [file] [log] [blame]
// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use acpi_tables::{facs::FACS, rsdp::RSDP, sdt::SDT};
use base::error;
use data_model::DataInit;
use vm_memory::{GuestAddress, GuestMemory};
pub struct ACPIDevResource {
pub amls: Vec<u8>,
pub pm_iobase: u64,
/// Additional system descriptor tables.
pub sdts: Vec<SDT>,
}
#[repr(C)]
#[derive(Clone, Copy, Default)]
struct LocalAPIC {
_type: u8,
_length: u8,
_processor_id: u8,
_apic_id: u8,
_flags: u32,
}
// Safe as LocalAPIC structure only contains raw data
unsafe impl DataInit for LocalAPIC {}
#[repr(C)]
#[derive(Clone, Copy, Default)]
struct IOAPIC {
_type: u8,
_length: u8,
_ioapic_id: u8,
_reserved: u8,
_apic_address: u32,
_gsi_base: u32,
}
// Safe as IOAPIC structure only contains raw data
unsafe impl DataInit for IOAPIC {}
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;
// FADT fields offset
const FADT_FIELD_FACS_ADDR32: usize = 36;
const FADT_FIELD_DSDT_ADDR32: usize = 40;
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_PM1A_CONTROL_BLK_ADDR: usize = 64;
const FADT_FIELD_PM1A_EVENT_BLK_LEN: usize = 88;
const FADT_FIELD_PM1A_CONTROL_BLK_LEN: usize = 89;
const FADT_FIELD_FLAGS: usize = 112;
const FADT_FIELD_MINOR_REVISION: usize = 131;
const FADT_FIELD_FACS_ADDR: usize = 132;
const FADT_FIELD_DSDT_ADDR: usize = 140;
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;
// 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;
// MADT flags
const MADT_ENABLED: u32 = 1;
// XSDT
const XSDT_REVISION: u8 = 1;
fn create_dsdt_table(amls: Vec<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.as_slice());
}
dsdt
}
fn create_facp_table(sci_irq: u16, pm_iobase: u32) -> SDT {
let mut facp = SDT::new(
*b"FACP",
FADT_LEN,
FADT_REVISION,
*b"CROSVM",
*b"CROSVMDT",
OEM_REVISION,
);
let fadt_flags: u32 = FADT_POWER_BUTTON | FADT_SLEEP_BUTTON; // mask POWER and SLEEP BUTTON
facp.write(FADT_FIELD_FLAGS, fadt_flags);
// SCI Interrupt
facp.write(FADT_FIELD_SCI_INTERRUPT, sci_irq);
// PM1A Event Block Address
facp.write(FADT_FIELD_PM1A_EVENT_BLK_ADDR, pm_iobase);
// PM1A Control Block Address
facp.write(
FADT_FIELD_PM1A_CONTROL_BLK_ADDR,
pm_iobase + devices::acpi::ACPIPM_RESOURCE_EVENTBLK_LEN as u32,
);
// PM1 Event Block Length
facp.write(
FADT_FIELD_PM1A_EVENT_BLK_LEN,
devices::acpi::ACPIPM_RESOURCE_EVENTBLK_LEN as u8,
);
// PM1 Control Block Length
facp.write(
FADT_FIELD_PM1A_CONTROL_BLK_LEN,
devices::acpi::ACPIPM_RESOURCE_CONTROLBLK_LEN as u8,
);
facp.write(FADT_FIELD_MINOR_REVISION, FADT_MINOR_REVISION); // FADT minor version
facp.write(FADT_FIELD_HYPERVISOR_ID, *b"CROSVM"); // Hypervisor Vendor Identity
facp
}
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)?),
}
}
/// 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
pub fn create_acpi_tables(
guest_mem: &GuestMemory,
num_cpus: u8,
sci_irq: u32,
acpi_dev_resource: ACPIDevResource,
) -> 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_slice(), 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 pm_iobase = acpi_dev_resource.pm_iobase as u32;
let mut facp = facp.unwrap_or_else(|| create_facp_table(sci_irq as u16, pm_iobase));
// Crosvm FACP overrides.
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 as u64);
facp.write(FADT_FIELD_DSDT_ADDR, dsdt_offset.0 as u64);
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 as u32,
);
for cpu in 0..num_cpus {
let lapic = 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(lapic);
}
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()
});
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)?;
// 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_slice(), rsdp_offset).ok()?;
Some(rsdp_offset)
}