Prechádzať zdrojové kódy

Add AcpiHandler interface and extension fields on the RSDP

Isaac Woods 7 rokov pred
rodič
commit
f4f0d87a70
3 zmenil súbory, kde vykonal 151 pridanie a 48 odobranie
  1. 53 38
      src/lib.rs
  2. 49 10
      src/rsdp.rs
  3. 49 0
      src/sdt.rs

+ 53 - 38
src/lib.rs

@@ -4,55 +4,70 @@
 extern crate std;
 
 mod rsdp;
+mod sdt;
 
-pub use rsdp::Rsdp;
+use core::ops::Deref;
+use core::ptr::NonNull;
+use rsdp::Rsdp;
 
-use core::str;
+pub enum AcpiError
+{
+    RsdpIncorrectSignature,
+    RsdpInvalidOemId,
+    RsdpInvalidChecksum,
+}
 
-/// All SDTs share the same header, and are `length` bytes long. The signature tells us which SDT
-/// this is.
-#[repr(C, packed)]
-pub struct SdtHeader
+/// Describes a physical mapping created by `AcpiHandler::map_physical_region` and unmapped by
+/// `AcpiHandler::unmap_physical_region`. The region mapped must be at least `mem::size_of::<T>()`
+/// bytes.
+pub struct PhysicalMapping<T>
 {
-    signature           : [u8; 4],
-    length              : u32,
-    revision            : u8,
-    checksum            : u8,
-    oem_id              : [u8; 6],
-    oem_table_id        : [u8; 8],
-    oem_revision        : u32,
-    creator_id          : u32,
-    creator_revision    : u32,
+    pub physical_start  : usize,
+    pub virtual_start   : NonNull<T>,
+    pub region_length   : usize,
+    pub mapped_length   : usize,    // Differs from `region_length` if padding is added to align to page boundaries
 }
 
-impl SdtHeader
+impl<T> Deref for PhysicalMapping<T>
 {
-    /// Check that:
-    ///     a) The signature is valid UTF8
-    ///     b) The checksum of the SDT.
-    ///
-    /// This assumes that the whole SDT is mapped.
-    fn validate(&self) -> Result<(), &str>
+    type Target = T;
+
+    fn deref(&self) -> &T
     {
-        // Check the signature
-        if str::from_utf8(&self.signature).is_err()
+        unsafe
         {
-            return Err("SDT signature is not valid UTF8");
+            self.virtual_start.as_ref()
         }
+    }
+}
 
-        // Sum all bytes in the SDT (not just the header)
-        let mut sum : usize = 0;
-        for i in 0..self.length
-        {
-            sum += unsafe { *(self as *const SdtHeader as *const u8).offset(i as isize) } as usize;
-        }
+/// The kernel must provide an implementation of this trait for `acpi` to interface with. It has
+/// utility methods `acpi` uses to for e.g. mapping physical memory, but also an interface for
+/// `acpi` to tell the kernel about the tables it's parsing, such as how the kernel should
+/// configure the APIC or PCI routing.
+pub trait AcpiHandler
+{
+    /// Given a starting physical address, map a region of physical memory that contains a `T`
+    /// somewhere in the virtual address space. The address doesn't have to be page-aligned, so
+    /// the implementation may have to add padding to either end.
+    fn map_physical_region<T>(&mut self, physical_address : usize) -> PhysicalMapping<T>;
 
-        // Check that the lowest byte is 0
-        if sum & 0b1111_1111 != 0
-        {
-            return Err("SDT has incorrect checksum");
-        }
+    /// Unmap the given physical mapping. Safe because we consume the mapping, and so it can't be
+    /// used after being passed to this function.
+    fn unmap_physical_region<T>(&mut self, region : PhysicalMapping<T>);
+}
 
-        Ok(())
-    }
+/// This is the entry point of `acpi`. Given the **physical** address of the RSDP, it parses all
+/// the SDTs in the RSDT, calling the relevant handlers in the implementation's `AcpiHandler`.
+pub fn parse_acpi<T>(handler : &mut T, rsdp_address : usize) -> Result<(), AcpiError>
+    where T : AcpiHandler
+{
+    let rsdp_mapping = handler.map_physical_region::<Rsdp>(rsdp_address);
+    (*rsdp_mapping).validate()?;
+
+    // TODO: map the RSDT
+    // TODO: parse the RSDT
+
+    handler.unmap_physical_region(rsdp_mapping);
+    Ok(())
 }

+ 49 - 10
src/rsdp.rs

@@ -1,4 +1,5 @@
-use core::mem;
+use core::{str,mem};
+use super::AcpiError;
 
 /// The first structure found in ACPI. It just tells us where the RSDT is.
 ///
@@ -8,14 +9,25 @@ use core::mem;
 ///
 /// 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.
 #[repr(C, packed)]
-pub struct Rsdp
+pub(crate) struct Rsdp
 {
-    pub(crate) signature    : [u8; 8],
-    pub(crate) checksum     : u8,
-    pub(crate) oem_id       : [u8; 6],
-    pub(crate) revision     : u8,
-    pub(crate) rsdt_address : u32,
+    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
@@ -24,7 +36,7 @@ impl Rsdp
     ///     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<(), &str>
+    pub(crate) fn validate(&self) -> Result<(), AcpiError>
     {
         // FIXME: Check what version of ACPI this is. This works for Version 1.0 (revision=0), but
         // for subsequent versions, we also need to check the checksum for the extension fields.
@@ -34,7 +46,13 @@ impl Rsdp
         // Check the signature
         if &self.signature != b"RSD PTR "
         {
-            return Err("RSDP has incorrect signature");
+            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);
         }
 
         // Sum all bytes in the structure
@@ -47,9 +65,30 @@ impl Rsdp
         // Check that the lowest byte is 0
         if sum & 0b1111_1111 != 0
         {
-            return Err("RSDP has incorrect checksum");
+            return Err(AcpiError::RsdpInvalidChecksum);
         }
 
         Ok(())
     }
+
+    pub(crate) fn oem_id<'a>(&'a self) -> &'a str
+    {
+        str::from_utf8(&self.oem_id).unwrap()
+    }
+
+    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
+    }
 }

+ 49 - 0
src/sdt.rs

@@ -0,0 +1,49 @@
+use core::str;
+
+/// All SDTs share the same header, and are `length` bytes long. The signature tells us which SDT
+/// this is.
+#[repr(C, packed)]
+pub struct SdtHeader
+{
+    signature           : [u8; 4],
+    length              : u32,
+    revision            : u8,
+    checksum            : u8,
+    oem_id              : [u8; 6],
+    oem_table_id        : [u8; 8],
+    oem_revision        : u32,
+    creator_id          : u32,
+    creator_revision    : u32,
+}
+
+impl SdtHeader
+{
+    /// Check that:
+    ///     a) The signature is valid UTF8
+    ///     b) The checksum of the SDT.
+    ///
+    /// This assumes that the whole SDT is mapped.
+    fn validate(&self) -> Result<(), &str>
+    {
+        // Check the signature
+        if str::from_utf8(&self.signature).is_err()
+        {
+            return Err("SDT signature is not valid UTF8");
+        }
+
+        // Sum all bytes in the SDT (not just the header)
+        let mut sum : usize = 0;
+        for i in 0..self.length
+        {
+            sum += unsafe { *(self as *const SdtHeader as *const u8).offset(i as isize) } as usize;
+        }
+
+        // Check that the lowest byte is 0
+        if sum & 0b1111_1111 != 0
+        {
+            return Err("SDT has incorrect checksum");
+        }
+
+        Ok(())
+    }
+}