Skip to content

Commit 58918f3

Browse files
committed
x86_64/pci: enable support for PCIe enhanced configuration
1 parent be0a653 commit 58918f3

File tree

9 files changed

+180
-26
lines changed

9 files changed

+180
-26
lines changed

src/arch/x86_64/kernel/acpi.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ const SLP_EN: u16 = 1 << 13;
4444

4545
/// The "Multiple APIC Description Table" (MADT) preserved for get_apic_table().
4646
static MADT: OnceCell<AcpiTable<'_>> = OnceCell::new();
47+
48+
/// The MCFG table, to address PCI-E configuration space
49+
#[cfg(feature = "pci")]
50+
static MCFG: OnceCell<AcpiTable<'_>> = OnceCell::new();
51+
4752
/// The PM1A Control I/O Port for powering off the computer through ACPI.
4853
static PM1A_CNT_BLK: OnceCell<Port<u16>> = OnceCell::new();
4954
/// The Sleeping State Type code for powering off the computer through ACPI.
@@ -471,6 +476,11 @@ pub fn get_madt() -> Option<&'static AcpiTable<'static>> {
471476
MADT.get()
472477
}
473478

479+
#[cfg(feature = "pci")]
480+
pub fn get_mcfg_table() -> Option<&'static AcpiTable<'static>> {
481+
MCFG.get()
482+
}
483+
474484
pub fn poweroff() {
475485
if let (Some(mut pm1a_cnt_blk), Some(&slp_typa)) = (PM1A_CNT_BLK.get().cloned(), SLP_TYPA.get())
476486
{
@@ -546,6 +556,12 @@ pub fn init() {
546556
"SSDT at {table_physical_address:p} has invalid checksum"
547557
);
548558
parse_ssdt(table);
559+
} else if cfg!(feature = "pci") && table.header.signature() == "MCFG" {
560+
assert!(
561+
verify_checksum(table.header_start_address(), table.header.length as usize).is_ok(),
562+
"MCFG at {table_physical_address:p} has invalid checksum"
563+
);
564+
MCFG.set(table).unwrap();
549565
}
550566
}
551567
}

