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 <jmcne...@invisible.ca> @@ -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); }