Index: head/usr.sbin/pciconf/cap.c =================================================================== --- head/usr.sbin/pciconf/cap.c (revision 353461) +++ head/usr.sbin/pciconf/cap.c (revision 353462) @@ -1,1083 +1,1086 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2007 Yahoo!, Inc. * All rights reserved. * Written by: John Baldwin * * 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. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. */ #ifndef lint static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include "pciconf.h" static void list_ecaps(int fd, struct pci_conf *p); static void cap_power(int fd, struct pci_conf *p, uint8_t ptr) { uint16_t cap, status; cap = read_config(fd, &p->pc_sel, ptr + PCIR_POWER_CAP, 2); status = read_config(fd, &p->pc_sel, ptr + PCIR_POWER_STATUS, 2); printf("powerspec %d supports D0%s%s D3 current D%d", cap & PCIM_PCAP_SPEC, cap & PCIM_PCAP_D1SUPP ? " D1" : "", cap & PCIM_PCAP_D2SUPP ? " D2" : "", status & PCIM_PSTAT_DMASK); } static void cap_agp(int fd, struct pci_conf *p, uint8_t ptr) { uint32_t status, command; status = read_config(fd, &p->pc_sel, ptr + AGP_STATUS, 4); command = read_config(fd, &p->pc_sel, ptr + AGP_CAPID, 4); printf("AGP "); if (AGP_MODE_GET_MODE_3(status)) { printf("v3 "); if (AGP_MODE_GET_RATE(status) & AGP_MODE_V3_RATE_8x) printf("8x "); if (AGP_MODE_GET_RATE(status) & AGP_MODE_V3_RATE_4x) printf("4x "); } else { if (AGP_MODE_GET_RATE(status) & AGP_MODE_V2_RATE_4x) printf("4x "); if (AGP_MODE_GET_RATE(status) & AGP_MODE_V2_RATE_2x) printf("2x "); if (AGP_MODE_GET_RATE(status) & AGP_MODE_V2_RATE_1x) printf("1x "); } if (AGP_MODE_GET_SBA(status)) printf("SBA "); if (AGP_MODE_GET_AGP(command)) { printf("enabled at "); if (AGP_MODE_GET_MODE_3(command)) { printf("v3 "); switch (AGP_MODE_GET_RATE(command)) { case AGP_MODE_V3_RATE_8x: printf("8x "); break; case AGP_MODE_V3_RATE_4x: printf("4x "); break; } } else switch (AGP_MODE_GET_RATE(command)) { case AGP_MODE_V2_RATE_4x: printf("4x "); break; case AGP_MODE_V2_RATE_2x: printf("2x "); break; case AGP_MODE_V2_RATE_1x: printf("1x "); break; } if (AGP_MODE_GET_SBA(command)) printf("SBA "); } else printf("disabled"); } static void cap_vpd(int fd __unused, struct pci_conf *p __unused, uint8_t ptr __unused) { printf("VPD"); } static void cap_msi(int fd, struct pci_conf *p, uint8_t ptr) { uint16_t ctrl; int msgnum; ctrl = read_config(fd, &p->pc_sel, ptr + PCIR_MSI_CTRL, 2); msgnum = 1 << ((ctrl & PCIM_MSICTRL_MMC_MASK) >> 1); printf("MSI supports %d message%s%s%s ", msgnum, (msgnum == 1) ? "" : "s", (ctrl & PCIM_MSICTRL_64BIT) ? ", 64 bit" : "", (ctrl & PCIM_MSICTRL_VECTOR) ? ", vector masks" : ""); if (ctrl & PCIM_MSICTRL_MSI_ENABLE) { msgnum = 1 << ((ctrl & PCIM_MSICTRL_MME_MASK) >> 4); printf("enabled with %d message%s", msgnum, (msgnum == 1) ? "" : "s"); } } static void cap_pcix(int fd, struct pci_conf *p, uint8_t ptr) { uint32_t status; int comma, max_splits, max_burst_read; status = read_config(fd, &p->pc_sel, ptr + PCIXR_STATUS, 4); printf("PCI-X "); if (status & PCIXM_STATUS_64BIT) printf("64-bit "); if ((p->pc_hdr & PCIM_HDRTYPE) == 1) printf("bridge "); if ((p->pc_hdr & PCIM_HDRTYPE) != 1 || (status & (PCIXM_STATUS_133CAP | PCIXM_STATUS_266CAP | PCIXM_STATUS_533CAP)) != 0) printf("supports"); comma = 0; if (status & PCIXM_STATUS_133CAP) { printf(" 133MHz"); comma = 1; } if (status & PCIXM_STATUS_266CAP) { printf("%s 266MHz", comma ? "," : ""); comma = 1; } if (status & PCIXM_STATUS_533CAP) { printf("%s 533MHz", comma ? "," : ""); comma = 1; } if ((p->pc_hdr & PCIM_HDRTYPE) == 1) return; max_burst_read = 0; switch (status & PCIXM_STATUS_MAX_READ) { case PCIXM_STATUS_MAX_READ_512: max_burst_read = 512; break; case PCIXM_STATUS_MAX_READ_1024: max_burst_read = 1024; break; case PCIXM_STATUS_MAX_READ_2048: max_burst_read = 2048; break; case PCIXM_STATUS_MAX_READ_4096: max_burst_read = 4096; break; } max_splits = 0; switch (status & PCIXM_STATUS_MAX_SPLITS) { case PCIXM_STATUS_MAX_SPLITS_1: max_splits = 1; break; case PCIXM_STATUS_MAX_SPLITS_2: max_splits = 2; break; case PCIXM_STATUS_MAX_SPLITS_3: max_splits = 3; break; case PCIXM_STATUS_MAX_SPLITS_4: max_splits = 4; break; case PCIXM_STATUS_MAX_SPLITS_8: max_splits = 8; break; case PCIXM_STATUS_MAX_SPLITS_12: max_splits = 12; break; case PCIXM_STATUS_MAX_SPLITS_16: max_splits = 16; break; case PCIXM_STATUS_MAX_SPLITS_32: max_splits = 32; break; } printf("%s %d burst read, %d split transaction%s", comma ? "," : "", max_burst_read, max_splits, max_splits == 1 ? "" : "s"); } static void cap_ht(int fd, struct pci_conf *p, uint8_t ptr) { uint32_t reg; uint16_t command; command = read_config(fd, &p->pc_sel, ptr + PCIR_HT_COMMAND, 2); printf("HT "); if ((command & 0xe000) == PCIM_HTCAP_SLAVE) printf("slave"); else if ((command & 0xe000) == PCIM_HTCAP_HOST) printf("host"); else switch (command & PCIM_HTCMD_CAP_MASK) { case PCIM_HTCAP_SWITCH: printf("switch"); break; case PCIM_HTCAP_INTERRUPT: printf("interrupt"); break; case PCIM_HTCAP_REVISION_ID: printf("revision ID"); break; case PCIM_HTCAP_UNITID_CLUMPING: printf("unit ID clumping"); break; case PCIM_HTCAP_EXT_CONFIG_SPACE: printf("extended config space"); break; case PCIM_HTCAP_ADDRESS_MAPPING: printf("address mapping"); break; case PCIM_HTCAP_MSI_MAPPING: printf("MSI %saddress window %s at 0x", command & PCIM_HTCMD_MSI_FIXED ? "fixed " : "", command & PCIM_HTCMD_MSI_ENABLE ? "enabled" : "disabled"); if (command & PCIM_HTCMD_MSI_FIXED) printf("fee00000"); else { reg = read_config(fd, &p->pc_sel, ptr + PCIR_HTMSI_ADDRESS_HI, 4); if (reg != 0) printf("%08x", reg); reg = read_config(fd, &p->pc_sel, ptr + PCIR_HTMSI_ADDRESS_LO, 4); printf("%08x", reg); } break; case PCIM_HTCAP_DIRECT_ROUTE: printf("direct route"); break; case PCIM_HTCAP_VCSET: printf("VC set"); break; case PCIM_HTCAP_RETRY_MODE: printf("retry mode"); break; case PCIM_HTCAP_X86_ENCODING: printf("X86 encoding"); break; case PCIM_HTCAP_GEN3: printf("Gen3"); break; case PCIM_HTCAP_FLE: printf("function-level extension"); break; case PCIM_HTCAP_PM: printf("power management"); break; case PCIM_HTCAP_HIGH_NODE_COUNT: printf("high node count"); break; default: printf("unknown %02x", command); break; } } static void cap_vendor(int fd, struct pci_conf *p, uint8_t ptr) { uint8_t length; length = read_config(fd, &p->pc_sel, ptr + PCIR_VENDOR_LENGTH, 1); printf("vendor (length %d)", length); if (p->pc_vendor == 0x8086) { /* Intel */ uint8_t version; version = read_config(fd, &p->pc_sel, ptr + PCIR_VENDOR_DATA, 1); printf(" Intel cap %d version %d", version >> 4, version & 0xf); if (version >> 4 == 1 && length == 12) { /* Feature Detection */ uint32_t fvec; int comma; comma = 0; fvec = read_config(fd, &p->pc_sel, ptr + PCIR_VENDOR_DATA + 5, 4); printf("\n\t\t features:"); if (fvec & (1 << 0)) { printf(" AMT"); comma = 1; } fvec = read_config(fd, &p->pc_sel, ptr + PCIR_VENDOR_DATA + 1, 4); if (fvec & (1 << 21)) { printf("%s Quick Resume", comma ? "," : ""); comma = 1; } if (fvec & (1 << 18)) { printf("%s SATA RAID-5", comma ? "," : ""); comma = 1; } if (fvec & (1 << 9)) { printf("%s Mobile", comma ? "," : ""); comma = 1; } if (fvec & (1 << 7)) { printf("%s 6 PCI-e x1 slots", comma ? "," : ""); comma = 1; } else { printf("%s 4 PCI-e x1 slots", comma ? "," : ""); comma = 1; } if (fvec & (1 << 5)) { printf("%s SATA RAID-0/1/10", comma ? "," : ""); comma = 1; } if (fvec & (1 << 3)) printf(", SATA AHCI"); } } } static void cap_debug(int fd, struct pci_conf *p, uint8_t ptr) { uint16_t debug_port; debug_port = read_config(fd, &p->pc_sel, ptr + PCIR_DEBUG_PORT, 2); printf("EHCI Debug Port at offset 0x%x in map 0x%x", debug_port & PCIM_DEBUG_PORT_OFFSET, PCIR_BAR(debug_port >> 13)); } static void cap_subvendor(int fd, struct pci_conf *p, uint8_t ptr) { uint32_t id; + uint16_t ssid, ssvid; id = read_config(fd, &p->pc_sel, ptr + PCIR_SUBVENDCAP_ID, 4); - printf("PCI Bridge card=0x%08x", id); + ssid = id >> 16; + ssvid = id & 0xffff; + printf("PCI Bridge subvendor=0x%04x subdevice=0x%04x", ssvid, ssid); } #define MAX_PAYLOAD(field) (128 << (field)) static const char * link_speed_string(uint8_t speed) { switch (speed) { case 1: return ("2.5"); case 2: return ("5.0"); case 3: return ("8.0"); case 4: return ("16.0"); default: return ("undef"); } } static const char * aspm_string(uint8_t aspm) { switch (aspm) { case 1: return ("L0s"); case 2: return ("L1"); case 3: return ("L0s/L1"); default: return ("disabled"); } } static int slot_power(uint32_t cap) { int mwatts; mwatts = (cap & PCIEM_SLOT_CAP_SPLV) >> 7; switch (cap & PCIEM_SLOT_CAP_SPLS) { case 0x0: mwatts *= 1000; break; case 0x1: mwatts *= 100; break; case 0x2: mwatts *= 10; break; default: break; } return (mwatts); } static void cap_express(int fd, struct pci_conf *p, uint8_t ptr) { uint32_t cap; uint16_t ctl, flags, sta; unsigned int version; flags = read_config(fd, &p->pc_sel, ptr + PCIER_FLAGS, 2); version = flags & PCIEM_FLAGS_VERSION; printf("PCI-Express %u ", version); switch (flags & PCIEM_FLAGS_TYPE) { case PCIEM_TYPE_ENDPOINT: printf("endpoint"); break; case PCIEM_TYPE_LEGACY_ENDPOINT: printf("legacy endpoint"); break; case PCIEM_TYPE_ROOT_PORT: printf("root port"); break; case PCIEM_TYPE_UPSTREAM_PORT: printf("upstream port"); break; case PCIEM_TYPE_DOWNSTREAM_PORT: printf("downstream port"); break; case PCIEM_TYPE_PCI_BRIDGE: printf("PCI bridge"); break; case PCIEM_TYPE_PCIE_BRIDGE: printf("PCI to PCIe bridge"); break; case PCIEM_TYPE_ROOT_INT_EP: printf("root endpoint"); break; case PCIEM_TYPE_ROOT_EC: printf("event collector"); break; default: printf("type %d", (flags & PCIEM_FLAGS_TYPE) >> 4); break; } if (flags & PCIEM_FLAGS_IRQ) printf(" MSI %d", (flags & PCIEM_FLAGS_IRQ) >> 9); cap = read_config(fd, &p->pc_sel, ptr + PCIER_DEVICE_CAP, 4); ctl = read_config(fd, &p->pc_sel, ptr + PCIER_DEVICE_CTL, 2); printf(" max data %d(%d)", MAX_PAYLOAD((ctl & PCIEM_CTL_MAX_PAYLOAD) >> 5), MAX_PAYLOAD(cap & PCIEM_CAP_MAX_PAYLOAD)); if ((cap & PCIEM_CAP_FLR) != 0) printf(" FLR"); if (ctl & PCIEM_CTL_RELAXED_ORD_ENABLE) printf(" RO"); if (ctl & PCIEM_CTL_NOSNOOP_ENABLE) printf(" NS"); if (version >= 2) { cap = read_config(fd, &p->pc_sel, ptr + PCIER_DEVICE_CAP2, 4); if ((cap & PCIEM_CAP2_ARI) != 0) { ctl = read_config(fd, &p->pc_sel, ptr + PCIER_DEVICE_CTL2, 4); printf(" ARI %s", (ctl & PCIEM_CTL2_ARI) ? "enabled" : "disabled"); } } cap = read_config(fd, &p->pc_sel, ptr + PCIER_LINK_CAP, 4); sta = read_config(fd, &p->pc_sel, ptr + PCIER_LINK_STA, 2); if (cap == 0 && sta == 0) return; printf("\n "); printf(" link x%d(x%d)", (sta & PCIEM_LINK_STA_WIDTH) >> 4, (cap & PCIEM_LINK_CAP_MAX_WIDTH) >> 4); if ((cap & PCIEM_LINK_CAP_MAX_WIDTH) != 0) { printf(" speed %s(%s)", (sta & PCIEM_LINK_STA_WIDTH) == 0 ? "0.0" : link_speed_string(sta & PCIEM_LINK_STA_SPEED), link_speed_string(cap & PCIEM_LINK_CAP_MAX_SPEED)); } if ((cap & PCIEM_LINK_CAP_ASPM) != 0) { ctl = read_config(fd, &p->pc_sel, ptr + PCIER_LINK_CTL, 2); printf(" ASPM %s(%s)", aspm_string(ctl & PCIEM_LINK_CTL_ASPMC), aspm_string((cap & PCIEM_LINK_CAP_ASPM) >> 10)); } if ((cap & PCIEM_LINK_CAP_CLOCK_PM) != 0) { ctl = read_config(fd, &p->pc_sel, ptr + PCIER_LINK_CTL, 2); printf(" ClockPM %s", (ctl & PCIEM_LINK_CTL_ECPM) ? "enabled" : "disabled"); } if (!(flags & PCIEM_FLAGS_SLOT)) return; cap = read_config(fd, &p->pc_sel, ptr + PCIER_SLOT_CAP, 4); sta = read_config(fd, &p->pc_sel, ptr + PCIER_SLOT_STA, 2); ctl = read_config(fd, &p->pc_sel, ptr + PCIER_SLOT_CTL, 2); printf("\n "); printf(" slot %d", (cap & PCIEM_SLOT_CAP_PSN) >> 19); printf(" power limit %d mW", slot_power(cap)); if (cap & PCIEM_SLOT_CAP_HPC) printf(" HotPlug(%s)", sta & PCIEM_SLOT_STA_PDS ? "present" : "empty"); if (cap & PCIEM_SLOT_CAP_HPS) printf(" surprise"); if (cap & PCIEM_SLOT_CAP_APB) printf(" Attn Button"); if (cap & PCIEM_SLOT_CAP_PCP) printf(" PC(%s)", ctl & PCIEM_SLOT_CTL_PCC ? "off" : "on"); if (cap & PCIEM_SLOT_CAP_MRLSP) printf(" MRL(%s)", sta & PCIEM_SLOT_STA_MRLSS ? "open" : "closed"); if (cap & PCIEM_SLOT_CAP_EIP) printf(" EI(%s)", sta & PCIEM_SLOT_STA_EIS ? "engaged" : "disengaged"); } static void cap_msix(int fd, struct pci_conf *p, uint8_t ptr) { uint32_t pba_offset, table_offset, val; int msgnum, pba_bar, table_bar; uint16_t ctrl; ctrl = read_config(fd, &p->pc_sel, ptr + PCIR_MSIX_CTRL, 2); msgnum = (ctrl & PCIM_MSIXCTRL_TABLE_SIZE) + 1; val = read_config(fd, &p->pc_sel, ptr + PCIR_MSIX_TABLE, 4); table_bar = PCIR_BAR(val & PCIM_MSIX_BIR_MASK); table_offset = val & ~PCIM_MSIX_BIR_MASK; val = read_config(fd, &p->pc_sel, ptr + PCIR_MSIX_PBA, 4); pba_bar = PCIR_BAR(val & PCIM_MSIX_BIR_MASK); pba_offset = val & ~PCIM_MSIX_BIR_MASK; printf("MSI-X supports %d message%s%s\n", msgnum, (msgnum == 1) ? "" : "s", (ctrl & PCIM_MSIXCTRL_MSIX_ENABLE) ? ", enabled" : ""); printf(" "); printf("Table in map 0x%x[0x%x], PBA in map 0x%x[0x%x]", table_bar, table_offset, pba_bar, pba_offset); } static void cap_sata(int fd __unused, struct pci_conf *p __unused, uint8_t ptr __unused) { printf("SATA Index-Data Pair"); } static void cap_pciaf(int fd, struct pci_conf *p, uint8_t ptr) { uint8_t cap; cap = read_config(fd, &p->pc_sel, ptr + PCIR_PCIAF_CAP, 1); printf("PCI Advanced Features:%s%s", cap & PCIM_PCIAFCAP_FLR ? " FLR" : "", cap & PCIM_PCIAFCAP_TP ? " TP" : ""); } static const char * ea_bei_to_name(int bei) { static const char *barstr[] = { "BAR0", "BAR1", "BAR2", "BAR3", "BAR4", "BAR5" }; static const char *vfbarstr[] = { "VFBAR0", "VFBAR1", "VFBAR2", "VFBAR3", "VFBAR4", "VFBAR5" }; if ((bei >= PCIM_EA_BEI_BAR_0) && (bei <= PCIM_EA_BEI_BAR_5)) return (barstr[bei - PCIM_EA_BEI_BAR_0]); if ((bei >= PCIM_EA_BEI_VF_BAR_0) && (bei <= PCIM_EA_BEI_VF_BAR_5)) return (vfbarstr[bei - PCIM_EA_BEI_VF_BAR_0]); switch (bei) { case PCIM_EA_BEI_BRIDGE: return "BRIDGE"; case PCIM_EA_BEI_ENI: return "ENI"; case PCIM_EA_BEI_ROM: return "ROM"; case PCIM_EA_BEI_RESERVED: default: return "RSVD"; } } static const char * ea_prop_to_name(uint8_t prop) { switch (prop) { case PCIM_EA_P_MEM: return "Non-Prefetchable Memory"; case PCIM_EA_P_MEM_PREFETCH: return "Prefetchable Memory"; case PCIM_EA_P_IO: return "I/O Space"; case PCIM_EA_P_VF_MEM_PREFETCH: return "VF Prefetchable Memory"; case PCIM_EA_P_VF_MEM: return "VF Non-Prefetchable Memory"; case PCIM_EA_P_BRIDGE_MEM: return "Bridge Non-Prefetchable Memory"; case PCIM_EA_P_BRIDGE_MEM_PREFETCH: return "Bridge Prefetchable Memory"; case PCIM_EA_P_BRIDGE_IO: return "Bridge I/O Space"; case PCIM_EA_P_MEM_RESERVED: return "Reserved Memory"; case PCIM_EA_P_IO_RESERVED: return "Reserved I/O Space"; case PCIM_EA_P_UNAVAILABLE: return "Unavailable"; default: return "Reserved"; } } static void cap_ea(int fd, struct pci_conf *p, uint8_t ptr) { int num_ent; int a, b; uint32_t bei; uint32_t val; int ent_size; uint32_t dw[4]; uint32_t flags, flags_pp, flags_sp; uint64_t base, max_offset; uint8_t fixed_sub_bus_nr, fixed_sec_bus_nr; /* Determine the number of entries */ num_ent = read_config(fd, &p->pc_sel, ptr + PCIR_EA_NUM_ENT, 2); num_ent &= PCIM_EA_NUM_ENT_MASK; printf("PCI Enhanced Allocation (%d entries)", num_ent); /* Find the first entry to care of */ ptr += PCIR_EA_FIRST_ENT; /* Print BUS numbers for bridges */ if ((p->pc_hdr & PCIM_HDRTYPE) == PCIM_HDRTYPE_BRIDGE) { val = read_config(fd, &p->pc_sel, ptr, 4); fixed_sec_bus_nr = PCIM_EA_SEC_NR(val); fixed_sub_bus_nr = PCIM_EA_SUB_NR(val); printf("\n\t\t BRIDGE, sec bus [%d], sub bus [%d]", fixed_sec_bus_nr, fixed_sub_bus_nr); ptr += 4; } for (a = 0; a < num_ent; a++) { /* Read a number of dwords in the entry */ val = read_config(fd, &p->pc_sel, ptr, 4); ptr += 4; ent_size = (val & PCIM_EA_ES); for (b = 0; b < ent_size; b++) { dw[b] = read_config(fd, &p->pc_sel, ptr, 4); ptr += 4; } flags = val; flags_pp = (flags & PCIM_EA_PP) >> PCIM_EA_PP_OFFSET; flags_sp = (flags & PCIM_EA_SP) >> PCIM_EA_SP_OFFSET; bei = (PCIM_EA_BEI & val) >> PCIM_EA_BEI_OFFSET; base = dw[0] & PCIM_EA_FIELD_MASK; max_offset = dw[1] | ~PCIM_EA_FIELD_MASK; b = 2; if (((dw[0] & PCIM_EA_IS_64) != 0) && (b < ent_size)) { base |= (uint64_t)dw[b] << 32UL; b++; } if (((dw[1] & PCIM_EA_IS_64) != 0) && (b < ent_size)) { max_offset |= (uint64_t)dw[b] << 32UL; b++; } printf("\n\t\t [%d] %s, %s, %s, base [0x%jx], size [0x%jx]" "\n\t\t\tPrimary properties [0x%x] (%s)" "\n\t\t\tSecondary properties [0x%x] (%s)", bei, ea_bei_to_name(bei), (flags & PCIM_EA_ENABLE ? "Enabled" : "Disabled"), (flags & PCIM_EA_WRITABLE ? "Writable" : "Read-only"), (uintmax_t)base, (uintmax_t)(max_offset + 1), flags_pp, ea_prop_to_name(flags_pp), flags_sp, ea_prop_to_name(flags_sp)); } } void list_caps(int fd, struct pci_conf *p) { int express; uint16_t sta; uint8_t ptr, cap; /* Are capabilities present for this device? */ sta = read_config(fd, &p->pc_sel, PCIR_STATUS, 2); if (!(sta & PCIM_STATUS_CAPPRESENT)) return; switch (p->pc_hdr & PCIM_HDRTYPE) { case PCIM_HDRTYPE_NORMAL: case PCIM_HDRTYPE_BRIDGE: ptr = PCIR_CAP_PTR; break; case PCIM_HDRTYPE_CARDBUS: ptr = PCIR_CAP_PTR_2; break; default: errx(1, "list_caps: bad header type"); } /* Walk the capability list. */ express = 0; ptr = read_config(fd, &p->pc_sel, ptr, 1); while (ptr != 0 && ptr != 0xff) { cap = read_config(fd, &p->pc_sel, ptr + PCICAP_ID, 1); printf(" cap %02x[%02x] = ", cap, ptr); switch (cap) { case PCIY_PMG: cap_power(fd, p, ptr); break; case PCIY_AGP: cap_agp(fd, p, ptr); break; case PCIY_VPD: cap_vpd(fd, p, ptr); break; case PCIY_MSI: cap_msi(fd, p, ptr); break; case PCIY_PCIX: cap_pcix(fd, p, ptr); break; case PCIY_HT: cap_ht(fd, p, ptr); break; case PCIY_VENDOR: cap_vendor(fd, p, ptr); break; case PCIY_DEBUG: cap_debug(fd, p, ptr); break; case PCIY_SUBVENDOR: cap_subvendor(fd, p, ptr); break; case PCIY_EXPRESS: express = 1; cap_express(fd, p, ptr); break; case PCIY_MSIX: cap_msix(fd, p, ptr); break; case PCIY_SATA: cap_sata(fd, p, ptr); break; case PCIY_PCIAF: cap_pciaf(fd, p, ptr); break; case PCIY_EA: cap_ea(fd, p, ptr); break; default: printf("unknown"); break; } printf("\n"); ptr = read_config(fd, &p->pc_sel, ptr + PCICAP_NEXTPTR, 1); } if (express) list_ecaps(fd, p); } /* From . */ static __inline uint32_t bitcount32(uint32_t x) { x = (x & 0x55555555) + ((x & 0xaaaaaaaa) >> 1); x = (x & 0x33333333) + ((x & 0xcccccccc) >> 2); x = (x + (x >> 4)) & 0x0f0f0f0f; x = (x + (x >> 8)); x = (x + (x >> 16)) & 0x000000ff; return (x); } static void ecap_aer(int fd, struct pci_conf *p, uint16_t ptr, uint8_t ver) { uint32_t sta, mask; printf("AER %d", ver); if (ver < 1) return; sta = read_config(fd, &p->pc_sel, ptr + PCIR_AER_UC_STATUS, 4); mask = read_config(fd, &p->pc_sel, ptr + PCIR_AER_UC_SEVERITY, 4); printf(" %d fatal", bitcount32(sta & mask)); printf(" %d non-fatal", bitcount32(sta & ~mask)); sta = read_config(fd, &p->pc_sel, ptr + PCIR_AER_COR_STATUS, 4); printf(" %d corrected\n", bitcount32(sta)); } static void ecap_vc(int fd, struct pci_conf *p, uint16_t ptr, uint8_t ver) { uint32_t cap1; printf("VC %d", ver); if (ver < 1) return; cap1 = read_config(fd, &p->pc_sel, ptr + PCIR_VC_CAP1, 4); printf(" max VC%d", cap1 & PCIM_VC_CAP1_EXT_COUNT); if ((cap1 & PCIM_VC_CAP1_LOWPRI_EXT_COUNT) != 0) printf(" lowpri VC0-VC%d", (cap1 & PCIM_VC_CAP1_LOWPRI_EXT_COUNT) >> 4); printf("\n"); } static void ecap_sernum(int fd, struct pci_conf *p, uint16_t ptr, uint8_t ver) { uint32_t high, low; printf("Serial %d", ver); if (ver < 1) return; low = read_config(fd, &p->pc_sel, ptr + PCIR_SERIAL_LOW, 4); high = read_config(fd, &p->pc_sel, ptr + PCIR_SERIAL_HIGH, 4); printf(" %08x%08x\n", high, low); } static void ecap_vendor(int fd, struct pci_conf *p, uint16_t ptr, uint8_t ver) { uint32_t val; printf("Vendor %d", ver); if (ver < 1) return; val = read_config(fd, &p->pc_sel, ptr + 4, 4); printf(" ID %d\n", val & 0xffff); } static void ecap_sec_pcie(int fd, struct pci_conf *p, uint16_t ptr, uint8_t ver) { uint32_t val; printf("PCIe Sec %d", ver); if (ver < 1) return; val = read_config(fd, &p->pc_sel, ptr + 8, 4); printf(" lane errors %#x\n", val); } static const char * check_enabled(int value) { return (value ? "enabled" : "disabled"); } static void ecap_sriov(int fd, struct pci_conf *p, uint16_t ptr, uint8_t ver) { const char *comma, *enabled; uint16_t iov_ctl, total_vfs, num_vfs, vf_offset, vf_stride, vf_did; uint32_t page_caps, page_size, page_shift, size; int i; printf("SR-IOV %d ", ver); iov_ctl = read_config(fd, &p->pc_sel, ptr + PCIR_SRIOV_CTL, 2); printf("IOV %s, Memory Space %s, ARI %s\n", check_enabled(iov_ctl & PCIM_SRIOV_VF_EN), check_enabled(iov_ctl & PCIM_SRIOV_VF_MSE), check_enabled(iov_ctl & PCIM_SRIOV_ARI_EN)); total_vfs = read_config(fd, &p->pc_sel, ptr + PCIR_SRIOV_TOTAL_VFS, 2); num_vfs = read_config(fd, &p->pc_sel, ptr + PCIR_SRIOV_NUM_VFS, 2); printf(" "); printf("%d VFs configured out of %d supported\n", num_vfs, total_vfs); vf_offset = read_config(fd, &p->pc_sel, ptr + PCIR_SRIOV_VF_OFF, 2); vf_stride = read_config(fd, &p->pc_sel, ptr + PCIR_SRIOV_VF_STRIDE, 2); printf(" "); printf("First VF RID Offset 0x%04x, VF RID Stride 0x%04x\n", vf_offset, vf_stride); vf_did = read_config(fd, &p->pc_sel, ptr + PCIR_SRIOV_VF_DID, 2); printf(" VF Device ID 0x%04x\n", vf_did); page_caps = read_config(fd, &p->pc_sel, ptr + PCIR_SRIOV_PAGE_CAP, 4); page_size = read_config(fd, &p->pc_sel, ptr + PCIR_SRIOV_PAGE_SIZE, 4); printf(" "); printf("Page Sizes: "); comma = ""; while (page_caps != 0) { page_shift = ffs(page_caps) - 1; if (page_caps & page_size) enabled = " (enabled)"; else enabled = ""; size = (1 << (page_shift + PCI_SRIOV_BASE_PAGE_SHIFT)); printf("%s%d%s", comma, size, enabled); comma = ", "; page_caps &= ~(1 << page_shift); } printf("\n"); for (i = 0; i <= PCIR_MAX_BAR_0; i++) print_bar(fd, p, "iov bar ", ptr + PCIR_SRIOV_BAR(i)); } static struct { uint16_t id; const char *name; } ecap_names[] = { { PCIZ_PWRBDGT, "Power Budgeting" }, { PCIZ_RCLINK_DCL, "Root Complex Link Declaration" }, { PCIZ_RCLINK_CTL, "Root Complex Internal Link Control" }, { PCIZ_RCEC_ASSOC, "Root Complex Event Collector ASsociation" }, { PCIZ_MFVC, "MFVC" }, { PCIZ_RCRB, "RCRB" }, { PCIZ_ACS, "ACS" }, { PCIZ_ARI, "ARI" }, { PCIZ_ATS, "ATS" }, { PCIZ_MULTICAST, "Multicast" }, { PCIZ_RESIZE_BAR, "Resizable BAR" }, { PCIZ_DPA, "DPA" }, { PCIZ_TPH_REQ, "TPH Requester" }, { PCIZ_LTR, "LTR" }, { 0, NULL } }; static void list_ecaps(int fd, struct pci_conf *p) { const char *name; uint32_t ecap; uint16_t ptr; int i; ptr = PCIR_EXTCAP; ecap = read_config(fd, &p->pc_sel, ptr, 4); if (ecap == 0xffffffff || ecap == 0) return; for (;;) { printf(" ecap %04x[%03x] = ", PCI_EXTCAP_ID(ecap), ptr); switch (PCI_EXTCAP_ID(ecap)) { case PCIZ_AER: ecap_aer(fd, p, ptr, PCI_EXTCAP_VER(ecap)); break; case PCIZ_VC: ecap_vc(fd, p, ptr, PCI_EXTCAP_VER(ecap)); break; case PCIZ_SERNUM: ecap_sernum(fd, p, ptr, PCI_EXTCAP_VER(ecap)); break; case PCIZ_VENDOR: ecap_vendor(fd, p, ptr, PCI_EXTCAP_VER(ecap)); break; case PCIZ_SEC_PCIE: ecap_sec_pcie(fd, p, ptr, PCI_EXTCAP_VER(ecap)); break; case PCIZ_SRIOV: ecap_sriov(fd, p, ptr, PCI_EXTCAP_VER(ecap)); break; default: name = "unknown"; for (i = 0; ecap_names[i].name != NULL; i++) if (ecap_names[i].id == PCI_EXTCAP_ID(ecap)) { name = ecap_names[i].name; break; } printf("%s %d\n", name, PCI_EXTCAP_VER(ecap)); break; } ptr = PCI_EXTCAP_NEXTPTR(ecap); if (ptr == 0) break; ecap = read_config(fd, &p->pc_sel, ptr, 4); } } /* Find offset of a specific capability. Returns 0 on failure. */ uint8_t pci_find_cap(int fd, struct pci_conf *p, uint8_t id) { uint16_t sta; uint8_t ptr, cap; /* Are capabilities present for this device? */ sta = read_config(fd, &p->pc_sel, PCIR_STATUS, 2); if (!(sta & PCIM_STATUS_CAPPRESENT)) return (0); switch (p->pc_hdr & PCIM_HDRTYPE) { case PCIM_HDRTYPE_NORMAL: case PCIM_HDRTYPE_BRIDGE: ptr = PCIR_CAP_PTR; break; case PCIM_HDRTYPE_CARDBUS: ptr = PCIR_CAP_PTR_2; break; default: return (0); } ptr = read_config(fd, &p->pc_sel, ptr, 1); while (ptr != 0 && ptr != 0xff) { cap = read_config(fd, &p->pc_sel, ptr + PCICAP_ID, 1); if (cap == id) return (ptr); ptr = read_config(fd, &p->pc_sel, ptr + PCICAP_NEXTPTR, 1); } return (0); } /* Find offset of a specific extended capability. Returns 0 on failure. */ uint16_t pcie_find_cap(int fd, struct pci_conf *p, uint16_t id) { uint32_t ecap; uint16_t ptr; ptr = PCIR_EXTCAP; ecap = read_config(fd, &p->pc_sel, ptr, 4); if (ecap == 0xffffffff || ecap == 0) return (0); for (;;) { if (PCI_EXTCAP_ID(ecap) == id) return (ptr); ptr = PCI_EXTCAP_NEXTPTR(ecap); if (ptr == 0) break; ecap = read_config(fd, &p->pc_sel, ptr, 4); } return (0); } Index: head/usr.sbin/pciconf/pciconf.8 =================================================================== --- head/usr.sbin/pciconf/pciconf.8 (revision 353461) +++ head/usr.sbin/pciconf/pciconf.8 (revision 353462) @@ -1,401 +1,398 @@ .\" 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, 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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$ .\" .Dd June 14, 2018 .Dt PCICONF 8 .Os .Sh NAME .Nm pciconf .Nd diagnostic utility for the PCI bus .Sh SYNOPSIS .Nm .Fl l Oo Fl BbceVv Oc Op Ar device .Nm .Fl a Ar device .Nm .Fl r Oo Fl b | h Oc Ar device addr Ns Op : Ns Ar addr2 .Nm .Fl w Oo Fl b | h Oc Ar device addr value .Nm .Fl D Oo Fl b | h | x Oc Ar device addr Op start Ns Op : Ns Ar count .Sh DESCRIPTION The .Nm utility provides a command line interface to functionality provided by the .Xr pci 4 .Xr ioctl 2 interface. As such, some of the functions are only available to users with write access to .Pa /dev/pci , normally only the super-user. .Pp With the .Fl l option, .Nm lists PCI devices in the following format: .Bd -literal -foo0@pci0:0:4:0: class=0x010000 card=0x00000000 chip=0x000f1000 rev=0x01 \ -hdr=0x00 -bar0@pci0:0:5:0: class=0x000100 card=0x00000000 chip=0x88c15333 rev=0x00 \ -hdr=0x00 -none0@pci0:0:6:0: class=0x020000 card=0x00000000 chip=0x802910ec rev=0x00 \ -hdr=0x00 +foo0@pci0:0:4:0: class=0x010000 rev=0x01 hdr=0x00 vendor=0x1000 device=0x000f \ +subvendor=0x0000 subdevice=0x0000 +bar0@pci0:0:5:0: class=0x000100 rev=0x00 hdr=0x00 vendor=0x88c1 device=0x5333 \ +subvendor=0x0000 subdevice=0x0000 +none0@pci0:0:6:0: class=0x020000 rev=0x00 hdr=0x00 vendor=0x10ec device=0x8029 \ +subvendor=0x0000 subdevice=0x0000 .Ed .Pp The first column gives the -driver name, unit number, and selector . +driver name, unit number, and selector. If there is no driver attached to the .Tn PCI device in question, the driver name will be .Dq none . Unit numbers for detached devices start at zero and are incremented for each detached device that is encountered. The selector is in a form which may directly be used for the other forms of the command. The second column is the class code, with the class byte printed as two hex digits, followed by the sub-class and the interface bytes. -The third column gives the contents of the subvendorid register, introduced -in revision 2.1 of the -.Tn PCI -standard. -Note that it will be 0 for older cards. -The field consists of the card ID in the upper -half and the card vendor ID in the lower half of the value. +The third column prints the device's revision. +The fourth column describes the header type. .Pp -The fourth column contains the chip device ID, which identifies the chip -this card is based on. -It consists of two fields, identifying the chip and -its vendor, as above. -The fifth column prints the chip's revision. -The sixth column describes the header type. -Currently assigned header types include 0 for most devices, +Currently assigned header types include 0 for standard devices, 1 for .Tn PCI to .Tn PCI bridges, and 2 for .Tn PCI to .Tn CardBus bridges. If the most significant bit of the header type register is set for function 0 of a .Tn PCI device, it is a .Em multi-function device, which contains several (similar or independent) functions on one chip. +.Pp +The sixth and seventh columns contain the vendor ID and the device ID of the +device. +The eigth and ninth columns contain subvendor and subdevice IDs, introduced +in revision 2.1 of the +.Tn PCI +standard. +Note that they will be 0 for older cards. .Pp If the .Fl B option is supplied, .Nm will list additional information for .Tn PCI to .Tn PCI and .Tn PCI to .Tn CardBus bridges, specifically the resource ranges decoded by the bridge for use by devices behind the bridge. Each bridge lists a range of bus numbers handled by the bridge and its downstream devices. Memory and I/O port decoding windows are enumerated via a line in the following format: .Bd -literal window[1c] = type I/O Port, range 16, addr 0x5000-0x8fff, enabled .Ed .Pp The first value after the .Dq Li window prefix in the square brackets is the offset of the decoding window in config space in hexadecimal. The type of a window is one of .Dq Memory , .Dq Prefetchable Memory , or .Dq I/O Port . The range indicates the binary log of the maximum address the window decodes. The address field indicates the start and end addresses of the decoded range. Finally, the last flag indicates if the window is enabled or disabled. .Pp If the .Fl b option is supplied, .Nm will list any base address registers .Pq BARs that are assigned resources for each device. Each BAR will be enumerated via a line in the following format: .Bd -literal bar [10] = type Memory, range 32, base 0xda060000, size 131072, enabled .Ed .Pp The first value after the .Dq Li bar prefix in the square brackets is the offset of the BAR in config space in hexadecimal. The type of a BAR is one of .Dq Memory , .Dq Prefetchable Memory , or .Dq I/O Port . The range indicates the binary log of the maximum address the BAR decodes. The base and size indicate the start and length of the BAR's address window, respectively. Finally, the last flag indicates if the BAR is enabled or disabled. .Pp If the .Fl c option is supplied, .Nm will list any capabilities supported by each device. Each capability is enumerated via a line in the following format: .Bd -literal cap 10[40] = PCI-Express 1 root port .Ed .Pp The first value after the .Dq Li cap prefix is the capability ID in hexadecimal. The second value in the square brackets is the offset of the capability in config space in hexadecimal. The format of the text after the equals sign is capability-specific. .Pp Each extended capability is enumerated via a line in a similar format: .Bd -literal ecap 0002[100] = VC 1 max VC0 .Ed .Pp The first value after the .Dq Li ecap prefix is the extended capability ID in hexadecimal. The second value in the square brackets is the offset of the extended capability in config space in hexadecimal. The format of the text after the equals sign is capability-specific. .Pp If the .Fl e option is supplied, .Nm will list any errors reported for this device in standard PCI error registers. Errors are checked for in the PCI status register, the PCI-express device status register, and the Advanced Error Reporting status registers. .Pp If the .Fl v option is supplied, .Nm will attempt to load the vendor/device information database, and print vendor, device, class and subclass identification strings for each device. .Pp If the .Fl V option is supplied, .Nm will list any vital product data .Pq VPD provided by each device. Each VPD keyword is enumerated via a line in the following format: .Bd -literal VPD ro PN = '110114640C0 ' .Ed .Pp The first string after the .Dq Li VPD prefix indicates if the keyword is read-only .Dq ro or read-write .Dq rw . The second string provides the keyword name. The text after the equals sign lists the value of the keyword which is usually an ASCII string. .Pp If the optional .Ar device argument is given with the .Fl l flag, .Nm will only list details about a single device instead of all devices. .Pp All invocations of .Nm except for .Fl l require a .Ar device . The device can be identified either by a device name if the device is attached to a driver or by a selector. Selectors identify a PCI device by its address in PCI config space and can take one of the following forms: .Pp .Bl -bullet -offset indent -compact .It .Li pci Ns Va domain Ns \&: Ns Va bus Ns \&: Ns Va device Ns \&: \ Ns Va function Ns .It .Li pci Ns Va bus Ns \&: Ns Va device Ns \&: Ns Va function Ns .It .Li pci Ns Va bus Ns \&: Ns Va device Ns .El .Pp In the case of an abridged form, omitted selector components are assumed to be 0. An optional leading device name followed by @ and an optional final colon will be ignored; this is so that the first column in the output of .Nm .Fl l can be used without modification. All numbers are base 10. .Pp With the .Fl a flag, .Nm determines whether any driver has been assigned to the device identified by .Ar selector . An exit status of zero indicates that the device has a driver; non-zero indicates that it does not. .Pp The .Fl r option reads a configuration space register at byte offset .Ar addr of device .Ar selector and prints out its value in hexadecimal. The optional second address .Ar addr2 specifies a range to read. The .Fl w option writes the .Ar value into a configuration space register at byte offset .Ar addr of device .Ar selector . .Pp The .Fl D option request a dump of the specified BAR. Dump is performed to the standard output, raw register values are written. Use .Xr hexdump 1 to convert them to human-readable dump, or redirect into a file to save the snapshot of the device state. Optionally, the .Ar start and .Ar count of the registers dumped can be specified, in multiple of the operation width, see next paragraph. .Pp For read, write, and dump operations, the flags .Fl b , .Fl h , and .Fl x select the width of the operation; .Fl b indicates a byte operation, and .Fl h indicates a halfword (two-byte) operation. .Fl x indicates a quadword (four-byte) operation. The default is to read or write a longword (four bytes). The quadword mode is only valid for BAR dump. .Sh ENVIRONMENT PCI vendor and device information is read from .Pa /usr/local/share/pciids/pci.ids . If that file is not present, it is read from .Pa /usr/share/misc/pci_vendors . This path can be overridden by setting the environment variable .Ev PCICONF_VENDOR_DATABASE . .Sh SEE ALSO .Xr ioctl 2 , .\" .Xr pci 4 , .Xr devinfo 8 , .Xr kldload 8 .Sh HISTORY The .Nm utility appeared first in .Fx 2.2 . The .Fl a option was added for .Tn PCI KLD support in .Fx 3.0 . .Sh AUTHORS .An -nosplit The .Nm utility was written by .An Stefan Esser and .An Garrett Wollman . .Sh BUGS The .Fl b and .Fl h options are implemented in .Nm , but not in the underlying .Xr ioctl 2 . .Pp It might be useful to give non-root users access to the .Fl a and .Fl r options. But only root will be able to execute a .Nm kldload to provide the device with a driver KLD, and reading of configuration space registers may cause a failure in badly designed .Tn PCI chips. .Pp There is currently no way to specify the caching mode for the mapping established by the .Fl D option, .Nm always uses uncached access. This is fine for control register BARs. Index: head/usr.sbin/pciconf/pciconf.c =================================================================== --- head/usr.sbin/pciconf/pciconf.c (revision 353461) +++ head/usr.sbin/pciconf/pciconf.c (revision 353462) @@ -1,1165 +1,1167 @@ /* * Copyright 1996 Massachusetts Institute of Technology * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that both the above copyright notice and this * permission notice appear in all copies, that both the above * copyright notice and this permission notice appear in all * supporting documentation, and that the name of M.I.T. not be used * in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. M.I.T. makes * no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied * warranty. * * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT * SHALL M.I.T. 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. */ #ifndef lint static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pathnames.h" #include "pciconf.h" struct pci_device_info { TAILQ_ENTRY(pci_device_info) link; int id; char *desc; }; struct pci_vendor_info { TAILQ_ENTRY(pci_vendor_info) link; TAILQ_HEAD(,pci_device_info) devs; int id; char *desc; }; static TAILQ_HEAD(,pci_vendor_info) pci_vendors; static struct pcisel getsel(const char *str); static void list_bridge(int fd, struct pci_conf *p); static void list_bars(int fd, struct pci_conf *p); static void list_devs(const char *name, int verbose, int bars, int bridge, int caps, int errors, int vpd); static void list_verbose(struct pci_conf *p); static void list_vpd(int fd, struct pci_conf *p); static const char *guess_class(struct pci_conf *p); static const char *guess_subclass(struct pci_conf *p); static int load_vendors(void); static void readit(const char *, const char *, int); static void writeit(const char *, const char *, const char *, int); static void chkattached(const char *); static void dump_bar(const char *name, const char *reg, const char *bar_start, const char *bar_count, int width, int verbose); static int exitstatus = 0; static void usage(void) { fprintf(stderr, "%s", "usage: pciconf -l [-BbcevV] [device]\n" " pciconf -a device\n" " pciconf -r [-b | -h] device addr[:addr2]\n" " pciconf -w [-b | -h] device addr value\n" " pciconf -D [-b | -h | -x] device bar [start [count]]" "\n"); exit(1); } int main(int argc, char **argv) { int c, width; int listmode, readmode, writemode, attachedmode, dumpbarmode; int bars, bridge, caps, errors, verbose, vpd; listmode = readmode = writemode = attachedmode = dumpbarmode = 0; bars = bridge = caps = errors = verbose = vpd= 0; width = 4; while ((c = getopt(argc, argv, "aBbcDehlrwVv")) != -1) { switch(c) { case 'a': attachedmode = 1; break; case 'B': bridge = 1; break; case 'b': bars = 1; width = 1; break; case 'c': caps = 1; break; case 'D': dumpbarmode = 1; break; case 'e': errors = 1; break; case 'h': width = 2; break; case 'l': listmode = 1; break; case 'r': readmode = 1; break; case 'w': writemode = 1; break; case 'v': verbose = 1; break; case 'V': vpd = 1; break; case 'x': width = 8; break; default: usage(); } } if ((listmode && optind >= argc + 1) || (writemode && optind + 3 != argc) || (readmode && optind + 2 != argc) || (attachedmode && optind + 1 != argc) || (dumpbarmode && (optind + 2 > argc || optind + 4 < argc)) || (width == 8 && !dumpbarmode)) usage(); if (listmode) { list_devs(optind + 1 == argc ? argv[optind] : NULL, verbose, bars, bridge, caps, errors, vpd); } else if (attachedmode) { chkattached(argv[optind]); } else if (readmode) { readit(argv[optind], argv[optind + 1], width); } else if (writemode) { writeit(argv[optind], argv[optind + 1], argv[optind + 2], width); } else if (dumpbarmode) { dump_bar(argv[optind], argv[optind + 1], optind + 2 < argc ? argv[optind + 2] : NULL, optind + 3 < argc ? argv[optind + 3] : NULL, width, verbose); } else { usage(); } return (exitstatus); } static void list_devs(const char *name, int verbose, int bars, int bridge, int caps, int errors, int vpd) { int fd; struct pci_conf_io pc; struct pci_conf conf[255], *p; struct pci_match_conf patterns[1]; int none_count = 0; if (verbose) load_vendors(); fd = open(_PATH_DEVPCI, (bridge || caps || errors) ? O_RDWR : O_RDONLY, 0); if (fd < 0) err(1, "%s", _PATH_DEVPCI); bzero(&pc, sizeof(struct pci_conf_io)); pc.match_buf_len = sizeof(conf); pc.matches = conf; if (name != NULL) { bzero(&patterns, sizeof(patterns)); patterns[0].pc_sel = getsel(name); patterns[0].flags = PCI_GETCONF_MATCH_DOMAIN | PCI_GETCONF_MATCH_BUS | PCI_GETCONF_MATCH_DEV | PCI_GETCONF_MATCH_FUNC; pc.num_patterns = 1; pc.pat_buf_len = sizeof(patterns); pc.patterns = patterns; } do { if (ioctl(fd, PCIOCGETCONF, &pc) == -1) err(1, "ioctl(PCIOCGETCONF)"); /* * 255 entries should be more than enough for most people, * but if someone has more devices, and then changes things * around between ioctls, we'll do the cheesy thing and * just bail. The alternative would be to go back to the * beginning of the list, and print things twice, which may * not be desirable. */ if (pc.status == PCI_GETCONF_LIST_CHANGED) { warnx("PCI device list changed, please try again"); exitstatus = 1; close(fd); return; } else if (pc.status == PCI_GETCONF_ERROR) { warnx("error returned from PCIOCGETCONF ioctl"); exitstatus = 1; close(fd); return; } for (p = conf; p < &conf[pc.num_matches]; p++) { - printf("%s%d@pci%d:%d:%d:%d:\tclass=0x%06x subvendor=0x%04x subdevice=0x%04x " - "vendor=0x%04x device=0x%04x rev=0x%02x hdr=0x%02x\n", + printf("%s%d@pci%d:%d:%d:%d:" + "\tclass=0x%06x rev=0x%02x hdr=0x%02x " + "vendor=0x%04x device=0x%04x " + "subvendor=0x%04x subdevice=0x%04x\n", *p->pd_name ? p->pd_name : "none", *p->pd_name ? (int)p->pd_unit : none_count++, p->pc_sel.pc_domain, p->pc_sel.pc_bus, p->pc_sel.pc_dev, p->pc_sel.pc_func, (p->pc_class << 16) | (p->pc_subclass << 8) | p->pc_progif, - p->pc_subdevice, p->pc_subvendor, - p->pc_device, p->pc_vendor, - p->pc_revid, p->pc_hdr); + p->pc_revid, p->pc_hdr, + p->pc_vendor, p->pc_device, + p->pc_subvendor, p->pc_subdevice); if (verbose) list_verbose(p); if (bars) list_bars(fd, p); if (bridge) list_bridge(fd, p); if (caps) list_caps(fd, p); if (errors) list_errors(fd, p); if (vpd) list_vpd(fd, p); } } while (pc.status == PCI_GETCONF_MORE_DEVS); close(fd); } static void print_bus_range(int fd, struct pci_conf *p, int secreg, int subreg) { uint8_t secbus, subbus; secbus = read_config(fd, &p->pc_sel, secreg, 1); subbus = read_config(fd, &p->pc_sel, subreg, 1); printf(" bus range = %u-%u\n", secbus, subbus); } static void print_window(int reg, const char *type, int range, uint64_t base, uint64_t limit) { printf(" window[%02x] = type %s, range %2d, addr %#jx-%#jx, %s\n", reg, type, range, (uintmax_t)base, (uintmax_t)limit, base < limit ? "enabled" : "disabled"); } static void print_special_decode(bool isa, bool vga, bool subtractive) { bool comma; if (isa || vga || subtractive) { comma = false; printf(" decode = "); if (isa) { printf("ISA"); comma = true; } if (vga) { printf("%sVGA", comma ? ", " : ""); comma = true; } if (subtractive) printf("%ssubtractive", comma ? ", " : ""); printf("\n"); } } static void print_bridge_windows(int fd, struct pci_conf *p) { uint64_t base, limit; uint32_t val; uint16_t bctl; bool subtractive; int range; /* * XXX: This assumes that a window with a base and limit of 0 * is not implemented. In theory a window might be programmed * at the smallest size with a base of 0, but those do not seem * common in practice. */ val = read_config(fd, &p->pc_sel, PCIR_IOBASEL_1, 1); if (val != 0 || read_config(fd, &p->pc_sel, PCIR_IOLIMITL_1, 1) != 0) { if ((val & PCIM_BRIO_MASK) == PCIM_BRIO_32) { base = PCI_PPBIOBASE( read_config(fd, &p->pc_sel, PCIR_IOBASEH_1, 2), val); limit = PCI_PPBIOLIMIT( read_config(fd, &p->pc_sel, PCIR_IOLIMITH_1, 2), read_config(fd, &p->pc_sel, PCIR_IOLIMITL_1, 1)); range = 32; } else { base = PCI_PPBIOBASE(0, val); limit = PCI_PPBIOLIMIT(0, read_config(fd, &p->pc_sel, PCIR_IOLIMITL_1, 1)); range = 16; } print_window(PCIR_IOBASEL_1, "I/O Port", range, base, limit); } base = PCI_PPBMEMBASE(0, read_config(fd, &p->pc_sel, PCIR_MEMBASE_1, 2)); limit = PCI_PPBMEMLIMIT(0, read_config(fd, &p->pc_sel, PCIR_MEMLIMIT_1, 2)); print_window(PCIR_MEMBASE_1, "Memory", 32, base, limit); val = read_config(fd, &p->pc_sel, PCIR_PMBASEL_1, 2); if (val != 0 || read_config(fd, &p->pc_sel, PCIR_PMLIMITL_1, 2) != 0) { if ((val & PCIM_BRPM_MASK) == PCIM_BRPM_64) { base = PCI_PPBMEMBASE( read_config(fd, &p->pc_sel, PCIR_PMBASEH_1, 4), val); limit = PCI_PPBMEMLIMIT( read_config(fd, &p->pc_sel, PCIR_PMLIMITH_1, 4), read_config(fd, &p->pc_sel, PCIR_PMLIMITL_1, 2)); range = 64; } else { base = PCI_PPBMEMBASE(0, val); limit = PCI_PPBMEMLIMIT(0, read_config(fd, &p->pc_sel, PCIR_PMLIMITL_1, 2)); range = 32; } print_window(PCIR_PMBASEL_1, "Prefetchable Memory", range, base, limit); } /* * XXX: This list of bridges that are subtractive but do not set * progif to indicate it is copied from pci_pci.c. */ subtractive = p->pc_progif == PCIP_BRIDGE_PCI_SUBTRACTIVE; switch (p->pc_device << 16 | p->pc_vendor) { case 0xa002177d: /* Cavium ThunderX */ case 0x124b8086: /* Intel 82380FB Mobile */ case 0x060513d7: /* Toshiba ???? */ subtractive = true; } if (p->pc_vendor == 0x8086 && (p->pc_device & 0xff00) == 0x2400) subtractive = true; bctl = read_config(fd, &p->pc_sel, PCIR_BRIDGECTL_1, 2); print_special_decode(bctl & PCIB_BCR_ISA_ENABLE, bctl & PCIB_BCR_VGA_ENABLE, subtractive); } static void print_cardbus_mem_window(int fd, struct pci_conf *p, int basereg, int limitreg, bool prefetch) { print_window(basereg, prefetch ? "Prefetchable Memory" : "Memory", 32, PCI_CBBMEMBASE(read_config(fd, &p->pc_sel, basereg, 4)), PCI_CBBMEMLIMIT(read_config(fd, &p->pc_sel, limitreg, 4))); } static void print_cardbus_io_window(int fd, struct pci_conf *p, int basereg, int limitreg) { uint32_t base, limit; uint32_t val; int range; val = read_config(fd, &p->pc_sel, basereg, 2); if ((val & PCIM_CBBIO_MASK) == PCIM_CBBIO_32) { base = PCI_CBBIOBASE(read_config(fd, &p->pc_sel, basereg, 4)); limit = PCI_CBBIOBASE(read_config(fd, &p->pc_sel, limitreg, 4)); range = 32; } else { base = PCI_CBBIOBASE(val); limit = PCI_CBBIOBASE(read_config(fd, &p->pc_sel, limitreg, 2)); range = 16; } print_window(basereg, "I/O Port", range, base, limit); } static void print_cardbus_windows(int fd, struct pci_conf *p) { uint16_t bctl; bctl = read_config(fd, &p->pc_sel, PCIR_BRIDGECTL_2, 2); print_cardbus_mem_window(fd, p, PCIR_MEMBASE0_2, PCIR_MEMLIMIT0_2, bctl & CBB_BCR_PREFETCH_0_ENABLE); print_cardbus_mem_window(fd, p, PCIR_MEMBASE1_2, PCIR_MEMLIMIT1_2, bctl & CBB_BCR_PREFETCH_1_ENABLE); print_cardbus_io_window(fd, p, PCIR_IOBASE0_2, PCIR_IOLIMIT0_2); print_cardbus_io_window(fd, p, PCIR_IOBASE1_2, PCIR_IOLIMIT1_2); print_special_decode(bctl & CBB_BCR_ISA_ENABLE, bctl & CBB_BCR_VGA_ENABLE, false); } static void list_bridge(int fd, struct pci_conf *p) { switch (p->pc_hdr & PCIM_HDRTYPE) { case PCIM_HDRTYPE_BRIDGE: print_bus_range(fd, p, PCIR_SECBUS_1, PCIR_SUBBUS_1); print_bridge_windows(fd, p); break; case PCIM_HDRTYPE_CARDBUS: print_bus_range(fd, p, PCIR_SECBUS_2, PCIR_SUBBUS_2); print_cardbus_windows(fd, p); break; } } static void list_bars(int fd, struct pci_conf *p) { int i, max; switch (p->pc_hdr & PCIM_HDRTYPE) { case PCIM_HDRTYPE_NORMAL: max = PCIR_MAX_BAR_0; break; case PCIM_HDRTYPE_BRIDGE: max = PCIR_MAX_BAR_1; break; case PCIM_HDRTYPE_CARDBUS: max = PCIR_MAX_BAR_2; break; default: return; } for (i = 0; i <= max; i++) print_bar(fd, p, "bar ", PCIR_BAR(i)); } void print_bar(int fd, struct pci_conf *p, const char *label, uint16_t bar_offset) { uint64_t base; const char *type; struct pci_bar_io bar; int range; bar.pbi_sel = p->pc_sel; bar.pbi_reg = bar_offset; if (ioctl(fd, PCIOCGETBAR, &bar) < 0) return; if (PCI_BAR_IO(bar.pbi_base)) { type = "I/O Port"; range = 32; base = bar.pbi_base & PCIM_BAR_IO_BASE; } else { if (bar.pbi_base & PCIM_BAR_MEM_PREFETCH) type = "Prefetchable Memory"; else type = "Memory"; switch (bar.pbi_base & PCIM_BAR_MEM_TYPE) { case PCIM_BAR_MEM_32: range = 32; break; case PCIM_BAR_MEM_1MB: range = 20; break; case PCIM_BAR_MEM_64: range = 64; break; default: range = -1; } base = bar.pbi_base & ~((uint64_t)0xf); } printf(" %s[%02x] = type %s, range %2d, base %#jx, ", label, bar_offset, type, range, (uintmax_t)base); printf("size %ju, %s\n", (uintmax_t)bar.pbi_length, bar.pbi_enabled ? "enabled" : "disabled"); } static void list_verbose(struct pci_conf *p) { struct pci_vendor_info *vi; struct pci_device_info *di; const char *dp; TAILQ_FOREACH(vi, &pci_vendors, link) { if (vi->id == p->pc_vendor) { printf(" vendor = '%s'\n", vi->desc); break; } } if (vi == NULL) { di = NULL; } else { TAILQ_FOREACH(di, &vi->devs, link) { if (di->id == p->pc_device) { printf(" device = '%s'\n", di->desc); break; } } } if ((dp = guess_class(p)) != NULL) printf(" class = %s\n", dp); if ((dp = guess_subclass(p)) != NULL) printf(" subclass = %s\n", dp); } static void list_vpd(int fd, struct pci_conf *p) { struct pci_list_vpd_io list; struct pci_vpd_element *vpd, *end; list.plvi_sel = p->pc_sel; list.plvi_len = 0; list.plvi_data = NULL; if (ioctl(fd, PCIOCLISTVPD, &list) < 0 || list.plvi_len == 0) return; list.plvi_data = malloc(list.plvi_len); if (ioctl(fd, PCIOCLISTVPD, &list) < 0) { free(list.plvi_data); return; } vpd = list.plvi_data; end = (struct pci_vpd_element *)((char *)vpd + list.plvi_len); for (; vpd < end; vpd = PVE_NEXT(vpd)) { if (vpd->pve_flags == PVE_FLAG_IDENT) { printf(" VPD ident = '%.*s'\n", (int)vpd->pve_datalen, vpd->pve_data); continue; } /* Ignore the checksum keyword. */ if (!(vpd->pve_flags & PVE_FLAG_RW) && memcmp(vpd->pve_keyword, "RV", 2) == 0) continue; /* Ignore remaining read-write space. */ if (vpd->pve_flags & PVE_FLAG_RW && memcmp(vpd->pve_keyword, "RW", 2) == 0) continue; /* Handle extended capability keyword. */ if (!(vpd->pve_flags & PVE_FLAG_RW) && memcmp(vpd->pve_keyword, "CP", 2) == 0) { printf(" VPD ro CP = ID %02x in map 0x%x[0x%x]\n", (unsigned int)vpd->pve_data[0], PCIR_BAR((unsigned int)vpd->pve_data[1]), (unsigned int)vpd->pve_data[3] << 8 | (unsigned int)vpd->pve_data[2]); continue; } /* Remaining keywords should all have ASCII values. */ printf(" VPD %s %c%c = '%.*s'\n", vpd->pve_flags & PVE_FLAG_RW ? "rw" : "ro", vpd->pve_keyword[0], vpd->pve_keyword[1], (int)vpd->pve_datalen, vpd->pve_data); } free(list.plvi_data); } /* * This is a direct cut-and-paste from the table in sys/dev/pci/pci.c. */ static struct { int class; int subclass; const char *desc; } pci_nomatch_tab[] = { {PCIC_OLD, -1, "old"}, {PCIC_OLD, PCIS_OLD_NONVGA, "non-VGA display device"}, {PCIC_OLD, PCIS_OLD_VGA, "VGA-compatible display device"}, {PCIC_STORAGE, -1, "mass storage"}, {PCIC_STORAGE, PCIS_STORAGE_SCSI, "SCSI"}, {PCIC_STORAGE, PCIS_STORAGE_IDE, "ATA"}, {PCIC_STORAGE, PCIS_STORAGE_FLOPPY, "floppy disk"}, {PCIC_STORAGE, PCIS_STORAGE_IPI, "IPI"}, {PCIC_STORAGE, PCIS_STORAGE_RAID, "RAID"}, {PCIC_STORAGE, PCIS_STORAGE_ATA_ADMA, "ATA (ADMA)"}, {PCIC_STORAGE, PCIS_STORAGE_SATA, "SATA"}, {PCIC_STORAGE, PCIS_STORAGE_SAS, "SAS"}, {PCIC_STORAGE, PCIS_STORAGE_NVM, "NVM"}, {PCIC_NETWORK, -1, "network"}, {PCIC_NETWORK, PCIS_NETWORK_ETHERNET, "ethernet"}, {PCIC_NETWORK, PCIS_NETWORK_TOKENRING, "token ring"}, {PCIC_NETWORK, PCIS_NETWORK_FDDI, "fddi"}, {PCIC_NETWORK, PCIS_NETWORK_ATM, "ATM"}, {PCIC_NETWORK, PCIS_NETWORK_ISDN, "ISDN"}, {PCIC_DISPLAY, -1, "display"}, {PCIC_DISPLAY, PCIS_DISPLAY_VGA, "VGA"}, {PCIC_DISPLAY, PCIS_DISPLAY_XGA, "XGA"}, {PCIC_DISPLAY, PCIS_DISPLAY_3D, "3D"}, {PCIC_MULTIMEDIA, -1, "multimedia"}, {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_VIDEO, "video"}, {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_AUDIO, "audio"}, {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_TELE, "telephony"}, {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_HDA, "HDA"}, {PCIC_MEMORY, -1, "memory"}, {PCIC_MEMORY, PCIS_MEMORY_RAM, "RAM"}, {PCIC_MEMORY, PCIS_MEMORY_FLASH, "flash"}, {PCIC_BRIDGE, -1, "bridge"}, {PCIC_BRIDGE, PCIS_BRIDGE_HOST, "HOST-PCI"}, {PCIC_BRIDGE, PCIS_BRIDGE_ISA, "PCI-ISA"}, {PCIC_BRIDGE, PCIS_BRIDGE_EISA, "PCI-EISA"}, {PCIC_BRIDGE, PCIS_BRIDGE_MCA, "PCI-MCA"}, {PCIC_BRIDGE, PCIS_BRIDGE_PCI, "PCI-PCI"}, {PCIC_BRIDGE, PCIS_BRIDGE_PCMCIA, "PCI-PCMCIA"}, {PCIC_BRIDGE, PCIS_BRIDGE_NUBUS, "PCI-NuBus"}, {PCIC_BRIDGE, PCIS_BRIDGE_CARDBUS, "PCI-CardBus"}, {PCIC_BRIDGE, PCIS_BRIDGE_RACEWAY, "PCI-RACEway"}, {PCIC_SIMPLECOMM, -1, "simple comms"}, {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_UART, "UART"}, /* could detect 16550 */ {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_PAR, "parallel port"}, {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_MULSER, "multiport serial"}, {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_MODEM, "generic modem"}, {PCIC_BASEPERIPH, -1, "base peripheral"}, {PCIC_BASEPERIPH, PCIS_BASEPERIPH_PIC, "interrupt controller"}, {PCIC_BASEPERIPH, PCIS_BASEPERIPH_DMA, "DMA controller"}, {PCIC_BASEPERIPH, PCIS_BASEPERIPH_TIMER, "timer"}, {PCIC_BASEPERIPH, PCIS_BASEPERIPH_RTC, "realtime clock"}, {PCIC_BASEPERIPH, PCIS_BASEPERIPH_PCIHOT, "PCI hot-plug controller"}, {PCIC_BASEPERIPH, PCIS_BASEPERIPH_SDHC, "SD host controller"}, {PCIC_BASEPERIPH, PCIS_BASEPERIPH_IOMMU, "IOMMU"}, {PCIC_INPUTDEV, -1, "input device"}, {PCIC_INPUTDEV, PCIS_INPUTDEV_KEYBOARD, "keyboard"}, {PCIC_INPUTDEV, PCIS_INPUTDEV_DIGITIZER,"digitizer"}, {PCIC_INPUTDEV, PCIS_INPUTDEV_MOUSE, "mouse"}, {PCIC_INPUTDEV, PCIS_INPUTDEV_SCANNER, "scanner"}, {PCIC_INPUTDEV, PCIS_INPUTDEV_GAMEPORT, "gameport"}, {PCIC_DOCKING, -1, "docking station"}, {PCIC_PROCESSOR, -1, "processor"}, {PCIC_SERIALBUS, -1, "serial bus"}, {PCIC_SERIALBUS, PCIS_SERIALBUS_FW, "FireWire"}, {PCIC_SERIALBUS, PCIS_SERIALBUS_ACCESS, "AccessBus"}, {PCIC_SERIALBUS, PCIS_SERIALBUS_SSA, "SSA"}, {PCIC_SERIALBUS, PCIS_SERIALBUS_USB, "USB"}, {PCIC_SERIALBUS, PCIS_SERIALBUS_FC, "Fibre Channel"}, {PCIC_SERIALBUS, PCIS_SERIALBUS_SMBUS, "SMBus"}, {PCIC_WIRELESS, -1, "wireless controller"}, {PCIC_WIRELESS, PCIS_WIRELESS_IRDA, "iRDA"}, {PCIC_WIRELESS, PCIS_WIRELESS_IR, "IR"}, {PCIC_WIRELESS, PCIS_WIRELESS_RF, "RF"}, {PCIC_INTELLIIO, -1, "intelligent I/O controller"}, {PCIC_INTELLIIO, PCIS_INTELLIIO_I2O, "I2O"}, {PCIC_SATCOM, -1, "satellite communication"}, {PCIC_SATCOM, PCIS_SATCOM_TV, "sat TV"}, {PCIC_SATCOM, PCIS_SATCOM_AUDIO, "sat audio"}, {PCIC_SATCOM, PCIS_SATCOM_VOICE, "sat voice"}, {PCIC_SATCOM, PCIS_SATCOM_DATA, "sat data"}, {PCIC_CRYPTO, -1, "encrypt/decrypt"}, {PCIC_CRYPTO, PCIS_CRYPTO_NETCOMP, "network/computer crypto"}, {PCIC_CRYPTO, PCIS_CRYPTO_NETCOMP, "entertainment crypto"}, {PCIC_DASP, -1, "dasp"}, {PCIC_DASP, PCIS_DASP_DPIO, "DPIO module"}, {PCIC_DASP, PCIS_DASP_PERFCNTRS, "performance counters"}, {PCIC_DASP, PCIS_DASP_COMM_SYNC, "communication synchronizer"}, {PCIC_DASP, PCIS_DASP_MGMT_CARD, "signal processing management"}, {PCIC_ACCEL, -1, "processing accelerators"}, {PCIC_ACCEL, PCIS_ACCEL_PROCESSING, "processing accelerators"}, {PCIC_INSTRUMENT, -1, "non-essential instrumentation"}, {0, 0, NULL} }; static const char * guess_class(struct pci_conf *p) { int i; for (i = 0; pci_nomatch_tab[i].desc != NULL; i++) { if (pci_nomatch_tab[i].class == p->pc_class) return(pci_nomatch_tab[i].desc); } return(NULL); } static const char * guess_subclass(struct pci_conf *p) { int i; for (i = 0; pci_nomatch_tab[i].desc != NULL; i++) { if ((pci_nomatch_tab[i].class == p->pc_class) && (pci_nomatch_tab[i].subclass == p->pc_subclass)) return(pci_nomatch_tab[i].desc); } return(NULL); } static int load_vendors(void) { const char *dbf; FILE *db; struct pci_vendor_info *cv; struct pci_device_info *cd; char buf[1024], str[1024]; char *ch; int id, error; /* * Locate the database and initialise. */ TAILQ_INIT(&pci_vendors); if ((dbf = getenv("PCICONF_VENDOR_DATABASE")) == NULL) dbf = _PATH_LPCIVDB; if ((db = fopen(dbf, "r")) == NULL) { dbf = _PATH_PCIVDB; if ((db = fopen(dbf, "r")) == NULL) return(1); } cv = NULL; cd = NULL; error = 0; /* * Scan input lines from the database */ for (;;) { if (fgets(buf, sizeof(buf), db) == NULL) break; if ((ch = strchr(buf, '#')) != NULL) *ch = '\0'; ch = strchr(buf, '\0') - 1; while (ch > buf && isspace(*ch)) *ch-- = '\0'; if (ch <= buf) continue; /* Can't handle subvendor / subdevice entries yet */ if (buf[0] == '\t' && buf[1] == '\t') continue; /* Check for vendor entry */ if (buf[0] != '\t' && sscanf(buf, "%04x %[^\n]", &id, str) == 2) { if ((id == 0) || (strlen(str) < 1)) continue; if ((cv = malloc(sizeof(struct pci_vendor_info))) == NULL) { warn("allocating vendor entry"); error = 1; break; } if ((cv->desc = strdup(str)) == NULL) { free(cv); warn("allocating vendor description"); error = 1; break; } cv->id = id; TAILQ_INIT(&cv->devs); TAILQ_INSERT_TAIL(&pci_vendors, cv, link); continue; } /* Check for device entry */ if (buf[0] == '\t' && sscanf(buf + 1, "%04x %[^\n]", &id, str) == 2) { if ((id == 0) || (strlen(str) < 1)) continue; if (cv == NULL) { warnx("device entry with no vendor!"); continue; } if ((cd = malloc(sizeof(struct pci_device_info))) == NULL) { warn("allocating device entry"); error = 1; break; } if ((cd->desc = strdup(str)) == NULL) { free(cd); warn("allocating device description"); error = 1; break; } cd->id = id; TAILQ_INSERT_TAIL(&cv->devs, cd, link); continue; } /* It's a comment or junk, ignore it */ } if (ferror(db)) error = 1; fclose(db); return(error); } uint32_t read_config(int fd, struct pcisel *sel, long reg, int width) { struct pci_io pi; pi.pi_sel = *sel; pi.pi_reg = reg; pi.pi_width = width; if (ioctl(fd, PCIOCREAD, &pi) < 0) err(1, "ioctl(PCIOCREAD)"); return (pi.pi_data); } static struct pcisel getdevice(const char *name) { struct pci_conf_io pc; struct pci_conf conf[1]; struct pci_match_conf patterns[1]; char *cp; int fd; fd = open(_PATH_DEVPCI, O_RDONLY, 0); if (fd < 0) err(1, "%s", _PATH_DEVPCI); bzero(&pc, sizeof(struct pci_conf_io)); pc.match_buf_len = sizeof(conf); pc.matches = conf; bzero(&patterns, sizeof(patterns)); /* * The pattern structure requires the unit to be split out from * the driver name. Walk backwards from the end of the name to * find the start of the unit. */ if (name[0] == '\0') errx(1, "Empty device name"); cp = strchr(name, '\0'); assert(cp != NULL && cp != name); cp--; while (cp != name && isdigit(cp[-1])) cp--; if (cp == name || !isdigit(*cp)) errx(1, "Invalid device name"); if ((size_t)(cp - name) + 1 > sizeof(patterns[0].pd_name)) errx(1, "Device name is too long"); memcpy(patterns[0].pd_name, name, cp - name); patterns[0].pd_unit = strtol(cp, &cp, 10); if (*cp != '\0') errx(1, "Invalid device name"); patterns[0].flags = PCI_GETCONF_MATCH_NAME | PCI_GETCONF_MATCH_UNIT; pc.num_patterns = 1; pc.pat_buf_len = sizeof(patterns); pc.patterns = patterns; if (ioctl(fd, PCIOCGETCONF, &pc) == -1) err(1, "ioctl(PCIOCGETCONF)"); if (pc.status != PCI_GETCONF_LAST_DEVICE && pc.status != PCI_GETCONF_MORE_DEVS) errx(1, "error returned from PCIOCGETCONF ioctl"); close(fd); if (pc.num_matches == 0) errx(1, "Device not found"); return (conf[0].pc_sel); } static struct pcisel parsesel(const char *str) { const char *ep; char *eppos; struct pcisel sel; unsigned long selarr[4]; int i; ep = strchr(str, '@'); if (ep != NULL) ep++; else ep = str; if (strncmp(ep, "pci", 3) == 0) { ep += 3; i = 0; while (isdigit(*ep) && i < 4) { selarr[i++] = strtoul(ep, &eppos, 10); ep = eppos; if (*ep == ':') ep++; } if (i > 0 && *ep == '\0') { sel.pc_func = (i > 2) ? selarr[--i] : 0; sel.pc_dev = (i > 0) ? selarr[--i] : 0; sel.pc_bus = (i > 0) ? selarr[--i] : 0; sel.pc_domain = (i > 0) ? selarr[--i] : 0; return (sel); } } errx(1, "cannot parse selector %s", str); } static struct pcisel getsel(const char *str) { /* * No device names contain colons and selectors always contain * at least one colon. */ if (strchr(str, ':') == NULL) return (getdevice(str)); else return (parsesel(str)); } static void readone(int fd, struct pcisel *sel, long reg, int width) { printf("%0*x", width*2, read_config(fd, sel, reg, width)); } static void readit(const char *name, const char *reg, int width) { long rstart; long rend; long r; char *end; int i; int fd; struct pcisel sel; fd = open(_PATH_DEVPCI, O_RDWR, 0); if (fd < 0) err(1, "%s", _PATH_DEVPCI); rend = rstart = strtol(reg, &end, 0); if (end && *end == ':') { end++; rend = strtol(end, (char **) 0, 0); } sel = getsel(name); for (i = 1, r = rstart; r <= rend; i++, r += width) { readone(fd, &sel, r, width); if (i && !(i % 8)) putchar(' '); putchar(i % (16/width) ? ' ' : '\n'); } if (i % (16/width) != 1) putchar('\n'); close(fd); } static void writeit(const char *name, const char *reg, const char *data, int width) { int fd; struct pci_io pi; pi.pi_sel = getsel(name); pi.pi_reg = strtoul(reg, (char **)0, 0); /* XXX error check */ pi.pi_width = width; pi.pi_data = strtoul(data, (char **)0, 0); /* XXX error check */ fd = open(_PATH_DEVPCI, O_RDWR, 0); if (fd < 0) err(1, "%s", _PATH_DEVPCI); if (ioctl(fd, PCIOCWRITE, &pi) < 0) err(1, "ioctl(PCIOCWRITE)"); close(fd); } static void chkattached(const char *name) { int fd; struct pci_io pi; pi.pi_sel = getsel(name); fd = open(_PATH_DEVPCI, O_RDWR, 0); if (fd < 0) err(1, "%s", _PATH_DEVPCI); if (ioctl(fd, PCIOCATTACHED, &pi) < 0) err(1, "ioctl(PCIOCATTACHED)"); exitstatus = pi.pi_data ? 0 : 2; /* exit(2), if NOT attached */ printf("%s: %s%s\n", name, pi.pi_data == 0 ? "not " : "", "attached"); close(fd); } static void dump_bar(const char *name, const char *reg, const char *bar_start, const char *bar_count, int width, int verbose) { struct pci_bar_mmap pbm; uint32_t *dd; uint16_t *dh; uint8_t *db; uint64_t *dx, a, start, count; char *el; size_t res; int fd; start = 0; if (bar_start != NULL) { start = strtoul(bar_start, &el, 0); if (*el != '\0') errx(1, "Invalid bar start specification %s", bar_start); } count = 0; if (bar_count != NULL) { count = strtoul(bar_count, &el, 0); if (*el != '\0') errx(1, "Invalid count specification %s", bar_count); } pbm.pbm_sel = getsel(name); pbm.pbm_reg = strtoul(reg, &el, 0); if (*reg == '\0' || *el != '\0') errx(1, "Invalid bar specification %s", reg); pbm.pbm_flags = 0; pbm.pbm_memattr = VM_MEMATTR_UNCACHEABLE; /* XXX */ fd = open(_PATH_DEVPCI, O_RDWR, 0); if (fd < 0) err(1, "%s", _PATH_DEVPCI); if (ioctl(fd, PCIOCBARMMAP, &pbm) < 0) err(1, "ioctl(PCIOCBARMMAP)"); if (count == 0) count = pbm.pbm_bar_length / width; if (start + count < start || (start + count) * width < (uint64_t)width) errx(1, "(start + count) x width overflow"); if ((start + count) * width > pbm.pbm_bar_length) { if (start * width > pbm.pbm_bar_length) count = 0; else count = (pbm.pbm_bar_length - start * width) / width; } if (verbose) { fprintf(stderr, "Dumping pci%d:%d:%d:%d BAR %x mapped base %p " "off %#x length %#jx from %#jx count %#jx in %d-bytes\n", pbm.pbm_sel.pc_domain, pbm.pbm_sel.pc_bus, pbm.pbm_sel.pc_dev, pbm.pbm_sel.pc_func, pbm.pbm_reg, pbm.pbm_map_base, pbm.pbm_bar_off, pbm.pbm_bar_length, start, count, width); } switch (width) { case 1: db = (uint8_t *)(uintptr_t)((uintptr_t)pbm.pbm_map_base + pbm.pbm_bar_off + start * width); for (a = 0; a < count; a += width, db++) { res = fwrite(db, width, 1, stdout); if (res != 1) { errx(1, "error writing to stdout"); break; } } break; case 2: dh = (uint16_t *)(uintptr_t)((uintptr_t)pbm.pbm_map_base + pbm.pbm_bar_off + start * width); for (a = 0; a < count; a += width, dh++) { res = fwrite(dh, width, 1, stdout); if (res != 1) { errx(1, "error writing to stdout"); break; } } break; case 4: dd = (uint32_t *)(uintptr_t)((uintptr_t)pbm.pbm_map_base + pbm.pbm_bar_off + start * width); for (a = 0; a < count; a += width, dd++) { res = fwrite(dd, width, 1, stdout); if (res != 1) { errx(1, "error writing to stdout"); break; } } break; case 8: dx = (uint64_t *)(uintptr_t)((uintptr_t)pbm.pbm_map_base + pbm.pbm_bar_off + start * width); for (a = 0; a < count; a += width, dx++) { res = fwrite(dx, width, 1, stdout); if (res != 1) { errx(1, "error writing to stdout"); break; } } break; default: errx(1, "invalid access width"); } munmap((void *)pbm.pbm_map_base, pbm.pbm_map_length); close(fd); }