lib.rs 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. //! A library for parsing ACPI tables. This crate can be used by bootloaders and kernels for
  2. //! architectures that support ACPI. The crate is far from feature-complete, but can still be used
  3. //! for finding and parsing the static tables, which is enough to set up hardware such as the APIC
  4. //! and HPET on x86_64.
  5. //!
  6. //! The crate is designed for use in conjunction with the `aml` crate, which is the (much
  7. //! less complete) AML parser used to parse the DSDT and SSDTs. These crates are separate because
  8. //! 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,
  12. //! which allows the library to make requests such as mapping a particular region of physical
  13. //! memory into the virtual address space.
  14. //!
  15. //! You should then call one of the entry points, based on how much information you have:
  16. //! * Call `parse_rsdp` if you have the physical address of the RSDP
  17. //! * Call `parse_rsdt` if you have the physical address of the RSDT / XSDT
  18. //! * Call `search_for_rsdp_bios` if you don't have the address of either structure, but **you know
  19. //! you're running on BIOS, not UEFI**
  20. //!
  21. //! All of these methods return an instance of `Acpi`. This struct contains all the information
  22. //! gathered from the static tables, and can be queried to set up hardware etc.
  23. #![no_std]
  24. #![feature(const_generics, unsafe_block_in_unsafe_fn)]
  25. extern crate alloc;
  26. #[cfg_attr(test, macro_use)]
  27. #[cfg(test)]
  28. extern crate std;
  29. mod fadt;
  30. pub mod handler;
  31. mod hpet;
  32. pub mod interrupt;
  33. mod madt;
  34. mod mcfg;
  35. mod rsdp;
  36. mod rsdp_search;
  37. mod sdt;
  38. pub use crate::{
  39. fadt::PowerProfile,
  40. handler::{AcpiHandler, PhysicalMapping},
  41. hpet::HpetInfo,
  42. interrupt::InterruptModel,
  43. madt::MadtError,
  44. mcfg::PciConfigRegions,
  45. rsdp_search::search_for_rsdp_bios,
  46. };
  47. use crate::{
  48. rsdp::Rsdp,
  49. sdt::{SdtHeader, Signature},
  50. };
  51. use alloc::{collections::BTreeMap, vec::Vec};
  52. use core::{marker::PhantomData, mem};
  53. use log::trace;
  54. #[derive(Debug)]
  55. pub enum AcpiError {
  56. RsdpIncorrectSignature,
  57. RsdpInvalidOemId,
  58. RsdpInvalidChecksum,
  59. NoValidRsdp,
  60. SdtInvalidSignature(Signature),
  61. SdtInvalidOemId(Signature),
  62. SdtInvalidTableId(Signature),
  63. SdtInvalidChecksum(Signature),
  64. InvalidDsdtAddress,
  65. InvalidMadt(MadtError),
  66. }
  67. #[derive(Clone, Copy, Debug)]
  68. #[repr(C, packed)]
  69. pub(crate) struct GenericAddress {
  70. address_space: u8,
  71. bit_width: u8,
  72. bit_offset: u8,
  73. access_size: u8,
  74. address: u64,
  75. }
  76. pub struct AcpiTables<H>
  77. where
  78. H: AcpiHandler,
  79. {
  80. /// The revision of ACPI that the system uses, as inferred from the revision of the RSDT/XSDT.
  81. pub revision: u8,
  82. pub sdts: BTreeMap<sdt::Signature, Sdt>,
  83. pub dsdt: Option<AmlTable>,
  84. pub ssdts: Vec<AmlTable>,
  85. _phantom: PhantomData<H>,
  86. }
  87. impl<H> AcpiTables<H>
  88. where
  89. H: AcpiHandler,
  90. {
  91. /// Create an `AcpiTables` if you have the physical address of the RSDP.
  92. pub unsafe fn from_rsdp(handler: &mut H, rsdp_address: usize) -> Result<AcpiTables<H>, AcpiError> {
  93. let rsdp_mapping = unsafe { handler.map_physical_region::<Rsdp>(rsdp_address, mem::size_of::<Rsdp>()) };
  94. rsdp_mapping.validate()?;
  95. Self::from_validated_rsdp(handler, rsdp_mapping)
  96. }
  97. /// Create an `AcpiTables` if you have a `PhysicalMapping` of the RSDP that you know is correct. This is called
  98. /// from `from_rsdp` after validation, and also from the RSDP search routines since they need to validate the
  99. /// RSDP anyways.
  100. fn from_validated_rsdp(
  101. handler: &mut H,
  102. rsdp_mapping: PhysicalMapping<H, Rsdp>,
  103. ) -> Result<AcpiTables<H>, AcpiError> {
  104. let revision = rsdp_mapping.revision();
  105. if revision == 0 {
  106. /*
  107. * We're running on ACPI Version 1.0. We should use the 32-bit RSDT address.
  108. */
  109. let rsdt_address = rsdp_mapping.rsdt_address();
  110. unsafe { Self::from_rsdt(handler, revision, rsdt_address as usize) }
  111. } else {
  112. /*
  113. * We're running on ACPI Version 2.0+. We should use the 64-bit XSDT address, truncated
  114. * to 32 bits on x86.
  115. */
  116. let xsdt_address = rsdp_mapping.xsdt_address();
  117. unsafe { Self::from_rsdt(handler, revision, xsdt_address as usize) }
  118. }
  119. }
  120. /// Create an `AcpiTables` if you have the physical address of the RSDT. This is useful, for example, if your chosen
  121. /// bootloader reads the RSDP and passes you the address of the RSDT. You also need to supply the correct ACPI
  122. /// revision - if `0`, a RSDT is expected, while a `XSDT` is expected for greater revisions.
  123. pub unsafe fn from_rsdt(
  124. handler: &mut H,
  125. revision: u8,
  126. rsdt_address: usize,
  127. ) -> Result<AcpiTables<H>, AcpiError> {
  128. let mut result =
  129. AcpiTables { revision, sdts: BTreeMap::new(), dsdt: None, ssdts: Vec::new(), _phantom: PhantomData };
  130. let header = sdt::peek_at_sdt_header(handler, rsdt_address);
  131. let mapping = unsafe { handler.map_physical_region::<SdtHeader>(rsdt_address, header.length as usize) };
  132. if revision == 0 {
  133. /*
  134. * ACPI Version 1.0. It's a RSDT!
  135. */
  136. mapping.validate(sdt::Signature::RSDT)?;
  137. let num_tables = (mapping.length as usize - mem::size_of::<SdtHeader>()) / mem::size_of::<u32>();
  138. let tables_base =
  139. ((mapping.virtual_start.as_ptr() as usize) + mem::size_of::<SdtHeader>()) as *const u32;
  140. for i in 0..num_tables {
  141. result.process_sdt(handler, unsafe { *tables_base.offset(i as isize) as usize })?;
  142. }
  143. } else {
  144. /*
  145. * ACPI Version 2.0+. It's a XSDT!
  146. */
  147. mapping.validate(sdt::Signature::XSDT)?;
  148. let num_tables = (mapping.length as usize - mem::size_of::<SdtHeader>()) / mem::size_of::<u64>();
  149. let tables_base =
  150. ((mapping.virtual_start.as_ptr() as usize) + mem::size_of::<SdtHeader>()) as *const u64;
  151. for i in 0..num_tables {
  152. result.process_sdt(handler, unsafe { *tables_base.offset(i as isize) as usize })?;
  153. }
  154. }
  155. Ok(result)
  156. }
  157. fn process_sdt(&mut self, handler: &mut H, physical_address: usize) -> Result<(), AcpiError> {
  158. let header = sdt::peek_at_sdt_header(handler, physical_address);
  159. trace!("Found ACPI table with signature {:?} and length {:?}", header.signature, header.length);
  160. match header.signature {
  161. Signature::FADT => {
  162. use fadt::Fadt;
  163. /*
  164. * For whatever reason, they chose to put the DSDT inside the FADT, instead of just listing it
  165. * as another SDT. We extract it here to provide a nicer public API.
  166. */
  167. let fadt_mapping =
  168. unsafe { handler.map_physical_region::<Fadt>(physical_address, mem::size_of::<Fadt>()) };
  169. fadt_mapping.validate()?;
  170. let dsdt_address = fadt_mapping.dsdt_address()?;
  171. let dsdt_header = sdt::peek_at_sdt_header(handler, dsdt_address);
  172. self.dsdt = Some(AmlTable::new(dsdt_address, dsdt_header.length));
  173. /*
  174. * We've already validated the FADT to get the DSDT out, so it doesn't need to be done again.
  175. */
  176. self.sdts
  177. .insert(Signature::FADT, Sdt { physical_address, length: header.length, validated: true });
  178. }
  179. Signature::SSDT => {
  180. self.ssdts.push(AmlTable::new(physical_address, header.length));
  181. }
  182. signature => {
  183. self.sdts.insert(signature, Sdt { physical_address, length: header.length, validated: false });
  184. }
  185. }
  186. Ok(())
  187. }
  188. pub unsafe fn get_sdt<T>(
  189. &self,
  190. handler: &mut H,
  191. signature: sdt::Signature,
  192. ) -> Result<Option<PhysicalMapping<H, T>>, AcpiError> {
  193. let sdt = match self.sdts.get(&signature) {
  194. Some(sdt) => sdt,
  195. None => return Ok(None),
  196. };
  197. let mapping =
  198. unsafe { handler.map_physical_region::<SdtHeader>(sdt.physical_address, sdt.length as usize) };
  199. if !sdt.validated {
  200. mapping.validate(signature)?;
  201. }
  202. Ok(Some(mapping.coerce_type()))
  203. }
  204. }
  205. pub struct Sdt {
  206. /// Physical address of the start of the SDT, including the header.
  207. pub physical_address: usize,
  208. /// Length of the table in bytes.
  209. pub length: u32,
  210. /// Whether this SDT has been validated. This is set to `true` the first time it is mapped and validated.
  211. pub validated: bool,
  212. }
  213. #[derive(Debug)]
  214. pub struct AmlTable {
  215. /// Physical address of the start of the AML stream (excluding the table header).
  216. pub address: usize,
  217. /// Length (in bytes) of the AML stream.
  218. pub length: u32,
  219. }
  220. impl AmlTable {
  221. /// Create an `AmlTable` from the address and length of the table **including the SDT header**.
  222. pub(crate) fn new(address: usize, length: u32) -> AmlTable {
  223. AmlTable {
  224. address: address + mem::size_of::<SdtHeader>(),
  225. length: length - mem::size_of::<SdtHeader>() as u32,
  226. }
  227. }
  228. }