• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)]
61 extern crate std;
62 
63 #[cfg(feature = "alloc")]
64 extern crate alloc;
65 
66 pub mod address;
67 pub mod bgrt;
68 pub mod fadt;
69 pub mod handler;
70 pub mod hpet;
71 pub mod madt;
72 pub mod mcfg;
73 pub mod rsdp;
74 pub mod sdt;
75 pub mod spcr;
76 
77 #[cfg(feature = "allocator_api")]
78 mod managed_slice;
79 #[cfg(feature = "allocator_api")]
80 pub use managed_slice::*;
81 
82 #[cfg(feature = "allocator_api")]
83 pub mod platform;
84 #[cfg(feature = "allocator_api")]
85 pub use crate::platform::{interrupt::InterruptModel, PlatformInfo};
86 
87 #[cfg(feature = "allocator_api")]
88 pub use crate::mcfg::PciConfigRegions;
89 
90 pub use fadt::PowerProfile;
91 pub use handler::{AcpiHandler, PhysicalMapping};
92 pub use hpet::HpetInfo;
93 pub use madt::MadtError;
94 
95 use crate::sdt::{SdtHeader, Signature};
96 use core::mem;
97 use rsdp::Rsdp;
98 
99 /// Result type used by error-returning functions.
100 pub 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).
113 pub unsafe trait AcpiTable {
114     const SIGNATURE: Signature;
115 
header(&self) -> &sdt::SdtHeader116     fn header(&self) -> &sdt::SdtHeader;
117 
validate(&self) -> AcpiResult<()>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)]
125 pub 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 
145 macro_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)]
190 pub struct AcpiTables<H: AcpiHandler> {
191     mapping: PhysicalMapping<H, SdtHeader>,
192     revision: u8,
193     handler: H,
194 }
195 
196 impl<H> AcpiTables<H>
197 where
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.
from_rsdp(handler: H, address: usize) -> AcpiResult<Self>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.
search_for_rsdp_bios(handler: H) -> AcpiResult<Self>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.
from_validated_rsdp(handler: H, rsdp_mapping: PhysicalMapping<H, Rsdp>) -> AcpiResult<Self>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.
from_rsdt(handler: H, revision: u8, address: usize) -> AcpiResult<Self>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]
revision(&self) -> u8281     pub const fn revision(&self) -> u8 {
282         self.revision
283     }
284 
285     /// Constructs a [`TablesPhysPtrsIter`] over this table.
tables_phys_ptrs(&self) -> TablesPhysPtrsIter<'_>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`.
find_table<T: AcpiTable>(&self) -> AcpiResult<PhysicalMapping<H, T>>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.
headers(&self) -> SdtHeaderIterator<'_, H>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.
dsdt(&self) -> AcpiResult<AmlTable>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.
ssdts(&self) -> SsdtIterator<H>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")]
platform_info(&self) -> AcpiResult<PlatformInfo<alloc::alloc::Global>>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")]
platform_info_in<A>(&self, allocator: A) -> AcpiResult<PlatformInfo<A>> where A: core::alloc::Allocator + Clone,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)]
388 pub 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.
398 type TablesPhysPtrsIter<'t> = core::iter::Map<core::slice::Chunks<'t, u8>, fn(&[u8]) -> *const SdtHeader>;
399 
400 #[derive(Debug)]
401 pub 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 
408 impl AmlTable {
409     /// Create an `AmlTable` from the address and length of the table **including the SDT header**.
new(address: usize, length: u32) -> AmlTable410     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`.
read_table<H: AcpiHandler, T: AcpiTable>( handler: H, address: usize, ) -> AcpiResult<PhysicalMapping<H, T>>421 unsafe 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.
435 pub struct SsdtIterator<'t, H>
436 where
437     H: AcpiHandler,
438 {
439     tables_phys_ptrs: TablesPhysPtrsIter<'t>,
440     handler: H,
441 }
442 
443 impl<H> Iterator for SsdtIterator<'_, H>
444 where
445     H: AcpiHandler,
446 {
447     type Item = AmlTable;
448 
next(&mut self) -> Option<Self::Item>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 
483 pub struct SdtHeaderIterator<'t, H>
484 where
485     H: AcpiHandler,
486 {
487     tables_phys_ptrs: TablesPhysPtrsIter<'t>,
488     handler: H,
489 }
490 
491 impl<H> Iterator for SdtHeaderIterator<'_, H>
492 where
493     H: AcpiHandler,
494 {
495     type Item = SdtHeader;
496 
next(&mut self) -> Option<Self::Item>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 }
514