From 0370f8fdf3b366e5510c19678645bc97e26f4313 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=8B=B1=E6=B3=B0?= <2253457010@qq.com> Date: Mon, 14 Jul 2025 00:57:08 +0800 Subject: [PATCH] Update the init of pci device for LoongArch --- ostd/src/arch/loongarch/pci.rs | 82 ++++++++++++++++++++++++++++++- ostd/src/bus/pci/cfg_space.rs | 38 ++++++++++++-- ostd/src/bus/pci/common_device.rs | 8 ++- 3 files changed, 123 insertions(+), 5 deletions(-) diff --git a/ostd/src/arch/loongarch/pci.rs b/ostd/src/arch/loongarch/pci.rs index 4bd70cafb..9908539df 100644 --- a/ostd/src/arch/loongarch/pci.rs +++ b/ostd/src/arch/loongarch/pci.rs @@ -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 = 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 { + 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> = 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 { + MMIO_ALLOCATOR.get().unwrap().lock().allocate(layout) +} diff --git a/ostd/src/bus/pci/cfg_space.rs b/ostd/src/bus/pci/cfg_space.rs index a3dab7207..f1db4b4ff 100644 --- a/ostd/src/bus/pci/cfg_space.rs +++ b/ostd/src/bus/pci/cfg_space.rs @@ -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 { diff --git a/ostd/src/bus/pci/common_device.rs b/ostd/src/bus/pci/common_device.rs index 14c3cd0e8..41087081f 100644 --- a/ostd/src/bus/pci/common_device.rs +++ b/ostd/src/bus/pci/common_device.rs @@ -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) }