Procházet zdrojové kódy

[BREAKS CI] Start of RSDT/XSDT parsing

Isaac Woods před 7 roky
rodič
revize
99a6b2e6ba
3 změnil soubory, kde provedl 156 přidání a 87 odebrání
  1. 87 0
      src/constructed_tables_test.rs
  2. 68 87
      src/lib.rs
  3. 1 0
      src/sdt.rs

+ 87 - 0
src/constructed_tables_test.rs

@@ -0,0 +1,87 @@
+/// These tests cover ideal sets of ACPI tables, which we construct on the fly. Eventually, this
+/// should cover all compliant implementations, but does not guarantee we'll be able to parse a
+/// particular hardware's tables, obviously.
+
+use std::mem;
+use std::ptr::NonNull;
+use std::boxed::Box;
+use super::{AcpiHandler, AcpiInfo, PhysicalMapping, rsdp::Rsdp};
+
+const OEM_ID : &[u8; 6] = b"RUST  ";
+
+/*
+ * We use fake physical addresses to track what is being requested. When a particular table or
+ * resource is requested, we just allocate it on the heap and return the "virtual address"
+ * (a pointer onto the heap).
+ */
+const RSDP_ADDRESS : usize = 0x0;
+const RSDT_ADDRESS : usize = 0x1;
+
+struct TestHandler;
+
+impl AcpiHandler for TestHandler
+{
+    fn map_physical_region<T>(&mut self, physical_address : usize) -> PhysicalMapping<T>
+    {
+        match physical_address
+        {
+            RSDP_ADDRESS =>
+            {
+                let rsdp = Rsdp::make_testcase(*b"RSD PTR ",
+                                               None,
+                                               *OEM_ID,
+                                               0,
+                                               RSDT_ADDRESS as u32,
+                                               0,
+                                               0x0,
+                                               None,
+                                               [0, 0, 0]
+                                              );
+
+                PhysicalMapping
+                {
+                    physical_start  : RSDP_ADDRESS,
+                    virtual_start   : unsafe
+                                      {
+                                          NonNull::<T>::new_unchecked(Box::into_raw(Box::new(rsdp)) as *mut T)
+                                      },
+                    region_length   : mem::size_of::<Rsdp>(),
+                    mapped_length   : mem::size_of::<Rsdp>(),
+                }
+            },
+
+            _ => panic!("ACPI requested invalid physical address: {:#x}", physical_address),
+        }
+    }
+
+    fn unmap_physical_region<T>(&mut self, region : PhysicalMapping<T>)
+    {
+        match region.physical_start
+        {
+            RSDP_ADDRESS =>
+            {
+                let _ = unsafe { Box::from_raw(region.virtual_start.as_ptr()) };
+            },
+
+            address => panic!("ACPI tried to unmap a region not created by test harness: {:#x}", address),
+        }
+    }
+}
+
+#[test]
+fn test_constructed_tables()
+{
+    let mut test_handler = TestHandler;
+    match AcpiInfo::parse_rsdp(&mut test_handler, RSDP_ADDRESS)
+    {
+        Ok(info) =>
+        {
+            assert_eq!(info.revision, 0);
+        },
+
+        Err(err) =>
+        {
+            panic!("Failed to parse ACPI: {:#?}", err);
+        },
+    }
+}

+ 68 - 87
src/lib.rs

@@ -5,10 +5,13 @@ extern crate std;
 
 mod rsdp;
 mod sdt;
+#[cfg(test)] mod constructed_tables_test;
 
+use core::mem;
 use core::ops::Deref;
 use core::ptr::NonNull;
 use rsdp::Rsdp;
+use sdt::SdtHeader;
 
 #[derive(Debug)]
 pub enum AcpiError
@@ -24,13 +27,14 @@ pub enum AcpiError
 }
 
 /// 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>()`
+/// `AcpiHandler::unmap_physical_region`. The region mapped must be at least `size_of::<T>()`
 /// bytes, but may be bigger.
 pub struct PhysicalMapping<T>
 {
     pub physical_start  : usize,
     pub virtual_start   : NonNull<T>,
-    pub mapped_length   : usize,    // Differs from `region_length` if padding is added to align to page boundaries
+    pub region_length   : usize,    // Can be equal or larger than size_of::<T>()
+    pub mapped_length   : usize,    // Differs from `region_length` if padding is added for alignment
 }
 
 impl<T> Deref for PhysicalMapping<T>
@@ -54,7 +58,8 @@ 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.
+    /// the implementation may have to add padding to either end. The supplied size must be large
+    /// enough to hold a `T`, but may also be larger.
     fn map_physical_region<T>(&mut self, physical_address : usize) -> PhysicalMapping<T>;
 
     /// Unmap the given physical mapping. Safe because we consume the mapping, and so it can't be
@@ -62,103 +67,79 @@ pub trait AcpiHandler
     fn unmap_physical_region<T>(&mut self, region : PhysicalMapping<T>);
 }
 
-/// 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
+pub struct AcpiInfo<'a, H : 'a>
+    where H : 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(())
+    handler     : &'a mut H,
+    revision    : u8,
 }
 
