瀏覽代碼

Parse the MCFG

Isaac Woods 5 年之前
父節點
當前提交
db1e1b72cb
共有 3 個文件被更改,包括 116 次插入5 次删除
  1. 10 4
      acpi/src/lib.rs
  2. 89 0
      acpi/src/mcfg.rs
  3. 17 1
      acpi/src/sdt.rs

+ 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 => {