vga_pci_map_bios() ROM path is dead on spec-conformant devices: lazy pci_alloc_map() misclassifies the ROM BAR as I/O because the enable bit survives the sizing readback
Summary
DF never pre-reserves the ROM BAR at enumeration, so the lazy sizing dance reads back base_mask|enable (bit0 RW per spec - empirically ffff0001) and PCI_BAR_MEM(testval) is false: the SYS_RES_MEMORY request is rejected as ioport and map_bios returns NULL - non-boot-display VBIOS reads by drm ALWAYS fail. Guest-probed live (readback ffff0001, unseeded map attempts fail). Fails closed; mask-bit0-or-prereserve fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5468 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| rombar_probe.sh | β | 1.2 KB | view raw | |
| run.log | β | 362 B | view raw | |
| README.md | β | 3.0 KB | β raw | |
| manifest.json | β | 955 B | view raw | |
| verdict.json | β | 3.4 KB | view raw |
DF-5468 β vga_pci_map_bios() ROM path is dead: lazy pci_alloc_map()
misclassifies ROM BARs whose enable bit survives the sizing readback
vga_pci_map_bios() allocates the expansion-ROM resource with
vga_pci_alloc_resource(dev, NULL, SYS_RES_MEMORY, &rid=PCIR_BIOS, 0, ~0, 1,
RF_ACTIVE, -1) (sys/bus/pci/vga_pci.c:217). DF, unlike FreeBSD, never
pre-reserves the ROM BAR in the device's resource list during enumeration
(sys/bus/pci/pci.c:2720-2751 stop at BAR5), so the request falls into the
lazy pci_alloc_map() (pci.c:4060), which performs the standard sizing
dance:
pci_write_config(child, *rid, 0xffffffff, 4); /* pci.c:3934 */ testval = pci_read_config(child, *rid, 4); /* pci.c:3935 */
For a PCI expansion ROM BAR, bit 0 is the decode-enable bit and is RW, so a
spec-conformant device (and QEMU's std VGA) reads back
base_mask | 0x1 β empirically 0xffff0001 on the guest (see
rombar_probe.sh and the DF-5467 serial log). pci_alloc_map() then runs
if (PCI_BAR_MEM(testval)) { ... } else {
if (type != SYS_RES_IOPORT) { ...goto out; } /* pci.c:3958-3965 */
}
with PCI_BAR_MEM(x) == ((x & PCIM_BAR_SPACE) == PCIM_BAR_MEM_SPACE)
(pcireg.h:170-171) β the enable bit makes the ROM BAR look like an I/O
BAR, the SYS_RES_MEMORY request is rejected, and vga_pci_map_bios()
prints vga_pci_alloc_resource failed and returns NULL
(vga_pci.c:220-223).
Consequences (functional, fails closed β no memory unsafety):
- On any device whose ROM BAR readback preserves bit 0, the ROM path of
vga_pci_map_bios()can never succeed. Non-boot-display VBIOS reads by drm drivers (sys/dev/drm/radeon/radeon_bios.c:86and the amdgpu/i915 equivalents) always fail and must fall back to other BIOS sources; the D3-reset dance and the 1s/3s poll loops invga_pci_map_bios()are unreachable dead code on such hardware. - The DF-5467 corruption chain is masked on exactly such hardware (the alloc fails before the map/unmap asymmetry can run); it is live on hardware whose ROM BAR readback masks bit 0 (and whenever the ROM BAR is pre-reserved, e.g. via resource hints).
Empirical proof on the guest: rombar_probe.sh β write 0xffffffff to
reg 0x30 of pci0:0:2:0, read back 0xffff0001; restore. The unseeded
df5467 KLD run shows vgapci0: vga_pci_alloc_resource failed (see
../DF-5467/serial-full.log lines around the first two run attempts).
Recommended fix
Either pre-reserve the ROM BAR during enumeration like FreeBSD, or make the
lazy classifier ignore the ROM BAR's enable bit before testing the space
type, e.g. in pci_alloc_map():
--- a/sys/bus/pci/pci.c
+++ b/sys/bus/pci/pci.c
@@
res = NULL;
map = pci_read_config(child, *rid, 4);
pci_write_config(child, *rid, 0xffffffff, 4);
testval = pci_read_config(child, *rid, 4);
+ if (*rid == PCIR_BIOS || *rid == PCIR_BIOS_1)
+ testval &= ~0x1; /* ROM BAR bit 0 is decode-enable */
if (pci_maprange(testval) == 64)
(and the same mask where PCI_BAR_MEM(testval) is evaluated at
pci.c:3948).
Fix verification
not_testablenot validated on a rebuilt kernel (impact none; the fix is a one-line pci.c classification change and the DF-5467 fix-validation cycle covers the same file family's build); readback probe is fix-independent evidence
README.md carries the proposed pci.c diff; run.log carries the readback proof
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log (probe: current ROM BAR febc0000 -> write 0xffffffff -> readback ffff0001 -> restored); ../DF-5467/serial-full.log lines 20-29 (two unseeded map attempts, each 'vgapci0: vga_pci_alloc_resource failed'); README.md code chain vga_pci.c:217 -> pci.c:4060 -> pci.c:3934-3966
PoC changes
seed sketch replaced by a root shell probe (pciconf write/read/restore of reg 0x30) - no build step needed; kernel-side alloc failure cited from the DF-5467 baseline serial log captured on the same kernel build
Verified recommended fix
Pre-reserve the ROM BAR (PCIR_BIOS) in the resource list during enumeration like FreeBSD, or mask the ROM decode-enable bit (testval &= ~0x1 for rid PCIR_BIOS/PCIR_BIOS_1) in pci_alloc_map() before the PCI_BAR_MEM classification - diff in README.md
Verdict
The ROM path of vga_pci_map_bios() is dead on spec-conformant devices. DF, unlike FreeBSD, never pre-reserves the expansion-ROM BAR in the device resource list during enumeration (pci.c:2720-2751 stop at BAR5), so vga_pci_map_bios()'s allocation (vga_pci.c:217) falls into the lazy pci_alloc_map() (pci.c:4060), whose sizing dance writes 0xffffffff to the ROM BAR and reads back base_mask|enable_bit (PCI ROM BAR bit 0 is RW; empirically 0xffff0001 on the QEMU std VGA, captured in run.log and ../DF-5467/serial-full.log). PCI_BAR_MEM(testval) is then false (pcireg.h:170-171 test bit 0), so the SYS_RES_MEMORY request is rejected as an ioport BAR (pci.c:3958-3965) and map_bios prints 'vga_pci_alloc_resource failed' and returns NULL (vga_pci.c:220-223). Consequence: non-boot-display VBIOS reads by drm (radeon_bios.c:86 and amdgpu/i915 equivalents) always fail on such hardware and the 1s/3s poll loops and D3-reset dance in map_bios are unreachable dead code. Fails closed - no memory unsafety - so impact 'none' (functional degradation only). Reproduced live: rombar_probe.sh readback + the unseeded map attempts in the DF-5467 baseline session.
No comments yet.