Browse Source

Merge #45

45: Parse the MCFG r=IsaacWoods a=IsaacWoods

This parses the MCFG SDT (I can't find what MCFG actually stands for, if anyone knows that'd be great), which describes the memory used by PCI-E configuration spaces. This PR parses the raw MCFG and constructs a nice interface type that is exposed to the library user. The main use of this type is through the `physical_address` method, that calculates the physical address of the start of the configuration region for a given PCI-E device.

Closes #43 

Co-authored-by: Isaac Woods <isaacwoods.home@gmail.com>
bors[bot] 5 năm trước cách đây
mục cha
commit
e00c0a8756
6 tập tin đã thay đổi với 119 bổ sung8 xóa
  1. 1 1
      README.md
  2. 1 1
      acpi/Cargo.toml
  3. 10 4
      acpi/src/lib.rs
  4. 89 0
      acpi/src/mcfg.rs
  5. 17 1
      acpi/src/sdt.rs
  6. 1 1
      aml_parser/Cargo.toml

+ 1 - 1
README.md

@@ -7,7 +7,7 @@
 
 A library to parse ACPI tables and AML, written in pure Rust. Designed to be easy to use from Rust bootloaders and kernels. The library is split into two crates:
 - `acpi` parses the static tables (useful but not feature-complete)
-- `aml_parser` parses the AML tables (still a work-in-progress, not that useful yet)
+- `aml_parser` parses the AML tables (can parse some namespaces, cannot yet execute methods)
 
 ## Contributing
 Contributions are more than welcome! You can:

+ 1 - 1
acpi/Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 name = "acpi"
-version = "0.2.1"
+version = "0.3.0"
 authors = ["Isaac Woods"]
 repository = "https://github.com/rust-osdev/acpi"
 description = "Library for parsing ACPI tables"

+ 10 - 4
acpi/src/lib.rs

@@ -22,9 +22,7 @@
 //! gathered from the static tables, and can be queried to set up hardware etc.
 
 #![no_std]
-#![feature(nll)]
-#![feature(alloc)]
-#![feature(exclusive_range_pattern)]
+#![feature(nll, exclusive_range_pattern)]
 
 extern crate alloc;
 #[cfg_attr(test, macro_use)]
@@ -36,17 +34,21 @@ pub mod handler;
 mod hpet;
 pub mod interrupt;
 mod madt;
+mod mcfg;
 mod rsdp;
 mod rsdp_search;
 mod sdt;
 
 pub use crate::{
     handler::{AcpiHandler, PhysicalMapping},
+    hpet::HpetInfo,
+    interrupt::InterruptModel,
     madt::MadtError,
+    mcfg::PciConfigRegions,
     rsdp_search::search_for_rsdp_bios,
 };
 
-use crate::{hpet::HpetInfo, interrupt::InterruptModel, rsdp::Rsdp, sdt::SdtHeader};
+use crate::{rsdp::Rsdp, sdt::SdtHeader};
 use alloc::vec::Vec;
 use core::mem;
 
@@ -144,6 +146,9 @@ pub struct Acpi {
 
     /// Info about any SSDTs, if there are any.
     pub ssdts: Vec<AmlTable>,
+
+    /// Info about the PCI-E configuration memory regions, collected from the MCFG.
+    pub pci_config_regions: Option<PciConfigRegions>,
 }
 
 /// This is the entry point of `acpi` if you have the **physical** address of the RSDP. It maps
@@ -208,6 +213,7 @@ where
         hpet: None,
         dsdt: None,
         ssdts: Vec::with_capacity(0),
+        pci_config_regions: None,
     };
 
     let header = sdt::peek_at_sdt_header(handler, physical_address);

+ 89 - 0
acpi/src/mcfg.rs

