lib.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  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. pub mod handler;
  44. mod hpet;
  45. mod madt;
  46. mod mcfg;
  47. pub mod platform;
  48. mod rsdp;
  49. mod sdt;
  50. pub use crate::{
  51. fadt::PowerProfile,
  52. handler::{AcpiHandler, PhysicalMapping},
  53. hpet::HpetInfo,
  54. madt::MadtError,
  55. mcfg::PciConfigRegions,
  56. platform::{InterruptModel, PlatformInfo},
  57. };
  58. use crate::{
  59. rsdp::Rsdp,
  60. sdt::{SdtHeader, Signature},
  61. };
  62. use alloc::{collections::BTreeMap, vec::Vec};
  63. use core::mem;
  64. use log::trace;
  65. #[derive(Debug)]
  66. pub enum AcpiError {
  67. RsdpIncorrectSignature,
  68. RsdpInvalidOemId,
  69. RsdpInvalidChecksum,
  70. NoValidRsdp,
  71. SdtInvalidSignature(Signature),
  72. SdtInvalidOemId(Signature),
  73. SdtInvalidTableId(Signature),
  74. SdtInvalidChecksum(Signature),
  75. TableMissing(Signature),
  76. InvalidDsdtAddress,
  77. InvalidMadt(MadtError),
  78. }
  79. #[derive(Clone, Copy, Debug)]
  80. #[repr(C, packed)]
  81. pub(crate) struct GenericAddress {
  82. address_space: u8,
  83. bit_width: u8,
  84. bit_offset: u8,
  85. access_size: u8,
  86. address: u64,
  87. }
  88. pub struct AcpiTables<H>
  89. where
  90. H: AcpiHandler,
  91. {
  92. /// The revision of ACPI that the system uses, as inferred from the revision of the RSDT/XSDT.
  93. pub revision: u8,
  94. pub sdts: BTreeMap<sdt::Signature, Sdt>,
  95. pub dsdt: Option<AmlTable>,
  96. pub ssdts: Vec<AmlTable>,
  97. handler: H,
  98. }
  99. impl<H> AcpiTables<H>
  100. where
  101. H: AcpiHandler,
  102. {
  103. /// Create an `AcpiTables` if you have the physical address of the RSDP.
  104. pub unsafe fn from_rsdp(handler: H, rsdp_address: usize) -> Result<AcpiTables<H>, AcpiError> {
  105. let rsdp_mapping = unsafe { handler.map_physical_region::<Rsdp>(rsdp_address, mem::size_of::<Rsdp>()) };
  106. rsdp_mapping.validate()?;
  107. Self::from_validated_rsdp(handler, rsdp_mapping)
  108. }
  109. /// Create an `AcpiTables` if you have a `PhysicalMapping` of the RSDP that you know is correct. This is called
  110. /// from `from_rsdp` after validation, and also from the RSDP search routines since they need to validate the
  111. /// RSDP anyways.
  112. fn from_validated_rsdp(
  113. handler: H,
  114. rsdp_mapping: PhysicalMapping<H, Rsdp>,
  115. ) -> Result<AcpiTables<H>, AcpiError> {
  116. let revision = rsdp_mapping.revision();
  117. if revision == 0 {
  118. /*
  119. * We're running on ACPI Version 1.0. We should use the 32-bit RSDT address.
  120. */
  121. let rsdt_address = rsdp_mapping.rsdt_address();
  122. unsafe { Self::from_rsdt(handler, revision, rsdt_address as usize) }
  123. } else {
  124. /*
  125. * We're running on ACPI Version 2.0+. We should use the 64-bit XSDT address, truncated
  126. * to 32 bits on x86.
  127. */
  128. let xsdt_address = rsdp_mapping.xsdt_address();
  129. unsafe { Self::from_rsdt(handler, revision, xsdt_address as usize) }
  130. }
  131. }
  132. /// Create an `AcpiTables` if you have the physical address of the RSDT. This is useful, for example, if your chosen
  133. /// bootloader reads the RSDP and passes you the address of the RSDT. You also need to supply the correct ACPI
  134. /// revision - if `0`, a RSDT is expected, while a `XSDT` is expected for greater revisions.
  135. pub unsafe fn from_rsdt(handler: H, revision: u8, rsdt_address: usize) -> Result<AcpiTables<H>, AcpiError> {
  136. let mut result = AcpiTables { revision, sdts: BTreeMap::new(), dsdt: None, ssdts: Vec::new(), handler };
  137. let header = sdt::peek_at_sdt_header(&result.handler, rsdt_address);
  138. let mapping =
  139. unsafe { result.handler.map_physical_region::<SdtHeader>(rsdt_address, header.length as usize) };
  140. if revision == 0 {
  141. /*
  142. * ACPI Version 1.0. It's a RSDT!
  143. */
  144. mapping.validate(sdt::Signature::RSDT)?;
  145. let num_tables = (mapping.length as usize - mem::size_of::<SdtHeader>()) / mem::size_of::<u32>();
  146. let tables_base =
  147. ((mapping.virtual_start.as_ptr() as usize) + mem::size_of::<SdtHeader>()) as *const u32;
  148. for i in 0..num_tables {
  149. result.process_sdt(unsafe { *tables_base.offset(i as isize) as usize })?;
  150. }
  151. } else {
  152. /*
  153. * ACPI Version 2.0+. It's a XSDT!
  154. */
  155. mapping.validate(sdt::Signature::XSDT)?;
  156. let num_tables = (mapping.length as usize - mem::size_of::<SdtHeader>()) / mem::size_of::<u64>();
  157. let tables_base =
  158. ((mapping.virtual_start.as_ptr() as usize) + mem::size_of::<SdtHeader>()) as *const u64;
  159. for i in 0..num_tables {
  160. result.process_sdt(unsafe { *tables_base.offset(i as isize) as usize })?;
  161. }
  162. }
  163. Ok(result)
  164. }
  165. /// Construct an `AcpiTables` from a custom set of "discovered" tables. This is provided to allow the library
  166. /// to be used from unconventional settings (e.g. in userspace), for example with a `AcpiHandler` that detects
  167. /// accesses to specific physical addresses, and provides the correct data.
  168. pub fn from_tables_direct(
  169. handler: H,
  170. revision: u8,
  171. sdts: BTreeMap<sdt::Signature, Sdt>,
  172. dsdt: Option<AmlTable>,
  173. ssdts: Vec<AmlTable>,
  174. ) -> AcpiTables<H> {
  175. AcpiTables { revision, sdts, dsdt, ssdts, handler }
  176. }
  177. fn process_sdt(&mut self, physical_address: usize) -> Result<(), AcpiError> {
  178. let header = sdt::peek_at_sdt_header(&self.handler, physical_address);
  179. trace!("Found ACPI table with signature {:?} and length {:?}", header.signature, header.length);
  180. match header.signature {
  181. Signature::FADT => {
  182. use fadt::Fadt;
  183. /*
  184. * For whatever reason, they chose to put the DSDT inside the FADT, instead of just listing it
  185. * as another SDT. We extract it here to provide a nicer public API.
  186. */
  187. let fadt_mapping =
  188. unsafe { self.handler.map_physical_region::<Fadt>(physical_address, mem::size_of::<Fadt>()) };
  189. fadt_mapping.validate()?;
  190. let dsdt_address = fadt_mapping.dsdt_address()?;
  191. let dsdt_header = sdt::peek_at_sdt_header(&self.handler, dsdt_address);
  192. self.dsdt = Some(AmlTable::new(dsdt_address, dsdt_header.length));
  193. /*
  194. * We've already validated the FADT to get the DSDT out, so it doesn't need to be done again.
  195. */
  196. self.sdts
  197. .insert(Signature::FADT, Sdt { physical_address, length: header.length, validated: true });
  198. }
  199. Signature::SSDT => {
  200. self.ssdts.push(AmlTable::new(physical_address, header.length));
  201. }
  202. signature => {
  203. self.sdts.insert(signature, Sdt { physical_address, length: header.length, validated: false });
  204. }
  205. }
  206. Ok(())
  207. }
  208. /// Create a mapping to a SDT, given its signature. This validates the SDT if it has not already been
  209. /// validated.
  210. ///
  211. /// ### Safety
  212. /// The table's memory is naively interpreted as a `T`, and so you must be careful in providing a type that
  213. /// correctly represents the table's structure. Regardless of the provided type's size, the region mapped will
  214. /// be the size specified in the SDT's header. Providing a `T` that is larger than this, *may* lead to
  215. /// page-faults, aliasing references, or derefencing uninitialized memory (the latter two of which are UB).
  216. /// This isn't forbidden, however, because some tables rely on `T` being larger than a provided SDT in some
  217. /// versions of ACPI (the [`ExtendedField`](crate::sdt::ExtendedField) type will be useful if you need to do
  218. /// this. See our [`Fadt`](crate::fadt::Fadt) type for an example of this).
  219. pub unsafe fn get_sdt<T>(&self, signature: sdt::Signature) -> Result<Option<PhysicalMapping<H, T>>, AcpiError>
  220. where
  221. T: AcpiTable,
  222. {
  223. let sdt = match self.sdts.get(&signature) {
  224. Some(sdt) => sdt,
  225. None => return Ok(None),
  226. };
  227. let mapping = unsafe { self.handler.map_physical_region::<T>(sdt.physical_address, sdt.length as usize) };
  228. if !sdt.validated {
  229. mapping.header().validate(signature)?;
  230. }
  231. Ok(Some(mapping))
  232. }
  233. /// Convenience method for contructing a [`PlatformInfo`](crate::platform::PlatformInfo). This is one of the
  234. /// first things you should usually do with an `AcpiTables`, and allows to collect helpful information about
  235. /// the platform from the ACPI tables.
  236. pub fn platform_info(&self) -> Result<PlatformInfo, AcpiError> {
  237. PlatformInfo::new(self)
  238. }
  239. }
  240. pub struct Sdt {
  241. /// Physical address of the start of the SDT, including the header.
  242. pub physical_address: usize,
  243. /// Length of the table in bytes.
  244. pub length: u32,
  245. /// Whether this SDT has been validated. This is set to `true` the first time it is mapped and validated.
  246. pub validated: bool,
  247. }
  248. /// All types representing ACPI tables should implement this trait.
  249. pub trait AcpiTable {
  250. fn header(&self) -> &sdt::SdtHeader;
  251. }
  252. #[derive(Debug)]
  253. pub struct AmlTable {
  254. /// Physical address of the start of the AML stream (excluding the table header).
  255. pub address: usize,
  256. /// Length (in bytes) of the AML stream.
  257. pub length: u32,
  258. }
  259. impl AmlTable {
  260. /// Create an `AmlTable` from the address and length of the table **including the SDT header**.
  261. pub(crate) fn new(address: usize, length: u32) -> AmlTable {
  262. AmlTable {
  263. address: address + mem::size_of::<SdtHeader>(),
  264. length: length - mem::size_of::<SdtHeader>() as u32,
  265. }
  266. }
  267. }