2019-08-14 16:14:34 +00:00
|
|
|
// Copyright © 2019 Intel Corporation
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
//
|
2020-08-27 17:32:03 +00:00
|
|
|
use crate::cpu::CpuManager;
|
|
|
|
use crate::device_manager::DeviceManager;
|
|
|
|
use crate::memory_manager::MemoryManager;
|
2020-09-04 09:26:43 +00:00
|
|
|
use crate::vm::NumaNodes;
|
2021-01-25 10:48:45 +00:00
|
|
|
#[cfg(target_arch = "x86_64")]
|
|
|
|
use acpi_tables::sdt::GenericAddress;
|
2021-03-25 15:57:27 +00:00
|
|
|
use acpi_tables::{aml::Aml, rsdp::Rsdp, sdt::Sdt};
|
2021-01-25 10:48:45 +00:00
|
|
|
|
2020-09-21 09:59:15 +00:00
|
|
|
use bitflags::bitflags;
|
2020-08-27 17:32:03 +00:00
|
|
|
use std::sync::{Arc, Mutex};
|
2020-09-21 09:59:15 +00:00
|
|
|
use vm_memory::GuestRegionMmap;
|
2020-08-27 17:32:03 +00:00
|
|
|
use vm_memory::{Address, ByteValued, Bytes, GuestAddress, GuestMemoryMmap, GuestMemoryRegion};
|
2019-09-27 13:11:50 +00:00
|
|
|
|
2021-01-31 02:16:17 +00:00
|
|
|
/* 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;
|
|
|
|
|
2019-08-20 06:31:44 +00:00
|
|
|
#[repr(packed)]
|
|
|
|
#[derive(Default)]
|
2021-03-25 17:01:21 +00:00
|
|
|
struct PciRangeEntry {
|
2019-08-20 06:31:44 +00:00
|
|
|
pub base_address: u64,
|
|
|
|
pub segment: u16,
|
|
|
|
pub start: u8,
|
|
|
|
pub end: u8,
|
2019-09-03 13:54:06 +00:00
|
|
|
_reserved: u32,
|
2019-08-20 06:31:44 +00:00
|
|
|
}
|
|
|
|
|
2020-08-27 17:32:03 +00:00
|
|
|
#[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,
|
|
|
|
}
|
|
|
|
|
2020-08-28 17:36:35 +00:00
|
|
|
#[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,
|
|
|
|
}
|
|
|
|
|
2020-09-21 09:59:15 +00:00
|
|
|
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 {
|
|
|
|
let base_addr = region.start_addr().raw_value();
|
|
|
|
let base_addr_lo = (base_addr & 0xffff_ffff) as u32;
|
|
|
|
let base_addr_hi = (base_addr >> 32) as u32;
|
|
|
|
let length = region.len() as u64;
|
|
|
|
let length_lo = (length & 0xffff_ffff) as u32;
|
|
|
|
let length_hi = (length >> 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()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-18 14:11:56 +00:00
|
|
|
pub fn create_dsdt_table(
|
2020-02-27 09:29:03 +00:00
|
|
|
device_manager: &Arc<Mutex<DeviceManager>>,
|
2019-12-06 15:25:57 +00:00
|
|
|
cpu_manager: &Arc<Mutex<CpuManager>>,
|
2020-01-10 16:11:32 +00:00
|
|
|
memory_manager: &Arc<Mutex<MemoryManager>>,
|
2021-03-25 15:57:27 +00:00
|
|
|
) -> Sdt {
|
2019-08-21 11:12:24 +00:00
|
|
|
// DSDT
|
2021-03-25 15:57:27 +00:00
|
|
|
let mut dsdt = Sdt::new(*b"DSDT", 36, 6, *b"CLOUDH", *b"CHDSDT ", 1);
|
2019-12-06 16:14:32 +00:00
|
|
|
|
2020-02-27 09:29:03 +00:00
|
|
|
dsdt.append_slice(device_manager.lock().unwrap().to_aml_bytes().as_slice());
|
2019-12-06 15:25:57 +00:00
|
|
|
dsdt.append_slice(cpu_manager.lock().unwrap().to_aml_bytes().as_slice());
|
2020-01-10 16:11:32 +00:00
|
|
|
dsdt.append_slice(memory_manager.lock().unwrap().to_aml_bytes().as_slice());
|
2019-08-21 11:12:24 +00:00
|
|
|
|
|
|
|
dsdt
|
|
|
|
}
|
2019-11-25 15:13:46 +00:00
|
|
|
|
2021-05-18 12:43:53 +00:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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() -> Sdt {
|
|
|
|
let mut mcfg = Sdt::new(*b"MCFG", 36, 1, *b"CLOUDH", *b"CHMCFG ", 1);
|
|
|
|
|
|
|
|
// MCFG reserved 8 bytes
|
|
|
|
mcfg.append(0u64);
|
|
|
|
|
|
|
|
// 32-bit PCI enhanced configuration mechanism
|
|
|
|
mcfg.append(PciRangeEntry {
|
|
|
|
base_address: arch::layout::PCI_MMCONFIG_START.0,
|
|
|
|
segment: 0,
|
|
|
|
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,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
|
|
|
for cpu in node.cpus() {
|
|
|
|
let x2apic_id = *cpu as u32;
|
|
|
|
|
|
|
|
// Flags
|
|
|
|
// - Enabled = 1 (bit 0)
|
|
|
|
// - Reserved bits 1-31
|
|
|
|
let flags = 1;
|
|
|
|
|
|
|
|
srat.append(ProcessorLocalX2ApicAffinity {
|
|
|
|
type_: 2,
|
|
|
|
length: 24,
|
|
|
|
proximity_domain,
|
|
|
|
x2apic_id,
|
|
|
|
flags,
|
|
|
|
clock_domain: 0,
|
|
|
|
..Default::default()
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2019-08-22 10:18:48 +00:00
|
|
|
pub fn create_acpi_tables(
|
|
|
|
guest_mem: &GuestMemoryMmap,
|
2020-02-27 09:29:03 +00:00
|
|
|
device_manager: &Arc<Mutex<DeviceManager>>,
|
2019-12-06 15:25:57 +00:00
|
|
|
cpu_manager: &Arc<Mutex<CpuManager>>,
|
2020-01-10 16:11:32 +00:00
|
|
|
memory_manager: &Arc<Mutex<MemoryManager>>,
|
2020-09-04 09:26:43 +00:00
|
|
|
numa_nodes: &NumaNodes,
|
2019-08-22 10:18:48 +00:00
|
|
|
) -> GuestAddress {
|
2021-01-25 10:48:45 +00:00
|
|
|
let rsdp_offset = arch::layout::RSDP_POINTER;
|
2019-08-14 16:14:34 +00:00
|
|
|
let mut tables: Vec<u64> = Vec::new();
|
|
|
|
|
|
|
|
// DSDT
|
2020-01-10 16:11:32 +00:00
|
|
|
let dsdt = create_dsdt_table(device_manager, cpu_manager, memory_manager);
|
2021-03-25 15:57:27 +00:00
|
|
|
let dsdt_offset = rsdp_offset.checked_add(Rsdp::len() as u64).unwrap();
|
2019-08-14 16:14:34 +00:00
|
|
|
guest_mem
|
|
|
|
.write_slice(dsdt.as_slice(), dsdt_offset)
|
|
|
|
.expect("Error writing DSDT table");
|
|
|
|
|
|
|
|
// FACP aka FADT
|
2021-05-18 12:43:53 +00:00
|
|
|
let facp = create_facp_table(dsdt_offset);
|
2019-08-14 16:14:34 +00:00
|
|
|
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);
|
|
|
|
|
2019-08-19 16:28:35 +00:00
|
|
|
// MADT
|
2019-12-06 15:25:57 +00:00
|
|
|
let madt = cpu_manager.lock().unwrap().create_madt();
|
2019-08-19 16:28:35 +00:00
|
|
|
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);
|
|
|
|
|
2019-08-20 06:31:44 +00:00
|
|
|
// MCFG
|
2021-05-18 12:43:53 +00:00
|
|
|
let mcfg = create_mcfg_table();
|
2019-08-20 06:31:44 +00:00
|
|
|
let mcfg_offset = madt_offset.checked_add(madt.len() as u64).unwrap();
|
|
|
|
guest_mem
|
|
|
|
.write_slice(mcfg.as_slice(), mcfg_offset)
|
|
|
|
.expect("Error writing MCFG table");
|
|
|
|
tables.push(mcfg_offset.0);
|
|
|
|
|
2020-08-31 13:35:19 +00:00
|
|
|
// SRAT and SLIT
|
2020-08-27 17:32:03 +00:00
|
|
|
// Only created if the NUMA nodes list is not empty.
|
|
|
|
let (prev_tbl_len, prev_tbl_off) = if numa_nodes.is_empty() {
|
|
|
|
(mcfg.len(), mcfg_offset)
|
|
|
|
} else {
|
2020-08-31 13:35:19 +00:00
|
|
|
// SRAT
|
2021-05-18 12:43:53 +00:00
|
|
|
let srat = create_srat_table(numa_nodes);
|
2020-08-27 17:32:03 +00:00
|
|
|
let srat_offset = mcfg_offset.checked_add(mcfg.len() as u64).unwrap();
|
|
|
|
guest_mem
|
|
|
|
.write_slice(srat.as_slice(), srat_offset)
|
|
|
|
.expect("Error writing SRAT table");
|
|
|
|
tables.push(srat_offset.0);
|
|
|
|
|
2020-08-31 13:35:19 +00:00
|
|
|
// SLIT
|
2021-05-18 12:43:53 +00:00
|
|
|
let slit = create_slit_table(numa_nodes);
|
2020-08-31 13:35:19 +00:00
|
|
|
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);
|
|
|
|
|
|
|
|
(slit.len(), slit_offset)
|
2020-08-27 17:32:03 +00:00
|
|
|
};
|
|
|
|
|
2019-08-14 16:14:34 +00:00
|
|
|
// XSDT
|
2021-03-25 15:57:27 +00:00
|
|
|
let mut xsdt = Sdt::new(*b"XSDT", 36, 1, *b"CLOUDH", *b"CHXSDT ", 1);
|
2019-08-19 16:28:35 +00:00
|
|
|
for table in tables {
|
|
|
|
xsdt.append(table);
|
|
|
|
}
|
2019-08-14 16:14:34 +00:00
|
|
|
xsdt.update_checksum();
|
|
|
|
|
2020-08-27 17:32:03 +00:00
|
|
|
let xsdt_offset = prev_tbl_off.checked_add(prev_tbl_len as u64).unwrap();
|
2019-08-14 16:14:34 +00:00
|
|
|
guest_mem
|
|
|
|
.write_slice(xsdt.as_slice(), xsdt_offset)
|
|
|
|
.expect("Error writing XSDT table");
|
|
|
|
|
|
|
|
// RSDP
|
2021-03-25 15:57:27 +00:00
|
|
|
let rsdp = Rsdp::new(*b"CLOUDH", xsdt_offset.0);
|
2019-08-14 16:14:34 +00:00
|
|
|
guest_mem
|
|
|
|
.write_slice(rsdp.as_slice(), rsdp_offset)
|
|
|
|
.expect("Error writing RSDP");
|
|
|
|
|
|
|
|
rsdp_offset
|
|
|
|
}
|