use crate::{AcpiError, AcpiHandler, AcpiResult, AcpiTable, PhysicalMapping}; | |
use core::{fmt, mem::MaybeUninit, str}; | |
/// Represents a field which may or may not be present within an ACPI structure, depending on the version of ACPI | |
/// that a system supports. If the field is not present, it is not safe to treat the data as initialised. | |
#[derive(Debug, Clone, Copy)] | |
#[repr(transparent)] | |
pub struct ExtendedField<T: Copy, const MIN_REVISION: u8>(MaybeUninit<T>); | |
impl<T: Copy, const MIN_REVISION: u8> ExtendedField<T, MIN_REVISION> { | |
/// Access the field if it's present for the given revision of the table. | |
/// | |
/// ### Safety | |
/// If a bogus ACPI version is passed, this function may access uninitialised data. | |
pub unsafe fn access(&self, revision: u8) -> Option<T> { | |
if revision >= MIN_REVISION { | |
Some(unsafe { self.0.assume_init() }) | |
} else { | |
None | |
} | |
} | |
} | |
/// All SDTs share the same header, and are `length` bytes long. The signature tells us which SDT | |
/// this is. | |
/// | |
/// The ACPI Spec (Version 6.4) defines the following SDT signatures: | |
/// | |
/// * APIC - Multiple APIC Description Table (MADT) | |
/// * BERT - Boot Error Record Table | |
/// * BGRT - Boot Graphics Resource Table | |
/// * CPEP - Corrected Platform Error Polling Table | |
/// * DSDT - Differentiated System Description Table (DSDT) | |
/// * ECDT - Embedded Controller Boot Resources Table | |
/// * EINJ - Error Injection Table | |
/// * ERST - Error Record Serialization Table | |
/// * FACP - Fixed ACPI Description Table (FADT) | |
/// * FACS - Firmware ACPI Control Structure | |
/// * FPDT - Firmware Performance Data Table | |
/// * GTDT - Generic Timer Description Table | |
/// * HEST - Hardware Error Source Table | |
/// * MSCT - Maximum System Characteristics Table | |
/// * MPST - Memory Power StateTable | |
/// * NFIT - NVDIMM Firmware Interface Table | |
/// * OEMx - OEM Specific Information Tables | |
/// * PCCT - Platform Communications Channel Table | |
/// * PHAT - Platform Health Assessment Table | |
/// * PMTT - Platform Memory Topology Table | |
/// * PSDT - Persistent System Description Table | |
/// * RASF - ACPI RAS Feature Table | |
/// * RSDT - Root System Description Table | |
/// * SBST - Smart Battery Specification Table | |
/// * SDEV - Secure DEVices Table | |
/// * SLIT - System Locality Distance Information Table | |
/// * SRAT - System Resource Affinity Table | |
/// * SSDT - Secondary System Description Table | |
/// * XSDT - Extended System Description Table | |
/// | |
/// Acpi reserves the following signatures and the specifications for them can be found [here](https://uefi.org/acpi): | |
/// | |
/// * AEST - ARM Error Source Table | |
/// * BDAT - BIOS Data ACPI Table | |
/// * CDIT - Component Distance Information Table | |
/// * CEDT - CXL Early Discovery Table | |
/// * CRAT - Component Resource Attribute Table | |
/// * CSRT - Core System Resource Table | |
/// * DBGP - Debug Port Table | |
/// * DBG2 - Debug Port Table 2 (note: ACPI 6.4 defines this as "DBPG2" but this is incorrect) | |
/// * DMAR - DMA Remapping Table | |
/// * DRTM -Dynamic Root of Trust for Measurement Table | |
/// * ETDT - Event Timer Description Table (obsolete, superseeded by HPET) | |
/// * HPET - IA-PC High Precision Event Timer Table | |
/// * IBFT - iSCSI Boot Firmware Table | |
/// * IORT - I/O Remapping Table | |
/// * IVRS - I/O Virtualization Reporting Structure | |
/// * LPIT - Low Power Idle Table | |
/// * MCFG - PCI Express Memory-mapped Configuration Space base address description table | |
/// * MCHI - Management Controller Host Interface table | |
/// * MPAM - ARM Memory Partitioning And Monitoring table | |
/// * MSDM - Microsoft Data Management Table | |
/// * PRMT - Platform Runtime Mechanism Table | |
/// * RGRT - Regulatory Graphics Resource Table | |
/// * SDEI - Software Delegated Exceptions Interface table | |
/// * SLIC - Microsoft Software Licensing table | |
/// * SPCR - Microsoft Serial Port Console Redirection table | |
/// * SPMI - Server Platform Management Interface table | |
/// * STAO - _STA Override table | |
/// * SVKL - Storage Volume Key Data table (Intel TDX only) | |
/// * TCPA - Trusted Computing Platform Alliance Capabilities Table | |
/// * TPM2 - Trusted Platform Module 2 Table | |
/// * UEFI - Unified Extensible Firmware Interface Specification table | |
/// * WAET - Windows ACPI Emulated Devices Table | |
/// * WDAT - Watch Dog Action Table | |
/// * WDRT - Watchdog Resource Table | |
/// * WPBT - Windows Platform Binary Table | |
/// * WSMT - Windows Security Mitigations Table | |
/// * XENV - Xen Project | |
#[derive(Debug, Clone, Copy)] | |
#[repr(C, packed)] | |
pub struct SdtHeader { | |
pub signature: Signature, | |
pub length: u32, | |
pub revision: u8, | |
pub checksum: u8, | |
pub oem_id: [u8; 6], | |
pub oem_table_id: [u8; 8], | |
pub oem_revision: u32, | |
pub creator_id: u32, | |
pub creator_revision: u32, | |
} | |
impl SdtHeader { | |
/// Whether values of header fields are permitted. | |
fn validate_header_fields(&self, signature: Signature) -> AcpiResult<()> { | |
// Check the signature | |
if self.signature != signature || str::from_utf8(&self.signature.0).is_err() { | |
return Err(AcpiError::SdtInvalidSignature(signature)); | |
} | |
// Check the OEM id | |
if str::from_utf8(&self.oem_id).is_err() { | |
return Err(AcpiError::SdtInvalidOemId(signature)); | |
} | |
// Check the OEM table id | |
if str::from_utf8(&self.oem_table_id).is_err() { | |
return Err(AcpiError::SdtInvalidTableId(signature)); | |
} | |
Ok(()) | |
} | |
/// Whether table is valid according to checksum. | |
fn validate_checksum(&self, signature: Signature) -> AcpiResult<()> { | |
// SAFETY: Entire table is mapped. | |
let table_bytes = | |
unsafe { core::slice::from_raw_parts((self as *const SdtHeader).cast::<u8>(), self.length as usize) }; | |
let sum = table_bytes.iter().fold(0u8, |sum, &byte| sum.wrapping_add(byte)); | |
if sum == 0 { | |
Ok(()) | |
} else { | |
Err(AcpiError::SdtInvalidChecksum(signature)) | |
} | |
} | |
/// Checks that: | |
/// | |
/// 1. The signature matches the one given. | |
/// 2. The values of various fields in the header are allowed. | |
/// 3. The checksum of the SDT is valid. | |
/// | |
/// This assumes that the whole SDT is mapped. | |
pub fn validate(&self, signature: Signature) -> AcpiResult<()> { | |
self.validate_header_fields(signature)?; | |
self.validate_checksum(signature)?; | |
Ok(()) | |
} | |
/// Validates header, proceeding with checking entire table and returning a [`PhysicalMapping`] to it if | |
/// successful. | |
/// | |
/// The same checks are performed as [`SdtHeader::validate`], but `header_mapping` does not have to map the | |
/// entire table when calling. This is useful to avoid completely mapping a table that will be immediately | |
/// unmapped if it does not have a particular signature or has an invalid header. | |
pub(crate) fn validate_lazy<H: AcpiHandler, T: AcpiTable>( | |
header_mapping: PhysicalMapping<H, Self>, | |
handler: H, | |
) -> AcpiResult<PhysicalMapping<H, T>> { | |
header_mapping.validate_header_fields(T::SIGNATURE)?; | |
// Reuse `header_mapping` to access the rest of the table if the latter is already mapped entirely | |
let table_length = header_mapping.length as usize; | |
let table_mapping = if header_mapping.mapped_length() >= table_length { | |
// Avoid requesting table unmap twice (from both `header_mapping` and `table_mapping`) | |
let header_mapping = core::mem::ManuallyDrop::new(header_mapping); | |
// SAFETY: `header_mapping` maps entire table. | |
unsafe { | |
PhysicalMapping::new( | |
header_mapping.physical_start(), | |
header_mapping.virtual_start().cast::<T>(), | |
table_length, | |
header_mapping.mapped_length(), | |
handler, | |
) | |
} | |
} else { | |
// Unmap header as soon as possible | |
let table_phys_start = header_mapping.physical_start(); | |
drop(header_mapping); | |
// SAFETY: `table_phys_start` is the physical address of the header and the rest of the table. | |
unsafe { handler.map_physical_region(table_phys_start, table_length) } | |
}; | |
// This is usually redundant compared to simply calling `validate_checksum` but respects custom | |
// `AcpiTable::validate` implementations. | |
table_mapping.validate()?; | |
Ok(table_mapping) | |
} | |
pub fn oem_id(&self) -> &str { | |
// Safe to unwrap because checked in `validate` | |
str::from_utf8(&self.oem_id).unwrap() | |
} | |
pub fn oem_table_id(&self) -> &str { | |
// Safe to unwrap because checked in `validate` | |
str::from_utf8(&self.oem_table_id).unwrap() | |
} | |
} | |
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] | |
#[repr(transparent)] | |
pub struct Signature([u8; 4]); | |
impl Signature { | |
pub const RSDT: Signature = Signature(*b"RSDT"); | |
pub const XSDT: Signature = Signature(*b"XSDT"); | |
pub const FADT: Signature = Signature(*b"FACP"); | |
pub const HPET: Signature = Signature(*b"HPET"); | |
pub const MADT: Signature = Signature(*b"APIC"); | |
pub const MCFG: Signature = Signature(*b"MCFG"); | |
pub const SSDT: Signature = Signature(*b"SSDT"); | |
pub const BERT: Signature = Signature(*b"BERT"); | |
pub const BGRT: Signature = Signature(*b"BGRT"); | |
pub const CPEP: Signature = Signature(*b"CPEP"); | |
pub const DSDT: Signature = Signature(*b"DSDT"); | |
pub const ECDT: Signature = Signature(*b"ECDT"); | |
pub const EINJ: Signature = Signature(*b"EINJ"); | |
pub const ERST: Signature = Signature(*b"ERST"); | |
pub const FACS: Signature = Signature(*b"FACS"); | |
pub const FPDT: Signature = Signature(*b"FPDT"); | |
pub const GTDT: Signature = Signature(*b"GTDT"); | |
pub const HEST: Signature = Signature(*b"HEST"); | |
pub const MSCT: Signature = Signature(*b"MSCT"); | |
pub const MPST: Signature = Signature(*b"MPST"); | |
pub const NFIT: Signature = Signature(*b"NFIT"); | |
pub const PCCT: Signature = Signature(*b"PCCT"); | |
pub const PHAT: Signature = Signature(*b"PHAT"); | |
pub const PMTT: Signature = Signature(*b"PMTT"); | |
pub const PSDT: Signature = Signature(*b"PSDT"); | |
pub const RASF: Signature = Signature(*b"RASF"); | |
pub const SBST: Signature = Signature(*b"SBST"); | |
pub const SDEV: Signature = Signature(*b"SDEV"); | |
pub const SLIT: Signature = Signature(*b"SLIT"); | |
pub const SRAT: Signature = Signature(*b"SRAT"); | |
pub const AEST: Signature = Signature(*b"AEST"); | |
pub const BDAT: Signature = Signature(*b"BDAT"); | |
pub const CDIT: Signature = Signature(*b"CDIT"); | |
pub const CEDT: Signature = Signature(*b"CEDT"); | |
pub const CRAT: Signature = Signature(*b"CRAT"); | |
pub const CSRT: Signature = Signature(*b"CSRT"); | |
pub const DBGP: Signature = Signature(*b"DBGP"); | |
pub const DBG2: Signature = Signature(*b"DBG2"); | |
pub const DMAR: Signature = Signature(*b"DMAR"); | |
pub const DRTM: Signature = Signature(*b"DRTM"); | |
pub const ETDT: Signature = Signature(*b"ETDT"); | |
pub const IBFT: Signature = Signature(*b"IBFT"); | |
pub const IORT: Signature = Signature(*b"IORT"); | |
pub const IVRS: Signature = Signature(*b"IVRS"); | |
pub const LPIT: Signature = Signature(*b"LPIT"); | |
pub const MCHI: Signature = Signature(*b"MCHI"); | |
pub const MPAM: Signature = Signature(*b"MPAM"); | |
pub const MSDM: Signature = Signature(*b"MSDM"); | |
pub const PRMT: Signature = Signature(*b"PRMT"); | |
pub const RGRT: Signature = Signature(*b"RGRT"); | |
pub const SDEI: Signature = Signature(*b"SDEI"); | |
pub const SLIC: Signature = Signature(*b"SLIC"); | |
pub const SPCR: Signature = Signature(*b"SPCR"); | |
pub const SPMI: Signature = Signature(*b"SPMI"); | |
pub const STAO: Signature = Signature(*b"STAO"); | |
pub const SVKL: Signature = Signature(*b"SVKL"); | |
pub const TCPA: Signature = Signature(*b"TCPA"); | |
pub const TPM2: Signature = Signature(*b"TPM2"); | |
pub const UEFI: Signature = Signature(*b"UEFI"); | |
pub const WAET: Signature = Signature(*b"WAET"); | |
pub const WDAT: Signature = Signature(*b"WDAT"); | |
pub const WDRT: Signature = Signature(*b"WDRT"); | |
pub const WPBT: Signature = Signature(*b"WPBT"); | |
pub const WSMT: Signature = Signature(*b"WSMT"); | |
pub const XENV: Signature = Signature(*b"XENV"); | |
pub fn as_str(&self) -> &str { | |
str::from_utf8(&self.0).unwrap() | |
} | |
} | |
impl fmt::Display for Signature { | |
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
write!(f, "{}", self.as_str()) | |
} | |
} | |
impl fmt::Debug for Signature { | |
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
write!(f, "\"{}\"", self.as_str()) | |
} | |
} |