• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::{AcpiError, AcpiHandler, AcpiResult, PhysicalMapping};
2 use core::{mem, ops::Range, slice, str};
3 
4 /// The size in bytes of the ACPI 1.0 RSDP.
5 const RSDP_V1_LENGTH: usize = 20;
6 /// The total size in bytes of the RSDP fields introduced in ACPI 2.0.
7 const RSDP_V2_EXT_LENGTH: usize = mem::size_of::<Rsdp>() - RSDP_V1_LENGTH;
8 
9 /// The first structure found in ACPI. It just tells us where the RSDT is.
10 ///
11 /// On BIOS systems, it is either found in the first 1KiB of the Extended Bios Data Area, or between `0x000e0000`
12 /// and `0x000fffff`. The signature is always on a 16 byte boundary. On (U)EFI, it may not be located in these
13 /// locations, and so an address should be found in the EFI configuration table instead.
14 ///
15 /// The recommended way of locating the RSDP is to let the bootloader do it - Multiboot2 can pass a
16 /// tag with the physical address of it. If this is not possible, a manual scan can be done.
17 ///
18 /// If `revision > 0`, (the hardware ACPI version is Version 2.0 or greater), the RSDP contains
19 /// some new fields. For ACPI Version 1.0, these fields are not valid and should not be accessed.
20 /// For ACPI Version 2.0+, `xsdt_address` should be used (truncated to `u32` on x86) instead of
21 /// `rsdt_address`.
22 #[derive(Clone, Copy, Debug)]
23 #[repr(C, packed)]
24 pub struct Rsdp {
25     signature: [u8; 8],
26     checksum: u8,
27     oem_id: [u8; 6],
28     revision: u8,
29     rsdt_address: u32,
30 
31     /*
32      * These fields are only valid for ACPI Version 2.0 and greater
33      */
34     length: u32,
35     xsdt_address: u64,
36     ext_checksum: u8,
37     reserved: [u8; 3],
38 }
39 
40 impl Rsdp {
41     /// This searches for a RSDP on BIOS systems.
42     ///
43     /// ### Safety
44     /// This function probes memory in three locations:
45     ///    - It reads a word from `40:0e` to locate the EBDA.
46     ///    - The first 1KiB of the EBDA (Extended BIOS Data Area).
47     ///    - The BIOS memory area at `0xe0000..=0xfffff`.
48     ///
49     /// This should be fine on all BIOS systems. However, UEFI platforms are free to put the RSDP wherever they
50     /// please, so this won't always find the RSDP. Further, prodding these memory locations may have unintended
51     /// side-effects. On UEFI systems, the RSDP should be found in the Configuration Table, using two GUIDs:
52     ///     - ACPI v1.0 structures use `eb9d2d30-2d88-11d3-9a16-0090273fc14d`.
53     ///     - ACPI v2.0 or later structures use `8868e871-e4f1-11d3-bc22-0080c73c8881`.
54     /// You should search the entire table for the v2.0 GUID before searching for the v1.0 one.
search_for_on_bios<H>(handler: H) -> AcpiResult<PhysicalMapping<H, Rsdp>> where H: AcpiHandler,55     pub unsafe fn search_for_on_bios<H>(handler: H) -> AcpiResult<PhysicalMapping<H, Rsdp>>
56     where
57         H: AcpiHandler,
58     {
59         let rsdp_address = find_search_areas(handler.clone()).iter().find_map(|area| {
60             // Map the search area for the RSDP followed by `RSDP_V2_EXT_LENGTH` bytes so an ACPI 1.0 RSDP at the
61             // end of the area can be read as an `Rsdp` (which always has the size of an ACPI 2.0 RSDP)
62             let mapping = unsafe {
63                 handler.map_physical_region::<u8>(area.start, area.end - area.start + RSDP_V2_EXT_LENGTH)
64             };
65 
66             let extended_area_bytes =
67                 unsafe { slice::from_raw_parts(mapping.virtual_start().as_ptr(), mapping.region_length()) };
68 
69             // Search `Rsdp`-sized windows at 16-byte boundaries relative to the base of the area (which is also
70             // aligned to 16 bytes due to the implementation of `find_search_areas`)
71             extended_area_bytes.windows(mem::size_of::<Rsdp>()).step_by(16).find_map(|maybe_rsdp_bytes_slice| {
72                 let maybe_rsdp_virt_ptr = maybe_rsdp_bytes_slice.as_ptr().cast::<Rsdp>();
73                 let maybe_rsdp_phys_start = maybe_rsdp_virt_ptr as usize
74                     - mapping.virtual_start().as_ptr() as usize
75                     + mapping.physical_start();
76                 // SAFETY: `maybe_rsdp_virt_ptr` points to an aligned, readable `Rsdp`-sized value, and the `Rsdp`
77                 // struct's fields are always initialized.
78                 let maybe_rsdp = unsafe { &*maybe_rsdp_virt_ptr };
79 
80                 match maybe_rsdp.validate() {
81                     Ok(()) => Some(maybe_rsdp_phys_start),
82                     Err(AcpiError::RsdpIncorrectSignature) => None,
83                     Err(err) => {
84                         log::warn!("Invalid RSDP found at {:#x}: {:?}", maybe_rsdp_phys_start, err);
85                         None
86                     }
87                 }
88             })
89         });
90 
91         match rsdp_address {
92             Some(address) => {
93                 let rsdp_mapping = unsafe { handler.map_physical_region::<Rsdp>(address, mem::size_of::<Rsdp>()) };
94                 Ok(rsdp_mapping)
95             }
96             None => Err(AcpiError::NoValidRsdp),
97         }
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
validate(&self) -> AcpiResult<()>104     pub fn validate(&self) -> AcpiResult<()> {
105         // Check the signature
106         if self.signature != RSDP_SIGNATURE {
107             return Err(AcpiError::RsdpIncorrectSignature);
108         }
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(AcpiError::RsdpInvalidOemId);
113         }
114 
115         /*
116          * `self.length` doesn't exist on ACPI version 1.0, so we mustn't rely on it. Instead,
117          * check for version 1.0 and use a hard-coded length instead.
118          */
119         let length = if self.revision > 0 {
120             // For Version 2.0+, include the number of bytes specified by `length`
121             self.length as usize
122         } else {
123             RSDP_V1_LENGTH
124         };
125 
126         let bytes = unsafe { slice::from_raw_parts(self as *const Rsdp as *const u8, length) };
127         let sum = bytes.iter().fold(0u8, |sum, &byte| sum.wrapping_add(byte));
128 
129         if sum != 0 {
130             return Err(AcpiError::RsdpInvalidChecksum);
131         }
132 
133         Ok(())
134     }
135 
signature(&self) -> [u8; 8]136     pub fn signature(&self) -> [u8; 8] {
137         self.signature
138     }
139 
checksum(&self) -> u8140     pub fn checksum(&self) -> u8 {
141         self.checksum
142     }
143 
oem_id(&self) -> &str144     pub fn oem_id(&self) -> &str {
145         str::from_utf8(&self.oem_id).unwrap()
146     }
147 
revision(&self) -> u8148     pub fn revision(&self) -> u8 {
149         self.revision
150     }
151 
rsdt_address(&self) -> u32152     pub fn rsdt_address(&self) -> u32 {
153         self.rsdt_address
154     }
155 
length(&self) -> u32156     pub fn length(&self) -> u32 {
157         assert!(self.revision > 0, "Tried to read extended RSDP field with ACPI Version 1.0");
158         self.length
159     }
160 
xsdt_address(&self) -> u64161     pub fn xsdt_address(&self) -> u64 {
162         assert!(self.revision > 0, "Tried to read extended RSDP field with ACPI Version 1.0");
163         self.xsdt_address
164     }
165 
ext_checksum(&self) -> u8166     pub fn ext_checksum(&self) -> u8 {
167         assert!(self.revision > 0, "Tried to read extended RSDP field with ACPI Version 1.0");
168         self.ext_checksum
169     }
170 }
171 
172 /// Find the areas we should search for the RSDP in.
find_search_areas<H>(handler: H) -> [Range<usize>; 2] where H: AcpiHandler,173 fn find_search_areas<H>(handler: H) -> [Range<usize>; 2]
174 where
175     H: AcpiHandler,
176 {
177     /*
178      * Read the base address of the EBDA from its location in the BDA (BIOS Data Area). Not all BIOSs fill this out
179      * unfortunately, so we might not get a sensible result. We shift it left 4, as it's a segment address.
180      */
181     let ebda_start_mapping =
182         unsafe { handler.map_physical_region::<u16>(EBDA_START_SEGMENT_PTR, mem::size_of::<u16>()) };
183     let ebda_start = (*ebda_start_mapping as usize) << 4;
184 
185     [
186         /*
187          * The main BIOS area below 1MiB. In practice, from my [Restioson's] testing, the RSDP is more often here
188          * than the EBDA. We also don't want to search the entire possibele EBDA range, if we've failed to find it
189          * from the BDA.
190          */
191         RSDP_BIOS_AREA_START..(RSDP_BIOS_AREA_END + 1),
192         // Check if base segment ptr is in valid range for EBDA base
193         if (EBDA_EARLIEST_START..EBDA_END).contains(&ebda_start) {
194             // First KiB of EBDA
195             ebda_start..ebda_start + 1024
196         } else {
197             // We don't know where the EBDA starts, so just search the largest possible EBDA
198             EBDA_EARLIEST_START..(EBDA_END + 1)
199         },
200     ]
201 }
202 
203 /// This (usually!) contains the base address of the EBDA (Extended Bios Data Area), shifted right by 4
204 const EBDA_START_SEGMENT_PTR: usize = 0x40e;
205 /// The earliest (lowest) memory address an EBDA (Extended Bios Data Area) can start
206 const EBDA_EARLIEST_START: usize = 0x80000;
207 /// The end of the EBDA (Extended Bios Data Area)
208 const EBDA_END: usize = 0x9ffff;
209 /// The start of the main BIOS area below 1MiB in which to search for the RSDP (Root System Description Pointer)
210 const RSDP_BIOS_AREA_START: usize = 0xe0000;
211 /// The end of the main BIOS area below 1MiB in which to search for the RSDP (Root System Description Pointer)
212 const RSDP_BIOS_AREA_END: usize = 0xfffff;
213 /// The RSDP (Root System Description Pointer)'s signature, "RSD PTR " (note trailing space)
214 const RSDP_SIGNATURE: [u8; 8] = *b"RSD PTR ";
215