-/// This module tests against idealistic sets of ACPI tables, which we construct on the fly. This
-/// should cover all standard-compliant implementations eventually, but does not guarantee we can
-/// handle all hardware's tables, obviously.
-#[cfg(test)]
-mod constructed_table_tests
+impl<'a, H> AcpiInfo<'a, H>
+    where H : AcpiHandler
 {
-    use std::mem;
-    use std::ptr::NonNull;
-    use std::boxed::Box;
-    use {AcpiHandler, PhysicalMapping, parse_acpi, rsdp::Rsdp};
-
-    const OEM_ID : &[u8; 6] = b"RUST  ";
-
-    /*
-     * We use fake physical addresses to track what is being requested. When a particular table or
-     * resource is requested, we just allocate it on the heap and return the "virtual address"
-     * (a pointer onto the heap).
-     */
-    const RSDP_ADDRESS : usize = 0x0;
-    const RSDT_ADDRESS : usize = 0x1;
-
-    struct TestHandler { }
-
-    impl AcpiHandler for TestHandler
+    /// This is the entry point of `acpi` if you have the **physical** address of the RSDP. 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`.
+    pub fn parse_rsdp(handler : &'a mut H, rsdp_address : usize) -> Result<AcpiInfo<H>, AcpiError>
     {
-        fn map_physical_region<T>(&mut self, physical_address : usize) -> PhysicalMapping<T>
+        let rsdp_mapping = handler.map_physical_region::<Rsdp>(rsdp_address);
+        (*rsdp_mapping).validate()?;
+        let revision = (*rsdp_mapping).revision();
+
+        if revision == 0
         {
-            match physical_address
-            {
-                RSDP_ADDRESS =>
-                {
-                    let rsdp = Rsdp::make_testcase(*b"RSD PTR ",
-                                                   None,
-                                                   *OEM_ID,
-                                                   0,
-                                                   RSDT_ADDRESS as u32,
-                                                   0,
-                                                   0x0,
-                                                   None,
-                                                   [0, 0, 0]
-                                                  );
-
-                    PhysicalMapping
-                    {
-                        physical_start  : RSDP_ADDRESS,
-                        virtual_start   : unsafe
-                                          {
-                                              NonNull::<T>::new_unchecked(Box::into_raw(Box::new(rsdp)) as *mut T)
-                                          },
-                        mapped_length   : mem::size_of::<Rsdp>(),
-                    }
-                },
-
-                _ => panic!("ACPI requested invalid physical address: {:#x}", physical_address),
-            }
+            /*
+             * We're running on ACPI Version 1.0. We should use the 32-bit RSDT address.
+             */
+            let rsdt_address = (*rsdp_mapping).rsdt_address();
+            handler.unmap_physical_region(rsdp_mapping);
+            AcpiInfo::parse_rsdt(handler, revision, rsdt_address as usize)
         }
-
-        fn unmap_physical_region<T>(&mut self, region : PhysicalMapping<T>)
+        else
         {
-            match region.physical_start
-            {
-                RSDP_ADDRESS =>
-                {
-                    let _ = unsafe { Box::from_raw(region.virtual_start.as_ptr()) };
-                },
-
-                address => panic!("ACPI tried to unmap a region not created by test harness: {:#x}", address),
-            }
+            /*
+             * We're running on ACPI Version 2.0+. We should use the 64-bit XSDT address, truncated
+             * to 32 bits on x86.
+             */
+            let xsdt_address = (*rsdp_mapping).xsdt_address();
+            handler.unmap_physical_region(rsdp_mapping);
+            AcpiInfo::parse_rsdt(handler, revision, xsdt_address as usize)
         }
     }
 
-    #[test]
-    fn test_constructed_tables()
+    /// This is the entry point of `acpi` if you already have the **physical** address of the
+    /// RSDT/XSDT; it parses all the SDTs in the RSDT/XSDT, calling the relevant handlers in the
+    /// implementation's `AcpiHandler`.
+    ///
+    /// If the given revision is 0, an address to the RSDT is expected. Otherwise, an address to
+    /// the XSDT is expected.
+    pub fn parse_rsdt(handler           : &'a mut H,
+                      revision          : u8,
+                      physical_address  : usize) -> Result<AcpiInfo<H>, AcpiError>
     {
-        let mut test_handler = TestHandler { };
-        match parse_acpi(&mut test_handler, RSDP_ADDRESS)
+        let mapping = handler.map_physical_region::<SdtHeader>(physical_address);
+
+        // TODO: extend the mapping to header.length
+        // TODO: validate the signature and checksum
+
+        if revision == 0
         {
-            Ok(_) => (),
-            Err(err) =>
-            {
-                panic!("Failed to parse ACPI: {:#?}", err);
-            },
+            /*
+             * ACPI Version 1.0. It's a RSDT!
+             */
+            let num_tables = ((*mapping).length() as usize - mem::size_of::<SdtHeader>()) / mem::size_of::<u32>();
+            let pointer_base = ((mapping.virtual_start.as_ptr() as usize) + mem::size_of::<SdtHeader>()) as *const u32;
         }
+        else
+        {
+            /*
+             * ACPI Version 2.0+. It's a XSDT!
+             */
+            let num_tables = ((*mapping).length() as usize - mem::size_of::<SdtHeader>()) / mem::size_of::<u64>();
+            let pointer_base = ((mapping.virtual_start.as_ptr() as usize) + mem::size_of::<SdtHeader>()) as *const u64;
+        }
+
+        handler.unmap_physical_region(mapping);
+        unimplemented!();
     }
 }

+ 1 - 0
src/sdt.rs

@@ -1,4 +1,5 @@
 use core::str;
+use AcpiError;
 
 /// All SDTs share the same header, and are `length` bytes long. The signature tells us which SDT
 /// this is.