Update the init of pci device for LoongArch

This commit is contained in:
王英泰 2025-07-14 00:57:08 +08:00 committed by Tate, Hongliang Tian
parent 3391863312
commit 0370f8fdf3
3 changed files with 123 additions and 5 deletions

View File

@ -2,10 +2,16 @@
//! PCI bus access
use core::alloc::Layout;
use align_ext::AlignExt;
use fdt::node::FdtNode;
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, mm::VmIoOnce, prelude::*, sync::SpinLock, Error,
};
static PCI_IO_MEM: Once<IoMem> = Once::new();
@ -64,6 +70,9 @@ pub(crate) fn init() -> Result<()> {
return Err(Error::IoError);
}
// Initialize the MMIO allocator
init_mmio_allocator_from_fdt(&pci);
PCI_IO_MEM.call_once(|| unsafe {
IoMem::acquire(
(region.starting_address as usize)
@ -80,3 +89,74 @@ pub(crate) const MSIX_DEFAULT_MSG_ADDR: u32 = 0x2ff0_0000;
pub(crate) fn construct_remappable_msix_address(remapping_index: u32) -> u32 {
unimplemented!()
}
/// A simple MMIO allocator managing a linear region.
///
/// In loongarch, the starting address of the memory bar of the PCI device
/// needs to be allocated within the specified range
struct MmioAllocator {
base: Paddr,
size: Paddr,
offset: Paddr,
}
impl MmioAllocator {
/// Creates a new MMIO allocator with a given base and size.
const fn new(base: Paddr, size: Paddr) -> Self {
MmioAllocator {
base,
size,
offset: 0,
}
}
/// Allocates a physical address range with the specified alignment and size.
fn allocate(&mut self, layout: Layout) -> Option<Paddr> {
let align = layout.align();
let size = layout.size();
let current = self.base + self.offset;
let aligned = current.align_up(align);
let aligned_offset = aligned - self.base;
if aligned_offset + size > self.size {
return None;
}
self.offset = aligned_offset + size;
Some(aligned)
}
}
static MMIO_ALLOCATOR: Once<SpinLock<MmioAllocator>> = Once::new();
/// Initializes the MMIO allocator from the PCIe node's "ranges" property.
fn init_mmio_allocator_from_fdt(node: &FdtNode) {
let ranges = node
.property("ranges")
.expect("Missing 'ranges' property in PCIe node");
let data = ranges.value;
let entry_size = 7 * 4; // Each entry is 7 x u32 = 28 bytes
let mut i = 0;
while i + entry_size <= data.len() {
let pci_space = u32::from_be_bytes(data[i..i + 4].try_into().unwrap());
let pci_addr = u64::from_be_bytes(data[i + 4..i + 12].try_into().unwrap());
let cpu_addr = u64::from_be_bytes(data[i + 12..i + 20].try_into().unwrap());
let size = u64::from_be_bytes(data[i + 20..i + 28].try_into().unwrap());
// Only initialize with memory-type region
if (pci_space >> 24) == 0x2 {
MMIO_ALLOCATOR
.call_once(|| SpinLock::new(MmioAllocator::new(cpu_addr as usize, size as usize)));
break;
}
i += entry_size;
}
}
/// Allocates an MMIO address range using the global allocator.
pub(crate) fn alloc_mmio(layout: Layout) -> Option<Paddr> {
MMIO_ALLOCATOR.get().unwrap().lock().allocate(layout)
}

View File

@ -166,8 +166,12 @@ impl Bar {
return Err(Error::InvalidArgs);
}
// Get the original value first, then write all 1 to the register to get the length
let raw = location.read32(index as u16 * 4 + PciDeviceCommonCfgOffset::Bar0 as u16);
if raw == 0 {
let offset = index as u16 * 4 + PciDeviceCommonCfgOffset::Bar0 as u16;
let raw = location.read32(offset);
location.write32(offset, !0);
let len_encoded = location.read32(offset);
location.write32(offset, raw);
if len_encoded == 0 {
// no BAR
return Err(Error::InvalidArgs);
}
@ -243,7 +247,7 @@ impl MemoryBar {
location.write32(offset, raw);
let mut address_length = AddrLen::Bits32;
// base address, it may be bit64 or bit32
let base: u64 = match (raw & 0b110) >> 1 {
let raw_base: u64 = match (raw & 0b110) >> 1 {
// bits32
0 => (raw & !0xF) as u64,
// bits64
@ -257,6 +261,34 @@ impl MemoryBar {
};
// length
let size = (!(len_encoded & !0xF)).wrapping_add(1);
// Some architectures may not initialize PCI devices at all. Even in x86-64 systems, the
// BIOS may leave some devices uninitialized. If support is available, we can allocate the
// region ourselves. Otherwise, if the BAR is not properly initialized, we will ignore it
// and return an error.
#[cfg(not(target_arch = "loongarch64"))]
let base = if raw_base == 0 {
return Err(Error::InvalidArgs);
} else {
raw_base
};
// In LoongArch, the BAR base address needs to be allocated manually.
#[cfg(target_arch = "loongarch64")]
let base = {
use core::alloc::Layout;
crate::arch::pci::alloc_mmio(
Layout::from_size_align(size as usize, size as usize).unwrap(),
)
.unwrap() as u64
};
if address_length == AddrLen::Bits64 {
location.write32(offset, base as u32);
location.write32(offset + 4, (base >> 32) as u32);
} else {
location.write32(offset, base as u32);
}
let prefetchable = raw & 0b1000 != 0;
// The BAR is located in I/O memory region
Ok(MemoryBar {

View File

@ -72,13 +72,19 @@ impl PciCommonDevice {
let capabilities = Vec::new();
let device_id = PciDeviceId::new(location);
let bar_manager = BarManager::new(location);
let bar_manager = BarManager {
bars: [const { None }; 6],
};
let mut device = Self {
device_id,
location,
bar_manager,
capabilities,
};
device.set_command(
device.command() | Command::MEMORY_SPACE | Command::BUS_MASTER | Command::IO_SPACE,
);
device.bar_manager = BarManager::new(location);
device.capabilities = Capability::device_capabilities(&mut device);
Some(device)
}