|
@@ -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!();
|
|
|
}
|
|
|
}
|