Procházet zdrojové kódy

rsdp: Improve handling of RSDP search areas' ends

This ensures the `Rsdp`-sized region being read while searching
remains within the area mapped. However, since a potential ACPI 1.0
RSDP at the end of the area is currently being read as a full-size
`Rsdp`, the 16 bytes following the area are also mapped to allow this
(not ideal).
rcerc před 2 roky
rodič
revize
a0db984096
1 změnil soubory, kde provedl 42 přidání a 26 odebrání
  1. 42 26
      rsdp/src/lib.rs

+ 42 - 26
rsdp/src/lib.rs

@@ -28,6 +28,11 @@ pub enum RsdpError {
     InvalidChecksum,
 }
 
+/// The size in bytes of the ACPI 1.0 RSDP.
+const RSDP_V1_LENGTH: usize = 20;
+/// The total size in bytes of the RSDP fields introduced in ACPI 2.0.
+const RSDP_V2_EXT_LENGTH: usize = mem::size_of::<Rsdp>() - RSDP_V1_LENGTH;
+
 /// 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
@@ -79,32 +84,45 @@ impl Rsdp {
     where
         H: AcpiHandler,
     {
-        let rsdp_address = {
-            let mut rsdp_address = None;
-            let areas = find_search_areas(handler.clone());
-
-            'areas: for area in areas.iter() {
-                let mapping = unsafe { handler.map_physical_region::<u8>(area.start, area.end - area.start) };
-
-                for address in area.clone().step_by(16) {
-                    let ptr_in_mapping =
-                        unsafe { mapping.virtual_start().as_ptr().add(address - area.start) };
-                    let signature = unsafe { *(ptr_in_mapping as *const [u8; 8]) };
-
-                    if signature == RSDP_SIGNATURE {
-                        match unsafe { *(ptr_in_mapping as *const Rsdp) }.validate() {
-                            Ok(()) => {
-                                rsdp_address = Some(address);
-                                break 'areas;
-                            }
-                            Err(err) => warn!("Invalid RSDP found at {:#x}: {:?}", address, err),
-                        }
+        let rsdp_address = find_search_areas(handler.clone()).iter().find_map(|area| {
+            // Map the search area for the RSDP followed by `RSDP_V2_EXT_LENGTH` bytes so an ACPI 1.0 RSDP at the
+            // end of the area can be read as an `Rsdp` (which always has the size of an ACPI 2.0 RSDP)
+            let mapping = unsafe {
+                handler.map_physical_region::<u8>(area.start, area.end - area.start + RSDP_V2_EXT_LENGTH)
+            };
+
+            // SAFETY: The entirety of `mapping` maps the search area and an additional `RSDP_V2_EXT_LENGTH` bytes
+            // that are assumed to be safe to read while `extended_area_bytes` lives. (Ideally, an assumption about
+            // the memory following the search area would not be made.)
+            let extended_area_bytes = unsafe {
+                slice::from_raw_parts(
+                    mapping.virtual_start().as_ptr(),
+                    mapping.region_length() + RSDP_V2_EXT_LENGTH,
+                )
+            };
+
+            // Search `Rsdp`-sized windows at 16-byte boundaries relative to the base of the area (which is also
+            // aligned to 16 bytes due to the implementation of `find_search_areas`)
+            extended_area_bytes.windows(mem::size_of::<Rsdp>()).step_by(16).find_map(|maybe_rsdp_bytes_slice| {
+                let maybe_rsdp_virt_ptr = maybe_rsdp_bytes_slice.as_ptr().cast::<Rsdp>();
+                let maybe_rsdp_phys_start = maybe_rsdp_virt_ptr as usize
+                    - mapping.virtual_start().as_ptr() as usize
+                    + mapping.physical_start();
+                // SAFETY: `maybe_rsdp_virt_ptr` points to an aligned, readable `Rsdp`-sized value, and the `Rsdp`
+                // struct's fields are always initialized.
+                let maybe_rsdp = unsafe { &*maybe_rsdp_virt_ptr };
+
+                match maybe_rsdp.validate() {
+                    Ok(()) => Some(maybe_rsdp_phys_start),
+                    Err(RsdpError::IncorrectSignature) => None,
+                    Err(e) => {
+                        warn!("Invalid RSDP found at {:#x}: {:?}", maybe_rsdp_phys_start, e);
+
+                        None
                     }
                 }
-            }
-
-            rsdp_address
-        };
+            })
+        });
 
         match rsdp_address {
             Some(address) => {
@@ -120,8 +138,6 @@ impl Rsdp {
     ///     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 != RSDP_SIGNATURE {
             return Err(RsdpError::IncorrectSignature);