lib.rs 13 KB

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