mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
devices: Add acpi tables to fw_cfg
The acpi tables are created in the same place the acpi tables would be created for the regular bootflow, except here we add them to the fw_cfg device to be measured by the fw and then the fw will put the acpi tables into memory. Signed-off-by: Alex Orozco <alexorozco@google.com>
This commit is contained in:
@@ -14,11 +14,12 @@
|
||||
use std::{
|
||||
fs::File,
|
||||
io::Result,
|
||||
mem::{size_of, size_of_val},
|
||||
mem::offset_of,
|
||||
os::unix::fs::FileExt,
|
||||
sync::{Arc, Barrier},
|
||||
};
|
||||
|
||||
use acpi_tables::rsdp::Rsdp;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use arch::aarch64::layout::{
|
||||
MEM_32BIT_DEVICES_START, MEM_32BIT_RESERVED_START, RAM_64BIT_START, RAM_START as HIGH_RAM_START,
|
||||
@@ -38,7 +39,7 @@ use vm_memory::ByteValued;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use vm_memory::GuestAddress;
|
||||
use vmm_sys_util::sock_ctrl_msg::IntoIovec;
|
||||
use zerocopy::{FromBytes, IntoBytes};
|
||||
use zerocopy::{FromBytes, Immutable, IntoBytes};
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
// https://github.com/project-oak/oak/tree/main/stage0_bin#memory-layout
|
||||
@@ -94,6 +95,17 @@ const FW_CFG_F_RESERVED: u8 = 1 << 0;
|
||||
const FW_CFG_F_DMA: u8 = 1 << 1;
|
||||
pub const FW_CFG_FEATURE: [u8; 4] = [FW_CFG_F_RESERVED | FW_CFG_F_DMA, 0, 0, 0];
|
||||
|
||||
const COMMAND_ALLOCATE: u32 = 0x1;
|
||||
const COMMAND_ADD_POINTER: u32 = 0x2;
|
||||
const COMMAND_ADD_CHECKSUM: u32 = 0x3;
|
||||
|
||||
const ALLOC_ZONE_HIGH: u8 = 0x1;
|
||||
const ALLOC_ZONE_FSEG: u8 = 0x2;
|
||||
|
||||
const FW_CFG_FILENAME_TABLE_LOADER: &str = "etc/table-loader";
|
||||
const FW_CFG_FILENAME_RSDP: &str = "acpi/rsdp";
|
||||
const FW_CFG_FILENAME_ACPI_TABLES: &str = "acpi/tables";
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum FwCfgContent {
|
||||
Bytes(Vec<u8>),
|
||||
@@ -168,6 +180,169 @@ struct FwCfgFile {
|
||||
name: [u8; FILE_NAME_SIZE],
|
||||
}
|
||||
|
||||
#[repr(C, align(4))]
|
||||
#[derive(Debug, IntoBytes, Immutable)]
|
||||
struct Allocate {
|
||||
command: u32,
|
||||
file: [u8; FILE_NAME_SIZE],
|
||||
align: u32,
|
||||
zone: u8,
|
||||
_pad: [u8; 63],
|
||||
}
|
||||
|
||||
#[repr(C, align(4))]
|
||||
#[derive(Debug, IntoBytes, Immutable)]
|
||||
struct AddPointer {
|
||||
command: u32,
|
||||
dst: [u8; FILE_NAME_SIZE],
|
||||
src: [u8; FILE_NAME_SIZE],
|
||||
offset: u32,
|
||||
size: u8,
|
||||
_pad: [u8; 7],
|
||||
}
|
||||
|
||||
#[repr(C, align(4))]
|
||||
#[derive(Debug, IntoBytes, Immutable)]
|
||||
struct AddChecksum {
|
||||
command: u32,
|
||||
file: [u8; FILE_NAME_SIZE],
|
||||
offset: u32,
|
||||
start: u32,
|
||||
len: u32,
|
||||
_pad: [u8; 56],
|
||||
}
|
||||
|
||||
fn create_intra_pointer(name: &str, offset: usize, size: u8) -> AddPointer {
|
||||
AddPointer {
|
||||
command: COMMAND_ADD_POINTER,
|
||||
dst: create_file_name(name),
|
||||
src: create_file_name(name),
|
||||
offset: offset as u32,
|
||||
size,
|
||||
_pad: [0; 7],
|
||||
}
|
||||
}
|
||||
|
||||
fn create_acpi_table_checksum(offset: usize, len: usize) -> AddChecksum {
|
||||
AddChecksum {
|
||||
command: COMMAND_ADD_CHECKSUM,
|
||||
file: create_file_name(FW_CFG_FILENAME_ACPI_TABLES),
|
||||
offset: (offset + offset_of!(AcpiTableHeader, checksum)) as u32,
|
||||
start: offset as u32,
|
||||
len: len as u32,
|
||||
_pad: [0; 56],
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C, align(4))]
|
||||
#[derive(Debug, Clone, Default, FromBytes, IntoBytes)]
|
||||
struct AcpiTableHeader {
|
||||
signature: [u8; 4],
|
||||
length: u32,
|
||||
revision: u8,
|
||||
checksum: u8,
|
||||
oem_id: [u8; 6],
|
||||
oem_table_id: [u8; 8],
|
||||
oem_revision: u32,
|
||||
asl_compiler_id: [u8; 4],
|
||||
asl_compiler_revision: u32,
|
||||
}
|
||||
|
||||
struct AcpiTable {
|
||||
rsdp: Rsdp,
|
||||
tables: Vec<u8>,
|
||||
table_pointers: Vec<usize>,
|
||||
table_checksums: Vec<(usize, usize)>,
|
||||
}
|
||||
|
||||
impl AcpiTable {
|
||||
fn pointers(&self) -> &[usize] {
|
||||
&self.table_pointers
|
||||
}
|
||||
|
||||
fn checksums(&self) -> &[(usize, usize)] {
|
||||
&self.table_checksums
|
||||
}
|
||||
|
||||
fn take(self) -> (Rsdp, Vec<u8>) {
|
||||
(self.rsdp, self.tables)
|
||||
}
|
||||
}
|
||||
|
||||
// Creates fw_cfg items used by firmware to load and verify Acpi tables
|
||||
// https://github.com/qemu/qemu/blob/master/hw/acpi/bios-linker-loader.c
|
||||
fn create_acpi_loader(acpi_table: AcpiTable) -> [FwCfgItem; 3] {
|
||||
let mut table_loader_bytes: Vec<u8> = Vec::new();
|
||||
let allocate_rsdp = Allocate {
|
||||
command: COMMAND_ALLOCATE,
|
||||
file: create_file_name(FW_CFG_FILENAME_RSDP),
|
||||
align: 4,
|
||||
zone: ALLOC_ZONE_FSEG,
|
||||
_pad: [0; 63],
|
||||
};
|
||||
table_loader_bytes.extend(allocate_rsdp.as_bytes());
|
||||
|
||||
let allocate_tables = Allocate {
|
||||
command: COMMAND_ALLOCATE,
|
||||
file: create_file_name(FW_CFG_FILENAME_ACPI_TABLES),
|
||||
align: 4,
|
||||
zone: ALLOC_ZONE_HIGH,
|
||||
_pad: [0; 63],
|
||||
};
|
||||
table_loader_bytes.extend(allocate_tables.as_bytes());
|
||||
|
||||
for pointer_offset in acpi_table.pointers().iter() {
|
||||
let pointer = create_intra_pointer(FW_CFG_FILENAME_ACPI_TABLES, *pointer_offset, 8);
|
||||
table_loader_bytes.extend(pointer.as_bytes());
|
||||
}
|
||||
for (offset, len) in acpi_table.checksums().iter() {
|
||||
let checksum = create_acpi_table_checksum(*offset, *len);
|
||||
table_loader_bytes.extend(checksum.as_bytes());
|
||||
}
|
||||
let pointer_rsdp_to_xsdt = AddPointer {
|
||||
command: COMMAND_ADD_POINTER,
|
||||
dst: create_file_name(FW_CFG_FILENAME_RSDP),
|
||||
src: create_file_name(FW_CFG_FILENAME_ACPI_TABLES),
|
||||
offset: offset_of!(Rsdp, xsdt_addr) as u32,
|
||||
size: 8,
|
||||
_pad: [0; 7],
|
||||
};
|
||||
table_loader_bytes.extend(pointer_rsdp_to_xsdt.as_bytes());
|
||||
let checksum_rsdp = AddChecksum {
|
||||
command: COMMAND_ADD_CHECKSUM,
|
||||
file: create_file_name(FW_CFG_FILENAME_RSDP),
|
||||
offset: offset_of!(Rsdp, checksum) as u32,
|
||||
start: 0,
|
||||
len: offset_of!(Rsdp, length) as u32,
|
||||
_pad: [0; 56],
|
||||
};
|
||||
let checksum_rsdp_ext = AddChecksum {
|
||||
command: COMMAND_ADD_CHECKSUM,
|
||||
file: create_file_name(FW_CFG_FILENAME_RSDP),
|
||||
offset: offset_of!(Rsdp, extended_checksum) as u32,
|
||||
start: 0,
|
||||
len: size_of::<Rsdp>() as u32,
|
||||
_pad: [0; 56],
|
||||
};
|
||||
table_loader_bytes.extend(checksum_rsdp.as_bytes());
|
||||
table_loader_bytes.extend(checksum_rsdp_ext.as_bytes());
|
||||
|
||||
let table_loader = FwCfgItem {
|
||||
name: FW_CFG_FILENAME_TABLE_LOADER.to_owned(),
|
||||
content: FwCfgContent::Bytes(table_loader_bytes),
|
||||
};
|
||||
let (rsdp, tables) = acpi_table.take();
|
||||
let acpi_rsdp = FwCfgItem {
|
||||
name: FW_CFG_FILENAME_RSDP.to_owned(),
|
||||
content: FwCfgContent::Bytes(rsdp.as_bytes().to_owned()),
|
||||
};
|
||||
let apci_tables = FwCfgItem {
|
||||
name: FW_CFG_FILENAME_ACPI_TABLES.to_owned(),
|
||||
content: FwCfgContent::Bytes(tables),
|
||||
};
|
||||
[table_loader, acpi_rsdp, apci_tables]
|
||||
}
|
||||
|
||||
impl FwCfg {
|
||||
pub fn new() -> FwCfg {
|
||||
const DEFAULT_ITEM: FwCfgContent = FwCfgContent::Slice(&[]);
|
||||
@@ -306,6 +481,25 @@ impl FwCfg {
|
||||
self.known_items[FW_CFG_CMDLINE_DATA as usize] = FwCfgContent::Bytes(bytes);
|
||||
}
|
||||
|
||||
pub fn add_acpi(
|
||||
&mut self,
|
||||
rsdp: Rsdp,
|
||||
tables: Vec<u8>,
|
||||
table_checksums: Vec<(usize, usize)>,
|
||||
table_pointers: Vec<usize>,
|
||||
) -> Result<()> {
|
||||
let acpi_table = AcpiTable {
|
||||
rsdp,
|
||||
tables,
|
||||
table_checksums,
|
||||
table_pointers,
|
||||
};
|
||||
let [table_loader, acpi_rsdp, apci_tables] = create_acpi_loader(acpi_table);
|
||||
self.add_item(table_loader)?;
|
||||
self.add_item(acpi_rsdp)?;
|
||||
self.add_item(apci_tables)
|
||||
}
|
||||
|
||||
pub fn add_initramfs_data(&mut self, file: &File) -> Result<()> {
|
||||
let initramfs_size = file.metadata()?.len();
|
||||
self.known_items[FW_CFG_INITRD_SIZE as usize] = FwCfgContent::U32(initramfs_size as _);
|
||||
|
||||
@@ -192,6 +192,8 @@ pub fn create_dsdt_table(
|
||||
dsdt
|
||||
}
|
||||
|
||||
const FACP_DSDT_OFFSET: usize = 140;
|
||||
|
||||
fn create_facp_table(dsdt_offset: GuestAddress, device_manager: &Arc<Mutex<DeviceManager>>) -> Sdt {
|
||||
trace_scoped!("create_facp_table");
|
||||
|
||||
@@ -241,7 +243,7 @@ fn create_facp_table(dsdt_offset: GuestAddress, device_manager: &Arc<Mutex<Devic
|
||||
// FADT minor version
|
||||
facp.write(131, 3u8);
|
||||
// X_DSDT
|
||||
facp.write(140, dsdt_offset.0);
|
||||
facp.write(FACP_DSDT_OFFSET, dsdt_offset.0);
|
||||
// Hypervisor Vendor Identity
|
||||
facp.write_bytes(268, b"CLOUDHYP");
|
||||
|
||||
@@ -807,6 +809,65 @@ fn create_acpi_tables_internal(
|
||||
(rsdp, tables_bytes, xsdt_table_pointers)
|
||||
}
|
||||
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
pub fn create_acpi_tables_for_fw_cfg(
|
||||
device_manager: &Arc<Mutex<DeviceManager>>,
|
||||
cpu_manager: &Arc<Mutex<CpuManager>>,
|
||||
memory_manager: &Arc<Mutex<MemoryManager>>,
|
||||
numa_nodes: &NumaNodes,
|
||||
tpm_enabled: bool,
|
||||
) -> Result<(), crate::vm::Error> {
|
||||
let dsdt_offset = GuestAddress(0);
|
||||
let (rsdp, table_bytes, xsdt_table_pointers) = create_acpi_tables_internal(
|
||||
dsdt_offset,
|
||||
device_manager,
|
||||
cpu_manager,
|
||||
memory_manager,
|
||||
numa_nodes,
|
||||
tpm_enabled,
|
||||
);
|
||||
let mut pointer_offsets: Vec<usize> = vec![];
|
||||
let mut checksums: Vec<(usize, usize)> = vec![];
|
||||
|
||||
let xsdt_addr = rsdp.xsdt_addr.get() as usize;
|
||||
let xsdt_checksum = (xsdt_addr, table_bytes.len() - xsdt_addr);
|
||||
|
||||
// create pointer offsets (use location of pointers in XSDT table)
|
||||
// XSDT doesn't have a pointer to DSDT so we use FACP's pointer to DSDT
|
||||
let facp_offset = xsdt_table_pointers[0] as usize;
|
||||
pointer_offsets.push(facp_offset + FACP_DSDT_OFFSET);
|
||||
let mut current_offset = xsdt_addr + 36;
|
||||
for _ in 0..xsdt_table_pointers.len() {
|
||||
pointer_offsets.push(current_offset);
|
||||
current_offset += 8;
|
||||
}
|
||||
|
||||
// create (offset, len) pairs for firmware to calculate
|
||||
// table checksums and verify ACPI tables
|
||||
let mut i = 0;
|
||||
while i < xsdt_table_pointers.len() - 1 {
|
||||
let current_table_offset = xsdt_table_pointers[i];
|
||||
let current_table_length = xsdt_table_pointers[i + 1] - current_table_offset;
|
||||
checksums.push((current_table_offset as usize, current_table_length as usize));
|
||||
i += 1;
|
||||
}
|
||||
checksums.push((
|
||||
xsdt_table_pointers[xsdt_table_pointers.len() - 1] as usize,
|
||||
0,
|
||||
));
|
||||
checksums.push(xsdt_checksum);
|
||||
|
||||
device_manager
|
||||
.lock()
|
||||
.unwrap()
|
||||
.fw_cfg()
|
||||
.expect("fw_cfg must be present")
|
||||
.lock()
|
||||
.unwrap()
|
||||
.add_acpi(rsdp, table_bytes, checksums, pointer_offsets)
|
||||
.map_err(crate::vm::Error::CreatingAcpiTables)
|
||||
}
|
||||
|
||||
pub fn create_acpi_tables(
|
||||
guest_mem: &GuestMemoryMmap,
|
||||
device_manager: &Arc<Mutex<DeviceManager>>,
|
||||
|
||||
@@ -355,6 +355,10 @@ pub enum Error {
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
#[error("Error creating e820 map")]
|
||||
CreatingE820Map(#[source] io::Error),
|
||||
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
#[error("Error creating acpi tables")]
|
||||
CreatingAcpiTables(#[source] io::Error),
|
||||
}
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
@@ -2361,10 +2365,21 @@ impl Vm {
|
||||
} else {
|
||||
VmState::Running
|
||||
};
|
||||
|
||||
current_state.valid_transition(new_state)?;
|
||||
|
||||
#[cfg(feature = "fw_cfg")]
|
||||
Self::populate_fw_cfg(&self.device_manager, &self.config)?;
|
||||
{
|
||||
Self::populate_fw_cfg(&self.device_manager, &self.config)?;
|
||||
let tpm_enabled = self.config.lock().unwrap().tpm.is_some();
|
||||
crate::acpi::create_acpi_tables_for_fw_cfg(
|
||||
&self.device_manager,
|
||||
&self.cpu_manager,
|
||||
&self.memory_manager,
|
||||
&self.numa_nodes,
|
||||
tpm_enabled,
|
||||
)?
|
||||
}
|
||||
|
||||
// Do earlier to parallelise with loading kernel
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
|
||||
Reference in New Issue
Block a user