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:
@@ -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<InterruptMap>,
|
||||
@@ -297,8 +303,7 @@ impl Pcie {
|
||||
) -> Result<Pcie, io::Error> {
|
||||
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;
|
||||
|
||||
@@ -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<usize> {
|
||||
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(),
|
||||
|
||||
Reference in New Issue
Block a user