acpi/
lib.rs

1//! A library for parsing ACPI tables. This crate can be used by bootloaders and kernels for architectures that
2//! support ACPI. This crate is not feature-complete, but can parse lots of the more common tables. Parsing the
3//! ACPI tables is required for correctly setting up the APICs, HPET, and provides useful information about power
4//! management and many other platform capabilities.
5//!
6//! This crate is designed to find and parse the static tables ACPI provides. It should be used in conjunction with
7//! the `aml` crate, which is the (much less complete) AML parser used to parse the DSDT and SSDTs. These crates
8//! are separate because some kernels may want to detect the static tables, but delay AML parsing to a later stage.
9//!
10//! This crate can be used in three configurations, depending on the environment it's being used from:
11//!    - **Without allocator support** - this can be achieved by disabling the `allocator_api` and `alloc`
12//!      features. The core parts of the library will still be usable, but with generally reduced functionality
13//!      and ease-of-use.
14//!    - **With a custom allocator** - by disabling just the `alloc` feature, you can use the `new_in` functions to
15//!      access increased functionality with your own allocator. This allows `acpi` to be integrated more closely
16//!      with environments that already provide a custom allocator, for example to gracefully handle allocation
17//!      errors.
18//!    - **With the globally-set allocator** - the `alloc` feature provides `new` functions that simply use the
19//!      global allocator. This is the easiest option, and the one the majority of users will want. It is the
20//!      default configuration of the crate.
21//!
22//! ### Usage
23//! To use the library, you will need to provide an implementation of the [`AcpiHandler`] trait, which allows the
24//! library to make requests such as mapping a particular region of physical memory into the virtual address space.
25//!
26//! You then need to construct an instance of [`AcpiTables`], which can be done in a few ways depending on how much
27//! information you have:
28//! * Use [`AcpiTables::from_rsdp`] if you have the physical address of the RSDP
29//! * Use [`AcpiTables::from_rsdt`] if you have the physical address of the RSDT/XSDT
30//! * Use [`AcpiTables::search_for_rsdp_bios`] if you don't have the address of either, but **you know you are
31//!   running on BIOS, not UEFI**
32//!
33//! `AcpiTables` stores the addresses of all of the tables detected on a platform. The SDTs are parsed by this
34//! library, or can be accessed directly with `from_sdt`, while the `DSDT` and any `SSDTs` should be parsed with
35//! `aml`.
36//!
37//! To gather information out of the static tables, a few of the types you should take a look at are:
38//!    - [`PlatformInfo`] parses the FADT and MADT to create a nice view of the processor topology and interrupt
39//!      controllers on `x86_64`, and the interrupt controllers on other platforms.
40//!      [`AcpiTables::platform_info`] is a convenience method for constructing a `PlatformInfo`.
41//!    - [`HpetInfo`] parses the HPET table and tells you how to configure the High Precision Event Timer.
42//!    - [`PciConfigRegions`] parses the MCFG and tells you how PCIe configuration space is mapped into physical
43//!      memory.
44
45/*
46 * Contributing notes (you may find these useful if you're new to contributing to the library):
47 *    - Accessing packed fields without UB: Lots of the structures defined by ACPI are defined with `repr(packed)`
48 *      to prevent padding being introduced, which would make the structure's layout incorrect. In Rust, this
49 *      creates a problem as references to these fields could be unaligned, which is undefined behaviour. For the
50 *      majority of these fields, this problem can be easily avoided by telling the compiler to make a copy of the
51 *      field's contents: this is the perhaps unfamiliar pattern of e.g. `!{ entry.flags }.get_bit(0)` we use
52 *      around the codebase.
53 */
54
55#![no_std]
56#![deny(unsafe_op_in_unsafe_fn)]
57#![cfg_attr(feature = "allocator_api", feature(allocator_api))]
58
59#[cfg_attr(test, macro_use)]
60#[cfg(test)]
61extern crate std;
62
63#[cfg(feature = "alloc")]
64extern crate alloc;
65
66pub mod address;
67pub mod bgrt;
68pub mod fadt;
69pub mod handler;
70pub mod hpet;
71pub mod madt;
72pub mod mcfg;
73pub mod rsdp;
74pub mod sdt;
75pub mod spcr;
76
77#[cfg(feature = "allocator_api")]
78mod managed_slice;
79#[cfg(feature = "allocator_api")]
80pub use managed_slice::*;
81
82#[cfg(feature = "allocator_api")]
83pub mod platform;
84#[cfg(feature = "allocator_api")]
85pub use crate::platform::{interrupt::InterruptModel, PlatformInfo};
86
87#[cfg(feature = "allocator_api")]
88pub use crate::mcfg::PciConfigRegions;
89
90pub use fadt::PowerProfile;
91pub use handler::{AcpiHandler, PhysicalMapping};
92pub use hpet::HpetInfo;
93pub use madt::MadtError;
94
95use crate::sdt::{SdtHeader, Signature};
96use core::mem;
97use rsdp::Rsdp;
98
99/// Result type used by error-returning functions.
100pub type AcpiResult<T> = core::result::Result<T, AcpiError>;
101
102/// All types representing ACPI tables should implement this trait.
103///
104/// ### Safety
105///
106/// The table's memory is naively interpreted, so you must be careful in providing a type that
107/// correctly represents the table's structure. Regardless of the provided type's size, the region mapped will
108/// be the size specified in the SDT's header. Providing a table impl that is larger than this, *may* lead to
109/// page-faults, aliasing references, or derefencing uninitialized memory (the latter two being UB).
110/// This isn't forbidden, however, because some tables rely on the impl being larger than a provided SDT in some
111/// versions of ACPI (the [`ExtendedField`](crate::sdt::ExtendedField) type will be useful if you need to do
112/// this. See our [`Fadt`](crate::fadt::Fadt) type for an example of this).
113pub unsafe trait AcpiTable {
114    const SIGNATURE: Signature;
115
116    fn header(&self) -> &sdt::SdtHeader;
117
118    fn validate(&self) -> AcpiResult<()> {
119        self.header().validate(Self::SIGNATURE)
120    }
121}
122
123/// Error type used by functions that return an `AcpiResult<T>`.
124#[derive(Debug)]
125pub enum AcpiError {
126    NoValidRsdp,
127    RsdpIncorrectSignature,
128    RsdpInvalidOemId,
129    RsdpInvalidChecksum,
130
131    SdtInvalidSignature(Signature),
132    SdtInvalidOemId(Signature),
133    SdtInvalidTableId(Signature),
134    SdtInvalidChecksum(Signature),
135
136    TableMissing(Signature),
137    InvalidFacsAddress,
138    InvalidDsdtAddress,
139    InvalidMadt(MadtError),
140    InvalidGenericAddress,
141
142    AllocError,
143}
144
145macro_rules! read_root_table {
146    ($signature_name:ident, $address:ident, $acpi_handler:ident) => {{
147        #[repr(transparent)]
148        struct RootTable {
149            header: SdtHeader,
150        }
151
152        unsafe impl AcpiTable for RootTable {
153            const SIGNATURE: Signature = Signature::$signature_name;
154
155            fn header(&self) -> &SdtHeader {
156                &self.header
157            }
158        }
159
160        // Map and validate root table
161        // SAFETY: Addresses from a validated RSDP are also guaranteed to be valid.
162        let table_mapping = unsafe { read_table::<_, RootTable>($acpi_handler.clone(), $address) }?;
163
164        // Convert `table_mapping` to header mapping for storage
165        // Avoid requesting table unmap twice (from both original and converted `table_mapping`s)
166        let table_mapping = mem::ManuallyDrop::new(table_mapping);
167        // SAFETY: `SdtHeader` is equivalent to `Sdt` memory-wise
168        let table_mapping = unsafe {
169            PhysicalMapping::new(
170                table_mapping.physical_start(),
171                table_mapping.virtual_start().cast::<SdtHeader>(),
172                table_mapping.region_length(),
173                table_mapping.mapped_length(),
174                $acpi_handler.clone(),
175            )
176        };
177
178        table_mapping
179    }};
180}
181
182/// Type capable of enumerating the existing ACPI tables on the system.
183///
184///
185/// ### Implementation Note
186///
187/// When using the `allocator_api`±`alloc` features, [`PlatformInfo::new()`] or [`PlatformInfo::new_in()`] provide
188/// a much cleaner API for enumerating ACPI structures once an `AcpiTables` has been constructed.
189#[derive(Debug)]
190pub struct AcpiTables<H: AcpiHandler> {
191    mapping: PhysicalMapping<H, SdtHeader>,
192    revision: u8,
193    handler: H,
194}
195
196impl<H> AcpiTables<H>
197where
198    H: AcpiHandler,
199{
200    /// Create an `AcpiTables` if you have the physical address of the RSDP.
201    ///
202    /// ### Safety
203    ///
204    /// Caller must ensure the provided address is valid to read as an RSDP.
205    pub unsafe fn from_rsdp(handler: H, address: usize) -> AcpiResult<Self> {
206        let rsdp_mapping = unsafe { handler.map_physical_region::<Rsdp>(address, mem::size_of::<Rsdp>()) };
207        rsdp_mapping.validate()?;
208
209        // Safety: RSDP has been validated.
210        unsafe { Self::from_validated_rsdp(handler, rsdp_mapping) }
211    }
212
213    /// Search for the RSDP on a BIOS platform. This accesses BIOS-specific memory locations and will probably not
214    /// work on UEFI platforms. See [`Rsdp::search_for_on_bios`] for details.
215    /// details.
216    ///
217    /// ### Safety
218    ///
219    /// The caller must ensure that this function is called on BIOS platforms.
220    pub unsafe fn search_for_rsdp_bios(handler: H) -> AcpiResult<Self> {
221        let rsdp_mapping = unsafe { Rsdp::search_for_on_bios(handler.clone())? };
222        // Safety: RSDP has been validated from `Rsdp::search_for_on_bios`
223        unsafe { Self::from_validated_rsdp(handler, rsdp_mapping) }
224    }
225
226    /// Create an `AcpiTables` if you have a `PhysicalMapping` of the RSDP that you know is correct. This is called
227    /// from `from_rsdp` after validation, but can also be used if you've searched for the RSDP manually on a BIOS
228    /// system.
229    ///
230    /// ### Safety
231    ///
232    /// Caller must ensure that the provided mapping is a fully validated RSDP.
233    pub unsafe fn from_validated_rsdp(handler: H, rsdp_mapping: PhysicalMapping<H, Rsdp>) -> AcpiResult<Self> {
234        let revision = rsdp_mapping.revision();
235        let root_table_mapping = if revision == 0 {
236            /*
237             * We're running on ACPI Version 1.0. We should use the 32-bit RSDT address.
238             */
239            let table_phys_start = rsdp_mapping.rsdt_address() as usize;
240            drop(rsdp_mapping);
241            read_root_table!(RSDT, table_phys_start, handler)
242        } else {
243            /*
244             * We're running on ACPI Version 2.0+. We should use the 64-bit XSDT address, truncated
245             * to 32 bits on x86.
246             */
247            let table_phys_start = rsdp_mapping.xsdt_address() as usize;
248            drop(rsdp_mapping);
249            read_root_table!(XSDT, table_phys_start, handler)
250        };
251
252        Ok(Self { mapping: root_table_mapping, revision, handler })
253    }
254
255    /// Create an `AcpiTables` if you have the physical address of the RSDT/XSDT.
256    ///
257    /// ### Safety
258    ///
259    /// Caller must ensure the provided address is valid RSDT/XSDT address.
260    pub unsafe fn from_rsdt(handler: H, revision: u8, address: usize) -> AcpiResult<Self> {
261        let root_table_mapping = if revision == 0 {
262            /*
263             * We're running on ACPI Version 1.0. We should use the 32-bit RSDT address.
264             */
265
266            read_root_table!(RSDT, address, handler)
267        } else {
268            /*
269             * We're running on ACPI Version 2.0+. We should use the 64-bit XSDT address, truncated
270             * to 32 bits on x86.
271             */
272
273            read_root_table!(XSDT, address, handler)
274        };
275
276        Ok(Self { mapping: root_table_mapping, revision, handler })
277    }
278
279    /// The ACPI revision of the tables enumerated by this structure.
280    #[inline]
281    pub const fn revision(&self) -> u8 {
282        self.revision
283    }
284
285    /// Constructs a [`TablesPhysPtrsIter`] over this table.
286    fn tables_phys_ptrs(&self) -> TablesPhysPtrsIter<'_> {
287        // SAFETY: The virtual address of the array of pointers follows the virtual address of the table in memory.
288        let ptrs_virt_start = unsafe { self.mapping.virtual_start().as_ptr().add(1).cast::<u8>() };
289        let ptrs_bytes_len = self.mapping.region_length() - mem::size_of::<SdtHeader>();
290        // SAFETY: `ptrs_virt_start` points to an array of `ptrs_bytes_len` bytes that lives as long as `self`.
291        let ptrs_bytes = unsafe { core::slice::from_raw_parts(ptrs_virt_start, ptrs_bytes_len) };
292        let ptr_size = if self.revision == 0 {
293            4 // RSDT entry size
294        } else {
295            8 // XSDT entry size
296        };
297
298        ptrs_bytes.chunks(ptr_size).map(|ptr_bytes_src| {
299            // Construct a native pointer using as many bytes as required from `ptr_bytes_src` (note that ACPI is
300            // little-endian)
301
302            let mut ptr_bytes_dst = [0; mem::size_of::<usize>()];
303            let common_ptr_size = usize::min(mem::size_of::<usize>(), ptr_bytes_src.len());
304            ptr_bytes_dst[..common_ptr_size].copy_from_slice(&ptr_bytes_src[..common_ptr_size]);
305
306            usize::from_le_bytes(ptr_bytes_dst) as *const SdtHeader
307        })
308    }
309
310    /// Searches through the ACPI table headers and attempts to locate the table with a matching `T::SIGNATURE`.
311    pub fn find_table<T: AcpiTable>(&self) -> AcpiResult<PhysicalMapping<H, T>> {
312        self.tables_phys_ptrs()
313            .find_map(|table_phys_ptr| {
314                // SAFETY: Table guarantees its contained addresses to be valid.
315                match unsafe { read_table(self.handler.clone(), table_phys_ptr as usize) } {
316                    Ok(table_mapping) => Some(table_mapping),
317                    Err(AcpiError::SdtInvalidSignature(_)) => None,
318                    Err(e) => {
319                        log::warn!(
320                            "Found invalid {} table at physical address {:p}: {:?}",
321                            T::SIGNATURE,
322                            table_phys_ptr,
323                            e
324                        );
325
326                        None
327                    }
328                }
329            })
330            .ok_or(AcpiError::TableMissing(T::SIGNATURE))
331    }
332
333    /// Iterates through all of the table headers.
334    pub fn headers(&self) -> SdtHeaderIterator<'_, H> {
335        SdtHeaderIterator { tables_phys_ptrs: self.tables_phys_ptrs(), handler: self.handler.clone() }
336    }
337
338    /// Finds and returns the DSDT AML table, if it exists.
339    pub fn dsdt(&self) -> AcpiResult<AmlTable> {
340        self.find_table::<fadt::Fadt>().and_then(|fadt| {
341            #[repr(transparent)]
342            struct Dsdt {
343                header: SdtHeader,
344            }
345
346            // Safety: Implementation properly represents a valid DSDT.
347            unsafe impl AcpiTable for Dsdt {
348                const SIGNATURE: Signature = Signature::DSDT;
349
350                fn header(&self) -> &SdtHeader {
351                    &self.header
352                }
353            }
354
355            let dsdt_address = fadt.dsdt_address()?;
356            let dsdt = unsafe { read_table::<H, Dsdt>(self.handler.clone(), dsdt_address)? };
357
358            Ok(AmlTable::new(dsdt_address, dsdt.header().length))
359        })
360    }
361
362    /// Iterates through all of the SSDT tables.
363    pub fn ssdts(&self) -> SsdtIterator<H> {
364        SsdtIterator { tables_phys_ptrs: self.tables_phys_ptrs(), handler: self.handler.clone() }
365    }
366
367    /// Convenience method for contructing a [`PlatformInfo`]. This is one of the first things you should usually do
368    /// with an `AcpiTables`, and allows to collect helpful information about the platform from the ACPI tables.
369    ///
370    /// Like [`platform_info_in`](Self::platform_info_in), but uses the global allocator.
371    #[cfg(feature = "alloc")]
372    pub fn platform_info(&self) -> AcpiResult<PlatformInfo<alloc::alloc::Global>> {
373        PlatformInfo::new(self)
374    }
375
376    /// Convenience method for contructing a [`PlatformInfo`]. This is one of the first things you should usually do
377    /// with an `AcpiTables`, and allows to collect helpful information about the platform from the ACPI tables.
378    #[cfg(feature = "allocator_api")]
379    pub fn platform_info_in<A>(&self, allocator: A) -> AcpiResult<PlatformInfo<A>>
380    where
381        A: core::alloc::Allocator + Clone,
382    {
383        PlatformInfo::new_in(self, allocator)
384    }
385}
386
387#[derive(Debug)]
388pub struct Sdt {
389    /// Physical address of the start of the SDT, including the header.
390    pub physical_address: usize,
391    /// Length of the table in bytes.
392    pub length: u32,
393    /// Whether this SDT has been validated. This is set to `true` the first time it is mapped and validated.
394    pub validated: bool,
395}
396
397/// An iterator over the physical table addresses in an RSDT or XSDT.
398type TablesPhysPtrsIter<'t> = core::iter::Map<core::slice::Chunks<'t, u8>, fn(&[u8]) -> *const SdtHeader>;
399
400#[derive(Debug)]
401pub struct AmlTable {
402    /// Physical address of the start of the AML stream (excluding the table header).
403    pub address: usize,
404    /// Length (in bytes) of the AML stream.
405    pub length: u32,
406}
407
408impl AmlTable {
409    /// Create an `AmlTable` from the address and length of the table **including the SDT header**.
410    pub(crate) fn new(address: usize, length: u32) -> AmlTable {
411        AmlTable {
412            address: address + mem::size_of::<SdtHeader>(),
413            length: length - mem::size_of::<SdtHeader>() as u32,
414        }
415    }
416}
417
418/// ### Safety
419///
420/// Caller must ensure the provided address is valid for being read as an `SdtHeader`.
421unsafe fn read_table<H: AcpiHandler, T: AcpiTable>(
422    handler: H,
423    address: usize,
424) -> AcpiResult<PhysicalMapping<H, T>> {
425    // Attempt to peek at the SDT header to correctly enumerate the entire table.
426
427    // SAFETY: `address` needs to be valid for the size of `SdtHeader`, or the ACPI tables are malformed (not a
428    // software issue).
429    let header_mapping = unsafe { handler.map_physical_region::<SdtHeader>(address, mem::size_of::<SdtHeader>()) };
430
431    SdtHeader::validate_lazy(header_mapping, handler)
432}
433
434/// Iterator that steps through all of the tables, and returns only the SSDTs as `AmlTable`s.
435pub struct SsdtIterator<'t, H>
436where
437    H: AcpiHandler,
438{
439    tables_phys_ptrs: TablesPhysPtrsIter<'t>,
440    handler: H,
441}
442
443impl<H> Iterator for SsdtIterator<'_, H>
444where
445    H: AcpiHandler,
446{
447    type Item = AmlTable;
448
449    fn next(&mut self) -> Option<Self::Item> {
450        #[repr(transparent)]
451        struct Ssdt {
452            header: SdtHeader,
453        }
454
455        // SAFETY: Implementation properly represents a valid SSDT.
456        unsafe impl AcpiTable for Ssdt {
457            const SIGNATURE: Signature = Signature::SSDT;
458
459            fn header(&self) -> &SdtHeader {
460                &self.header
461            }
462        }
463
464        // Borrow single field for closure to avoid immutable reference to `self` that inhibits `find_map`
465        let handler = &self.handler;
466
467        // Consume iterator until next valid SSDT and return the latter
468        self.tables_phys_ptrs.find_map(|table_phys_ptr| {
469            // SAFETY: Table guarantees its contained addresses to be valid.
470            match unsafe { read_table::<_, Ssdt>(handler.clone(), table_phys_ptr as usize) } {
471                Ok(ssdt_mapping) => Some(AmlTable::new(ssdt_mapping.physical_start(), ssdt_mapping.header.length)),
472                Err(AcpiError::SdtInvalidSignature(_)) => None,
473                Err(e) => {
474                    log::warn!("Found invalid SSDT at physical address {:p}: {:?}", table_phys_ptr, e);
475
476                    None
477                }
478            }
479        })
480    }
481}
482
483pub struct SdtHeaderIterator<'t, H>
484where
485    H: AcpiHandler,
486{
487    tables_phys_ptrs: TablesPhysPtrsIter<'t>,
488    handler: H,
489}
490
491impl<H> Iterator for SdtHeaderIterator<'_, H>
492where
493    H: AcpiHandler,
494{
495    type Item = SdtHeader;
496
497    fn next(&mut self) -> Option<Self::Item> {
498        loop {
499            let table_phys_ptr = self.tables_phys_ptrs.next()?;
500            // SAFETY: `address` needs to be valid for the size of `SdtHeader`, or the ACPI tables are malformed (not a
501            // software issue).
502            let header_mapping = unsafe {
503                self.handler.map_physical_region::<SdtHeader>(table_phys_ptr as usize, mem::size_of::<SdtHeader>())
504            };
505            let r = header_mapping.validate(header_mapping.signature);
506            if r.is_err() {
507                log::warn!("Found invalid SDT at physical address {:p}: {:?}", table_phys_ptr, r);
508                continue;
509            }
510            return Some(*header_mapping);
511        }
512    }
513}