Fix discovery and access of PCIe (MMIO) configuration space
This commit is contained in:
parent
19f7eea68b
commit
b20d8461fd
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue