|
@@ -1,28 +1,45 @@
|
|
|
-//! A library for parsing ACPI tables. This crate can be used by bootloaders and kernels for
|
|
|
-//! architectures that support ACPI. The crate is far from feature-complete, but can still be used
|
|
|
-//! for finding and parsing the static tables, which is enough to set up hardware such as the APIC
|
|
|
-//! and HPET on x86_64.
|
|
|
+//! 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.
|
|
|
//!
|
|
|
-//! The crate is designed for use 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 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 requires `alloc` to make heap allocations. If you are trying to find the RSDP in an environment that
|
|
|
+//! does not have a heap (e.g. a bootloader), you can use the `rsdp` crate. The types from that crate are
|
|
|
+//! compatible with `acpi`.
|
|
|
//!
|
|
|
//! ### 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.
|
|
|
+//! 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.
|
|
|
//!
|
|
|
-//! You should then call one of the entry points, based on how much information you have:
|
|
|
-//! * Call `parse_rsdp` if you have the physical address of the RSDP
|
|
|
-//! * Call `parse_rsdt` if you have the physical address of the RSDT / XSDT
|
|
|
-//! * Call `search_for_rsdp_bios` if you don't have the address of either structure, but **you know
|
|
|
-//! you're running on BIOS, not UEFI**
|
|
|
+//! `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`.
|
|
|
//!
|
|
|
-//! All of these methods return an instance of `Acpi`. This struct contains all the information
|
|
|
-//! gathered from the static tables, and can be queried to set up hardware etc.
|
|
|
+//! 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.
|
|
|
|
|
|
#![no_std]
|
|
|
#![feature(const_generics, unsafe_block_in_unsafe_fn)]
|
|
|
+#![deny(unsafe_op_in_unsafe_fn)]
|
|
|
|
|
|
extern crate alloc;
|
|
|
#[cfg_attr(test, macro_use)]
|
|
@@ -30,83 +47,241 @@ extern crate alloc;
|
|
|
extern crate std;
|
|
|
|
|
|
mod fadt;
|
|
|
-pub mod handler;
|
|
|
mod hpet;
|
|
|
-pub mod interrupt;
|
|
|
mod madt;
|
|
|
mod mcfg;
|
|
|
-mod rsdp;
|
|
|
-mod rsdp_search;
|
|
|
+pub mod platform;
|
|
|
mod sdt;
|
|
|
|
|
|
pub use crate::{
|
|
|
fadt::PowerProfile,
|
|
|
- handler::{AcpiHandler, PhysicalMapping},
|
|
|
hpet::HpetInfo,
|
|
|
- interrupt::InterruptModel,
|
|
|
madt::MadtError,
|
|
|
mcfg::PciConfigRegions,
|
|
|
- rsdp_search::search_for_rsdp_bios,
|
|
|
+ platform::{InterruptModel, PlatformInfo},
|
|
|
};
|
|
|
-
|
|
|
-use crate::{
|
|
|
- rsdp::Rsdp,
|
|
|
- sdt::{SdtHeader, Signature},
|
|
|
+pub use rsdp::{
|
|
|
+ handler::{AcpiHandler, PhysicalMapping},
|
|
|
+ RsdpError,
|
|
|
};
|
|
|
-use alloc::vec::Vec;
|
|
|
+
|
|
|
+use crate::sdt::{SdtHeader, Signature};
|
|
|
+use alloc::{collections::BTreeMap, vec::Vec};
|
|
|
use core::mem;
|
|
|
+use log::trace;
|
|
|
+use rsdp::Rsdp;
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
pub enum AcpiError {
|
|
|
- RsdpIncorrectSignature,
|
|
|
- RsdpInvalidOemId,
|
|
|
- RsdpInvalidChecksum,
|
|
|
- NoValidRsdp,
|
|
|
+ Rsdp(RsdpError),
|
|
|
|
|
|
SdtInvalidSignature(Signature),
|
|
|
SdtInvalidOemId(Signature),
|
|
|
SdtInvalidTableId(Signature),
|
|
|
SdtInvalidChecksum(Signature),
|
|
|
|
|
|
+ TableMissing(Signature),
|
|
|
+ InvalidDsdtAddress,
|
|
|
InvalidMadt(MadtError),
|
|
|
}
|
|
|
|
|
|
-#[derive(Clone, Copy, Debug)]
|
|
|
-#[repr(C, packed)]
|
|
|
-pub(crate) struct GenericAddress {
|
|
|
- address_space: u8,
|
|
|
- bit_width: u8,
|
|
|
- bit_offset: u8,
|
|
|
- access_size: u8,
|
|
|
- address: u64,
|
|
|
+pub struct AcpiTables<H>
|
|
|
+where
|
|
|
+ H: AcpiHandler,
|
|
|
+{
|
|
|
+ /// The revision of ACPI that the system uses, as inferred from the revision of the RSDT/XSDT.
|
|
|
+ pub revision: u8,
|
|
|
+ pub sdts: BTreeMap<sdt::Signature, Sdt>,
|
|
|
+ pub dsdt: Option<AmlTable>,
|
|
|
+ pub ssdts: Vec<AmlTable>,
|
|
|
+ handler: H,
|
|
|
}
|
|
|
|
|
|
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
|
-pub enum ProcessorState {
|
|
|
- /// A processor in this state is unusable, and you must not attempt to bring it up.
|
|
|
- Disabled,
|
|
|
+impl<H> AcpiTables<H>
|
|
|
+where
|
|
|
+ H: AcpiHandler,
|
|
|
+{
|
|
|
+ /// Create an `AcpiTables` if you have the physical address of the RSDP.
|
|
|
+ pub unsafe fn from_rsdp(handler: H, rsdp_address: usize) -> Result<AcpiTables<H>, AcpiError> {
|
|
|
+ let rsdp_mapping = unsafe { handler.map_physical_region::<Rsdp>(rsdp_address, mem::size_of::<Rsdp>()) };
|
|
|
+ rsdp_mapping.validate().map_err(|err| AcpiError::Rsdp(err))?;
|
|
|
|
|
|
- /// A processor waiting for a SIPI (Startup Inter-processor Interrupt) is currently not active,
|
|
|
- /// but may be brought up.
|
|
|
- WaitingForSipi,
|
|
|
+ Self::from_validated_rsdp(handler, rsdp_mapping)
|
|
|
+ }
|
|
|
|
|
|
- /// A Running processor is currently brought up and running code.
|
|
|
- Running,
|
|
|
-}
|
|
|
+ /// 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) -> Result<AcpiTables<H>, AcpiError> {
|
|
|
+ let rsdp_mapping =
|
|
|
+ unsafe { Rsdp::search_for_on_bios(handler.clone()) }.map_err(|err| AcpiError::Rsdp(err))?;
|
|
|
+ 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.
|
|
|
+ pub fn from_validated_rsdp(
|
|
|
+ handler: H,
|
|
|
+ rsdp_mapping: PhysicalMapping<H, Rsdp>,
|
|
|
+ ) -> Result<AcpiTables<H>, AcpiError> {
|
|
|
+ let revision = rsdp_mapping.revision();
|
|
|
+
|
|
|
+ if revision == 0 {
|
|
|
+ /*
|
|
|
+ * We're running on ACPI Version 1.0. We should use the 32-bit RSDT address.
|
|
|
+ */
|
|
|
+ let rsdt_address = rsdp_mapping.rsdt_address();
|
|
|
+ unsafe { Self::from_rsdt(handler, revision, rsdt_address as usize) }
|
|
|
+ } else {
|
|
|
+ /*
|
|
|
+ * We're running on ACPI Version 2.0+. We should use the 64-bit XSDT address, truncated
|
|
|
+ * to 32 bits on x86.
|
|
|
+ */
|
|
|
+ let xsdt_address = rsdp_mapping.xsdt_address();
|
|
|
+ unsafe { Self::from_rsdt(handler, revision, xsdt_address as usize) }
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
|
-pub struct Processor {
|
|
|
- pub processor_uid: u8,
|
|
|
- pub local_apic_id: u8,
|
|
|
+ /// Create an `AcpiTables` if you have the physical address of the RSDT. This is useful, for example, if your chosen
|
|
|
+ /// bootloader reads the RSDP and passes you the address of the RSDT. You also need to supply the correct ACPI
|
|
|
+ /// revision - if `0`, a RSDT is expected, while a `XSDT` is expected for greater revisions.
|
|
|
+ pub unsafe fn from_rsdt(handler: H, revision: u8, rsdt_address: usize) -> Result<AcpiTables<H>, AcpiError> {
|
|
|
+ let mut result = AcpiTables { revision, sdts: BTreeMap::new(), dsdt: None, ssdts: Vec::new(), handler };
|
|
|
+
|
|
|
+ let header = sdt::peek_at_sdt_header(&result.handler, rsdt_address);
|
|
|
+ let mapping =
|
|
|
+ unsafe { result.handler.map_physical_region::<SdtHeader>(rsdt_address, header.length as usize) };
|
|
|
+
|
|
|
+ if revision == 0 {
|
|
|
+ /*
|
|
|
+ * ACPI Version 1.0. It's a RSDT!
|
|
|
+ */
|
|
|
+ mapping.validate(sdt::Signature::RSDT)?;
|
|
|
+
|
|
|
+ let num_tables = (mapping.length as usize - mem::size_of::<SdtHeader>()) / mem::size_of::<u32>();
|
|
|
+ let tables_base =
|
|
|
+ ((mapping.virtual_start.as_ptr() as usize) + mem::size_of::<SdtHeader>()) as *const u32;
|
|
|
+
|
|
|
+ for i in 0..num_tables {
|
|
|
+ result.process_sdt(unsafe { *tables_base.offset(i as isize) as usize })?;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ /*
|
|
|
+ * ACPI Version 2.0+. It's a XSDT!
|
|
|
+ */
|
|
|
+ mapping.validate(sdt::Signature::XSDT)?;
|
|
|
+
|
|
|
+ let num_tables = (mapping.length as usize - mem::size_of::<SdtHeader>()) / mem::size_of::<u64>();
|
|
|
+ let tables_base =
|
|
|
+ ((mapping.virtual_start.as_ptr() as usize) + mem::size_of::<SdtHeader>()) as *const u64;
|
|
|
+
|
|
|
+ for i in 0..num_tables {
|
|
|
+ result.process_sdt(unsafe { *tables_base.offset(i as isize) as usize })?;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Ok(result)
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Construct an `AcpiTables` from a custom set of "discovered" tables. This is provided to allow the library
|
|
|
+ /// to be used from unconventional settings (e.g. in userspace), for example with a `AcpiHandler` that detects
|
|
|
+ /// accesses to specific physical addresses, and provides the correct data.
|
|
|
+ pub fn from_tables_direct(
|
|
|
+ handler: H,
|
|
|
+ revision: u8,
|
|
|
+ sdts: BTreeMap<sdt::Signature, Sdt>,
|
|
|
+ dsdt: Option<AmlTable>,
|
|
|
+ ssdts: Vec<AmlTable>,
|
|
|
+ ) -> AcpiTables<H> {
|
|
|
+ AcpiTables { revision, sdts, dsdt, ssdts, handler }
|
|
|
+ }
|
|
|
+
|
|
|
+ fn process_sdt(&mut self, physical_address: usize) -> Result<(), AcpiError> {
|
|
|
+ let header = sdt::peek_at_sdt_header(&self.handler, physical_address);
|
|
|
+ trace!("Found ACPI table with signature {:?} and length {:?}", header.signature, header.length);
|
|
|
+
|
|
|
+ match header.signature {
|
|
|
+ Signature::FADT => {
|
|
|
+ use fadt::Fadt;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * For whatever reason, they chose to put the DSDT inside the FADT, instead of just listing it
|
|
|
+ * as another SDT. We extract it here to provide a nicer public API.
|
|
|
+ */
|
|
|
+ let fadt_mapping =
|
|
|
+ unsafe { self.handler.map_physical_region::<Fadt>(physical_address, mem::size_of::<Fadt>()) };
|
|
|
+ fadt_mapping.validate()?;
|
|
|
+
|
|
|
+ let dsdt_address = fadt_mapping.dsdt_address()?;
|
|
|
+ let dsdt_header = sdt::peek_at_sdt_header(&self.handler, dsdt_address);
|
|
|
+ self.dsdt = Some(AmlTable::new(dsdt_address, dsdt_header.length));
|
|
|
+
|
|
|
+ /*
|
|
|
+ * We've already validated the FADT to get the DSDT out, so it doesn't need to be done again.
|
|
|
+ */
|
|
|
+ self.sdts
|
|
|
+ .insert(Signature::FADT, Sdt { physical_address, length: header.length, validated: true });
|
|
|
+ }
|
|
|
+ Signature::SSDT => {
|
|
|
+ self.ssdts.push(AmlTable::new(physical_address, header.length));
|
|
|
+ }
|
|
|
+ signature => {
|
|
|
+ self.sdts.insert(signature, Sdt { physical_address, length: header.length, validated: false });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Ok(())
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Create a mapping to a SDT, given its signature. This validates the SDT if it has not already been
|
|
|
+ /// validated.
|
|
|
+ ///
|
|
|
+ /// ### Safety
|
|
|
+ /// The table's memory is naively interpreted as a `T`, and 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 `T` that is larger than this, *may* lead to
|
|
|
+ /// page-faults, aliasing references, or derefencing uninitialized memory (the latter two of which are UB).
|
|
|
+ /// This isn't forbidden, however, because some tables rely on `T` 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 fn get_sdt<T>(&self, signature: sdt::Signature) -> Result<Option<PhysicalMapping<H, T>>, AcpiError>
|
|
|
+ where
|
|
|
+ T: AcpiTable,
|
|
|
+ {
|
|
|
+ let sdt = match self.sdts.get(&signature) {
|
|
|
+ Some(sdt) => sdt,
|
|
|
+ None => return Ok(None),
|
|
|
+ };
|
|
|
+ let mapping = unsafe { self.handler.map_physical_region::<T>(sdt.physical_address, sdt.length as usize) };
|
|
|
+
|
|
|
+ if !sdt.validated {
|
|
|
+ mapping.header().validate(signature)?;
|
|
|
+ }
|
|
|
+
|
|
|
+ Ok(Some(mapping))
|
|
|
+ }
|
|
|
+
|
|
|
+ /// 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.
|
|
|
+ pub fn platform_info(&self) -> Result<PlatformInfo, AcpiError> {
|
|
|
+ PlatformInfo::new(self)
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
- /// The state of this processor. Always check that the processor is not `Disabled` before
|
|
|
- /// attempting to bring it up!
|
|
|
- pub state: ProcessorState,
|
|
|
+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,
|
|
|
+}
|
|
|
|
|
|
- /// Whether this processor is the Bootstrap Processor (BSP), or an Application Processor (AP).
|
|
|
- /// When the bootloader is entered, the BSP is the only processor running code. To run code on
|
|
|
- /// more than one processor, you need to "bring up" the APs.
|
|
|
- pub is_ap: bool,
|
|
|
+/// All types representing ACPI tables should implement this trait.
|
|
|
+pub trait AcpiTable {
|
|
|
+ fn header(&self) -> &sdt::SdtHeader;
|
|
|
}
|
|
|
|
|
|
#[derive(Debug)]
|
|
@@ -127,122 +302,12 @@ impl AmlTable {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-#[derive(Debug)]
|
|
|
-pub struct Acpi {
|
|
|
- pub acpi_revision: u8,
|
|
|
-
|
|
|
- /// The boot processor. Until you bring up any APs, this is the only processor running code.
|
|
|
- pub boot_processor: Option<Processor>,
|
|
|
-
|
|
|
- /// Application processes. These are not brought up until you do so, and must be brought up in
|
|
|
- /// the order they appear in this list.
|
|
|
- pub application_processors: Vec<Processor>,
|
|
|
-
|
|
|
- /// ACPI theoretically allows for more than one interrupt model to be supported by the same
|
|
|
- /// hardware. For simplicity and because hardware practically will only support one model, we
|
|
|
- /// just error in cases that the tables detail more than one.
|
|
|
- pub interrupt_model: Option<InterruptModel>,
|
|
|
- pub hpet: Option<HpetInfo>,
|
|
|
- pub power_profile: PowerProfile,
|
|
|
-
|
|
|
- /// Info about the DSDT, if we find it.
|
|
|
- pub dsdt: Option<AmlTable>,
|
|
|
-
|
|
|
- /// Info about any SSDTs, if there are any.
|
|
|
- pub ssdts: Vec<AmlTable>,
|
|
|
-
|
|
|
- /// Info about the PCI-E configuration memory regions, collected from the MCFG.
|
|
|
- pub pci_config_regions: Option<PciConfigRegions>,
|
|
|
-}
|
|
|
-
|
|
|
-/// This is the entry point of `acpi` if you have the **physical** address of the RSDP. It maps
|
|
|
-/// the RSDP, works out what version of ACPI the hardware supports, and passes the physical
|
|
|
-/// address of the RSDT/XSDT to `parse_rsdt`.
|
|
|
-pub unsafe fn parse_rsdp<H>(handler: &mut H, rsdp_address: usize) -> Result<Acpi, AcpiError>
|
|
|
-where
|
|
|
- H: AcpiHandler,
|
|
|
-{
|
|
|
- let rsdp_mapping = unsafe { handler.map_physical_region::<Rsdp>(rsdp_address, mem::size_of::<Rsdp>()) };
|
|
|
- (*rsdp_mapping).validate()?;
|
|
|
-
|
|
|
- parse_validated_rsdp(handler, rsdp_mapping)
|
|
|
-}
|
|
|
-
|
|
|
-fn parse_validated_rsdp<H>(handler: &mut H, rsdp_mapping: PhysicalMapping<Rsdp>) -> Result<Acpi, AcpiError>
|
|
|
-where
|
|
|
- H: AcpiHandler,
|
|
|
-{
|
|
|
- let revision = (*rsdp_mapping).revision();
|
|
|
-
|
|
|
- if revision == 0 {
|
|
|
- /*
|
|
|
- * We're running on ACPI Version 1.0. We should use the 32-bit RSDT address.
|
|
|
- */
|
|
|
- let rsdt_address = (*rsdp_mapping).rsdt_address();
|
|
|
- handler.unmap_physical_region(rsdp_mapping);
|
|
|
- unsafe { parse_rsdt(handler, revision, rsdt_address as usize) }
|
|
|
- } else {
|
|
|
- /*
|
|
|
- * We're running on ACPI Version 2.0+. We should use the 64-bit XSDT address, truncated
|
|
|
- * to 32 bits on x86.
|
|
|
- */
|
|
|
- let xsdt_address = (*rsdp_mapping).xsdt_address();
|
|
|
- handler.unmap_physical_region(rsdp_mapping);
|
|
|
- unsafe { parse_rsdt(handler, revision, xsdt_address as usize) }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/// This is the entry point of `acpi` if you already have the **physical** address of the
|
|
|
-/// RSDT/XSDT; it parses all the SDTs in the RSDT/XSDT, calling the relevant handlers in the
|
|
|
-/// implementation's `AcpiHandler`.
|
|
|
-///
|
|
|
-/// If the given revision is 0, an address to the RSDT is expected. Otherwise, an address to
|
|
|
-/// the XSDT is expected.
|
|
|
-pub unsafe fn parse_rsdt<H>(handler: &mut H, revision: u8, physical_address: usize) -> Result<Acpi, AcpiError>
|
|
|
-where
|
|
|
- H: AcpiHandler,
|
|
|
-{
|
|
|
- let mut acpi = Acpi {
|
|
|
- acpi_revision: revision,
|
|
|
- boot_processor: None,
|
|
|
- application_processors: Vec::new(),
|
|
|
- interrupt_model: None,
|
|
|
- hpet: None,
|
|
|
- power_profile: PowerProfile::Unspecified,
|
|
|
- dsdt: None,
|
|
|
- ssdts: Vec::new(),
|
|
|
- pci_config_regions: None,
|
|
|
- };
|
|
|
-
|
|
|
- let header = sdt::peek_at_sdt_header(handler, physical_address);
|
|
|
- let mapping = unsafe { handler.map_physical_region::<SdtHeader>(physical_address, header.length as usize) };
|
|
|
-
|
|
|
- if revision == 0 {
|
|
|
- /*
|
|
|
- * ACPI Version 1.0. It's a RSDT!
|
|
|
- */
|
|
|
- (*mapping).validate(sdt::Signature::RSDT)?;
|
|
|
-
|
|
|
- let num_tables = ((*mapping).length as usize - mem::size_of::<SdtHeader>()) / mem::size_of::<u32>();
|
|
|
- let tables_base = ((mapping.virtual_start.as_ptr() as usize) + mem::size_of::<SdtHeader>()) as *const u32;
|
|
|
-
|
|
|
- for i in 0..num_tables {
|
|
|
- sdt::dispatch_sdt(&mut acpi, handler, unsafe { *tables_base.offset(i as isize) } as usize)?;
|
|
|
- }
|
|
|
- } else {
|
|
|
- /*
|
|
|
- * ACPI Version 2.0+. It's a XSDT!
|
|
|
- */
|
|
|
- (*mapping).validate(sdt::Signature::XSDT)?;
|
|
|
-
|
|
|
- let num_tables = ((*mapping).length as usize - mem::size_of::<SdtHeader>()) / mem::size_of::<u64>();
|
|
|
- let tables_base = ((mapping.virtual_start.as_ptr() as usize) + mem::size_of::<SdtHeader>()) as *const u64;
|
|
|
-
|
|
|
- for i in 0..num_tables {
|
|
|
- sdt::dispatch_sdt(&mut acpi, handler, unsafe { *tables_base.offset(i as isize) } as usize)?;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- handler.unmap_physical_region(mapping);
|
|
|
- Ok(acpi)
|
|
|
+#[derive(Clone, Copy, Debug)]
|
|
|
+#[repr(C, packed)]
|
|
|
+pub(crate) struct GenericAddress {
|
|
|
+ address_space: u8,
|
|
|
+ bit_width: u8,
|
|
|
+ bit_offset: u8,
|
|
|
+ access_size: u8,
|
|
|
+ address: u64,
|
|
|
}
|