mcfg.rs 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. use crate::{sdt::SdtHeader, AcpiError, AcpiHandler, AcpiTable, AcpiTables};
  2. use alloc::vec::Vec;
  3. use core::{mem, slice};
  4. /// Describes a set of regions of physical memory used to access the PCIe configuration space. A
  5. /// region is created for each entry in the MCFG. Given the segment group, bus, device number, and
  6. /// function of a PCIe device, the `physical_address` method on this will give you the physical
  7. /// address of the start of that device function's configuration space (each function has 4096
  8. /// bytes of configuration space in PCIe).
  9. #[derive(Clone, Debug)]
  10. pub struct PciConfigRegions {
  11. regions: Vec<McfgEntry>,
  12. }
  13. impl PciConfigRegions {
  14. pub fn new<H>(tables: &AcpiTables<H>) -> Result<PciConfigRegions, AcpiError>
  15. where
  16. H: AcpiHandler,
  17. {
  18. let mcfg = unsafe {
  19. tables
  20. .get_sdt::<Mcfg>(crate::sdt::Signature::MCFG)?
  21. .ok_or(AcpiError::TableMissing(crate::sdt::Signature::MCFG))?
  22. };
  23. Ok(PciConfigRegions { regions: mcfg.entries().iter().map(|&entry| entry).collect() })
  24. }
  25. /// Get the physical address of the start of the configuration space for a given PCIe device
  26. /// function. Returns `None` if there isn't an entry in the MCFG that manages that device.
  27. pub fn physical_address(&self, segment_group_no: u16, bus: u8, device: u8, function: u8) -> Option<u64> {
  28. // First, find the memory region that handles this segment and bus. This method is fine
  29. // because there should only be one region that handles each segment group + bus
  30. // combination.
  31. let region = self.regions.iter().find(|region| {
  32. region.pci_segment_group == segment_group_no
  33. && (region.bus_number_start..=region.bus_number_end).contains(&bus)
  34. })?;
  35. Some(
  36. region.base_address
  37. + ((u64::from(bus - region.bus_number_start) << 20)
  38. | (u64::from(device) << 15)
  39. | (u64::from(function) << 12)),
  40. )
  41. }
  42. }
  43. #[repr(C, packed)]
  44. pub struct Mcfg {
  45. header: SdtHeader,
  46. _reserved: u64,
  47. // Followed by `n` entries with format `McfgEntry`
  48. }
  49. impl AcpiTable for Mcfg {
  50. fn header(&self) -> &SdtHeader {
  51. &self.header
  52. }
  53. }
  54. impl Mcfg {
  55. fn entries(&self) -> &[McfgEntry] {
  56. let length = self.header.length as usize - mem::size_of::<Mcfg>();
  57. // Intentionally round down in case length isn't an exact multiple of McfgEntry size
  58. // (see rust-osdev/acpi#58)
  59. let num_entries = length / mem::size_of::<McfgEntry>();
  60. unsafe {
  61. let pointer =
  62. (self as *const Mcfg as *const u8).offset(mem::size_of::<Mcfg>() as isize) as *const McfgEntry;
  63. slice::from_raw_parts(pointer, num_entries)
  64. }
  65. }
  66. }
  67. #[derive(Clone, Copy, Debug)]
  68. #[repr(C, packed)]
  69. pub struct McfgEntry {
  70. base_address: u64,
  71. pci_segment_group: u16,
  72. bus_number_start: u8,
  73. bus_number_end: u8,
  74. _reserved: u32,
  75. }