pcid: EOF config reads at the config-space boundary (256 fallback / 4096 ECAM)

The Handle::Config read handler never returned 0, so fs::read /
read_to_end on /scheme/pci/<addr>/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.
This commit is contained in:
Red Bear OS
2026-07-23 22:19:19 +09:00
parent f2fbf7ad83
commit 92bf05c20a
2 changed files with 20 additions and 5 deletions
+9 -4
View File
@@ -264,8 +264,7 @@ impl Pcie {
Ok(pcie) => pcie, Ok(pcie) => pcie,
Err(acpi_error) => match locate_ecam_dtb(Self::from_allocs) { Err(acpi_error) => match locate_ecam_dtb(Self::from_allocs) {
Ok(pcie) => pcie, Ok(pcie) => pcie,
Err(fdt_error) => { Err(fdt_error) => { // Not a warning: legacy PCI 3.0 I/O-port config access is a
// Not a warning: legacy PCI 3.0 I/O-port config access is a
// fully supported path (e.g. QEMU's default i440fx machine // fully supported path (e.g. QEMU's default i440fx machine
// has no MCFG). Report the fallback + how to get PCIe at // has no MCFG). Report the fallback + how to get PCIe at
// info level so it doesn't read as a boot problem. // 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( fn from_allocs(
allocs: PcieAllocs<'_>, allocs: PcieAllocs<'_>,
interrupt_map: Vec<InterruptMap>, interrupt_map: Vec<InterruptMap>,
@@ -297,8 +303,7 @@ impl Pcie {
) -> Result<Pcie, io::Error> { ) -> Result<Pcie, io::Error> {
let mut allocs = allocs let mut allocs = allocs
.0 .0
.iter() .iter() .filter_map(|desc| {
.filter_map(|desc| {
if desc.seg_group_num != 0 { if desc.seg_group_num != 0 {
let seg = desc.seg_group_num; let seg = desc.seg_group_num;
let start = desc.start_bus; let start = desc.start_bus;
+11 -1
View File
@@ -134,12 +134,13 @@ impl SchemeSync for PciScheme {
}) })
} }
fn fstat(&mut self, id: usize, stat: &mut syscall::Stat, _ctx: &CallerCtx) -> Result<()> { 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 handle = self.handles.get_mut(id)?;
let (len, mode) = match handle.inner { let (len, mode) = match handle.inner {
Handle::TopLevel { ref entries } => (entries.len(), MODE_DIR | 0o755), Handle::TopLevel { ref entries } => (entries.len(), MODE_DIR | 0o755),
Handle::Device => (DEVICE_CONTENTS.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::Access | Handle::Channel { .. } => (0, MODE_CHR | 0o600),
Handle::SchemeRoot => return Err(Error::new(EBADF)), Handle::SchemeRoot => return Err(Error::new(EBADF)),
}; };
@@ -155,6 +156,7 @@ impl SchemeSync for PciScheme {
_fcntl_flags: u32, _fcntl_flags: u32,
_ctx: &CallerCtx, _ctx: &CallerCtx,
) -> Result<usize> { ) -> Result<usize> {
let config_size = self.config_space_size();
let handle = self.handles.get_mut(id)?; let handle = self.handles.get_mut(id)?;
if handle.stat { if handle.stat {
@@ -165,7 +167,11 @@ impl SchemeSync for PciScheme {
Handle::TopLevel { .. } => Err(Error::new(EISDIR)), Handle::TopLevel { .. } => Err(Error::new(EISDIR)),
Handle::Device => Err(Error::new(EISDIR)), Handle::Device => Err(Error::new(EISDIR)),
Handle::Config { addr } => { Handle::Config { addr } => {
let size = config_size;
let offset = _offset as u16; let offset = _offset as u16;
if offset >= size {
return Ok(0);
}
let dword_offset = offset & !0x3; let dword_offset = offset & !0x3;
let byte_offset = (offset & 0x3) as usize; let byte_offset = (offset & 0x3) as usize;
let bytes_to_read = buf.len().min(4 - byte_offset); let bytes_to_read = buf.len().min(4 - byte_offset);
@@ -373,6 +379,10 @@ impl SchemeSync for PciScheme {
} }
impl PciScheme { impl PciScheme {
fn config_space_size(&self) -> u16 {
if self.pcie.has_ecam() { 4096 } else { 256 }
}
pub fn new(pcie: Pcie) -> Self { pub fn new(pcie: Pcie) -> Self {
Self { Self {
handles: HandleMap::new(), handles: HandleMap::new(),