Get the PCI bus number range

This commit is contained in:
Ruihan Li 2026-01-18 11:04:10 +08:00 committed by Tate, Hongliang Tian
parent 5499436807
commit f055a387a7
5 changed files with 83 additions and 45 deletions

View File

@ -2,7 +2,7 @@
//! PCI bus access
use core::alloc::Layout;
use core::{alloc::Layout, ops::RangeInclusive};
use align_ext::AlignExt;
use fdt::node::FdtNode;
@ -39,11 +39,10 @@ fn encode_as_address_offset(location: &PciDeviceLocation) -> u32 {
| ((location.function as u32) << 12)
}
pub(crate) fn has_pci_bus() -> bool {
PCI_ECAM_CFG_SPACE.is_completed()
}
pub(crate) fn init() {
/// Initializes the platform-specific module for accessing the PCI configuration space.
///
/// Returns a range for the PCI bus number, or [`None`] if there is no PCI bus.
pub(crate) fn init() -> Option<RangeInclusive<u8>> {
// 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>.
@ -56,31 +55,56 @@ pub(crate) fn init() {
.find_compatible(&["pci-host-ecam-generic"])
else {
warn!("No generic PCI host controller node found in the device tree");
return;
return None;
};
let Some(mut reg) = pci.reg() else {
warn!("PCI node should have exactly one `reg` property, but found zero `reg`s");
return;
return None;
};
let Some(region) = reg.next() else {
warn!("PCI node should have exactly one `reg` property, but found zero `reg`s");
return;
return None;
};
if reg.next().is_some() {
warn!(
"PCI node should have exactly one `reg` property, but found {} `reg`s",
reg.count() + 2
);
return;
return None;
}
// Initialize the MMIO allocator
let bus_range = if let Some(prop) = pci.property("bus-range") {
if prop.value.len() != 8 || prop.value[0..3] != [0, 0, 0] || prop.value[4..7] != [0, 0, 0] {
warn!(
"PCI node should have a `bus-range` property with two bytes, but found `{:?}`",
prop.value
);
return None;
}
if prop.value[3] != 0 {
// TODO: We don't support this case because the base address corresponds to the first
// bus. Therefore, an offset must be applied to the bus value in `read32`/`write32`.
warn!(
"PCI node with a non-zero bus start `{}` is not supported yet",
prop.value[3]
);
return None;
}
Some(prop.value[3]..=prop.value[7])
} else {
// "bus-range: Optional property [..] If absent, defaults to <0 255> (i.e. all buses)."
Some(0..=255)
};
// Initialize the MMIO allocator.
init_mmio_allocator_from_fdt(&pci);
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());
bus_range
}
/// A simple MMIO allocator managing a linear region.

View File

@ -2,6 +2,8 @@
//! PCI bus access
use core::ops::RangeInclusive;
use log::warn;
use ostd::{Error, arch::boot::DEVICE_TREE, io::IoMem, mm::VmIoOnce};
use spin::Once;
@ -33,11 +35,10 @@ fn encode_as_address_offset(location: &PciDeviceLocation) -> u32 {
| ((location.function as u32) << 12)
}
pub(crate) fn has_pci_bus() -> bool {
PCI_ECAM_CFG_SPACE.is_completed()
}
pub(crate) fn init() {
/// Initializes the platform-specific module for accessing the PCI configuration space.
///
/// Returns a range for the PCI bus number, or [`None`] if there is no PCI bus.
pub(crate) fn init() -> Option<RangeInclusive<u8>> {
// 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>.
@ -50,28 +51,53 @@ pub(crate) fn init() {
.find_compatible(&["pci-host-ecam-generic"])
else {
warn!("No generic PCI host controller node found in the device tree");
return;
return None;
};
let Some(mut reg) = pci.reg() else {
warn!("PCI node should have exactly one `reg` property, but found zero `reg`s");
return;
return None;
};
let Some(region) = reg.next() else {
warn!("PCI node should have exactly one `reg` property, but found zero `reg`s");
return;
return None;
};
if reg.next().is_some() {
warn!(
"PCI node should have exactly one `reg` property, but found {} `reg`s",
reg.count() + 2
);
return;
return None;
}
let bus_range = if let Some(prop) = pci.property("bus-range") {
if prop.value.len() != 8 || prop.value[0..3] != [0, 0, 0] || prop.value[4..7] != [0, 0, 0] {
warn!(
"PCI node should have a `bus-range` property with two bytes, but found `{:?}`",
prop.value
);
return None;
}
if prop.value[3] != 0 {
// TODO: We don't support this case because the base address corresponds to the first
// bus. Therefore, an offset must be applied to the bus value in `read32`/`write32`.
warn!(
"PCI node with a non-zero bus start `{}` is not supported yet",
prop.value[3]
);
return None;
}
Some(prop.value[3]..=prop.value[7])
} else {
// "bus-range: Optional property [..] If absent, defaults to <0 255> (i.e. all buses)."
Some(0..=255)
};
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());
bus_range
}
pub(crate) const MSIX_DEFAULT_MSG_ADDR: u32 = 0x2400_0000;

View File

@ -2,6 +2,8 @@
//! PCI bus access
use core::ops::RangeInclusive;
use ostd::{
Error,
arch::device::io_port::{ReadWriteAccess, WriteOnlyAccess},
@ -45,17 +47,18 @@ fn encode_as_port(location: &PciDeviceLocation) -> u32 {
| (((location.function as u32) & 0b111) << 8)
}
pub(crate) fn has_pci_bus() -> bool {
true
}
pub(crate) fn init() {
/// Initializes the platform-specific module for accessing the PCI configuration space.
///
/// Returns a range for the PCI bus number, or [`None`] if there is no PCI bus.
pub(crate) fn init() -> Option<RangeInclusive<u8>> {
// We use `acquire_overlapping` to acquire the port at 0xCF8 because 0xCF9 may be used as a
// reset control register in the PIIX4. Although the two ports overlap in their I/O range, they
// serve completely different purposes. See
// <https://www.intel.com/Assets/PDF/datasheet/290562.pdf>.
PCI_ADDRESS_PORT.call_once(|| IoPort::acquire_overlapping(0xCF8).unwrap());
PCI_DATA_PORT.call_once(|| IoPort::acquire(0xCFC).unwrap());
Some(0..=255)
}
pub(crate) const MSIX_DEFAULT_MSG_ADDR: u32 = 0xFEE0_0000;

View File

@ -16,15 +16,6 @@ pub struct PciDeviceLocation {
}
impl PciDeviceLocation {
// TODO: Find a proper way to obtain the bus range. For example, if the PCI bus is identified
// from a device tree, this information can be obtained from the `bus-range` field (e.g.,
// `bus-range = <0x00 0x7f>`).
pub const MIN_BUS: u8 = 0;
#[cfg(not(target_arch = "loongarch64"))]
pub const MAX_BUS: u8 = 255;
#[cfg(target_arch = "loongarch64")]
pub const MAX_BUS: u8 = 127;
pub const MIN_DEVICE: u8 = 0;
pub const MAX_DEVICE: u8 = 31;

View File

@ -80,24 +80,18 @@ fn pci_init() -> Result<(), ComponentInitError> {
Ok(())
}
/// Checks if the system has a PCI bus.
pub fn has_pci_bus() -> bool {
crate::arch::has_pci_bus()
}
/// PCI bus instance
/// The PCI bus instance.
pub static PCI_BUS: Mutex<PciBus> = Mutex::new(PciBus::new());
fn init() {
crate::arch::init();
if !crate::arch::has_pci_bus() {
let Some(all_bus) = arch::init() else {
log::info!("no PCI bus was found");
return;
}
};
log::info!("initializing the PCI bus with bus numbers `{:?}`", all_bus);
let mut lock = PCI_BUS.lock();
let all_bus = PciDeviceLocation::MIN_BUS..=PciDeviceLocation::MAX_BUS;
let all_dev = PciDeviceLocation::MIN_DEVICE..=PciDeviceLocation::MAX_DEVICE;
let all_func = PciDeviceLocation::MIN_FUNCTION..=PciDeviceLocation::MAX_FUNCTION;