浏览代码

Add RSDP manual search (#22)

Add an entry point to search for the RSDP on BIOS systems for when the OSPM doesn’t have any other information
Restioson 6 年之前
父节点
当前提交
76333dcd40
共有 2 个文件被更改,包括 122 次插入5 次删除
  1. 18 5
      src/lib.rs
  2. 104 0
      src/rsdp_search.rs

+ 18 - 5
src/lib.rs

@@ -1,7 +1,7 @@
 #![no_std]
 #![feature(nll)]
 #![feature(alloc)]
-#![feature(exclusive_range_pattern)]
+#![feature(exclusive_range_pattern, range_contains)]
 
 #[cfg(test)]
 #[macro_use]
@@ -16,8 +16,11 @@ mod aml;
 mod fadt;
 mod hpet;
 mod rsdp;
+mod rsdp_search;
 mod sdt;
 
+pub use rsdp_search::search_for_rsdp_bios;
+
 use alloc::{collections::BTreeMap, string::String};
 use aml::{AmlError, AmlValue};
 use core::mem;
@@ -32,6 +35,7 @@ pub enum AcpiError {
     RsdpIncorrectSignature,
     RsdpInvalidOemId,
     RsdpInvalidChecksum,
+    NoValidRsdp,
 
     SdtInvalidSignature([u8; 4]),
     SdtInvalidOemId([u8; 4]),
@@ -108,6 +112,17 @@ where
 {
     let rsdp_mapping = handler.map_physical_region::<Rsdp>(rsdp_address, mem::size_of::<Rsdp>());
     (*rsdp_mapping).validate()?;
+
+    parse_validated_rsdp(handler, rsdp_mapping)
+}
+
+fn parse_validated_rsdp<H>(
+    handler: &mut H,
+    rsdp_mapping: PhysicalMapping<Rsdp>,
+) -> Result<(), AcpiError>
+where
+    H: AcpiHandler,
+{
     let revision = (*rsdp_mapping).revision();
 
     if revision == 0 {
@@ -116,7 +131,7 @@ where
          */
         let rsdt_address = (*rsdp_mapping).rsdt_address();
         handler.unmap_physical_region(rsdp_mapping);
-        parse_rsdt(handler, revision, rsdt_address as usize)?;
+        parse_rsdt(handler, revision, rsdt_address as usize)
     } else {
         /*
          * We're running on ACPI Version 2.0+. We should use the 64-bit XSDT address, truncated
@@ -124,10 +139,8 @@ where
          */
         let xsdt_address = (*rsdp_mapping).xsdt_address();
         handler.unmap_physical_region(rsdp_mapping);
-        parse_rsdt(handler, revision, xsdt_address as usize)?;
+        parse_rsdt(handler, revision, xsdt_address as usize)
     }
-
-    Ok(())
 }
 
 /// This is the entry point of `acpi` if you already have the **physical** address of the

+ 104 - 0
src/rsdp_search.rs

@@ -0,0 +1,104 @@
+use core::{mem, ops::RangeInclusive};
+use rsdp::Rsdp;
+use {parse_validated_rsdp, AcpiError, AcpiHandler};
+
+/// The pointer to the EBDA (Extended Bios Data Area) start segment pointer
+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 ";
+
+/// Find the begining of the EBDA (Extended Bios Data Area) and return `None` if the ptr at
+/// `0x40e` is invalid.
+pub fn find_search_areas<H>(handler: &mut H) -> [RangeInclusive<usize>; 2]
+where
+    H: AcpiHandler,
+{
+    // Read base segment from BIOS area. This is not always given by the bios, so it needs to be
+    // checked. We left shift 4 because it is a segment ptr.
+    let ebda_start_mapping =
+        handler.map_physical_region::<u16>(EBDA_START_SEGMENT_PTR, mem::size_of::<u16>());
+    let ebda_start = (*ebda_start_mapping as usize) << 4;
+    handler.unmap_physical_region(ebda_start_mapping);
+
+    [
+        // Main bios area below 1 mb
+        // In practice (from my [Restioson's] testing, at least), the RSDP is more often here than
+        // the in EBDA. Also, if we cannot find the EBDA, then we don't want to search the largest
+        // possible EBDA first.
+        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 is the entry point of `acpi` if you have no information except that the machine is running
+/// BIOS and not UEFI. It maps the RSDP, works out what version of ACPI the hardware supports, and
+/// passes the physical address of the RSDT/XSDT to `parse_rsdt`.
+///
+/// # Unsafety
+///
+/// This function is unsafe because it may read from protected memory if the computer is using UEFI.
+/// Only use this function if you are sure the computer is using BIOS.
+pub unsafe fn search_for_rsdp_bios<H>(handler: &mut H) -> Result<(), AcpiError>
+where
+    H: AcpiHandler,
+{
+    // The areas that will be searched for the RSDP
+    let areas = find_search_areas(handler);
+
+    // On x86 it is more efficient to map 4096 bytes at a time because of how paging works
+    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) {
+            handler.unmap_physical_region(area_mapping);
+            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;
+        }
+
+        handler.unmap_physical_region(area_mapping);
+        return parse_validated_rsdp(handler, rsdp_mapping);
+    }
+
+    Err(AcpiError::NoValidRsdp)
+}