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
                }
            }
        })
    }
}