From a3314c643284a45bc541eae7162984f1a78149ca Mon Sep 17 00:00:00 2001 From: Ruihan Li Date: Thu, 15 Jan 2026 14:33:50 +0800 Subject: [PATCH] Ignore high bits in PCI BAR sizes --- kernel/comps/pci/src/cfg_space.rs | 46 ++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/kernel/comps/pci/src/cfg_space.rs b/kernel/comps/pci/src/cfg_space.rs index 3daf0b16f..0eb6f6177 100644 --- a/kernel/comps/pci/src/cfg_space.rs +++ b/kernel/comps/pci/src/cfg_space.rs @@ -372,14 +372,12 @@ impl MemoryBar { } }; - // "Size calculation can be done from the 32 bit value read by first clearing encoding - // information bits (bits 1:0 for I/O, bits 3:0 for memory), inverting all 32 bits (logical - // NOT), then incrementing by 1." - let size = !(size_encoded64 & !0xF) + 1; + // Decode the BAR's size. + let size = decode_size(size_encoded64, BarKind::Memory); // Restore the original base address. #[cfg(not(target_arch = "loongarch64"))] - let base = raw64 & !0xF; + let base = raw64 & MEMORY_ADDRESS_MASK; // In LoongArch, the BAR base address needs to be allocated manually. #[cfg(target_arch = "loongarch64")] let base = { @@ -492,13 +490,11 @@ impl IoBar { location.write32(offset, !0); let size_encoded = location.read32(offset); - // "Size calculation can be done from the 32 bit value read by first clearing encoding - // information bits (bits 1:0 for I/O, bits 3:0 for memory), inverting all 32 bits (logical - // NOT), then incrementing by 1." - let size = !(size_encoded & !0x3) + 1; + // Decode the BAR's size. + let size = decode_size(size_encoded as u64, BarKind::Io) as u32; // Restore the original base address. - let base = raw & !0x3; + let base = raw & IO_ADDRESS_MASK; location.write32(offset, base); // FIXME: As with the memory BAR check, we assume that a zero base address means that the @@ -517,3 +513,33 @@ impl IoBar { Ok(Self { base, size }) } } + +/// The kind of a BAR (memory or I/O). +enum BarKind { + Memory, + Io, +} + +const MEMORY_ADDRESS_MASK: u64 = !0xF; +const IO_ADDRESS_MASK: u32 = !0x3; + +/// Decodes a BAR's size from the encoded value. +fn decode_size(size_encoded: u64, kind: BarKind) -> u64 { + let mask = match kind { + BarKind::Memory => MEMORY_ADDRESS_MASK, + BarKind::Io => IO_ADDRESS_MASK as u64, + }; + + // "Size calculation can be done from the 32 bit value read by first clearing encoding + // information bits (bits 1:0 for I/O, bits 3:0 for memory), inverting all 32 bits (logical + // NOT), then incrementing by 1. [..] 64-bit (memory) Base Address registers can be handled the + // same, [..]" + // + // However, some devices may hardwire the high bits of the address as zero. In that case, the + // approach outlined in the PCI specification quoted above can result in a size of something + // like `0xfffffc0000100000`. As a result, after clearing information bits, we use the least + // significant bit for the size. This approach is also used in Linux. For more details, see + // . + let size_masked = size_encoded & mask; + size_masked & !(size_masked - 1) +}