pci: bcm2838: cleanup on attach failure to fix devmatch panic

Specifically on the RPi CM4, we currently don't set the controller up
right and it never moves into the ready state (we don't observe the link
active bit).  Failure to cleanup here actually results in a panic not
long after, due to a use-after-free in the rman bits.  Further down in
pci_host_generic, we have some rman stashed in the softc that are
initialized and placed onto the rman tailq, then the softc is later
freed without an rman_fini() to pull them off of the tailq properly.

Note that PCIe on this board won't come up at boot without something
plugged in, so it currently can't be booted with an empty slot with the
intent to hotplug a supported card.  Some issues with controller startup
have been observed with Broadcom NICs in the wild, but no problems have
been observed with other NICs and a variety of different PCIe cards.

Shout-out to Vince <git@darkain.com> for the extensive debugging and
analysis to arrive at this conclusion.

Reviewed by:	andrew, imp
Differential Revision:	https://reviews.freebsd.org/D56897
This commit is contained in:
Kyle Evans
2026-05-08 21:49:35 -05:00
parent 31a94ec32b
commit a05af6ddf9
+9 -4
View File
@@ -646,7 +646,7 @@ bcm_pcib_attach(device_t dev)
error = bcm_pcib_check_ranges(dev);
if (error != 0)
return (error);
goto failed;
mtx_init(&sc->config_mtx, "bcm_pcib: config_mtx", NULL, MTX_DEF);
@@ -680,7 +680,8 @@ bcm_pcib_attach(device_t dev)
if (tries > 100) {
device_printf(dev,
"error: controller failed to start.\n");
return (ENXIO);
error = ENXIO;
goto failed;
}
DELAY(1000);
@@ -690,7 +691,8 @@ bcm_pcib_attach(device_t dev)
if (!link_state) {
device_printf(dev, "error: controller started but link is not "
"up.\n");
return (ENXIO);
error = ENXIO;
goto failed;
}
if (bootverbose)
device_printf(dev, "note: reported link speed is %s.\n",
@@ -741,12 +743,15 @@ bcm_pcib_attach(device_t dev)
/* Configure interrupts. */
error = bcm_pcib_msi_attach(dev);
if (error != 0)
return (error);
goto failed;
/* Done. */
device_add_child(dev, "pci", DEVICE_UNIT_ANY);
bus_attach_children(dev);
return (0);
failed:
pci_host_generic_destroy_fdt(dev);
return (error);
}
/*