浏览代码

Divide RSDP searching into rsdp_search crate

Isaac Woods 4 年之前
父节点
当前提交
93f9e3b337
共有 8 个文件被更改,包括 250 次插入216 次删除
  1. 1 1
      Cargo.toml
  2. 1 0
      acpi/Cargo.toml
  3. 21 25
      acpi/src/lib.rs
  4. 0 86
      acpi/src/rsdp.rs
  5. 0 104
      acpi/src/rsdp_search.rs
  6. 8 0
      rsdp_search/Cargo.toml
  7. 0 0
      rsdp_search/src/handler.rs
  8. 219 0
      rsdp_search/src/lib.rs

+ 1 - 1
Cargo.toml

@@ -1,2 +1,2 @@
 [workspace]
-members = ["acpi", "aml", "acpi-dumper", "aml_tester"]
+members = ["rsdp_search", "acpi", "aml", "acpi-dumper", "aml_tester"]

+ 1 - 0
acpi/Cargo.toml

@@ -12,3 +12,4 @@ edition = "2018"
 [dependencies]
 log = "0.4"
 bit_field = "0.10"
+rsdp_search = { path = "../rsdp_search" }

+ 21 - 25
acpi/src/lib.rs

@@ -43,37 +43,33 @@ extern crate alloc;
 extern crate std;
 
 mod fadt;
-pub mod handler;
 mod hpet;
 mod madt;
 mod mcfg;
 pub mod platform;
-mod rsdp;
 mod sdt;
 
 pub use crate::{
     fadt::PowerProfile,
-    handler::{AcpiHandler, PhysicalMapping},
     hpet::HpetInfo,
     madt::MadtError,
     mcfg::PciConfigRegions,
     platform::{InterruptModel, PlatformInfo},
 };
-
-use crate::{
-    rsdp::Rsdp,
-    sdt::{SdtHeader, Signature},
+pub use rsdp_search::{
+    handler::{AcpiHandler, PhysicalMapping},
+    RsdpError,
 };
+
+use crate::sdt::{SdtHeader, Signature};
 use alloc::{collections::BTreeMap, vec::Vec};
 use core::mem;
 use log::trace;
+use rsdp_search::Rsdp;
 
 #[derive(Debug)]
 pub enum AcpiError {
-    RsdpIncorrectSignature,
-    RsdpInvalidOemId,
-    RsdpInvalidChecksum,
-    NoValidRsdp,
+    Rsdp(RsdpError),
 
     SdtInvalidSignature(Signature),
     SdtInvalidOemId(Signature),
@@ -85,16 +81,6 @@ pub enum AcpiError {
     InvalidMadt(MadtError),
 }
 
-#[derive(Clone, Copy, Debug)]
-#[repr(C, packed)]
-pub(crate) struct GenericAddress {
-    address_space: u8,
-    bit_width: u8,
-    bit_offset: u8,
-    access_size: u8,
-    address: u64,
-}
-
 pub struct AcpiTables<H>
 where
     H: AcpiHandler,
@@ -114,15 +100,15 @@ where
     /// Create an `AcpiTables` if you have the physical address of the RSDP.
     pub unsafe fn from_rsdp(handler: H, rsdp_address: usize) -> Result<AcpiTables<H>, AcpiError> {
         let rsdp_mapping = unsafe { handler.map_physical_region::<Rsdp>(rsdp_address, mem::size_of::<Rsdp>()) };
-        rsdp_mapping.validate()?;
+        rsdp_mapping.validate().map_err(|err| AcpiError::Rsdp(err))?;
 
         Self::from_validated_rsdp(handler, rsdp_mapping)
     }
 
     /// Create an `AcpiTables` if you have a `PhysicalMapping` of the RSDP that you know is correct. This is called
-    /// from `from_rsdp` after validation, and also from the RSDP search routines since they need to validate the
-    /// RSDP anyways.
-    fn from_validated_rsdp(
+    /// from `from_rsdp` after validation, but can also be used if you've searched for the RSDP manually on a BIOS
+    /// system.
+    pub fn from_validated_rsdp(
         handler: H,
         rsdp_mapping: PhysicalMapping<H, Rsdp>,
     ) -> Result<AcpiTables<H>, AcpiError> {
@@ -302,3 +288,13 @@ impl AmlTable {
         }
     }
 }
+
+#[derive(Clone, Copy, Debug)]
+#[repr(C, packed)]
+pub(crate) struct GenericAddress {
+    address_space: u8,
+    bit_width: u8,
+    bit_offset: u8,
+    access_size: u8,
+    address: u64,
+}

+ 0 - 86
acpi/src/rsdp.rs

@@ -1,86 +0,0 @@
-use super::AcpiError;
-use core::{slice, str};
-
-/// 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(crate) 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 {
-    /// Checks that:
-    ///     1) The signature is correct
-    ///     2) The checksum is correct
-    ///     3) For Version 2.0+, that the extension checksum is correct
-    pub(crate) fn validate(&self) -> Result<(), AcpiError> {
-        const RSDP_V1_LENGTH: usize = 20;
-
-        // Check the signature
-        if &self.signature != b"RSD PTR " {
-            return Err(AcpiError::RsdpIncorrectSignature);
-        }
-
-        // Check the OEM id is valid UTF8 (allows use of unwrap)
-        if str::from_utf8(&self.oem_id).is_err() {
-            return Err(AcpiError::RsdpInvalidOemId);
-        }
-
-        /*
-         * `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(AcpiError::RsdpInvalidChecksum);
-        }
-
-        Ok(())
-    }
-
-    pub(crate) fn revision(&self) -> u8 {
-        self.revision
-    }
-
-    pub(crate) fn rsdt_address(&self) -> u32 {
-        self.rsdt_address
-    }
-
-    pub(crate) fn xsdt_address(&self) -> u64 {
-        assert!(self.revision > 0, "Tried to read extended RSDP field with ACPI Version 1.0");
-        self.xsdt_address
-    }
-}

+ 0 - 104
acpi/src/rsdp_search.rs

@@ -1,104 +0,0 @@
-use crate::{parse_validated_rsdp, rsdp::Rsdp, Acpi, AcpiError, AcpiHandler};
-use core::{mem, ops::RangeInclusive};
-use log::warn;
-
-/// 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 =
-        unsafe { 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<Acpi, 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)
-}

+ 8 - 0
rsdp_search/Cargo.toml

@@ -0,0 +1,8 @@
+[package]
+name = "rsdp_search"
+version = "0.1.0"
+authors = ["Isaac Woods", "Restioson"]
+edition = "2018"
+
+[dependencies]
+log = "0.4"

+ 0 - 0
acpi/src/handler.rs → rsdp_search/src/handler.rs


+ 219 - 0
rsdp_search/src/lib.rs

@@ -0,0 +1,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);
+
+        /*
+         * 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 ";