mcfg.rs 5.0 KB

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