1 use crate::{
2     sdt::{SdtHeader, Signature},
3     AcpiTable,
4 };
5 use core::{mem, slice};
6 
7 /// Describes a set of regions of physical memory used to access the PCIe configuration space. A
8 /// region is created for each entry in the MCFG. Given the segment group, bus, device number, and
9 /// function of a PCIe device, the `physical_address` method on this will give you the physical
10 /// address of the start of that device function's configuration space (each function has 4096
11 /// bytes of configuration space in PCIe).
12 #[cfg(feature = "allocator_api")]
13 pub struct PciConfigRegions<'a, A>
14 where
15     A: core::alloc::Allocator,
16 {
17     regions: crate::ManagedSlice<'a, McfgEntry, A>,
18 }
19 
20 #[cfg(feature = "alloc")]
21 impl<'a> PciConfigRegions<'a, alloc::alloc::Global> {
new<H>(tables: &crate::AcpiTables<H>) -> crate::AcpiResult<PciConfigRegions<'a, alloc::alloc::Global>> where H: crate::AcpiHandler,22     pub fn new<H>(tables: &crate::AcpiTables<H>) -> crate::AcpiResult<PciConfigRegions<'a, alloc::alloc::Global>>
23     where
24         H: crate::AcpiHandler,
25     {
26         Self::new_in(tables, alloc::alloc::Global)
27     }
28 }
29 
30 #[cfg(feature = "allocator_api")]
31 impl<'a, A> PciConfigRegions<'a, A>
32 where
33     A: core::alloc::Allocator,
34 {
new_in<H>(tables: &crate::AcpiTables<H>, allocator: A) -> crate::AcpiResult<PciConfigRegions<'a, A>> where H: crate::AcpiHandler,35     pub fn new_in<H>(tables: &crate::AcpiTables<H>, allocator: A) -> crate::AcpiResult<PciConfigRegions<'a, A>>
36     where
37         H: crate::AcpiHandler,
38     {
39         let mcfg = tables.find_table::<Mcfg>()?;
40         let mcfg_entries = mcfg.entries();
41 
42         let mut regions = crate::ManagedSlice::new_in(mcfg_entries.len(), allocator)?;
43         regions.copy_from_slice(mcfg_entries);
44 
45         Ok(Self { regions })
46     }
47 
48     /// Get the physical address of the start of the configuration space for a given PCIe device
49     /// function. Returns `None` if there isn't an entry in the MCFG that manages that device.
physical_address(&self, segment_group_no: u16, bus: u8, device: u8, function: u8) -> Option<u64>50     pub fn physical_address(&self, segment_group_no: u16, bus: u8, device: u8, function: u8) -> Option<u64> {
51         // First, find the memory region that handles this segment and bus. This method is fine
52         // because there should only be one region that handles each segment group + bus
53         // combination.
54         let region = self.regions.iter().find(|region| {
55             region.pci_segment_group == segment_group_no
56                 && (region.bus_number_start..=region.bus_number_end).contains(&bus)
57         })?;
58 
59         Some(
60             region.base_address
61                 + ((u64::from(bus - region.bus_number_start) << 20)
62                     | (u64::from(device) << 15)
63                     | (u64::from(function) << 12)),
64         )
65     }
66 
67     /// Returns an iterator providing information about the system's present PCI busses.
68     /// This is roughly equivalent to manually iterating the system's MCFG table.
iter(&self) -> PciConfigEntryIterator69     pub fn iter(&self) -> PciConfigEntryIterator {
70         PciConfigEntryIterator { entries: &self.regions, index: 0 }
71     }
72 }
73 
74 /// Configuration entry describing a valid bus range for the given PCI segment group.
75 pub struct PciConfigEntry {
76     pub segment_group: u16,
77     pub bus_range: core::ops::RangeInclusive<u8>,
78     pub physical_address: usize,
79 }
80 
81 /// Iterator providing a [`PciConfigEntry`] for all of the valid bus ranges on the system.
82 pub struct PciConfigEntryIterator<'a> {
83     entries: &'a [McfgEntry],
84     index: usize,
85 }
86 
87 impl Iterator for PciConfigEntryIterator<'_> {
88     type Item = PciConfigEntry;
89 
next(&mut self) -> Option<Self::Item>90     fn next(&mut self) -> Option<Self::Item> {
91         let entry = self.entries.get(self.index)?;
92         self.index += 1;
93 
94         Some(PciConfigEntry {
95             segment_group: entry.pci_segment_group,
96             bus_range: entry.bus_number_start..=entry.bus_number_end,
97             physical_address: entry.base_address as usize,
98         })
99     }
100 }
101 
102 #[repr(C, packed)]
103 pub struct Mcfg {
104     header: SdtHeader,
105     _reserved: u64,
106     // Followed by `n` entries with format `McfgEntry`
107 }
108 
109 /// ### Safety: Implementation properly represents a valid MCFG.
110 unsafe impl AcpiTable for Mcfg {
111     const SIGNATURE: Signature = Signature::MCFG;
112 
header(&self) -> &SdtHeader113     fn header(&self) -> &SdtHeader {
114         &self.header
115     }
116 }
117 
118 impl Mcfg {
119     /// Returns a slice containing each of the entries in the MCFG table. Where possible, `PlatformInfo.interrupt_model` should
120     /// be enumerated instead.
entries(&self) -> &[McfgEntry]121     pub fn entries(&self) -> &[McfgEntry] {
122         let length = self.header.length as usize - mem::size_of::<Mcfg>();
123 
124         // Intentionally round down in case length isn't an exact multiple of McfgEntry size
125         // (see rust-osdev/acpi#58)
126         let num_entries = length / mem::size_of::<McfgEntry>();
127 
128         unsafe {
129             let pointer = (self as *const Mcfg as *const u8).add(mem::size_of::<Mcfg>()) as *const McfgEntry;
130             slice::from_raw_parts(pointer, num_entries)
131         }
132     }
133 }
134 
135 impl core::fmt::Debug for Mcfg {
fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result136     fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
137         formatter.debug_struct("Mcfg").field("header", &self.header).field("entries", &self.entries()).finish()
138     }
139 }
140 
141 #[derive(Clone, Copy, Debug)]
142 #[repr(C, packed)]
143 pub struct McfgEntry {
144     pub base_address: u64,
145     pub pci_segment_group: u16,
146     pub bus_number_start: u8,
147     pub bus_number_end: u8,
148     _reserved: u32,
149 }
150