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