@@ -0,0 +1,89 @@
+use crate::{handler::PhysicalMapping, sdt::SdtHeader, Acpi, AcpiError};
+use alloc::vec::Vec;
+use core::{mem, slice};
+use log::info;
+
+/// Describes a set of regions of physical memory used to access the PCI-E configuration space. A
+/// region  is created for each entry in the MCFG. Given the segment group, bus, device number, and
+/// function of a PCI-E device, the `physical_address` method on this will give you the physical
+/// address of the start of that device function's configuration space (each function has 4096
+/// bytes of configuration space in PCI-E).
+#[derive(Debug)]
+pub struct PciConfigRegions {
+    regions: Vec<McfgEntry>,
+}
+
+impl PciConfigRegions {
+    /// Get the physical address of the start of the configuration space for a given PCI-E device
+    /// function. Returns `None` if there isn't an entry in the MCFG that manages that device.
+    pub fn physical_address(
+        &self,
+        segment_group_no: u16,
+        bus: u8,
+        device: u8,
+        function: u8,
+    ) -> Option<u64> {
+        // First, find the memory region that handles this segment and bus. This method is fine
+        // because there should only be one region that handles each segment group + bus
+        // combination.
+        let region = self.regions.iter().find(|region| {
+            region.pci_segment_group == segment_group_no
+                && (region.bus_number_start..=region.bus_number_end).contains(&bus)
+        })?;
+
+        Some(
+            region.base_address
+                + ((u64::from(bus - region.bus_number_start) << 20)
+                    | (u64::from(device) << 15)
+                    | (u64::from(function) << 12)),
+        )
+    }
+}
+
+#[repr(C, packed)]
+pub(crate) struct Mcfg {
+    header: SdtHeader,
+    _reserved: u64,
+    // Followed by `n` entries with format `McfgEntry`
+}
+
+impl Mcfg {
+    fn entries(&self) -> &[McfgEntry] {
+        let length = self.header.length() as usize - mem::size_of::<Mcfg>();
+        info!(
+            "(raw length = {}, header length = {}, length of entries = {})",
+            self.header.length(),
+            mem::size_of::<SdtHeader>(),
+            length
+        );
+        assert!(length % mem::size_of::<McfgEntry>() == 0);
+        let num_entries = length / mem::size_of::<McfgEntry>();
+
+        unsafe {
+            let pointer = (self as *const Mcfg as *const u8).offset(mem::size_of::<Mcfg>() as isize)
+                as *const McfgEntry;
+            slice::from_raw_parts(pointer, num_entries)
+        }
+    }
+}
+
+#[derive(Clone, Copy, Debug)]
+#[repr(C, packed)]
+struct McfgEntry {
+    base_address: u64,
+    pci_segment_group: u16,
+    bus_number_start: u8,
+    bus_number_end: u8,
+    _reserved: u32,
+}
+
+pub(crate) fn parse_mcfg(
+    acpi: &mut Acpi,
+    mapping: &PhysicalMapping<Mcfg>,
+) -> Result<(), AcpiError> {
+    (*mapping).header.validate(b"MCFG")?;
+
+    acpi.pci_config_regions =
+        Some(PciConfigRegions { regions: mapping.entries().iter().map(|&entry| entry).collect() });
+    Ok(())
+}

+ 17 - 1
acpi/src/sdt.rs

@@ -1,4 +1,13 @@
-use crate::{fadt::Fadt, hpet::HpetTable, madt::Madt, Acpi, AcpiError, AcpiHandler, AmlTable};
+use crate::{
+    fadt::Fadt,
+    hpet::HpetTable,
+    madt::Madt,
+    mcfg::Mcfg,
+    Acpi,
+    AcpiError,
+    AcpiHandler,
+    AmlTable,
+};
 use core::{marker::PhantomData, mem, str};
 use log::{trace, warn};
 use typenum::Unsigned;
@@ -198,6 +207,13 @@ where
             handler.unmap_physical_region(madt_mapping);
         }
 
+        "MCFG" => {
+            let mcfg_mapping =
+                handler.map_physical_region::<Mcfg>(physical_address, header.length() as usize);
+            crate::mcfg::parse_mcfg(acpi, &mcfg_mapping)?;
+            handler.unmap_physical_region(mcfg_mapping);
+        }
+
         "SSDT" => acpi.ssdts.push(AmlTable::new(physical_address, header.length())),
 
         signature => {

+ 1 - 1
aml_parser/Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 name = "aml_parser"
-version = "0.2.0"
+version = "0.3.0"
 authors = ["Isaac Woods"]
 repository = "https://github.com/rust-osdev/acpi"
 description = "Library for parsing AML"