diff --git a/drivers/pcid/src/cfg_access/mod.rs b/drivers/pcid/src/cfg_access/mod.rs index 1abd29a02d..b23bdd4e73 100644 --- a/drivers/pcid/src/cfg_access/mod.rs +++ b/drivers/pcid/src/cfg_access/mod.rs @@ -404,8 +404,22 @@ impl Pcie { } } +/// PCIe configuration space is exactly 4096 bytes: valid offsets are +/// `0..=4095`. `bus_addr_offset_in_dwords` asserts this. A caller that runs off +/// the end — most easily an extended-capability walk (`pci_types` +/// `capabilities()`) following a chain whose next-pointer lands at or past the +/// boundary — would otherwise abort pcid with an "Invalid opcode fault" / +/// UNHANDLED EXCEPTION on boot. Guard the single access choke point: an +/// out-of-range read returns the PCI "no response" pattern (all ones, which +/// also terminates a capability walk cleanly) and an out-of-range write is a +/// no-op. +const PCIE_CONFIG_SPACE_LEN: u16 = 4096; + impl ConfigRegionAccess for Pcie { unsafe fn read(&self, address: PciAddress, offset: u16) -> u32 { + if offset >= PCIE_CONFIG_SPACE_LEN { + return 0xFFFF_FFFF; + } let _guard = self.lock.lock().unwrap(); match self.mmio_addr(address, offset) { @@ -415,6 +429,9 @@ impl ConfigRegionAccess for Pcie { } unsafe fn write(&self, address: PciAddress, offset: u16, value: u32) { + if offset >= PCIE_CONFIG_SPACE_LEN { + return; + } let _guard = self.lock.lock().unwrap(); match self.mmio_addr(address, offset) { diff --git a/drivers/pcid/src/scheme.rs b/drivers/pcid/src/scheme.rs index c748b7b46d..135db0006a 100644 --- a/drivers/pcid/src/scheme.rs +++ b/drivers/pcid/src/scheme.rs @@ -311,7 +311,18 @@ impl SchemeSync for PciScheme { // lower level only works with 4 byte reads and writes let unaligned = offset % 4; let start = offset - unaligned; - let end = offset + payload_len; + // PCIe extended config space is exactly 4096 bytes (valid + // offsets 0..=4095). A request whose offset+len runs past the + // end must be clamped here — otherwise the loop below issues a + // read/write at offset 4096, which trips the dword-offset + // assert in bus_addr_offset_in_dwords and aborts pcid (observed + // as an "Invalid opcode fault" / UNHANDLED EXCEPTION on boot + // when a consumer reads config near the boundary). Bytes past + // the boundary are left untouched in the caller's payload. + const PCIE_CONFIG_SPACE_LEN: u16 = 4096; + // saturating_add so a large payload_len cannot wrap u16 and + // defeat the clamp. + let end = offset.saturating_add(payload_len).min(PCIE_CONFIG_SPACE_LEN); let mut i = 0; while start + i < end { let mut bytes = unsafe { self.pcie.read(addr, start + i) }.to_le_bytes();