acpi/
lib.rs

1//! `acpi` is a Rust library for interacting with the Advanced Configuration and Power Interface, a
2//! complex framework for power management and device discovery and configuration. ACPI is used on
3//! modern x64, as well as some ARM and RISC-V platforms. An operating system needs to interact with
4//! ACPI to correctly set up a platform's interrupt controllers, perform power management, and fully
5//! support many other platform capabilities.
6//!
7//! This crate provides a limited API that can be used without an allocator, for example for use
8//! from a bootloader. This API will allow you to search for the RSDP, enumerate over the available
9//! tables, and interact with the tables using their raw structures. All other functionality is
10//! behind an `alloc` feature (enabled by default) and requires an allocator.
11//!
12//! With an allocator, this crate also provides higher-level interfaces to the static tables, as
13//! well as a dynamic interpreter for AML - the bytecode format encoded in the DSDT and SSDT
14//! tables.
15//!
16//! ### Usage
17//! To use the library, you will need to provide an implementation of the [`Handler`] trait,
18//! which allows the library to make requests such as mapping a particular region of physical
19//! memory into the virtual address space.
20//!
21//! Next, you'll need to get the physical address of either the RSDP, or the RSDT/XSDT. The method
22//! for doing this depends on the platform you're running on and how you were booted. If you know
23//! the system was booted via the BIOS, you can use [`rsdp::Rsdp::search_for_on_bios`]. UEFI provides a
24//! separate mechanism for getting the address of the RSDP.
25//!
26//! You then need to construct an instance of [`AcpiTables`], which can be done in a few ways
27//! depending on how much 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//!
31//! Once you have an [`AcpiTables`], you can search for relevant tables, or use the higher-level
32//! interfaces, such as [`PlatformInfo`], [`PciConfigRegions`], or [`HpetInfo`].
33
34#![no_std]
35#![feature(allocator_api)]
36
37#[cfg_attr(test, macro_use)]
38#[cfg(test)]
39extern crate std;
40
41#[cfg(feature = "alloc")]
42extern crate alloc;
43
44pub mod address;
45#[cfg(feature = "aml")]
46pub mod aml;
47#[cfg(feature = "alloc")]
48pub mod platform;
49pub mod registers;
50pub mod rsdp;
51pub mod sdt;
52
53pub use pci_types::PciAddress;
54pub use sdt::{fadt::PowerProfile, hpet::HpetInfo, madt::MadtError};
55
56use crate::sdt::{SdtHeader, Signature};
57use core::{
58    fmt,
59    mem,
60    ops::{Deref, DerefMut},
61    pin::Pin,
62    ptr::NonNull,
63};
64use log::warn;
65use rsdp::Rsdp;
66
67/// `AcpiTables` should be constructed after finding the RSDP or RSDT/XSDT and allows enumeration
68/// of the system's ACPI tables.
69pub struct AcpiTables<H: Handler> {
70    rsdt_mapping: PhysicalMapping<H, SdtHeader>,
71    pub rsdp_revision: u8,
72    handler: H,
73}
74
75unsafe impl<H> Send for AcpiTables<H> where H: Handler + Send {}
76unsafe impl<H> Sync for AcpiTables<H> where H: Handler + Send {}
77
78impl<H> AcpiTables<H>
79where
80    H: Handler,
81{
82    /// Construct an `AcpiTables` from the **physical** address of the RSDP.
83    ///
84    /// # Safety
85    /// The address of the RSDP must be valid.
86    pub unsafe fn from_rsdp(handler: H, rsdp_address: usize) -> Result<AcpiTables<H>, AcpiError> {
87        let rsdp_mapping = unsafe { handler.map_physical_region::<Rsdp>(rsdp_address, mem::size_of::<Rsdp>()) };
88
89        /*
90         * If the address given does not have a correct RSDP signature, the user has probably given
91         * us an invalid address, and we should not continue. We're more lenient with other errors
92         * as it's probably a real RSDP and the firmware developers are just lazy.
93         */
94        match rsdp_mapping.validate() {
95            Ok(()) => (),
96            Err(AcpiError::RsdpIncorrectSignature) => return Err(AcpiError::RsdpIncorrectSignature),
97            Err(AcpiError::RsdpInvalidOemId) | Err(AcpiError::RsdpInvalidChecksum) => {
98                warn!("RSDP has invalid checksum or OEM ID. Continuing.");
99            }
100            Err(_) => (),
101        }
102
103        let rsdp_revision = rsdp_mapping.revision();
104        let rsdt_address = if rsdp_revision == 0 {
105            // We're running on ACPI Version 1.0. We should use the 32-bit RSDT address.
106            rsdp_mapping.rsdt_address() as usize
107        } else {
108            /*
109             * We're running on ACPI Version 2.0+. We should use the 64-bit XSDT address, truncated
110             * to 32 bits on x86.
111             */
112            rsdp_mapping.xsdt_address() as usize
113        };
114
115        unsafe { Self::from_rsdt(handler, rsdp_revision, rsdt_address) }
116    }
117
118    /// Construct an `AcpiTables` from the **physical** address of the RSDT/XSDT, and the revision
119    /// found in the RSDP.
120    ///
121    /// # Safety
122    /// The address of the RSDT must be valid.
123    pub unsafe fn from_rsdt(
124        handler: H,
125        rsdp_revision: u8,
126        rsdt_address: usize,
127    ) -> Result<AcpiTables<H>, AcpiError> {
128        let rsdt_mapping =
129            unsafe { handler.map_physical_region::<SdtHeader>(rsdt_address, mem::size_of::<SdtHeader>()) };
130        let rsdt_length = rsdt_mapping.length;
131        let rsdt_mapping = unsafe { handler.map_physical_region::<SdtHeader>(rsdt_address, rsdt_length as usize) };
132        Ok(Self { rsdt_mapping, rsdp_revision, handler })
133    }
134
135    /// Iterate over the **physical** addresses of the SDTs.
136    pub fn table_entries(&self) -> impl Iterator<Item = usize> {
137        let entry_size = if self.rsdp_revision == 0 { 4 } else { 8 };
138        let mut table_entries_ptr =
139            unsafe { self.rsdt_mapping.virtual_start.as_ptr().byte_add(mem::size_of::<SdtHeader>()) }.cast::<u8>();
140        let mut num_entries = (self.rsdt_mapping.region_length - mem::size_of::<SdtHeader>()) / entry_size;
141
142        core::iter::from_fn(move || {
143            if num_entries > 0 {
144                unsafe {
145                    let entry = if entry_size == 4 {
146                        *table_entries_ptr.cast::<u32>() as usize
147                    } else {
148                        *table_entries_ptr.cast::<u64>() as usize
149                    };
150                    table_entries_ptr = table_entries_ptr.byte_add(entry_size);
151                    num_entries -= 1;
152
153                    Some(entry)
154                }
155            } else {
156                None
157            }
158        })
159    }
160
161    /// Iterate over the headers of each SDT, along with their **physical** addresses.
162    pub fn table_headers(&self) -> impl Iterator<Item = (usize, SdtHeader)> {
163        self.table_entries().map(|table_phys_address| {
164            let mapping = unsafe {
165                self.handler.map_physical_region::<SdtHeader>(table_phys_address, mem::size_of::<SdtHeader>())
166            };
167            (table_phys_address, *mapping)
168        })
169    }
170
171    /// Find all tables with the signature `T::SIGNATURE`.
172    pub fn find_tables<T>(&self) -> impl Iterator<Item = PhysicalMapping<H, T>>
173    where
174        T: AcpiTable,
175    {
176        self.table_entries().filter_map(|table_phys_address| {
177            let header_mapping = unsafe {
178                self.handler.map_physical_region::<SdtHeader>(table_phys_address, mem::size_of::<SdtHeader>())
179            };
180            if header_mapping.signature == T::SIGNATURE {
181                // Extend the mapping to the entire table
182                let length = header_mapping.length;
183                drop(header_mapping);
184                Some(unsafe { self.handler.map_physical_region::<T>(table_phys_address, length as usize) })
185            } else {
186                None
187            }
188        })
189    }
190
191    /// Find the first table with the signature `T::SIGNATURE`.
192    pub fn find_table<T>(&self) -> Option<PhysicalMapping<H, T>>
193    where
194        T: AcpiTable,
195    {
196        self.find_tables().next()
197    }
198
199    pub fn dsdt(&self) -> Result<AmlTable, AcpiError> {
200        let Some(fadt) = self.find_table::<sdt::fadt::Fadt>() else {
201            Err(AcpiError::TableNotFound(Signature::FADT))?
202        };
203        let phys_address = fadt.dsdt_address()?;
204        let header =
205            unsafe { self.handler.map_physical_region::<SdtHeader>(phys_address, mem::size_of::<SdtHeader>()) };
206        Ok(AmlTable { phys_address, length: header.length, revision: header.revision })
207    }
208
209    pub fn ssdts(&self) -> impl Iterator<Item = AmlTable> {
210        self.table_headers().filter_map(|(phys_address, header)| {
211            if header.signature == Signature::SSDT {
212                Some(AmlTable { phys_address, length: header.length, revision: header.revision })
213            } else {
214                None
215            }
216        })
217    }
218}
219
220#[derive(Clone, Copy, Debug)]
221pub struct AmlTable {
222    /// The physical address of the start of the table. Add `mem::size_of::<SdtHeader>()` to this
223    /// to get the physical address of the start of the AML stream.
224    pub phys_address: usize,
225    /// The length of the table, including the header.
226    pub length: u32,
227    pub revision: u8,
228}
229
230/// All types representing ACPI tables should implement this trait.
231///
232/// ### Safety
233/// The table's memory is naively interpreted, so you must be careful in providing a type that
234/// correctly represents the table's structure. Regardless of the provided type's size, the region mapped will
235/// be the size specified in the SDT's header. If a table's definition may be larger than a valid
236/// SDT's size, [`ExtendedField`](sdt::ExtendedField) should be used to define fields that may or
237/// may not exist.
238pub unsafe trait AcpiTable {
239    const SIGNATURE: Signature;
240
241    fn header(&self) -> &SdtHeader;
242
243    fn validate(&self) -> Result<(), AcpiError> {
244        unsafe { self.header().validate(Self::SIGNATURE) }
245    }
246}
247
248#[derive(Clone, Debug)]
249#[non_exhaustive]
250pub enum AcpiError {
251    NoValidRsdp,
252    RsdpIncorrectSignature,
253    RsdpInvalidOemId,
254    RsdpInvalidChecksum,
255
256    SdtInvalidSignature(Signature),
257    SdtInvalidOemId(Signature),
258    SdtInvalidTableId(Signature),
259    SdtInvalidChecksum(Signature),
260    SdtInvalidCreatorId(Signature),
261
262    TableNotFound(Signature),
263    InvalidFacsAddress,
264    InvalidDsdtAddress,
265    InvalidMadt(MadtError),
266    InvalidGenericAddress,
267
268    Timeout,
269
270    #[cfg(feature = "alloc")]
271    Aml(aml::AmlError),
272
273    /// This is emitted to signal that the library does not support the requested behaviour. This
274    /// should eventually never be emitted.
275    LibUnimplemented,
276
277    /// This can be returned by the host (user of the library) to signal that required behaviour
278    /// has not been implemented. This will cause the error to be propagated back to the host if an
279    /// operation that requires that behaviour is performed.
280    HostUnimplemented,
281}
282
283/// Describes a physical mapping created by [`Handler::map_physical_region`] and unmapped by
284/// [`Handler::unmap_physical_region`]. The region mapped must be at least `size_of::<T>()`
285/// bytes, but may be bigger.
286pub struct PhysicalMapping<H, T>
287where
288    H: Handler,
289{
290    /// The physical address of the mapped structure. The actual mapping may start at a lower address
291    /// if the requested physical address is not well-aligned.
292    pub physical_start: usize,
293    /// The virtual address of the mapped structure. It must be a valid, non-null pointer to the
294    /// start of the requested structure. The actual virtual mapping may start at a lower address
295    /// if the requested address is not well-aligned.
296    pub virtual_start: NonNull<T>,
297    /// The size of the requested region, in bytes. Can be equal or larger to `size_of::<T>()`. If a
298    /// larger region has been mapped, this should still be the requested size.
299    pub region_length: usize,
300    /// The total size of the produced mapping. This may be the same as `region_length`, or larger to
301    /// meet requirements of the mapping implementation.
302    pub mapped_length: usize,
303    /// The [`Handler`] that was used to produce the mapping. When this mapping is dropped, this
304    /// handler will be used to unmap the region.
305    pub handler: H,
306}
307
308impl<H, T> PhysicalMapping<H, T>
309where
310    H: Handler,
311{
312    /// Get a pinned reference to the inner `T`. This is generally only useful if `T` is `!Unpin`,
313    /// otherwise the mapping can simply be dereferenced to access the inner type.
314    pub fn get(&self) -> Pin<&T> {
315        unsafe { Pin::new_unchecked(self.virtual_start.as_ref()) }
316    }
317}
318
319impl<H, T> fmt::Debug for PhysicalMapping<H, T>
320where
321    H: Handler,
322{
323    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
324        f.debug_struct("PhysicalMapping")
325            .field("physical_start", &self.physical_start)
326            .field("virtual_start", &self.virtual_start)
327            .field("region_length", &self.region_length)
328            .field("mapped_length", &self.mapped_length)
329            .field("handler", &())
330            .finish()
331    }
332}
333
334unsafe impl<H: Handler + Send, T: Send> Send for PhysicalMapping<H, T> {}
335
336impl<H, T> Deref for PhysicalMapping<H, T>
337where
338    T: Unpin,
339    H: Handler,
340{
341    type Target = T;
342
343    fn deref(&self) -> &T {
344        unsafe { self.virtual_start.as_ref() }
345    }
346}
347
348impl<H, T> DerefMut for PhysicalMapping<H, T>
349where
350    T: Unpin,
351    H: Handler,
352{
353    fn deref_mut(&mut self) -> &mut T {
354        unsafe { self.virtual_start.as_mut() }
355    }
356}
357
358impl<H, T> Drop for PhysicalMapping<H, T>
359where
360    H: Handler,
361{
362    fn drop(&mut self) {
363        H::unmap_physical_region(self)
364    }
365}
366
367/// A `Handle` is an opaque reference to an object that is managed by the host on behalf of this
368/// library.
369///
370/// The library will treat the value of a handle as entirely opaque. You may manage handles
371/// however you wish, and the same value can be used to refer to objects of different types, if
372/// desired.
373#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
374pub struct Handle(pub u32);
375
376/// An implementation of this trait must be provided to allow `acpi` to perform operations that
377/// interface with the underlying hardware and other systems in your host implementation. This
378/// interface is designed to be flexible to allow usage of the library from a variety of settings.
379///
380/// Depending on your usage of this library, not all functionality may be required. If you do not
381/// provide certain functionality, you should return [`AcpiError::HostUnimplemented`]. The library
382/// will attempt to propagate this error back to the host if an operation cannot be performed
383/// without that functionality.
384///
385/// The `Handler` must be cheaply clonable (e.g. a reference, `Arc`, marker struct, etc.) as a copy
386/// of the handler is stored in various structures, such as in each [`PhysicalMapping`] to
387/// facilitate unmapping.
388pub trait Handler: Clone {
389    /// Given a physical address and a size, map a region of physical memory that contains `T` (note: the passed
390    /// size may be larger than `size_of::<T>()`). The address is not neccessarily page-aligned, so the
391    /// implementation may need to map more than `size` bytes. The virtual address the region is mapped to does not
392    /// matter, as long as it is accessible to `acpi`. Refer to the fields on [`PhysicalMapping`] to understand how
393    /// to produce one properly.
394    ///
395    /// ## Safety
396    ///
397    /// - `physical_address` must point to a valid `T` in physical memory.
398    /// - `size` must be at least `size_of::<T>()`.
399    unsafe fn map_physical_region<T>(&self, physical_address: usize, size: usize) -> PhysicalMapping<Self, T>;
400
401    /// Unmap the given physical mapping. This is called when a `PhysicalMapping` is dropped, you should **not** manually call this.
402    ///
403    /// Note: A reference to the `Handler` used to construct `region` can be acquired by calling [`PhysicalMapping::mapper`].
404    fn unmap_physical_region<T>(region: &PhysicalMapping<Self, T>);
405
406    // TODO: maybe we should map stuff ourselves in the AML interpreter and do this internally?
407    // Maybe provide a hook for tracing the IO / emit trace events ourselves if we do do that?
408    fn read_u8(&self, address: usize) -> u8;
409    fn read_u16(&self, address: usize) -> u16;
410    fn read_u32(&self, address: usize) -> u32;
411    fn read_u64(&self, address: usize) -> u64;
412
413    fn write_u8(&self, address: usize, value: u8);
414    fn write_u16(&self, address: usize, value: u16);
415    fn write_u32(&self, address: usize, value: u32);
416    fn write_u64(&self, address: usize, value: u64);
417
418    // TODO: would be nice to provide defaults that just do the actual port IO on x86?
419    fn read_io_u8(&self, port: u16) -> u8;
420    fn read_io_u16(&self, port: u16) -> u16;
421    fn read_io_u32(&self, port: u16) -> u32;
422
423    fn write_io_u8(&self, port: u16, value: u8);
424    fn write_io_u16(&self, port: u16, value: u16);
425    fn write_io_u32(&self, port: u16, value: u32);
426
427    fn read_pci_u8(&self, address: PciAddress, offset: u16) -> u8;
428    fn read_pci_u16(&self, address: PciAddress, offset: u16) -> u16;
429    fn read_pci_u32(&self, address: PciAddress, offset: u16) -> u32;
430
431    fn write_pci_u8(&self, address: PciAddress, offset: u16, value: u8);
432    fn write_pci_u16(&self, address: PciAddress, offset: u16, value: u16);
433    fn write_pci_u32(&self, address: PciAddress, offset: u16, value: u32);
434
435    /// Returns a monotonically-increasing value of nanoseconds.
436    fn nanos_since_boot(&self) -> u64;
437
438    /// Stall for at least the given number of **microseconds**. An implementation should not relinquish control of
439    /// the processor during the stall, and for this reason, firmwares should not stall for periods of more than
440    /// 100 microseconds.
441    fn stall(&self, microseconds: u64);
442
443    /// Sleep for at least the given number of **milliseconds**. An implementation may round to the closest sleep
444    /// time supported, and should relinquish the processor.
445    fn sleep(&self, milliseconds: u64);
446
447    #[cfg(feature = "aml")]
448    fn create_mutex(&self) -> Handle;
449
450    /// Acquire the mutex referred to by the given handle. `timeout` is a millisecond timeout value
451    /// with the following meaning:
452    ///    - `0` - try to acquire the mutex once, in a non-blocking manner. If the mutex cannot be
453    ///      acquired immediately, return `Err(AmlError::MutexAcquireTimeout)`
454    ///    - `1-0xfffe` - try to acquire the mutex for at least `timeout` milliseconds.
455    ///    - `0xffff` - try to acquire the mutex indefinitely. Should not return `MutexAcquireTimeout`.
456    ///
457    /// AML mutexes are **reentrant** - that is, a thread may acquire the same mutex more than once
458    /// without causing a deadlock.
459    #[cfg(feature = "aml")]
460    fn acquire(&self, mutex: Handle, timeout: u16) -> Result<(), aml::AmlError>;
461    #[cfg(feature = "aml")]
462    fn release(&self, mutex: Handle);
463
464    #[cfg(feature = "aml")]
465    fn breakpoint(&self) {}
466
467    #[cfg(feature = "aml")]
468    fn handle_debug(&self, _object: &aml::object::Object) {}
469
470    #[cfg(feature = "aml")]
471    fn handle_fatal_error(&self, fatal_type: u8, fatal_code: u32, fatal_arg: u64) {
472        panic!(
473            "Fatal error while executing AML (encountered DefFatalOp). fatal_type = {}, fatal_code = {}, fatal_arg = {}",
474            fatal_type, fatal_code, fatal_arg
475        );
476    }
477}
478
479#[cfg(test)]
480mod tests {
481    use super::*;
482
483    #[test]
484    #[allow(dead_code)]
485    fn test_physical_mapping_send_sync() {
486        fn test_send_sync<T: Send>() {}
487        fn caller<H: Handler + Send, T: Send>() {
488            test_send_sync::<PhysicalMapping<H, T>>();
489        }
490    }
491}