Module Name: src Committed By: jmcneill Date: Fri Oct 14 22:10:16 UTC 2022
Modified Files: src/sys/arch/arm/acpi: acpipchb.c src/sys/arch/arm/fdt: arm_simplefb.c src/sys/arch/evbarm/conf: std.generic64 src/sys/dev/acpi: acpi_mcfg.c acpi_mcfg.h acpi_pci.c acpi_pci.h src/sys/dev/pci: files.pci Added Files: src/sys/dev/pci: pci_resource.c pci_resource.h Log Message: Add a PCI resource manager and use it on Arm ACPI platforms. The Arm ACPI code relied on PCI_NETBSD_CONFIGURE to configure devices that were not enabled by system firmware. This is not safe to do unless the firmware explicitly permits it using a device specific method defined in the PCI firmware spec. Introduce a new PCI resource manager that discovers what has already been configured by firmware and allocates from the remaining space. This will ensure that devices setup by firmware are untouched and only will program BARs of devices that are not enabled at boot time. The current implementation assumes that the parent PCI-PCI bridge's are already configured. A worthwhile improvement in the future would be to support programming windows for bridges that are not fully configured. To generate a diff of this commit: cvs rdiff -u -r1.30 -r1.31 src/sys/arch/arm/acpi/acpipchb.c cvs rdiff -u -r1.12 -r1.13 src/sys/arch/arm/fdt/arm_simplefb.c cvs rdiff -u -r1.19 -r1.20 src/sys/arch/evbarm/conf/std.generic64 cvs rdiff -u -r1.25 -r1.26 src/sys/dev/acpi/acpi_mcfg.c cvs rdiff -u -r1.4 -r1.5 src/sys/dev/acpi/acpi_mcfg.h cvs rdiff -u -r1.36 -r1.37 src/sys/dev/acpi/acpi_pci.c cvs rdiff -u -r1.12 -r1.13 src/sys/dev/acpi/acpi_pci.h cvs rdiff -u -r1.444 -r1.445 src/sys/dev/pci/files.pci cvs rdiff -u -r0 -r1.1 src/sys/dev/pci/pci_resource.c \ src/sys/dev/pci/pci_resource.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/arm/acpi/acpipchb.c diff -u src/sys/arch/arm/acpi/acpipchb.c:1.30 src/sys/arch/arm/acpi/acpipchb.c:1.31 --- src/sys/arch/arm/acpi/acpipchb.c:1.30 Sat Aug 13 20:08:36 2022 +++ src/sys/arch/arm/acpi/acpipchb.c Fri Oct 14 22:10:15 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: acpipchb.c,v 1.30 2022/08/13 20:08:36 jmcneill Exp $ */ +/* $NetBSD: acpipchb.c,v 1.31 2022/10/14 22:10:15 jmcneill Exp $ */ /*- * Copyright (c) 2018 The NetBSD Foundation, Inc. @@ -30,7 +30,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: acpipchb.c,v 1.30 2022/08/13 20:08:36 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: acpipchb.c,v 1.31 2022/10/14 22:10:15 jmcneill Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -48,7 +48,7 @@ __KERNEL_RCSID(0, "$NetBSD: acpipchb.c,v #include <dev/pci/pcireg.h> #include <dev/pci/pcivar.h> -#include <dev/pci/pciconf.h> +#include <dev/pci/pci_resource.h> #include <dev/acpi/acpivar.h> #include <dev/acpi/acpi_pci.h> @@ -56,8 +56,6 @@ __KERNEL_RCSID(0, "$NetBSD: acpipchb.c,v #include <arm/acpi/acpi_pci_machdep.h> -#define PCIHOST_CACHELINE_SIZE arm_dcache_align - #define ACPIPCHB_MAX_RANGES 64 /* XXX arbitrary limit */ struct acpipchb_bus_range { @@ -194,34 +192,16 @@ acpipchb_configure_bus(struct acpipchb_s struct arm32_pci_chipset *md_pc = (struct arm32_pci_chipset *)pba->pba_pc; struct acpi_pci_context *ap = md_pc->pc_conf_v; - struct pciconf_resources *pcires; - ACPI_STATUS rv; + const bool mapcfgspace = (ap->ap_flags & ACPI_PCI_FLAG_NO_MCFG) == 0; int error, val; - if (!acpi_pci_ignore_boot_config(sc->sc_handle)) { - return; - } if (get_bootconf_option(boot_args, "nopciconf", BOOTOPT_TYPE_BOOLEAN, &val) && val) { return; } - if ((ap->ap_flags & ACPI_PCI_FLAG_NO_MCFG) != 0) { - pcires = pciconf_resource_init(); - rv = AcpiWalkResources(sc->sc_handle, "_CRS", - acpimcfg_configure_bus_cb, pcires); - if (ACPI_FAILURE(rv)) { - error = ENXIO; - } else { - error = pci_configure_bus(pba->pba_pc, pcires, ap->ap_bus, - PCIHOST_CACHELINE_SIZE); - } - pciconf_resource_fini(pcires); - } else { - error = acpimcfg_configure_bus(sc->sc_dev, pba->pba_pc, sc->sc_handle, - sc->sc_bus, PCIHOST_CACHELINE_SIZE); - } - + error = acpimcfg_configure_bus(sc->sc_dev, pba->pba_pc, sc->sc_handle, + sc->sc_bus, mapcfgspace); if (error != 0) { aprint_error_dev(sc->sc_dev, "failed to configure bus, error %d\n", error); Index: src/sys/arch/arm/fdt/arm_simplefb.c diff -u src/sys/arch/arm/fdt/arm_simplefb.c:1.12 src/sys/arch/arm/fdt/arm_simplefb.c:1.13 --- src/sys/arch/arm/fdt/arm_simplefb.c:1.12 Sun Jul 17 20:23:17 2022 +++ src/sys/arch/arm/fdt/arm_simplefb.c Fri Oct 14 22:10:15 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: arm_simplefb.c,v 1.12 2022/07/17 20:23:17 riastradh Exp $ */ +/* $NetBSD: arm_simplefb.c,v 1.13 2022/10/14 22:10:15 jmcneill Exp $ */ /*- * Copyright (c) 2019 The NetBSD Foundation, Inc. @@ -34,7 +34,7 @@ #include "opt_vcons.h" #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: arm_simplefb.c,v 1.12 2022/07/17 20:23:17 riastradh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: arm_simplefb.c,v 1.13 2022/10/14 22:10:15 jmcneill Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -174,26 +174,6 @@ arm_simplefb_pollc(void *v, int on) { } -#if NPCI > 0 && defined(PCI_NETBSD_CONFIGURE) -static void -arm_simplefb_reconfig(void *arg, uint64_t new_addr) -{ - struct arm_simplefb_softc * const sc = &arm_simplefb_softc; - struct rasops_info *ri = &arm_simplefb_screen.scr_ri; - bus_space_tag_t bst = &arm_generic_bs_tag; - - bus_space_unmap(bst, arm_simplefb_bsh, arm_simplefb_size); - bus_space_map(bst, new_addr, arm_simplefb_size, - BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE, - &arm_simplefb_bsh); - - sc->sc_bits = bus_space_vaddr(bst, arm_simplefb_bsh); - ri->ri_bits = sc->sc_bits; - - arm_simplefb_addr = (bus_addr_t)new_addr; -} -#endif - uint64_t arm_simplefb_physaddr(void) { @@ -296,14 +276,4 @@ arm_simplefb_preattach(void) wsdisplay_preattach(&arm_simplefb_stdscreen, ri, 0, 0, defattr); vcons_replay_msgbuf(&arm_simplefb_screen); - -#if NPCI > 0 && defined(PCI_NETBSD_CONFIGURE) - /* - * Let the PCI resource allocator know about our framebuffer. This - * lets us know if the FB base address changes so we can remap the - * framebuffer if necessary. - */ - pciconf_resource_reserve(PCI_CONF_MAP_MEM, addr, size, - arm_simplefb_reconfig, NULL); -#endif } Index: src/sys/arch/evbarm/conf/std.generic64 diff -u src/sys/arch/evbarm/conf/std.generic64:1.19 src/sys/arch/evbarm/conf/std.generic64:1.20 --- src/sys/arch/evbarm/conf/std.generic64:1.19 Sat Jun 25 13:24:35 2022 +++ src/sys/arch/evbarm/conf/std.generic64 Fri Oct 14 22:10:15 2022 @@ -1,4 +1,4 @@ -# $NetBSD: std.generic64,v 1.19 2022/06/25 13:24:35 jmcneill Exp $ +# $NetBSD: std.generic64,v 1.20 2022/10/14 22:10:15 jmcneill Exp $ # # generic NetBSD/evbarm64 with FDT support @@ -19,6 +19,7 @@ options FPU_VFP options MODULAR options MODULAR_DEFAULT_AUTOLOAD options PCI_NETBSD_CONFIGURE +options PCI_RESOURCE options PCI_SMCCC # Arm PCI Conf Access Firmware Interface options _ARM32_NEED_BUS_DMA_BOUNCE options __HAVE_GENERIC_CPU_INITCLOCKS Index: src/sys/dev/acpi/acpi_mcfg.c diff -u src/sys/dev/acpi/acpi_mcfg.c:1.25 src/sys/dev/acpi/acpi_mcfg.c:1.26 --- src/sys/dev/acpi/acpi_mcfg.c:1.25 Sun Feb 27 14:19:07 2022 +++ src/sys/dev/acpi/acpi_mcfg.c Fri Oct 14 22:10:15 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: acpi_mcfg.c,v 1.25 2022/02/27 14:19:07 riastradh Exp $ */ +/* $NetBSD: acpi_mcfg.c,v 1.26 2022/10/14 22:10:15 jmcneill Exp $ */ /*- * Copyright (C) 2015 NONAKA Kimihiro <non...@netbsd.org> @@ -28,7 +28,7 @@ #include "opt_pci.h" #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: acpi_mcfg.c,v 1.25 2022/02/27 14:19:07 riastradh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: acpi_mcfg.c,v 1.26 2022/10/14 22:10:15 jmcneill Exp $"); #include <sys/param.h> #include <sys/device.h> @@ -37,7 +37,7 @@ __KERNEL_RCSID(0, "$NetBSD: acpi_mcfg.c, #include <dev/pci/pcireg.h> #include <dev/pci/pcivar.h> -#include <dev/pci/pciconf.h> +#include <dev/pci/pci_resource.h> #include <dev/pci/pcidevs.h> #include <dev/acpi/acpireg.h> @@ -687,16 +687,14 @@ out: return error; } -#ifdef PCI_NETBSD_CONFIGURE +#ifdef PCI_RESOURCE ACPI_STATUS acpimcfg_configure_bus_cb(ACPI_RESOURCE *res, void *ctx) { - struct pciconf_resources *pcires = ctx; - int type; + struct pci_resource_info *pciinfo = ctx; bus_addr_t addr; bus_size_t size; - const char *s; - int error; + int type; if (res->Type != ACPI_RESOURCE_TYPE_ADDRESS16 && res->Type != ACPI_RESOURCE_TYPE_ADDRESS32 && @@ -707,21 +705,25 @@ acpimcfg_configure_bus_cb(ACPI_RESOURCE return AE_OK; if (res->Data.Address.ResourceType != ACPI_MEMORY_RANGE && - res->Data.Address.ResourceType != ACPI_IO_RANGE) + res->Data.Address.ResourceType != ACPI_IO_RANGE && + res->Data.Address.ResourceType != ACPI_BUS_NUMBER_RANGE) return AE_OK; if (res->Data.Address.ResourceType == ACPI_MEMORY_RANGE && res->Data.Address.Info.Mem.Caching == ACPI_PREFETCHABLE_MEMORY) { - type = PCICONF_RESOURCE_PREFETCHABLE_MEM; - s = "prefetchable"; + type = PCI_RANGE_PMEM; } else if (res->Data.Address.ResourceType == ACPI_MEMORY_RANGE && res->Data.Address.Info.Mem.Caching != ACPI_PREFETCHABLE_MEMORY) { - type = PCICONF_RESOURCE_MEM; - s = "non-prefetchable"; + if (res->Type == ACPI_RESOURCE_TYPE_ADDRESS64) { + type = PCI_RANGE_PMEM; + } else { + type = PCI_RANGE_MEM; + } + } else if (res->Data.Address.ResourceType == ACPI_BUS_NUMBER_RANGE) { + type = PCI_RANGE_BUS; } else { KASSERT(res->Data.Address.ResourceType == ACPI_IO_RANGE); - type = PCICONF_RESOURCE_IO; - s = "i/o"; + type = PCI_RANGE_IO; } switch (res->Type) { @@ -730,7 +732,7 @@ acpimcfg_configure_bus_cb(ACPI_RESOURCE "MCFG: range 0x%04" PRIx16 " size %#" PRIx16 " (16-bit %s)\n", res->Data.Address16.Address.Minimum, res->Data.Address16.Address.AddressLength, - s); + pci_resource_typename(type)); addr = res->Data.Address16.Address.Minimum; size = res->Data.Address16.Address.AddressLength; break; @@ -739,7 +741,7 @@ acpimcfg_configure_bus_cb(ACPI_RESOURCE "MCFG: range 0x%08" PRIx32 " size %#" PRIx32 " (32-bit %s)\n", res->Data.Address32.Address.Minimum, res->Data.Address32.Address.AddressLength, - s); + pci_resource_typename(type)); addr = res->Data.Address32.Address.Minimum; size = res->Data.Address32.Address.AddressLength; break; @@ -748,7 +750,7 @@ acpimcfg_configure_bus_cb(ACPI_RESOURCE "MCFG: range 0x%016" PRIx64 " size %#" PRIx64 " (64-bit %s)\n", res->Data.Address64.Address.Minimum, res->Data.Address64.Address.AddressLength, - s); + pci_resource_typename(type)); addr = res->Data.Address64.Address.Minimum; size = res->Data.Address64.Address.AddressLength; break; @@ -757,93 +759,108 @@ acpimcfg_configure_bus_cb(ACPI_RESOURCE return AE_OK; } - error = pciconf_resource_add(pcires, type, addr, size); + pciinfo->ranges[type].start = addr; + pciinfo->ranges[type].end = addr + size - 1; - return error == 0 ? AE_OK : AE_NO_MEMORY; + return AE_OK; } int acpimcfg_configure_bus(device_t self, pci_chipset_tag_t pc, ACPI_HANDLE handle, - int bus, int cacheline_size) + int bus, bool mapcfgspace) { - struct pciconf_resources *pcires; + struct pci_resource_info pciinfo; struct mcfg_segment *seg; struct mcfg_bus *mb; bus_space_handle_t bsh[256]; bool bsh_mapped[256]; - int error, boff, b, d, f; + int error, boff, b, d, f, endbus; bus_addr_t baddr; ACPI_STATUS rv; - seg = acpimcfg_get_segment(pc, bus); - if (seg == NULL) - return ENOENT; - - pcires = pciconf_resource_init(); + if (mapcfgspace) { + seg = acpimcfg_get_segment(pc, bus); + if (seg == NULL) { + return ENOENT; + } + endbus = seg->ms_bus_end; - /* - * Map config space for all possible busses and mark them valid during - * configuration so pci_configure_bus can access them through our chipset - * tag with acpimcfg_conf_read/write below. - */ - memset(bsh_mapped, 0, sizeof(bsh_mapped)); - for (b = seg->ms_bus_start; b <= seg->ms_bus_end; b++) { - boff = b - seg->ms_bus_start; - mb = &seg->ms_bus[boff]; - baddr = seg->ms_address + (b * ACPIMCFG_SIZE_PER_BUS); - - /* Map extended configuration space of all dev/func. */ - error = bus_space_map(seg->ms_bst, baddr, ACPIMCFG_SIZE_PER_BUS, 0, - &bsh[b]); - if (error != 0) - goto cleanup; - bsh_mapped[b] = true; - for (d = 0; d < 32; d++) { - for (f = 0; f < 8; f++) { - error = bus_space_subregion(seg->ms_bst, bsh[b], - EXTCONF_OFFSET(d, f, 0), PCI_EXTCONF_SIZE, - &mb->bsh[d][f]); - if (error != 0) - break; + /* + * Map config space for all possible busses and mark them valid + * during configuration so pci_configure_bus can access them + * through our chipset tag with acpimcfg_conf_read/write below. + */ + memset(bsh_mapped, 0, sizeof(bsh_mapped)); + for (b = seg->ms_bus_start; b <= seg->ms_bus_end; b++) { + boff = b - seg->ms_bus_start; + mb = &seg->ms_bus[boff]; + baddr = seg->ms_address + (b * ACPIMCFG_SIZE_PER_BUS); + + /* Map extended configuration space of all dev/func. */ + error = bus_space_map(seg->ms_bst, baddr, + ACPIMCFG_SIZE_PER_BUS, 0, &bsh[b]); + if (error != 0) { + goto cleanup; + } + bsh_mapped[b] = true; + for (d = 0; d < 32; d++) { + for (f = 0; f < 8; f++) { + error = bus_space_subregion(seg->ms_bst, + bsh[b], EXTCONF_OFFSET(d, f, 0), + PCI_EXTCONF_SIZE, &mb->bsh[d][f]); + if (error != 0) { + break; + } + } + } + if (error != 0) { + goto cleanup; } - } - if (error != 0) - goto cleanup; - memset(mb->valid_devs, 0xff, sizeof(mb->valid_devs)); + memset(mb->valid_devs, 0xff, sizeof(mb->valid_devs)); + } + } else { + endbus = 255; } + memset(&pciinfo, 0, sizeof(pciinfo)); + pciinfo.pc = pc; + pciinfo.ranges[PCI_RANGE_BUS].start = bus; + pciinfo.ranges[PCI_RANGE_BUS].end = endbus; rv = AcpiWalkResources(handle, "_CRS", acpimcfg_configure_bus_cb, - pcires); + &pciinfo); if (ACPI_FAILURE(rv)) { error = ENXIO; goto cleanup; } + error = 0; - error = pci_configure_bus(pc, pcires, bus, cacheline_size); + pci_resource_init(&pciinfo); cleanup: - /* - * Unmap config space for the segment's busses. Valid devices will be - * re-mapped later on by acpimcfg_map_bus. - */ - for (b = seg->ms_bus_start; b <= seg->ms_bus_end; b++) { - boff = b - seg->ms_bus_start; - mb = &seg->ms_bus[boff]; - memset(mb->valid_devs, 0, sizeof(mb->valid_devs)); - - if (bsh_mapped[b]) - bus_space_unmap(seg->ms_bst, bsh[b], ACPIMCFG_SIZE_PER_BUS); + if (mapcfgspace) { + /* + * Unmap config space for the segment's busses. Valid devices + * will be re-mapped later on by acpimcfg_map_bus. + */ + for (b = seg->ms_bus_start; b <= seg->ms_bus_end; b++) { + boff = b - seg->ms_bus_start; + mb = &seg->ms_bus[boff]; + memset(mb->valid_devs, 0, sizeof(mb->valid_devs)); + + if (bsh_mapped[b]) { + bus_space_unmap(seg->ms_bst, bsh[b], + ACPIMCFG_SIZE_PER_BUS); + } + } } - pciconf_resource_fini(pcires); - return error; } #else int acpimcfg_configure_bus(device_t self, pci_chipset_tag_t pc, ACPI_HANDLE handle, - int bus, int cacheline_size) + int bus, bool mapcfgspace) { return ENXIO; } Index: src/sys/dev/acpi/acpi_mcfg.h diff -u src/sys/dev/acpi/acpi_mcfg.h:1.4 src/sys/dev/acpi/acpi_mcfg.h:1.5 --- src/sys/dev/acpi/acpi_mcfg.h:1.4 Sat Aug 7 21:19:15 2021 +++ src/sys/dev/acpi/acpi_mcfg.h Fri Oct 14 22:10:15 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: acpi_mcfg.h,v 1.4 2021/08/07 21:19:15 jmcneill Exp $ */ +/* $NetBSD: acpi_mcfg.h,v 1.5 2022/10/14 22:10:15 jmcneill Exp $ */ /*- * Copyright (C) 2015 NONAKA Kimihiro <non...@netbsd.org> @@ -32,7 +32,8 @@ struct acpimcfg_ops; void acpimcfg_probe(struct acpi_softc *); int acpimcfg_init(bus_space_tag_t, const struct acpimcfg_ops *); int acpimcfg_map_bus(device_t, pci_chipset_tag_t, int); -int acpimcfg_configure_bus(device_t, pci_chipset_tag_t, ACPI_HANDLE, int, int); +int acpimcfg_configure_bus(device_t, pci_chipset_tag_t, ACPI_HANDLE, int, + bool); ACPI_STATUS acpimcfg_configure_bus_cb(ACPI_RESOURCE *, void *); int acpimcfg_conf_read(pci_chipset_tag_t, pcitag_t, int, pcireg_t *); Index: src/sys/dev/acpi/acpi_pci.c diff -u src/sys/dev/acpi/acpi_pci.c:1.36 src/sys/dev/acpi/acpi_pci.c:1.37 --- src/sys/dev/acpi/acpi_pci.c:1.36 Sun Feb 27 14:19:07 2022 +++ src/sys/dev/acpi/acpi_pci.c Fri Oct 14 22:10:15 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: acpi_pci.c,v 1.36 2022/02/27 14:19:07 riastradh Exp $ */ +/* $NetBSD: acpi_pci.c,v 1.37 2022/10/14 22:10:15 jmcneill Exp $ */ /* * Copyright (c) 2009, 2010 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: acpi_pci.c,v 1.36 2022/02/27 14:19:07 riastradh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: acpi_pci.c,v 1.37 2022/10/14 22:10:15 jmcneill Exp $"); #include <sys/param.h> #include <sys/device.h> @@ -59,14 +59,6 @@ static ACPI_STATUS acpi_pcidev_pciroot void *); /* - * UUID for _DSM control method, from PCI Firmware Specification. - */ -static UINT8 acpi_pci_dsm_uuid[ACPI_UUID_LENGTH] = { - 0xd0, 0x37, 0xc9, 0xe5, 0x53, 0x35, 0x7a, 0x4d, - 0x91, 0x17, 0xea, 0x4d, 0x19, 0xc3, 0x43, 0x4d -}; - -/* * Regarding PCI Segment Groups (ACPI 4.0, p. 277): * * "The optional _SEG object is located under a PCI host bridge and @@ -486,63 +478,6 @@ acpi_pcidev_find_dev(struct acpi_devnode } /* - * acpi_pci_ignore_boot_config: - * - * Returns 1 if the operating system may ignore the boot configuration - * of PCI resources. - */ -ACPI_INTEGER -acpi_pci_ignore_boot_config(ACPI_HANDLE handle) -{ - ACPI_OBJECT *pobj = NULL; - ACPI_INTEGER ret; - - /* - * This one is a little confusing, but the result of - * evaluating _DSM #5 is: - * - * 0: The operating system may not ignore the boot configuration - * of PCI resources. - * - * 1: The operating system may ignore the boot configuration of - * PCI resources, and reconfigure or rebalance these resources - * in the hierarchy as required. - */ - - if (ACPI_FAILURE(acpi_dsm(handle, acpi_pci_dsm_uuid, - 1, 5, NULL, &pobj))) { - /* - * In the absence of _DSM #5, we may assume that the - * boot config can be ignored. - */ - return 1; - } - - /* - * ...and we default to "may ignore" in the event that the - * method returns nonsense. - */ - ret = 1; - - if (pobj != NULL) { - switch (pobj->Type) { - case ACPI_TYPE_INTEGER: - ret = pobj->Integer.Value; - break; - - case ACPI_TYPE_PACKAGE: - if (pobj->Package.Count == 1 && - pobj->Package.Elements[0].Type == ACPI_TYPE_INTEGER) - ret = pobj->Package.Elements[0].Integer.Value; - break; - } - ACPI_FREE(pobj); - } - - return ret; -} - -/* * acpi_pci_bus_get_child_devhandle: * * Implements the "pci-bus-get-child-devhandle" device call for Index: src/sys/dev/acpi/acpi_pci.h diff -u src/sys/dev/acpi/acpi_pci.h:1.12 src/sys/dev/acpi/acpi_pci.h:1.13 --- src/sys/dev/acpi/acpi_pci.h:1.12 Fri May 8 14:42:38 2020 +++ src/sys/dev/acpi/acpi_pci.h Fri Oct 14 22:10:15 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: acpi_pci.h,v 1.12 2020/05/08 14:42:38 jmcneill Exp $ */ +/* $NetBSD: acpi_pci.h,v 1.13 2022/10/14 22:10:15 jmcneill Exp $ */ /* * Copyright (c) 2009 The NetBSD Foundation, Inc. @@ -42,6 +42,5 @@ pci_chipset_tag_t acpi_pcidev_get_tag(u uint16_t, uint16_t); device_t acpi_pcidev_find_dev(struct acpi_devnode *); struct acpi_devnode *acpi_pciroot_find(uint16_t, uint16_t); -ACPI_INTEGER acpi_pci_ignore_boot_config(ACPI_HANDLE); #endif /* !_SYS_DEV_ACPI_ACPI_PCI_H */ Index: src/sys/dev/pci/files.pci diff -u src/sys/dev/pci/files.pci:1.444 src/sys/dev/pci/files.pci:1.445 --- src/sys/dev/pci/files.pci:1.444 Fri Sep 16 03:12:03 2022 +++ src/sys/dev/pci/files.pci Fri Oct 14 22:10:15 2022 @@ -1,10 +1,11 @@ -# $NetBSD: files.pci,v 1.444 2022/09/16 03:12:03 knakahara Exp $ +# $NetBSD: files.pci,v 1.445 2022/10/14 22:10:15 jmcneill Exp $ # # Config file and device description for machine-independent PCI code. # Included by ports that need it. Requires that the SCSI files be # defined first. defflag opt_pci.h PCIVERBOSE PCI_CONFIG_DUMP PCI_NETBSD_CONFIGURE + PCI_RESOURCE defparam opt_pci.h PCI_NETBSD_ENABLE_IDE defflag opt_bktr.h BKTR_430_FX_MODE BKTR_GPIO_ACCESS BKTR_NO_MSP_RESET @@ -20,6 +21,7 @@ attach pci at pcibus file dev/pci/pci.c pci needs-flag file dev/pci/pci_map.c pci file dev/pci/pci_quirks.c pci +file dev/pci/pci_resource.c pci & pci_resource file dev/pci/pci_subr.c pci file dev/pci/pci_stub.c pci file dev/pci/pci_usrreq.c pci Added files: Index: src/sys/dev/pci/pci_resource.c diff -u /dev/null src/sys/dev/pci/pci_resource.c:1.1 --- /dev/null Fri Oct 14 22:10:16 2022 +++ src/sys/dev/pci/pci_resource.c Fri Oct 14 22:10:15 2022 @@ -0,0 +1,1001 @@ +/* $NetBSD: pci_resource.c,v 1.1 2022/10/14 22:10:15 jmcneill Exp $ */ + +/*- + * Copyright (c) 2022 Jared McNeill <jmcne...@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * pci_resource.c -- + * + * Scan current PCI resource allocations and attempt to assign resources + * to devices that are not configured WITHOUT changing any configuration + * performed by system firmware. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: pci_resource.c,v 1.1 2022/10/14 22:10:15 jmcneill Exp $"); + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/systm.h> +#include <sys/kmem.h> +#include <sys/vmem.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pcidevs.h> +#include <dev/pci/pci_resource.h> + +#define DPRINT aprint_debug + +#if defined(PCI_RESOURCE_TEST_VENDOR_ID) && \ + defined(PCI_RESOURCE_TEST_PRODUCT_ID) +#define IS_TEST_DEVICE(_pd) \ + (PCI_VENDOR(pd->pd_id) == PCI_RESOURCE_TEST_VENDOR_ID && \ + PCI_PRODUCT(pd->pd_id) == PCI_RESOURCE_TEST_PRODUCT_ID) +#else +#define IS_TEST_DEVICE(_pd) 0 +#endif + +#define PCI_MAX_DEVICE 32 +#define PCI_MAX_FUNC 8 + +#define PCI_MAX_IORES 6 + +#define PCI_RANGE_FOREACH(_type) \ + for (u_int _type = PCI_RANGE_BUS; _type < NUM_PCI_RANGES; _type++) + +static const char *pci_range_typenames[NUM_PCI_RANGES] = { + [PCI_RANGE_BUS] = "bus", + [PCI_RANGE_IO] = "io", + [PCI_RANGE_MEM] = "mem", + [PCI_RANGE_PMEM] = "pmem", +}; + +struct pci_bus; + +struct pci_iores { + uint64_t pi_base; /* Base address */ + uint64_t pi_size; /* Resource size */ + uint8_t pi_type; /* PCI_MAPREG_TYPE_* */ + u_int pi_bar; /* PCI bar number */ + union { + struct { + uint8_t memtype; + bool prefetch; + } pi_mem; + }; +}; + +struct pci_device { + bool pd_present; /* Device is present */ + bool pd_configured; /* Device is configured */ + struct pci_bus *pd_bus; /* Parent bus */ + uint8_t pd_devno; /* Device number */ + uint8_t pd_funcno; /* Function number */ + pcitag_t pd_tag; /* PCI tag */ + + pcireg_t pd_id; /* Vendor ID, Device ID */ + pcireg_t pd_class; /* Revision ID, Class Code */ + pcireg_t pd_bhlc; /* BIST, Header Type, Primary Latency + * Timer, Cache Line Size */ + + struct pci_iores pd_iores[PCI_MAX_IORES]; + u_int pd_niores; + + bool pd_ppb; /* PCI-PCI bridge */ + union { + struct { + pcireg_t bridge_bus; + struct pci_resource_range ranges[NUM_PCI_RANGES]; + } pd_bridge; + }; +}; + +struct pci_bus { + uint8_t pb_busno; /* Bus number */ + struct pci_device *pb_bridge; /* Parent bridge, or NULL */ + + struct pci_device pb_device[PCI_MAX_DEVICE * PCI_MAX_FUNC]; + /* Devices on bus */ + u_int pb_lastdevno; /* Last device found */ + + struct pci_resource_range pb_ranges[NUM_PCI_RANGES]; + vmem_t *pb_res[NUM_PCI_RANGES]; +}; + +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_range pr_ranges[NUM_PCI_RANGES]; + vmem_t *pr_res[NUM_PCI_RANGES]; +}; + +static void pci_resource_scan_bus(struct pci_resources *, + struct pci_device *, uint8_t); + +#define PCI_SBDF_FMT "%04x:%02x:%02x.%u" +#define PCI_SBDF_FMT_ARGS(_pr, _pd) \ + pci_get_segment((_pr)->pr_pc), \ + (_pd)->pd_bus->pb_busno, \ + (_pd)->pd_devno, \ + (_pd)->pd_funcno + +#define PCICONF_RES_BUS(_pr, _busno) \ + ((_pr)->pr_bus[(_busno) - (_pr)->pr_startbus]) +#define PCICONF_BUS_DEVICE(_pb, _devno, _funcno) \ + (&(_pb)->pb_device[(_devno) * PCI_MAX_FUNC + (_funcno)]) + +/* + * pci_create_vmem -- + * + * Create a vmem arena covering the specified range, used for tracking + * PCI resources. + */ +static vmem_t * +pci_create_vmem(const char *name, bus_addr_t start, bus_addr_t end) +{ + vmem_t *arena; + + arena = vmem_create(name, 0, 0, 1, NULL, NULL, NULL, 0, VM_SLEEP, + IPL_NONE); + if (arena == NULL) { + return NULL; + } + + if (vmem_add(arena, start, end - start + 1, VM_SLEEP) != 0) { + vmem_destroy(arena); + arena = NULL; + } + + return arena; +} + +/* + * pci_new_bus -- + * + * Create a new PCI bus and initialize its resource ranges. + */ +static struct pci_bus * +pci_new_bus(struct pci_resources *pr, uint8_t busno, struct pci_device *bridge) +{ + struct pci_bus *pb; + struct pci_resource_range *ranges; + + pb = kmem_zalloc(sizeof(*pb), KM_SLEEP); + pb->pb_busno = busno; + pb->pb_bridge = bridge; + if (bridge == NULL) { + /* + * No additional constraints on resource allocations for + * the root bus. + */ + ranges = pr->pr_ranges; + } else { + /* + * Resource allocations for this bus are constrained by the + * bridge forwarding settings. + */ + ranges = bridge->pd_bridge.ranges; + } + memcpy(pb->pb_ranges, ranges, sizeof(pb->pb_ranges)); + + return pb; +} + +/* + * pci_resource_device_functions -- + * + * Returns the number of PCI functions for a a given bus and device. + */ +static uint8_t +pci_resource_device_functions(struct pci_resources *pr, + uint8_t busno, uint8_t devno) +{ + struct pci_bus *pb; + struct pci_device *pd; + + pb = PCICONF_RES_BUS(pr, busno); + pd = PCICONF_BUS_DEVICE(pb, devno, 0); + if (!pd->pd_present) { + return 0; + } + + return PCI_HDRTYPE_MULTIFN(pd->pd_bhlc) ? 8 : 1; +} + +/* + * pci_resource_device_print -- + * + * Log details about a device. + */ +static void +pci_resource_device_print(struct pci_resources *pr, + struct pci_device *pd) +{ + struct pci_iores *pi; + u_int res; + + DPRINT("PCI: " PCI_SBDF_FMT " %04x:%04x %02x 0x%06x", + PCI_SBDF_FMT_ARGS(pr, pd), + PCI_VENDOR(pd->pd_id), PCI_PRODUCT(pd->pd_id), + PCI_REVISION(pd->pd_class), (pd->pd_class >> 8) & 0xffffff); + + switch (PCI_HDRTYPE_TYPE(pd->pd_bhlc)) { + case PCI_HDRTYPE_DEVICE: + DPRINT(" (device)\n"); + break; + case PCI_HDRTYPE_PPB: + DPRINT(" (bridge %u -> %u-%u)\n", + PCI_BRIDGE_BUS_NUM_PRIMARY(pd->pd_bridge.bridge_bus), + PCI_BRIDGE_BUS_NUM_SECONDARY(pd->pd_bridge.bridge_bus), + PCI_BRIDGE_BUS_NUM_SUBORDINATE(pd->pd_bridge.bridge_bus)); + + if (pd->pd_bridge.ranges[PCI_RANGE_IO].end) { + DPRINT("PCI: " PCI_SBDF_FMT + " [bridge] window io %#" PRIx64 "-%#" PRIx64 + "\n", + PCI_SBDF_FMT_ARGS(pr, pd), + pd->pd_bridge.ranges[PCI_RANGE_IO].start, + pd->pd_bridge.ranges[PCI_RANGE_IO].end); + } + if (pd->pd_bridge.ranges[PCI_RANGE_MEM].end) { + DPRINT("PCI: " PCI_SBDF_FMT + " [bridge] window mem %#" PRIx64 "-%#" PRIx64 + " (non-prefetchable)\n", + PCI_SBDF_FMT_ARGS(pr, pd), + pd->pd_bridge.ranges[PCI_RANGE_MEM].start, + pd->pd_bridge.ranges[PCI_RANGE_MEM].end); + } + if (pd->pd_bridge.ranges[PCI_RANGE_PMEM].end) { + DPRINT("PCI: " PCI_SBDF_FMT + " [bridge] window mem %#" PRIx64 "-%#" PRIx64 + " (prefetchable)\n", + PCI_SBDF_FMT_ARGS(pr, pd), + pd->pd_bridge.ranges[PCI_RANGE_PMEM].start, + pd->pd_bridge.ranges[PCI_RANGE_PMEM].end); + } + + break; + default: + DPRINT(" (0x%02x)\n", PCI_HDRTYPE_TYPE(pd->pd_bhlc)); + } + + for (res = 0; res < pd->pd_niores; res++) { + pi = &pd->pd_iores[res]; + + DPRINT("PCI: " PCI_SBDF_FMT + " [device] resource BAR%u: %s @ %#" PRIx64 " size %#" + PRIx64, + PCI_SBDF_FMT_ARGS(pr, pd), pi->pi_bar, + pi->pi_type == PCI_MAPREG_TYPE_MEM ? "mem" : "io ", + pi->pi_base, pi->pi_size); + + if (pi->pi_type == PCI_MAPREG_TYPE_MEM) { + switch (pi->pi_mem.memtype) { + case PCI_MAPREG_MEM_TYPE_32BIT: + DPRINT(", 32-bit"); + break; + case PCI_MAPREG_MEM_TYPE_32BIT_1M: + DPRINT(", 32-bit (1M)"); + break; + case PCI_MAPREG_MEM_TYPE_64BIT: + DPRINT(", 64-bit"); + break; + } + DPRINT(" %sprefetchable", + pi->pi_mem.prefetch ? "" : "non-"); + } + DPRINT("\n"); + } +} + +/* + * pci_resource_scan_bar -- + * + * Determine the current BAR configuration for a given device. + */ +static void +pci_resource_scan_bar(struct pci_resources *pr, + struct pci_device *pd, pcireg_t mapreg_start, pcireg_t mapreg_end, + bool is_ppb) +{ + pci_chipset_tag_t pc = pr->pr_pc; + pcitag_t tag = pd->pd_tag; + pcireg_t mapreg = mapreg_start; + pcireg_t ocmd, cmd, bar[2], mask[2]; + uint64_t addr, size; + struct pci_iores *pi; + + if (!is_ppb) { + ocmd = cmd = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG); + cmd &= ~(PCI_COMMAND_MASTER_ENABLE | + PCI_COMMAND_MEM_ENABLE | + PCI_COMMAND_IO_ENABLE); + pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, cmd); + } + + while (mapreg < mapreg_end) { + u_int width = 4; + + bar[0] = pci_conf_read(pc, tag, mapreg); + pci_conf_write(pc, tag, mapreg, 0xffffffff); + mask[0] = pci_conf_read(pc, tag, mapreg); + pci_conf_write(pc, tag, mapreg, bar[0]); + + switch (PCI_MAPREG_TYPE(mask[0])) { + case PCI_MAPREG_TYPE_MEM: + switch (PCI_MAPREG_MEM_TYPE(mask[0])) { + case PCI_MAPREG_MEM_TYPE_32BIT: + case PCI_MAPREG_MEM_TYPE_32BIT_1M: + size = PCI_MAPREG_MEM_SIZE(mask[0]); + addr = PCI_MAPREG_MEM_ADDR(bar[0]); + break; + case PCI_MAPREG_MEM_TYPE_64BIT: + bar[1] = pci_conf_read(pc, tag, mapreg + 4); + pci_conf_write(pc, tag, mapreg + 4, 0xffffffff); + mask[1] = pci_conf_read(pc, tag, mapreg + 4); + pci_conf_write(pc, tag, mapreg + 4, bar[1]); + + size = PCI_MAPREG_MEM64_SIZE( + ((uint64_t)mask[1] << 32) | mask[0]); + addr = PCI_MAPREG_MEM64_ADDR( + ((uint64_t)bar[1] << 32) | bar[0]); + width = 8; + break; + default: + size = 0; + } + if (size > 0) { + pi = &pd->pd_iores[pd->pd_niores++]; + pi->pi_type = PCI_MAPREG_TYPE_MEM; + pi->pi_base = addr; + pi->pi_size = size; + pi->pi_bar = (mapreg - mapreg_start) / 4; + pi->pi_mem.memtype = + PCI_MAPREG_MEM_TYPE(mask[0]); + pi->pi_mem.prefetch = + PCI_MAPREG_MEM_PREFETCHABLE(mask[0]); + } + break; + case PCI_MAPREG_TYPE_IO: + size = PCI_MAPREG_IO_SIZE(mask[0] | 0xffff0000); + addr = PCI_MAPREG_IO_ADDR(bar[0]); + if (size > 0) { + pi = &pd->pd_iores[pd->pd_niores++]; + pi->pi_type = PCI_MAPREG_TYPE_IO; + pi->pi_base = addr; + pi->pi_size = size; + pi->pi_bar = (mapreg - mapreg_start) / 4; + } + break; + } + + KASSERT(pd->pd_niores <= PCI_MAX_IORES); + + mapreg += width; + } + + if (!is_ppb) { + pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, ocmd); + } +} + +/* + * pci_resource_scan_bridge -- + * + * Determine the current configuration of a PCI-PCI bridge. + */ +static void +pci_resource_scan_bridge(struct pci_resources *pr, + struct pci_device *pd) +{ + pci_chipset_tag_t pc = pr->pr_pc; + pcitag_t tag = pd->pd_tag; + pcireg_t res, reshigh; + + pd->pd_ppb = true; + + res = pci_conf_read(pc, tag, PCI_BRIDGE_BUS_REG); + pd->pd_bridge.bridge_bus = res; + pd->pd_bridge.ranges[PCI_RANGE_BUS].start = + PCI_BRIDGE_BUS_NUM_SECONDARY(res); + pd->pd_bridge.ranges[PCI_RANGE_BUS].end = + PCI_BRIDGE_BUS_NUM_SUBORDINATE(res); + + res = pci_conf_read(pc, tag, PCI_BRIDGE_STATIO_REG); + pd->pd_bridge.ranges[PCI_RANGE_IO].start = + PCI_BRIDGE_STATIO_IOBASE_ADDR(res); + pd->pd_bridge.ranges[PCI_RANGE_IO].end = + PCI_BRIDGE_STATIO_IOLIMIT_ADDR(res); + if (PCI_BRIDGE_IO_32BITS(res)) { + reshigh = pci_conf_read(pc, tag, PCI_BRIDGE_IOHIGH_REG); + pd->pd_bridge.ranges[PCI_RANGE_IO].start |= + __SHIFTOUT(reshigh, PCI_BRIDGE_IOHIGH_BASE) << 16; + pd->pd_bridge.ranges[PCI_RANGE_IO].end |= + __SHIFTOUT(reshigh, PCI_BRIDGE_IOHIGH_LIMIT) << 16; + } + if (pd->pd_bridge.ranges[PCI_RANGE_IO].start >= + pd->pd_bridge.ranges[PCI_RANGE_IO].end) { + pd->pd_bridge.ranges[PCI_RANGE_IO].start = 0; + pd->pd_bridge.ranges[PCI_RANGE_IO].end = 0; + } + + res = pci_conf_read(pc, tag, PCI_BRIDGE_MEMORY_REG); + pd->pd_bridge.ranges[PCI_RANGE_MEM].start = + PCI_BRIDGE_MEMORY_BASE_ADDR(res); + pd->pd_bridge.ranges[PCI_RANGE_MEM].end = + PCI_BRIDGE_MEMORY_LIMIT_ADDR(res); + if (pd->pd_bridge.ranges[PCI_RANGE_MEM].start >= + pd->pd_bridge.ranges[PCI_RANGE_MEM].end) { + pd->pd_bridge.ranges[PCI_RANGE_MEM].start = 0; + pd->pd_bridge.ranges[PCI_RANGE_MEM].end = 0; + } + + res = pci_conf_read(pc, tag, PCI_BRIDGE_PREFETCHMEM_REG); + pd->pd_bridge.ranges[PCI_RANGE_PMEM].start = + PCI_BRIDGE_PREFETCHMEM_BASE_ADDR(res); + pd->pd_bridge.ranges[PCI_RANGE_PMEM].end = + PCI_BRIDGE_PREFETCHMEM_LIMIT_ADDR(res); + if (PCI_BRIDGE_PREFETCHMEM_64BITS(res)) { + reshigh = pci_conf_read(pc, tag, + PCI_BRIDGE_PREFETCHBASEUP32_REG); + pd->pd_bridge.ranges[PCI_RANGE_PMEM].start |= + (uint64_t)reshigh << 32; + reshigh = pci_conf_read(pc, tag, + PCI_BRIDGE_PREFETCHLIMITUP32_REG); + pd->pd_bridge.ranges[PCI_RANGE_PMEM].end |= + (uint64_t)reshigh << 32; + } + if (pd->pd_bridge.ranges[PCI_RANGE_PMEM].start >= + pd->pd_bridge.ranges[PCI_RANGE_PMEM].end) { + pd->pd_bridge.ranges[PCI_RANGE_PMEM].start = 0; + pd->pd_bridge.ranges[PCI_RANGE_PMEM].end = 0; + } +} + +/* + * pci_resource_scan_device -- + * + * Determine the current configuration of a PCI device. + */ +static bool +pci_resource_scan_device(struct pci_resources *pr, + struct pci_bus *parent_bus, uint8_t devno, uint8_t funcno) +{ + struct pci_device *pd; + pcitag_t tag; + pcireg_t id, bridge_bus; + uint8_t sec_bus; + + tag = pci_make_tag(pr->pr_pc, parent_bus->pb_busno, devno, funcno); + id = pci_conf_read(pr->pr_pc, tag, PCI_ID_REG); + if (PCI_VENDOR(id) == PCI_VENDOR_INVALID) { + return false; + } + + pd = PCICONF_BUS_DEVICE(parent_bus, devno, funcno); + pd->pd_present = true; + pd->pd_bus = parent_bus; + pd->pd_tag = tag; + pd->pd_devno = devno; + pd->pd_funcno = funcno; + pd->pd_id = id; + pd->pd_class = pci_conf_read(pr->pr_pc, tag, PCI_CLASS_REG); + pd->pd_bhlc = pci_conf_read(pr->pr_pc, tag, PCI_BHLC_REG); + + switch (PCI_HDRTYPE_TYPE(pd->pd_bhlc)) { + case PCI_HDRTYPE_DEVICE: + pci_resource_scan_bar(pr, pd, PCI_MAPREG_START, + PCI_MAPREG_END, false); + break; + case PCI_HDRTYPE_PPB: + pci_resource_scan_bar(pr, pd, PCI_MAPREG_START, + PCI_MAPREG_PPB_END, true); + pci_resource_scan_bridge(pr, pd); + break; + } + + pci_resource_device_print(pr, pd); + + if (PCI_HDRTYPE_TYPE(pd->pd_bhlc) == PCI_HDRTYPE_PPB && + PCI_CLASS(pd->pd_class) == PCI_CLASS_BRIDGE && + 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) { + pci_resource_scan_bus(pr, pd, sec_bus); + } + } + + return true; +} + +/* + * pci_resource_scan_bus -- + * + * Enumerate devices on a bus, recursively. + */ +static void +pci_resource_scan_bus(struct pci_resources *pr, + struct pci_device *bridge_dev, uint8_t busno) +{ + struct pci_bus *pb; + uint8_t devno, funcno; + uint8_t nfunc; + + KASSERT(busno >= pr->pr_startbus); + KASSERT(busno <= pr->pr_endbus); + + if (PCICONF_RES_BUS(pr, busno) != NULL) { + /* + * Firmware has configured more than one bridge with the + * same secondary bus number. + */ + panic("Bus %u already scanned (firmware bug!)", busno); + return; + } + + pb = pci_new_bus(pr, busno, bridge_dev); + PCICONF_RES_BUS(pr, busno) = pb; + + for (devno = 0; devno < PCI_MAX_DEVICE; devno++) { + if (!pci_resource_scan_device(pr, pb, devno, 0)) { + continue; + } + pb->pb_lastdevno = devno; + + nfunc = pci_resource_device_functions(pr, busno, devno); + for (funcno = 1; funcno < nfunc; funcno++) { + pci_resource_scan_device(pr, pb, devno, funcno); + } + } +} + +/* + * pci_resource_claim -- + * + * Claim a resource from a vmem arena. This is called to inform the + * resource manager about resources already configured by system firmware. + */ +static int +pci_resource_claim(vmem_t *arena, vmem_addr_t start, vmem_addr_t end) +{ + KASSERT(end >= start); + + return vmem_xalloc(arena, end - start + 1, 0, 0, 0, start, end, + VM_BESTFIT | VM_NOSLEEP, NULL); +} + +/* + * pci_resource_alloc -- + * + * Allocate a resource from a vmem arena. This is called when configuring + * devices that were not already configured by system firmware. + */ +static int +pci_resource_alloc(vmem_t *arena, vmem_size_t size, vmem_size_t align, + uint64_t *base) +{ + vmem_addr_t addr; + int error; + + KASSERT(size != 0); + + error = vmem_xalloc(arena, size, align, 0, 0, VMEM_ADDR_MIN, + VMEM_ADDR_MAX, VM_BESTFIT | VM_NOSLEEP, &addr); + if (error == 0) { + *base = (uint64_t)addr; + } + + return error; +} + +/* + * pci_resource_init_device -- + * + * Discover resources assigned by system firmware, notify the resource + * manager of these ranges, and determine if the device has additional + * resources that need to be allocated. + */ +static void +pci_resource_init_device(struct pci_resources *pr, + struct pci_device *pd) +{ + struct pci_iores *pi; + struct pci_bus *pb = pd->pd_bus; + vmem_t *res_io = pb->pb_res[PCI_RANGE_IO]; + vmem_t *res_mem = pb->pb_res[PCI_RANGE_MEM]; + vmem_t *res_pmem = pb->pb_res[PCI_RANGE_PMEM]; + pcireg_t cmd; + u_int enabled, required; + u_int iores; + int error; + + KASSERT(pd->pd_present); + + if (IS_TEST_DEVICE(pd)) { + cmd = pci_conf_read(pr->pr_pc, pd->pd_tag, + PCI_COMMAND_STATUS_REG); + cmd &= ~(PCI_COMMAND_MEM_ENABLE|PCI_COMMAND_IO_ENABLE| + PCI_COMMAND_MASTER_ENABLE); + pci_conf_write(pr->pr_pc, pd->pd_tag, PCI_COMMAND_STATUS_REG, + cmd); + } + + enabled = required = 0; + cmd = pci_conf_read(pr->pr_pc, pd->pd_tag, PCI_COMMAND_STATUS_REG); + if ((cmd & PCI_COMMAND_MEM_ENABLE) != 0) { + enabled |= __BIT(PCI_MAPREG_TYPE_MEM); + } + if ((cmd & PCI_COMMAND_IO_ENABLE) != 0) { + enabled |= __BIT(PCI_MAPREG_TYPE_IO); + } + + for (iores = 0; iores < pd->pd_niores; iores++) { + pi = &pd->pd_iores[iores]; + + required |= __BIT(pi->pi_type); + + if (IS_TEST_DEVICE(pd)) { + pci_conf_write(pr->pr_pc, pd->pd_tag, + PCI_BAR(pi->pi_bar), 0); + continue; + } + if ((enabled & __BIT(pi->pi_type)) == 0) { + continue; + } + + if (pi->pi_type == PCI_MAPREG_TYPE_IO) { + error = res_io == NULL ? ERANGE : + pci_resource_claim(res_io, pi->pi_base, + pi->pi_base + pi->pi_size - 1); + if (error) { + DPRINT("PCI: " PCI_SBDF_FMT " [device] io " + " %#" PRIx64 "-%#" PRIx64 + " invalid (%d)\n", + PCI_SBDF_FMT_ARGS(pr, pd), + pi->pi_base, + pi->pi_base + pi->pi_size - 1, + error); + } + continue; + } + + KASSERT(pi->pi_type == PCI_MAPREG_TYPE_MEM); + error = ERANGE; + if (pi->pi_mem.prefetch && res_pmem != NULL) { + error = pci_resource_claim(res_pmem, pi->pi_base, + pi->pi_base + pi->pi_size - 1); + } + if (error && res_mem != NULL) { + error = pci_resource_claim(res_mem, pi->pi_base, + pi->pi_base + pi->pi_size - 1); + } + if (error) { + DPRINT("PCI: " PCI_SBDF_FMT " [device] mem" + " (%sprefetchable)" + " %#" PRIx64 "-%#" PRIx64 + " invalid (%d)\n", + PCI_SBDF_FMT_ARGS(pr, pd), + pi->pi_mem.prefetch ? "" : "non-", + pi->pi_base, + pi->pi_base + pi->pi_size - 1, + error); + } + } + + pd->pd_configured = (enabled & required) == required; + + if (!pd->pd_configured) { + DPRINT("PCI: " PCI_SBDF_FMT " [device] " + "not configured by firmware\n", + PCI_SBDF_FMT_ARGS(pr, pd)); + } +} + +/* + * pci_resource_init_bus -- + * + * Discover resources in use on a given bus, recursively. + */ +static void +pci_resource_init_bus(struct pci_resources *pr, uint8_t busno) +{ + struct pci_bus *pb, *parent_bus; + struct pci_device *pd, *bridge; + uint8_t devno, funcno; + uint8_t nfunc; + int error; + + KASSERT(busno >= pr->pr_startbus); + KASSERT(busno <= pr->pr_endbus); + + pb = PCICONF_RES_BUS(pr, busno); + bridge = pb->pb_bridge; + + KASSERT(pb != NULL); + KASSERT((busno == pr->pr_startbus) == (bridge == NULL)); + + if (bridge == NULL) { + /* Use resources provided by firmware. */ + PCI_RANGE_FOREACH(prtype) { + pb->pb_res[prtype] = pr->pr_res[prtype]; + pr->pr_res[prtype] = NULL; + } + } else { + /* + * Using the resources configured in to the bridge by + * firmware, claim the resources on the parent bus and + * create a new vmem arena for the secondary bus. + */ + KASSERT(bridge->pd_bus != NULL); + parent_bus = bridge->pd_bus; + PCI_RANGE_FOREACH(prtype) { + if (parent_bus->pb_res[prtype] == NULL || + !bridge->pd_bridge.ranges[prtype].end) { + continue; + } + error = pci_resource_claim( + parent_bus->pb_res[prtype], + bridge->pd_bridge.ranges[prtype].start, + bridge->pd_bridge.ranges[prtype].end); + if (error == 0) { + pb->pb_res[prtype] = pci_create_vmem( + pci_resource_typename(prtype), + bridge->pd_bridge.ranges[prtype].start, + bridge->pd_bridge.ranges[prtype].end); + KASSERT(pb->pb_res[prtype] != NULL); + } else { + DPRINT("PCI: " PCI_SBDF_FMT " bridge (bus %u)" + " %-4s %#" PRIx64 "-%#" PRIx64 + " invalid\n", + PCI_SBDF_FMT_ARGS(pr, bridge), busno, + pci_resource_typename(prtype), + bridge->pd_bridge.ranges[prtype].start, + bridge->pd_bridge.ranges[prtype].end); + } + } + } + + for (devno = 0; devno <= pb->pb_lastdevno; devno++) { + KASSERT(devno < PCI_MAX_DEVICE); + nfunc = pci_resource_device_functions(pr, busno, devno); + for (funcno = 0; funcno < nfunc; funcno++) { + pd = PCICONF_BUS_DEVICE(pb, devno, funcno); + if (!pd->pd_present) { + continue; + } + if (pd->pd_ppb) { + uint8_t sec_bus = PCI_BRIDGE_BUS_NUM_SECONDARY( + pd->pd_bridge.bridge_bus); + pci_resource_init_bus(pr, sec_bus); + } + pci_resource_init_device(pr, pd); + } + } +} + +/* + * pci_resource_probe -- + * + * Scan for PCI devices and initialize the resource manager. + */ +static void +pci_resource_probe(struct pci_resources *pr, + const struct pci_resource_info *info) +{ + uint8_t startbus = (uint8_t)info->ranges[PCI_RANGE_BUS].start; + uint8_t endbus = (uint8_t)info->ranges[PCI_RANGE_BUS].end; + u_int nbus; + + KASSERT(startbus <= endbus); + KASSERT(pr->pr_bus == NULL); + + nbus = endbus - startbus + 1; + + 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); + memcpy(pr->pr_ranges, info->ranges, sizeof(pr->pr_ranges)); + PCI_RANGE_FOREACH(prtype) { + if (prtype == PCI_RANGE_BUS || info->ranges[prtype].end) { + pr->pr_res[prtype] = pci_create_vmem( + pci_resource_typename(prtype), + info->ranges[prtype].start, + info->ranges[prtype].end); + KASSERT(pr->pr_res[prtype] != NULL); + } + } + + /* Scan devices */ + pci_resource_scan_bus(pr, NULL, pr->pr_startbus); + + /* + * Create per-bus resource pools and remove ranges that are already + * in use by devices and downstream bridges. + */ + pci_resource_init_bus(pr, pr->pr_startbus); +} + +/* + * pci_resource_alloc_device -- + * + * Attempt to allocate resources for a given device. + */ +static void +pci_resource_alloc_device(struct pci_resources *pr, struct pci_device *pd) +{ + struct pci_iores *pi; + vmem_t *arena; + pcireg_t cmd, ocmd, base; + uint64_t addr; + u_int enabled; + u_int res; + u_int align; + int error; + + enabled = 0; + ocmd = cmd = pci_conf_read(pr->pr_pc, pd->pd_tag, + PCI_COMMAND_STATUS_REG); + if ((cmd & PCI_COMMAND_MEM_ENABLE) != 0) { + enabled |= __BIT(PCI_MAPREG_TYPE_MEM); + } + if ((cmd & PCI_COMMAND_IO_ENABLE) != 0) { + enabled |= __BIT(PCI_MAPREG_TYPE_IO); + } + + for (res = 0; res < pd->pd_niores; res++) { + pi = &pd->pd_iores[res]; + + if ((enabled & __BIT(pi->pi_type)) != 0) { + continue; + } + + if (pi->pi_type == PCI_MAPREG_TYPE_IO) { + arena = pd->pd_bus->pb_res[PCI_RANGE_IO]; + align = uimax(pi->pi_size, 4); + } else { + KASSERT(pi->pi_type == PCI_MAPREG_TYPE_MEM); + arena = NULL; + align = uimax(pi->pi_size, 16); + if (pi->pi_mem.prefetch) { + arena = pd->pd_bus->pb_res[PCI_RANGE_PMEM]; + } + if (arena == NULL) { + arena = pd->pd_bus->pb_res[PCI_RANGE_MEM]; + } + } + if (arena == NULL) { + DPRINT("PCI: " PCI_SBDF_FMT " BAR%u failed to" + " allocate %#" PRIx64 " bytes (no arena)\n", + PCI_SBDF_FMT_ARGS(pr, pd), + pi->pi_bar, pi->pi_size); + return; + } + error = pci_resource_alloc(arena, pi->pi_size, align, &addr); + if (error != 0) { + DPRINT("PCI: " PCI_SBDF_FMT " BAR%u failed to" + " allocate %#" PRIx64 " bytes (no space)\n", + PCI_SBDF_FMT_ARGS(pr, pd), + pi->pi_bar, pi->pi_size); + return; + } + DPRINT("PCI: " PCI_SBDF_FMT " BAR%u assigned range" + " 0x%#" PRIx64 "-0x%#" PRIx64 "\n", + PCI_SBDF_FMT_ARGS(pr, pd), + pi->pi_bar, addr, addr + pi->pi_size - 1); + + if (pi->pi_type == PCI_MAPREG_TYPE_IO) { + cmd |= PCI_COMMAND_IO_ENABLE; + pci_conf_write(pr->pr_pc, pd->pd_tag, + PCI_BAR(pi->pi_bar), + PCI_MAPREG_IO_ADDR(addr) | PCI_MAPREG_TYPE_IO); + } else { + cmd |= PCI_COMMAND_MEM_ENABLE; + base = pci_conf_read(pr->pr_pc, pd->pd_tag, + PCI_BAR(pi->pi_bar)); + base = PCI_MAPREG_MEM_ADDR(addr) | + PCI_MAPREG_MEM_TYPE(base); + pci_conf_write(pr->pr_pc, pd->pd_tag, + PCI_BAR(pi->pi_bar), base); + if (pi->pi_mem.memtype == PCI_MAPREG_MEM_TYPE_64BIT) { + base = (pcireg_t) + (PCI_MAPREG_MEM64_ADDR(addr) >> 32); + pci_conf_write(pr->pr_pc, pd->pd_tag, + PCI_BAR(pi->pi_bar + 1), base); + } + } + } + + if (ocmd != cmd) { + pci_conf_write(pr->pr_pc, pd->pd_tag, + PCI_COMMAND_STATUS_REG, cmd); + } +} + +/* + * pci_resource_alloc_bus -- + * + * Attempt to assign resources to all devices on a given bus, recursively. + */ +static void +pci_resource_alloc_bus(struct pci_resources *pr, uint8_t busno) +{ + struct pci_bus *pb = PCICONF_RES_BUS(pr, busno); + struct pci_device *pd; + uint8_t devno, funcno; + + for (devno = 0; devno <= pb->pb_lastdevno; devno++) { + for (funcno = 0; funcno < 8; funcno++) { + pd = PCICONF_BUS_DEVICE(pb, devno, funcno); + if (!pd->pd_present) { + if (funcno == 0) { + break; + } + continue; + } + if (!pd->pd_configured) { + pci_resource_alloc_device(pr, pd); + } + if (pd->pd_ppb) { + uint8_t sec_bus = PCI_BRIDGE_BUS_NUM_SECONDARY( + pd->pd_bridge.bridge_bus); + pci_resource_alloc_bus(pr, sec_bus); + } + } + } +} + +/* + * pci_resource_init -- + * + * Public interface to PCI resource manager. Scans for available devices + * and assigns resources. + */ +void +pci_resource_init(const struct pci_resource_info *info) +{ + struct pci_resources pr = {}; + + pci_resource_probe(&pr, info); + pci_resource_alloc_bus(&pr, pr.pr_startbus); +} + +/* + * pci_resource_typename -- + * + * Return a string description of a PCI range type. + */ +const char * +pci_resource_typename(enum pci_range_type prtype) +{ + KASSERT(prtype < NUM_PCI_RANGES); + return pci_range_typenames[prtype]; +} Index: src/sys/dev/pci/pci_resource.h diff -u /dev/null src/sys/dev/pci/pci_resource.h:1.1 --- /dev/null Fri Oct 14 22:10:16 2022 +++ src/sys/dev/pci/pci_resource.h Fri Oct 14 22:10:15 2022 @@ -0,0 +1,50 @@ +/* $NetBSD: pci_resource.h,v 1.1 2022/10/14 22:10:15 jmcneill Exp $ */ + +/*- + * Copyright (c) 2022 Jared McNeill <jmcne...@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#pragma once + +enum pci_range_type { + PCI_RANGE_BUS, + PCI_RANGE_IO, + PCI_RANGE_MEM, + PCI_RANGE_PMEM, + NUM_PCI_RANGES +}; + +struct pci_resource_range { + uint64_t start; + uint64_t end; +}; + +struct pci_resource_info { + pci_chipset_tag_t pc; + struct pci_resource_range ranges[NUM_PCI_RANGES]; +}; + +void pci_resource_init(const struct pci_resource_info *); +const char * pci_resource_typename(enum pci_range_type);