lib.rs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. //! This crate provides types for representing the RSDP (the Root System Descriptor Table; the first ACPI table)
  2. //! and methods for searching for it on BIOS systems. Importantly, this crate (unlike `acpi`, which re-exports the
  3. //! contents of this crate) does not need `alloc`, and so can be used in environments that can't allocate. This is
  4. //! specifically meant to be used from bootloaders for finding the RSDP, so it can be passed to the payload.
  5. //!
  6. //! To use this crate, you will need to provide an implementation of `AcpiHandler`. This is the same handler type
  7. //! used in the `acpi` crate.
  8. #![no_std]
  9. #![feature(unsafe_block_in_unsafe_fn)]
  10. #![deny(unsafe_op_in_unsafe_fn)]
  11. pub mod handler;
  12. use core::{mem, ops::Range, slice, str};
  13. use handler::{AcpiHandler, PhysicalMapping};
  14. use log::warn;
  15. #[derive(Clone, Copy, PartialEq, Eq, Debug)]
  16. pub enum RsdpError {
  17. NoValidRsdp,
  18. IncorrectSignature,
  19. InvalidOemId,
  20. InvalidChecksum,
  21. }
  22. /// The first structure found in ACPI. It just tells us where the RSDT is.
  23. ///
  24. /// On BIOS systems, it is either found in the first 1KB of the Extended Bios Data Area, or between
  25. /// 0x000E0000 and 0x000FFFFF. The signature is always on a 16 byte boundary. On (U)EFI, it may not
  26. /// be located in these locations, and so an address should be found in the EFI_SYSTEM_TABLE
  27. /// instead.
  28. ///
  29. /// The recommended way of locating the RSDP is to let the bootloader do it - Multiboot2 can pass a
  30. /// tag with the physical address of it. If this is not possible, a manual scan can be done.
  31. ///
  32. /// If `revision > 0`, (the hardware ACPI version is Version 2.0 or greater), the RSDP contains
  33. /// some new fields. For ACPI Version 1.0, these fields are not valid and should not be accessed.
  34. /// For ACPI Version 2.0+, `xsdt_address` should be used (truncated to `u32` on x86) instead of
  35. /// `rsdt_address`.
  36. #[derive(Clone, Copy, Debug)]
  37. #[repr(C, packed)]
  38. pub struct Rsdp {
  39. signature: [u8; 8],
  40. checksum: u8,
  41. oem_id: [u8; 6],
  42. revision: u8,
  43. rsdt_address: u32,
  44. /*
  45. * These fields are only valid for ACPI Version 2.0 and greater
  46. */
  47. length: u32,
  48. xsdt_address: u64,
  49. ext_checksum: u8,
  50. reserved: [u8; 3],
  51. }
  52. impl Rsdp {
  53. /// This searches for a RSDP on BIOS systems.
  54. ///
  55. /// ### Safety
  56. /// This function probes memory in three locations:
  57. /// - It reads a word from `40:0e` to locate the EBDA.
  58. /// - The first 1KiB of the EBDA (Extended BIOS Data Area).
  59. /// - The BIOS memory area at `0xe0000..=0xfffff`.
  60. ///
  61. /// This should be fine on all BIOS systems. However, UEFI platforms are free to put the RSDP wherever they
  62. /// please, so this won't always find the RSDP. Further, prodding these memory locations may have unintended
  63. /// side-effects. On UEFI systems, the RSDP should be found in the Configuration Table, using two GUIDs:
  64. /// - ACPI v1.0 structures use `eb9d2d30-2d88-11d3-9a16-0090273fc14d`.
  65. /// - ACPI v2.0 or later structures use `8868e871-e4f1-11d3-bc22-0080c73c8881`.
  66. pub unsafe fn search_for_on_bios<H>(handler: H) -> Result<PhysicalMapping<H, Rsdp>, RsdpError>
  67. where
  68. H: AcpiHandler,
  69. {
  70. let rsdp_address = {
  71. let mut rsdp_address = None;
  72. let areas = find_search_areas(handler.clone());
  73. 'areas: for area in areas.iter() {
  74. let mapping = unsafe { handler.map_physical_region::<u8>(area.start, area.end - area.start) };
  75. for address in area.clone().step_by(16) {
  76. let ptr_in_mapping =
  77. unsafe { mapping.virtual_start().as_ptr().offset((address - area.start) as isize) };
  78. let signature = unsafe { *(ptr_in_mapping as *const [u8; 8]) };
  79. if signature == *RSDP_SIGNATURE {
  80. match unsafe { *(ptr_in_mapping as *const Rsdp) }.validate() {
  81. Ok(()) => {
  82. rsdp_address = Some(address);
  83. break 'areas;
  84. }
  85. Err(err) => warn!("Invalid RSDP found at {:#x}: {:?}", address, err),
  86. }
  87. }
  88. }
  89. }
  90. rsdp_address
  91. };
  92. match rsdp_address {
  93. Some(address) => {
  94. let rsdp_mapping = unsafe { handler.map_physical_region::<Rsdp>(address, mem::size_of::<Rsdp>()) };
  95. Ok(rsdp_mapping)
  96. }
  97. None => Err(RsdpError::NoValidRsdp),
  98. }
  99. }
  100. /// Checks that:
  101. /// 1) The signature is correct
  102. /// 2) The checksum is correct
  103. /// 3) For Version 2.0+, that the extension checksum is correct
  104. pub fn validate(&self) -> Result<(), RsdpError> {
  105. const RSDP_V1_LENGTH: usize = 20;
  106. // Check the signature
  107. if &self.signature != RSDP_SIGNATURE {
  108. return Err(RsdpError::IncorrectSignature);
  109. }
  110. // Check the OEM id is valid UTF8 (allows use of unwrap)
  111. if str::from_utf8(&self.oem_id).is_err() {
  112. return Err(RsdpError::InvalidOemId);
  113. }
  114. /*
  115. * `self.length` doesn't exist on ACPI version 1.0, so we mustn't rely on it. Instead,
  116. * check for version 1.0 and use a hard-coded length instead.
  117. */
  118. let length = if self.revision > 0 {
  119. // For Version 2.0+, include the number of bytes specified by `length`
  120. self.length as usize
  121. } else {
  122. RSDP_V1_LENGTH
  123. };
  124. let bytes = unsafe { slice::from_raw_parts(self as *const Rsdp as *const u8, length) };
  125. let sum = bytes.iter().fold(0u8, |sum, &byte| sum.wrapping_add(byte));
  126. if sum != 0 {
  127. return Err(RsdpError::InvalidChecksum);
  128. }
  129. Ok(())
  130. }
  131. pub fn oem_id(&self) -> &str {
  132. str::from_utf8(&self.oem_id).unwrap()
  133. }
  134. pub fn revision(&self) -> u8 {
  135. self.revision
  136. }
  137. pub fn rsdt_address(&self) -> u32 {
  138. self.rsdt_address
  139. }
  140. pub fn xsdt_address(&self) -> u64 {
  141. assert!(self.revision > 0, "Tried to read extended RSDP field with ACPI Version 1.0");
  142. self.xsdt_address
  143. }
  144. }
  145. /// Find the areas we should search for the RSDP in.
  146. pub fn find_search_areas<H>(handler: H) -> [Range<usize>; 2]
  147. where
  148. H: AcpiHandler,
  149. {
  150. /*
  151. * Read the base address of the EBDA from its location in the BDA (BIOS Data Area). Not all BIOSs fill this out
  152. * unfortunately, so we might not get a sensible result. We shift it left 4, as it's a segment address.
  153. */
  154. let ebda_start_mapping =
  155. unsafe { handler.map_physical_region::<u16>(EBDA_START_SEGMENT_PTR, mem::size_of::<u16>()) };
  156. let ebda_start = (*ebda_start_mapping as usize) << 4;
  157. [
  158. /*
  159. * The main BIOS area below 1MiB. In practice, from my [Restioson's] testing, the RSDP is more often here
  160. * than the EBDA. We also don't want to search the entire possibele EBDA range, if we've failed to find it
  161. * from the BDA.
  162. */
  163. RSDP_BIOS_AREA_START..(RSDP_BIOS_AREA_END + 1),
  164. // Check if base segment ptr is in valid range for EBDA base
  165. if (EBDA_EARLIEST_START..EBDA_END).contains(&ebda_start) {
  166. // First KiB of EBDA
  167. ebda_start..ebda_start + 1024
  168. } else {
  169. // We don't know where the EBDA starts, so just search the largest possible EBDA
  170. EBDA_EARLIEST_START..(EBDA_END + 1)
  171. },
  172. ]
  173. }
  174. /// This (usually!) contains the base address of the EBDA (Extended Bios Data Area), shifted right by 4
  175. const EBDA_START_SEGMENT_PTR: usize = 0x40e;
  176. /// The earliest (lowest) memory address an EBDA (Extended Bios Data Area) can start
  177. const EBDA_EARLIEST_START: usize = 0x80000;
  178. /// The end of the EBDA (Extended Bios Data Area)
  179. const EBDA_END: usize = 0x9ffff;
  180. /// The start of the main BIOS area below 1mb in which to search for the RSDP (Root System Description Pointer)
  181. const RSDP_BIOS_AREA_START: usize = 0xe0000;
  182. /// The end of the main BIOS area below 1mb in which to search for the RSDP (Root System Description Pointer)
  183. const RSDP_BIOS_AREA_END: usize = 0xfffff;
  184. /// The RSDP (Root System Description Pointer)'s signature, "RSD PTR " (note trailing space)
  185. const RSDP_SIGNATURE: &'static [u8; 8] = b"RSD PTR ";