blob: 83facc5bf8fea863b5cc4836cdd6dd6dc22cf291 [file] [log] [blame]
Arve Hjønnevåg8dac36a2024-02-26 14:19:16 -08001//! 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)]
64extern crate std;
65
66#[cfg(feature = "alloc")]
67extern crate alloc;
68
69pub mod address;
70pub mod bgrt;
71pub mod fadt;
72pub mod handler;
73pub mod hpet;
74pub mod madt;
75pub mod mcfg;
76pub mod rsdp;
77pub mod sdt;
78
79#[cfg(feature = "allocator_api")]
80mod managed_slice;
81#[cfg(feature = "allocator_api")]
82pub use managed_slice::*;
83
84#[cfg(feature = "allocator_api")]
85pub mod platform;
86#[cfg(feature = "allocator_api")]
87pub use crate::platform::{interrupt::InterruptModel, PlatformInfo};
88
89#[cfg(feature = "allocator_api")]
90pub use crate::mcfg::PciConfigRegions;
91
92pub use fadt::PowerProfile;
93pub use handler::{AcpiHandler, PhysicalMapping};
94pub use hpet::HpetInfo;
95pub use madt::MadtError;
96
97use crate::sdt::{SdtHeader, Signature};
98use core::mem;
99use rsdp::Rsdp;
100
101/// Result type used by error-returning functions.
102pub 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).
115pub unsafe trait AcpiTable {
116 const SIGNATURE: Signature;
117
118 fn header(&self) -> &sdt::SdtHeader;
119
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)]
127pub 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)]
155pub struct AcpiTables<H: AcpiHandler> {
156 mapping: PhysicalMapping<H, SdtHeader>,
157 revision: u8,
158 handler: H,
159}
160
161impl<H> AcpiTables<H>
162where
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.
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.
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.
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]
253 pub const fn revision(&self) -> u8 {
254 self.revision
255 }
256
257 /// Constructs a [`TablesPhysPtrsIter`] over this table.
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`.
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.
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.
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")]
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")]
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)]
357pub 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.
367type TablesPhysPtrsIter<'t> = core::iter::Map<core::slice::Chunks<'t, u8>, fn(&[u8]) -> *const SdtHeader>;
368
369#[derive(Debug)]
370pub 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
377impl AmlTable {
378 /// Create an `AmlTable` from the address and length of the table **including the SDT header**.
379 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`.
388unsafe 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.
402pub struct SsdtIterator<'t, H>
403where
404 H: AcpiHandler,
405{
406 tables_phys_ptrs: TablesPhysPtrsIter<'t>,
407 handler: H,
408}
409
410impl<'t, H> Iterator for SsdtIterator<'t, H>
411where
412 H: AcpiHandler,
413{
414 type Item = AmlTable;
415
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}