[PATCH] dm: pci: fix uninitialized fdt_pci_addr fall-through in pci_get_devfn

Scott Moser <[email protected]>
Newsgroups org.u-boot-project.lists.u-boot
Message-ID <[email protected]>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.