[PATCH] dm: pci: fix uninitialized fdt_pci_addr fall-through in pci_get_devfn
Scott Moser <[email protected]> Wed, 5 Aug 2026 13:31:13 -0700
| Newsgroups | gmane.comp.boot-loaders.u-boot |
|---|---|
| Message-ID | <20260805203113.46567-1-smoser__34156.8892401241$1785964024$gmane$org@brickies.net> |
From: Dustin Kirkland <[email protected]> pci_get_devfn() reads addr.phys_hi and returns the low bits of it as the requested devfn even on the -ENOENT branch, where ofnode_read_pci_addr() has NOT written *addr. The C standard leaves that read undefined; in practice the returned value depends on the compilers stack layout and on -ftrivial-auto-var-init. That value ends up in pplat->devfn (set by pci_uclass_child_post_bind()) and is later compared for equality in pci_bus_find_devfn() during PCI enumeration, so an under-determined value produces an under-determined driver-binding outcome. Concretely, this fires on Raspberry Pi 5 (BCM2712) with vendor U-Boot v2026.07 when the tree is compiled with GCC -ftrivial-auto-var-init=zero. In that build addr.phys_hi is zeroed rather than left as stack junk, so pci_get_devfn() returns 0 for every DT-declared non-PCI child of a PCI bus. The Pi 5 device tree includes one such child under the second root complex -- the rp1 simple-bus node representing the on-SoC RP1 south bridge as seen from the OS side. Under zero-init: * pci_uclass_child_post_bind(rp1) sets pplat->devfn = 0 * pci_bind_bus_devices() of the second root complex reads vendor at bdf 02:00.0, calls pci_bus_find_devfn(bus, 0x0000, &dev) * pci_bus_find_devfn() finds rp1 with pplat->devfn == 0x0000 == the requested devfn, returns it as the pre-bound match * pci_find_and_bind_driver() is skipped; the RP1 root port is never bound as pci_bridge_drv and its downstream bus is never enumerated * dm_pciauto_postscan_setup_bridge() then writes PCI_SUBORDINATE_BUS = 0 and PCI_MEMORY_LIMIT = 0 on the root port; Linux flags the bridge as "bridge configuration invalid ([bus 01-00])", the rp1 driver fails to enable the endpoint with -EINVAL, and every RP1-hosted peripheral (USB, onboard Ethernet, ttyAMA10) is non-functional for the rest of boot. Under the default -ftrivial-auto-var-init=uninitialized, addr.phys_hi happens to be non-zero stack residue that does not collide with the requested devfn, so the same code path just returns -ENODEV from pci_bus_find_devfn() and pci_find_and_bind_driver() correctly runs the fallback. The bug has been latent since introduction; -zero exposes it deterministically. The only observable behaviour change is that DT-declared non-PCI children of PCI buses now consistently report -EINVAL from pci_get_devfn() rather than a value that depends on stack state. Reported-by: Dustin Kirkland <[email protected]> Fixes: b52142004fbd ("pci: Add pci_get_devfn() to extract devfn from the fdt_pci_addr") Signed-off-by: Dustin Kirkland <[email protected]> Investigated-by: Claude Opus 4.7 <[email protected]> Co-authored-by: Claude Opus 4.7 <[email protected]> --- drivers/core/util.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/core/util.c b/drivers/core/util.c index fa893485..57006554 100644 --- a/drivers/core/util.c +++ b/drivers/core/util.c @@ -20,10 +20,8 @@ int pci_get_devfn(struct udevice *dev) /* Extract the devfn from fdt_pci_addr */ ret = ofnode_read_pci_addr(dev_ofnode(dev), FDT_PCI_SPACE_CONFIG, "reg", &addr, NULL); - if (ret) { - if (ret != -ENOENT) - return -EINVAL; - } + if (ret) + return -EINVAL; return addr.phys_hi & 0xff00; } -- 2.43.0