cloud-hypervisor/vmm/src/acpi.rs
Sebastien Boeuf 7b9a110540 vmm: tdx: Pass ACPI tables through the HOB
Relying on helpers for creating the ACPI tables and to add each table to
the HOB, this patch connects the dot to provide the set of ACPI tables
to the TD firmware.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2022-01-20 16:50:55 +00:00

823 lines
25 KiB
Rust

// Copyright © 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
use crate::cpu::CpuManager;
use crate::device_manager::DeviceManager;
use crate::memory_manager::MemoryManager;
use crate::pci_segment::PciSegment;
use crate::{GuestMemoryMmap, GuestRegionMmap};
use acpi_tables::sdt::GenericAddress;
use acpi_tables::{aml::Aml, rsdp::Rsdp, sdt::Sdt};
#[cfg(target_arch = "aarch64")]
use arch::aarch64::DeviceInfoForFdt;
#[cfg(target_arch = "aarch64")]
use arch::DeviceType;
#[cfg(any(target_arch = "aarch64", feature = "acpi"))]
use arch::NumaNodes;
use bitflags::bitflags;
use pci::PciBdf;
use std::sync::{Arc, Mutex};
use std::time::Instant;
use vm_memory::{Address, ByteValued, Bytes, GuestAddress, GuestMemoryRegion};
/* Values for Type in APIC sub-headers */
#[cfg(target_arch = "x86_64")]
pub const ACPI_APIC_PROCESSOR: u8 = 0;
#[cfg(target_arch = "x86_64")]
pub const ACPI_APIC_IO: u8 = 1;
#[cfg(target_arch = "x86_64")]
pub const ACPI_APIC_XRUPT_OVERRIDE: u8 = 2;
#[cfg(target_arch = "aarch64")]
pub const ACPI_APIC_GENERIC_CPU_INTERFACE: u8 = 11;
#[cfg(target_arch = "aarch64")]
pub const ACPI_APIC_GENERIC_DISTRIBUTOR: u8 = 12;
#[cfg(target_arch = "aarch64")]
pub const ACPI_APIC_GENERIC_REDISTRIBUTOR: u8 = 14;
#[cfg(target_arch = "aarch64")]
pub const ACPI_APIC_GENERIC_TRANSLATOR: u8 = 15;
#[allow(dead_code)]
#[repr(packed)]
#[derive(Default)]
struct PciRangeEntry {
pub base_address: u64,
pub segment: u16,
pub start: u8,
pub end: u8,
_reserved: u32,
}
#[allow(dead_code)]
#[repr(packed)]
#[derive(Default)]
struct MemoryAffinity {
pub type_: u8,
pub length: u8,
pub proximity_domain: u32,
_reserved1: u16,
pub base_addr_lo: u32,
pub base_addr_hi: u32,
pub length_lo: u32,
pub length_hi: u32,
_reserved2: u32,
pub flags: u32,
_reserved3: u64,
}
#[allow(dead_code)]
#[repr(packed)]
#[derive(Default)]
struct ProcessorLocalX2ApicAffinity {
pub type_: u8,
pub length: u8,
_reserved1: u16,
pub proximity_domain: u32,
pub x2apic_id: u32,
pub flags: u32,
pub clock_domain: u32,
_reserved2: u32,
}
#[allow(dead_code)]
#[repr(packed)]
#[derive(Default)]
struct ProcessorGiccAffinity {
pub type_: u8,
pub length: u8,
pub proximity_domain: u32,
pub acpi_processor_uid: u32,
pub flags: u32,
pub clock_domain: u32,
}
bitflags! {
pub struct MemAffinityFlags: u32 {
const NOFLAGS = 0;
const ENABLE = 0b1;
const HOTPLUGGABLE = 0b10;
const NON_VOLATILE = 0b100;
}
}
impl MemoryAffinity {
fn from_region(
region: &Arc<GuestRegionMmap>,
proximity_domain: u32,
flags: MemAffinityFlags,
) -> Self {
Self::from_range(
region.start_addr().raw_value(),
region.len(),
proximity_domain,
flags,
)
}
fn from_range(
base_addr: u64,
size: u64,
proximity_domain: u32,
flags: MemAffinityFlags,
) -> Self {
let base_addr_lo = (base_addr & 0xffff_ffff) as u32;
let base_addr_hi = (base_addr >> 32) as u32;
let length_lo = (size & 0xffff_ffff) as u32;
let length_hi = (size >> 32) as u32;
MemoryAffinity {
type_: 1,
length: 40,
proximity_domain,
base_addr_lo,
base_addr_hi,
length_lo,
length_hi,
flags: flags.bits(),
..Default::default()
}
}
}
#[allow(dead_code)]
#[repr(packed)]
#[derive(Default)]
struct ViotVirtioPciNode {
pub type_: u8,
_reserved: u8,
pub length: u16,
pub pci_segment: u16,
pub pci_bdf_number: u16,
_reserved2: [u8; 8],
}
#[allow(dead_code)]
#[repr(packed)]
#[derive(Default)]
struct ViotPciRangeNode {
pub type_: u8,
_reserved: u8,
pub length: u16,
pub endpoint_start: u32,
pub pci_segment_start: u16,
pub pci_segment_end: u16,
pub pci_bdf_start: u16,
pub pci_bdf_end: u16,
pub output_node: u16,
_reserved2: [u8; 6],
}
pub fn create_dsdt_table(
device_manager: &Arc<Mutex<DeviceManager>>,
cpu_manager: &Arc<Mutex<CpuManager>>,
memory_manager: &Arc<Mutex<MemoryManager>>,
) -> Sdt {
// DSDT
let mut dsdt = Sdt::new(*b"DSDT", 36, 6, *b"CLOUDH", *b"CHDSDT ", 1);
let mut bytes = Vec::new();
device_manager.lock().unwrap().append_aml_bytes(&mut bytes);
cpu_manager.lock().unwrap().append_aml_bytes(&mut bytes);
memory_manager.lock().unwrap().append_aml_bytes(&mut bytes);
dsdt.append_slice(&bytes);
dsdt
}
fn create_facp_table(dsdt_offset: GuestAddress) -> Sdt {
// Revision 6 of the ACPI FADT table is 276 bytes long
let mut facp = Sdt::new(*b"FACP", 276, 6, *b"CLOUDH", *b"CHFACP ", 1);
// x86_64 specific fields
#[cfg(target_arch = "x86_64")]
{
// PM_TMR_BLK I/O port
facp.write(76, 0xb008u32);
// RESET_REG
facp.write(116, GenericAddress::io_port_address::<u8>(0x3c0));
// RESET_VALUE
facp.write(128, 1u8);
// X_PM_TMR_BLK
facp.write(208, GenericAddress::io_port_address::<u32>(0xb008));
// SLEEP_CONTROL_REG
facp.write(244, GenericAddress::io_port_address::<u8>(0x3c0));
// SLEEP_STATUS_REG
facp.write(256, GenericAddress::io_port_address::<u8>(0x3c0));
}
// aarch64 specific fields
#[cfg(target_arch = "aarch64")]
// ARM_BOOT_ARCH: enable PSCI with HVC enable-method
facp.write(129, 3u16);
// Architecture common fields
// HW_REDUCED_ACPI, RESET_REG_SUP, TMR_VAL_EXT
let fadt_flags: u32 = 1 << 20 | 1 << 10 | 1 << 8;
facp.write(112, fadt_flags);
// FADT minor version
facp.write(131, 3u8);
// X_DSDT
facp.write(140, dsdt_offset.0);
// Hypervisor Vendor Identity
facp.write(268, b"CLOUDHYP");
facp.update_checksum();
facp
}
fn create_mcfg_table(pci_segments: &[PciSegment]) -> Sdt {
let mut mcfg = Sdt::new(*b"MCFG", 36, 1, *b"CLOUDH", *b"CHMCFG ", 1);
// MCFG reserved 8 bytes
mcfg.append(0u64);
for segment in pci_segments {
// 32-bit PCI enhanced configuration mechanism
mcfg.append(PciRangeEntry {
base_address: segment.mmio_config_address,
segment: segment.id,
start: 0,
end: 0,
..Default::default()
});
}
mcfg
}
fn create_srat_table(numa_nodes: &NumaNodes) -> Sdt {
let mut srat = Sdt::new(*b"SRAT", 36, 3, *b"CLOUDH", *b"CHSRAT ", 1);
// SRAT reserved 12 bytes
srat.append_slice(&[0u8; 12]);
// Check the MemoryAffinity structure is the right size as expected by
// the ACPI specification.
assert_eq!(std::mem::size_of::<MemoryAffinity>(), 40);
for (node_id, node) in numa_nodes.iter() {
let proximity_domain = *node_id as u32;
for region in &node.memory_regions {
srat.append(MemoryAffinity::from_region(
region,
proximity_domain,
MemAffinityFlags::ENABLE,
))
}
for region in &node.hotplug_regions {
srat.append(MemoryAffinity::from_region(
region,
proximity_domain,
MemAffinityFlags::ENABLE | MemAffinityFlags::HOTPLUGGABLE,
))
}
#[cfg(target_arch = "x86_64")]
for section in &node.sgx_epc_sections {
srat.append(MemoryAffinity::from_range(
section.start().raw_value(),
section.size(),
proximity_domain,
MemAffinityFlags::ENABLE,
))
}
for cpu in &node.cpus {
let x2apic_id = *cpu as u32;
// Flags
// - Enabled = 1 (bit 0)
// - Reserved bits 1-31
let flags = 1;
#[cfg(target_arch = "x86_64")]
srat.append(ProcessorLocalX2ApicAffinity {
type_: 2,
length: 24,
proximity_domain,
x2apic_id,
flags,
clock_domain: 0,
..Default::default()
});
#[cfg(target_arch = "aarch64")]
srat.append(ProcessorGiccAffinity {
type_: 3,
length: 18,
proximity_domain,
acpi_processor_uid: x2apic_id,
flags,
clock_domain: 0,
});
}
}
srat
}
fn create_slit_table(numa_nodes: &NumaNodes) -> Sdt {
let mut slit = Sdt::new(*b"SLIT", 36, 1, *b"CLOUDH", *b"CHSLIT ", 1);
// Number of System Localities on 8 bytes.
slit.append(numa_nodes.len() as u64);
let existing_nodes: Vec<u32> = numa_nodes.keys().cloned().collect();
for (node_id, node) in numa_nodes.iter() {
let distances = &node.distances;
for i in existing_nodes.iter() {
let dist: u8 = if *node_id == *i {
10
} else if let Some(distance) = distances.get(i) {
*distance as u8
} else {
20
};
slit.append(dist);
}
}
slit
}
#[cfg(target_arch = "aarch64")]
fn create_gtdt_table() -> Sdt {
const ARCH_TIMER_NS_EL2_IRQ: u32 = 10;
const ARCH_TIMER_VIRT_IRQ: u32 = 11;
const ARCH_TIMER_S_EL1_IRQ: u32 = 13;
const ARCH_TIMER_NS_EL1_IRQ: u32 = 14;
const ACPI_GTDT_INTERRUPT_MODE_LEVEL: u32 = 0;
const ACPI_GTDT_CAP_ALWAYS_ON: u32 = 1 << 2;
let irqflags: u32 = ACPI_GTDT_INTERRUPT_MODE_LEVEL;
// GTDT
let mut gtdt = Sdt::new(*b"GTDT", 104, 2, *b"CLOUDH", *b"CHGTDT ", 1);
// Secure EL1 Timer GSIV
gtdt.write(48, (ARCH_TIMER_S_EL1_IRQ + 16) as u32);
// Secure EL1 Timer Flags
gtdt.write(52, irqflags);
// Non-Secure EL1 Timer GSIV
gtdt.write(56, (ARCH_TIMER_NS_EL1_IRQ + 16) as u32);
// Non-Secure EL1 Timer Flags
gtdt.write(60, (irqflags | ACPI_GTDT_CAP_ALWAYS_ON) as u32);
// Virtual EL1 Timer GSIV
gtdt.write(64, (ARCH_TIMER_VIRT_IRQ + 16) as u32);
// Virtual EL1 Timer Flags
gtdt.write(68, irqflags);
// EL2 Timer GSIV
gtdt.write(72, (ARCH_TIMER_NS_EL2_IRQ + 16) as u32);
// EL2 Timer Flags
gtdt.write(76, irqflags);
gtdt.update_checksum();
gtdt
}
#[cfg(target_arch = "aarch64")]
fn create_spcr_table(base_address: u64, gsi: u32) -> Sdt {
// SPCR
let mut spcr = Sdt::new(*b"SPCR", 80, 2, *b"CLOUDH", *b"CHSPCR ", 1);
// Interface Type
spcr.write(36, 3u8);
// Base Address in format ACPI Generic Address Structure
spcr.write(40, GenericAddress::mmio_address::<u8>(base_address));
// Interrupt Type: Bit[3] ARMH GIC interrupt
spcr.write(52, (1 << 3) as u8);
// Global System Interrupt used by the UART
spcr.write(54, (gsi as u32).to_le());
// Baud Rate: 3 = 9600
spcr.write(58, 3u8);
// Stop Bits: 1 Stop bit
spcr.write(60, 1u8);
// Flow Control: Bit[1] = RTS/CTS hardware flow control
spcr.write(61, (1 << 1) as u8);
// PCI Device ID: Not a PCI device
spcr.write(64, 0xffff_u16);
// PCI Vendor ID: Not a PCI device
spcr.write(66, 0xffff_u16);
spcr.update_checksum();
spcr
}
#[cfg(target_arch = "aarch64")]
fn create_dbg2_table(base_address: u64) -> Sdt {
let namespace = "_SB_.COM1";
let debug_device_info_offset = 44usize;
let debug_device_info_len: u16 = 22 /* BaseAddressRegisterOffset */ +
12 /* BaseAddressRegister */ +
4 /* AddressSize */ +
namespace.len() as u16 + 1 /* zero-terminated */;
let tbl_len: u32 = debug_device_info_offset as u32 + debug_device_info_len as u32;
let mut dbg2 = Sdt::new(*b"DBG2", tbl_len, 0, *b"CLOUDH", *b"CHDBG2 ", 1);
/* OffsetDbgDeviceInfo */
dbg2.write_u32(36, 44);
/* NumberDbgDeviceInfo */
dbg2.write_u32(40, 1);
/* Debug Device Information structure */
/* Offsets are calculated from the start of this structure. */
let namespace_offset = 38u16;
let base_address_register_offset = 22u16;
let address_size_offset = 34u16;
/* Revision */
dbg2.write_u8(debug_device_info_offset, 0);
/* Length */
dbg2.write_u16(debug_device_info_offset + 1, debug_device_info_len);
/* NumberofGenericAddressRegisters */
dbg2.write_u8(debug_device_info_offset + 3, 1);
/* NameSpaceStringLength */
dbg2.write_u16(debug_device_info_offset + 4, namespace.len() as u16 + 1);
/* NameSpaceStringOffset */
dbg2.write_u16(debug_device_info_offset + 6, namespace_offset);
/* OemDataLength */
dbg2.write_u16(debug_device_info_offset + 8, 0);
/* OemDataOffset */
dbg2.write_u16(debug_device_info_offset + 10, 0);
/* Port Type */
dbg2.write_u16(debug_device_info_offset + 12, 0x8000);
/* Port Subtype */
dbg2.write_u16(debug_device_info_offset + 14, 0x0003);
/* Reserved */
dbg2.write_u16(debug_device_info_offset + 16, 0);
/* BaseAddressRegisterOffset */
dbg2.write_u16(debug_device_info_offset + 18, base_address_register_offset);
/* AddressSizeOffset */
dbg2.write_u16(debug_device_info_offset + 20, address_size_offset);
/* BaseAddressRegister */
dbg2.write(
debug_device_info_offset + base_address_register_offset as usize,
GenericAddress::mmio_address::<u8>(base_address),
);
/* AddressSize */
dbg2.write_u32(
debug_device_info_offset + address_size_offset as usize,
0x1000,
);
/* NamespaceString, zero-terminated ASCII */
for (k, c) in namespace.chars().enumerate() {
dbg2.write_u8(
debug_device_info_offset + namespace_offset as usize + k,
c as u8,
);
}
dbg2.write_u8(
debug_device_info_offset + namespace_offset as usize + namespace.len(),
0,
);
dbg2.update_checksum();
dbg2
}
#[cfg(target_arch = "aarch64")]
fn create_iort_table(pci_segments: &[PciSegment]) -> Sdt {
const ACPI_IORT_NODE_ITS_GROUP: u8 = 0x00;
const ACPI_IORT_NODE_PCI_ROOT_COMPLEX: u8 = 0x02;
const ACPI_IORT_NODE_ROOT_COMPLEX_OFFSET: usize = 72;
const ACPI_IORT_NODE_ROOT_COMPLEX_SIZE: usize = 60;
// The IORT table containes:
// - Header (size = 40)
// - 1 x ITS Group Node (size = 24)
// - N x Root Complex Node (N = number of pci segments, size = 60 x N)
let iort_table_size: u32 = (ACPI_IORT_NODE_ROOT_COMPLEX_OFFSET
+ ACPI_IORT_NODE_ROOT_COMPLEX_SIZE * pci_segments.len())
as u32;
let mut iort = Sdt::new(*b"IORT", iort_table_size, 2, *b"CLOUDH", *b"CHIORT ", 1);
iort.write(36, ((1 + pci_segments.len()) as u32).to_le());
iort.write(40, (48u32).to_le());
// ITS group node
iort.write(48, ACPI_IORT_NODE_ITS_GROUP as u8);
// Length of the ITS group node in bytes
iort.write(49, (24u16).to_le());
// ITS counts
iort.write(64, (1u32).to_le());
// Root Complex Nodes
for (i, segment) in pci_segments.iter().enumerate() {
let node_offset: usize =
ACPI_IORT_NODE_ROOT_COMPLEX_OFFSET + i * ACPI_IORT_NODE_ROOT_COMPLEX_SIZE;
iort.write(node_offset, ACPI_IORT_NODE_PCI_ROOT_COMPLEX as u8);
// Length of the root complex node in bytes
iort.write(
node_offset + 1,
(ACPI_IORT_NODE_ROOT_COMPLEX_SIZE as u16).to_le(),
);
// Revision
iort.write(node_offset + 3, (3u8).to_le());
// Node ID
iort.write(node_offset + 4, (segment.id as u32).to_le());
// Mapping counts
iort.write(node_offset + 8, (1u32).to_le());
// Offset from the start of the RC node to the start of its Array of ID mappings
iort.write(node_offset + 12, (36u32).to_le());
// Fully coherent device
iort.write(node_offset + 16, (1u32).to_le());
// CCA = CPM = DCAS = 1
iort.write(node_offset + 24, 3u8);
// PCI segment number
iort.write(node_offset + 28, (segment.id as u32).to_le());
// Memory address size limit
iort.write(node_offset + 32, (64u8).to_le());
// From offset 32 onward is the space for ID mappings Array.
// Now we have only one mapping.
let mapping_offset: usize = node_offset + 36;
// The lowest value in the input range
iort.write(mapping_offset, (0u32).to_le());
// The number of IDs in the range minus one:
// This should cover all the devices of a segment:
// 1 (bus) x 32 (devices) x 8 (functions) = 256
// Note: Currently only 1 bus is supported in a segment.
iort.write(mapping_offset + 4, (255_u32).to_le());
// The lowest value in the output range
iort.write(mapping_offset + 8, ((256 * segment.id) as u32).to_le());
// id_mapping_array_output_reference should be
// the ITS group node (the first node) if no SMMU
iort.write(mapping_offset + 12, (48u32).to_le());
// Flags
iort.write(mapping_offset + 16, (0u32).to_le());
}
iort.update_checksum();
iort
}
fn create_viot_table(iommu_bdf: &PciBdf, devices_bdf: &[PciBdf]) -> Sdt {
// VIOT
let mut viot = Sdt::new(*b"VIOT", 36, 0, *b"CLOUDH", *b"CHVIOT ", 0);
// Node count
viot.append((devices_bdf.len() + 1) as u16);
// Node offset
viot.append(48u16);
// VIOT reserved 8 bytes
viot.append_slice(&[0u8; 8]);
// Virtio-iommu based on virtio-pci node
viot.append(ViotVirtioPciNode {
type_: 3,
length: 16,
pci_segment: iommu_bdf.segment(),
pci_bdf_number: iommu_bdf.into(),
..Default::default()
});
for device_bdf in devices_bdf {
viot.append(ViotPciRangeNode {
type_: 1,
length: 24,
endpoint_start: device_bdf.into(),
pci_segment_start: device_bdf.segment(),
pci_segment_end: device_bdf.segment(),
pci_bdf_start: device_bdf.into(),
pci_bdf_end: device_bdf.into(),
output_node: 48,
..Default::default()
});
}
viot
}
pub fn create_acpi_tables(
guest_mem: &GuestMemoryMmap,
device_manager: &Arc<Mutex<DeviceManager>>,
cpu_manager: &Arc<Mutex<CpuManager>>,
memory_manager: &Arc<Mutex<MemoryManager>>,
numa_nodes: &NumaNodes,
) -> GuestAddress {
let start_time = Instant::now();
let rsdp_offset = arch::layout::RSDP_POINTER;
let mut tables: Vec<u64> = Vec::new();
// DSDT
let dsdt = create_dsdt_table(device_manager, cpu_manager, memory_manager);
let dsdt_offset = rsdp_offset.checked_add(Rsdp::len() as u64).unwrap();
guest_mem
.write_slice(dsdt.as_slice(), dsdt_offset)
.expect("Error writing DSDT table");
// FACP aka FADT
let facp = create_facp_table(dsdt_offset);
let facp_offset = dsdt_offset.checked_add(dsdt.len() as u64).unwrap();
guest_mem
.write_slice(facp.as_slice(), facp_offset)
.expect("Error writing FACP table");
tables.push(facp_offset.0);
// MADT
let madt = cpu_manager.lock().unwrap().create_madt();
let madt_offset = facp_offset.checked_add(facp.len() as u64).unwrap();
guest_mem
.write_slice(madt.as_slice(), madt_offset)
.expect("Error writing MADT table");
tables.push(madt_offset.0);
let mut prev_tbl_len = madt.len() as u64;
let mut prev_tbl_off = madt_offset;
// PPTT
#[cfg(target_arch = "aarch64")]
{
let pptt = cpu_manager.lock().unwrap().create_pptt();
let pptt_offset = prev_tbl_off.checked_add(prev_tbl_len).unwrap();
guest_mem
.write_slice(pptt.as_slice(), pptt_offset)
.expect("Error writing PPTT table");
tables.push(pptt_offset.0);
prev_tbl_len = pptt.len() as u64;
prev_tbl_off = pptt_offset;
}
// GTDT
#[cfg(target_arch = "aarch64")]
{
let gtdt = create_gtdt_table();
let gtdt_offset = prev_tbl_off.checked_add(prev_tbl_len).unwrap();
guest_mem
.write_slice(gtdt.as_slice(), gtdt_offset)
.expect("Error writing GTDT table");
tables.push(gtdt_offset.0);
prev_tbl_len = gtdt.len() as u64;
prev_tbl_off = gtdt_offset;
}
// MCFG
let mcfg = create_mcfg_table(device_manager.lock().unwrap().pci_segments());
let mcfg_offset = prev_tbl_off.checked_add(prev_tbl_len).unwrap();
guest_mem
.write_slice(mcfg.as_slice(), mcfg_offset)
.expect("Error writing MCFG table");
tables.push(mcfg_offset.0);
prev_tbl_len = mcfg.len() as u64;
prev_tbl_off = mcfg_offset;
// SPCR and DBG2
#[cfg(target_arch = "aarch64")]
{
let is_serial_on = device_manager
.lock()
.unwrap()
.get_device_info()
.clone()
.get(&(DeviceType::Serial, DeviceType::Serial.to_string()))
.is_some();
let serial_device_addr = arch::layout::LEGACY_SERIAL_MAPPED_IO_START;
let serial_device_irq = if is_serial_on {
device_manager
.lock()
.unwrap()
.get_device_info()
.clone()
.get(&(DeviceType::Serial, DeviceType::Serial.to_string()))
.unwrap()
.irq()
} else {
// If serial is turned off, add a fake device with invalid irq.
31
};
// SPCR
let spcr = create_spcr_table(serial_device_addr, serial_device_irq);
let spcr_offset = prev_tbl_off.checked_add(prev_tbl_len).unwrap();
guest_mem
.write_slice(spcr.as_slice(), spcr_offset)
.expect("Error writing SPCR table");
tables.push(spcr_offset.0);
prev_tbl_len = spcr.len() as u64;
prev_tbl_off = spcr_offset;
// DBG2
let dbg2 = create_dbg2_table(serial_device_addr);
let dbg2_offset = prev_tbl_off.checked_add(prev_tbl_len).unwrap();
guest_mem
.write_slice(dbg2.as_slice(), dbg2_offset)
.expect("Error writing DBG2 table");
tables.push(dbg2_offset.0);
prev_tbl_len = dbg2.len() as u64;
prev_tbl_off = dbg2_offset;
}
// SRAT and SLIT
// Only created if the NUMA nodes list is not empty.
if !numa_nodes.is_empty() {
// SRAT
let srat = create_srat_table(numa_nodes);
let srat_offset = prev_tbl_off.checked_add(prev_tbl_len).unwrap();
guest_mem
.write_slice(srat.as_slice(), srat_offset)
.expect("Error writing SRAT table");
tables.push(srat_offset.0);
// SLIT
let slit = create_slit_table(numa_nodes);
let slit_offset = srat_offset.checked_add(srat.len() as u64).unwrap();
guest_mem
.write_slice(slit.as_slice(), slit_offset)
.expect("Error writing SRAT table");
tables.push(slit_offset.0);
prev_tbl_len = slit.len() as u64;
prev_tbl_off = slit_offset;
};
#[cfg(target_arch = "aarch64")]
{
let iort = create_iort_table(device_manager.lock().unwrap().pci_segments());
let iort_offset = prev_tbl_off.checked_add(prev_tbl_len).unwrap();
guest_mem
.write_slice(iort.as_slice(), iort_offset)
.expect("Error writing IORT table");
tables.push(iort_offset.0);
prev_tbl_len = iort.len() as u64;
prev_tbl_off = iort_offset;
}
// VIOT
if let Some((iommu_bdf, devices_bdf)) = device_manager.lock().unwrap().iommu_attached_devices()
{
let viot = create_viot_table(iommu_bdf, devices_bdf);
let viot_offset = prev_tbl_off.checked_add(prev_tbl_len).unwrap();
guest_mem
.write_slice(viot.as_slice(), viot_offset)
.expect("Error writing VIOT table");
tables.push(viot_offset.0);
prev_tbl_len = viot.len() as u64;
prev_tbl_off = viot_offset;
}
// XSDT
let mut xsdt = Sdt::new(*b"XSDT", 36, 1, *b"CLOUDH", *b"CHXSDT ", 1);
for table in tables {
xsdt.append(table);
}
xsdt.update_checksum();
let xsdt_offset = prev_tbl_off.checked_add(prev_tbl_len).unwrap();
guest_mem
.write_slice(xsdt.as_slice(), xsdt_offset)
.expect("Error writing XSDT table");
// RSDP
let rsdp = Rsdp::new(*b"CLOUDH", xsdt_offset.0);
guest_mem
.write_slice(rsdp.as_slice(), rsdp_offset)
.expect("Error writing RSDP");
info!(
"Generated ACPI tables: took {}µs",
Instant::now().duration_since(start_time).as_micros()
);
rsdp_offset
}
#[cfg(feature = "tdx")]
pub fn create_acpi_tables_tdx(
device_manager: &Arc<Mutex<DeviceManager>>,
cpu_manager: &Arc<Mutex<CpuManager>>,
memory_manager: &Arc<Mutex<MemoryManager>>,
numa_nodes: &NumaNodes,
) -> Vec<Sdt> {
// DSDT
let mut tables = vec![create_dsdt_table(
device_manager,
cpu_manager,
memory_manager,
)];
// FACP aka FADT
tables.push(create_facp_table(GuestAddress(0)));
// MADT
tables.push(cpu_manager.lock().unwrap().create_madt());
// MCFG
tables.push(create_mcfg_table(
device_manager.lock().unwrap().pci_segments(),
));
// SRAT and SLIT
// Only created if the NUMA nodes list is not empty.
if !numa_nodes.is_empty() {
// SRAT
tables.push(create_srat_table(numa_nodes));
// SLIT
tables.push(create_slit_table(numa_nodes));
};
// VIOT
if let Some((iommu_bdf, devices_bdf)) = device_manager.lock().unwrap().iommu_attached_devices()
{
tables.push(create_viot_table(iommu_bdf, devices_bdf));
}
tables
}