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