src/arch/x86_64/kernel/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -166,14 +166,14 @@ pub fn boot_processor_init() {
166166
interrupts::install();
167167
systemtime::init();
168168

169-
if is_uhyve_with_pci() || !is_uhyve() {
170-
#[cfg(feature = "pci")]
171-
pci::init();
172-
}
173169
if !env::is_uhyve() {
174170
#[cfg(feature = "acpi")]
175171
acpi::init();
176172
}
173+
if is_uhyve_with_pci() || !is_uhyve() {
174+
#[cfg(feature = "pci")]
175+
pci::init();
176+
}
177177

178178
apic::init();
179179
scheduler::install_timer_handler();

src/arch/x86_64/kernel/pci.rs

Lines changed: 143 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,37 @@ const CONFIG_ADDRESS: Port<u32> = Port::new(0xcf8);
1212
const CONFIG_DATA: Port<u32> = Port::new(0xcfc);
1313

1414
#[derive(Debug, Copy, Clone)]
15-
pub(crate) struct PciConfigRegion;
15+
struct PciConfigRegion;
1616

1717
impl PciConfigRegion {
1818
pub const fn new() -> Self {
1919
Self {}
2020
}
2121
}
2222

23+
#[derive(Debug, Copy, Clone)]
24+
pub enum PciConfigAccess {
25+
PciConfigRegion(PciConfigRegion),
26+
#[cfg(feature = "acpi")]
27+
PcieConfigRegion(pcie::McfgTableEntry),
28+
}
29+
30+
impl ConfigRegionAccess for PciConfigAccess {
31+
unsafe fn read(&self, address: PciAddress, offset: u16) -> u32 {
32+
match self {
33+
PciConfigAccess::PciConfigRegion(entry) => entry.read(address, offset),
34+
PciConfigAccess::PcieConfigRegion(entry) => entry.read(address, offset),
35+
}
36+
}
37+
38+
unsafe fn write(&self, address: PciAddress, offset: u16, value: u32) {
39+
match self {
40+
PciConfigAccess::PciConfigRegion(entry) => entry.write(address, offset, value),
41+
PciConfigAccess::PcieConfigRegion(entry) => entry.write(address, offset, value),
42+
}
43+
}
44+
}
45+
2346
impl ConfigRegionAccess for PciConfigRegion {
2447
#[inline]
2548
unsafe fn read(&self, pci_addr: PciAddress, register: u16) -> u32 {
@@ -59,20 +82,135 @@ impl ConfigRegionAccess for PciConfigRegion {
5982
pub(crate) fn init() {
6083
debug!("Scanning PCI Busses 0 to {}", PCI_MAX_BUS_NUMBER - 1);
6184

85+
#[cfg(feature = "acpi")]
86+
if pcie::init_pcie() { return; }
87+
88+
enumerate_devices(0, PCI_MAX_BUS_NUMBER, PciConfigAccess::PciConfigRegion(PciConfigRegion::new()))
89+
}
90+
91+
fn enumerate_devices(bus_start: u8, bus_end: u8, access: PciConfigAccess) {
6292
// Hermit only uses PCI for network devices.
6393
// Therefore, multifunction devices as well as additional bridges are not scanned.
6494
// We also limit scanning to the first 32 buses.
65-
let pci_config = PciConfigRegion::new();
66-
for bus in 0..PCI_MAX_BUS_NUMBER {
95+
for bus in bus_start..bus_end {
6796
for device in 0..PCI_MAX_DEVICE_NUMBER {
6897
let pci_address = PciAddress::new(0, bus, device, 0);
6998
let header = PciHeader::new(pci_address);
7099

71-
let (device_id, vendor_id) = header.id(pci_config);
100+
let (device_id, vendor_id) = header.id(access);
72101
if device_id != u16::MAX && vendor_id != u16::MAX {
73-
let device = PciDevice::new(pci_address, pci_config);
102+
let device = PciDevice::new(pci_address, access);
74103
PCI_DEVICES.with(|pci_devices| pci_devices.unwrap().push(device));
75104
}
76105
}
77106
}
78107
}
108+
109+
#[cfg(feature = "acpi")]
110+
mod pcie {
111+
use core::ptr;
112+
use pci_types::{ConfigRegionAccess, PciAddress};
113+
use memory_addresses::{PhysAddr, VirtAddr};
114+
use super::{PciConfigAccess, PCI_MAX_BUS_NUMBER};
115+
use crate::env;
116+
use crate::env::kernel::acpi;
117+
118+
pub fn init_pcie() -> bool {
119+
let Some(table) = acpi::get_mcfg_table() else { return false; };
120+
121+
let mut start_addr: *const McfgTableEntry = core::ptr::with_exposed_provenance(table.table_start_address() + 8);
122+
let end_addr: *const McfgTableEntry = core::ptr::with_exposed_provenance(table.table_end_address() + 8);
123+
124+
if start_addr == end_addr {
125+
return false;
126+
}
127+
128+
while start_addr < end_addr {
129+
unsafe {
130+
let read = ptr::read_unaligned(start_addr);
131+
init_pcie_bus(read);
132+
start_addr = start_addr.add(1);
133+
}
134+
}
135+
136+
true
137+
}
138+
139+
#[derive(Debug)]
140+
#[repr(C)]
141+
struct PcieDeviceConfig {
142+
vendor_id: u16,
143+
device_id: u16,
144+
_reserved: [u8; 4096 - 8]
145+
}
146+
147+
#[derive(Debug, Copy, Clone)]
148+
#[repr(C)]
149+
pub(crate) struct McfgTableEntry {
150+
pub base_address: u64,
151+
pub pci_segment_number: u16,
152+
pub start_pci_bus: u8,
153+
pub end_pci_bus: u8,
154+
_reserved: u32
155+
}
156+
157+
impl McfgTableEntry {
158+
pub fn pci_config_space_address(&self, bus_number: u8, device: u8, function: u8) -> PhysAddr {
159+
PhysAddr::new(
160+
self.base_address +
161+
((bus_number as u64) << 20) |
162+
(((device as u64) & 0x1f) << 15) |
163+
(((function as u64) & 0x7) << 12)
164+
)
165+
}
166+
}
167+
168+
#[derive(Debug)]
169+
#[cfg(feature = "pci")]
170+
struct McfgTable(alloc::vec::Vec<McfgTableEntry>);
171+
172+
impl PcieDeviceConfig {
173+
fn get<'a>(physical_address: PhysAddr) -> &'a Self {
174+
assert!(env::is_uefi());
175+
176+
// For UEFI Systems, the tables are already mapped so we only need to return a proper reference to the table
177+
let allocated_virtual_address = VirtAddr::new(physical_address.as_u64());
178+
let ptr: *const PcieDeviceConfig = allocated_virtual_address.as_ptr();
179+
180+
unsafe { ptr.as_ref().unwrap() }
181+
}
182+
}
183+
184+
impl ConfigRegionAccess for McfgTableEntry {
185+
unsafe fn read(&self, address: PciAddress, offset: u16) -> u32 {
186+
assert_eq!(address.segment(), self.pci_segment_number);
187+
assert!(address.bus() >= self.start_pci_bus);
188+
assert!(address.bus() <= self.end_pci_bus);
189+
190+
let ptr = self.pci_config_space_address(address.bus(), address.device(), address.function()) + offset as u64;
191+
let ptr = ptr.as_usize() as *const u32;
192+
193+
unsafe { *ptr }
194+
}
195+
196+
unsafe fn write(&self, address: PciAddress, offset: u16, value: u32) {
197+
assert_eq!(address.segment(), self.pci_segment_number);
198+
assert!(address.bus() >= self.start_pci_bus);
199+
assert!(address.bus() <= self.end_pci_bus);
200+
201+
let ptr = self.pci_config_space_address(address.bus(), address.device(), address.function()) + offset as u64;
202+
let ptr = ptr.as_usize() as *mut u32;
203+
204+
unsafe { *ptr = value; }
205+
}
206+
}
207+
208+
fn init_pcie_bus(bus_entry: McfgTableEntry) {
209+
if bus_entry.start_pci_bus > PCI_MAX_BUS_NUMBER {
210+
return;
211+
}
212+
213+
let end = if bus_entry.end_pci_bus > PCI_MAX_BUS_NUMBER { PCI_MAX_BUS_NUMBER } else { bus_entry.end_pci_bus };
214+
super::enumerate_devices(bus_entry.start_pci_bus, end, PciConfigAccess::PcieConfigRegion(bus_entry));
215+
}
216+
}

src/drivers/fs/virtio_pci.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use alloc::vec::Vec;
22

33
use volatile::VolatileRef;
44

5-
use crate::arch::pci::PciConfigRegion;
5+
use crate::arch::pci::PciConfigAccess;
66
use crate::drivers::fs::virtio_fs::{FsDevCfg, VirtioFsDriver};
77
use crate::drivers::pci::PciDevice;
88
use crate::drivers::virtio::error::{self, VirtioError};
@@ -26,7 +26,7 @@ impl VirtioFsDriver {
2626
/// configuration structures and moving them into the struct.
2727
pub fn new(
2828
caps_coll: UniCapsColl,
29-
device: &PciDevice<PciConfigRegion>,
29+
device: &PciDevice<PciConfigAccess>,
3030
) -> Result<Self, error::VirtioFsError> {
3131
let device_id = device.device_id();
3232

@@ -54,7 +54,7 @@ impl VirtioFsDriver {
5454
}
5555

5656
/// Initializes virtio filesystem device
57-
pub fn init(device: &PciDevice<PciConfigRegion>) -> Result<VirtioFsDriver, VirtioError> {
57+
pub fn init(device: &PciDevice<PciConfigAccess>) -> Result<VirtioFsDriver, VirtioError> {
5858
let mut drv = match pci::map_caps(device) {
5959
Ok(caps) => match VirtioFsDriver::new(caps, device) {
6060
Ok(driver) => driver,

src/drivers/net/rtl8139.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use pci_types::{Bar, CommandRegister, InterruptLine, MAX_BARS};
1010
use x86_64::instructions::port::Port;
1111

1212
use crate::arch::kernel::interrupts::*;
13-
use crate::arch::pci::PciConfigRegion;
13+
use crate::arch::pci::PciConfigAccess;
1414
use crate::drivers::Driver;
1515
use crate::drivers::error::DriverError;
1616
use crate::drivers::net::{NetworkDriver, mtu};
@@ -423,7 +423,7 @@ impl Drop for RTL8139Driver {
423423
}
424424

425425
pub(crate) fn init_device(
426-
device: &PciDevice<PciConfigRegion>,
426+
device: &PciDevice<PciConfigAccess>,
427427
) -> Result<RTL8139Driver, DriverError> {
428428
let irq = device.get_irq().unwrap();
429429
let mut iobase: Option<u32> = None;

src/drivers/net/virtio/pci.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use smoltcp::phy::ChecksumCapabilities;
77
use volatile::VolatileRef;
88

99
use super::{Init, Uninit};
10-
use crate::arch::pci::PciConfigRegion;
10+
use crate::arch::pci::PciConfigAccess;
1111
use crate::drivers::net::virtio::{NetDevCfg, VirtioNetDriver};
1212
use crate::drivers::pci::PciDevice;
1313
use crate::drivers::virtio::error::{self, VirtioError};
@@ -32,7 +32,7 @@ impl VirtioNetDriver<Uninit> {
3232
/// configuration structures and moving them into the struct.
3333
pub(crate) fn new(
3434
caps_coll: UniCapsColl,
35-
device: &PciDevice<PciConfigRegion>,
35+
device: &PciDevice<PciConfigAccess>,
3636
) -> Result<Self, error::VirtioNetError> {
3737
let device_id = device.device_id();
3838
let UniCapsColl {
@@ -69,7 +69,7 @@ impl VirtioNetDriver<Uninit> {
6969
/// Returns a driver instance of
7070
/// [VirtioNetDriver](structs.virtionetdriver.html) or an [VirtioError](enums.virtioerror.html).
7171
pub(crate) fn init(
72-
device: &PciDevice<PciConfigRegion>,
72+
device: &PciDevice<PciConfigAccess>,
7373
) -> Result<VirtioNetDriver<Init>, VirtioError> {
7474
// enable bus master mode
7575
device.set_command(CommandRegister::BUS_MASTER_ENABLE);

src/drivers/pci.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use pci_types::{
1616
InterruptPin, MAX_BARS, PciAddress, PciHeader, StatusRegister, VendorId,
1717
};
1818

19-
use crate::arch::pci::PciConfigRegion;
19+
use crate::arch::pci::PciConfigAccess;
2020
#[cfg(feature = "fuse")]
2121
use crate::drivers::fs::virtio_fs::VirtioFsDriver;
2222
#[cfg(any(feature = "tcp", feature = "udp"))]
@@ -53,7 +53,7 @@ use crate::drivers::{Driver, InterruptHandlerQueue};
5353
use crate::env;
5454
use crate::init_cell::InitCell;
5555

56-
pub(crate) static PCI_DEVICES: InitCell<Vec<PciDevice<PciConfigRegion>>> =
56+
pub(crate) static PCI_DEVICES: InitCell<Vec<PciDevice<PciConfigAccess>>> =
5757
InitCell::new(Vec::new());
5858
static PCI_DRIVERS: InitCell<Vec<PciDriver>> = InitCell::new(Vec::new());
5959

src/drivers/virtio/transport/pci.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use volatile::access::ReadOnly;
2020
use volatile::{VolatilePtr, VolatileRef};
2121

2222
use crate::arch::memory_barrier;
23-
use crate::arch::pci::PciConfigRegion;
23+
use crate::arch::pci::PciConfigAccess;
2424
use crate::drivers::error::DriverError;
2525
#[cfg(feature = "fuse")]
2626
use crate::drivers::fs::virtio_fs::VirtioFsDriver;
@@ -675,7 +675,7 @@ impl PciBar {
675675
///
676676
/// Returns ONLY Virtio specific capabilities, which allow to locate the actual capability
677677
/// structures inside the memory areas, indicated by the BaseAddressRegisters (BAR's).
678-
fn read_caps(device: &PciDevice<PciConfigRegion>) -> Result<Vec<PciCap>, PciError> {
678+
fn read_caps(device: &PciDevice<PciConfigAccess>) -> Result<Vec<PciCap>, PciError> {
679679
let device_id = device.device_id();
680680

681681
let capabilities = device
@@ -706,7 +706,7 @@ fn read_caps(device: &PciDevice<PciConfigRegion>) -> Result<Vec<PciCap>, PciErro
706706
}
707707
}
708708

709-
pub(crate) fn map_caps(device: &PciDevice<PciConfigRegion>) -> Result<UniCapsColl, VirtioError> {
709+
pub(crate) fn map_caps(device: &PciDevice<PciConfigAccess>) -> Result<UniCapsColl, VirtioError> {
710710
let device_id = device.device_id();
711711

712712
// In case caplist pointer is not used, abort as it is essential
@@ -790,7 +790,7 @@ pub(crate) fn map_caps(device: &PciDevice<PciConfigRegion>) -> Result<UniCapsCol
790790
/// driver with a [`PciDevice<PciConfigRegion>`] reference, allowing access to the capabilities
791791
/// list of the given device through [map_caps].
792792
pub(crate) fn init_device(
793-
device: &PciDevice<PciConfigRegion>,
793+
device: &PciDevice<PciConfigAccess>,
794794
) -> Result<VirtioDriver, DriverError> {
795795
let device_id = device.device_id();
796796

src/drivers/vsock/pci.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::arch::pci::PciConfigRegion;
1+
use crate::arch::pci::PciConfigAccess;
22
use crate::drivers::pci::PciDevice;
33
use crate::drivers::virtio::error::{self, VirtioError};
44
use crate::drivers::virtio::transport::pci;
@@ -31,7 +31,7 @@ impl VirtioVsockDriver {
3131
/// configuration structures and moving them into the struct.
3232
pub fn new(
3333
caps_coll: UniCapsColl,
34-
device: &PciDevice<PciConfigRegion>,
34+
device: &PciDevice<PciConfigAccess>,
3535
) -> Result<Self, error::VirtioVsockError> {
3636
let device_id = device.device_id();
3737

@@ -64,7 +64,7 @@ impl VirtioVsockDriver {
6464
///
6565
/// Returns a driver instance of VirtioVsockDriver.
6666
pub(crate) fn init(
67-
device: &PciDevice<PciConfigRegion>,
67+
device: &PciDevice<PciConfigAccess>,
6868
) -> Result<VirtioVsockDriver, VirtioError> {
6969
let mut drv = match pci::map_caps(device) {
7070
Ok(caps) => match VirtioVsockDriver::new(caps, device) {

0 commit comments

Comments
 (0)