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