119102Sse/* 219102Sse * Copyright 1996 Massachusetts Institute of Technology 319102Sse * 419102Sse * Permission to use, copy, modify, and distribute this software and 519102Sse * its documentation for any purpose and without fee is hereby 619102Sse * granted, provided that both the above copyright notice and this 719102Sse * permission notice appear in all copies, that both the above 819102Sse * copyright notice and this permission notice appear in all 919102Sse * supporting documentation, and that the name of M.I.T. not be used 1019102Sse * in advertising or publicity pertaining to distribution of the 1119102Sse * software without specific, written prior permission. M.I.T. makes 1219102Sse * no representations about the suitability of this software for any 1319102Sse * purpose. It is provided "as is" without express or implied 1419102Sse * warranty. 15149223Sdes * 1619102Sse * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS 1719102Sse * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, 1819102Sse * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 1919102Sse * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 2019102Sse * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2119102Sse * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2219102Sse * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 2319102Sse * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 2419102Sse * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 2519102Sse * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 2619102Sse * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2719102Sse * SUCH DAMAGE. 2819102Sse */ 2919102Sse 3030172Scharnier#ifndef lint 3130172Scharnierstatic const char rcsid[] = 3250479Speter "$FreeBSD$"; 3330172Scharnier#endif /* not lint */ 3430172Scharnier 3519102Sse#include <sys/types.h> 3619102Sse#include <sys/fcntl.h> 3719102Sse 38261250Sjhb#include <assert.h> 39166435Sjhb#include <ctype.h> 4019102Sse#include <err.h> 41188018Sjhb#include <inttypes.h> 4219102Sse#include <stdlib.h> 4319102Sse#include <stdio.h> 4419102Sse#include <string.h> 4519102Sse#include <unistd.h> 4654322Sken#include <sys/pciio.h> 4769700Smsmith#include <sys/queue.h> 4819102Sse 4969785Smsmith#include <dev/pci/pcireg.h> 5069700Smsmith 5119102Sse#include "pathnames.h" 52166435Sjhb#include "pciconf.h" 5319102Sse 54149223Sdesstruct pci_device_info 5569700Smsmith{ 5669700Smsmith TAILQ_ENTRY(pci_device_info) link; 5769700Smsmith int id; 5869700Smsmith char *desc; 5969700Smsmith}; 6069700Smsmith 6169700Smsmithstruct pci_vendor_info 6269700Smsmith{ 6369700Smsmith TAILQ_ENTRY(pci_vendor_info) link; 6469700Smsmith TAILQ_HEAD(,pci_device_info) devs; 6569700Smsmith int id; 6669700Smsmith char *desc; 6769700Smsmith}; 6869700Smsmith 6969700SmsmithTAILQ_HEAD(,pci_vendor_info) pci_vendors; 7069700Smsmith 71261250Sjhbstatic struct pcisel getsel(const char *str); 72188018Sjhbstatic void list_bars(int fd, struct pci_conf *p); 73261250Sjhbstatic void list_devs(const char *name, int verbose, int bars, int caps, 74262134Sjhb int errors, int vpd); 7569700Smsmithstatic void list_verbose(struct pci_conf *p); 76262134Sjhbstatic void list_vpd(int fd, struct pci_conf *p); 77166435Sjhbstatic const char *guess_class(struct pci_conf *p); 78166435Sjhbstatic const char *guess_subclass(struct pci_conf *p); 7969700Smsmithstatic int load_vendors(void); 8019102Ssestatic void readit(const char *, const char *, int); 8119102Ssestatic void writeit(const char *, const char *, const char *, int); 82212329Sjhbstatic void chkattached(const char *); 8319102Sse 8450225Speterstatic int exitstatus = 0; 8519102Sse 8619102Ssestatic void 87166435Sjhbusage(void) 8830172Scharnier{ 8930172Scharnier fprintf(stderr, "%s\n%s\n%s\n%s\n", 90262134Sjhb "usage: pciconf -l [-bcevV] [device]", 91261250Sjhb " pciconf -a device", 92261250Sjhb " pciconf -r [-b | -h] device addr[:addr2]", 93261250Sjhb " pciconf -w [-b | -h] device addr value"); 9419817Sse exit (1); 9519102Sse} 9619102Sse 9719102Sseint 9819102Ssemain(int argc, char **argv) 9919102Sse{ 10019102Sse int c; 101236415Sjhb int listmode, readmode, writemode, attachedmode; 102262134Sjhb int bars, caps, errors, verbose, vpd; 10319102Sse int byte, isshort; 10419102Sse 105236415Sjhb listmode = readmode = writemode = attachedmode = 0; 106262134Sjhb bars = caps = errors = verbose = vpd = byte = isshort = 0; 10719102Sse 108262134Sjhb while ((c = getopt(argc, argv, "abcehlrwvV")) != -1) { 10919102Sse switch(c) { 11021935Sse case 'a': 11121935Sse attachedmode = 1; 11221935Sse break; 11321935Sse 114182707Simp case 'b': 115188018Sjhb bars = 1; 116182707Simp byte = 1; 117182707Simp break; 118182707Simp 119166435Sjhb case 'c': 120166435Sjhb caps = 1; 121166435Sjhb break; 122166435Sjhb 123236415Sjhb case 'e': 124236415Sjhb errors = 1; 125236415Sjhb break; 126236415Sjhb 127182707Simp case 'h': 128182707Simp isshort = 1; 129182707Simp break; 130182707Simp 13119102Sse case 'l': 13219102Sse listmode = 1; 13319102Sse break; 13419102Sse 13519102Sse case 'r': 13619102Sse readmode = 1; 13719102Sse break; 138149223Sdes 13919102Sse case 'w': 14019102Sse writemode = 1; 14119102Sse break; 14219102Sse 14368677Smsmith case 'v': 14469700Smsmith verbose = 1; 14568677Smsmith break; 14668677Smsmith 147262134Sjhb case 'V': 148262134Sjhb vpd = 1; 149262134Sjhb break; 150262134Sjhb 15119102Sse default: 15230172Scharnier usage(); 15319102Sse } 15419102Sse } 15519102Sse 156261250Sjhb if ((listmode && optind >= argc + 1) 15719102Sse || (writemode && optind + 3 != argc) 15821935Sse || (readmode && optind + 2 != argc) 159133271Simp || (attachedmode && optind + 1 != argc)) 16030172Scharnier usage(); 16119102Sse 16219102Sse if (listmode) { 163261250Sjhb list_devs(optind + 1 == argc ? argv[optind] : NULL, verbose, 164262134Sjhb bars, caps, errors, vpd); 16577513Simp } else if (attachedmode) { 166212329Sjhb chkattached(argv[optind]); 16777513Simp } else if (readmode) { 168149223Sdes readit(argv[optind], argv[optind + 1], 169182708Simp byte ? 1 : isshort ? 2 : 4); 17077513Simp } else if (writemode) { 17119102Sse writeit(argv[optind], argv[optind + 1], argv[optind + 2], 172182708Simp byte ? 1 : isshort ? 2 : 4); 17319102Sse } else { 174149223Sdes usage(); 17519102Sse } 17619102Sse 17721935Sse return exitstatus; 17819102Sse} 17919102Sse 18019102Ssestatic void 181262134Sjhblist_devs(const char *name, int verbose, int bars, int caps, int errors, 182262134Sjhb int vpd) 18319102Sse{ 18419102Sse int fd; 18519102Sse struct pci_conf_io pc; 18619102Sse struct pci_conf conf[255], *p; 187261250Sjhb struct pci_match_conf patterns[1]; 18841103Sken int none_count = 0; 18919102Sse 19069700Smsmith if (verbose) 19169700Smsmith load_vendors(); 19269700Smsmith 193236415Sjhb fd = open(_PATH_DEVPCI, (caps || errors) ? O_RDWR : O_RDONLY, 0); 19419102Sse if (fd < 0) 19519102Sse err(1, "%s", _PATH_DEVPCI); 19619102Sse 19739231Sgibbs bzero(&pc, sizeof(struct pci_conf_io)); 19839231Sgibbs pc.match_buf_len = sizeof(conf); 19939231Sgibbs pc.matches = conf; 200261250Sjhb if (name != NULL) { 201261250Sjhb bzero(&patterns, sizeof(patterns)); 202261250Sjhb patterns[0].pc_sel = getsel(name); 203261250Sjhb patterns[0].flags = PCI_GETCONF_MATCH_DOMAIN | 204261250Sjhb PCI_GETCONF_MATCH_BUS | PCI_GETCONF_MATCH_DEV | 205261250Sjhb PCI_GETCONF_MATCH_FUNC; 206261250Sjhb pc.num_patterns = 1; 207261250Sjhb pc.pat_buf_len = sizeof(patterns); 208261250Sjhb pc.patterns = patterns; 209261250Sjhb } 21019102Sse 21139231Sgibbs do { 21239231Sgibbs if (ioctl(fd, PCIOCGETCONF, &pc) == -1) 21339231Sgibbs err(1, "ioctl(PCIOCGETCONF)"); 21419102Sse 21539231Sgibbs /* 21639231Sgibbs * 255 entries should be more than enough for most people, 21739231Sgibbs * but if someone has more devices, and then changes things 218228990Suqs * around between ioctls, we'll do the cheesy thing and 21939231Sgibbs * just bail. The alternative would be to go back to the 22039231Sgibbs * beginning of the list, and print things twice, which may 221228990Suqs * not be desirable. 22239231Sgibbs */ 22339231Sgibbs if (pc.status == PCI_GETCONF_LIST_CHANGED) { 22439231Sgibbs warnx("PCI device list changed, please try again"); 22539231Sgibbs exitstatus = 1; 22639231Sgibbs close(fd); 22739231Sgibbs return; 22839231Sgibbs } else if (pc.status == PCI_GETCONF_ERROR) { 22953761Scharnier warnx("error returned from PCIOCGETCONF ioctl"); 23039231Sgibbs exitstatus = 1; 23139231Sgibbs close(fd); 23239231Sgibbs return; 23339231Sgibbs } 23439231Sgibbs for (p = conf; p < &conf[pc.num_matches]; p++) { 235172394Smarius printf("%s%d@pci%d:%d:%d:%d:\tclass=0x%06x card=0x%08x " 236182708Simp "chip=0x%08x rev=0x%02x hdr=0x%02x\n", 237182708Simp (p->pd_name && *p->pd_name) ? p->pd_name : 238182708Simp "none", 239182708Simp (p->pd_name && *p->pd_name) ? (int)p->pd_unit : 240182708Simp none_count++, p->pc_sel.pc_domain, 241182708Simp p->pc_sel.pc_bus, p->pc_sel.pc_dev, 242182708Simp p->pc_sel.pc_func, (p->pc_class << 16) | 243182708Simp (p->pc_subclass << 8) | p->pc_progif, 244182708Simp (p->pc_subdevice << 16) | p->pc_subvendor, 245182708Simp (p->pc_device << 16) | p->pc_vendor, 246182708Simp p->pc_revid, p->pc_hdr); 24769700Smsmith if (verbose) 24869700Smsmith list_verbose(p); 249188018Sjhb if (bars) 250188018Sjhb list_bars(fd, p); 251166435Sjhb if (caps) 252166435Sjhb list_caps(fd, p); 253236415Sjhb if (errors) 254236415Sjhb list_errors(fd, p); 255262134Sjhb if (vpd) 256262134Sjhb list_vpd(fd, p); 25739231Sgibbs } 25839231Sgibbs } while (pc.status == PCI_GETCONF_MORE_DEVS); 25939231Sgibbs 26019102Sse close(fd); 26119102Sse} 26219102Sse 26368677Smsmithstatic void 264188018Sjhblist_bars(int fd, struct pci_conf *p) 265188018Sjhb{ 266188018Sjhb struct pci_bar_io bar; 267188018Sjhb uint64_t base; 268188018Sjhb const char *type; 269188018Sjhb int i, range, max; 270188018Sjhb 271188018Sjhb switch (p->pc_hdr & PCIM_HDRTYPE) { 272188018Sjhb case PCIM_HDRTYPE_NORMAL: 273188018Sjhb max = PCIR_MAX_BAR_0; 274188018Sjhb break; 275188018Sjhb case PCIM_HDRTYPE_BRIDGE: 276188018Sjhb max = PCIR_MAX_BAR_1; 277188018Sjhb break; 278188018Sjhb case PCIM_HDRTYPE_CARDBUS: 279188018Sjhb max = PCIR_MAX_BAR_2; 280188018Sjhb break; 281188018Sjhb default: 282188018Sjhb return; 283188018Sjhb } 284188018Sjhb 285188018Sjhb for (i = 0; i <= max; i++) { 286188018Sjhb bar.pbi_sel = p->pc_sel; 287188018Sjhb bar.pbi_reg = PCIR_BAR(i); 288188018Sjhb if (ioctl(fd, PCIOCGETBAR, &bar) < 0) 289188018Sjhb continue; 290188018Sjhb if (PCI_BAR_IO(bar.pbi_base)) { 291188018Sjhb type = "I/O Port"; 292188018Sjhb range = 32; 293188018Sjhb base = bar.pbi_base & PCIM_BAR_IO_BASE; 294188018Sjhb } else { 295188018Sjhb if (bar.pbi_base & PCIM_BAR_MEM_PREFETCH) 296188018Sjhb type = "Prefetchable Memory"; 297188018Sjhb else 298188018Sjhb type = "Memory"; 299188018Sjhb switch (bar.pbi_base & PCIM_BAR_MEM_TYPE) { 300188018Sjhb case PCIM_BAR_MEM_32: 301188018Sjhb range = 32; 302188018Sjhb break; 303188018Sjhb case PCIM_BAR_MEM_1MB: 304188018Sjhb range = 20; 305188018Sjhb break; 306188018Sjhb case PCIM_BAR_MEM_64: 307188018Sjhb range = 64; 308188018Sjhb break; 309188018Sjhb default: 310188018Sjhb range = -1; 311188018Sjhb } 312188018Sjhb base = bar.pbi_base & ~((uint64_t)0xf); 313188018Sjhb } 314188018Sjhb printf(" bar [%02x] = type %s, range %2d, base %#jx, ", 315188018Sjhb PCIR_BAR(i), type, range, (uintmax_t)base); 316246632Sneel printf("size %ju, %s\n", (uintmax_t)bar.pbi_length, 317188018Sjhb bar.pbi_enabled ? "enabled" : "disabled"); 318188018Sjhb } 319188018Sjhb} 320188018Sjhb 321188018Sjhbstatic void 32269700Smsmithlist_verbose(struct pci_conf *p) 32368677Smsmith{ 32469700Smsmith struct pci_vendor_info *vi; 32569700Smsmith struct pci_device_info *di; 326166435Sjhb const char *dp; 327149223Sdes 32869700Smsmith TAILQ_FOREACH(vi, &pci_vendors, link) { 32969700Smsmith if (vi->id == p->pc_vendor) { 330166435Sjhb printf(" vendor = '%s'\n", vi->desc); 33168677Smsmith break; 33269700Smsmith } 33369700Smsmith } 33469700Smsmith if (vi == NULL) { 33569700Smsmith di = NULL; 33668677Smsmith } else { 33769700Smsmith TAILQ_FOREACH(di, &vi->devs, link) { 33869700Smsmith if (di->id == p->pc_device) { 339166435Sjhb printf(" device = '%s'\n", di->desc); 34069700Smsmith break; 34169700Smsmith } 34269700Smsmith } 34368677Smsmith } 34469700Smsmith if ((dp = guess_class(p)) != NULL) 345166435Sjhb printf(" class = %s\n", dp); 34669700Smsmith if ((dp = guess_subclass(p)) != NULL) 347166435Sjhb printf(" subclass = %s\n", dp); 34869700Smsmith} 34969700Smsmith 350262134Sjhbstatic void 351262134Sjhblist_vpd(int fd, struct pci_conf *p) 352262134Sjhb{ 353262134Sjhb struct pci_list_vpd_io list; 354262134Sjhb struct pci_vpd_element *vpd, *end; 355262134Sjhb 356262134Sjhb list.plvi_sel = p->pc_sel; 357262134Sjhb list.plvi_len = 0; 358262134Sjhb list.plvi_data = NULL; 359262134Sjhb if (ioctl(fd, PCIOCLISTVPD, &list) < 0 || list.plvi_len == 0) 360262134Sjhb return; 361262134Sjhb 362262134Sjhb list.plvi_data = malloc(list.plvi_len); 363262134Sjhb if (ioctl(fd, PCIOCLISTVPD, &list) < 0) { 364262134Sjhb free(list.plvi_data); 365262134Sjhb return; 366262134Sjhb } 367262134Sjhb 368262134Sjhb vpd = list.plvi_data; 369262134Sjhb end = (struct pci_vpd_element *)((char *)vpd + list.plvi_len); 370262134Sjhb for (; vpd < end; vpd = PVE_NEXT(vpd)) { 371262134Sjhb if (vpd->pve_flags == PVE_FLAG_IDENT) { 372262134Sjhb printf(" VPD ident = '%.*s'\n", 373262134Sjhb (int)vpd->pve_datalen, vpd->pve_data); 374262134Sjhb continue; 375262134Sjhb } 376262134Sjhb 377262134Sjhb /* Ignore the checksum keyword. */ 378262134Sjhb if (!(vpd->pve_flags & PVE_FLAG_RW) && 379262134Sjhb memcmp(vpd->pve_keyword, "RV", 2) == 0) 380262134Sjhb continue; 381262134Sjhb 382262134Sjhb /* Ignore remaining read-write space. */ 383262134Sjhb if (vpd->pve_flags & PVE_FLAG_RW && 384262134Sjhb memcmp(vpd->pve_keyword, "RW", 2) == 0) 385262134Sjhb continue; 386262134Sjhb 387262134Sjhb /* Handle extended capability keyword. */ 388262134Sjhb if (!(vpd->pve_flags & PVE_FLAG_RW) && 389262134Sjhb memcmp(vpd->pve_keyword, "CP", 2) == 0) { 390262134Sjhb printf(" VPD ro CP = ID %02x in map 0x%x[0x%x]\n", 391262134Sjhb (unsigned int)vpd->pve_data[0], 392262134Sjhb PCIR_BAR((unsigned int)vpd->pve_data[1]), 393262134Sjhb (unsigned int)vpd->pve_data[3] << 8 | 394262134Sjhb (unsigned int)vpd->pve_data[2]); 395262134Sjhb continue; 396262134Sjhb } 397262134Sjhb 398262134Sjhb /* Remaining keywords should all have ASCII values. */ 399262134Sjhb printf(" VPD %s %c%c = '%.*s'\n", 400262134Sjhb vpd->pve_flags & PVE_FLAG_RW ? "rw" : "ro", 401262134Sjhb vpd->pve_keyword[0], vpd->pve_keyword[1], 402262134Sjhb (int)vpd->pve_datalen, vpd->pve_data); 403262134Sjhb } 404262134Sjhb free(list.plvi_data); 405262134Sjhb} 406262134Sjhb 40769700Smsmith/* 40869700Smsmith * This is a direct cut-and-paste from the table in sys/dev/pci/pci.c. 40969700Smsmith */ 41069700Smsmithstatic struct 41169700Smsmith{ 41269700Smsmith int class; 41369700Smsmith int subclass; 414166435Sjhb const char *desc; 41569700Smsmith} pci_nomatch_tab[] = { 41669700Smsmith {PCIC_OLD, -1, "old"}, 41769700Smsmith {PCIC_OLD, PCIS_OLD_NONVGA, "non-VGA display device"}, 41869700Smsmith {PCIC_OLD, PCIS_OLD_VGA, "VGA-compatible display device"}, 41969700Smsmith {PCIC_STORAGE, -1, "mass storage"}, 42069700Smsmith {PCIC_STORAGE, PCIS_STORAGE_SCSI, "SCSI"}, 42169700Smsmith {PCIC_STORAGE, PCIS_STORAGE_IDE, "ATA"}, 42269700Smsmith {PCIC_STORAGE, PCIS_STORAGE_FLOPPY, "floppy disk"}, 42369700Smsmith {PCIC_STORAGE, PCIS_STORAGE_IPI, "IPI"}, 42469700Smsmith {PCIC_STORAGE, PCIS_STORAGE_RAID, "RAID"}, 425184936Smav {PCIC_STORAGE, PCIS_STORAGE_ATA_ADMA, "ATA (ADMA)"}, 426184936Smav {PCIC_STORAGE, PCIS_STORAGE_SATA, "SATA"}, 427184936Smav {PCIC_STORAGE, PCIS_STORAGE_SAS, "SAS"}, 428240739Sgavin {PCIC_STORAGE, PCIS_STORAGE_NVM, "NVM"}, 42969700Smsmith {PCIC_NETWORK, -1, "network"}, 43069700Smsmith {PCIC_NETWORK, PCIS_NETWORK_ETHERNET, "ethernet"}, 43169700Smsmith {PCIC_NETWORK, PCIS_NETWORK_TOKENRING, "token ring"}, 43269700Smsmith {PCIC_NETWORK, PCIS_NETWORK_FDDI, "fddi"}, 43369700Smsmith {PCIC_NETWORK, PCIS_NETWORK_ATM, "ATM"}, 434144156Sjmg {PCIC_NETWORK, PCIS_NETWORK_ISDN, "ISDN"}, 43569700Smsmith {PCIC_DISPLAY, -1, "display"}, 43669700Smsmith {PCIC_DISPLAY, PCIS_DISPLAY_VGA, "VGA"}, 43769700Smsmith {PCIC_DISPLAY, PCIS_DISPLAY_XGA, "XGA"}, 438144156Sjmg {PCIC_DISPLAY, PCIS_DISPLAY_3D, "3D"}, 43969700Smsmith {PCIC_MULTIMEDIA, -1, "multimedia"}, 44069700Smsmith {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_VIDEO, "video"}, 44169700Smsmith {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_AUDIO, "audio"}, 442144156Sjmg {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_TELE, "telephony"}, 443184142Smav {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_HDA, "HDA"}, 44469700Smsmith {PCIC_MEMORY, -1, "memory"}, 44569700Smsmith {PCIC_MEMORY, PCIS_MEMORY_RAM, "RAM"}, 44669700Smsmith {PCIC_MEMORY, PCIS_MEMORY_FLASH, "flash"}, 44769700Smsmith {PCIC_BRIDGE, -1, "bridge"}, 44869700Smsmith {PCIC_BRIDGE, PCIS_BRIDGE_HOST, "HOST-PCI"}, 44969700Smsmith {PCIC_BRIDGE, PCIS_BRIDGE_ISA, "PCI-ISA"}, 45069700Smsmith {PCIC_BRIDGE, PCIS_BRIDGE_EISA, "PCI-EISA"}, 45169700Smsmith {PCIC_BRIDGE, PCIS_BRIDGE_MCA, "PCI-MCA"}, 45269700Smsmith {PCIC_BRIDGE, PCIS_BRIDGE_PCI, "PCI-PCI"}, 45369700Smsmith {PCIC_BRIDGE, PCIS_BRIDGE_PCMCIA, "PCI-PCMCIA"}, 45469700Smsmith {PCIC_BRIDGE, PCIS_BRIDGE_NUBUS, "PCI-NuBus"}, 45569700Smsmith {PCIC_BRIDGE, PCIS_BRIDGE_CARDBUS, "PCI-CardBus"}, 456144156Sjmg {PCIC_BRIDGE, PCIS_BRIDGE_RACEWAY, "PCI-RACEway"}, 45769700Smsmith {PCIC_SIMPLECOMM, -1, "simple comms"}, 45869700Smsmith {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_UART, "UART"}, /* could detect 16550 */ 45969700Smsmith {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_PAR, "parallel port"}, 460144156Sjmg {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_MULSER, "multiport serial"}, 461144156Sjmg {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_MODEM, "generic modem"}, 46269700Smsmith {PCIC_BASEPERIPH, -1, "base peripheral"}, 46369700Smsmith {PCIC_BASEPERIPH, PCIS_BASEPERIPH_PIC, "interrupt controller"}, 46469700Smsmith {PCIC_BASEPERIPH, PCIS_BASEPERIPH_DMA, "DMA controller"}, 46569700Smsmith {PCIC_BASEPERIPH, PCIS_BASEPERIPH_TIMER, "timer"}, 46669700Smsmith {PCIC_BASEPERIPH, PCIS_BASEPERIPH_RTC, "realtime clock"}, 467144156Sjmg {PCIC_BASEPERIPH, PCIS_BASEPERIPH_PCIHOT, "PCI hot-plug controller"}, 468184140Smav {PCIC_BASEPERIPH, PCIS_BASEPERIPH_SDHC, "SD host controller"}, 469267002Smav {PCIC_BASEPERIPH, PCIS_BASEPERIPH_IOMMU, "IOMMU"}, 47069700Smsmith {PCIC_INPUTDEV, -1, "input device"}, 47169700Smsmith {PCIC_INPUTDEV, PCIS_INPUTDEV_KEYBOARD, "keyboard"}, 47269700Smsmith {PCIC_INPUTDEV, PCIS_INPUTDEV_DIGITIZER,"digitizer"}, 47369700Smsmith {PCIC_INPUTDEV, PCIS_INPUTDEV_MOUSE, "mouse"}, 474144156Sjmg {PCIC_INPUTDEV, PCIS_INPUTDEV_SCANNER, "scanner"}, 475144156Sjmg {PCIC_INPUTDEV, PCIS_INPUTDEV_GAMEPORT, "gameport"}, 47669700Smsmith {PCIC_DOCKING, -1, "docking station"}, 47769700Smsmith {PCIC_PROCESSOR, -1, "processor"}, 47869700Smsmith {PCIC_SERIALBUS, -1, "serial bus"}, 47969700Smsmith {PCIC_SERIALBUS, PCIS_SERIALBUS_FW, "FireWire"}, 480149223Sdes {PCIC_SERIALBUS, PCIS_SERIALBUS_ACCESS, "AccessBus"}, 48169700Smsmith {PCIC_SERIALBUS, PCIS_SERIALBUS_SSA, "SSA"}, 48269700Smsmith {PCIC_SERIALBUS, PCIS_SERIALBUS_USB, "USB"}, 48369700Smsmith {PCIC_SERIALBUS, PCIS_SERIALBUS_FC, "Fibre Channel"}, 48469700Smsmith {PCIC_SERIALBUS, PCIS_SERIALBUS_SMBUS, "SMBus"}, 485144156Sjmg {PCIC_WIRELESS, -1, "wireless controller"}, 486144156Sjmg {PCIC_WIRELESS, PCIS_WIRELESS_IRDA, "iRDA"}, 487144156Sjmg {PCIC_WIRELESS, PCIS_WIRELESS_IR, "IR"}, 488144156Sjmg {PCIC_WIRELESS, PCIS_WIRELESS_RF, "RF"}, 489144156Sjmg {PCIC_INTELLIIO, -1, "intelligent I/O controller"}, 490144156Sjmg {PCIC_INTELLIIO, PCIS_INTELLIIO_I2O, "I2O"}, 491144156Sjmg {PCIC_SATCOM, -1, "satellite communication"}, 492144156Sjmg {PCIC_SATCOM, PCIS_SATCOM_TV, "sat TV"}, 493144156Sjmg {PCIC_SATCOM, PCIS_SATCOM_AUDIO, "sat audio"}, 494144156Sjmg {PCIC_SATCOM, PCIS_SATCOM_VOICE, "sat voice"}, 495144156Sjmg {PCIC_SATCOM, PCIS_SATCOM_DATA, "sat data"}, 496144156Sjmg {PCIC_CRYPTO, -1, "encrypt/decrypt"}, 497144156Sjmg {PCIC_CRYPTO, PCIS_CRYPTO_NETCOMP, "network/computer crypto"}, 498144156Sjmg {PCIC_CRYPTO, PCIS_CRYPTO_NETCOMP, "entertainment crypto"}, 499144156Sjmg {PCIC_DASP, -1, "dasp"}, 500144156Sjmg {PCIC_DASP, PCIS_DASP_DPIO, "DPIO module"}, 50169700Smsmith {0, 0, NULL} 50269700Smsmith}; 50369700Smsmith 504166435Sjhbstatic const char * 50569700Smsmithguess_class(struct pci_conf *p) 50669700Smsmith{ 50769700Smsmith int i; 50869700Smsmith 50969700Smsmith for (i = 0; pci_nomatch_tab[i].desc != NULL; i++) { 51069700Smsmith if (pci_nomatch_tab[i].class == p->pc_class) 51169700Smsmith return(pci_nomatch_tab[i].desc); 51269700Smsmith } 51369700Smsmith return(NULL); 51469700Smsmith} 51569700Smsmith 516166435Sjhbstatic const char * 51769700Smsmithguess_subclass(struct pci_conf *p) 51869700Smsmith{ 51969700Smsmith int i; 52069700Smsmith 52169700Smsmith for (i = 0; pci_nomatch_tab[i].desc != NULL; i++) { 52269700Smsmith if ((pci_nomatch_tab[i].class == p->pc_class) && 52369700Smsmith (pci_nomatch_tab[i].subclass == p->pc_subclass)) 52469700Smsmith return(pci_nomatch_tab[i].desc); 52569700Smsmith } 52669700Smsmith return(NULL); 52769700Smsmith} 52869700Smsmith 52969700Smsmithstatic int 53069700Smsmithload_vendors(void) 53169700Smsmith{ 532166435Sjhb const char *dbf; 53369700Smsmith FILE *db; 53469700Smsmith struct pci_vendor_info *cv; 53569700Smsmith struct pci_device_info *cd; 536149225Sdes char buf[1024], str[1024]; 537149225Sdes char *ch; 53869700Smsmith int id, error; 53969700Smsmith 54069700Smsmith /* 54169700Smsmith * Locate the database and initialise. 54269700Smsmith */ 54369700Smsmith TAILQ_INIT(&pci_vendors); 54469700Smsmith if ((dbf = getenv("PCICONF_VENDOR_DATABASE")) == NULL) 54569700Smsmith dbf = _PATH_PCIVDB; 54669700Smsmith if ((db = fopen(dbf, "r")) == NULL) 54769700Smsmith return(1); 54869700Smsmith cv = NULL; 54969700Smsmith cd = NULL; 55069700Smsmith error = 0; 55169700Smsmith 55269700Smsmith /* 55369700Smsmith * Scan input lines from the database 55469700Smsmith */ 55569700Smsmith for (;;) { 55669700Smsmith if (fgets(buf, sizeof(buf), db) == NULL) 55768677Smsmith break; 55869700Smsmith 559149225Sdes if ((ch = strchr(buf, '#')) != NULL) 560149225Sdes *ch = '\0'; 561149225Sdes ch = strchr(buf, '\0') - 1; 562149225Sdes while (ch > buf && isspace(*ch)) 563149225Sdes *ch-- = '\0'; 564149225Sdes if (ch <= buf) 565149225Sdes continue; 566149225Sdes 567149225Sdes /* Can't handle subvendor / subdevice entries yet */ 568149225Sdes if (buf[0] == '\t' && buf[1] == '\t') 569149225Sdes continue; 570149225Sdes 57169700Smsmith /* Check for vendor entry */ 572149225Sdes if (buf[0] != '\t' && sscanf(buf, "%04x %[^\n]", &id, str) == 2) { 57369700Smsmith if ((id == 0) || (strlen(str) < 1)) 57469700Smsmith continue; 57569700Smsmith if ((cv = malloc(sizeof(struct pci_vendor_info))) == NULL) { 57669700Smsmith warn("allocating vendor entry"); 57769700Smsmith error = 1; 57869700Smsmith break; 57969700Smsmith } 58069700Smsmith if ((cv->desc = strdup(str)) == NULL) { 58169700Smsmith free(cv); 58269700Smsmith warn("allocating vendor description"); 58369700Smsmith error = 1; 58469700Smsmith break; 58569700Smsmith } 58669700Smsmith cv->id = id; 58769700Smsmith TAILQ_INIT(&cv->devs); 58869700Smsmith TAILQ_INSERT_TAIL(&pci_vendors, cv, link); 58969700Smsmith continue; 59069700Smsmith } 591149223Sdes 59269700Smsmith /* Check for device entry */ 593149225Sdes if (buf[0] == '\t' && sscanf(buf + 1, "%04x %[^\n]", &id, str) == 2) { 59469700Smsmith if ((id == 0) || (strlen(str) < 1)) 59569700Smsmith continue; 59669700Smsmith if (cv == NULL) { 59769700Smsmith warnx("device entry with no vendor!"); 59869700Smsmith continue; 59969700Smsmith } 60069700Smsmith if ((cd = malloc(sizeof(struct pci_device_info))) == NULL) { 60169700Smsmith warn("allocating device entry"); 60269700Smsmith error = 1; 60369700Smsmith break; 60469700Smsmith } 60569700Smsmith if ((cd->desc = strdup(str)) == NULL) { 60669700Smsmith free(cd); 60769700Smsmith warn("allocating device description"); 60869700Smsmith error = 1; 60969700Smsmith break; 61069700Smsmith } 61169700Smsmith cd->id = id; 61269700Smsmith TAILQ_INSERT_TAIL(&cv->devs, cd, link); 61369700Smsmith continue; 61469700Smsmith } 61569700Smsmith 61669700Smsmith /* It's a comment or junk, ignore it */ 61768677Smsmith } 61869700Smsmith if (ferror(db)) 61969700Smsmith error = 1; 62069700Smsmith fclose(db); 621149223Sdes 62269700Smsmith return(error); 62368677Smsmith} 62468677Smsmith 625166435Sjhbuint32_t 626166435Sjhbread_config(int fd, struct pcisel *sel, long reg, int width) 627166435Sjhb{ 628166435Sjhb struct pci_io pi; 62969700Smsmith 630166435Sjhb pi.pi_sel = *sel; 631166435Sjhb pi.pi_reg = reg; 632166435Sjhb pi.pi_width = width; 633166435Sjhb 634166435Sjhb if (ioctl(fd, PCIOCREAD, &pi) < 0) 635166435Sjhb err(1, "ioctl(PCIOCREAD)"); 636166435Sjhb 637166435Sjhb return (pi.pi_data); 638166435Sjhb} 639166435Sjhb 64019102Ssestatic struct pcisel 641261250Sjhbgetdevice(const char *name) 64219102Sse{ 643261250Sjhb struct pci_conf_io pc; 644261250Sjhb struct pci_conf conf[1]; 645261250Sjhb struct pci_match_conf patterns[1]; 646261250Sjhb char *cp; 647261250Sjhb int fd; 648261250Sjhb 649261250Sjhb fd = open(_PATH_DEVPCI, O_RDONLY, 0); 650261250Sjhb if (fd < 0) 651261250Sjhb err(1, "%s", _PATH_DEVPCI); 652261250Sjhb 653261250Sjhb bzero(&pc, sizeof(struct pci_conf_io)); 654261250Sjhb pc.match_buf_len = sizeof(conf); 655261250Sjhb pc.matches = conf; 656261250Sjhb 657261250Sjhb bzero(&patterns, sizeof(patterns)); 658261250Sjhb 659261250Sjhb /* 660261250Sjhb * The pattern structure requires the unit to be split out from 661261250Sjhb * the driver name. Walk backwards from the end of the name to 662261250Sjhb * find the start of the unit. 663261250Sjhb */ 664261250Sjhb if (name[0] == '\0') 665261250Sjhb err(1, "Empty device name"); 666261250Sjhb cp = strchr(name, '\0'); 667261250Sjhb assert(cp != NULL && cp != name); 668261250Sjhb cp--; 669261250Sjhb while (cp != name && isdigit(cp[-1])) 670261250Sjhb cp--; 671261250Sjhb if (cp == name) 672261250Sjhb errx(1, "Invalid device name"); 673261250Sjhb if ((size_t)(cp - name) + 1 > sizeof(patterns[0].pd_name)) 674261250Sjhb errx(1, "Device name i2s too long"); 675261250Sjhb memcpy(patterns[0].pd_name, name, cp - name); 676261250Sjhb patterns[0].pd_unit = strtol(cp, &cp, 10); 677261250Sjhb assert(*cp == '\0'); 678261250Sjhb patterns[0].flags = PCI_GETCONF_MATCH_NAME | PCI_GETCONF_MATCH_UNIT; 679261250Sjhb pc.num_patterns = 1; 680261250Sjhb pc.pat_buf_len = sizeof(patterns); 681261250Sjhb pc.patterns = patterns; 682261250Sjhb 683261250Sjhb if (ioctl(fd, PCIOCGETCONF, &pc) == -1) 684261250Sjhb err(1, "ioctl(PCIOCGETCONF)"); 685261250Sjhb if (pc.status != PCI_GETCONF_LAST_DEVICE && 686261250Sjhb pc.status != PCI_GETCONF_MORE_DEVS) 687261250Sjhb errx(1, "error returned from PCIOCGETCONF ioctl"); 688261250Sjhb close(fd); 689261250Sjhb if (pc.num_matches == 0) 690261250Sjhb errx(1, "Device not found"); 691261250Sjhb return (conf[0].pc_sel); 692261250Sjhb} 693261250Sjhb 694261250Sjhbstatic struct pcisel 695261250Sjhbparsesel(const char *str) 696261250Sjhb{ 697116640Sjmg char *ep = strchr(str, '@'); 698116640Sjmg char *epbase; 69919102Sse struct pcisel sel; 700172447Sse unsigned long selarr[4]; 701172447Sse int i; 702116640Sjmg 703116640Sjmg if (ep == NULL) 704116640Sjmg ep = (char *)str; 705116640Sjmg else 706116640Sjmg ep++; 707116640Sjmg 708116640Sjmg epbase = ep; 709116640Sjmg 71019102Sse if (strncmp(ep, "pci", 3) == 0) { 71119102Sse ep += 3; 712172447Sse i = 0; 713172447Sse do { 714172447Sse selarr[i++] = strtoul(ep, &ep, 10); 715172448Sse } while ((*ep == ':' || *ep == '.') && *++ep != '\0' && i < 4); 716172447Sse 717172447Sse if (i > 2) 718172447Sse sel.pc_func = selarr[--i]; 719172447Sse else 72019102Sse sel.pc_func = 0; 721172447Sse sel.pc_dev = selarr[--i]; 722172447Sse sel.pc_bus = selarr[--i]; 723172447Sse if (i > 0) 724172447Sse sel.pc_domain = selarr[--i]; 725172447Sse else 726172447Sse sel.pc_domain = 0; 72719102Sse } 728116640Sjmg if (*ep != '\x0' || ep == epbase) 72919102Sse errx(1, "cannot parse selector %s", str); 73019102Sse return sel; 73119102Sse} 73219102Sse 733261250Sjhbstatic struct pcisel 734261250Sjhbgetsel(const char *str) 735261250Sjhb{ 736261250Sjhb 737261250Sjhb /* 738261250Sjhb * No device names contain colons and selectors always contain 739261250Sjhb * at least one colon. 740261250Sjhb */ 741261250Sjhb if (strchr(str, ':') == NULL) 742261250Sjhb return (getdevice(str)); 743261250Sjhb else 744261250Sjhb return (parsesel(str)); 745261250Sjhb} 746261250Sjhb 74719102Ssestatic void 74877533Simpreadone(int fd, struct pcisel *sel, long reg, int width) 74919102Sse{ 75019102Sse 751166435Sjhb printf("%0*x", width*2, read_config(fd, sel, reg, width)); 75277533Simp} 75377533Simp 75477533Simpstatic void 75577533Simpreadit(const char *name, const char *reg, int width) 75677533Simp{ 75777533Simp long rstart; 75877533Simp long rend; 75977533Simp long r; 76077533Simp char *end; 76177533Simp int i; 76277533Simp int fd; 76377533Simp struct pcisel sel; 76477533Simp 76519102Sse fd = open(_PATH_DEVPCI, O_RDWR, 0); 76619102Sse if (fd < 0) 76719102Sse err(1, "%s", _PATH_DEVPCI); 76819102Sse 76977533Simp rend = rstart = strtol(reg, &end, 0); 77077533Simp if (end && *end == ':') { 77177533Simp end++; 77277533Simp rend = strtol(end, (char **) 0, 0); 77377533Simp } 77477533Simp sel = getsel(name); 775149223Sdes for (i = 1, r = rstart; r <= rend; i++, r += width) { 77677533Simp readone(fd, &sel, r, width); 777182708Simp if (i && !(i % 8)) 778182708Simp putchar(' '); 77991299Ssos putchar(i % (16/width) ? ' ' : '\n'); 78077533Simp } 78191299Ssos if (i % (16/width) != 1) 78277533Simp putchar('\n'); 78377533Simp close(fd); 78419102Sse} 78519102Sse 78619102Ssestatic void 78719102Ssewriteit(const char *name, const char *reg, const char *data, int width) 78819102Sse{ 78919102Sse int fd; 79019102Sse struct pci_io pi; 79119102Sse 79219102Sse pi.pi_sel = getsel(name); 79319102Sse pi.pi_reg = strtoul(reg, (char **)0, 0); /* XXX error check */ 79419102Sse pi.pi_width = width; 79519102Sse pi.pi_data = strtoul(data, (char **)0, 0); /* XXX error check */ 79619102Sse 79719102Sse fd = open(_PATH_DEVPCI, O_RDWR, 0); 79819102Sse if (fd < 0) 79919102Sse err(1, "%s", _PATH_DEVPCI); 80019102Sse 80119102Sse if (ioctl(fd, PCIOCWRITE, &pi) < 0) 80219102Sse err(1, "ioctl(PCIOCWRITE)"); 80319102Sse} 80421935Sse 80521935Ssestatic void 806212329Sjhbchkattached(const char *name) 80721935Sse{ 80821935Sse int fd; 80921935Sse struct pci_io pi; 81021935Sse 81121935Sse pi.pi_sel = getsel(name); 81221935Sse 81321935Sse fd = open(_PATH_DEVPCI, O_RDWR, 0); 81421935Sse if (fd < 0) 81521935Sse err(1, "%s", _PATH_DEVPCI); 81621935Sse 81721935Sse if (ioctl(fd, PCIOCATTACHED, &pi) < 0) 81821935Sse err(1, "ioctl(PCIOCATTACHED)"); 81921935Sse 82021935Sse exitstatus = pi.pi_data ? 0 : 2; /* exit(2), if NOT attached */ 82121935Sse printf("%s: %s%s\n", name, pi.pi_data == 0 ? "not " : "", "attached"); 82221935Sse} 823