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()) } }