From 2506b24c66d657b77937c5e6a6a8cbc2212455e7 Mon Sep 17 00:00:00 2001 From: Xiaofan Xxf Date: Mon, 18 May 2026 14:59:31 +0800 Subject: [PATCH] dragonball: Add basic ACPI implementation for TDX boot Added basic implementation for a few ACPI tables (MADT, FADT and DSDT). Td-shim does not support mptable, and requires VMM to pass ACPI table contents to virtual firmware via HOB list. Note that this is PR contains only minimal implementation enough for booting a TDX VM. More comprehensive ACPI support may require future updates. Signed-off-by: Xiaofan Xxf --- src/dragonball/dbs_acpi/src/fadt.rs | 121 ++++++++++++++++++++ src/dragonball/dbs_acpi/src/lib.rs | 13 +++ src/dragonball/dbs_acpi/src/madt.rs | 164 ++++++++++++++++++++++++++++ src/dragonball/dbs_acpi/src/sdt.rs | 13 ++- src/dragonball/src/vm/x86_64.rs | 13 ++- 5 files changed, 318 insertions(+), 6 deletions(-) create mode 100644 src/dragonball/dbs_acpi/src/fadt.rs create mode 100644 src/dragonball/dbs_acpi/src/madt.rs diff --git a/src/dragonball/dbs_acpi/src/fadt.rs b/src/dragonball/dbs_acpi/src/fadt.rs new file mode 100644 index 0000000000..0956793720 --- /dev/null +++ b/src/dragonball/dbs_acpi/src/fadt.rs @@ -0,0 +1,121 @@ +// Copyright (c) 2026 Ant Group +// +// SPDX-License-Identifier: Apache-2.0 +// + +use crate::sdt::{GenericAddress, Sdt}; +use vm_memory::ByteValued; + +#[repr(C, packed)] +#[derive(Default, Copy, Clone)] +#[allow(non_snake_case)] +pub struct FadtBody { + pub FirmwareCtrl: u32, + pub Dsdt: u32, + pub Reserved: u8, + pub PreferredPowerManagementProfile: u8, + pub SCI_Interrupt: u16, + pub SMI_CommandPort: u32, + pub AcpiEnable: u8, + pub AcpiDisable: u8, + pub S4BIOS_REQ: u8, + pub PSTATE_Control: u8, + pub PM1aEventBlock: u32, + pub PM1bEventBlock: u32, + pub PM1aControlBlock: u32, + pub PM1bControlBlock: u32, + pub PM2ControlBlock: u32, + pub PMTimerBlock: u32, + pub GPE0Block: u32, + pub GPE1Block: u32, + pub PM1EventLength: u8, + pub PM1ControlLength: u8, + pub PM2ControlLength: u8, + pub PMTimerLength: u8, + pub GPE0Length: u8, + pub GPE1Length: u8, + pub GPE1Base: u8, + pub CStateControl: u8, + pub WorstC2Latency: u16, + pub WorstC3Latency: u16, + pub FlushSize: u16, + pub FlushStride: u16, + pub DutyOffset: u8, + pub DutyWidth: u8, + pub DayAlarm: u8, + pub MonthAlarm: u8, + pub Century: u8, + pub BootArchitectureFlags: u16, + pub Reserved2: u8, + pub Flags: u32, + pub ResetReg: GenericAddress, + pub ResetValue: u8, + pub ArmBootArch: u16, + pub FadtMinorVersion: u8, + pub X_FirmwareControl: u64, + pub X_Dsdt: u64, + pub X_PM1aEventBlock: GenericAddress, + pub X_PM1bEventBlock: GenericAddress, + pub X_PM1aControlBlock: GenericAddress, + pub X_PM1bControlBlock: GenericAddress, + pub X_PM2ControlBlock: GenericAddress, + pub X_PMTimerBlock: GenericAddress, + pub X_GPE0Block: GenericAddress, + pub X_GPE1Block: GenericAddress, + pub SleepControlReg: GenericAddress, + pub SleepStatusReg: GenericAddress, + pub HypervisorVendorIdentity: u64, +} + +unsafe impl ByteValued for FadtBody {} + +impl FadtBody { + pub fn new() -> Self { + FadtBody { + SCI_Interrupt: 9, + + PM1aEventBlock: 0xb000, + PM1aControlBlock: 0xb004, + PMTimerBlock: 0xb008, + GPE0Block: 0xb020, + + PM1EventLength: 4, + PM1ControlLength: 2, + PMTimerLength: 4, + GPE0Length: 2, + + BootArchitectureFlags: 1, + Flags: (1 << 0) | (1 << 8) | (1 << 9) | (1 << 10), + + X_PM1aEventBlock: GenericAddress { + address_space_id: 1, + register_bit_width: 32, + register_bit_offset: 0, + access_size: 3, + address: 0xb000, + }, + X_PM1aControlBlock: GenericAddress { + address_space_id: 1, + register_bit_width: 16, + register_bit_offset: 0, + access_size: 2, + address: 0xb004, + }, + X_PMTimerBlock: GenericAddress { + address_space_id: 1, + register_bit_width: 32, + register_bit_offset: 0, + access_size: 3, + address: 0xb008, + }, + ..Default::default() + } + } +} + +pub fn create_fadt_table() -> Sdt { + let mut fadt = Sdt::new(*b"FACP", 36, 6); + fadt.append_slice(FadtBody::new().as_slice()); + + fadt +} diff --git a/src/dragonball/dbs_acpi/src/lib.rs b/src/dragonball/dbs_acpi/src/lib.rs index a3094e3096..584722dcbe 100644 --- a/src/dragonball/dbs_acpi/src/lib.rs +++ b/src/dragonball/dbs_acpi/src/lib.rs @@ -2,9 +2,22 @@ // Copyright (c) 2023 Alibaba Cloud // // SPDX-License-Identifier: Apache-2.0 + +// Please refer to the official ACPI 6.0 documentation: +// https://uefi.org/sites/default/files/resources/ACPI_6.0.pdf +// for specification of ACPI tables. + +#![allow(missing_docs)] + +pub mod fadt; +pub mod madt; pub mod rsdp; pub mod sdt; +pub use fadt::create_fadt_table; +pub use madt::create_madt_table; +pub use sdt::create_dsdt_table; + fn generate_checksum(data: &[u8]) -> u8 { (255 - data.iter().fold(0u8, |acc, x| acc.wrapping_add(*x))).wrapping_add(1) } diff --git a/src/dragonball/dbs_acpi/src/madt.rs b/src/dragonball/dbs_acpi/src/madt.rs new file mode 100644 index 0000000000..1567ae4ec2 --- /dev/null +++ b/src/dragonball/dbs_acpi/src/madt.rs @@ -0,0 +1,164 @@ +// Copyright (c) 2026 Ant Group +// +// SPDX-License-Identifier: Apache-2.0 +// + +use crate::sdt::Sdt; +use vm_memory::ByteValued; + +const IOAPIC_START: u32 = 0xfec0_0000; +const APIC_START: u32 = 0xfee0_0000; + +const MADT_CPU_ENABLE_FLAG: usize = 0; + +#[repr(u8)] +#[derive(Default, Copy, Clone)] +pub enum MadtEntryType { + #[default] + LocalApic, + Ioapic, + InterruptSourceOverride, + LocalX2Apic = 9, +} + +#[repr(C, packed)] +#[derive(Default, Copy, Clone)] +pub struct MadtBody { + pub apic_address: u32, + pub flags: u32, +} + +impl MadtBody { + pub fn new(apic_address: u32, flags: u32) -> Self { + Self { + apic_address, + flags, + } + } +} + +#[repr(C, packed)] +#[derive(Default, Copy, Clone)] +pub struct MadtEntryLocalApic { + pub r#type: MadtEntryType, + pub length: u8, + pub processor_id: u8, + pub apic_id: u8, + pub flags: u32, +} + +impl MadtEntryLocalApic { + pub fn new(processor_id: u8, flags: u32) -> Self { + Self { + r#type: MadtEntryType::LocalApic, + length: 8, + processor_id, + apic_id: processor_id, + flags, + } + } +} + +#[repr(C, packed)] +#[derive(Default, Copy, Clone)] +pub struct MadtEntryIoapic { + pub r#type: MadtEntryType, + pub length: u8, + pub ioapic_id: u8, + pub reserved: u8, + pub ioapic_address: u32, + pub gsi_base: u32, +} + +impl MadtEntryIoapic { + pub fn new(ioapic_id: u8, ioapic_address: u32, gsi_base: u32) -> Self { + Self { + r#type: MadtEntryType::Ioapic, + length: 12, + ioapic_id, + reserved: 0, + ioapic_address, + gsi_base, + } + } +} + +#[repr(C, packed)] +#[derive(Default, Copy, Clone)] +pub struct MadtEntryIntrSrcOverride { + pub r#type: MadtEntryType, + pub length: u8, + pub bus_source: u8, + pub irq_source: u8, + pub gsi: u32, + pub flags: u16, +} + +impl MadtEntryIntrSrcOverride { + pub fn new(bus_source: u8, irq_source: u8, gsi: u32, flags: u16) -> Self { + Self { + r#type: MadtEntryType::InterruptSourceOverride, + length: 10, + bus_source, + irq_source, + gsi, + flags, + } + } +} + +#[repr(C, packed)] +#[derive(Default, Copy, Clone)] +pub struct MadtEntryLocalX2Apic { + r#type: MadtEntryType, + length: u8, + reserved: u16, + x2apic_id: u32, + flags: u32, + processor_id: u32, +} + +impl MadtEntryLocalX2Apic { + pub fn new(processor_id: u32, flags: u32) -> Self { + Self { + r#type: MadtEntryType::LocalX2Apic, + length: 16, + reserved: 0, + // TODO: Calculate x2apic id from processor id + x2apic_id: processor_id, + flags, + processor_id, + } + } +} + +unsafe impl ByteValued for MadtBody {} +unsafe impl ByteValued for MadtEntryLocalApic {} +unsafe impl ByteValued for MadtEntryIoapic {} +unsafe impl ByteValued for MadtEntryIntrSrcOverride {} +unsafe impl ByteValued for MadtEntryLocalX2Apic {} + +pub fn create_madt_table(max_vcpus: u8, boot_vcpus: u8) -> Sdt { + let mut madt = Sdt::new(*b"APIC", 36, 5); + madt.append_slice(MadtBody::new(APIC_START, 0).as_slice()); + + for cpu_id in 0..max_vcpus { + madt.append_slice( + MadtEntryLocalApic::new( + cpu_id, + if cpu_id < boot_vcpus { + 1 << MADT_CPU_ENABLE_FLAG + } else { + 0 + }, + ) + .as_slice(), + ); + } + + madt.append_slice(MadtEntryIoapic::new(0, IOAPIC_START, 0).as_slice()); + + madt.append_slice(MadtEntryIntrSrcOverride::new(0, 2, 2, 0).as_slice()); + + madt +} diff --git a/src/dragonball/dbs_acpi/src/sdt.rs b/src/dragonball/dbs_acpi/src/sdt.rs index 62d463458e..2b763dca87 100644 --- a/src/dragonball/dbs_acpi/src/sdt.rs +++ b/src/dragonball/dbs_acpi/src/sdt.rs @@ -2,7 +2,11 @@ // Copyright (c) 2023 Alibaba Cloud // // SPDX-License-Identifier: Apache-2.0 -#[repr(Rust, packed)] + +use vm_memory::ByteValued; + +#[repr(C, packed)] +#[derive(Copy, Clone, Default)] pub struct GenericAddress { pub address_space_id: u8, pub register_bit_width: u8, @@ -33,6 +37,8 @@ impl GenericAddress { } } +unsafe impl ByteValued for GenericAddress {} + pub struct Sdt { data: Vec, } @@ -116,6 +122,11 @@ impl Sdt { self.data.len() } } + +pub fn create_dsdt_table() -> Sdt { + Sdt::new(*b"DSDT", 36, 2) +} + #[cfg(test)] mod tests { use super::Sdt; diff --git a/src/dragonball/src/vm/x86_64.rs b/src/dragonball/src/vm/x86_64.rs index cd075c047f..ae5dff798d 100644 --- a/src/dragonball/src/vm/x86_64.rs +++ b/src/dragonball/src/vm/x86_64.rs @@ -10,7 +10,7 @@ use std::collections::HashMap; use std::convert::TryInto; use std::ops::Deref; -use dbs_acpi::sdt::Sdt; +use dbs_acpi::*; use dbs_address_space::{AddressSpace, AddressSpaceRegionType}; use dbs_boot::{ add_e820_entry, bootparam, layout, mptable, tdshim::*, BootParamsWrapper, FirmwareType, @@ -215,8 +215,11 @@ impl Vm { .cloned() .ok_or(StartMicroVmError::GuestMemoryNotInitialized)?; let mut hob_address = 0; - // TODO: Fill the empty list with ACPI table content - let acpi_tables: Vec = Vec::new(); + let acpi_tables: Vec = vec![ + create_madt_table(self.vm_config.max_vcpu_count, self.vm_config.vcpu_count), + create_fadt_table(), + create_dsdt_table(), + ]; self.load_kernel_with_tdshim( §ions, @@ -383,7 +386,7 @@ impl Vm { vm_memory: &GuestMemoryImpl, address_space: AddressSpace, hob_address: &mut u64, - acpi_tables: &Vec, + acpi_tables: &Vec, ) -> std::result::Result<(), StartMicroVmError> { let mut required_sections = vec!["Bfv", "TdHob", "PayloadParam"]; @@ -461,7 +464,7 @@ impl Vm { vm_memory: &GuestMemoryImpl, address_space: AddressSpace, payload_info: PayloadInfo, - acpi_tables: &Vec, + acpi_tables: &Vec, ) -> std::result::Result<(), StartMicroVmError> { let mut hob = TdHob::start(hob_address);