Fix discovery and access of PCIe (MMIO) configuration space

This commit is contained in:
Zejun Zhao 2025-04-28 14:36:33 +08:00 committed by Ruihan Li
parent 19f7eea68b
commit b20d8461fd
2 changed files with 40 additions and 26 deletions

View File

@ -34,12 +34,13 @@ pub(crate) unsafe fn late_init_on_bsp() {
// in the boot context of the BSP, with no timer-related operations having
// been performed.
unsafe { timer::init() };
let _ = pci::init();
// SAFETY:
// 1. All the system device memory have been removed from the builder.
// 2. RISC-V platforms do not have port I/O.
unsafe { crate::io::init(io_mem_builder) };
pci::init();
}
pub(crate) unsafe fn init_on_ap() {

View File

@ -6,58 +6,69 @@ use log::warn;
use spin::Once;
use super::boot::DEVICE_TREE;
use crate::{bus::pci::PciDeviceLocation, io::IoMem, mm::VmIoOnce, prelude::*, Error};
use crate::{
bus::pci::PciDeviceLocation,
io::{IoMem, IoMemAllocatorBuilder},
mm::{CachePolicy, PageFlags, VmIoOnce},
prelude::*,
Error,
};
static PCI_BASE_ADDR: Once<IoMem> = Once::new();
static PCI_ECAM_CFG_SPACE: Once<IoMem> = Once::new();
pub(crate) fn write32(location: &PciDeviceLocation, offset: u32, value: u32) -> Result<()> {
PCI_BASE_ADDR.get().ok_or(Error::IoError)?.write_once(
PCI_ECAM_CFG_SPACE.get().ok_or(Error::IoError)?.write_once(
(encode_as_address_offset(location) | (offset & 0xfc)) as usize,
&value,
)
}
pub(crate) fn read32(location: &PciDeviceLocation, offset: u32) -> Result<u32> {
PCI_BASE_ADDR
PCI_ECAM_CFG_SPACE
.get()
.ok_or(Error::IoError)?
.read_once((encode_as_address_offset(location) | (offset & 0xfc)) as usize)
}
pub(crate) fn has_pci_bus() -> bool {
PCI_BASE_ADDR.is_completed()
PCI_ECAM_CFG_SPACE.is_completed()
}
pub(crate) fn init() -> Result<()> {
let pci = DEVICE_TREE
pub(crate) fn init() {
// We follow the Linux's PCI device tree to obtain the register information
// about the PCI bus. See also the specification at
// <https://www.kernel.org/doc/Documentation/devicetree/bindings/pci/host-generic-pci.txt>.
//
// TODO: Support multiple PCIe segment groups instead of assuming only one
// PCIe segment group is in use.
let Some(pci) = DEVICE_TREE
.get()
.unwrap()
.find_node("/soc/pci")
.ok_or(Error::IoError)?;
let mut reg = pci.reg().ok_or(Error::IoError)?;
.find_compatible(&["pci-host-ecam-generic"])
else {
warn!("No generic PCI host controller node found in the device tree");
return;
};
let Some(mut reg) = pci.reg() else {
warn!("PCI node should have exactly one `reg` property, but found zero `reg`s");
return;
};
let Some(region) = reg.next() else {
warn!("PCI node should have exactly one `reg` property, but found zero `reg`s");
return Err(Error::IoError);
return;
};
if reg.next().is_some() {
warn!(
"PCI node should have exactly one `reg` property, but found {} `reg`s",
reg.count() + 2
);
return Err(Error::IoError);
return;
}
PCI_BASE_ADDR.call_once(|| {
IoMem::acquire(
(region.starting_address as usize)
..(region.starting_address as usize + region.size.unwrap()),
)
.unwrap()
});
Ok(())
let addr_start = region.starting_address as usize;
let addr_end = addr_start.checked_add(region.size.unwrap()).unwrap();
PCI_ECAM_CFG_SPACE.call_once(|| IoMem::acquire(addr_start..addr_end).unwrap());
}
pub(crate) const MSIX_DEFAULT_MSG_ADDR: u32 = 0x2400_0000;
@ -68,7 +79,9 @@ pub(crate) fn construct_remappable_msix_address(remapping_index: u32) -> u32 {
/// Encodes the bus, device, and function into an address offset in the PCI MMIO region.
fn encode_as_address_offset(location: &PciDeviceLocation) -> u32 {
((location.bus as u32) << 16)
| ((location.device as u32) << 11)
| ((location.function as u32) << 8)
// We only support ECAM here for RISC-V platforms. Offsets are from
// <https://www.kernel.org/doc/Documentation/devicetree/bindings/pci/host-generic-pci.txt>.
((location.bus as u32) << 20)
| ((location.device as u32) << 15)
| ((location.function as u32) << 12)
}