Module Name: src
Committed By: riastradh
Date: Mon Mar 3 19:38:44 UTC 2025
Modified Files:
src/sys/dev/pci: pci_resource.c
Log Message:
pci_resource(9): Handle multiple bus ranges too.
Seen on orion o6.
Prompted by:
PR port-amd64/59118: Thinkpad T495s - iwm PCI BAR is zero
To generate a diff of this commit:
cvs rdiff -u -r1.6 -r1.7 src/sys/dev/pci/pci_resource.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/dev/pci/pci_resource.c
diff -u src/sys/dev/pci/pci_resource.c:1.6 src/sys/dev/pci/pci_resource.c:1.7
--- src/sys/dev/pci/pci_resource.c:1.6 Mon Mar 3 19:02:30 2025
+++ src/sys/dev/pci/pci_resource.c Mon Mar 3 19:38:43 2025
@@ -1,4 +1,4 @@
-/* $NetBSD: pci_resource.c,v 1.6 2025/03/03 19:02:30 riastradh Exp $ */
+/* $NetBSD: pci_resource.c,v 1.7 2025/03/03 19:38:43 riastradh Exp $ */
/*-
* Copyright (c) 2022 Jared McNeill <[email protected]>
@@ -35,7 +35,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: pci_resource.c,v 1.6 2025/03/03 19:02:30 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: pci_resource.c,v 1.7 2025/03/03 19:38:43 riastradh Exp $");
#include <sys/param.h>
#include <sys/types.h>
@@ -134,20 +134,20 @@ struct pci_resources {
struct pci_bus **pr_bus; /* Bus list */
pci_chipset_tag_t pr_pc; /* Chipset tag */
uint8_t pr_startbus; /* First bus number */
- uint8_t pr_endbus; /* Last bus number */
+ struct pci_resource_arena *pr_busranges;
struct pci_resource_arena *pr_ranges[NUM_PCI_RANGES];
};
struct pci_resource_arena {
vmem_t *vmem;
- SLIST_HEAD(, pci_resource_range) list;
+ SIMPLEQ_HEAD(, pci_resource_range) list;
};
struct pci_resource_range {
- uint64_t start;
- uint64_t end;
- SLIST_ENTRY(pci_resource_range) entry;
+ uint64_t start;
+ uint64_t end;
+ SIMPLEQ_ENTRY(pci_resource_range) entry;
};
static int pci_resource_scan_bus(struct pci_resources *,
@@ -165,6 +165,18 @@ static int pci_resource_scan_bus(struct
#define PCICONF_BUS_DEVICE(_pb, _devno, _funcno) \
(&(_pb)->pb_device[(_devno) * PCI_MAX_FUNC + (_funcno)])
+static bool
+pci_bus_in_range(struct pci_resources *pr, int busno)
+{
+ struct pci_resource_range *range;
+
+ SIMPLEQ_FOREACH(range, &pr->pr_busranges->list, entry) {
+ if (busno >= range->start && busno <= range->end)
+ return true;
+ }
+ return false;
+}
+
static void
pci_resource_arena_add_range(struct pci_resource_arena **arenas,
enum pci_range_type type, uint64_t start, uint64_t end)
@@ -173,29 +185,15 @@ pci_resource_arena_add_range(struct pci_
struct pci_resource_range *new, *range, *prev;
int error;
- /*
- * Create an arena if we haven't already.
- */
- if ((arena = arenas[type]) == NULL) {
- arena = arenas[type] = kmem_zalloc(sizeof(*arenas[type]),
- KM_SLEEP);
- arena->vmem = vmem_create(pci_resource_typename(type),
- 0, 0, 1, NULL, NULL, NULL, 0, VM_SLEEP, IPL_NONE);
- SLIST_INIT(&arena->list);
- }
+ KASSERTMSG(start <= end, "type=%d start=%" PRIu64 " end=%" PRIu64,
+ type, start, end);
/*
- * Warn if this is a bus range and there already is a bus
- * range, or if the start/end are bad. The other types of
- * ranges can have more than one range and larger addresses.
- *
- * XXX Not accurate: some machines do have multiple bus ranges.
- * But currently this logic can't handle that -- requires some
- * extra work to iterate over all the bus ranges. TBD.
+ * Warn if this is a bus range and the start/end are bad. The
+ * other types of ranges can have larger addresses.
*/
if (type == PCI_RANGE_BUS &&
- (start > UINT8_MAX || end > UINT8_MAX ||
- !SLIST_EMPTY(&arena->list))) {
+ (start > UINT8_MAX || end > UINT8_MAX)) {
aprint_error("PCI: unexpected bus range"
" %" PRIu64 "-%" PRIu64 ", ignoring\n",
start, end);
@@ -203,6 +201,17 @@ pci_resource_arena_add_range(struct pci_
}
/*
+ * Create an arena if we haven't already.
+ */
+ if ((arena = arenas[type]) == NULL) {
+ arena = arenas[type] = kmem_zalloc(sizeof(*arenas[type]),
+ KM_SLEEP);
+ arena->vmem = vmem_create(pci_resource_typename(type),
+ 0, 0, 1, NULL, NULL, NULL, 0, VM_SLEEP, IPL_NONE);
+ SIMPLEQ_INIT(&arena->list);
+ }
+
+ /*
* Reserve the range in the vmem for allocation. If there's
* already an overlapping range, just drop this one.
*/
@@ -225,15 +234,16 @@ pci_resource_arena_add_range(struct pci_
new->start = start;
new->end = end;
prev = NULL;
- SLIST_FOREACH(range, &arena->list, entry) {
+ SIMPLEQ_FOREACH(range, &arena->list, entry) {
if (new->start < range->start)
break;
+ KASSERT(new->start > range->end);
prev = range;
}
if (prev) {
- SLIST_INSERT_AFTER(prev, new, entry);
+ SIMPLEQ_INSERT_AFTER(&arena->list, prev, new, entry);
} else {
- SLIST_INSERT_HEAD(&arena->list, new, entry);
+ SIMPLEQ_INSERT_HEAD(&arena->list, new, entry);
}
}
@@ -333,7 +343,7 @@ pci_resource_device_print(struct pci_res
PCI_BRIDGE_BUS_NUM_SUBORDINATE(pd->pd_bridge.bridge_bus));
if (pd->pd_bridge.ranges[PCI_RANGE_IO]) {
- SLIST_FOREACH(range,
+ SIMPLEQ_FOREACH(range,
&pd->pd_bridge.ranges[PCI_RANGE_IO]->list,
entry) {
DPRINT("PCI: " PCI_SBDF_FMT
@@ -346,7 +356,7 @@ pci_resource_device_print(struct pci_res
}
}
if (pd->pd_bridge.ranges[PCI_RANGE_MEM]) {
- SLIST_FOREACH(range,
+ SIMPLEQ_FOREACH(range,
&pd->pd_bridge.ranges[PCI_RANGE_MEM]->list,
entry) {
DPRINT("PCI: " PCI_SBDF_FMT
@@ -359,7 +369,7 @@ pci_resource_device_print(struct pci_res
}
}
if (pd->pd_bridge.ranges[PCI_RANGE_PMEM]) {
- SLIST_FOREACH(range,
+ SIMPLEQ_FOREACH(range,
&pd->pd_bridge.ranges[PCI_RANGE_PMEM]->list,
entry) {
DPRINT("PCI: " PCI_SBDF_FMT
@@ -609,12 +619,16 @@ pci_resource_scan_device(struct pci_reso
PCI_SUBCLASS(pd->pd_class) == PCI_SUBCLASS_BRIDGE_PCI) {
bridge_bus = pci_conf_read(pr->pr_pc, tag, PCI_BRIDGE_BUS_REG);
sec_bus = PCI_BRIDGE_BUS_NUM_SECONDARY(bridge_bus);
- if (sec_bus <= pr->pr_endbus) {
+ if (pci_bus_in_range(pr, sec_bus)) {
if (pci_resource_scan_bus(pr, pd, sec_bus) != 0) {
DPRINT("PCI: " PCI_SBDF_FMT " bus %u "
"already scanned (firmware bug!)\n",
PCI_SBDF_FMT_ARGS(pr, pd), sec_bus);
}
+ } else {
+ DPRINT("PCI: " PCI_SBDF_FMT " bus %u "
+ "out of range (firmware bug!)\n",
+ PCI_SBDF_FMT_ARGS(pr, pd), sec_bus);
}
}
@@ -635,7 +649,7 @@ pci_resource_scan_bus(struct pci_resourc
uint8_t nfunc;
KASSERT(busno >= pr->pr_startbus);
- KASSERT(busno <= pr->pr_endbus);
+ KASSERT(pci_bus_in_range(pr, busno));
if (PCICONF_RES_BUS(pr, busno) != NULL) {
/*
@@ -855,7 +869,7 @@ pci_resource_init_bus(struct pci_resourc
int error;
KASSERT(busno >= pr->pr_startbus);
- KASSERT(busno <= pr->pr_endbus);
+ KASSERT(pci_bus_in_range(pr, busno));
pb = PCICONF_RES_BUS(pr, busno);
bridge = pb->pb_bridge;
@@ -884,7 +898,7 @@ pci_resource_init_bus(struct pci_resourc
bridge->pd_bridge.ranges[prtype] == NULL) {
continue;
}
- SLIST_FOREACH(range,
+ SIMPLEQ_FOREACH(range,
&bridge->pd_bridge.ranges[prtype]->list,
entry) {
error = pci_resource_claim(
@@ -920,6 +934,7 @@ pci_resource_init_bus(struct pci_resourc
if (pd->pd_ppb) {
uint8_t sec_bus = PCI_BRIDGE_BUS_NUM_SECONDARY(
pd->pd_bridge.bridge_bus);
+ KASSERT(pci_bus_in_range(pr, sec_bus));
pci_resource_init_bus(pr, sec_bus);
}
pci_resource_init_device(pr, pd);
@@ -937,9 +952,9 @@ pci_resource_probe(struct pci_resources
const struct pci_resource_info *info)
{
struct pci_resource_arena *busarena = info->ranges[PCI_RANGE_BUS];
- struct pci_resource_range *busrange = SLIST_FIRST(&busarena->list);
- uint8_t startbus = (uint8_t)busrange->start;
- uint8_t endbus = (uint8_t)busrange->end;
+ uint8_t startbus = SIMPLEQ_FIRST(&busarena->list)->start;
+ uint8_t endbus = SIMPLEQ_LAST(&busarena->list, pci_resource_range,
+ entry)->end;
u_int nbus;
KASSERT(startbus <= endbus);
@@ -949,8 +964,8 @@ pci_resource_probe(struct pci_resources
pr->pr_pc = info->pc;
pr->pr_startbus = startbus;
- pr->pr_endbus = endbus;
- pr->pr_bus = kmem_zalloc(nbus * sizeof(struct pci_bus *), KM_SLEEP);
+ pr->pr_busranges = busarena;
+ pr->pr_bus = kmem_zalloc(nbus * sizeof(pr->pr_bus[0]), KM_SLEEP);
memcpy(pr->pr_ranges, info->ranges, sizeof(pr->pr_ranges));
/* Scan devices */
@@ -1107,7 +1122,7 @@ pci_resource_init(const struct pci_resou
aprint_error("PCI: no buses\n");
return;
}
- KASSERT(!SLIST_EMPTY(&info->ranges[PCI_RANGE_BUS]->list));
+ KASSERT(!SIMPLEQ_EMPTY(&info->ranges[PCI_RANGE_BUS]->list));
pci_resource_probe(&pr, info);
pci_resource_alloc_bus(&pr, pr.pr_startbus);
}