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