1 /* 2 * Copyright 1996 Massachusetts Institute of Technology 3 * 4 * Permission to use, copy, modify, and distribute this software and 5 * its documentation for any purpose and without fee is hereby 6 * granted, provided that both the above copyright notice and this 7 * permission notice appear in all copies, that both the above 8 * copyright notice and this permission notice appear in all 9 * supporting documentation, and that the name of M.I.T. not be used 10 * in advertising or publicity pertaining to distribution of the 11 * software without specific, written prior permission. M.I.T. makes 12 * no representations about the suitability of this software for any 13 * purpose. It is provided "as is" without express or implied 14 * warranty. 15 * 16 * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS 17 * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, 18 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 20 * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 23 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 26 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #ifndef lint 31 static const char rcsid[] = 32 "$FreeBSD$"; 33 #endif /* not lint */ 34 35 #include <sys/types.h> 36 #include <sys/fcntl.h> 37 #include <sys/mman.h> 38 #include <sys/pciio.h> 39 #include <sys/queue.h> 40 41 #include <vm/vm.h> 42 43 #include <dev/pci/pcireg.h> 44 45 #include <assert.h> 46 #include <ctype.h> 47 #include <err.h> 48 #include <inttypes.h> 49 #include <stdbool.h> 50 #include <stdlib.h> 51 #include <stdio.h> 52 #include <string.h> 53 #include <unistd.h> 54 55 #include "pathnames.h" 56 #include "pciconf.h" 57 58 struct pci_device_info 59 { 60 TAILQ_ENTRY(pci_device_info) link; 61 int id; 62 char *desc; 63 }; 64 65 struct pci_vendor_info 66 { 67 TAILQ_ENTRY(pci_vendor_info) link; 68 TAILQ_HEAD(,pci_device_info) devs; 69 int id; 70 char *desc; 71 }; 72 73 static TAILQ_HEAD(,pci_vendor_info) pci_vendors; 74 75 static struct pcisel getsel(const char *str); 76 static void list_bridge(int fd, struct pci_conf *p); 77 static void list_bars(int fd, struct pci_conf *p); 78 static void list_devs(const char *name, int verbose, int bars, int bridge, 79 int caps, int errors, int vpd, int listmode); 80 static void list_verbose(struct pci_conf *p); 81 static void list_vpd(int fd, struct pci_conf *p); 82 static const char *guess_class(struct pci_conf *p); 83 static const char *guess_subclass(struct pci_conf *p); 84 static int load_vendors(void); 85 static void readit(const char *, const char *, int); 86 static void writeit(const char *, const char *, const char *, int); 87 static void chkattached(const char *); 88 static void dump_bar(const char *name, const char *reg, const char *bar_start, 89 const char *bar_count, int width, int verbose); 90 91 static int exitstatus = 0; 92 93 static void 94 usage(void) 95 { 96 97 fprintf(stderr, "%s", 98 "usage: pciconf -l [-BbcevV] [device]\n" 99 " pciconf -a device\n" 100 " pciconf -r [-b | -h] device addr[:addr2]\n" 101 " pciconf -w [-b | -h] device addr value\n" 102 " pciconf -D [-b | -h | -x] device bar [start [count]]" 103 "\n"); 104 exit(1); 105 } 106 107 int 108 main(int argc, char **argv) 109 { 110 int c, width; 111 int listmode, readmode, writemode, attachedmode, dumpbarmode; 112 int bars, bridge, caps, errors, verbose, vpd; 113 114 listmode = readmode = writemode = attachedmode = dumpbarmode = 0; 115 bars = bridge = caps = errors = verbose = vpd= 0; 116 width = 4; 117 118 while ((c = getopt(argc, argv, "aBbcDehlrwVv")) != -1) { 119 switch(c) { 120 case 'a': 121 attachedmode = 1; 122 break; 123 124 case 'B': 125 bridge = 1; 126 break; 127 128 case 'b': 129 bars = 1; 130 width = 1; 131 break; 132 133 case 'c': 134 caps++; 135 break; 136 137 case 'D': 138 dumpbarmode = 1; 139 break; 140 141 case 'e': 142 errors = 1; 143 break; 144 145 case 'h': 146 width = 2; 147 break; 148 149 case 'l': 150 listmode++; 151 break; 152 153 case 'r': 154 readmode = 1; 155 break; 156 157 case 'w': 158 writemode = 1; 159 break; 160 161 case 'v': 162 verbose = 1; 163 break; 164 165 case 'V': 166 vpd = 1; 167 break; 168 169 case 'x': 170 width = 8; 171 break; 172 173 default: 174 usage(); 175 } 176 } 177 178 if ((listmode && optind >= argc + 1) 179 || (writemode && optind + 3 != argc) 180 || (readmode && optind + 2 != argc) 181 || (attachedmode && optind + 1 != argc) 182 || (dumpbarmode && (optind + 2 > argc || optind + 4 < argc)) 183 || (width == 8 && !dumpbarmode)) 184 usage(); 185 186 if (listmode) { 187 list_devs(optind + 1 == argc ? argv[optind] : NULL, verbose, 188 bars, bridge, caps, errors, vpd, listmode); 189 } else if (attachedmode) { 190 chkattached(argv[optind]); 191 } else if (readmode) { 192 readit(argv[optind], argv[optind + 1], width); 193 } else if (writemode) { 194 writeit(argv[optind], argv[optind + 1], argv[optind + 2], 195 width); 196 } else if (dumpbarmode) { 197 dump_bar(argv[optind], argv[optind + 1], 198 optind + 2 < argc ? argv[optind + 2] : NULL, 199 optind + 3 < argc ? argv[optind + 3] : NULL, 200 width, verbose); 201 } else { 202 usage(); 203 } 204 205 return (exitstatus); 206 } 207 208 static void 209 list_devs(const char *name, int verbose, int bars, int bridge, int caps, 210 int errors, int vpd, int listmode) 211 { 212 int fd; 213 struct pci_conf_io pc; 214 struct pci_conf conf[255], *p; 215 struct pci_match_conf patterns[1]; 216 int none_count = 0; 217 218 if (verbose) 219 load_vendors(); 220 221 fd = open(_PATH_DEVPCI, (bridge || caps || errors) ? O_RDWR : O_RDONLY, 222 0); 223 if (fd < 0) 224 err(1, "%s", _PATH_DEVPCI); 225 226 bzero(&pc, sizeof(struct pci_conf_io)); 227 pc.match_buf_len = sizeof(conf); 228 pc.matches = conf; 229 if (name != NULL) { 230 bzero(&patterns, sizeof(patterns)); 231 patterns[0].pc_sel = getsel(name); 232 patterns[0].flags = PCI_GETCONF_MATCH_DOMAIN | 233 PCI_GETCONF_MATCH_BUS | PCI_GETCONF_MATCH_DEV | 234 PCI_GETCONF_MATCH_FUNC; 235 pc.num_patterns = 1; 236 pc.pat_buf_len = sizeof(patterns); 237 pc.patterns = patterns; 238 } 239 240 do { 241 if (ioctl(fd, PCIOCGETCONF, &pc) == -1) 242 err(1, "ioctl(PCIOCGETCONF)"); 243 244 /* 245 * 255 entries should be more than enough for most people, 246 * but if someone has more devices, and then changes things 247 * around between ioctls, we'll do the cheesy thing and 248 * just bail. The alternative would be to go back to the 249 * beginning of the list, and print things twice, which may 250 * not be desirable. 251 */ 252 if (pc.status == PCI_GETCONF_LIST_CHANGED) { 253 warnx("PCI device list changed, please try again"); 254 exitstatus = 1; 255 close(fd); 256 return; 257 } else if (pc.status == PCI_GETCONF_ERROR) { 258 warnx("error returned from PCIOCGETCONF ioctl"); 259 exitstatus = 1; 260 close(fd); 261 return; 262 } 263 if (listmode == 2) 264 printf("drv\tselector\tclass rev hdr vendor device " 265 "subven subdev\n"); 266 for (p = conf; p < &conf[pc.num_matches]; p++) { 267 if (listmode == 2) 268 printf("%s%d@pci%d:%d:%d:%d:" 269 "\t%06x %02x %02x %04x %04x %04x %04x\n", 270 *p->pd_name ? p->pd_name : "none", 271 *p->pd_name ? (int)p->pd_unit : 272 none_count++, p->pc_sel.pc_domain, 273 p->pc_sel.pc_bus, p->pc_sel.pc_dev, 274 p->pc_sel.pc_func, (p->pc_class << 16) | 275 (p->pc_subclass << 8) | p->pc_progif, 276 p->pc_revid, p->pc_hdr, 277 p->pc_vendor, p->pc_device, 278 p->pc_subvendor, p->pc_subdevice); 279 else 280 printf("%s%d@pci%d:%d:%d:%d:" 281 "\tclass=0x%06x rev=0x%02x hdr=0x%02x " 282 "vendor=0x%04x device=0x%04x " 283 "subvendor=0x%04x subdevice=0x%04x\n", 284 *p->pd_name ? p->pd_name : "none", 285 *p->pd_name ? (int)p->pd_unit : 286 none_count++, p->pc_sel.pc_domain, 287 p->pc_sel.pc_bus, p->pc_sel.pc_dev, 288 p->pc_sel.pc_func, (p->pc_class << 16) | 289 (p->pc_subclass << 8) | p->pc_progif, 290 p->pc_revid, p->pc_hdr, 291 p->pc_vendor, p->pc_device, 292 p->pc_subvendor, p->pc_subdevice); 293 if (verbose) 294 list_verbose(p); 295 if (bars) 296 list_bars(fd, p); 297 if (bridge) 298 list_bridge(fd, p); 299 if (caps) 300 list_caps(fd, p, caps); 301 if (errors) 302 list_errors(fd, p); 303 if (vpd) 304 list_vpd(fd, p); 305 } 306 } while (pc.status == PCI_GETCONF_MORE_DEVS); 307 308 close(fd); 309 } 310 311 static void 312 print_bus_range(int fd, struct pci_conf *p, int secreg, int subreg) 313 { 314 uint8_t secbus, subbus; 315 316 secbus = read_config(fd, &p->pc_sel, secreg, 1); 317 subbus = read_config(fd, &p->pc_sel, subreg, 1); 318 printf(" bus range = %u-%u\n", secbus, subbus); 319 } 320 321 static void 322 print_window(int reg, const char *type, int range, uint64_t base, 323 uint64_t limit) 324 { 325 326 printf(" window[%02x] = type %s, range %2d, addr %#jx-%#jx, %s\n", 327 reg, type, range, (uintmax_t)base, (uintmax_t)limit, 328 base < limit ? "enabled" : "disabled"); 329 } 330 331 static void 332 print_special_decode(bool isa, bool vga, bool subtractive) 333 { 334 bool comma; 335 336 if (isa || vga || subtractive) { 337 comma = false; 338 printf(" decode = "); 339 if (isa) { 340 printf("ISA"); 341 comma = true; 342 } 343 if (vga) { 344 printf("%sVGA", comma ? ", " : ""); 345 comma = true; 346 } 347 if (subtractive) 348 printf("%ssubtractive", comma ? ", " : ""); 349 printf("\n"); 350 } 351 } 352 353 static void 354 print_bridge_windows(int fd, struct pci_conf *p) 355 { 356 uint64_t base, limit; 357 uint32_t val; 358 uint16_t bctl; 359 bool subtractive; 360 int range; 361 362 /* 363 * XXX: This assumes that a window with a base and limit of 0 364 * is not implemented. In theory a window might be programmed 365 * at the smallest size with a base of 0, but those do not seem 366 * common in practice. 367 */ 368 val = read_config(fd, &p->pc_sel, PCIR_IOBASEL_1, 1); 369 if (val != 0 || read_config(fd, &p->pc_sel, PCIR_IOLIMITL_1, 1) != 0) { 370 if ((val & PCIM_BRIO_MASK) == PCIM_BRIO_32) { 371 base = PCI_PPBIOBASE( 372 read_config(fd, &p->pc_sel, PCIR_IOBASEH_1, 2), 373 val); 374 limit = PCI_PPBIOLIMIT( 375 read_config(fd, &p->pc_sel, PCIR_IOLIMITH_1, 2), 376 read_config(fd, &p->pc_sel, PCIR_IOLIMITL_1, 1)); 377 range = 32; 378 } else { 379 base = PCI_PPBIOBASE(0, val); 380 limit = PCI_PPBIOLIMIT(0, 381 read_config(fd, &p->pc_sel, PCIR_IOLIMITL_1, 1)); 382 range = 16; 383 } 384 print_window(PCIR_IOBASEL_1, "I/O Port", range, base, limit); 385 } 386 387 base = PCI_PPBMEMBASE(0, 388 read_config(fd, &p->pc_sel, PCIR_MEMBASE_1, 2)); 389 limit = PCI_PPBMEMLIMIT(0, 390 read_config(fd, &p->pc_sel, PCIR_MEMLIMIT_1, 2)); 391 print_window(PCIR_MEMBASE_1, "Memory", 32, base, limit); 392 393 val = read_config(fd, &p->pc_sel, PCIR_PMBASEL_1, 2); 394 if (val != 0 || read_config(fd, &p->pc_sel, PCIR_PMLIMITL_1, 2) != 0) { 395 if ((val & PCIM_BRPM_MASK) == PCIM_BRPM_64) { 396 base = PCI_PPBMEMBASE( 397 read_config(fd, &p->pc_sel, PCIR_PMBASEH_1, 4), 398 val); 399 limit = PCI_PPBMEMLIMIT( 400 read_config(fd, &p->pc_sel, PCIR_PMLIMITH_1, 4), 401 read_config(fd, &p->pc_sel, PCIR_PMLIMITL_1, 2)); 402 range = 64; 403 } else { 404 base = PCI_PPBMEMBASE(0, val); 405 limit = PCI_PPBMEMLIMIT(0, 406 read_config(fd, &p->pc_sel, PCIR_PMLIMITL_1, 2)); 407 range = 32; 408 } 409 print_window(PCIR_PMBASEL_1, "Prefetchable Memory", range, base, 410 limit); 411 } 412 413 /* 414 * XXX: This list of bridges that are subtractive but do not set 415 * progif to indicate it is copied from pci_pci.c. 416 */ 417 subtractive = p->pc_progif == PCIP_BRIDGE_PCI_SUBTRACTIVE; 418 switch (p->pc_device << 16 | p->pc_vendor) { 419 case 0xa002177d: /* Cavium ThunderX */ 420 case 0x124b8086: /* Intel 82380FB Mobile */ 421 case 0x060513d7: /* Toshiba ???? */ 422 subtractive = true; 423 } 424 if (p->pc_vendor == 0x8086 && (p->pc_device & 0xff00) == 0x2400) 425 subtractive = true; 426 427 bctl = read_config(fd, &p->pc_sel, PCIR_BRIDGECTL_1, 2); 428 print_special_decode(bctl & PCIB_BCR_ISA_ENABLE, 429 bctl & PCIB_BCR_VGA_ENABLE, subtractive); 430 } 431 432 static void 433 print_cardbus_mem_window(int fd, struct pci_conf *p, int basereg, int limitreg, 434 bool prefetch) 435 { 436 437 print_window(basereg, prefetch ? "Prefetchable Memory" : "Memory", 32, 438 PCI_CBBMEMBASE(read_config(fd, &p->pc_sel, basereg, 4)), 439 PCI_CBBMEMLIMIT(read_config(fd, &p->pc_sel, limitreg, 4))); 440 } 441 442 static void 443 print_cardbus_io_window(int fd, struct pci_conf *p, int basereg, int limitreg) 444 { 445 uint32_t base, limit; 446 uint32_t val; 447 int range; 448 449 val = read_config(fd, &p->pc_sel, basereg, 2); 450 if ((val & PCIM_CBBIO_MASK) == PCIM_CBBIO_32) { 451 base = PCI_CBBIOBASE(read_config(fd, &p->pc_sel, basereg, 4)); 452 limit = PCI_CBBIOBASE(read_config(fd, &p->pc_sel, limitreg, 4)); 453 range = 32; 454 } else { 455 base = PCI_CBBIOBASE(val); 456 limit = PCI_CBBIOBASE(read_config(fd, &p->pc_sel, limitreg, 2)); 457 range = 16; 458 } 459 print_window(basereg, "I/O Port", range, base, limit); 460 } 461 462 static void 463 print_cardbus_windows(int fd, struct pci_conf *p) 464 { 465 uint16_t bctl; 466 467 bctl = read_config(fd, &p->pc_sel, PCIR_BRIDGECTL_2, 2); 468 print_cardbus_mem_window(fd, p, PCIR_MEMBASE0_2, PCIR_MEMLIMIT0_2, 469 bctl & CBB_BCR_PREFETCH_0_ENABLE); 470 print_cardbus_mem_window(fd, p, PCIR_MEMBASE1_2, PCIR_MEMLIMIT1_2, 471 bctl & CBB_BCR_PREFETCH_1_ENABLE); 472 print_cardbus_io_window(fd, p, PCIR_IOBASE0_2, PCIR_IOLIMIT0_2); 473 print_cardbus_io_window(fd, p, PCIR_IOBASE1_2, PCIR_IOLIMIT1_2); 474 print_special_decode(bctl & CBB_BCR_ISA_ENABLE, 475 bctl & CBB_BCR_VGA_ENABLE, false); 476 } 477 478 static void 479 list_bridge(int fd, struct pci_conf *p) 480 { 481 482 switch (p->pc_hdr & PCIM_HDRTYPE) { 483 case PCIM_HDRTYPE_BRIDGE: 484 print_bus_range(fd, p, PCIR_SECBUS_1, PCIR_SUBBUS_1); 485 print_bridge_windows(fd, p); 486 break; 487 case PCIM_HDRTYPE_CARDBUS: 488 print_bus_range(fd, p, PCIR_SECBUS_2, PCIR_SUBBUS_2); 489 print_cardbus_windows(fd, p); 490 break; 491 } 492 } 493 494 static void 495 list_bars(int fd, struct pci_conf *p) 496 { 497 int i, max; 498 499 switch (p->pc_hdr & PCIM_HDRTYPE) { 500 case PCIM_HDRTYPE_NORMAL: 501 max = PCIR_MAX_BAR_0; 502 break; 503 case PCIM_HDRTYPE_BRIDGE: 504 max = PCIR_MAX_BAR_1; 505 break; 506 case PCIM_HDRTYPE_CARDBUS: 507 max = PCIR_MAX_BAR_2; 508 break; 509 default: 510 return; 511 } 512 513 for (i = 0; i <= max; i++) 514 print_bar(fd, p, "bar ", PCIR_BAR(i)); 515 } 516 517 void 518 print_bar(int fd, struct pci_conf *p, const char *label, uint16_t bar_offset) 519 { 520 uint64_t base; 521 const char *type; 522 struct pci_bar_io bar; 523 int range; 524 525 bar.pbi_sel = p->pc_sel; 526 bar.pbi_reg = bar_offset; 527 if (ioctl(fd, PCIOCGETBAR, &bar) < 0) 528 return; 529 if (PCI_BAR_IO(bar.pbi_base)) { 530 type = "I/O Port"; 531 range = 32; 532 base = bar.pbi_base & PCIM_BAR_IO_BASE; 533 } else { 534 if (bar.pbi_base & PCIM_BAR_MEM_PREFETCH) 535 type = "Prefetchable Memory"; 536 else 537 type = "Memory"; 538 switch (bar.pbi_base & PCIM_BAR_MEM_TYPE) { 539 case PCIM_BAR_MEM_32: 540 range = 32; 541 break; 542 case PCIM_BAR_MEM_1MB: 543 range = 20; 544 break; 545 case PCIM_BAR_MEM_64: 546 range = 64; 547 break; 548 default: 549 range = -1; 550 } 551 base = bar.pbi_base & ~((uint64_t)0xf); 552 } 553 printf(" %s[%02x] = type %s, range %2d, base %#jx, ", 554 label, bar_offset, type, range, (uintmax_t)base); 555 printf("size %ju, %s\n", (uintmax_t)bar.pbi_length, 556 bar.pbi_enabled ? "enabled" : "disabled"); 557 } 558 559 static void 560 list_verbose(struct pci_conf *p) 561 { 562 struct pci_vendor_info *vi; 563 struct pci_device_info *di; 564 const char *dp; 565 566 TAILQ_FOREACH(vi, &pci_vendors, link) { 567 if (vi->id == p->pc_vendor) { 568 printf(" vendor = '%s'\n", vi->desc); 569 break; 570 } 571 } 572 if (vi == NULL) { 573 di = NULL; 574 } else { 575 TAILQ_FOREACH(di, &vi->devs, link) { 576 if (di->id == p->pc_device) { 577 printf(" device = '%s'\n", di->desc); 578 break; 579 } 580 } 581 } 582 if ((dp = guess_class(p)) != NULL) 583 printf(" class = %s\n", dp); 584 if ((dp = guess_subclass(p)) != NULL) 585 printf(" subclass = %s\n", dp); 586 } 587 588 static void 589 list_vpd(int fd, struct pci_conf *p) 590 { 591 struct pci_list_vpd_io list; 592 struct pci_vpd_element *vpd, *end; 593 594 list.plvi_sel = p->pc_sel; 595 list.plvi_len = 0; 596 list.plvi_data = NULL; 597 if (ioctl(fd, PCIOCLISTVPD, &list) < 0 || list.plvi_len == 0) 598 return; 599 600 list.plvi_data = malloc(list.plvi_len); 601 if (ioctl(fd, PCIOCLISTVPD, &list) < 0) { 602 free(list.plvi_data); 603 return; 604 } 605 606 vpd = list.plvi_data; 607 end = (struct pci_vpd_element *)((char *)vpd + list.plvi_len); 608 for (; vpd < end; vpd = PVE_NEXT(vpd)) { 609 if (vpd->pve_flags == PVE_FLAG_IDENT) { 610 printf(" VPD ident = '%.*s'\n", 611 (int)vpd->pve_datalen, vpd->pve_data); 612 continue; 613 } 614 615 /* Ignore the checksum keyword. */ 616 if (!(vpd->pve_flags & PVE_FLAG_RW) && 617 memcmp(vpd->pve_keyword, "RV", 2) == 0) 618 continue; 619 620 /* Ignore remaining read-write space. */ 621 if (vpd->pve_flags & PVE_FLAG_RW && 622 memcmp(vpd->pve_keyword, "RW", 2) == 0) 623 continue; 624 625 /* Handle extended capability keyword. */ 626 if (!(vpd->pve_flags & PVE_FLAG_RW) && 627 memcmp(vpd->pve_keyword, "CP", 2) == 0) { 628 printf(" VPD ro CP = ID %02x in map 0x%x[0x%x]\n", 629 (unsigned int)vpd->pve_data[0], 630 PCIR_BAR((unsigned int)vpd->pve_data[1]), 631 (unsigned int)vpd->pve_data[3] << 8 | 632 (unsigned int)vpd->pve_data[2]); 633 continue; 634 } 635 636 /* Remaining keywords should all have ASCII values. */ 637 printf(" VPD %s %c%c = '%.*s'\n", 638 vpd->pve_flags & PVE_FLAG_RW ? "rw" : "ro", 639 vpd->pve_keyword[0], vpd->pve_keyword[1], 640 (int)vpd->pve_datalen, vpd->pve_data); 641 } 642 free(list.plvi_data); 643 } 644 645 /* 646 * This is a direct cut-and-paste from the table in sys/dev/pci/pci.c. 647 */ 648 static struct 649 { 650 int class; 651 int subclass; 652 const char *desc; 653 } pci_nomatch_tab[] = { 654 {PCIC_OLD, -1, "old"}, 655 {PCIC_OLD, PCIS_OLD_NONVGA, "non-VGA display device"}, 656 {PCIC_OLD, PCIS_OLD_VGA, "VGA-compatible display device"}, 657 {PCIC_STORAGE, -1, "mass storage"}, 658 {PCIC_STORAGE, PCIS_STORAGE_SCSI, "SCSI"}, 659 {PCIC_STORAGE, PCIS_STORAGE_IDE, "ATA"}, 660 {PCIC_STORAGE, PCIS_STORAGE_FLOPPY, "floppy disk"}, 661 {PCIC_STORAGE, PCIS_STORAGE_IPI, "IPI"}, 662 {PCIC_STORAGE, PCIS_STORAGE_RAID, "RAID"}, 663 {PCIC_STORAGE, PCIS_STORAGE_ATA_ADMA, "ATA (ADMA)"}, 664 {PCIC_STORAGE, PCIS_STORAGE_SATA, "SATA"}, 665 {PCIC_STORAGE, PCIS_STORAGE_SAS, "SAS"}, 666 {PCIC_STORAGE, PCIS_STORAGE_NVM, "NVM"}, 667 {PCIC_NETWORK, -1, "network"}, 668 {PCIC_NETWORK, PCIS_NETWORK_ETHERNET, "ethernet"}, 669 {PCIC_NETWORK, PCIS_NETWORK_TOKENRING, "token ring"}, 670 {PCIC_NETWORK, PCIS_NETWORK_FDDI, "fddi"}, 671 {PCIC_NETWORK, PCIS_NETWORK_ATM, "ATM"}, 672 {PCIC_NETWORK, PCIS_NETWORK_ISDN, "ISDN"}, 673 {PCIC_DISPLAY, -1, "display"}, 674 {PCIC_DISPLAY, PCIS_DISPLAY_VGA, "VGA"}, 675 {PCIC_DISPLAY, PCIS_DISPLAY_XGA, "XGA"}, 676 {PCIC_DISPLAY, PCIS_DISPLAY_3D, "3D"}, 677 {PCIC_MULTIMEDIA, -1, "multimedia"}, 678 {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_VIDEO, "video"}, 679 {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_AUDIO, "audio"}, 680 {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_TELE, "telephony"}, 681 {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_HDA, "HDA"}, 682 {PCIC_MEMORY, -1, "memory"}, 683 {PCIC_MEMORY, PCIS_MEMORY_RAM, "RAM"}, 684 {PCIC_MEMORY, PCIS_MEMORY_FLASH, "flash"}, 685 {PCIC_BRIDGE, -1, "bridge"}, 686 {PCIC_BRIDGE, PCIS_BRIDGE_HOST, "HOST-PCI"}, 687 {PCIC_BRIDGE, PCIS_BRIDGE_ISA, "PCI-ISA"}, 688 {PCIC_BRIDGE, PCIS_BRIDGE_EISA, "PCI-EISA"}, 689 {PCIC_BRIDGE, PCIS_BRIDGE_MCA, "PCI-MCA"}, 690 {PCIC_BRIDGE, PCIS_BRIDGE_PCI, "PCI-PCI"}, 691 {PCIC_BRIDGE, PCIS_BRIDGE_PCMCIA, "PCI-PCMCIA"}, 692 {PCIC_BRIDGE, PCIS_BRIDGE_NUBUS, "PCI-NuBus"}, 693 {PCIC_BRIDGE, PCIS_BRIDGE_CARDBUS, "PCI-CardBus"}, 694 {PCIC_BRIDGE, PCIS_BRIDGE_RACEWAY, "PCI-RACEway"}, 695 {PCIC_SIMPLECOMM, -1, "simple comms"}, 696 {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_UART, "UART"}, /* could detect 16550 */ 697 {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_PAR, "parallel port"}, 698 {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_MULSER, "multiport serial"}, 699 {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_MODEM, "generic modem"}, 700 {PCIC_BASEPERIPH, -1, "base peripheral"}, 701 {PCIC_BASEPERIPH, PCIS_BASEPERIPH_PIC, "interrupt controller"}, 702 {PCIC_BASEPERIPH, PCIS_BASEPERIPH_DMA, "DMA controller"}, 703 {PCIC_BASEPERIPH, PCIS_BASEPERIPH_TIMER, "timer"}, 704 {PCIC_BASEPERIPH, PCIS_BASEPERIPH_RTC, "realtime clock"}, 705 {PCIC_BASEPERIPH, PCIS_BASEPERIPH_PCIHOT, "PCI hot-plug controller"}, 706 {PCIC_BASEPERIPH, PCIS_BASEPERIPH_SDHC, "SD host controller"}, 707 {PCIC_BASEPERIPH, PCIS_BASEPERIPH_IOMMU, "IOMMU"}, 708 {PCIC_INPUTDEV, -1, "input device"}, 709 {PCIC_INPUTDEV, PCIS_INPUTDEV_KEYBOARD, "keyboard"}, 710 {PCIC_INPUTDEV, PCIS_INPUTDEV_DIGITIZER,"digitizer"}, 711 {PCIC_INPUTDEV, PCIS_INPUTDEV_MOUSE, "mouse"}, 712 {PCIC_INPUTDEV, PCIS_INPUTDEV_SCANNER, "scanner"}, 713 {PCIC_INPUTDEV, PCIS_INPUTDEV_GAMEPORT, "gameport"}, 714 {PCIC_DOCKING, -1, "docking station"}, 715 {PCIC_PROCESSOR, -1, "processor"}, 716 {PCIC_SERIALBUS, -1, "serial bus"}, 717 {PCIC_SERIALBUS, PCIS_SERIALBUS_FW, "FireWire"}, 718 {PCIC_SERIALBUS, PCIS_SERIALBUS_ACCESS, "AccessBus"}, 719 {PCIC_SERIALBUS, PCIS_SERIALBUS_SSA, "SSA"}, 720 {PCIC_SERIALBUS, PCIS_SERIALBUS_USB, "USB"}, 721 {PCIC_SERIALBUS, PCIS_SERIALBUS_FC, "Fibre Channel"}, 722 {PCIC_SERIALBUS, PCIS_SERIALBUS_SMBUS, "SMBus"}, 723 {PCIC_WIRELESS, -1, "wireless controller"}, 724 {PCIC_WIRELESS, PCIS_WIRELESS_IRDA, "iRDA"}, 725 {PCIC_WIRELESS, PCIS_WIRELESS_IR, "IR"}, 726 {PCIC_WIRELESS, PCIS_WIRELESS_RF, "RF"}, 727 {PCIC_INTELLIIO, -1, "intelligent I/O controller"}, 728 {PCIC_INTELLIIO, PCIS_INTELLIIO_I2O, "I2O"}, 729 {PCIC_SATCOM, -1, "satellite communication"}, 730 {PCIC_SATCOM, PCIS_SATCOM_TV, "sat TV"}, 731 {PCIC_SATCOM, PCIS_SATCOM_AUDIO, "sat audio"}, 732 {PCIC_SATCOM, PCIS_SATCOM_VOICE, "sat voice"}, 733 {PCIC_SATCOM, PCIS_SATCOM_DATA, "sat data"}, 734 {PCIC_CRYPTO, -1, "encrypt/decrypt"}, 735 {PCIC_CRYPTO, PCIS_CRYPTO_NETCOMP, "network/computer crypto"}, 736 {PCIC_CRYPTO, PCIS_CRYPTO_NETCOMP, "entertainment crypto"}, 737 {PCIC_DASP, -1, "dasp"}, 738 {PCIC_DASP, PCIS_DASP_DPIO, "DPIO module"}, 739 {PCIC_DASP, PCIS_DASP_PERFCNTRS, "performance counters"}, 740 {PCIC_DASP, PCIS_DASP_COMM_SYNC, "communication synchronizer"}, 741 {PCIC_DASP, PCIS_DASP_MGMT_CARD, "signal processing management"}, 742 {PCIC_ACCEL, -1, "processing accelerators"}, 743 {PCIC_ACCEL, PCIS_ACCEL_PROCESSING, "processing accelerators"}, 744 {PCIC_INSTRUMENT, -1, "non-essential instrumentation"}, 745 {0, 0, NULL} 746 }; 747 748 static const char * 749 guess_class(struct pci_conf *p) 750 { 751 int i; 752 753 for (i = 0; pci_nomatch_tab[i].desc != NULL; i++) { 754 if (pci_nomatch_tab[i].class == p->pc_class) 755 return(pci_nomatch_tab[i].desc); 756 } 757 return(NULL); 758 } 759 760 static const char * 761 guess_subclass(struct pci_conf *p) 762 { 763 int i; 764 765 for (i = 0; pci_nomatch_tab[i].desc != NULL; i++) { 766 if ((pci_nomatch_tab[i].class == p->pc_class) && 767 (pci_nomatch_tab[i].subclass == p->pc_subclass)) 768 return(pci_nomatch_tab[i].desc); 769 } 770 return(NULL); 771 } 772 773 static int 774 load_vendors(void) 775 { 776 const char *dbf; 777 FILE *db; 778 struct pci_vendor_info *cv; 779 struct pci_device_info *cd; 780 char buf[1024], str[1024]; 781 char *ch; 782 int id, error; 783 784 /* 785 * Locate the database and initialise. 786 */ 787 TAILQ_INIT(&pci_vendors); 788 if ((dbf = getenv("PCICONF_VENDOR_DATABASE")) == NULL) 789 dbf = _PATH_LPCIVDB; 790 if ((db = fopen(dbf, "r")) == NULL) { 791 dbf = _PATH_PCIVDB; 792 if ((db = fopen(dbf, "r")) == NULL) 793 return(1); 794 } 795 cv = NULL; 796 cd = NULL; 797 error = 0; 798 799 /* 800 * Scan input lines from the database 801 */ 802 for (;;) { 803 if (fgets(buf, sizeof(buf), db) == NULL) 804 break; 805 806 if ((ch = strchr(buf, '#')) != NULL) 807 *ch = '\0'; 808 ch = strchr(buf, '\0') - 1; 809 while (ch > buf && isspace(*ch)) 810 *ch-- = '\0'; 811 if (ch <= buf) 812 continue; 813 814 /* Can't handle subvendor / subdevice entries yet */ 815 if (buf[0] == '\t' && buf[1] == '\t') 816 continue; 817 818 /* Check for vendor entry */ 819 if (buf[0] != '\t' && sscanf(buf, "%04x %[^\n]", &id, str) == 2) { 820 if ((id == 0) || (strlen(str) < 1)) 821 continue; 822 if ((cv = malloc(sizeof(struct pci_vendor_info))) == NULL) { 823 warn("allocating vendor entry"); 824 error = 1; 825 break; 826 } 827 if ((cv->desc = strdup(str)) == NULL) { 828 free(cv); 829 warn("allocating vendor description"); 830 error = 1; 831 break; 832 } 833 cv->id = id; 834 TAILQ_INIT(&cv->devs); 835 TAILQ_INSERT_TAIL(&pci_vendors, cv, link); 836 continue; 837 } 838 839 /* Check for device entry */ 840 if (buf[0] == '\t' && sscanf(buf + 1, "%04x %[^\n]", &id, str) == 2) { 841 if ((id == 0) || (strlen(str) < 1)) 842 continue; 843 if (cv == NULL) { 844 warnx("device entry with no vendor!"); 845 continue; 846 } 847 if ((cd = malloc(sizeof(struct pci_device_info))) == NULL) { 848 warn("allocating device entry"); 849 error = 1; 850 break; 851 } 852 if ((cd->desc = strdup(str)) == NULL) { 853 free(cd); 854 warn("allocating device description"); 855 error = 1; 856 break; 857 } 858 cd->id = id; 859 TAILQ_INSERT_TAIL(&cv->devs, cd, link); 860 continue; 861 } 862 863 /* It's a comment or junk, ignore it */ 864 } 865 if (ferror(db)) 866 error = 1; 867 fclose(db); 868 869 return(error); 870 } 871 872 uint32_t 873 read_config(int fd, struct pcisel *sel, long reg, int width) 874 { 875 struct pci_io pi; 876 877 pi.pi_sel = *sel; 878 pi.pi_reg = reg; 879 pi.pi_width = width; 880 881 if (ioctl(fd, PCIOCREAD, &pi) < 0) 882 err(1, "ioctl(PCIOCREAD)"); 883 884 return (pi.pi_data); 885 } 886 887 static struct pcisel 888 getdevice(const char *name) 889 { 890 struct pci_conf_io pc; 891 struct pci_conf conf[1]; 892 struct pci_match_conf patterns[1]; 893 char *cp; 894 int fd; 895 896 fd = open(_PATH_DEVPCI, O_RDONLY, 0); 897 if (fd < 0) 898 err(1, "%s", _PATH_DEVPCI); 899 900 bzero(&pc, sizeof(struct pci_conf_io)); 901 pc.match_buf_len = sizeof(conf); 902 pc.matches = conf; 903 904 bzero(&patterns, sizeof(patterns)); 905 906 /* 907 * The pattern structure requires the unit to be split out from 908 * the driver name. Walk backwards from the end of the name to 909 * find the start of the unit. 910 */ 911 if (name[0] == '\0') 912 errx(1, "Empty device name"); 913 cp = strchr(name, '\0'); 914 assert(cp != NULL && cp != name); 915 cp--; 916 while (cp != name && isdigit(cp[-1])) 917 cp--; 918 if (cp == name || !isdigit(*cp)) 919 errx(1, "Invalid device name"); 920 if ((size_t)(cp - name) + 1 > sizeof(patterns[0].pd_name)) 921 errx(1, "Device name is too long"); 922 memcpy(patterns[0].pd_name, name, cp - name); 923 patterns[0].pd_unit = strtol(cp, &cp, 10); 924 if (*cp != '\0') 925 errx(1, "Invalid device name"); 926 patterns[0].flags = PCI_GETCONF_MATCH_NAME | PCI_GETCONF_MATCH_UNIT; 927 pc.num_patterns = 1; 928 pc.pat_buf_len = sizeof(patterns); 929 pc.patterns = patterns; 930 931 if (ioctl(fd, PCIOCGETCONF, &pc) == -1) 932 err(1, "ioctl(PCIOCGETCONF)"); 933 if (pc.status != PCI_GETCONF_LAST_DEVICE && 934 pc.status != PCI_GETCONF_MORE_DEVS) 935 errx(1, "error returned from PCIOCGETCONF ioctl"); 936 close(fd); 937 if (pc.num_matches == 0) 938 errx(1, "Device not found"); 939 return (conf[0].pc_sel); 940 } 941 942 static struct pcisel 943 parsesel(const char *str) 944 { 945 const char *ep; 946 char *eppos; 947 struct pcisel sel; 948 unsigned long selarr[4]; 949 int i; 950 951 ep = strchr(str, '@'); 952 if (ep != NULL) 953 ep++; 954 else 955 ep = str; 956 957 if (strncmp(ep, "pci", 3) == 0) { 958 ep += 3; 959 i = 0; 960 while (isdigit(*ep) && i < 4) { 961 selarr[i++] = strtoul(ep, &eppos, 10); 962 ep = eppos; 963 if (*ep == ':') 964 ep++; 965 } 966 if (i > 0 && *ep == '\0') { 967 sel.pc_func = (i > 2) ? selarr[--i] : 0; 968 sel.pc_dev = (i > 0) ? selarr[--i] : 0; 969 sel.pc_bus = (i > 0) ? selarr[--i] : 0; 970 sel.pc_domain = (i > 0) ? selarr[--i] : 0; 971 return (sel); 972 } 973 } 974 errx(1, "cannot parse selector %s", str); 975 } 976 977 static struct pcisel 978 getsel(const char *str) 979 { 980 981 /* 982 * No device names contain colons and selectors always contain 983 * at least one colon. 984 */ 985 if (strchr(str, ':') == NULL) 986 return (getdevice(str)); 987 else 988 return (parsesel(str)); 989 } 990 991 static void 992 readone(int fd, struct pcisel *sel, long reg, int width) 993 { 994 995 printf("%0*x", width*2, read_config(fd, sel, reg, width)); 996 } 997 998 static void 999 readit(const char *name, const char *reg, int width) 1000 { 1001 long rstart; 1002 long rend; 1003 long r; 1004 char *end; 1005 int i; 1006 int fd; 1007 struct pcisel sel; 1008 1009 fd = open(_PATH_DEVPCI, O_RDWR, 0); 1010 if (fd < 0) 1011 err(1, "%s", _PATH_DEVPCI); 1012 1013 rend = rstart = strtol(reg, &end, 0); 1014 if (end && *end == ':') { 1015 end++; 1016 rend = strtol(end, (char **) 0, 0); 1017 } 1018 sel = getsel(name); 1019 for (i = 1, r = rstart; r <= rend; i++, r += width) { 1020 readone(fd, &sel, r, width); 1021 if (i && !(i % 8)) 1022 putchar(' '); 1023 putchar(i % (16/width) ? ' ' : '\n'); 1024 } 1025 if (i % (16/width) != 1) 1026 putchar('\n'); 1027 close(fd); 1028 } 1029 1030 static void 1031 writeit(const char *name, const char *reg, const char *data, int width) 1032 { 1033 int fd; 1034 struct pci_io pi; 1035 1036 pi.pi_sel = getsel(name); 1037 pi.pi_reg = strtoul(reg, (char **)0, 0); /* XXX error check */ 1038 pi.pi_width = width; 1039 pi.pi_data = strtoul(data, (char **)0, 0); /* XXX error check */ 1040 1041 fd = open(_PATH_DEVPCI, O_RDWR, 0); 1042 if (fd < 0) 1043 err(1, "%s", _PATH_DEVPCI); 1044 1045 if (ioctl(fd, PCIOCWRITE, &pi) < 0) 1046 err(1, "ioctl(PCIOCWRITE)"); 1047 close(fd); 1048 } 1049 1050 static void 1051 chkattached(const char *name) 1052 { 1053 int fd; 1054 struct pci_io pi; 1055 1056 pi.pi_sel = getsel(name); 1057 1058 fd = open(_PATH_DEVPCI, O_RDWR, 0); 1059 if (fd < 0) 1060 err(1, "%s", _PATH_DEVPCI); 1061 1062 if (ioctl(fd, PCIOCATTACHED, &pi) < 0) 1063 err(1, "ioctl(PCIOCATTACHED)"); 1064 1065 exitstatus = pi.pi_data ? 0 : 2; /* exit(2), if NOT attached */ 1066 printf("%s: %s%s\n", name, pi.pi_data == 0 ? "not " : "", "attached"); 1067 close(fd); 1068 } 1069 1070 static void 1071 dump_bar(const char *name, const char *reg, const char *bar_start, 1072 const char *bar_count, int width, int verbose) 1073 { 1074 struct pci_bar_mmap pbm; 1075 uint32_t *dd; 1076 uint16_t *dh; 1077 uint8_t *db; 1078 uint64_t *dx, a, start, count; 1079 char *el; 1080 size_t res; 1081 int fd; 1082 1083 start = 0; 1084 if (bar_start != NULL) { 1085 start = strtoul(bar_start, &el, 0); 1086 if (*el != '\0') 1087 errx(1, "Invalid bar start specification %s", 1088 bar_start); 1089 } 1090 count = 0; 1091 if (bar_count != NULL) { 1092 count = strtoul(bar_count, &el, 0); 1093 if (*el != '\0') 1094 errx(1, "Invalid count specification %s", 1095 bar_count); 1096 } 1097 1098 pbm.pbm_sel = getsel(name); 1099 pbm.pbm_reg = strtoul(reg, &el, 0); 1100 if (*reg == '\0' || *el != '\0') 1101 errx(1, "Invalid bar specification %s", reg); 1102 pbm.pbm_flags = 0; 1103 pbm.pbm_memattr = VM_MEMATTR_UNCACHEABLE; /* XXX */ 1104 1105 fd = open(_PATH_DEVPCI, O_RDWR, 0); 1106 if (fd < 0) 1107 err(1, "%s", _PATH_DEVPCI); 1108 1109 if (ioctl(fd, PCIOCBARMMAP, &pbm) < 0) 1110 err(1, "ioctl(PCIOCBARMMAP)"); 1111 1112 if (count == 0) 1113 count = pbm.pbm_bar_length / width; 1114 if (start + count < start || (start + count) * width < (uint64_t)width) 1115 errx(1, "(start + count) x width overflow"); 1116 if ((start + count) * width > pbm.pbm_bar_length) { 1117 if (start * width > pbm.pbm_bar_length) 1118 count = 0; 1119 else 1120 count = (pbm.pbm_bar_length - start * width) / width; 1121 } 1122 if (verbose) { 1123 fprintf(stderr, 1124 "Dumping pci%d:%d:%d:%d BAR %x mapped base %p " 1125 "off %#x length %#jx from %#jx count %#jx in %d-bytes\n", 1126 pbm.pbm_sel.pc_domain, pbm.pbm_sel.pc_bus, 1127 pbm.pbm_sel.pc_dev, pbm.pbm_sel.pc_func, 1128 pbm.pbm_reg, pbm.pbm_map_base, pbm.pbm_bar_off, 1129 pbm.pbm_bar_length, start, count, width); 1130 } 1131 switch (width) { 1132 case 1: 1133 db = (uint8_t *)(uintptr_t)((uintptr_t)pbm.pbm_map_base + 1134 pbm.pbm_bar_off + start * width); 1135 for (a = 0; a < count; a += width, db++) { 1136 res = fwrite(db, width, 1, stdout); 1137 if (res != 1) { 1138 errx(1, "error writing to stdout"); 1139 break; 1140 } 1141 } 1142 break; 1143 case 2: 1144 dh = (uint16_t *)(uintptr_t)((uintptr_t)pbm.pbm_map_base + 1145 pbm.pbm_bar_off + start * width); 1146 for (a = 0; a < count; a += width, dh++) { 1147 res = fwrite(dh, width, 1, stdout); 1148 if (res != 1) { 1149 errx(1, "error writing to stdout"); 1150 break; 1151 } 1152 } 1153 break; 1154 case 4: 1155 dd = (uint32_t *)(uintptr_t)((uintptr_t)pbm.pbm_map_base + 1156 pbm.pbm_bar_off + start * width); 1157 for (a = 0; a < count; a += width, dd++) { 1158 res = fwrite(dd, width, 1, stdout); 1159 if (res != 1) { 1160 errx(1, "error writing to stdout"); 1161 break; 1162 } 1163 } 1164 break; 1165 case 8: 1166 dx = (uint64_t *)(uintptr_t)((uintptr_t)pbm.pbm_map_base + 1167 pbm.pbm_bar_off + start * width); 1168 for (a = 0; a < count; a += width, dx++) { 1169 res = fwrite(dx, width, 1, stdout); 1170 if (res != 1) { 1171 errx(1, "error writing to stdout"); 1172 break; 1173 } 1174 } 1175 break; 1176 default: 1177 errx(1, "invalid access width"); 1178 } 1179 1180 munmap((void *)pbm.pbm_map_base, pbm.pbm_map_length); 1181 close(fd); 1182 } 1183