Index: head/sys/amd64/include/pci_cfgreg.h =================================================================== --- head/sys/amd64/include/pci_cfgreg.h (revision 111067) +++ head/sys/amd64/include/pci_cfgreg.h (revision 111068) @@ -1,56 +1,54 @@ /* * Copyright (c) 1997, Stefan Esser * 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 unmodified, 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. * * $FreeBSD$ * */ #define CONF1_ADDR_PORT 0x0cf8 #define CONF1_DATA_PORT 0x0cfc #define CONF1_ENABLE 0x80000000ul #define CONF1_ENABLE_CHK 0x80000000ul #define CONF1_ENABLE_MSK 0x7ff00000ul #define CONF1_ENABLE_CHK1 0xff000001ul #define CONF1_ENABLE_MSK1 0x80000001ul #define CONF1_ENABLE_RES1 0x80000000ul #define CONF2_ENABLE_PORT 0x0cf8 #ifdef PC98 #define CONF2_FORWARD_PORT 0x0cf9 #else #define CONF2_FORWARD_PORT 0x0cfa #endif #define CONF2_ENABLE_CHK 0x0e #define CONF2_ENABLE_RES 0x0e int pci_cfgregopen(void); u_int32_t pci_cfgregread(int bus, int slot, int func, int reg, int bytes); void pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes); int pci_cfgintr(int bus, int device, int pin, int oldirq); -int pci_kill_pcibios(void); -int pci_pcibios_active(void); int pci_probe_route_table(int bus); Index: head/sys/amd64/pci/pci_bus.c =================================================================== --- head/sys/amd64/pci/pci_bus.c (revision 111067) +++ head/sys/amd64/pci/pci_bus.c (revision 111068) @@ -1,689 +1,671 @@ /* * Copyright (c) 1997, Stefan Esser * 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 unmodified, 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. * * $FreeBSD$ * */ #include "opt_cpu.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pcib_if.h" static int pcibios_pcib_route_interrupt(device_t pcib, device_t dev, int pin); static int nexus_pcib_maxslots(device_t dev) { return 31; } /* read configuration space register */ static u_int32_t nexus_pcib_read_config(device_t dev, int bus, int slot, int func, int reg, int bytes) { return(pci_cfgregread(bus, slot, func, reg, bytes)); } /* write configuration space register */ static void nexus_pcib_write_config(device_t dev, int bus, int slot, int func, int reg, u_int32_t data, int bytes) { pci_cfgregwrite(bus, slot, func, reg, data, bytes); } /* route interrupt */ static int nexus_pcib_route_interrupt(device_t pcib, device_t dev, int pin) { return (pcibios_pcib_route_interrupt(pcib, dev, pin)); } static const char * nexus_pcib_is_host_bridge(int bus, int slot, int func, u_int32_t id, u_int8_t class, u_int8_t subclass, u_int8_t *busnum) { const char *s = NULL; static u_int8_t pxb[4]; /* hack for 450nx */ *busnum = 0; switch (id) { case 0x12258086: s = "Intel 824?? host to PCI bridge"; /* XXX This is a guess */ /* *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x41, 1); */ *busnum = bus; break; case 0x71208086: s = "Intel 82810 (i810 GMCH) Host To Hub bridge"; break; case 0x71228086: s = "Intel 82810-DC100 (i810-DC100 GMCH) Host To Hub bridge"; break; case 0x71248086: s = "Intel 82810E (i810E GMCH) Host To Hub bridge"; break; case 0x11308086: s = "Intel 82815 (i815 GMCH) Host To Hub bridge"; break; case 0x71808086: s = "Intel 82443LX (440 LX) host to PCI bridge"; break; case 0x71908086: s = "Intel 82443BX (440 BX) host to PCI bridge"; break; case 0x71928086: s = "Intel 82443BX host to PCI bridge (AGP disabled)"; break; case 0x71948086: s = "Intel 82443MX host to PCI bridge"; break; case 0x71a08086: s = "Intel 82443GX host to PCI bridge"; break; case 0x71a18086: s = "Intel 82443GX host to AGP bridge"; break; case 0x71a28086: s = "Intel 82443GX host to PCI bridge (AGP disabled)"; break; case 0x84c48086: s = "Intel 82454KX/GX (Orion) host to PCI bridge"; *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x4a, 1); break; case 0x84ca8086: /* * For the 450nx chipset, there is a whole bundle of * things pretending to be host bridges. The MIOC will * be seen first and isn't really a pci bridge (the * actual busses are attached to the PXB's). We need to * read the registers of the MIOC to figure out the * bus numbers for the PXB channels. * * Since the MIOC doesn't have a pci bus attached, we * pretend it wasn't there. */ pxb[0] = nexus_pcib_read_config(0, bus, slot, func, 0xd0, 1); /* BUSNO[0] */ pxb[1] = nexus_pcib_read_config(0, bus, slot, func, 0xd1, 1) + 1; /* SUBA[0]+1 */ pxb[2] = nexus_pcib_read_config(0, bus, slot, func, 0xd3, 1); /* BUSNO[1] */ pxb[3] = nexus_pcib_read_config(0, bus, slot, func, 0xd4, 1) + 1; /* SUBA[1]+1 */ return NULL; case 0x84cb8086: switch (slot) { case 0x12: s = "Intel 82454NX PXB#0, Bus#A"; *busnum = pxb[0]; break; case 0x13: s = "Intel 82454NX PXB#0, Bus#B"; *busnum = pxb[1]; break; case 0x14: s = "Intel 82454NX PXB#1, Bus#A"; *busnum = pxb[2]; break; case 0x15: s = "Intel 82454NX PXB#1, Bus#B"; *busnum = pxb[3]; break; } break; /* AMD -- vendor 0x1022 */ case 0x30001022: s = "AMD Elan SC520 host to PCI bridge"; #ifdef CPU_ELAN init_AMD_Elan_sc520(); #else printf( "*** WARNING: missing CPU_ELAN -- timekeeping may be wrong\n"); #endif break; case 0x70061022: s = "AMD-751 host to PCI bridge"; break; case 0x700e1022: s = "AMD-761 host to PCI bridge"; break; /* SiS -- vendor 0x1039 */ case 0x04961039: s = "SiS 85c496"; break; case 0x04061039: s = "SiS 85c501"; break; case 0x06011039: s = "SiS 85c601"; break; case 0x55911039: s = "SiS 5591 host to PCI bridge"; break; case 0x00011039: s = "SiS 5591 host to AGP bridge"; break; /* VLSI -- vendor 0x1004 */ case 0x00051004: s = "VLSI 82C592 Host to PCI bridge"; break; /* XXX Here is MVP3, I got the datasheet but NO M/B to test it */ /* totally. Please let me know if anything wrong. -F */ /* XXX need info on the MVP3 -- any takers? */ case 0x05981106: s = "VIA 82C598MVP (Apollo MVP3) host bridge"; break; /* AcerLabs -- vendor 0x10b9 */ /* Funny : The datasheet told me vendor id is "10b8",sub-vendor */ /* id is '10b9" but the register always shows "10b9". -Foxfair */ case 0x154110b9: s = "AcerLabs M1541 (Aladdin-V) PCI host bridge"; break; /* OPTi -- vendor 0x1045 */ case 0xc7011045: s = "OPTi 82C700 host to PCI bridge"; break; case 0xc8221045: s = "OPTi 82C822 host to PCI Bridge"; break; /* ServerWorks -- vendor 0x1166 */ case 0x00051166: s = "ServerWorks NB6536 2.0HE host to PCI bridge"; *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x44, 1); break; case 0x00061166: /* FALLTHROUGH */ case 0x00081166: /* FALLTHROUGH */ case 0x02011166: /* FALLTHROUGH */ case 0x010f1014: /* IBM re-badged ServerWorks chipset */ s = "ServerWorks host to PCI bridge"; *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x44, 1); break; case 0x00091166: s = "ServerWorks NB6635 3.0LE host to PCI bridge"; *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x44, 1); break; case 0x00101166: s = "ServerWorks CIOB30 host to PCI bridge"; *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x44, 1); break; case 0x00111166: /* FALLTHROUGH */ case 0x03021014: /* IBM re-badged ServerWorks chipset */ s = "ServerWorks CMIC-HE host to PCI-X bridge"; *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x44, 1); break; /* XXX unknown chipset, but working */ case 0x00171166: /* FALLTHROUGH */ case 0x01011166: s = "ServerWorks host to PCI bridge(unknown chipset)"; *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x44, 1); break; /* Integrated Micro Solutions -- vendor 0x10e0 */ case 0x884910e0: s = "Integrated Micro Solutions VL Bridge"; break; default: if (class == PCIC_BRIDGE && subclass == PCIS_BRIDGE_HOST) s = "Host to PCI bridge"; break; } return s; } /* * Scan the first pci bus for host-pci bridges and add pcib instances * to the nexus for each bridge. */ static void nexus_pcib_identify(driver_t *driver, device_t parent) { int bus, slot, func; u_int8_t hdrtype; int found = 0; int pcifunchigh; int found824xx = 0; int found_orion = 0; - int found_pcibios_flaming_death = 0; device_t child; devclass_t pci_devclass; if (pci_cfgregopen() == 0) return; /* * Check to see if we haven't already had a PCI bus added * via some other means. If we have, bail since otherwise * we're going to end up duplicating it. */ if ((pci_devclass = devclass_find("pci")) && devclass_get_device(pci_devclass, 0)) return; bus = 0; retry: for (slot = 0; slot <= PCI_SLOTMAX; slot++) { func = 0; hdrtype = nexus_pcib_read_config(0, bus, slot, func, PCIR_HEADERTYPE, 1); if ((hdrtype & PCIM_MFDEV) && (!found_orion || hdrtype != 0xff)) pcifunchigh = 7; else pcifunchigh = 0; for (func = 0; func <= pcifunchigh; func++) { /* * Read the IDs and class from the device. */ u_int32_t id; u_int8_t class, subclass, busnum; const char *s; device_t *devs; int ndevs, i; id = nexus_pcib_read_config(0, bus, slot, func, PCIR_DEVVENDOR, 4); if (id == -1) continue; class = nexus_pcib_read_config(0, bus, slot, func, PCIR_CLASS, 1); subclass = nexus_pcib_read_config(0, bus, slot, func, PCIR_SUBCLASS, 1); s = nexus_pcib_is_host_bridge(bus, slot, func, id, class, subclass, &busnum); if (s == NULL) continue; /* * Check to see if the physical bus has already * been seen. Eg: hybrid 32 and 64 bit host * bridges to the same logical bus. */ if (device_get_children(parent, &devs, &ndevs) == 0) { for (i = 0; s != NULL && i < ndevs; i++) { if (strcmp(device_get_name(devs[i]), "pcib") != 0) continue; if (legacy_get_pcibus(devs[i]) == busnum) s = NULL; } free(devs, M_TEMP); } if (s == NULL) continue; /* * Add at priority 100 to make sure we * go after any motherboard resources */ child = BUS_ADD_CHILD(parent, 100, "pcib", busnum); device_set_desc(child, s); legacy_set_pcibus(child, busnum); found = 1; if (id == 0x12258086) found824xx = 1; if (id == 0x84c48086) found_orion = 1; } } if (found824xx && bus == 0) { bus++; - goto retry; - } - - /* - * This is just freaking brilliant! Some BIOS writers have - * decided that we must be forcibly prevented from using - * PCIBIOS to query the host->pci bridges. If you try and - * access configuration registers, it pretends there is - * no pci device at that bus:device:function address. - */ - if (!found && pci_pcibios_active() && !found_pcibios_flaming_death) { - /* retry with the old mechanism, or fail */ - if (pci_kill_pcibios() == 0) - return; - printf("nexus_pcib_identify: found broken PCIBIOS - disabling it and retrying.\n"); - printf("nexus_pcib_identify: it is bogusly censoring host->pci bridges.\n"); - found_pcibios_flaming_death = 1; goto retry; } /* * Make sure we add at least one bridge since some old * hardware doesn't actually have a host-pci bridge device. * Note that pci_cfgregopen() thinks we have PCI devices.. */ if (!found) { if (bootverbose) printf( "nexus_pcib_identify: no bridge found, adding pcib0 anyway\n"); child = BUS_ADD_CHILD(parent, 100, "pcib", 0); legacy_set_pcibus(child, 0); } } static int nexus_pcib_probe(device_t dev) { if (pci_cfgregopen() == 0) return ENXIO; return 0; } static int nexus_pcib_attach(device_t dev) { device_t child; child = device_add_child(dev, "pci", pcib_get_bus(dev)); return bus_generic_attach(dev); } static int nexus_pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) { switch (which) { case PCIB_IVAR_BUS: *result = legacy_get_pcibus(dev); return 0; } return ENOENT; } static int nexus_pcib_write_ivar(device_t dev, device_t child, int which, uintptr_t value) { switch (which) { case PCIB_IVAR_BUS: legacy_set_pcibus(dev, value); return 0; } return ENOENT; } static device_method_t nexus_pcib_methods[] = { /* Device interface */ DEVMETHOD(device_identify, nexus_pcib_identify), DEVMETHOD(device_probe, nexus_pcib_probe), DEVMETHOD(device_attach, nexus_pcib_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_read_ivar, nexus_pcib_read_ivar), DEVMETHOD(bus_write_ivar, nexus_pcib_write_ivar), DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), /* pcib interface */ DEVMETHOD(pcib_maxslots, nexus_pcib_maxslots), DEVMETHOD(pcib_read_config, nexus_pcib_read_config), DEVMETHOD(pcib_write_config, nexus_pcib_write_config), DEVMETHOD(pcib_route_interrupt, nexus_pcib_route_interrupt), { 0, 0 } }; static driver_t nexus_pcib_driver = { "pcib", nexus_pcib_methods, 1, }; DRIVER_MODULE(pcib, legacy, nexus_pcib_driver, pcib_devclass, 0, 0); /* * Provide a device to "eat" the host->pci bridges that we dug up above * and stop them showing up twice on the probes. This also stops them * showing up as 'none' in pciconf -l. */ static int pci_hostb_probe(device_t dev) { u_int32_t id; id = pci_get_devid(dev); switch (id) { /* VIA VT82C596 Power Managment Function */ case 0x30501106: return ENXIO; default: break; } if (pci_get_class(dev) == PCIC_BRIDGE && pci_get_subclass(dev) == PCIS_BRIDGE_HOST) { device_set_desc(dev, "Host to PCI bridge"); device_quiet(dev); return -10000; } return ENXIO; } static int pci_hostb_attach(device_t dev) { return 0; } static device_method_t pci_hostb_methods[] = { /* Device interface */ DEVMETHOD(device_probe, pci_hostb_probe), DEVMETHOD(device_attach, pci_hostb_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), { 0, 0 } }; static driver_t pci_hostb_driver = { "hostb", pci_hostb_methods, 1, }; static devclass_t pci_hostb_devclass; DRIVER_MODULE(hostb, pci, pci_hostb_driver, pci_hostb_devclass, 0, 0); /* * Install placeholder to claim the resources owned by the * PCI bus interface. This could be used to extract the * config space registers in the extreme case where the PnP * ID is available and the PCI BIOS isn't, but for now we just * eat the PnP ID and do nothing else. * * XXX we should silence this probe, as it will generally confuse * people. */ static struct isa_pnp_id pcibus_pnp_ids[] = { { 0x030ad041 /* PNP0A03 */, "PCI Bus" }, { 0 } }; static int pcibus_pnp_probe(device_t dev) { int result; if ((result = ISA_PNP_PROBE(device_get_parent(dev), dev, pcibus_pnp_ids)) <= 0) device_quiet(dev); return(result); } static int pcibus_pnp_attach(device_t dev) { return(0); } static device_method_t pcibus_pnp_methods[] = { /* Device interface */ DEVMETHOD(device_probe, pcibus_pnp_probe), DEVMETHOD(device_attach, pcibus_pnp_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), { 0, 0 } }; static driver_t pcibus_pnp_driver = { "pcibus_pnp", pcibus_pnp_methods, 1, /* no softc */ }; static devclass_t pcibus_pnp_devclass; DRIVER_MODULE(pcibus_pnp, isa, pcibus_pnp_driver, pcibus_pnp_devclass, 0, 0); /* * Provide a PCI-PCI bridge driver for PCI busses behind PCI-PCI bridges * that appear in the PCIBIOS Interrupt Routing Table to use the routing * table for interrupt routing when possible. */ static int pcibios_pcib_probe(device_t bus); static device_method_t pcibios_pcib_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, pcibios_pcib_probe), DEVMETHOD(device_attach, pcib_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_read_ivar, pcib_read_ivar), DEVMETHOD(bus_write_ivar, pcib_write_ivar), DEVMETHOD(bus_alloc_resource, pcib_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), /* pcib interface */ DEVMETHOD(pcib_maxslots, pcib_maxslots), DEVMETHOD(pcib_read_config, pcib_read_config), DEVMETHOD(pcib_write_config, pcib_write_config), DEVMETHOD(pcib_route_interrupt, pcibios_pcib_route_interrupt), {0, 0} }; static driver_t pcibios_pcib_driver = { "pcib", pcibios_pcib_pci_methods, sizeof(struct pcib_softc), }; DRIVER_MODULE(pcibios_pcib, pci, pcibios_pcib_driver, pcib_devclass, 0, 0); static int pcibios_pcib_probe(device_t dev) { if ((pci_get_class(dev) != PCIC_BRIDGE) || (pci_get_subclass(dev) != PCIS_BRIDGE_PCI)) return (ENXIO); if (pci_probe_route_table(pcib_get_bus(dev)) == 0) return (ENXIO); device_set_desc(dev, "PCIBIOS PCI-PCI bridge"); return (-2000); } static int pcibios_pcib_route_interrupt(device_t pcib, device_t dev, int pin) { return(pci_cfgintr(pci_get_bus(dev), pci_get_slot(dev), pin, pci_get_irq(dev))); } Index: head/sys/amd64/pci/pci_cfgreg.c =================================================================== --- head/sys/amd64/pci/pci_cfgreg.c (revision 111067) +++ head/sys/amd64/pci/pci_cfgreg.c (revision 111068) @@ -1,923 +1,822 @@ /* * Copyright (c) 1997, Stefan Esser * Copyright (c) 2000, Michael Smith * Copyright (c) 2000, BSDi * 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 unmodified, 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. * * $FreeBSD$ * */ #include /* XXX trim includes */ #include #include #include #include #include +#include +#include #include #include #include #include #include #include #include #include #include #ifdef APIC_IO #include #endif /* APIC_IO */ #include "pcib_if.h" #define PRVERB(a) do { \ if (bootverbose) \ printf a ; \ } while(0) static int cfgmech; static int devmax; -static int usebios; -static int enable_pcibios = 0; -TUNABLE_INT("hw.pci.enable_pcibios", &enable_pcibios); - static int pci_cfgintr_valid(struct PIR_entry *pe, int pin, int irq); static int pci_cfgintr_unique(struct PIR_entry *pe, int pin); static int pci_cfgintr_linked(struct PIR_entry *pe, int pin); static int pci_cfgintr_search(struct PIR_entry *pe, int bus, int device, int matchpin, int pin); static int pci_cfgintr_virgin(struct PIR_entry *pe, int pin); static void pci_print_irqmask(u_int16_t irqs); static void pci_print_route_table(struct PIR_table *prt, int size); -#ifdef USE_PCI_BIOS_FOR_READ_WRITE -static int pcibios_cfgread(int bus, int slot, int func, int reg, int bytes); -static void pcibios_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes); -#endif -static int pcibios_cfgopen(void); static int pcireg_cfgread(int bus, int slot, int func, int reg, int bytes); static void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes); static int pcireg_cfgopen(void); static struct PIR_table *pci_route_table; static int pci_route_count; +static struct mtx pcicfg_mtx; + /* * Some BIOS writers seem to want to ignore the spec and put * 0 in the intline rather than 255 to indicate none. Some use * numbers in the range 128-254 to indicate something strange and * apparently undocumented anywhere. Assume these are completely bogus * and map them to 255, which means "none". */ static __inline__ int pci_i386_map_intline(int line) { if (line == 0 || line >= 128) return (PCI_INVALID_IRQ); return (line); } -int -pci_pcibios_active(void) -{ - return (usebios); -} - -int -pci_kill_pcibios(void) -{ - usebios = 0; - return (pcireg_cfgopen() != 0); -} - static u_int16_t pcibios_get_version(void) { struct bios_regs args; if (PCIbios.ventry == 0) { PRVERB(("pcibios: No call entry point\n")); return (0); } args.eax = PCIBIOS_BIOS_PRESENT; if (bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL))) { PRVERB(("pcibios: BIOS_PRESENT call failed\n")); return (0); } if (args.edx != 0x20494350) { PRVERB(("pcibios: BIOS_PRESENT didn't return 'PCI ' in edx\n")); return (0); } return (args.ebx & 0xffff); } /* * Initialise access to PCI configuration space */ int pci_cfgregopen(void) { static int opened = 0; u_long sigaddr; static struct PIR_table *pt; + u_int16_t v; u_int8_t ck, *cv; int i; if (opened) return(1); - if (pcibios_cfgopen() != 0) - usebios = 1; - else if (pcireg_cfgopen() != 0) - usebios = 0; - else + if (pcireg_cfgopen() == 0) return(0); + v = pcibios_get_version(); + if (v > 0) + printf("pcibios: BIOS version %x.%02x\n", (v & 0xff00) >> 8, + v & 0xff); + /* * Look for the interrupt routing table. * * We use PCI BIOS's PIR table if it's available $PIR is the * standard way to do this. Sadly, some machines are not * standards conforming and have _PIR instead. We shrug and cope * by looking for both. */ if (pcibios_get_version() >= 0x0210 && pt == NULL) { sigaddr = bios_sigsearch(0, "$PIR", 4, 16, 0); if (sigaddr == 0) sigaddr = bios_sigsearch(0, "_PIR", 4, 16, 0); if (sigaddr != 0) { pt = (struct PIR_table *)(uintptr_t) BIOS_PADDRTOVADDR(sigaddr); for (cv = (u_int8_t *)pt, ck = 0, i = 0; i < (pt->pt_header.ph_length); i++) { ck += cv[i]; } if (ck == 0 && pt->pt_header.ph_length > sizeof(struct PIR_header)) { pci_route_table = pt; pci_route_count = (pt->pt_header.ph_length - sizeof(struct PIR_header)) / sizeof(struct PIR_entry); printf("Using $PIR table, %d entries at %p\n", pci_route_count, pci_route_table); if (bootverbose) pci_print_route_table(pci_route_table, pci_route_count); } } } + mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN); opened = 1; return(1); } /* * Read configuration space register */ -static u_int32_t -pci_do_cfgregread(int bus, int slot, int func, int reg, int bytes) -{ -#ifdef USE_PCI_BIOS_FOR_READ_WRITE - return(usebios ? - pcibios_cfgread(bus, slot, func, reg, bytes) : - pcireg_cfgread(bus, slot, func, reg, bytes)); -#else - return (pcireg_cfgread(bus, slot, func, reg, bytes)); -#endif -} - u_int32_t pci_cfgregread(int bus, int slot, int func, int reg, int bytes) { uint32_t line; #ifdef APIC_IO uint32_t pin; /* * If we are using the APIC, the contents of the intline * register will probably be wrong (since they are set up for * use with the PIC. Rather than rewrite these registers * (maybe that would be smarter) we trap attempts to read them * and translate to our private vector numbers. */ if ((reg == PCIR_INTLINE) && (bytes == 1)) { - pin = pci_do_cfgregread(bus, slot, func, PCIR_INTPIN, 1); - line = pci_do_cfgregread(bus, slot, func, PCIR_INTLINE, 1); + pin = pcireg_cfgread(bus, slot, func, PCIR_INTPIN, 1); + line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1); if (pin != 0) { int airq; airq = pci_apic_irq(bus, slot, pin); if (airq >= 0) { /* PCI specific entry found in MP table */ if (airq != line) undirect_pci_irq(line); return(airq); } else { /* * PCI interrupts might be redirected * to the ISA bus according to some MP * tables. Use the same methods as * used by the ISA devices devices to * find the proper IOAPIC int pin. */ airq = isa_apic_irq(line); if ((airq >= 0) && (airq != line)) { /* XXX: undirect_pci_irq() ? */ undirect_isa_irq(line); return(airq); } } } return(line); } #else /* * Some BIOS writers seem to want to ignore the spec and put * 0 in the intline rather than 255 to indicate none. The rest of * the code uses 255 as an invalid IRQ. */ if (reg == PCIR_INTLINE && bytes == 1) { - line = pci_do_cfgregread(bus, slot, func, PCIR_INTLINE, 1); + line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1); return pci_i386_map_intline(line); } #endif /* APIC_IO */ - return(pci_do_cfgregread(bus, slot, func, reg, bytes)); + return(pcireg_cfgread(bus, slot, func, reg, bytes)); } /* * Write configuration space register */ void pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes) { -#ifdef USE_PCI_BIOS_FOR_READ_WRITE - if (usebios) - pcibios_cfgwrite(bus, slot, func, reg, data, bytes); - else - pcireg_cfgwrite(bus, slot, func, reg, data, bytes); -#else + pcireg_cfgwrite(bus, slot, func, reg, data, bytes); -#endif } /* * Route a PCI interrupt */ int pci_cfgintr(int bus, int device, int pin, int oldirq) { struct PIR_entry *pe; int i, irq; struct bios_regs args; u_int16_t v; int already = 0; int errok = 0; v = pcibios_get_version(); if (v < 0x0210) { PRVERB(( "pci_cfgintr: BIOS %x.%02x doesn't support interrupt routing\n", (v & 0xff00) >> 8, v & 0xff)); return (PCI_INVALID_IRQ); } if ((bus < 0) || (bus > 255) || (device < 0) || (device > 255) || (pin < 1) || (pin > 4)) return(PCI_INVALID_IRQ); /* * Scan the entry table for a contender */ for (i = 0, pe = &pci_route_table->pt_entry[0]; i < pci_route_count; i++, pe++) { if ((bus != pe->pe_bus) || (device != pe->pe_device)) continue; /* * A link of 0 means that this intpin is not connected to * any other device's interrupt pins and is not connected to * any of the Interrupt Router's interrupt pins, so we can't * route it. */ if (pe->pe_intpin[pin - 1].link == 0) continue; if (pci_cfgintr_valid(pe, pin, oldirq)) { printf("pci_cfgintr: %d:%d INT%c BIOS irq %d\n", bus, device, 'A' + pin - 1, oldirq); return (oldirq); } /* * We try to find a linked interrupt, then we look to see * if the interrupt is uniquely routed, then we look for * a virgin interrupt. The virgin interrupt should return * an interrupt we can route, but if that fails, maybe we * should try harder to route a different interrupt. * However, experience has shown that that's rarely the * failure mode we see. */ irq = pci_cfgintr_linked(pe, pin); if (irq != PCI_INVALID_IRQ) already = 1; if (irq == PCI_INVALID_IRQ) { irq = pci_cfgintr_unique(pe, pin); if (irq != PCI_INVALID_IRQ) errok = 1; } if (irq == PCI_INVALID_IRQ) irq = pci_cfgintr_virgin(pe, pin); if (irq == PCI_INVALID_IRQ) break; /* * Ask the BIOS to route the interrupt. If we picked an * interrupt that failed, we should really try other * choices that the BIOS offers us. * * For uniquely routed interrupts, we need to try * to route them on some machines. Yet other machines * fail to route, so we have to pretend that in that * case it worked. Isn't pc hardware fun? * * NOTE: if we want to whack hardware to do this, then * I think the right way to do that would be to have * bridge drivers that do this. I'm not sure that the * $PIR table would be valid for those interrupt * routers. */ args.eax = PCIBIOS_ROUTE_INTERRUPT; args.ebx = (bus << 8) | (device << 3); /* pin value is 0xa - 0xd */ args.ecx = (irq << 8) | (0xa + pin - 1); if (!already && bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL)) && !errok) { PRVERB(("pci_cfgintr: ROUTE_INTERRUPT failed.\n")); return(PCI_INVALID_IRQ); } printf("pci_cfgintr: %d:%d INT%c routed to irq %d\n", bus, device, 'A' + pin - 1, irq); return(irq); } PRVERB(("pci_cfgintr: can't route an interrupt to %d:%d INT%c\n", bus, device, 'A' + pin - 1)); return(PCI_INVALID_IRQ); } /* * Check to see if an existing IRQ setting is valid. */ static int pci_cfgintr_valid(struct PIR_entry *pe, int pin, int irq) { uint32_t irqmask; if (!PCI_INTERRUPT_VALID(irq)) return (0); irqmask = pe->pe_intpin[pin - 1].irqs; if (irqmask & (1 << irq)) { PRVERB(("pci_cfgintr_valid: BIOS irq %d is valid\n", irq)); return (1); } return (0); } /* * Look to see if the routing table claims this pin is uniquely routed. */ static int pci_cfgintr_unique(struct PIR_entry *pe, int pin) { int irq; uint32_t irqmask; irqmask = pe->pe_intpin[pin - 1].irqs; if (irqmask != 0 && powerof2(irqmask)) { irq = ffs(irqmask) - 1; PRVERB(("pci_cfgintr_unique: hard-routed to irq %d\n", irq)); return(irq); } return(PCI_INVALID_IRQ); } /* * Look for another device which shares the same link byte and * already has a unique IRQ, or which has had one routed already. */ static int pci_cfgintr_linked(struct PIR_entry *pe, int pin) { struct PIR_entry *oe; struct PIR_intpin *pi; int i, j, irq; /* * Scan table slots. */ for (i = 0, oe = &pci_route_table->pt_entry[0]; i < pci_route_count; i++, oe++) { /* scan interrupt pins */ for (j = 0, pi = &oe->pe_intpin[0]; j < 4; j++, pi++) { /* don't look at the entry we're trying to match */ if ((pe == oe) && (i == (pin - 1))) continue; /* compare link bytes */ if (pi->link != pe->pe_intpin[pin - 1].link) continue; /* link destination mapped to a unique interrupt? */ if (pi->irqs != 0 && powerof2(pi->irqs)) { irq = ffs(pi->irqs) - 1; PRVERB(("pci_cfgintr_linked: linked (%x) to hard-routed irq %d\n", pi->link, irq)); return(irq); } /* * look for the real PCI device that matches this * table entry */ irq = pci_cfgintr_search(pe, oe->pe_bus, oe->pe_device, j, pin); if (irq != PCI_INVALID_IRQ) return(irq); } } return(PCI_INVALID_IRQ); } /* * Scan for the real PCI device at (bus)/(device) using intpin (matchpin) and * see if it has already been assigned an interrupt. */ static int pci_cfgintr_search(struct PIR_entry *pe, int bus, int device, int matchpin, int pin) { devclass_t pci_devclass; device_t *pci_devices; int pci_count; device_t *pci_children; int pci_childcount; device_t *busp, *childp; int i, j, irq; /* * Find all the PCI busses. */ pci_count = 0; if ((pci_devclass = devclass_find("pci")) != NULL) devclass_get_devices(pci_devclass, &pci_devices, &pci_count); /* * Scan all the PCI busses/devices looking for this one. */ irq = PCI_INVALID_IRQ; for (i = 0, busp = pci_devices; (i < pci_count) && (irq == PCI_INVALID_IRQ); i++, busp++) { pci_childcount = 0; device_get_children(*busp, &pci_children, &pci_childcount); for (j = 0, childp = pci_children; j < pci_childcount; j++, childp++) { if ((pci_get_bus(*childp) == bus) && (pci_get_slot(*childp) == device) && (pci_get_intpin(*childp) == matchpin)) { irq = pci_i386_map_intline(pci_get_irq(*childp)); if (irq != PCI_INVALID_IRQ) PRVERB(("pci_cfgintr_search: linked (%x) to configured irq %d at %d:%d:%d\n", pe->pe_intpin[pin - 1].link, irq, pci_get_bus(*childp), pci_get_slot(*childp), pci_get_function(*childp))); break; } } if (pci_children != NULL) free(pci_children, M_TEMP); } if (pci_devices != NULL) free(pci_devices, M_TEMP); return(irq); } /* * Pick a suitable IRQ from those listed as routable to this device. */ static int pci_cfgintr_virgin(struct PIR_entry *pe, int pin) { int irq, ibit; /* * first scan the set of PCI-only interrupts and see if any of these * are routable */ for (irq = 0; irq < 16; irq++) { ibit = (1 << irq); /* can we use this interrupt? */ if ((pci_route_table->pt_header.ph_pci_irqs & ibit) && (pe->pe_intpin[pin - 1].irqs & ibit)) { PRVERB(("pci_cfgintr_virgin: using routable PCI-only interrupt %d\n", irq)); return(irq); } } /* life is tough, so just pick an interrupt */ for (irq = 0; irq < 16; irq++) { ibit = (1 << irq); if (pe->pe_intpin[pin - 1].irqs & ibit) { PRVERB(("pci_cfgintr_virgin: using routable interrupt %d\n", irq)); return(irq); } } return(PCI_INVALID_IRQ); } static void pci_print_irqmask(u_int16_t irqs) { int i, first; if (irqs == 0) { printf("none"); return; } first = 1; for (i = 0; i < 16; i++, irqs >>= 1) if (irqs & 1) { if (!first) printf(" "); else first = 0; printf("%d", i); } } /* * Dump the contents of a PCI BIOS Interrupt Routing Table to the console. */ static void pci_print_route_table(struct PIR_table *prt, int size) { struct PIR_entry *entry; struct PIR_intpin *intpin; int i, pin; printf("PCI-Only Interrupts: "); pci_print_irqmask(prt->pt_header.ph_pci_irqs); printf("\nLocation Bus Device Pin Link IRQs\n"); entry = &prt->pt_entry[0]; for (i = 0; i < size; i++, entry++) { intpin = &entry->pe_intpin[0]; for (pin = 0; pin < 4; pin++, intpin++) if (intpin->link != 0) { if (entry->pe_slot == 0) printf("embedded "); else printf("slot %-3d ", entry->pe_slot); printf(" %3d %3d %c 0x%02x ", entry->pe_bus, entry->pe_device, 'A' + pin, intpin->link); pci_print_irqmask(intpin->irqs); printf("\n"); } } } /* * See if any interrupts for a given PCI bus are routed in the PIR. Don't * even bother looking if the BIOS doesn't support routing anyways. */ int pci_probe_route_table(int bus) { int i; u_int16_t v; v = pcibios_get_version(); if (v < 0x0210) return (0); for (i = 0; i < pci_route_count; i++) if (pci_route_table->pt_entry[i].pe_bus == bus) return (1); return (0); } -#ifdef USE_PCI_BIOS_FOR_READ_WRITE -/* - * Config space access using BIOS functions - */ -static int -pcibios_cfgread(int bus, int slot, int func, int reg, int bytes) -{ - struct bios_regs args; - u_int mask; - - switch(bytes) { - case 1: - args.eax = PCIBIOS_READ_CONFIG_BYTE; - mask = 0xff; - break; - case 2: - args.eax = PCIBIOS_READ_CONFIG_WORD; - mask = 0xffff; - break; - case 4: - args.eax = PCIBIOS_READ_CONFIG_DWORD; - mask = 0xffffffff; - break; - default: - return(-1); - } - args.ebx = (bus << 8) | (slot << 3) | func; - args.edi = reg; - bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL)); - /* check call results? */ - return(args.ecx & mask); -} - -static void -pcibios_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) -{ - struct bios_regs args; - - switch(bytes) { - case 1: - args.eax = PCIBIOS_WRITE_CONFIG_BYTE; - break; - case 2: - args.eax = PCIBIOS_WRITE_CONFIG_WORD; - break; - case 4: - args.eax = PCIBIOS_WRITE_CONFIG_DWORD; - break; - default: - return; - } - args.ebx = (bus << 8) | (slot << 3) | func; - args.ecx = data; - args.edi = reg; - bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL)); -} -#endif - -/* - * Determine whether there is a PCI BIOS present - */ -static int -pcibios_cfgopen(void) -{ - u_int16_t v = 0; - - if (PCIbios.ventry != 0 && enable_pcibios) { - v = pcibios_get_version(); - if (v > 0) - printf("pcibios: BIOS version %x.%02x\n", - (v & 0xff00) >> 8, v & 0xff); - } - return (v > 0); -} - /* * Configuration space access using direct register operations */ /* enable configuration space accesses and return data port address */ static int pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) { int dataport = 0; if (bus <= PCI_BUSMAX && slot < devmax && func <= PCI_FUNCMAX && reg <= PCI_REGMAX && bytes != 3 && (unsigned) bytes <= 4 && (reg & (bytes - 1)) == 0) { switch (cfgmech) { case 1: outl(CONF1_ADDR_PORT, (1 << 31) | (bus << 16) | (slot << 11) | (func << 8) | (reg & ~0x03)); dataport = CONF1_DATA_PORT + (reg & 0x03); break; case 2: outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1)); outb(CONF2_FORWARD_PORT, bus); dataport = 0xc000 | (slot << 8) | reg; break; } } return (dataport); } /* disable configuration space accesses */ static void pci_cfgdisable(void) { switch (cfgmech) { case 1: outl(CONF1_ADDR_PORT, 0); break; case 2: outb(CONF2_ENABLE_PORT, 0); outb(CONF2_FORWARD_PORT, 0); break; } } static int pcireg_cfgread(int bus, int slot, int func, int reg, int bytes) { int data = -1; int port; + mtx_lock_spin(&pcicfg_mtx); port = pci_cfgenable(bus, slot, func, reg, bytes); - if (port != 0) { switch (bytes) { case 1: data = inb(port); break; case 2: data = inw(port); break; case 4: data = inl(port); break; } pci_cfgdisable(); } + mtx_unlock_spin(&pcicfg_mtx); return (data); } static void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) { int port; + mtx_lock_spin(&pcicfg_mtx); port = pci_cfgenable(bus, slot, func, reg, bytes); if (port != 0) { switch (bytes) { case 1: outb(port, data); break; case 2: outw(port, data); break; case 4: outl(port, data); break; } pci_cfgdisable(); } + mtx_unlock_spin(&pcicfg_mtx); } /* check whether the configuration mechanism has been correctly identified */ static int pci_cfgcheck(int maxdev) { uint32_t id, class; uint8_t header; uint8_t device; + int port; if (bootverbose) printf("pci_cfgcheck:\tdevice "); for (device = 0; device < maxdev; device++) { if (bootverbose) printf("%d ", device); - id = inl(pci_cfgenable(0, device, 0, 0, 4)); + port = pci_cfgenable(0, device, 0, 0, 4); + id = inl(port); if (id == 0 || id == 0xffffffff) continue; - class = inl(pci_cfgenable(0, device, 0, 8, 4)) >> 8; + port = pci_cfgenable(0, device, 0, 8, 4); + class = inl(port) >> 8; if (bootverbose) printf("[class=%06x] ", class); if (class == 0 || (class & 0xf870ff) != 0) continue; - header = inb(pci_cfgenable(0, device, 0, 14, 1)); + port = pci_cfgenable(0, device, 0, 14, 1); + header = inb(port); if (bootverbose) printf("[hdr=%02x] ", header); if ((header & 0x7e) != 0) continue; if (bootverbose) printf("is there (id=%08x)\n", id); pci_cfgdisable(); return (1); } if (bootverbose) printf("-- nothing found\n"); pci_cfgdisable(); return (0); } static int pcireg_cfgopen(void) { uint32_t mode1res, oldval1; uint8_t mode2res, oldval2; oldval1 = inl(CONF1_ADDR_PORT); if (bootverbose) { printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08x\n", oldval1); } if ((oldval1 & CONF1_ENABLE_MSK) == 0) { cfgmech = 1; devmax = 32; outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK); outb(CONF1_ADDR_PORT + 3, 0); mode1res = inl(CONF1_ADDR_PORT); outl(CONF1_ADDR_PORT, oldval1); if (bootverbose) printf("pci_open(1a):\tmode1res=0x%08x (0x%08lx)\n", mode1res, CONF1_ENABLE_CHK); if (mode1res) { if (pci_cfgcheck(32)) return (cfgmech); } outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); mode1res = inl(CONF1_ADDR_PORT); outl(CONF1_ADDR_PORT, oldval1); if (bootverbose) printf("pci_open(1b):\tmode1res=0x%08x (0x%08lx)\n", mode1res, CONF1_ENABLE_CHK1); if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { if (pci_cfgcheck(32)) return (cfgmech); } } oldval2 = inb(CONF2_ENABLE_PORT); if (bootverbose) { printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", oldval2); } if ((oldval2 & 0xf0) == 0) { cfgmech = 2; devmax = 16; outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); mode2res = inb(CONF2_ENABLE_PORT); outb(CONF2_ENABLE_PORT, oldval2); if (bootverbose) printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", mode2res, CONF2_ENABLE_CHK); if (mode2res == CONF2_ENABLE_RES) { if (bootverbose) printf("pci_open(2a):\tnow trying mechanism 2\n"); if (pci_cfgcheck(16)) return (cfgmech); } } cfgmech = 0; devmax = 0; return (cfgmech); } Index: head/sys/i386/include/pci_cfgreg.h =================================================================== --- head/sys/i386/include/pci_cfgreg.h (revision 111067) +++ head/sys/i386/include/pci_cfgreg.h (revision 111068) @@ -1,56 +1,54 @@ /* * Copyright (c) 1997, Stefan Esser * 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 unmodified, 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. * * $FreeBSD$ * */ #define CONF1_ADDR_PORT 0x0cf8 #define CONF1_DATA_PORT 0x0cfc #define CONF1_ENABLE 0x80000000ul #define CONF1_ENABLE_CHK 0x80000000ul #define CONF1_ENABLE_MSK 0x7ff00000ul #define CONF1_ENABLE_CHK1 0xff000001ul #define CONF1_ENABLE_MSK1 0x80000001ul #define CONF1_ENABLE_RES1 0x80000000ul #define CONF2_ENABLE_PORT 0x0cf8 #ifdef PC98 #define CONF2_FORWARD_PORT 0x0cf9 #else #define CONF2_FORWARD_PORT 0x0cfa #endif #define CONF2_ENABLE_CHK 0x0e #define CONF2_ENABLE_RES 0x0e int pci_cfgregopen(void); u_int32_t pci_cfgregread(int bus, int slot, int func, int reg, int bytes); void pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes); int pci_cfgintr(int bus, int device, int pin, int oldirq); -int pci_kill_pcibios(void); -int pci_pcibios_active(void); int pci_probe_route_table(int bus); Index: head/sys/i386/pci/pci_bus.c =================================================================== --- head/sys/i386/pci/pci_bus.c (revision 111067) +++ head/sys/i386/pci/pci_bus.c (revision 111068) @@ -1,689 +1,671 @@ /* * Copyright (c) 1997, Stefan Esser * 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 unmodified, 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. * * $FreeBSD$ * */ #include "opt_cpu.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pcib_if.h" static int pcibios_pcib_route_interrupt(device_t pcib, device_t dev, int pin); static int nexus_pcib_maxslots(device_t dev) { return 31; } /* read configuration space register */ static u_int32_t nexus_pcib_read_config(device_t dev, int bus, int slot, int func, int reg, int bytes) { return(pci_cfgregread(bus, slot, func, reg, bytes)); } /* write configuration space register */ static void nexus_pcib_write_config(device_t dev, int bus, int slot, int func, int reg, u_int32_t data, int bytes) { pci_cfgregwrite(bus, slot, func, reg, data, bytes); } /* route interrupt */ static int nexus_pcib_route_interrupt(device_t pcib, device_t dev, int pin) { return (pcibios_pcib_route_interrupt(pcib, dev, pin)); } static const char * nexus_pcib_is_host_bridge(int bus, int slot, int func, u_int32_t id, u_int8_t class, u_int8_t subclass, u_int8_t *busnum) { const char *s = NULL; static u_int8_t pxb[4]; /* hack for 450nx */ *busnum = 0; switch (id) { case 0x12258086: s = "Intel 824?? host to PCI bridge"; /* XXX This is a guess */ /* *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x41, 1); */ *busnum = bus; break; case 0x71208086: s = "Intel 82810 (i810 GMCH) Host To Hub bridge"; break; case 0x71228086: s = "Intel 82810-DC100 (i810-DC100 GMCH) Host To Hub bridge"; break; case 0x71248086: s = "Intel 82810E (i810E GMCH) Host To Hub bridge"; break; case 0x11308086: s = "Intel 82815 (i815 GMCH) Host To Hub bridge"; break; case 0x71808086: s = "Intel 82443LX (440 LX) host to PCI bridge"; break; case 0x71908086: s = "Intel 82443BX (440 BX) host to PCI bridge"; break; case 0x71928086: s = "Intel 82443BX host to PCI bridge (AGP disabled)"; break; case 0x71948086: s = "Intel 82443MX host to PCI bridge"; break; case 0x71a08086: s = "Intel 82443GX host to PCI bridge"; break; case 0x71a18086: s = "Intel 82443GX host to AGP bridge"; break; case 0x71a28086: s = "Intel 82443GX host to PCI bridge (AGP disabled)"; break; case 0x84c48086: s = "Intel 82454KX/GX (Orion) host to PCI bridge"; *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x4a, 1); break; case 0x84ca8086: /* * For the 450nx chipset, there is a whole bundle of * things pretending to be host bridges. The MIOC will * be seen first and isn't really a pci bridge (the * actual busses are attached to the PXB's). We need to * read the registers of the MIOC to figure out the * bus numbers for the PXB channels. * * Since the MIOC doesn't have a pci bus attached, we * pretend it wasn't there. */ pxb[0] = nexus_pcib_read_config(0, bus, slot, func, 0xd0, 1); /* BUSNO[0] */ pxb[1] = nexus_pcib_read_config(0, bus, slot, func, 0xd1, 1) + 1; /* SUBA[0]+1 */ pxb[2] = nexus_pcib_read_config(0, bus, slot, func, 0xd3, 1); /* BUSNO[1] */ pxb[3] = nexus_pcib_read_config(0, bus, slot, func, 0xd4, 1) + 1; /* SUBA[1]+1 */ return NULL; case 0x84cb8086: switch (slot) { case 0x12: s = "Intel 82454NX PXB#0, Bus#A"; *busnum = pxb[0]; break; case 0x13: s = "Intel 82454NX PXB#0, Bus#B"; *busnum = pxb[1]; break; case 0x14: s = "Intel 82454NX PXB#1, Bus#A"; *busnum = pxb[2]; break; case 0x15: s = "Intel 82454NX PXB#1, Bus#B"; *busnum = pxb[3]; break; } break; /* AMD -- vendor 0x1022 */ case 0x30001022: s = "AMD Elan SC520 host to PCI bridge"; #ifdef CPU_ELAN init_AMD_Elan_sc520(); #else printf( "*** WARNING: missing CPU_ELAN -- timekeeping may be wrong\n"); #endif break; case 0x70061022: s = "AMD-751 host to PCI bridge"; break; case 0x700e1022: s = "AMD-761 host to PCI bridge"; break; /* SiS -- vendor 0x1039 */ case 0x04961039: s = "SiS 85c496"; break; case 0x04061039: s = "SiS 85c501"; break; case 0x06011039: s = "SiS 85c601"; break; case 0x55911039: s = "SiS 5591 host to PCI bridge"; break; case 0x00011039: s = "SiS 5591 host to AGP bridge"; break; /* VLSI -- vendor 0x1004 */ case 0x00051004: s = "VLSI 82C592 Host to PCI bridge"; break; /* XXX Here is MVP3, I got the datasheet but NO M/B to test it */ /* totally. Please let me know if anything wrong. -F */ /* XXX need info on the MVP3 -- any takers? */ case 0x05981106: s = "VIA 82C598MVP (Apollo MVP3) host bridge"; break; /* AcerLabs -- vendor 0x10b9 */ /* Funny : The datasheet told me vendor id is "10b8",sub-vendor */ /* id is '10b9" but the register always shows "10b9". -Foxfair */ case 0x154110b9: s = "AcerLabs M1541 (Aladdin-V) PCI host bridge"; break; /* OPTi -- vendor 0x1045 */ case 0xc7011045: s = "OPTi 82C700 host to PCI bridge"; break; case 0xc8221045: s = "OPTi 82C822 host to PCI Bridge"; break; /* ServerWorks -- vendor 0x1166 */ case 0x00051166: s = "ServerWorks NB6536 2.0HE host to PCI bridge"; *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x44, 1); break; case 0x00061166: /* FALLTHROUGH */ case 0x00081166: /* FALLTHROUGH */ case 0x02011166: /* FALLTHROUGH */ case 0x010f1014: /* IBM re-badged ServerWorks chipset */ s = "ServerWorks host to PCI bridge"; *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x44, 1); break; case 0x00091166: s = "ServerWorks NB6635 3.0LE host to PCI bridge"; *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x44, 1); break; case 0x00101166: s = "ServerWorks CIOB30 host to PCI bridge"; *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x44, 1); break; case 0x00111166: /* FALLTHROUGH */ case 0x03021014: /* IBM re-badged ServerWorks chipset */ s = "ServerWorks CMIC-HE host to PCI-X bridge"; *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x44, 1); break; /* XXX unknown chipset, but working */ case 0x00171166: /* FALLTHROUGH */ case 0x01011166: s = "ServerWorks host to PCI bridge(unknown chipset)"; *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x44, 1); break; /* Integrated Micro Solutions -- vendor 0x10e0 */ case 0x884910e0: s = "Integrated Micro Solutions VL Bridge"; break; default: if (class == PCIC_BRIDGE && subclass == PCIS_BRIDGE_HOST) s = "Host to PCI bridge"; break; } return s; } /* * Scan the first pci bus for host-pci bridges and add pcib instances * to the nexus for each bridge. */ static void nexus_pcib_identify(driver_t *driver, device_t parent) { int bus, slot, func; u_int8_t hdrtype; int found = 0; int pcifunchigh; int found824xx = 0; int found_orion = 0; - int found_pcibios_flaming_death = 0; device_t child; devclass_t pci_devclass; if (pci_cfgregopen() == 0) return; /* * Check to see if we haven't already had a PCI bus added * via some other means. If we have, bail since otherwise * we're going to end up duplicating it. */ if ((pci_devclass = devclass_find("pci")) && devclass_get_device(pci_devclass, 0)) return; bus = 0; retry: for (slot = 0; slot <= PCI_SLOTMAX; slot++) { func = 0; hdrtype = nexus_pcib_read_config(0, bus, slot, func, PCIR_HEADERTYPE, 1); if ((hdrtype & PCIM_MFDEV) && (!found_orion || hdrtype != 0xff)) pcifunchigh = 7; else pcifunchigh = 0; for (func = 0; func <= pcifunchigh; func++) { /* * Read the IDs and class from the device. */ u_int32_t id; u_int8_t class, subclass, busnum; const char *s; device_t *devs; int ndevs, i; id = nexus_pcib_read_config(0, bus, slot, func, PCIR_DEVVENDOR, 4); if (id == -1) continue; class = nexus_pcib_read_config(0, bus, slot, func, PCIR_CLASS, 1); subclass = nexus_pcib_read_config(0, bus, slot, func, PCIR_SUBCLASS, 1); s = nexus_pcib_is_host_bridge(bus, slot, func, id, class, subclass, &busnum); if (s == NULL) continue; /* * Check to see if the physical bus has already * been seen. Eg: hybrid 32 and 64 bit host * bridges to the same logical bus. */ if (device_get_children(parent, &devs, &ndevs) == 0) { for (i = 0; s != NULL && i < ndevs; i++) { if (strcmp(device_get_name(devs[i]), "pcib") != 0) continue; if (legacy_get_pcibus(devs[i]) == busnum) s = NULL; } free(devs, M_TEMP); } if (s == NULL) continue; /* * Add at priority 100 to make sure we * go after any motherboard resources */ child = BUS_ADD_CHILD(parent, 100, "pcib", busnum); device_set_desc(child, s); legacy_set_pcibus(child, busnum); found = 1; if (id == 0x12258086) found824xx = 1; if (id == 0x84c48086) found_orion = 1; } } if (found824xx && bus == 0) { bus++; - goto retry; - } - - /* - * This is just freaking brilliant! Some BIOS writers have - * decided that we must be forcibly prevented from using - * PCIBIOS to query the host->pci bridges. If you try and - * access configuration registers, it pretends there is - * no pci device at that bus:device:function address. - */ - if (!found && pci_pcibios_active() && !found_pcibios_flaming_death) { - /* retry with the old mechanism, or fail */ - if (pci_kill_pcibios() == 0) - return; - printf("nexus_pcib_identify: found broken PCIBIOS - disabling it and retrying.\n"); - printf("nexus_pcib_identify: it is bogusly censoring host->pci bridges.\n"); - found_pcibios_flaming_death = 1; goto retry; } /* * Make sure we add at least one bridge since some old * hardware doesn't actually have a host-pci bridge device. * Note that pci_cfgregopen() thinks we have PCI devices.. */ if (!found) { if (bootverbose) printf( "nexus_pcib_identify: no bridge found, adding pcib0 anyway\n"); child = BUS_ADD_CHILD(parent, 100, "pcib", 0); legacy_set_pcibus(child, 0); } } static int nexus_pcib_probe(device_t dev) { if (pci_cfgregopen() == 0) return ENXIO; return 0; } static int nexus_pcib_attach(device_t dev) { device_t child; child = device_add_child(dev, "pci", pcib_get_bus(dev)); return bus_generic_attach(dev); } static int nexus_pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) { switch (which) { case PCIB_IVAR_BUS: *result = legacy_get_pcibus(dev); return 0; } return ENOENT; } static int nexus_pcib_write_ivar(device_t dev, device_t child, int which, uintptr_t value) { switch (which) { case PCIB_IVAR_BUS: legacy_set_pcibus(dev, value); return 0; } return ENOENT; } static device_method_t nexus_pcib_methods[] = { /* Device interface */ DEVMETHOD(device_identify, nexus_pcib_identify), DEVMETHOD(device_probe, nexus_pcib_probe), DEVMETHOD(device_attach, nexus_pcib_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_read_ivar, nexus_pcib_read_ivar), DEVMETHOD(bus_write_ivar, nexus_pcib_write_ivar), DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), /* pcib interface */ DEVMETHOD(pcib_maxslots, nexus_pcib_maxslots), DEVMETHOD(pcib_read_config, nexus_pcib_read_config), DEVMETHOD(pcib_write_config, nexus_pcib_write_config), DEVMETHOD(pcib_route_interrupt, nexus_pcib_route_interrupt), { 0, 0 } }; static driver_t nexus_pcib_driver = { "pcib", nexus_pcib_methods, 1, }; DRIVER_MODULE(pcib, legacy, nexus_pcib_driver, pcib_devclass, 0, 0); /* * Provide a device to "eat" the host->pci bridges that we dug up above * and stop them showing up twice on the probes. This also stops them * showing up as 'none' in pciconf -l. */ static int pci_hostb_probe(device_t dev) { u_int32_t id; id = pci_get_devid(dev); switch (id) { /* VIA VT82C596 Power Managment Function */ case 0x30501106: return ENXIO; default: break; } if (pci_get_class(dev) == PCIC_BRIDGE && pci_get_subclass(dev) == PCIS_BRIDGE_HOST) { device_set_desc(dev, "Host to PCI bridge"); device_quiet(dev); return -10000; } return ENXIO; } static int pci_hostb_attach(device_t dev) { return 0; } static device_method_t pci_hostb_methods[] = { /* Device interface */ DEVMETHOD(device_probe, pci_hostb_probe), DEVMETHOD(device_attach, pci_hostb_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), { 0, 0 } }; static driver_t pci_hostb_driver = { "hostb", pci_hostb_methods, 1, }; static devclass_t pci_hostb_devclass; DRIVER_MODULE(hostb, pci, pci_hostb_driver, pci_hostb_devclass, 0, 0); /* * Install placeholder to claim the resources owned by the * PCI bus interface. This could be used to extract the * config space registers in the extreme case where the PnP * ID is available and the PCI BIOS isn't, but for now we just * eat the PnP ID and do nothing else. * * XXX we should silence this probe, as it will generally confuse * people. */ static struct isa_pnp_id pcibus_pnp_ids[] = { { 0x030ad041 /* PNP0A03 */, "PCI Bus" }, { 0 } }; static int pcibus_pnp_probe(device_t dev) { int result; if ((result = ISA_PNP_PROBE(device_get_parent(dev), dev, pcibus_pnp_ids)) <= 0) device_quiet(dev); return(result); } static int pcibus_pnp_attach(device_t dev) { return(0); } static device_method_t pcibus_pnp_methods[] = { /* Device interface */ DEVMETHOD(device_probe, pcibus_pnp_probe), DEVMETHOD(device_attach, pcibus_pnp_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), { 0, 0 } }; static driver_t pcibus_pnp_driver = { "pcibus_pnp", pcibus_pnp_methods, 1, /* no softc */ }; static devclass_t pcibus_pnp_devclass; DRIVER_MODULE(pcibus_pnp, isa, pcibus_pnp_driver, pcibus_pnp_devclass, 0, 0); /* * Provide a PCI-PCI bridge driver for PCI busses behind PCI-PCI bridges * that appear in the PCIBIOS Interrupt Routing Table to use the routing * table for interrupt routing when possible. */ static int pcibios_pcib_probe(device_t bus); static device_method_t pcibios_pcib_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, pcibios_pcib_probe), DEVMETHOD(device_attach, pcib_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_read_ivar, pcib_read_ivar), DEVMETHOD(bus_write_ivar, pcib_write_ivar), DEVMETHOD(bus_alloc_resource, pcib_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), /* pcib interface */ DEVMETHOD(pcib_maxslots, pcib_maxslots), DEVMETHOD(pcib_read_config, pcib_read_config), DEVMETHOD(pcib_write_config, pcib_write_config), DEVMETHOD(pcib_route_interrupt, pcibios_pcib_route_interrupt), {0, 0} }; static driver_t pcibios_pcib_driver = { "pcib", pcibios_pcib_pci_methods, sizeof(struct pcib_softc), }; DRIVER_MODULE(pcibios_pcib, pci, pcibios_pcib_driver, pcib_devclass, 0, 0); static int pcibios_pcib_probe(device_t dev) { if ((pci_get_class(dev) != PCIC_BRIDGE) || (pci_get_subclass(dev) != PCIS_BRIDGE_PCI)) return (ENXIO); if (pci_probe_route_table(pcib_get_bus(dev)) == 0) return (ENXIO); device_set_desc(dev, "PCIBIOS PCI-PCI bridge"); return (-2000); } static int pcibios_pcib_route_interrupt(device_t pcib, device_t dev, int pin) { return(pci_cfgintr(pci_get_bus(dev), pci_get_slot(dev), pin, pci_get_irq(dev))); } Index: head/sys/i386/pci/pci_cfgreg.c =================================================================== --- head/sys/i386/pci/pci_cfgreg.c (revision 111067) +++ head/sys/i386/pci/pci_cfgreg.c (revision 111068) @@ -1,923 +1,822 @@ /* * Copyright (c) 1997, Stefan Esser * Copyright (c) 2000, Michael Smith * Copyright (c) 2000, BSDi * 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 unmodified, 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. * * $FreeBSD$ * */ #include /* XXX trim includes */ #include #include #include #include #include +#include +#include #include #include #include #include #include #include #include #include #include #ifdef APIC_IO #include #endif /* APIC_IO */ #include "pcib_if.h" #define PRVERB(a) do { \ if (bootverbose) \ printf a ; \ } while(0) static int cfgmech; static int devmax; -static int usebios; -static int enable_pcibios = 0; -TUNABLE_INT("hw.pci.enable_pcibios", &enable_pcibios); - static int pci_cfgintr_valid(struct PIR_entry *pe, int pin, int irq); static int pci_cfgintr_unique(struct PIR_entry *pe, int pin); static int pci_cfgintr_linked(struct PIR_entry *pe, int pin); static int pci_cfgintr_search(struct PIR_entry *pe, int bus, int device, int matchpin, int pin); static int pci_cfgintr_virgin(struct PIR_entry *pe, int pin); static void pci_print_irqmask(u_int16_t irqs); static void pci_print_route_table(struct PIR_table *prt, int size); -#ifdef USE_PCI_BIOS_FOR_READ_WRITE -static int pcibios_cfgread(int bus, int slot, int func, int reg, int bytes); -static void pcibios_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes); -#endif -static int pcibios_cfgopen(void); static int pcireg_cfgread(int bus, int slot, int func, int reg, int bytes); static void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes); static int pcireg_cfgopen(void); static struct PIR_table *pci_route_table; static int pci_route_count; +static struct mtx pcicfg_mtx; + /* * Some BIOS writers seem to want to ignore the spec and put * 0 in the intline rather than 255 to indicate none. Some use * numbers in the range 128-254 to indicate something strange and * apparently undocumented anywhere. Assume these are completely bogus * and map them to 255, which means "none". */ static __inline__ int pci_i386_map_intline(int line) { if (line == 0 || line >= 128) return (PCI_INVALID_IRQ); return (line); } -int -pci_pcibios_active(void) -{ - return (usebios); -} - -int -pci_kill_pcibios(void) -{ - usebios = 0; - return (pcireg_cfgopen() != 0); -} - static u_int16_t pcibios_get_version(void) { struct bios_regs args; if (PCIbios.ventry == 0) { PRVERB(("pcibios: No call entry point\n")); return (0); } args.eax = PCIBIOS_BIOS_PRESENT; if (bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL))) { PRVERB(("pcibios: BIOS_PRESENT call failed\n")); return (0); } if (args.edx != 0x20494350) { PRVERB(("pcibios: BIOS_PRESENT didn't return 'PCI ' in edx\n")); return (0); } return (args.ebx & 0xffff); } /* * Initialise access to PCI configuration space */ int pci_cfgregopen(void) { static int opened = 0; u_long sigaddr; static struct PIR_table *pt; + u_int16_t v; u_int8_t ck, *cv; int i; if (opened) return(1); - if (pcibios_cfgopen() != 0) - usebios = 1; - else if (pcireg_cfgopen() != 0) - usebios = 0; - else + if (pcireg_cfgopen() == 0) return(0); + v = pcibios_get_version(); + if (v > 0) + printf("pcibios: BIOS version %x.%02x\n", (v & 0xff00) >> 8, + v & 0xff); + /* * Look for the interrupt routing table. * * We use PCI BIOS's PIR table if it's available $PIR is the * standard way to do this. Sadly, some machines are not * standards conforming and have _PIR instead. We shrug and cope * by looking for both. */ if (pcibios_get_version() >= 0x0210 && pt == NULL) { sigaddr = bios_sigsearch(0, "$PIR", 4, 16, 0); if (sigaddr == 0) sigaddr = bios_sigsearch(0, "_PIR", 4, 16, 0); if (sigaddr != 0) { pt = (struct PIR_table *)(uintptr_t) BIOS_PADDRTOVADDR(sigaddr); for (cv = (u_int8_t *)pt, ck = 0, i = 0; i < (pt->pt_header.ph_length); i++) { ck += cv[i]; } if (ck == 0 && pt->pt_header.ph_length > sizeof(struct PIR_header)) { pci_route_table = pt; pci_route_count = (pt->pt_header.ph_length - sizeof(struct PIR_header)) / sizeof(struct PIR_entry); printf("Using $PIR table, %d entries at %p\n", pci_route_count, pci_route_table); if (bootverbose) pci_print_route_table(pci_route_table, pci_route_count); } } } + mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN); opened = 1; return(1); } /* * Read configuration space register */ -static u_int32_t -pci_do_cfgregread(int bus, int slot, int func, int reg, int bytes) -{ -#ifdef USE_PCI_BIOS_FOR_READ_WRITE - return(usebios ? - pcibios_cfgread(bus, slot, func, reg, bytes) : - pcireg_cfgread(bus, slot, func, reg, bytes)); -#else - return (pcireg_cfgread(bus, slot, func, reg, bytes)); -#endif -} - u_int32_t pci_cfgregread(int bus, int slot, int func, int reg, int bytes) { uint32_t line; #ifdef APIC_IO uint32_t pin; /* * If we are using the APIC, the contents of the intline * register will probably be wrong (since they are set up for * use with the PIC. Rather than rewrite these registers * (maybe that would be smarter) we trap attempts to read them * and translate to our private vector numbers. */ if ((reg == PCIR_INTLINE) && (bytes == 1)) { - pin = pci_do_cfgregread(bus, slot, func, PCIR_INTPIN, 1); - line = pci_do_cfgregread(bus, slot, func, PCIR_INTLINE, 1); + pin = pcireg_cfgread(bus, slot, func, PCIR_INTPIN, 1); + line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1); if (pin != 0) { int airq; airq = pci_apic_irq(bus, slot, pin); if (airq >= 0) { /* PCI specific entry found in MP table */ if (airq != line) undirect_pci_irq(line); return(airq); } else { /* * PCI interrupts might be redirected * to the ISA bus according to some MP * tables. Use the same methods as * used by the ISA devices devices to * find the proper IOAPIC int pin. */ airq = isa_apic_irq(line); if ((airq >= 0) && (airq != line)) { /* XXX: undirect_pci_irq() ? */ undirect_isa_irq(line); return(airq); } } } return(line); } #else /* * Some BIOS writers seem to want to ignore the spec and put * 0 in the intline rather than 255 to indicate none. The rest of * the code uses 255 as an invalid IRQ. */ if (reg == PCIR_INTLINE && bytes == 1) { - line = pci_do_cfgregread(bus, slot, func, PCIR_INTLINE, 1); + line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1); return pci_i386_map_intline(line); } #endif /* APIC_IO */ - return(pci_do_cfgregread(bus, slot, func, reg, bytes)); + return(pcireg_cfgread(bus, slot, func, reg, bytes)); } /* * Write configuration space register */ void pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes) { -#ifdef USE_PCI_BIOS_FOR_READ_WRITE - if (usebios) - pcibios_cfgwrite(bus, slot, func, reg, data, bytes); - else - pcireg_cfgwrite(bus, slot, func, reg, data, bytes); -#else + pcireg_cfgwrite(bus, slot, func, reg, data, bytes); -#endif } /* * Route a PCI interrupt */ int pci_cfgintr(int bus, int device, int pin, int oldirq) { struct PIR_entry *pe; int i, irq; struct bios_regs args; u_int16_t v; int already = 0; int errok = 0; v = pcibios_get_version(); if (v < 0x0210) { PRVERB(( "pci_cfgintr: BIOS %x.%02x doesn't support interrupt routing\n", (v & 0xff00) >> 8, v & 0xff)); return (PCI_INVALID_IRQ); } if ((bus < 0) || (bus > 255) || (device < 0) || (device > 255) || (pin < 1) || (pin > 4)) return(PCI_INVALID_IRQ); /* * Scan the entry table for a contender */ for (i = 0, pe = &pci_route_table->pt_entry[0]; i < pci_route_count; i++, pe++) { if ((bus != pe->pe_bus) || (device != pe->pe_device)) continue; /* * A link of 0 means that this intpin is not connected to * any other device's interrupt pins and is not connected to * any of the Interrupt Router's interrupt pins, so we can't * route it. */ if (pe->pe_intpin[pin - 1].link == 0) continue; if (pci_cfgintr_valid(pe, pin, oldirq)) { printf("pci_cfgintr: %d:%d INT%c BIOS irq %d\n", bus, device, 'A' + pin - 1, oldirq); return (oldirq); } /* * We try to find a linked interrupt, then we look to see * if the interrupt is uniquely routed, then we look for * a virgin interrupt. The virgin interrupt should return * an interrupt we can route, but if that fails, maybe we * should try harder to route a different interrupt. * However, experience has shown that that's rarely the * failure mode we see. */ irq = pci_cfgintr_linked(pe, pin); if (irq != PCI_INVALID_IRQ) already = 1; if (irq == PCI_INVALID_IRQ) { irq = pci_cfgintr_unique(pe, pin); if (irq != PCI_INVALID_IRQ) errok = 1; } if (irq == PCI_INVALID_IRQ) irq = pci_cfgintr_virgin(pe, pin); if (irq == PCI_INVALID_IRQ) break; /* * Ask the BIOS to route the interrupt. If we picked an * interrupt that failed, we should really try other * choices that the BIOS offers us. * * For uniquely routed interrupts, we need to try * to route them on some machines. Yet other machines * fail to route, so we have to pretend that in that * case it worked. Isn't pc hardware fun? * * NOTE: if we want to whack hardware to do this, then * I think the right way to do that would be to have * bridge drivers that do this. I'm not sure that the * $PIR table would be valid for those interrupt * routers. */ args.eax = PCIBIOS_ROUTE_INTERRUPT; args.ebx = (bus << 8) | (device << 3); /* pin value is 0xa - 0xd */ args.ecx = (irq << 8) | (0xa + pin - 1); if (!already && bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL)) && !errok) { PRVERB(("pci_cfgintr: ROUTE_INTERRUPT failed.\n")); return(PCI_INVALID_IRQ); } printf("pci_cfgintr: %d:%d INT%c routed to irq %d\n", bus, device, 'A' + pin - 1, irq); return(irq); } PRVERB(("pci_cfgintr: can't route an interrupt to %d:%d INT%c\n", bus, device, 'A' + pin - 1)); return(PCI_INVALID_IRQ); } /* * Check to see if an existing IRQ setting is valid. */ static int pci_cfgintr_valid(struct PIR_entry *pe, int pin, int irq) { uint32_t irqmask; if (!PCI_INTERRUPT_VALID(irq)) return (0); irqmask = pe->pe_intpin[pin - 1].irqs; if (irqmask & (1 << irq)) { PRVERB(("pci_cfgintr_valid: BIOS irq %d is valid\n", irq)); return (1); } return (0); } /* * Look to see if the routing table claims this pin is uniquely routed. */ static int pci_cfgintr_unique(struct PIR_entry *pe, int pin) { int irq; uint32_t irqmask; irqmask = pe->pe_intpin[pin - 1].irqs; if (irqmask != 0 && powerof2(irqmask)) { irq = ffs(irqmask) - 1; PRVERB(("pci_cfgintr_unique: hard-routed to irq %d\n", irq)); return(irq); } return(PCI_INVALID_IRQ); } /* * Look for another device which shares the same link byte and * already has a unique IRQ, or which has had one routed already. */ static int pci_cfgintr_linked(struct PIR_entry *pe, int pin) { struct PIR_entry *oe; struct PIR_intpin *pi; int i, j, irq; /* * Scan table slots. */ for (i = 0, oe = &pci_route_table->pt_entry[0]; i < pci_route_count; i++, oe++) { /* scan interrupt pins */ for (j = 0, pi = &oe->pe_intpin[0]; j < 4; j++, pi++) { /* don't look at the entry we're trying to match */ if ((pe == oe) && (i == (pin - 1))) continue; /* compare link bytes */ if (pi->link != pe->pe_intpin[pin - 1].link) continue; /* link destination mapped to a unique interrupt? */ if (pi->irqs != 0 && powerof2(pi->irqs)) { irq = ffs(pi->irqs) - 1; PRVERB(("pci_cfgintr_linked: linked (%x) to hard-routed irq %d\n", pi->link, irq)); return(irq); } /* * look for the real PCI device that matches this * table entry */ irq = pci_cfgintr_search(pe, oe->pe_bus, oe->pe_device, j, pin); if (irq != PCI_INVALID_IRQ) return(irq); } } return(PCI_INVALID_IRQ); } /* * Scan for the real PCI device at (bus)/(device) using intpin (matchpin) and * see if it has already been assigned an interrupt. */ static int pci_cfgintr_search(struct PIR_entry *pe, int bus, int device, int matchpin, int pin) { devclass_t pci_devclass; device_t *pci_devices; int pci_count; device_t *pci_children; int pci_childcount; device_t *busp, *childp; int i, j, irq; /* * Find all the PCI busses. */ pci_count = 0; if ((pci_devclass = devclass_find("pci")) != NULL) devclass_get_devices(pci_devclass, &pci_devices, &pci_count); /* * Scan all the PCI busses/devices looking for this one. */ irq = PCI_INVALID_IRQ; for (i = 0, busp = pci_devices; (i < pci_count) && (irq == PCI_INVALID_IRQ); i++, busp++) { pci_childcount = 0; device_get_children(*busp, &pci_children, &pci_childcount); for (j = 0, childp = pci_children; j < pci_childcount; j++, childp++) { if ((pci_get_bus(*childp) == bus) && (pci_get_slot(*childp) == device) && (pci_get_intpin(*childp) == matchpin)) { irq = pci_i386_map_intline(pci_get_irq(*childp)); if (irq != PCI_INVALID_IRQ) PRVERB(("pci_cfgintr_search: linked (%x) to configured irq %d at %d:%d:%d\n", pe->pe_intpin[pin - 1].link, irq, pci_get_bus(*childp), pci_get_slot(*childp), pci_get_function(*childp))); break; } } if (pci_children != NULL) free(pci_children, M_TEMP); } if (pci_devices != NULL) free(pci_devices, M_TEMP); return(irq); } /* * Pick a suitable IRQ from those listed as routable to this device. */ static int pci_cfgintr_virgin(struct PIR_entry *pe, int pin) { int irq, ibit; /* * first scan the set of PCI-only interrupts and see if any of these * are routable */ for (irq = 0; irq < 16; irq++) { ibit = (1 << irq); /* can we use this interrupt? */ if ((pci_route_table->pt_header.ph_pci_irqs & ibit) && (pe->pe_intpin[pin - 1].irqs & ibit)) { PRVERB(("pci_cfgintr_virgin: using routable PCI-only interrupt %d\n", irq)); return(irq); } } /* life is tough, so just pick an interrupt */ for (irq = 0; irq < 16; irq++) { ibit = (1 << irq); if (pe->pe_intpin[pin - 1].irqs & ibit) { PRVERB(("pci_cfgintr_virgin: using routable interrupt %d\n", irq)); return(irq); } } return(PCI_INVALID_IRQ); } static void pci_print_irqmask(u_int16_t irqs) { int i, first; if (irqs == 0) { printf("none"); return; } first = 1; for (i = 0; i < 16; i++, irqs >>= 1) if (irqs & 1) { if (!first) printf(" "); else first = 0; printf("%d", i); } } /* * Dump the contents of a PCI BIOS Interrupt Routing Table to the console. */ static void pci_print_route_table(struct PIR_table *prt, int size) { struct PIR_entry *entry; struct PIR_intpin *intpin; int i, pin; printf("PCI-Only Interrupts: "); pci_print_irqmask(prt->pt_header.ph_pci_irqs); printf("\nLocation Bus Device Pin Link IRQs\n"); entry = &prt->pt_entry[0]; for (i = 0; i < size; i++, entry++) { intpin = &entry->pe_intpin[0]; for (pin = 0; pin < 4; pin++, intpin++) if (intpin->link != 0) { if (entry->pe_slot == 0) printf("embedded "); else printf("slot %-3d ", entry->pe_slot); printf(" %3d %3d %c 0x%02x ", entry->pe_bus, entry->pe_device, 'A' + pin, intpin->link); pci_print_irqmask(intpin->irqs); printf("\n"); } } } /* * See if any interrupts for a given PCI bus are routed in the PIR. Don't * even bother looking if the BIOS doesn't support routing anyways. */ int pci_probe_route_table(int bus) { int i; u_int16_t v; v = pcibios_get_version(); if (v < 0x0210) return (0); for (i = 0; i < pci_route_count; i++) if (pci_route_table->pt_entry[i].pe_bus == bus) return (1); return (0); } -#ifdef USE_PCI_BIOS_FOR_READ_WRITE -/* - * Config space access using BIOS functions - */ -static int -pcibios_cfgread(int bus, int slot, int func, int reg, int bytes) -{ - struct bios_regs args; - u_int mask; - - switch(bytes) { - case 1: - args.eax = PCIBIOS_READ_CONFIG_BYTE; - mask = 0xff; - break; - case 2: - args.eax = PCIBIOS_READ_CONFIG_WORD; - mask = 0xffff; - break; - case 4: - args.eax = PCIBIOS_READ_CONFIG_DWORD; - mask = 0xffffffff; - break; - default: - return(-1); - } - args.ebx = (bus << 8) | (slot << 3) | func; - args.edi = reg; - bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL)); - /* check call results? */ - return(args.ecx & mask); -} - -static void -pcibios_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) -{ - struct bios_regs args; - - switch(bytes) { - case 1: - args.eax = PCIBIOS_WRITE_CONFIG_BYTE; - break; - case 2: - args.eax = PCIBIOS_WRITE_CONFIG_WORD; - break; - case 4: - args.eax = PCIBIOS_WRITE_CONFIG_DWORD; - break; - default: - return; - } - args.ebx = (bus << 8) | (slot << 3) | func; - args.ecx = data; - args.edi = reg; - bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL)); -} -#endif - -/* - * Determine whether there is a PCI BIOS present - */ -static int -pcibios_cfgopen(void) -{ - u_int16_t v = 0; - - if (PCIbios.ventry != 0 && enable_pcibios) { - v = pcibios_get_version(); - if (v > 0) - printf("pcibios: BIOS version %x.%02x\n", - (v & 0xff00) >> 8, v & 0xff); - } - return (v > 0); -} - /* * Configuration space access using direct register operations */ /* enable configuration space accesses and return data port address */ static int pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) { int dataport = 0; if (bus <= PCI_BUSMAX && slot < devmax && func <= PCI_FUNCMAX && reg <= PCI_REGMAX && bytes != 3 && (unsigned) bytes <= 4 && (reg & (bytes - 1)) == 0) { switch (cfgmech) { case 1: outl(CONF1_ADDR_PORT, (1 << 31) | (bus << 16) | (slot << 11) | (func << 8) | (reg & ~0x03)); dataport = CONF1_DATA_PORT + (reg & 0x03); break; case 2: outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1)); outb(CONF2_FORWARD_PORT, bus); dataport = 0xc000 | (slot << 8) | reg; break; } } return (dataport); } /* disable configuration space accesses */ static void pci_cfgdisable(void) { switch (cfgmech) { case 1: outl(CONF1_ADDR_PORT, 0); break; case 2: outb(CONF2_ENABLE_PORT, 0); outb(CONF2_FORWARD_PORT, 0); break; } } static int pcireg_cfgread(int bus, int slot, int func, int reg, int bytes) { int data = -1; int port; + mtx_lock_spin(&pcicfg_mtx); port = pci_cfgenable(bus, slot, func, reg, bytes); - if (port != 0) { switch (bytes) { case 1: data = inb(port); break; case 2: data = inw(port); break; case 4: data = inl(port); break; } pci_cfgdisable(); } + mtx_unlock_spin(&pcicfg_mtx); return (data); } static void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) { int port; + mtx_lock_spin(&pcicfg_mtx); port = pci_cfgenable(bus, slot, func, reg, bytes); if (port != 0) { switch (bytes) { case 1: outb(port, data); break; case 2: outw(port, data); break; case 4: outl(port, data); break; } pci_cfgdisable(); } + mtx_unlock_spin(&pcicfg_mtx); } /* check whether the configuration mechanism has been correctly identified */ static int pci_cfgcheck(int maxdev) { uint32_t id, class; uint8_t header; uint8_t device; + int port; if (bootverbose) printf("pci_cfgcheck:\tdevice "); for (device = 0; device < maxdev; device++) { if (bootverbose) printf("%d ", device); - id = inl(pci_cfgenable(0, device, 0, 0, 4)); + port = pci_cfgenable(0, device, 0, 0, 4); + id = inl(port); if (id == 0 || id == 0xffffffff) continue; - class = inl(pci_cfgenable(0, device, 0, 8, 4)) >> 8; + port = pci_cfgenable(0, device, 0, 8, 4); + class = inl(port) >> 8; if (bootverbose) printf("[class=%06x] ", class); if (class == 0 || (class & 0xf870ff) != 0) continue; - header = inb(pci_cfgenable(0, device, 0, 14, 1)); + port = pci_cfgenable(0, device, 0, 14, 1); + header = inb(port); if (bootverbose) printf("[hdr=%02x] ", header); if ((header & 0x7e) != 0) continue; if (bootverbose) printf("is there (id=%08x)\n", id); pci_cfgdisable(); return (1); } if (bootverbose) printf("-- nothing found\n"); pci_cfgdisable(); return (0); } static int pcireg_cfgopen(void) { uint32_t mode1res, oldval1; uint8_t mode2res, oldval2; oldval1 = inl(CONF1_ADDR_PORT); if (bootverbose) { printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08x\n", oldval1); } if ((oldval1 & CONF1_ENABLE_MSK) == 0) { cfgmech = 1; devmax = 32; outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK); outb(CONF1_ADDR_PORT + 3, 0); mode1res = inl(CONF1_ADDR_PORT); outl(CONF1_ADDR_PORT, oldval1); if (bootverbose) printf("pci_open(1a):\tmode1res=0x%08x (0x%08lx)\n", mode1res, CONF1_ENABLE_CHK); if (mode1res) { if (pci_cfgcheck(32)) return (cfgmech); } outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); mode1res = inl(CONF1_ADDR_PORT); outl(CONF1_ADDR_PORT, oldval1); if (bootverbose) printf("pci_open(1b):\tmode1res=0x%08x (0x%08lx)\n", mode1res, CONF1_ENABLE_CHK1); if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { if (pci_cfgcheck(32)) return (cfgmech); } } oldval2 = inb(CONF2_ENABLE_PORT); if (bootverbose) { printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", oldval2); } if ((oldval2 & 0xf0) == 0) { cfgmech = 2; devmax = 16; outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); mode2res = inb(CONF2_ENABLE_PORT); outb(CONF2_ENABLE_PORT, oldval2); if (bootverbose) printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", mode2res, CONF2_ENABLE_CHK); if (mode2res == CONF2_ENABLE_RES) { if (bootverbose) printf("pci_open(2a):\tnow trying mechanism 2\n"); if (pci_cfgcheck(16)) return (cfgmech); } } cfgmech = 0; devmax = 0; return (cfgmech); } Index: head/sys/i386/pci/pci_pir.c =================================================================== --- head/sys/i386/pci/pci_pir.c (revision 111067) +++ head/sys/i386/pci/pci_pir.c (revision 111068) @@ -1,923 +1,822 @@ /* * Copyright (c) 1997, Stefan Esser * Copyright (c) 2000, Michael Smith * Copyright (c) 2000, BSDi * 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 unmodified, 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. * * $FreeBSD$ * */ #include /* XXX trim includes */ #include #include #include #include #include +#include +#include #include #include #include #include #include #include #include #include #include #ifdef APIC_IO #include #endif /* APIC_IO */ #include "pcib_if.h" #define PRVERB(a) do { \ if (bootverbose) \ printf a ; \ } while(0) static int cfgmech; static int devmax; -static int usebios; -static int enable_pcibios = 0; -TUNABLE_INT("hw.pci.enable_pcibios", &enable_pcibios); - static int pci_cfgintr_valid(struct PIR_entry *pe, int pin, int irq); static int pci_cfgintr_unique(struct PIR_entry *pe, int pin); static int pci_cfgintr_linked(struct PIR_entry *pe, int pin); static int pci_cfgintr_search(struct PIR_entry *pe, int bus, int device, int matchpin, int pin); static int pci_cfgintr_virgin(struct PIR_entry *pe, int pin); static void pci_print_irqmask(u_int16_t irqs); static void pci_print_route_table(struct PIR_table *prt, int size); -#ifdef USE_PCI_BIOS_FOR_READ_WRITE -static int pcibios_cfgread(int bus, int slot, int func, int reg, int bytes); -static void pcibios_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes); -#endif -static int pcibios_cfgopen(void); static int pcireg_cfgread(int bus, int slot, int func, int reg, int bytes); static void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes); static int pcireg_cfgopen(void); static struct PIR_table *pci_route_table; static int pci_route_count; +static struct mtx pcicfg_mtx; + /* * Some BIOS writers seem to want to ignore the spec and put * 0 in the intline rather than 255 to indicate none. Some use * numbers in the range 128-254 to indicate something strange and * apparently undocumented anywhere. Assume these are completely bogus * and map them to 255, which means "none". */ static __inline__ int pci_i386_map_intline(int line) { if (line == 0 || line >= 128) return (PCI_INVALID_IRQ); return (line); } -int -pci_pcibios_active(void) -{ - return (usebios); -} - -int -pci_kill_pcibios(void) -{ - usebios = 0; - return (pcireg_cfgopen() != 0); -} - static u_int16_t pcibios_get_version(void) { struct bios_regs args; if (PCIbios.ventry == 0) { PRVERB(("pcibios: No call entry point\n")); return (0); } args.eax = PCIBIOS_BIOS_PRESENT; if (bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL))) { PRVERB(("pcibios: BIOS_PRESENT call failed\n")); return (0); } if (args.edx != 0x20494350) { PRVERB(("pcibios: BIOS_PRESENT didn't return 'PCI ' in edx\n")); return (0); } return (args.ebx & 0xffff); } /* * Initialise access to PCI configuration space */ int pci_cfgregopen(void) { static int opened = 0; u_long sigaddr; static struct PIR_table *pt; + u_int16_t v; u_int8_t ck, *cv; int i; if (opened) return(1); - if (pcibios_cfgopen() != 0) - usebios = 1; - else if (pcireg_cfgopen() != 0) - usebios = 0; - else + if (pcireg_cfgopen() == 0) return(0); + v = pcibios_get_version(); + if (v > 0) + printf("pcibios: BIOS version %x.%02x\n", (v & 0xff00) >> 8, + v & 0xff); + /* * Look for the interrupt routing table. * * We use PCI BIOS's PIR table if it's available $PIR is the * standard way to do this. Sadly, some machines are not * standards conforming and have _PIR instead. We shrug and cope * by looking for both. */ if (pcibios_get_version() >= 0x0210 && pt == NULL) { sigaddr = bios_sigsearch(0, "$PIR", 4, 16, 0); if (sigaddr == 0) sigaddr = bios_sigsearch(0, "_PIR", 4, 16, 0); if (sigaddr != 0) { pt = (struct PIR_table *)(uintptr_t) BIOS_PADDRTOVADDR(sigaddr); for (cv = (u_int8_t *)pt, ck = 0, i = 0; i < (pt->pt_header.ph_length); i++) { ck += cv[i]; } if (ck == 0 && pt->pt_header.ph_length > sizeof(struct PIR_header)) { pci_route_table = pt; pci_route_count = (pt->pt_header.ph_length - sizeof(struct PIR_header)) / sizeof(struct PIR_entry); printf("Using $PIR table, %d entries at %p\n", pci_route_count, pci_route_table); if (bootverbose) pci_print_route_table(pci_route_table, pci_route_count); } } } + mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN); opened = 1; return(1); } /* * Read configuration space register */ -static u_int32_t -pci_do_cfgregread(int bus, int slot, int func, int reg, int bytes) -{ -#ifdef USE_PCI_BIOS_FOR_READ_WRITE - return(usebios ? - pcibios_cfgread(bus, slot, func, reg, bytes) : - pcireg_cfgread(bus, slot, func, reg, bytes)); -#else - return (pcireg_cfgread(bus, slot, func, reg, bytes)); -#endif -} - u_int32_t pci_cfgregread(int bus, int slot, int func, int reg, int bytes) { uint32_t line; #ifdef APIC_IO uint32_t pin; /* * If we are using the APIC, the contents of the intline * register will probably be wrong (since they are set up for * use with the PIC. Rather than rewrite these registers * (maybe that would be smarter) we trap attempts to read them * and translate to our private vector numbers. */ if ((reg == PCIR_INTLINE) && (bytes == 1)) { - pin = pci_do_cfgregread(bus, slot, func, PCIR_INTPIN, 1); - line = pci_do_cfgregread(bus, slot, func, PCIR_INTLINE, 1); + pin = pcireg_cfgread(bus, slot, func, PCIR_INTPIN, 1); + line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1); if (pin != 0) { int airq; airq = pci_apic_irq(bus, slot, pin); if (airq >= 0) { /* PCI specific entry found in MP table */ if (airq != line) undirect_pci_irq(line); return(airq); } else { /* * PCI interrupts might be redirected * to the ISA bus according to some MP * tables. Use the same methods as * used by the ISA devices devices to * find the proper IOAPIC int pin. */ airq = isa_apic_irq(line); if ((airq >= 0) && (airq != line)) { /* XXX: undirect_pci_irq() ? */ undirect_isa_irq(line); return(airq); } } } return(line); } #else /* * Some BIOS writers seem to want to ignore the spec and put * 0 in the intline rather than 255 to indicate none. The rest of * the code uses 255 as an invalid IRQ. */ if (reg == PCIR_INTLINE && bytes == 1) { - line = pci_do_cfgregread(bus, slot, func, PCIR_INTLINE, 1); + line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1); return pci_i386_map_intline(line); } #endif /* APIC_IO */ - return(pci_do_cfgregread(bus, slot, func, reg, bytes)); + return(pcireg_cfgread(bus, slot, func, reg, bytes)); } /* * Write configuration space register */ void pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes) { -#ifdef USE_PCI_BIOS_FOR_READ_WRITE - if (usebios) - pcibios_cfgwrite(bus, slot, func, reg, data, bytes); - else - pcireg_cfgwrite(bus, slot, func, reg, data, bytes); -#else + pcireg_cfgwrite(bus, slot, func, reg, data, bytes); -#endif } /* * Route a PCI interrupt */ int pci_cfgintr(int bus, int device, int pin, int oldirq) { struct PIR_entry *pe; int i, irq; struct bios_regs args; u_int16_t v; int already = 0; int errok = 0; v = pcibios_get_version(); if (v < 0x0210) { PRVERB(( "pci_cfgintr: BIOS %x.%02x doesn't support interrupt routing\n", (v & 0xff00) >> 8, v & 0xff)); return (PCI_INVALID_IRQ); } if ((bus < 0) || (bus > 255) || (device < 0) || (device > 255) || (pin < 1) || (pin > 4)) return(PCI_INVALID_IRQ); /* * Scan the entry table for a contender */ for (i = 0, pe = &pci_route_table->pt_entry[0]; i < pci_route_count; i++, pe++) { if ((bus != pe->pe_bus) || (device != pe->pe_device)) continue; /* * A link of 0 means that this intpin is not connected to * any other device's interrupt pins and is not connected to * any of the Interrupt Router's interrupt pins, so we can't * route it. */ if (pe->pe_intpin[pin - 1].link == 0) continue; if (pci_cfgintr_valid(pe, pin, oldirq)) { printf("pci_cfgintr: %d:%d INT%c BIOS irq %d\n", bus, device, 'A' + pin - 1, oldirq); return (oldirq); } /* * We try to find a linked interrupt, then we look to see * if the interrupt is uniquely routed, then we look for * a virgin interrupt. The virgin interrupt should return * an interrupt we can route, but if that fails, maybe we * should try harder to route a different interrupt. * However, experience has shown that that's rarely the * failure mode we see. */ irq = pci_cfgintr_linked(pe, pin); if (irq != PCI_INVALID_IRQ) already = 1; if (irq == PCI_INVALID_IRQ) { irq = pci_cfgintr_unique(pe, pin); if (irq != PCI_INVALID_IRQ) errok = 1; } if (irq == PCI_INVALID_IRQ) irq = pci_cfgintr_virgin(pe, pin); if (irq == PCI_INVALID_IRQ) break; /* * Ask the BIOS to route the interrupt. If we picked an * interrupt that failed, we should really try other * choices that the BIOS offers us. * * For uniquely routed interrupts, we need to try * to route them on some machines. Yet other machines * fail to route, so we have to pretend that in that * case it worked. Isn't pc hardware fun? * * NOTE: if we want to whack hardware to do this, then * I think the right way to do that would be to have * bridge drivers that do this. I'm not sure that the * $PIR table would be valid for those interrupt * routers. */ args.eax = PCIBIOS_ROUTE_INTERRUPT; args.ebx = (bus << 8) | (device << 3); /* pin value is 0xa - 0xd */ args.ecx = (irq << 8) | (0xa + pin - 1); if (!already && bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL)) && !errok) { PRVERB(("pci_cfgintr: ROUTE_INTERRUPT failed.\n")); return(PCI_INVALID_IRQ); } printf("pci_cfgintr: %d:%d INT%c routed to irq %d\n", bus, device, 'A' + pin - 1, irq); return(irq); } PRVERB(("pci_cfgintr: can't route an interrupt to %d:%d INT%c\n", bus, device, 'A' + pin - 1)); return(PCI_INVALID_IRQ); } /* * Check to see if an existing IRQ setting is valid. */ static int pci_cfgintr_valid(struct PIR_entry *pe, int pin, int irq) { uint32_t irqmask; if (!PCI_INTERRUPT_VALID(irq)) return (0); irqmask = pe->pe_intpin[pin - 1].irqs; if (irqmask & (1 << irq)) { PRVERB(("pci_cfgintr_valid: BIOS irq %d is valid\n", irq)); return (1); } return (0); } /* * Look to see if the routing table claims this pin is uniquely routed. */ static int pci_cfgintr_unique(struct PIR_entry *pe, int pin) { int irq; uint32_t irqmask; irqmask = pe->pe_intpin[pin - 1].irqs; if (irqmask != 0 && powerof2(irqmask)) { irq = ffs(irqmask) - 1; PRVERB(("pci_cfgintr_unique: hard-routed to irq %d\n", irq)); return(irq); } return(PCI_INVALID_IRQ); } /* * Look for another device which shares the same link byte and * already has a unique IRQ, or which has had one routed already. */ static int pci_cfgintr_linked(struct PIR_entry *pe, int pin) { struct PIR_entry *oe; struct PIR_intpin *pi; int i, j, irq; /* * Scan table slots. */ for (i = 0, oe = &pci_route_table->pt_entry[0]; i < pci_route_count; i++, oe++) { /* scan interrupt pins */ for (j = 0, pi = &oe->pe_intpin[0]; j < 4; j++, pi++) { /* don't look at the entry we're trying to match */ if ((pe == oe) && (i == (pin - 1))) continue; /* compare link bytes */ if (pi->link != pe->pe_intpin[pin - 1].link) continue; /* link destination mapped to a unique interrupt? */ if (pi->irqs != 0 && powerof2(pi->irqs)) { irq = ffs(pi->irqs) - 1; PRVERB(("pci_cfgintr_linked: linked (%x) to hard-routed irq %d\n", pi->link, irq)); return(irq); } /* * look for the real PCI device that matches this * table entry */ irq = pci_cfgintr_search(pe, oe->pe_bus, oe->pe_device, j, pin); if (irq != PCI_INVALID_IRQ) return(irq); } } return(PCI_INVALID_IRQ); } /* * Scan for the real PCI device at (bus)/(device) using intpin (matchpin) and * see if it has already been assigned an interrupt. */ static int pci_cfgintr_search(struct PIR_entry *pe, int bus, int device, int matchpin, int pin) { devclass_t pci_devclass; device_t *pci_devices; int pci_count; device_t *pci_children; int pci_childcount; device_t *busp, *childp; int i, j, irq; /* * Find all the PCI busses. */ pci_count = 0; if ((pci_devclass = devclass_find("pci")) != NULL) devclass_get_devices(pci_devclass, &pci_devices, &pci_count); /* * Scan all the PCI busses/devices looking for this one. */ irq = PCI_INVALID_IRQ; for (i = 0, busp = pci_devices; (i < pci_count) && (irq == PCI_INVALID_IRQ); i++, busp++) { pci_childcount = 0; device_get_children(*busp, &pci_children, &pci_childcount); for (j = 0, childp = pci_children; j < pci_childcount; j++, childp++) { if ((pci_get_bus(*childp) == bus) && (pci_get_slot(*childp) == device) && (pci_get_intpin(*childp) == matchpin)) { irq = pci_i386_map_intline(pci_get_irq(*childp)); if (irq != PCI_INVALID_IRQ) PRVERB(("pci_cfgintr_search: linked (%x) to configured irq %d at %d:%d:%d\n", pe->pe_intpin[pin - 1].link, irq, pci_get_bus(*childp), pci_get_slot(*childp), pci_get_function(*childp))); break; } } if (pci_children != NULL) free(pci_children, M_TEMP); } if (pci_devices != NULL) free(pci_devices, M_TEMP); return(irq); } /* * Pick a suitable IRQ from those listed as routable to this device. */ static int pci_cfgintr_virgin(struct PIR_entry *pe, int pin) { int irq, ibit; /* * first scan the set of PCI-only interrupts and see if any of these * are routable */ for (irq = 0; irq < 16; irq++) { ibit = (1 << irq); /* can we use this interrupt? */ if ((pci_route_table->pt_header.ph_pci_irqs & ibit) && (pe->pe_intpin[pin - 1].irqs & ibit)) { PRVERB(("pci_cfgintr_virgin: using routable PCI-only interrupt %d\n", irq)); return(irq); } } /* life is tough, so just pick an interrupt */ for (irq = 0; irq < 16; irq++) { ibit = (1 << irq); if (pe->pe_intpin[pin - 1].irqs & ibit) { PRVERB(("pci_cfgintr_virgin: using routable interrupt %d\n", irq)); return(irq); } } return(PCI_INVALID_IRQ); } static void pci_print_irqmask(u_int16_t irqs) { int i, first; if (irqs == 0) { printf("none"); return; } first = 1; for (i = 0; i < 16; i++, irqs >>= 1) if (irqs & 1) { if (!first) printf(" "); else first = 0; printf("%d", i); } } /* * Dump the contents of a PCI BIOS Interrupt Routing Table to the console. */ static void pci_print_route_table(struct PIR_table *prt, int size) { struct PIR_entry *entry; struct PIR_intpin *intpin; int i, pin; printf("PCI-Only Interrupts: "); pci_print_irqmask(prt->pt_header.ph_pci_irqs); printf("\nLocation Bus Device Pin Link IRQs\n"); entry = &prt->pt_entry[0]; for (i = 0; i < size; i++, entry++) { intpin = &entry->pe_intpin[0]; for (pin = 0; pin < 4; pin++, intpin++) if (intpin->link != 0) { if (entry->pe_slot == 0) printf("embedded "); else printf("slot %-3d ", entry->pe_slot); printf(" %3d %3d %c 0x%02x ", entry->pe_bus, entry->pe_device, 'A' + pin, intpin->link); pci_print_irqmask(intpin->irqs); printf("\n"); } } } /* * See if any interrupts for a given PCI bus are routed in the PIR. Don't * even bother looking if the BIOS doesn't support routing anyways. */ int pci_probe_route_table(int bus) { int i; u_int16_t v; v = pcibios_get_version(); if (v < 0x0210) return (0); for (i = 0; i < pci_route_count; i++) if (pci_route_table->pt_entry[i].pe_bus == bus) return (1); return (0); } -#ifdef USE_PCI_BIOS_FOR_READ_WRITE -/* - * Config space access using BIOS functions - */ -static int -pcibios_cfgread(int bus, int slot, int func, int reg, int bytes) -{ - struct bios_regs args; - u_int mask; - - switch(bytes) { - case 1: - args.eax = PCIBIOS_READ_CONFIG_BYTE; - mask = 0xff; - break; - case 2: - args.eax = PCIBIOS_READ_CONFIG_WORD; - mask = 0xffff; - break; - case 4: - args.eax = PCIBIOS_READ_CONFIG_DWORD; - mask = 0xffffffff; - break; - default: - return(-1); - } - args.ebx = (bus << 8) | (slot << 3) | func; - args.edi = reg; - bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL)); - /* check call results? */ - return(args.ecx & mask); -} - -static void -pcibios_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) -{ - struct bios_regs args; - - switch(bytes) { - case 1: - args.eax = PCIBIOS_WRITE_CONFIG_BYTE; - break; - case 2: - args.eax = PCIBIOS_WRITE_CONFIG_WORD; - break; - case 4: - args.eax = PCIBIOS_WRITE_CONFIG_DWORD; - break; - default: - return; - } - args.ebx = (bus << 8) | (slot << 3) | func; - args.ecx = data; - args.edi = reg; - bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL)); -} -#endif - -/* - * Determine whether there is a PCI BIOS present - */ -static int -pcibios_cfgopen(void) -{ - u_int16_t v = 0; - - if (PCIbios.ventry != 0 && enable_pcibios) { - v = pcibios_get_version(); - if (v > 0) - printf("pcibios: BIOS version %x.%02x\n", - (v & 0xff00) >> 8, v & 0xff); - } - return (v > 0); -} - /* * Configuration space access using direct register operations */ /* enable configuration space accesses and return data port address */ static int pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) { int dataport = 0; if (bus <= PCI_BUSMAX && slot < devmax && func <= PCI_FUNCMAX && reg <= PCI_REGMAX && bytes != 3 && (unsigned) bytes <= 4 && (reg & (bytes - 1)) == 0) { switch (cfgmech) { case 1: outl(CONF1_ADDR_PORT, (1 << 31) | (bus << 16) | (slot << 11) | (func << 8) | (reg & ~0x03)); dataport = CONF1_DATA_PORT + (reg & 0x03); break; case 2: outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1)); outb(CONF2_FORWARD_PORT, bus); dataport = 0xc000 | (slot << 8) | reg; break; } } return (dataport); } /* disable configuration space accesses */ static void pci_cfgdisable(void) { switch (cfgmech) { case 1: outl(CONF1_ADDR_PORT, 0); break; case 2: outb(CONF2_ENABLE_PORT, 0); outb(CONF2_FORWARD_PORT, 0); break; } } static int pcireg_cfgread(int bus, int slot, int func, int reg, int bytes) { int data = -1; int port; + mtx_lock_spin(&pcicfg_mtx); port = pci_cfgenable(bus, slot, func, reg, bytes); - if (port != 0) { switch (bytes) { case 1: data = inb(port); break; case 2: data = inw(port); break; case 4: data = inl(port); break; } pci_cfgdisable(); } + mtx_unlock_spin(&pcicfg_mtx); return (data); } static void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) { int port; + mtx_lock_spin(&pcicfg_mtx); port = pci_cfgenable(bus, slot, func, reg, bytes); if (port != 0) { switch (bytes) { case 1: outb(port, data); break; case 2: outw(port, data); break; case 4: outl(port, data); break; } pci_cfgdisable(); } + mtx_unlock_spin(&pcicfg_mtx); } /* check whether the configuration mechanism has been correctly identified */ static int pci_cfgcheck(int maxdev) { uint32_t id, class; uint8_t header; uint8_t device; + int port; if (bootverbose) printf("pci_cfgcheck:\tdevice "); for (device = 0; device < maxdev; device++) { if (bootverbose) printf("%d ", device); - id = inl(pci_cfgenable(0, device, 0, 0, 4)); + port = pci_cfgenable(0, device, 0, 0, 4); + id = inl(port); if (id == 0 || id == 0xffffffff) continue; - class = inl(pci_cfgenable(0, device, 0, 8, 4)) >> 8; + port = pci_cfgenable(0, device, 0, 8, 4); + class = inl(port) >> 8; if (bootverbose) printf("[class=%06x] ", class); if (class == 0 || (class & 0xf870ff) != 0) continue; - header = inb(pci_cfgenable(0, device, 0, 14, 1)); + port = pci_cfgenable(0, device, 0, 14, 1); + header = inb(port); if (bootverbose) printf("[hdr=%02x] ", header); if ((header & 0x7e) != 0) continue; if (bootverbose) printf("is there (id=%08x)\n", id); pci_cfgdisable(); return (1); } if (bootverbose) printf("-- nothing found\n"); pci_cfgdisable(); return (0); } static int pcireg_cfgopen(void) { uint32_t mode1res, oldval1; uint8_t mode2res, oldval2; oldval1 = inl(CONF1_ADDR_PORT); if (bootverbose) { printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08x\n", oldval1); } if ((oldval1 & CONF1_ENABLE_MSK) == 0) { cfgmech = 1; devmax = 32; outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK); outb(CONF1_ADDR_PORT + 3, 0); mode1res = inl(CONF1_ADDR_PORT); outl(CONF1_ADDR_PORT, oldval1); if (bootverbose) printf("pci_open(1a):\tmode1res=0x%08x (0x%08lx)\n", mode1res, CONF1_ENABLE_CHK); if (mode1res) { if (pci_cfgcheck(32)) return (cfgmech); } outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); mode1res = inl(CONF1_ADDR_PORT); outl(CONF1_ADDR_PORT, oldval1); if (bootverbose) printf("pci_open(1b):\tmode1res=0x%08x (0x%08lx)\n", mode1res, CONF1_ENABLE_CHK1); if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { if (pci_cfgcheck(32)) return (cfgmech); } } oldval2 = inb(CONF2_ENABLE_PORT); if (bootverbose) { printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", oldval2); } if ((oldval2 & 0xf0) == 0) { cfgmech = 2; devmax = 16; outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); mode2res = inb(CONF2_ENABLE_PORT); outb(CONF2_ENABLE_PORT, oldval2); if (bootverbose) printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", mode2res, CONF2_ENABLE_CHK); if (mode2res == CONF2_ENABLE_RES) { if (bootverbose) printf("pci_open(2a):\tnow trying mechanism 2\n"); if (pci_cfgcheck(16)) return (cfgmech); } } cfgmech = 0; devmax = 0; return (cfgmech); } Index: head/sys/kern/subr_witness.c =================================================================== --- head/sys/kern/subr_witness.c (revision 111067) +++ head/sys/kern/subr_witness.c (revision 111068) @@ -1,1556 +1,1559 @@ /*- * Copyright (c) 1998 Berkeley Software Design, Inc. 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. * 3. Berkeley Software Design Inc's name may not be used to endorse or * promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN INC ``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 BERKELEY SOFTWARE DESIGN INC 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. * * from BSDI $Id: mutex_witness.c,v 1.1.2.20 2000/04/27 03:10:27 cp Exp $ * and BSDI $Id: synch_machdep.c,v 2.3.2.39 2000/04/27 03:10:25 cp Exp $ * $FreeBSD$ */ /* * Implementation of the `witness' lock verifier. Originally implemented for * mutexes in BSD/OS. Extended to handle generic lock objects and lock * classes in FreeBSD. */ /* * Main Entry: witness * Pronunciation: 'wit-n&s * Function: noun * Etymology: Middle English witnesse, from Old English witnes knowledge, * testimony, witness, from 2wit * Date: before 12th century * 1 : attestation of a fact or event : TESTIMONY * 2 : one that gives evidence; specifically : one who testifies in * a cause or before a judicial tribunal * 3 : one asked to be present at a transaction so as to be able to * testify to its having taken place * 4 : one who has personal knowledge of something * 5 a : something serving as evidence or proof : SIGN * b : public affirmation by word or example of usually * religious faith or conviction * 6 capitalized : a member of the Jehovah's Witnesses */ #include "opt_ddb.h" #include "opt_witness.h" #include #include #include #include #include #include #include #include #include #include #include /* Define this to check for blessed mutexes */ #undef BLESSING #define WITNESS_COUNT 200 #define WITNESS_CHILDCOUNT (WITNESS_COUNT * 4) /* * XXX: This is somewhat bogus, as we assume here that at most 1024 threads * will hold LOCK_NCHILDREN * 2 locks. We handle failure ok, and we should * probably be safe for the most part, but it's still a SWAG. */ #define LOCK_CHILDCOUNT (MAXCPU + 1024) * 2 #define WITNESS_NCHILDREN 6 struct witness_child_list_entry; struct witness { const char *w_name; struct lock_class *w_class; STAILQ_ENTRY(witness) w_list; /* List of all witnesses. */ STAILQ_ENTRY(witness) w_typelist; /* Witnesses of a type. */ struct witness_child_list_entry *w_children; /* Great evilness... */ const char *w_file; int w_line; u_int w_level; u_int w_refcount; u_char w_Giant_squawked:1; u_char w_other_squawked:1; u_char w_same_squawked:1; }; struct witness_child_list_entry { struct witness_child_list_entry *wcl_next; struct witness *wcl_children[WITNESS_NCHILDREN]; u_int wcl_count; }; STAILQ_HEAD(witness_list, witness); #ifdef BLESSING struct witness_blessed { const char *b_lock1; const char *b_lock2; }; #endif struct witness_order_list_entry { const char *w_name; struct lock_class *w_class; }; static struct witness *enroll(const char *description, struct lock_class *lock_class); static int itismychild(struct witness *parent, struct witness *child); static void removechild(struct witness *parent, struct witness *child); static int isitmychild(struct witness *parent, struct witness *child); static int isitmydescendant(struct witness *parent, struct witness *child); #ifdef BLESSING static int blessed(struct witness *, struct witness *); #endif static void witness_displaydescendants(void(*)(const char *fmt, ...), struct witness *); static void witness_leveldescendents(struct witness *parent, int level); static void witness_levelall(void); static struct witness *witness_get(void); static void witness_free(struct witness *m); static struct witness_child_list_entry *witness_child_get(void); static void witness_child_free(struct witness_child_list_entry *wcl); static struct lock_list_entry *witness_lock_list_get(void); static void witness_lock_list_free(struct lock_list_entry *lle); static struct lock_instance *find_instance(struct lock_list_entry *lock_list, struct lock_object *lock); #if defined(DDB) static void witness_display_list(void(*prnt)(const char *fmt, ...), struct witness_list *list); static void witness_display(void(*)(const char *fmt, ...)); #endif MALLOC_DEFINE(M_WITNESS, "witness", "witness structure"); static int witness_watch = 1; TUNABLE_INT("debug.witness_watch", &witness_watch); SYSCTL_INT(_debug, OID_AUTO, witness_watch, CTLFLAG_RD, &witness_watch, 0, ""); #ifdef DDB /* * When DDB is enabled and witness_ddb is set to 1, it will cause the system to * drop into kdebug() when: * - a lock heirarchy violation occurs * - locks are held when going to sleep. */ #ifdef WITNESS_DDB int witness_ddb = 1; #else int witness_ddb = 0; #endif TUNABLE_INT("debug.witness_ddb", &witness_ddb); SYSCTL_INT(_debug, OID_AUTO, witness_ddb, CTLFLAG_RW, &witness_ddb, 0, ""); /* * When DDB is enabled and witness_trace is set to 1, it will cause the system * to print a stack trace: * - a lock heirarchy violation occurs * - locks are held when going to sleep. */ int witness_trace = 1; TUNABLE_INT("debug.witness_trace", &witness_trace); SYSCTL_INT(_debug, OID_AUTO, witness_trace, CTLFLAG_RW, &witness_trace, 0, ""); #endif /* DDB */ #ifdef WITNESS_SKIPSPIN int witness_skipspin = 1; #else int witness_skipspin = 0; #endif TUNABLE_INT("debug.witness_skipspin", &witness_skipspin); SYSCTL_INT(_debug, OID_AUTO, witness_skipspin, CTLFLAG_RD, &witness_skipspin, 0, ""); static struct mtx w_mtx; static struct witness_list w_free = STAILQ_HEAD_INITIALIZER(w_free); static struct witness_list w_all = STAILQ_HEAD_INITIALIZER(w_all); static struct witness_list w_spin = STAILQ_HEAD_INITIALIZER(w_spin); static struct witness_list w_sleep = STAILQ_HEAD_INITIALIZER(w_sleep); static struct witness_child_list_entry *w_child_free = NULL; static struct lock_list_entry *w_lock_list_free = NULL; static int witness_dead; /* fatal error, probably no memory */ static struct witness w_data[WITNESS_COUNT]; static struct witness_child_list_entry w_childdata[WITNESS_CHILDCOUNT]; static struct lock_list_entry w_locklistdata[LOCK_CHILDCOUNT]; static struct witness_order_list_entry order_lists[] = { { "Giant", &lock_class_mtx_sleep }, { "proctree", &lock_class_sx }, { "allproc", &lock_class_sx }, { "filedesc structure", &lock_class_mtx_sleep }, { "pipe mutex", &lock_class_mtx_sleep }, { "sigio lock", &lock_class_mtx_sleep }, { "process group", &lock_class_mtx_sleep }, { "process lock", &lock_class_mtx_sleep }, { "session", &lock_class_mtx_sleep }, { "uidinfo hash", &lock_class_mtx_sleep }, { "uidinfo struct", &lock_class_mtx_sleep }, { NULL, NULL }, /* * spin locks */ #ifdef SMP { "ap boot", &lock_class_mtx_spin }, #ifdef __i386__ { "com", &lock_class_mtx_spin }, #endif #endif { "sio", &lock_class_mtx_spin }, #ifdef __i386__ { "cy", &lock_class_mtx_spin }, #endif { "sabtty", &lock_class_mtx_spin }, { "zstty", &lock_class_mtx_spin }, { "ng_node", &lock_class_mtx_spin }, { "ng_worklist", &lock_class_mtx_spin }, { "ithread table lock", &lock_class_mtx_spin }, { "sched lock", &lock_class_mtx_spin }, { "callout", &lock_class_mtx_spin }, /* * leaf locks */ { "allpmaps", &lock_class_mtx_spin }, { "vm page queue free mutex", &lock_class_mtx_spin }, { "icu", &lock_class_mtx_spin }, #ifdef SMP { "smp rendezvous", &lock_class_mtx_spin }, #if defined(__i386__) && defined(APIC_IO) { "tlb", &lock_class_mtx_spin }, #endif #ifdef __sparc64__ { "ipi", &lock_class_mtx_spin }, #endif #endif { "clk", &lock_class_mtx_spin }, { "mutex profiling lock", &lock_class_mtx_spin }, { "kse zombie lock", &lock_class_mtx_spin }, { "ALD Queue", &lock_class_mtx_spin }, #ifdef __ia64__ { "MCA spin lock", &lock_class_mtx_spin }, #endif +#ifdef __i386__ + { "pcicfg", &lock_class_mtx_spin }, +#endif { NULL, NULL }, { NULL, NULL } }; #ifdef BLESSING /* * Pairs of locks which have been blessed * Don't complain about order problems with blessed locks */ static struct witness_blessed blessed_list[] = { }; static int blessed_count = sizeof(blessed_list) / sizeof(struct witness_blessed); #endif /* * List of all locks in the system. */ TAILQ_HEAD(, lock_object) all_locks = TAILQ_HEAD_INITIALIZER(all_locks); static struct mtx all_mtx = { { &lock_class_mtx_sleep, /* mtx_object.lo_class */ "All locks list", /* mtx_object.lo_name */ "All locks list", /* mtx_object.lo_type */ LO_INITIALIZED, /* mtx_object.lo_flags */ { NULL, NULL }, /* mtx_object.lo_list */ NULL }, /* mtx_object.lo_witness */ MTX_UNOWNED, 0, /* mtx_lock, mtx_recurse */ TAILQ_HEAD_INITIALIZER(all_mtx.mtx_blocked), { NULL, NULL } /* mtx_contested */ }; /* * This global is set to 0 once it becomes safe to use the witness code. */ static int witness_cold = 1; /* * Global variables for book keeping. */ static int lock_cur_cnt; static int lock_max_cnt; /* * The WITNESS-enabled diagnostic code. */ static void witness_initialize(void *dummy __unused) { struct lock_object *lock; struct witness_order_list_entry *order; struct witness *w, *w1; int i; /* * We have to release Giant before initializing its witness * structure so that WITNESS doesn't get confused. */ mtx_unlock(&Giant); mtx_assert(&Giant, MA_NOTOWNED); CTR1(KTR_WITNESS, "%s: initializing witness", __func__); TAILQ_INSERT_HEAD(&all_locks, &all_mtx.mtx_object, lo_list); mtx_init(&w_mtx, "witness lock", NULL, MTX_SPIN | MTX_QUIET | MTX_NOWITNESS); for (i = 0; i < WITNESS_COUNT; i++) witness_free(&w_data[i]); for (i = 0; i < WITNESS_CHILDCOUNT; i++) witness_child_free(&w_childdata[i]); for (i = 0; i < LOCK_CHILDCOUNT; i++) witness_lock_list_free(&w_locklistdata[i]); /* First add in all the specified order lists. */ for (order = order_lists; order->w_name != NULL; order++) { w = enroll(order->w_name, order->w_class); if (w == NULL) continue; w->w_file = "order list"; for (order++; order->w_name != NULL; order++) { w1 = enroll(order->w_name, order->w_class); if (w1 == NULL) continue; w1->w_file = "order list"; itismychild(w, w1); w = w1; } } /* Iterate through all locks and add them to witness. */ mtx_lock(&all_mtx); TAILQ_FOREACH(lock, &all_locks, lo_list) { if (lock->lo_flags & LO_WITNESS) lock->lo_witness = enroll(lock->lo_type, lock->lo_class); else lock->lo_witness = NULL; } mtx_unlock(&all_mtx); /* Mark the witness code as being ready for use. */ atomic_store_rel_int(&witness_cold, 0); mtx_lock(&Giant); } SYSINIT(witness_init, SI_SUB_WITNESS, SI_ORDER_FIRST, witness_initialize, NULL) void witness_init(struct lock_object *lock) { struct lock_class *class; class = lock->lo_class; if (lock->lo_flags & LO_INITIALIZED) panic("%s: lock (%s) %s is already initialized", __func__, class->lc_name, lock->lo_name); if ((lock->lo_flags & LO_RECURSABLE) != 0 && (class->lc_flags & LC_RECURSABLE) == 0) panic("%s: lock (%s) %s can not be recursable", __func__, class->lc_name, lock->lo_name); if ((lock->lo_flags & LO_SLEEPABLE) != 0 && (class->lc_flags & LC_SLEEPABLE) == 0) panic("%s: lock (%s) %s can not be sleepable", __func__, class->lc_name, lock->lo_name); if ((lock->lo_flags & LO_UPGRADABLE) != 0 && (class->lc_flags & LC_UPGRADABLE) == 0) panic("%s: lock (%s) %s can not be upgradable", __func__, class->lc_name, lock->lo_name); mtx_lock(&all_mtx); TAILQ_INSERT_TAIL(&all_locks, lock, lo_list); lock->lo_flags |= LO_INITIALIZED; lock_cur_cnt++; if (lock_cur_cnt > lock_max_cnt) lock_max_cnt = lock_cur_cnt; mtx_unlock(&all_mtx); if (!witness_cold && !witness_dead && panicstr == NULL && (lock->lo_flags & LO_WITNESS) != 0) lock->lo_witness = enroll(lock->lo_type, class); else lock->lo_witness = NULL; } void witness_destroy(struct lock_object *lock) { struct witness *w; if (witness_cold) panic("lock (%s) %s destroyed while witness_cold", lock->lo_class->lc_name, lock->lo_name); if ((lock->lo_flags & LO_INITIALIZED) == 0) panic("%s: lock (%s) %s is not initialized", __func__, lock->lo_class->lc_name, lock->lo_name); /* XXX: need to verify that no one holds the lock */ w = lock->lo_witness; if (w != NULL) { mtx_lock_spin(&w_mtx); MPASS(w->w_refcount > 0); w->w_refcount--; mtx_unlock_spin(&w_mtx); } mtx_lock(&all_mtx); lock_cur_cnt--; TAILQ_REMOVE(&all_locks, lock, lo_list); lock->lo_flags &= ~LO_INITIALIZED; mtx_unlock(&all_mtx); } #if defined(DDB) static void witness_display_list(void(*prnt)(const char *fmt, ...), struct witness_list *list) { struct witness *w, *w1; int found; STAILQ_FOREACH(w, list, w_typelist) { if (w->w_file == NULL) continue; found = 0; STAILQ_FOREACH(w1, list, w_typelist) { if (isitmychild(w1, w)) { found++; break; } } if (found) continue; /* * This lock has no anscestors, display its descendants. */ witness_displaydescendants(prnt, w); } } static void witness_display(void(*prnt)(const char *fmt, ...)) { struct witness *w; KASSERT(!witness_cold, ("%s: witness_cold", __func__)); witness_levelall(); /* * First, handle sleep locks which have been acquired at least * once. */ prnt("Sleep locks:\n"); witness_display_list(prnt, &w_sleep); /* * Now do spin locks which have been acquired at least once. */ prnt("\nSpin locks:\n"); witness_display_list(prnt, &w_spin); /* * Finally, any locks which have not been acquired yet. */ prnt("\nLocks which were never acquired:\n"); STAILQ_FOREACH(w, &w_all, w_list) { if (w->w_file != NULL || w->w_refcount == 0) continue; prnt("%s\n", w->w_name); } } #endif void witness_lock(struct lock_object *lock, int flags, const char *file, int line) { struct lock_list_entry **lock_list, *lle; struct lock_instance *lock1, *lock2; struct lock_class *class; struct witness *w, *w1; struct thread *td; int i, j; #ifdef DDB int go_into_ddb = 0; #endif /* DDB */ if (witness_cold || witness_dead || lock->lo_witness == NULL || panicstr != NULL) return; w = lock->lo_witness; class = lock->lo_class; td = curthread; if (class->lc_flags & LC_SLEEPLOCK) { /* * Since spin locks include a critical section, this check * impliclty enforces a lock order of all sleep locks before * all spin locks. */ if (td->td_critnest != 0 && (flags & LOP_TRYLOCK) == 0) panic("blockable sleep lock (%s) %s @ %s:%d", class->lc_name, lock->lo_name, file, line); lock_list = &td->td_sleeplocks; } else lock_list = PCPU_PTR(spinlocks); /* * Try locks do not block if they fail to acquire the lock, thus * there is no danger of deadlocks or of switching while holding a * spin lock if we acquire a lock via a try operation. */ if (flags & LOP_TRYLOCK) goto out; /* * Is this the first lock acquired? If so, then no order checking * is needed. */ if (*lock_list == NULL) goto out; /* * Check to see if we are recursing on a lock we already own. */ lock1 = find_instance(*lock_list, lock); if (lock1 != NULL) { if ((lock1->li_flags & LI_EXCLUSIVE) != 0 && (flags & LOP_EXCLUSIVE) == 0) { printf("shared lock of (%s) %s @ %s:%d\n", class->lc_name, lock->lo_name, file, line); printf("while exclusively locked from %s:%d\n", lock1->li_file, lock1->li_line); panic("share->excl"); } if ((lock1->li_flags & LI_EXCLUSIVE) == 0 && (flags & LOP_EXCLUSIVE) != 0) { printf("exclusive lock of (%s) %s @ %s:%d\n", class->lc_name, lock->lo_name, file, line); printf("while share locked from %s:%d\n", lock1->li_file, lock1->li_line); panic("excl->share"); } lock1->li_flags++; if ((lock->lo_flags & LO_RECURSABLE) == 0) { printf( "recursed on non-recursive lock (%s) %s @ %s:%d\n", class->lc_name, lock->lo_name, file, line); printf("first acquired @ %s:%d\n", lock1->li_file, lock1->li_line); panic("recurse"); } CTR4(KTR_WITNESS, "%s: pid %d recursed on %s r=%d", __func__, td->td_proc->p_pid, lock->lo_name, lock1->li_flags & LI_RECURSEMASK); lock1->li_file = file; lock1->li_line = line; return; } /* * Check for duplicate locks of the same type. Note that we only * have to check for this on the last lock we just acquired. Any * other cases will be caught as lock order violations. */ lock1 = &(*lock_list)->ll_children[(*lock_list)->ll_count - 1]; w1 = lock1->li_lock->lo_witness; if (w1 == w) { if (w->w_same_squawked || (lock->lo_flags & LO_DUPOK)) goto out; w->w_same_squawked = 1; printf("acquiring duplicate lock of same type: \"%s\"\n", lock->lo_type); printf(" 1st %s @ %s:%d\n", lock1->li_lock->lo_name, lock1->li_file, lock1->li_line); printf(" 2nd %s @ %s:%d\n", lock->lo_name, file, line); #ifdef DDB go_into_ddb = 1; #endif /* DDB */ goto out; } MPASS(!mtx_owned(&w_mtx)); mtx_lock_spin(&w_mtx); /* * If we have a known higher number just say ok */ if (witness_watch > 1 && w->w_level > w1->w_level) { mtx_unlock_spin(&w_mtx); goto out; } if (isitmydescendant(w1, w)) { mtx_unlock_spin(&w_mtx); goto out; } for (j = 0, lle = *lock_list; lle != NULL; lle = lle->ll_next) { for (i = lle->ll_count - 1; i >= 0; i--, j++) { MPASS(j < WITNESS_COUNT); lock1 = &lle->ll_children[i]; w1 = lock1->li_lock->lo_witness; /* * If this lock doesn't undergo witness checking, * then skip it. */ if (w1 == NULL) { KASSERT((lock1->li_lock->lo_flags & LO_WITNESS) == 0, ("lock missing witness structure")); continue; } /* * If we are locking Giant and we slept with this * lock, then skip it. */ if ((lock1->li_flags & LI_SLEPT) != 0 && lock == &Giant.mtx_object) continue; /* * If we are locking a sleepable lock and this lock * isn't sleepable and isn't Giant, we want to treat * it as a lock order violation to enfore a general * lock order of sleepable locks before non-sleepable * locks. Thus, we only bother checking the lock * order hierarchy if we pass the initial test. */ if (!((lock->lo_flags & LO_SLEEPABLE) != 0 && ((lock1->li_lock->lo_flags & LO_SLEEPABLE) == 0 && lock1->li_lock != &Giant.mtx_object)) && !isitmydescendant(w, w1)) continue; /* * We have a lock order violation, check to see if it * is allowed or has already been yelled about. */ mtx_unlock_spin(&w_mtx); #ifdef BLESSING if (blessed(w, w1)) goto out; #endif if (lock1->li_lock == &Giant.mtx_object) { if (w1->w_Giant_squawked) goto out; else w1->w_Giant_squawked = 1; } else { if (w1->w_other_squawked) goto out; else w1->w_other_squawked = 1; } /* * Ok, yell about it. */ printf("lock order reversal\n"); /* * Try to locate an earlier lock with * witness w in our list. */ do { lock2 = &lle->ll_children[i]; MPASS(lock2->li_lock != NULL); if (lock2->li_lock->lo_witness == w) break; i--; if (i == 0 && lle->ll_next != NULL) { lle = lle->ll_next; i = lle->ll_count - 1; MPASS(i >= 0 && i < LOCK_NCHILDREN); } } while (i >= 0); if (i < 0) { printf(" 1st %p %s (%s) @ %s:%d\n", lock1->li_lock, lock1->li_lock->lo_name, lock1->li_lock->lo_type, lock1->li_file, lock1->li_line); printf(" 2nd %p %s (%s) @ %s:%d\n", lock, lock->lo_name, lock->lo_type, file, line); } else { printf(" 1st %p %s (%s) @ %s:%d\n", lock2->li_lock, lock2->li_lock->lo_name, lock2->li_lock->lo_type, lock2->li_file, lock2->li_line); printf(" 2nd %p %s (%s) @ %s:%d\n", lock1->li_lock, lock1->li_lock->lo_name, lock1->li_lock->lo_type, lock1->li_file, lock1->li_line); printf(" 3rd %p %s (%s) @ %s:%d\n", lock, lock->lo_name, lock->lo_type, file, line); } #ifdef DDB go_into_ddb = 1; #endif /* DDB */ goto out; } } lock1 = &(*lock_list)->ll_children[(*lock_list)->ll_count - 1]; /* * Don't build a new relationship if we are locking Giant just * after waking up and the previous lock in the list was acquired * prior to blocking. */ if (lock == &Giant.mtx_object && (lock1->li_flags & LI_SLEPT) != 0) mtx_unlock_spin(&w_mtx); else { CTR3(KTR_WITNESS, "%s: adding %s as a child of %s", __func__, lock->lo_type, lock1->li_lock->lo_type); if (!itismychild(lock1->li_lock->lo_witness, w)) mtx_unlock_spin(&w_mtx); } out: #ifdef DDB if (go_into_ddb) { if (witness_trace) backtrace(); if (witness_ddb) Debugger(__func__); } #endif /* DDB */ w->w_file = file; w->w_line = line; lle = *lock_list; if (lle == NULL || lle->ll_count == LOCK_NCHILDREN) { lle = witness_lock_list_get(); if (lle == NULL) return; lle->ll_next = *lock_list; CTR3(KTR_WITNESS, "%s: pid %d added lle %p", __func__, td->td_proc->p_pid, lle); *lock_list = lle; } lock1 = &lle->ll_children[lle->ll_count++]; lock1->li_lock = lock; lock1->li_line = line; lock1->li_file = file; if ((flags & LOP_EXCLUSIVE) != 0) lock1->li_flags = LI_EXCLUSIVE; else lock1->li_flags = 0; CTR4(KTR_WITNESS, "%s: pid %d added %s as lle[%d]", __func__, td->td_proc->p_pid, lock->lo_name, lle->ll_count - 1); } void witness_upgrade(struct lock_object *lock, int flags, const char *file, int line) { struct lock_instance *instance; struct lock_class *class; KASSERT(!witness_cold, ("%s: witness_cold", __func__)); if (lock->lo_witness == NULL || witness_dead || panicstr != NULL) return; class = lock->lo_class; if ((lock->lo_flags & LO_UPGRADABLE) == 0) panic("upgrade of non-upgradable lock (%s) %s @ %s:%d", class->lc_name, lock->lo_name, file, line); if ((flags & LOP_TRYLOCK) == 0) panic("non-try upgrade of lock (%s) %s @ %s:%d", class->lc_name, lock->lo_name, file, line); if ((lock->lo_class->lc_flags & LC_SLEEPLOCK) == 0) panic("upgrade of non-sleep lock (%s) %s @ %s:%d", class->lc_name, lock->lo_name, file, line); instance = find_instance(curthread->td_sleeplocks, lock); if (instance == NULL) panic("upgrade of unlocked lock (%s) %s @ %s:%d", class->lc_name, lock->lo_name, file, line); if ((instance->li_flags & LI_EXCLUSIVE) != 0) panic("upgrade of exclusive lock (%s) %s @ %s:%d", class->lc_name, lock->lo_name, file, line); if ((instance->li_flags & LI_RECURSEMASK) != 0) panic("upgrade of recursed lock (%s) %s r=%d @ %s:%d", class->lc_name, lock->lo_name, instance->li_flags & LI_RECURSEMASK, file, line); instance->li_flags |= LI_EXCLUSIVE; } void witness_downgrade(struct lock_object *lock, int flags, const char *file, int line) { struct lock_instance *instance; struct lock_class *class; KASSERT(!witness_cold, ("%s: witness_cold", __func__)); if (lock->lo_witness == NULL || witness_dead || panicstr != NULL) return; class = lock->lo_class; if ((lock->lo_flags & LO_UPGRADABLE) == 0) panic("downgrade of non-upgradable lock (%s) %s @ %s:%d", class->lc_name, lock->lo_name, file, line); if ((lock->lo_class->lc_flags & LC_SLEEPLOCK) == 0) panic("downgrade of non-sleep lock (%s) %s @ %s:%d", class->lc_name, lock->lo_name, file, line); instance = find_instance(curthread->td_sleeplocks, lock); if (instance == NULL) panic("downgrade of unlocked lock (%s) %s @ %s:%d", class->lc_name, lock->lo_name, file, line); if ((instance->li_flags & LI_EXCLUSIVE) == 0) panic("downgrade of shared lock (%s) %s @ %s:%d", class->lc_name, lock->lo_name, file, line); if ((instance->li_flags & LI_RECURSEMASK) != 0) panic("downgrade of recursed lock (%s) %s r=%d @ %s:%d", class->lc_name, lock->lo_name, instance->li_flags & LI_RECURSEMASK, file, line); instance->li_flags &= ~LI_EXCLUSIVE; } void witness_unlock(struct lock_object *lock, int flags, const char *file, int line) { struct lock_list_entry **lock_list, *lle; struct lock_instance *instance; struct lock_class *class; struct thread *td; register_t s; int i, j; if (witness_cold || witness_dead || lock->lo_witness == NULL || panicstr != NULL) return; td = curthread; class = lock->lo_class; if (class->lc_flags & LC_SLEEPLOCK) lock_list = &td->td_sleeplocks; else lock_list = PCPU_PTR(spinlocks); for (; *lock_list != NULL; lock_list = &(*lock_list)->ll_next) for (i = 0; i < (*lock_list)->ll_count; i++) { instance = &(*lock_list)->ll_children[i]; if (instance->li_lock == lock) { if ((instance->li_flags & LI_EXCLUSIVE) != 0 && (flags & LOP_EXCLUSIVE) == 0) { printf( "shared unlock of (%s) %s @ %s:%d\n", class->lc_name, lock->lo_name, file, line); printf( "while exclusively locked from %s:%d\n", instance->li_file, instance->li_line); panic("excl->ushare"); } if ((instance->li_flags & LI_EXCLUSIVE) == 0 && (flags & LOP_EXCLUSIVE) != 0) { printf( "exclusive unlock of (%s) %s @ %s:%d\n", class->lc_name, lock->lo_name, file, line); printf( "while share locked from %s:%d\n", instance->li_file, instance->li_line); panic("share->uexcl"); } /* If we are recursed, unrecurse. */ if ((instance->li_flags & LI_RECURSEMASK) > 0) { CTR4(KTR_WITNESS, "%s: pid %d unrecursed on %s r=%d", __func__, td->td_proc->p_pid, instance->li_lock->lo_name, instance->li_flags); instance->li_flags--; return; } s = intr_disable(); CTR4(KTR_WITNESS, "%s: pid %d removed %s from lle[%d]", __func__, td->td_proc->p_pid, instance->li_lock->lo_name, (*lock_list)->ll_count - 1); for (j = i; j < (*lock_list)->ll_count - 1; j++) (*lock_list)->ll_children[j] = (*lock_list)->ll_children[j + 1]; (*lock_list)->ll_count--; intr_restore(s); if ((*lock_list)->ll_count == 0) { lle = *lock_list; *lock_list = lle->ll_next; CTR3(KTR_WITNESS, "%s: pid %d removed lle %p", __func__, td->td_proc->p_pid, lle); witness_lock_list_free(lle); } return; } } panic("lock (%s) %s not locked @ %s:%d", class->lc_name, lock->lo_name, file, line); } /* * Warn if any held locks are not sleepable. Note that Giant and the lock * passed in are both special cases since they are both released during the * sleep process and aren't actually held while the thread is asleep. */ int witness_sleep(int check_only, struct lock_object *lock, const char *file, int line) { struct lock_list_entry **lock_list, *lle; struct lock_instance *lock1; struct thread *td; int i, n; if (witness_cold || witness_dead || panicstr != NULL) return (0); n = 0; td = curthread; lock_list = &td->td_sleeplocks; again: for (lle = *lock_list; lle != NULL; lle = lle->ll_next) for (i = lle->ll_count - 1; i >= 0; i--) { lock1 = &lle->ll_children[i]; if (lock1->li_lock == lock || lock1->li_lock == &Giant.mtx_object) continue; if ((lock1->li_lock->lo_flags & LO_SLEEPABLE) != 0) { if (check_only == 0) { CTR3(KTR_WITNESS, "pid %d: sleeping with lock (%s) %s held", td->td_proc->p_pid, lock1->li_lock->lo_class->lc_name, lock1->li_lock->lo_name); lock1->li_flags |= LI_SLEPT; } continue; } n++; printf("%s:%d: %s with \"%s\" locked from %s:%d\n", file, line, check_only ? "could sleep" : "sleeping", lock1->li_lock->lo_name, lock1->li_file, lock1->li_line); } if (lock_list == &td->td_sleeplocks && PCPU_GET(spinlocks) != NULL) { /* * Since we already hold a spinlock preemption is * already blocked. */ lock_list = PCPU_PTR(spinlocks); goto again; } #ifdef DDB if (witness_ddb && n) Debugger(__func__); #endif /* DDB */ return (n); } const char * witness_file(struct lock_object *lock) { struct witness *w; if (witness_cold || witness_dead || lock->lo_witness == NULL) return ("?"); w = lock->lo_witness; return (w->w_file); } int witness_line(struct lock_object *lock) { struct witness *w; if (witness_cold || witness_dead || lock->lo_witness == NULL) return (0); w = lock->lo_witness; return (w->w_line); } static struct witness * enroll(const char *description, struct lock_class *lock_class) { struct witness *w; if (!witness_watch || witness_dead || panicstr != NULL) return (NULL); if ((lock_class->lc_flags & LC_SPINLOCK) && witness_skipspin) return (NULL); mtx_lock_spin(&w_mtx); STAILQ_FOREACH(w, &w_all, w_list) { if (w->w_name == description || (w->w_refcount > 0 && strcmp(description, w->w_name) == 0)) { w->w_refcount++; mtx_unlock_spin(&w_mtx); if (lock_class != w->w_class) panic( "lock (%s) %s does not match earlier (%s) lock", description, lock_class->lc_name, w->w_class->lc_name); return (w); } } /* * This isn't quite right, as witness_cold is still 0 while we * enroll all the locks initialized before witness_initialize(). */ if ((lock_class->lc_flags & LC_SPINLOCK) && !witness_cold) { mtx_unlock_spin(&w_mtx); panic("spin lock %s not in order list", description); } if ((w = witness_get()) == NULL) return (NULL); w->w_name = description; w->w_class = lock_class; w->w_refcount = 1; STAILQ_INSERT_HEAD(&w_all, w, w_list); if (lock_class->lc_flags & LC_SPINLOCK) STAILQ_INSERT_HEAD(&w_spin, w, w_typelist); else if (lock_class->lc_flags & LC_SLEEPLOCK) STAILQ_INSERT_HEAD(&w_sleep, w, w_typelist); else { mtx_unlock_spin(&w_mtx); panic("lock class %s is not sleep or spin", lock_class->lc_name); } mtx_unlock_spin(&w_mtx); return (w); } static int itismychild(struct witness *parent, struct witness *child) { static int recursed; struct witness_child_list_entry **wcl; struct witness_list *list; MPASS(child != NULL && parent != NULL); if ((parent->w_class->lc_flags & (LC_SLEEPLOCK | LC_SPINLOCK)) != (child->w_class->lc_flags & (LC_SLEEPLOCK | LC_SPINLOCK))) panic( "%s: parent (%s) and child (%s) are not the same lock type", __func__, parent->w_class->lc_name, child->w_class->lc_name); /* * Insert "child" after "parent" */ wcl = &parent->w_children; while (*wcl != NULL && (*wcl)->wcl_count == WITNESS_NCHILDREN) wcl = &(*wcl)->wcl_next; if (*wcl == NULL) { *wcl = witness_child_get(); if (*wcl == NULL) return (1); } (*wcl)->wcl_children[(*wcl)->wcl_count++] = child; /* * Now prune whole tree. We look for cases where a lock is now * both a descendant and a direct child of a given lock. In that * case, we want to remove the direct child link from the tree. */ if (recursed) return (0); recursed = 1; if (parent->w_class->lc_flags & LC_SLEEPLOCK) list = &w_sleep; else list = &w_spin; STAILQ_FOREACH(child, list, w_typelist) { STAILQ_FOREACH(parent, list, w_typelist) { if (!isitmychild(parent, child)) continue; removechild(parent, child); if (isitmydescendant(parent, child)) continue; itismychild(parent, child); } } recursed = 0; witness_levelall(); return (0); } static void removechild(struct witness *parent, struct witness *child) { struct witness_child_list_entry **wcl, *wcl1; int i; for (wcl = &parent->w_children; *wcl != NULL; wcl = &(*wcl)->wcl_next) for (i = 0; i < (*wcl)->wcl_count; i++) if ((*wcl)->wcl_children[i] == child) goto found; return; found: (*wcl)->wcl_count--; if ((*wcl)->wcl_count > i) (*wcl)->wcl_children[i] = (*wcl)->wcl_children[(*wcl)->wcl_count]; MPASS((*wcl)->wcl_children[i] != NULL); if ((*wcl)->wcl_count != 0) return; wcl1 = *wcl; *wcl = wcl1->wcl_next; witness_child_free(wcl1); } static int isitmychild(struct witness *parent, struct witness *child) { struct witness_child_list_entry *wcl; int i; for (wcl = parent->w_children; wcl != NULL; wcl = wcl->wcl_next) { for (i = 0; i < wcl->wcl_count; i++) { if (wcl->wcl_children[i] == child) return (1); } } return (0); } static int isitmydescendant(struct witness *parent, struct witness *child) { struct witness_child_list_entry *wcl; int i, j; if (isitmychild(parent, child)) return (1); j = 0; for (wcl = parent->w_children; wcl != NULL; wcl = wcl->wcl_next) { MPASS(j < 1000); for (i = 0; i < wcl->wcl_count; i++) { if (isitmydescendant(wcl->wcl_children[i], child)) return (1); } j++; } return (0); } static void witness_levelall (void) { struct witness_list *list; struct witness *w, *w1; /* * First clear all levels. */ STAILQ_FOREACH(w, &w_all, w_list) { w->w_level = 0; } /* * Look for locks with no parent and level all their descendants. */ STAILQ_FOREACH(w, &w_all, w_list) { /* * This is just an optimization, technically we could get * away just walking the all list each time. */ if (w->w_class->lc_flags & LC_SLEEPLOCK) list = &w_sleep; else list = &w_spin; STAILQ_FOREACH(w1, list, w_typelist) { if (isitmychild(w1, w)) goto skip; } witness_leveldescendents(w, 0); skip: ; /* silence GCC 3.x */ } } static void witness_leveldescendents(struct witness *parent, int level) { struct witness_child_list_entry *wcl; int i; if (parent->w_level < level) parent->w_level = level; level++; for (wcl = parent->w_children; wcl != NULL; wcl = wcl->wcl_next) for (i = 0; i < wcl->wcl_count; i++) witness_leveldescendents(wcl->wcl_children[i], level); } static void witness_displaydescendants(void(*prnt)(const char *fmt, ...), struct witness *parent) { struct witness_child_list_entry *wcl; int i, level; level = parent->w_level; prnt("%-2d", level); for (i = 0; i < level; i++) prnt(" "); if (parent->w_refcount > 0) { prnt("%s", parent->w_name); if (parent->w_file != NULL) prnt(" -- last acquired @ %s:%d\n", parent->w_file, parent->w_line); } else prnt("(dead)\n"); for (wcl = parent->w_children; wcl != NULL; wcl = wcl->wcl_next) for (i = 0; i < wcl->wcl_count; i++) witness_displaydescendants(prnt, wcl->wcl_children[i]); } #ifdef BLESSING static int blessed(struct witness *w1, struct witness *w2) { int i; struct witness_blessed *b; for (i = 0; i < blessed_count; i++) { b = &blessed_list[i]; if (strcmp(w1->w_name, b->b_lock1) == 0) { if (strcmp(w2->w_name, b->b_lock2) == 0) return (1); continue; } if (strcmp(w1->w_name, b->b_lock2) == 0) if (strcmp(w2->w_name, b->b_lock1) == 0) return (1); } return (0); } #endif static struct witness * witness_get(void) { struct witness *w; if (witness_dead) { mtx_unlock_spin(&w_mtx); return (NULL); } if (STAILQ_EMPTY(&w_free)) { witness_dead = 1; mtx_unlock_spin(&w_mtx); printf("%s: witness exhausted\n", __func__); return (NULL); } w = STAILQ_FIRST(&w_free); STAILQ_REMOVE_HEAD(&w_free, w_list); bzero(w, sizeof(*w)); return (w); } static void witness_free(struct witness *w) { STAILQ_INSERT_HEAD(&w_free, w, w_list); } static struct witness_child_list_entry * witness_child_get(void) { struct witness_child_list_entry *wcl; if (witness_dead) { mtx_unlock_spin(&w_mtx); return (NULL); } wcl = w_child_free; if (wcl == NULL) { witness_dead = 1; mtx_unlock_spin(&w_mtx); printf("%s: witness exhausted\n", __func__); return (NULL); } w_child_free = wcl->wcl_next; bzero(wcl, sizeof(*wcl)); return (wcl); } static void witness_child_free(struct witness_child_list_entry *wcl) { wcl->wcl_next = w_child_free; w_child_free = wcl; } static struct lock_list_entry * witness_lock_list_get(void) { struct lock_list_entry *lle; if (witness_dead) return (NULL); mtx_lock_spin(&w_mtx); lle = w_lock_list_free; if (lle == NULL) { witness_dead = 1; mtx_unlock_spin(&w_mtx); printf("%s: witness exhausted\n", __func__); return (NULL); } w_lock_list_free = lle->ll_next; mtx_unlock_spin(&w_mtx); bzero(lle, sizeof(*lle)); return (lle); } static void witness_lock_list_free(struct lock_list_entry *lle) { mtx_lock_spin(&w_mtx); lle->ll_next = w_lock_list_free; w_lock_list_free = lle; mtx_unlock_spin(&w_mtx); } static struct lock_instance * find_instance(struct lock_list_entry *lock_list, struct lock_object *lock) { struct lock_list_entry *lle; struct lock_instance *instance; int i; for (lle = lock_list; lle != NULL; lle = lle->ll_next) for (i = lle->ll_count - 1; i >= 0; i--) { instance = &lle->ll_children[i]; if (instance->li_lock == lock) return (instance); } return (NULL); } int witness_list_locks(struct lock_list_entry **lock_list) { struct lock_list_entry *lle; struct lock_instance *instance; struct lock_object *lock; int i, nheld; nheld = 0; for (lle = *lock_list; lle != NULL; lle = lle->ll_next) for (i = lle->ll_count - 1; i >= 0; i--) { instance = &lle->ll_children[i]; lock = instance->li_lock; printf("%s %s %s", (instance->li_flags & LI_EXCLUSIVE) != 0 ? "exclusive" : "shared", lock->lo_class->lc_name, lock->lo_name); if (lock->lo_type != lock->lo_name) printf(" (%s)", lock->lo_type); printf(" r = %d (%p) locked @ %s:%d\n", instance->li_flags & LI_RECURSEMASK, lock, instance->li_file, instance->li_line); nheld++; } return (nheld); } /* * Calling this on td != curthread is bad unless we are in ddb. */ int witness_list(struct thread *td) { int nheld; KASSERT(!witness_cold, ("%s: witness_cold", __func__)); #ifdef DDB KASSERT(td == curthread || db_active, ("%s: td != curthread and we aren't in the debugger", __func__)); if (!db_active && witness_dead) return (0); #else KASSERT(td == curthread, ("%s: p != curthread", __func__)); if (witness_dead) return (0); #endif nheld = witness_list_locks(&td->td_sleeplocks); /* * We only handle spinlocks if td == curthread. This is somewhat broken * if td is currently executing on some other CPU and holds spin locks * as we won't display those locks. If we had a MI way of getting * the per-cpu data for a given cpu then we could use * td->td_kse->ke_oncpu to get the list of spinlocks for this thread * and "fix" this. * * That still wouldn't really fix this unless we locked sched_lock * or stopped the other CPU to make sure it wasn't changing the list * out from under us. It is probably best to just not try to handle * threads on other CPU's for now. */ if (td == curthread && PCPU_GET(spinlocks) != NULL) nheld += witness_list_locks(PCPU_PTR(spinlocks)); return (nheld); } void witness_save(struct lock_object *lock, const char **filep, int *linep) { struct lock_instance *instance; KASSERT(!witness_cold, ("%s: witness_cold", __func__)); if (lock->lo_witness == NULL || witness_dead || panicstr != NULL) return; if ((lock->lo_class->lc_flags & LC_SLEEPLOCK) == 0) panic("%s: lock (%s) %s is not a sleep lock", __func__, lock->lo_class->lc_name, lock->lo_name); instance = find_instance(curthread->td_sleeplocks, lock); if (instance == NULL) panic("%s: lock (%s) %s not locked", __func__, lock->lo_class->lc_name, lock->lo_name); *filep = instance->li_file; *linep = instance->li_line; } void witness_restore(struct lock_object *lock, const char *file, int line) { struct lock_instance *instance; KASSERT(!witness_cold, ("%s: witness_cold", __func__)); if (lock->lo_witness == NULL || witness_dead || panicstr != NULL) return; if ((lock->lo_class->lc_flags & LC_SLEEPLOCK) == 0) panic("%s: lock (%s) %s is not a sleep lock", __func__, lock->lo_class->lc_name, lock->lo_name); instance = find_instance(curthread->td_sleeplocks, lock); if (instance == NULL) panic("%s: lock (%s) %s not locked", __func__, lock->lo_class->lc_name, lock->lo_name); lock->lo_witness->w_file = file; lock->lo_witness->w_line = line; instance->li_file = file; instance->li_line = line; } void witness_assert(struct lock_object *lock, int flags, const char *file, int line) { #ifdef INVARIANT_SUPPORT struct lock_instance *instance; if (lock->lo_witness == NULL || witness_dead || panicstr != NULL) return; if ((lock->lo_class->lc_flags & LC_SLEEPLOCK) != 0) instance = find_instance(curthread->td_sleeplocks, lock); else if ((lock->lo_class->lc_flags & LC_SPINLOCK) != 0) instance = find_instance(PCPU_GET(spinlocks), lock); else { panic("Lock (%s) %s is not sleep or spin!", lock->lo_class->lc_name, lock->lo_name); return; } switch (flags) { case LA_UNLOCKED: if (instance != NULL) panic("Lock (%s) %s locked @ %s:%d.", lock->lo_class->lc_name, lock->lo_name, file, line); break; case LA_LOCKED: case LA_LOCKED | LA_RECURSED: case LA_LOCKED | LA_NOTRECURSED: case LA_SLOCKED: case LA_SLOCKED | LA_RECURSED: case LA_SLOCKED | LA_NOTRECURSED: case LA_XLOCKED: case LA_XLOCKED | LA_RECURSED: case LA_XLOCKED | LA_NOTRECURSED: if (instance == NULL) { panic("Lock (%s) %s not locked @ %s:%d.", lock->lo_class->lc_name, lock->lo_name, file, line); break; } if ((flags & LA_XLOCKED) != 0 && (instance->li_flags & LI_EXCLUSIVE) == 0) panic("Lock (%s) %s not exclusively locked @ %s:%d.", lock->lo_class->lc_name, lock->lo_name, file, line); if ((flags & LA_SLOCKED) != 0 && (instance->li_flags & LI_EXCLUSIVE) != 0) panic("Lock (%s) %s exclusively locked @ %s:%d.", lock->lo_class->lc_name, lock->lo_name, file, line); if ((flags & LA_RECURSED) != 0 && (instance->li_flags & LI_RECURSEMASK) == 0) panic("Lock (%s) %s not recursed @ %s:%d.", lock->lo_class->lc_name, lock->lo_name, file, line); if ((flags & LA_NOTRECURSED) != 0 && (instance->li_flags & LI_RECURSEMASK) != 0) panic("Lock (%s) %s recursed @ %s:%d.", lock->lo_class->lc_name, lock->lo_name, file, line); break; default: panic("Invalid lock assertion at %s:%d.", file, line); } #endif /* INVARIANT_SUPPORT */ } #ifdef DDB DB_SHOW_COMMAND(locks, db_witness_list) { struct thread *td; pid_t pid; struct proc *p; if (have_addr) { pid = (addr % 16) + ((addr >> 4) % 16) * 10 + ((addr >> 8) % 16) * 100 + ((addr >> 12) % 16) * 1000 + ((addr >> 16) % 16) * 10000; /* sx_slock(&allproc_lock); */ FOREACH_PROC_IN_SYSTEM(p) { if (p->p_pid == pid) break; } /* sx_sunlock(&allproc_lock); */ if (p == NULL) { db_printf("pid %d not found\n", pid); return; } FOREACH_THREAD_IN_PROC(p, td) { witness_list(td); } } else { td = curthread; witness_list(td); } } DB_SHOW_COMMAND(witness, db_witness_display) { witness_display(db_printf); } #endif