lib.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. //! A library for parsing ACPI tables. This crate can be used by bootloaders and kernels for
  2. //! architectures that support ACPI. This crate is not feature-complete, but can parse lots of the more common
  3. //! tables. Parsing the ACPI tables is required for correctly setting up the APICs, HPET, and provides useful
  4. //! information about power 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. //! ### Usage
  11. //! To use the library, you will need to provide an implementation of the `AcpiHandler` trait, which allows the
  12. //! library to make requests such as mapping a particular region of physical memory into the virtual address space.
  13. //!
  14. //! You then need to construct an instance of `AcpiTables`, which can be done in a few ways depending on how much
  15. //! information you have:
  16. //! * Use `AcpiTables::from_rsdp` if you have the physical address of the RSDP
  17. //! * Use `AcpiTables::from_rsdt` if you have the physical address of the RSDT/XSDT
  18. //! * Use `AcpiTables::search_for_rsdp_bios` if you don't have the address of either, but **you know you are
  19. //! running on BIOS, not UEFI**
  20. //! * Use `AcpiTables::from_tables_direct` if you are using the library in an unusual setting, such as in usermode,
  21. //! and have a custom method to enumerate and access the tables.
  22. //!
  23. //! `AcpiTables` stores the addresses of all of the tables detected on a platform. The SDTs are parsed by this
  24. //! library, or can be accessed directly with `from_sdt`, while the `DSDT` and any `SSDTs` should be parsed with
  25. //! `aml`.
  26. //!
  27. //! To gather information out of the static tables, a few of the types you should take a look at are:
  28. //! - [`PlatformInfo`](crate::platform::PlatformInfo) parses the FADT and MADT to create a nice view of the
  29. //! processor topology and interrupt controllers on `x86_64`, and the interrupt controllers on other platforms.
  30. //! `AcpiTables::platform_info` is a convenience method for constructing a `PlatformInfo`.
  31. //! - [`HpetInfo`](crate::hpet::HpetInfo) parses the HPET table and tells you how to configure the High
  32. //! Precision Event Timer.
  33. //! - [`PciConfigRegions`](crate::mcfg::PciConfigRegions) parses the MCFG and tells you how PCIe configuration
  34. //! space is mapped into physical memory.
  35. #![no_std]
  36. #![feature(const_generics, unsafe_block_in_unsafe_fn)]
  37. #![deny(unsafe_op_in_unsafe_fn)]
  38. extern crate alloc;
  39. #[cfg_attr(test, macro_use)]
  40. #[cfg(test)]
  41. extern crate std;
  42. mod fadt;
  43. mod hpet;
  44. mod madt;
  45. mod mcfg;
  46. pub mod platform;
  47. mod sdt;
  48. pub use crate::{
  49. fadt::PowerProfile,
  50. hpet::HpetInfo,
  51. madt::MadtError,
  52. mcfg::PciConfigRegions,
  53. platform::{InterruptModel, PlatformInfo},
  54. };
  55. pub use rsdp::{
  56. handler::{AcpiHandler, PhysicalMapping},
  57. RsdpError,
  58. };
  59. use crate::sdt::{SdtHeader, Signature};
  60. use alloc::{collections::BTreeMap, vec::Vec};
  61. use core::mem;
  62. use log::trace;
  63. use rsdp::Rsdp;
  64. #[derive(Debug)]
  65. pub enum AcpiError {
  66. Rsdp(RsdpError),
  67. SdtInvalidSignature(Signature),
  68. SdtInvalidOemId(Signature),
  69. SdtInvalidTableId(Signature),
  70. SdtInvalidChecksum(Signature),
  71. TableMissing(Signature),
  72. InvalidDsdtAddress,
  73. InvalidMadt(MadtError),
  74. }
  75. pub struct AcpiTables<H>
  76. where
  77. H: AcpiHandler,
  78. {
  79. /// The revision of ACPI that the system uses, as inferred from the revision of the RSDT/XSDT.
  80. pub revision: u8,
  81. pub sdts: BTreeMap<sdt::Signature, Sdt>,
  82. pub dsdt: Option<AmlTable>,
  83. pub ssdts: Vec<AmlTable>,
  84. handler: H,
  85. }
  86. impl<H> AcpiTables<H>
  87. where
  88. H: AcpiHandler,
  89. {
  90. /// Create an `AcpiTables` if you have the physical address of the RSDP.
  91. pub unsafe fn from_rsdp(handler: H, rsdp_address: usize) -> Result<AcpiTables<H>, AcpiError> {
  92. let rsdp_mapping = unsafe { handler.map_physical_region::<Rsdp>(rsdp_address, mem::size_of::<Rsdp>()) };
  93. rsdp_mapping.validate().map_err(|err| AcpiError::Rsdp(err))?;
  94. Self::from_validated_rsdp(handler, rsdp_mapping)
  95. }
  96. /// Create an `AcpiTables` if you have a `PhysicalMapping` of the RSDP that you know is correct. This is called
  97. /// from `from_rsdp` after validation, but can also be used if you've searched for the RSDP manually on a BIOS
  98. /// system.
  99. pub fn from_validated_rsdp(
  100. handler: H,
  101. rsdp_mapping: PhysicalMapping<H, Rsdp>,
  102. ) -> Result<AcpiTables<H>, AcpiError> {
  103. let revision = rsdp_mapping.revision();
  104. if revision == 0 {
  105. /*
  106. * We're running on ACPI Version 1.0. We should use the 32-bit RSDT address.
  107. */
  108. let rsdt_address = rsdp_mapping.rsdt_address();
  109. unsafe { Self::from_rsdt(handler, revision, rsdt_address as usize) }
  110. } else {
  111. /*
  112. * We're running on ACPI Version 2.0+. We should use the 64-bit XSDT address, truncated
  113. * to 32 bits on x86.
  114. */
  115. let xsdt_address = rsdp_mapping.xsdt_address();
  116. unsafe { Self::from_rsdt(handler, revision, xsdt_address as usize) }
  117. }
  118. }
  119. /// Create an `AcpiTables` if you have the physical address of the RSDT. This is useful, for example, if your chosen
  120. /// bootloader reads the RSDP and passes you the address of the RSDT. You also need to supply the correct ACPI
  121. /// revision - if `0`, a RSDT is expected, while a `XSDT` is expected for greater revisions.
  122. pub unsafe fn from_rsdt(handler: H, revision: u8, rsdt_address: usize) -> Result<AcpiTables<H>, AcpiError> {
  123. let mut result = AcpiTables { revision, sdts: BTreeMap::new(), dsdt: None, ssdts: Vec::new(), handler };
  124. let header = sdt::peek_at_sdt_header(&result.handler, rsdt_address);
  125. let mapping =
  126. unsafe { result.handler.map_physical_region::<SdtHeader>(rsdt_address, header.length as usize) };
  127. if revision == 0 {
  128. /*
  129. * ACPI Version 1.0. It's a RSDT!
  130. */
  131. mapping.validate(sdt::Signature::RSDT)?;
  132. let num_tables = (mapping.length as usize - mem::size_of::<SdtHeader>()) / mem::size_of::<u32>();
  133. let tables_base =
  134. ((mapping.virtual_start.as_ptr() as usize) + mem::size_of::<SdtHeader>()) as *const u32;
  135. for i in 0..num_tables {
  136. result.process_sdt(unsafe { *tables_base.offset(i as isize) as usize })?;
  137. }
  138. } else {
  139. /*
  140. * ACPI Version 2.0+. It's a XSDT!
  141. */
  142. mapping.validate(sdt::Signature::XSDT)?;
  143. let num_tables = (mapping.length as usize - mem::size_of::<SdtHeader>()) / mem::size_of::<u64>();
  144. let tables_base =
  145. ((mapping.virtual_start.as_ptr() as usize) + mem::size_of::<SdtHeader>()) as *const u64;
  146. for i in 0..num_tables {
  147. result.process_sdt(unsafe { *tables_base.offset(i as isize) as usize })?;
  148. }
  149. }
  150. Ok(result)
  151. }
  152. /// Construct an `AcpiTables` from a custom set of "discovered" tables. This is provided to allow the library
  153. /// to be used from unconventional settings (e.g. in userspace), for example with a `AcpiHandler` that detects
  154. /// accesses to specific physical addresses, and provides the correct data.
  155. pub fn from_tables_direct(
  156. handler: H,
  157. revision: u8,
  158. sdts: BTreeMap<sdt::Signature, Sdt>,
  159. dsdt: Option<AmlTable>,
  160. ssdts: Vec<AmlTable>,
  161. ) -> AcpiTables<H> {
  162. AcpiTables { revision, sdts, dsdt, ssdts, handler }
  163. }
  164. fn process_sdt(&mut self, physical_address: usize) -> Result<(), AcpiError> {
  165. let header = sdt::peek_at_sdt_header(&self.handler, physical_address);
  166. trace!("Found ACPI table with signature {:?} and length {:?}", header.signature, header.length);
  167. match header.signature {
  168. Signature::FADT => {
  169. use fadt::Fadt;
  170. /*
  171. * For whatever reason, they chose to put the DSDT inside the FADT, instead of just listing it
  172. * as another SDT. We extract it here to provide a nicer public API.
  173. */
  174. let fadt_mapping =
  175. unsafe { self.handler.map_physical_region::<Fadt>(physical_address, mem::size_of::<Fadt>()) };
  176. fadt_mapping.validate()?;
  177. let dsdt_address = fadt_mapping.dsdt_address()?;
  178. let dsdt_header = sdt::peek_at_sdt_header(&self.handler, dsdt_address);
  179. self.dsdt = Some(AmlTable::new(dsdt_address, dsdt_header.length));
  180. /*
  181. * We've already validated the FADT to get the DSDT out, so it doesn't need to be done again.
  182. */
  183. self.sdts
  184. .insert(Signature::FADT, Sdt { physical_address, length: header.length, validated: true });
  185. }
  186. Signature::SSDT => {
  187. self.ssdts.push(AmlTable::new(physical_address, header.length));
  188. }
  189. signature => {
  190. self.sdts.insert(signature, Sdt { physical_address, length: header.length, validated: false });
  191. }
  192. }
  193. Ok(())
  194. }
  195. /// Create a mapping to a SDT, given its signature. This validates the SDT if it has not already been
  196. /// validated.
  197. ///
  198. /// ### Safety
  199. /// The table's memory is naively interpreted as a `T`, and so you must be careful in providing a type that
  200. /// correctly represents the table's structure. Regardless of the provided type's size, the region mapped will
  201. /// be the size specified in the SDT's header. Providing a `T` that is larger than this, *may* lead to
  202. /// page-faults, aliasing references, or derefencing uninitialized memory (the latter two of which are UB).
  203. /// This isn't forbidden, however, because some tables rely on `T` being larger than a provided SDT in some
  204. /// versions of ACPI (the [`ExtendedField`](crate::sdt::ExtendedField) type will be useful if you need to do
  205. /// this. See our [`Fadt`](crate::fadt::Fadt) type for an example of this).
  206. pub unsafe fn get_sdt<T>(&self, signature: sdt::Signature) -> Result<Option<PhysicalMapping<H, T>>, AcpiError>
  207. where
  208. T: AcpiTable,
  209. {
  210. let sdt = match self.sdts.get(&signature) {
  211. Some(sdt) => sdt,
  212. None => return Ok(None),
  213. };
  214. let mapping = unsafe { self.handler.map_physical_region::<T>(sdt.physical_address, sdt.length as usize) };
  215. if !sdt.validated {
  216. mapping.header().validate(signature)?;
  217. }
  218. Ok(Some(mapping))
  219. }
  220. /// Convenience method for contructing a [`PlatformInfo`](crate::platform::PlatformInfo). This is one of the
  221. /// first things you should usually do with an `AcpiTables`, and allows to collect helpful information about
  222. /// the platform from the ACPI tables.
  223. pub fn platform_info(&self) -> Result<PlatformInfo, AcpiError> {
  224. PlatformInfo::new(self)
  225. }
  226. }
  227. pub struct Sdt {
  228. /// Physical address of the start of the SDT, including the header.
  229. pub physical_address: usize,
  230. /// Length of the table in bytes.
  231. pub length: u32,
  232. /// Whether this SDT has been validated. This is set to `true` the first time it is mapped and validated.
  233. pub validated: bool,
  234. }
  235. /// All types representing ACPI tables should implement this trait.
  236. pub trait AcpiTable {
  237. fn header(&self) -> &sdt::SdtHeader;
  238. }
  239. #[derive(Debug)]
  240. pub struct AmlTable {
  241. /// Physical address of the start of the AML stream (excluding the table header).
  242. pub address: usize,
  243. /// Length (in bytes) of the AML stream.
  244. pub length: u32,
  245. }
  246. impl AmlTable {
  247. /// Create an `AmlTable` from the address and length of the table **including the SDT header**.
  248. pub(crate) fn new(address: usize, length: u32) -> AmlTable {
  249. AmlTable {
  250. address: address + mem::size_of::<SdtHeader>(),
  251. length: length - mem::size_of::<SdtHeader>() as u32,
  252. }
  253. }
  254. }
  255. #[derive(Clone, Copy, Debug)]
  256. #[repr(C, packed)]
  257. pub(crate) struct GenericAddress {
  258. address_space: u8,
  259. bit_width: u8,
  260. bit_offset: u8,
  261. access_size: u8,
  262. address: u64,
  263. }