pci_host_generic: Tolerate range resource allocation failures

QEMU for armv7 includes a PCI memory range whose CPU address is
greater than 4GB.  This falls outside the range of armv7's global
mem_rman used by the nexus driver.  As a result, pcib0 fails to
attach blocking all PCI devices.

Instead, change the driver to be a bit more tolerant.  If allocating a
resource for a range fails, don't fail attaching the entire driver,
but do skip adding the associated PCI range to the relevant rman in
the pcib driver.  This will prevent child devices from using BARs that
allocate from this range.  In the case of QEMU on armv7 devices can
still allocate from an earlier PCI memory range that is within the
32-bit address space (and in fact none of the firmware-assigned memory
BARs use addresses from the upper range).

While here, reorder the operations on I/O ranges a bit: 1) print the
range under bootverbose first (rather than last) so that the range is
printed before any relevant errors for the range, 2) move
rman_manage_region last after the parent resource has been set and
allocated.

Reported by:	markj, Jenkins
Reviewed by:	markj
Fixes:		d79b6b8ec2 pci_host_generic: Don't rewrite resource start address for translation
Differential Revision:	https://reviews.freebsd.org/D44698
(cherry picked from commit 03713f805a)
This commit is contained in:
John Baldwin 2024-04-09 14:55:40 -07:00
parent d172f42e4b
commit 92e5936686

View file

@ -80,6 +80,7 @@ pci_host_generic_core_attach(device_t dev)
struct resource_map map;
#endif
struct generic_pcie_core_softc *sc;
struct rman *rm;
uint64_t phys_base;
uint64_t pci_base;
uint64_t size;
@ -183,75 +184,51 @@ pci_host_generic_core_attach(device_t dev)
range_descr = "prefetch";
flags = RF_PREFETCHABLE;
type = SYS_RES_MEMORY;
error = rman_manage_region(&sc->pmem_rman,
pci_base, pci_base + size - 1);
rm = &sc->pmem_rman;
break;
case FLAG_TYPE_MEM:
range_descr = "memory";
flags = 0;
type = SYS_RES_MEMORY;
error = rman_manage_region(&sc->mem_rman,
pci_base, pci_base + size - 1);
rm = &sc->mem_rman;
break;
case FLAG_TYPE_IO:
range_descr = "I/O port";
flags = 0;
type = SYS_RES_IOPORT;
error = rman_manage_region(&sc->io_rman,
pci_base, pci_base + size - 1);
rm = &sc->io_rman;
break;
default:
continue;
}
if (error) {
device_printf(dev, "rman_manage_region() failed."
"error = %d\n", error);
goto err_rman_manage;
}
if (bootverbose)
device_printf(dev,
"PCI addr: 0x%jx, CPU addr: 0x%jx, Size: 0x%jx, Type: %s\n",
pci_base, phys_base, size, range_descr);
error = bus_set_resource(dev, type, rid, phys_base, size);
if (error != 0) {
device_printf(dev,
"failed to set resource for range %d: %d\n", tuple,
error);
goto err_rman_manage;
continue;
}
sc->ranges[tuple].res = bus_alloc_resource_any(dev, type, &rid,
RF_ACTIVE | RF_UNMAPPED | flags);
if (sc->ranges[tuple].res == NULL) {
device_printf(dev,
"failed to allocate resource for range %d\n", tuple);
error = ENXIO;
goto err_rman_manage;
continue;
}
error = rman_manage_region(rm, pci_base, pci_base + size - 1);
if (error) {
device_printf(dev, "rman_manage_region() failed."
"error = %d\n", error);
continue;
}
if (bootverbose)
device_printf(dev,
"PCI addr: 0x%jx, CPU addr: 0x%jx, Size: 0x%jx, Type: %s\n",
pci_base, phys_base, size, range_descr);
}
return (0);
err_rman_manage:
for (tuple = 0; tuple < MAX_RANGES_TUPLES; tuple++) {
if (sc->ranges[tuple].size == 0)
continue; /* empty range element */
switch (FLAG_TYPE(sc->ranges[tuple].flags)) {
case FLAG_TYPE_PMEM:
case FLAG_TYPE_MEM:
type = SYS_RES_MEMORY;
break;
case FLAG_TYPE_IO:
type = SYS_RES_IOPORT;
break;
default:
continue;
}
if (sc->ranges[tuple].res != NULL)
bus_release_resource(dev, type, tuple + 1,
sc->ranges[tuple].res);
bus_delete_resource(dev, type, tuple + 1);
}
rman_fini(&sc->io_rman);
err_io_rman:
rman_fini(&sc->mem_rman);
err_mem_rman: