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