From 92bf05c20ae96f28a23efdcde298704987c52e90 Mon Sep 17 00:00:00 2001 From: Red Bear OS Date: Thu, 23 Jul 2026 22:19:19 +0900 Subject: [PATCH] pcid: EOF config reads at the config-space boundary (256 fallback / 4096 ECAM) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Handle::Config read handler never returned 0, so fs::read / read_to_end on /scheme/pci//config streamed dwords forever — any unbounded read of a config file hung. Found by the initfs driver-manager hang: propose_msix_vectors' unbounded config read never terminated, stalling the oneshot initfs service and the whole boot. - cfg_access: Pcie::has_ecam() reports ECAM availability. - scheme: config_space_size() = 4096 with ECAM, 256 in PCI 3.0 fallback; the read handler returns Ok(0) at the boundary and fstat advertises the correct size. --- drivers/pcid/src/cfg_access/mod.rs | 13 +++++++++---- drivers/pcid/src/scheme.rs | 12 +++++++++++- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/drivers/pcid/src/cfg_access/mod.rs b/drivers/pcid/src/cfg_access/mod.rs index b23bdd4e73..7fa3598e35 100644 --- a/drivers/pcid/src/cfg_access/mod.rs +++ b/drivers/pcid/src/cfg_access/mod.rs @@ -264,8 +264,7 @@ impl Pcie { Ok(pcie) => pcie, Err(acpi_error) => match locate_ecam_dtb(Self::from_allocs) { Ok(pcie) => pcie, - Err(fdt_error) => { - // Not a warning: legacy PCI 3.0 I/O-port config access is a + Err(fdt_error) => { // Not a warning: legacy PCI 3.0 I/O-port config access is a // fully supported path (e.g. QEMU's default i440fx machine // has no MCFG). Report the fallback + how to get PCIe at // info level so it doesn't read as a boot problem. @@ -290,6 +289,13 @@ impl Pcie { } } + /// True when at least one ECAM (PCIe extended config space) region is + /// mapped. Decides the per-device config file size: 4096 bytes with + /// ECAM, 256 bytes in PCI 3.0 I/O-port fallback mode. + pub fn has_ecam(&self) -> bool { + !self.allocs.is_empty() + } + fn from_allocs( allocs: PcieAllocs<'_>, interrupt_map: Vec, @@ -297,8 +303,7 @@ impl Pcie { ) -> Result { let mut allocs = allocs .0 - .iter() - .filter_map(|desc| { + .iter() .filter_map(|desc| { if desc.seg_group_num != 0 { let seg = desc.seg_group_num; let start = desc.start_bus; diff --git a/drivers/pcid/src/scheme.rs b/drivers/pcid/src/scheme.rs index 135db0006a..8dc7136e70 100644 --- a/drivers/pcid/src/scheme.rs +++ b/drivers/pcid/src/scheme.rs @@ -134,12 +134,13 @@ impl SchemeSync for PciScheme { }) } fn fstat(&mut self, id: usize, stat: &mut syscall::Stat, _ctx: &CallerCtx) -> Result<()> { + let config_size = self.config_space_size() as usize; let handle = self.handles.get_mut(id)?; let (len, mode) = match handle.inner { Handle::TopLevel { ref entries } => (entries.len(), MODE_DIR | 0o755), Handle::Device => (DEVICE_CONTENTS.len(), MODE_DIR | 0o755), - Handle::Config { .. } => (256, MODE_CHR | 0o600), + Handle::Config { .. } => (config_size, MODE_CHR | 0o600), Handle::Access | Handle::Channel { .. } => (0, MODE_CHR | 0o600), Handle::SchemeRoot => return Err(Error::new(EBADF)), }; @@ -155,6 +156,7 @@ impl SchemeSync for PciScheme { _fcntl_flags: u32, _ctx: &CallerCtx, ) -> Result { + let config_size = self.config_space_size(); let handle = self.handles.get_mut(id)?; if handle.stat { @@ -165,7 +167,11 @@ impl SchemeSync for PciScheme { Handle::TopLevel { .. } => Err(Error::new(EISDIR)), Handle::Device => Err(Error::new(EISDIR)), Handle::Config { addr } => { + let size = config_size; let offset = _offset as u16; + if offset >= size { + return Ok(0); + } let dword_offset = offset & !0x3; let byte_offset = (offset & 0x3) as usize; let bytes_to_read = buf.len().min(4 - byte_offset); @@ -373,6 +379,10 @@ impl SchemeSync for PciScheme { } impl PciScheme { + fn config_space_size(&self) -> u16 { + if self.pcie.has_ecam() { 4096 } else { 256 } + } + pub fn new(pcie: Pcie) -> Self { Self { handles: HandleMap::new(),