1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2012, Joyent, Inc. All rights reserved. 23 * Copyright 2021 Oxide Computer Company 24 */ 25 26 /* 27 * This library exists to understand and parse the pci.ids database that is 28 * maintained at http://pci-ids.ucw.cz/ and in the gate at cmd/hwdata. This 29 * database provides a way to map the PCI device, vendor, and subsystem ids to 30 * a human understandable name. 31 * 32 * This library exports this data in a similar way to a tree. The handle that 33 * is returned from pcidb_open is the root of the tree. The next level are the 34 * vendors. Each vendor has a unique set of devices and each device has a unique 35 * set of subvendor and subdevice pairs. 36 * 37 * Parsing information: 38 * 39 * The database is formatted in the following basic format: 40 * vendor_id<two spaces>vendor_name 41 * <tab>device_id<two spaces>device_name 42 * <tab><tab>subvendor<space>subdevice<two spaces>subsystem_name 43 * 44 * For any given vendor, there can be multiple devices. And for any given device 45 * there will be multiple subsystems. In addition, there can be comments that 46 * start a line which use the '#' character. 47 * 48 * At the end of the file, there are a series of PCI classes. Those will start 49 * with a single C<space>. Once we hit those, we stop all parsing. We currently 50 * don't care about consuming or presenting those. 51 */ 52 53 #include <sys/types.h> 54 #include <stdint.h> 55 #include <stdio.h> 56 #include <stdlib.h> 57 #include <errno.h> 58 #include <string.h> 59 #include <assert.h> 60 #include <unistd.h> 61 #include <stddef.h> 62 #include <sys/list.h> 63 64 #include "pcidb.h" 65 66 #define PCI_NAME_MAX 256 67 #define PCI_READLINE 1024 68 69 struct pcidb_progif { 70 char pp_name[PCI_NAME_MAX]; 71 list_node_t pp_link; 72 pcidb_subclass_t *pp_subclass; 73 uint8_t pp_code; 74 }; 75 76 struct pcidb_subclass { 77 char psc_name[PCI_NAME_MAX]; 78 list_node_t psc_link; 79 list_t psc_progifs; 80 pcidb_class_t *psc_class; 81 uint8_t psc_code; 82 }; 83 84 struct pcidb_class { 85 char pc_name[PCI_NAME_MAX]; 86 list_node_t pc_link; 87 list_t pc_subclass; 88 pcidb_hdl_t *pc_hdl; 89 uint8_t pc_code; 90 }; 91 92 struct pcidb_subvd { 93 uint16_t ps_vid; 94 uint16_t ps_did; 95 char ps_name[PCI_NAME_MAX]; 96 list_node_t ps_link; 97 pcidb_device_t *ps_dev; 98 pcidb_vendor_t *ps_vend; 99 }; 100 101 struct pcidb_device { 102 uint16_t pd_id; 103 char pd_name[PCI_NAME_MAX]; 104 list_t pd_subs; 105 list_node_t pd_link; 106 pcidb_vendor_t *pd_vend; 107 }; 108 109 struct pcidb_vendor { 110 uint16_t pv_id; 111 char pv_name[PCI_NAME_MAX]; 112 list_t pv_devs; 113 list_node_t pv_link; 114 pcidb_hdl_t *pv_hdl; 115 }; 116 117 struct pcidb_hdl { 118 list_t ph_vendors; 119 list_t ph_classes; 120 }; 121 122 typedef enum pcidb_parse { 123 PDB_INIT, 124 PDB_VENDOR, 125 PDB_DEVICE, 126 PDB_SUBDEV, 127 PDB_CLASS, 128 PDB_SUBCLASS, 129 PDB_PROGIF 130 } pcidb_parse_t; 131 132 static const char *pci_db = "/usr/share/hwdata/pci.ids"; 133 134 static pcidb_vendor_t * 135 parse_vendor(char *buf, pcidb_hdl_t *hdl) 136 { 137 pcidb_vendor_t *vend; 138 139 vend = malloc(sizeof (pcidb_vendor_t)); 140 if (vend == NULL) 141 return (NULL); 142 143 list_create(&vend->pv_devs, sizeof (pcidb_device_t), 144 offsetof(pcidb_device_t, pd_link)); 145 vend->pv_hdl = hdl; 146 list_insert_tail(&hdl->ph_vendors, vend); 147 148 buf[4] = '\0'; 149 vend->pv_id = strtol(buf, NULL, 16); 150 buf += 6; 151 152 (void) strlcpy(vend->pv_name, buf, PCI_NAME_MAX); 153 154 return (vend); 155 } 156 157 static pcidb_device_t * 158 parse_device(char *buf, pcidb_vendor_t *vend) 159 { 160 pcidb_device_t *dev; 161 162 dev = malloc(sizeof (pcidb_device_t)); 163 if (dev == NULL) 164 return (dev); 165 166 list_create(&dev->pd_subs, sizeof (pcidb_subvd_t), 167 offsetof(pcidb_subvd_t, ps_link)); 168 dev->pd_vend = vend; 169 list_insert_tail(&vend->pv_devs, dev); 170 171 buf++; 172 buf[4] = '\0'; 173 dev->pd_id = strtol(buf, NULL, 16); 174 buf += 6; 175 176 (void) strlcpy(dev->pd_name, buf, PCI_NAME_MAX); 177 return (dev); 178 } 179 180 static pcidb_subvd_t * 181 parse_subdev(char *buf, pcidb_device_t *dev) 182 { 183 pcidb_subvd_t *sub; 184 185 sub = malloc(sizeof (pcidb_subvd_t)); 186 if (sub == NULL) 187 return (NULL); 188 189 sub->ps_dev = dev; 190 sub->ps_vend = dev->pd_vend; 191 list_insert_tail(&dev->pd_subs, sub); 192 193 buf += 2; 194 buf[4] = '\0'; 195 sub->ps_vid = strtol(buf, NULL, 16); 196 buf += 5; 197 buf[4] = '\0'; 198 sub->ps_did = strtol(buf, NULL, 16); 199 buf += 6; 200 201 (void) strlcpy(sub->ps_name, buf, PCI_NAME_MAX); 202 203 return (sub); 204 } 205 206 static pcidb_class_t * 207 pcidb_parse_class(char *buf, pcidb_hdl_t *hdl) 208 { 209 pcidb_class_t *class; 210 211 class = malloc(sizeof (pcidb_class_t)); 212 if (class == NULL) 213 return (NULL); 214 215 list_create(&class->pc_subclass, sizeof (pcidb_subclass_t), 216 offsetof(pcidb_subclass_t, psc_link)); 217 class->pc_hdl = hdl; 218 list_insert_tail(&hdl->ph_classes, class); 219 220 buf += 2; 221 buf[3] = '\0'; 222 class->pc_code = strtol(buf, NULL, 16); 223 buf += 4; 224 (void) strlcpy(class->pc_name, buf, PCI_NAME_MAX); 225 226 return (class); 227 } 228 229 static pcidb_subclass_t * 230 pcidb_parse_subclass(char *buf, pcidb_class_t *class) 231 { 232 pcidb_subclass_t *sub; 233 234 sub = malloc(sizeof (pcidb_subclass_t)); 235 if (sub == NULL) 236 return (NULL); 237 238 list_create(&sub->psc_progifs, sizeof (pcidb_progif_t), 239 offsetof(pcidb_progif_t, pp_link)); 240 sub->psc_class = class; 241 list_insert_tail(&class->pc_subclass, sub); 242 243 buf++; 244 buf[3] = '\0'; 245 sub->psc_code = strtol(buf, NULL, 16); 246 buf += 4; 247 (void) strlcpy(sub->psc_name, buf, PCI_NAME_MAX); 248 249 return (sub); 250 } 251 252 static pcidb_progif_t * 253 pcidb_parse_progif(char *buf, pcidb_subclass_t *sub) 254 { 255 pcidb_progif_t *prog; 256 257 prog = malloc(sizeof (pcidb_progif_t)); 258 if (prog == NULL) { 259 return (NULL); 260 } 261 262 prog->pp_subclass = sub; 263 list_insert_tail(&sub->psc_progifs, prog); 264 265 buf += 2; 266 buf[3] = '\0'; 267 prog->pp_code = strtol(buf, NULL, 16); 268 buf += 4; 269 (void) strlcpy(prog->pp_name, buf, PCI_NAME_MAX); 270 271 return (prog); 272 } 273 274 static int 275 readline(FILE *f, char *buf, size_t len) 276 { 277 for (;;) { 278 char *c; 279 280 if (fgets(buf, len, f) == NULL) 281 return (-1); 282 283 if ((c = strchr(buf, '\n')) != NULL) 284 *c = '\0'; 285 286 if (buf[0] != '#' && buf[0] != '\0') 287 return (0); 288 } 289 } 290 291 static int 292 parse_db(FILE *f, pcidb_hdl_t *hdl) 293 { 294 pcidb_vendor_t *vend = NULL; 295 pcidb_device_t *dev = NULL; 296 pcidb_class_t *class = NULL; 297 pcidb_subclass_t *sub = NULL; 298 pcidb_parse_t state = PDB_INIT; 299 300 for (;;) { 301 char buf[1024]; 302 303 errno = 0; 304 if (readline(f, buf, sizeof (buf)) != 0) { 305 if (errno != 0) 306 return (-1); 307 else 308 return (0); 309 } 310 311 newstate: 312 switch (state) { 313 case PDB_INIT: 314 vend = NULL; 315 dev = NULL; 316 class = NULL; 317 sub = NULL; 318 if (buf[0] == 'C') { 319 state = PDB_CLASS; 320 } else { 321 state = PDB_VENDOR; 322 } 323 goto newstate; 324 case PDB_VENDOR: 325 vend = parse_vendor(buf, hdl); 326 if (vend == NULL) 327 return (-1); 328 state = PDB_DEVICE; 329 break; 330 case PDB_DEVICE: 331 if (buf[0] != '\t') { 332 state = PDB_INIT; 333 goto newstate; 334 } 335 336 if (buf[1] == '\t') { 337 state = PDB_SUBDEV; 338 goto newstate; 339 } 340 341 assert(vend != NULL); 342 dev = parse_device(buf, vend); 343 if (dev == NULL) 344 return (0); 345 break; 346 case PDB_SUBDEV: 347 if (buf[0] != '\t') { 348 state = PDB_INIT; 349 goto newstate; 350 } 351 352 if (buf[0] == '\t' && buf[1] != '\t') { 353 state = PDB_DEVICE; 354 goto newstate; 355 } 356 357 assert(buf[0] == '\t' && buf[1] == '\t'); 358 assert(dev != NULL); 359 if (parse_subdev(buf, dev) == NULL) { 360 return (-1); 361 } 362 break; 363 case PDB_CLASS: 364 class = pcidb_parse_class(buf, hdl); 365 state = PDB_SUBCLASS; 366 break; 367 case PDB_SUBCLASS: 368 if (buf[0] != '\t') { 369 state = PDB_INIT; 370 goto newstate; 371 } 372 373 if (buf[1] == '\t') { 374 state = PDB_PROGIF; 375 goto newstate; 376 } 377 378 assert(class != NULL); 379 sub = pcidb_parse_subclass(buf, class); 380 if (sub == NULL) { 381 return (-1); 382 } 383 break; 384 case PDB_PROGIF: 385 if (buf[0] != '\t') { 386 state = PDB_INIT; 387 goto newstate; 388 } 389 390 if (buf[0] == '\t' && buf[1] != '\t') { 391 state = PDB_SUBCLASS; 392 goto newstate; 393 } 394 395 assert(sub != NULL); 396 if (pcidb_parse_progif(buf, sub) == NULL) { 397 return (-1); 398 } 399 break; 400 } 401 } 402 } 403 404 pcidb_hdl_t * 405 pcidb_open(int version) 406 { 407 pcidb_hdl_t *h; 408 FILE *f; 409 410 if (version != PCIDB_VERSION) { 411 errno = EINVAL; 412 return (NULL); 413 } 414 415 h = malloc(sizeof (pcidb_hdl_t)); 416 if (h == NULL) 417 return (NULL); 418 419 list_create(&h->ph_vendors, sizeof (pcidb_vendor_t), 420 offsetof(pcidb_vendor_t, pv_link)); 421 list_create(&h->ph_classes, sizeof (pcidb_class_t), 422 offsetof(pcidb_class_t, pc_link)); 423 424 f = fopen(pci_db, "rF"); 425 if (f == NULL) { 426 free(h); 427 return (NULL); 428 } 429 430 if (parse_db(f, h) < 0) { 431 (void) fclose(f); 432 pcidb_close(h); 433 return (NULL); 434 } 435 436 (void) fclose(f); 437 438 return (h); 439 } 440 441 void 442 pcidb_close(pcidb_hdl_t *hdl) 443 { 444 pcidb_vendor_t *vend; 445 pcidb_class_t *class; 446 447 if (hdl == NULL) 448 return; 449 450 while ((vend = list_remove_head(&hdl->ph_vendors)) != NULL) { 451 pcidb_device_t *dev; 452 453 while ((dev = list_remove_head(&vend->pv_devs)) != NULL) { 454 pcidb_subvd_t *sub; 455 456 while ((sub = list_remove_head(&dev->pd_subs)) != 457 NULL) { 458 free(sub); 459 } 460 list_destroy(&dev->pd_subs); 461 free(dev); 462 } 463 list_destroy(&vend->pv_devs); 464 free(vend); 465 } 466 list_destroy(&hdl->ph_vendors); 467 468 while ((class = list_remove_head(&hdl->ph_classes)) != NULL) { 469 pcidb_subclass_t *sub; 470 471 while ((sub = list_remove_head(&class->pc_subclass)) != NULL) { 472 pcidb_progif_t *prog; 473 474 while ((prog = list_remove_head(&sub->psc_progifs)) != 475 NULL) { 476 free(prog); 477 } 478 list_destroy(&sub->psc_progifs); 479 free(sub); 480 } 481 list_destroy(&class->pc_subclass); 482 free(class); 483 } 484 list_destroy(&hdl->ph_classes); 485 486 free(hdl); 487 } 488 489 pcidb_vendor_t * 490 pcidb_lookup_vendor(pcidb_hdl_t *hdl, uint16_t id) 491 { 492 pcidb_vendor_t *v; 493 494 for (v = list_head(&hdl->ph_vendors); v != NULL; 495 v = list_next(&hdl->ph_vendors, v)) { 496 if (v->pv_id == id) 497 return (v); 498 } 499 500 return (NULL); 501 } 502 503 const char * 504 pcidb_vendor_name(pcidb_vendor_t *vend) 505 { 506 return (vend->pv_name); 507 } 508 509 uint16_t 510 pcidb_vendor_id(pcidb_vendor_t *vend) 511 { 512 return (vend->pv_id); 513 } 514 515 pcidb_vendor_t * 516 pcidb_vendor_iter(pcidb_hdl_t *hdl) 517 { 518 return (list_head(&hdl->ph_vendors)); 519 } 520 521 pcidb_vendor_t * 522 pcidb_vendor_iter_next(pcidb_vendor_t *vend) 523 { 524 assert(vend != NULL); 525 return (list_next(&vend->pv_hdl->ph_vendors, vend)); 526 } 527 528 pcidb_device_t * 529 pcidb_lookup_device_by_vendor(pcidb_vendor_t *vend, uint16_t id) 530 { 531 assert(vend != NULL); 532 533 for (pcidb_device_t *dev = list_head(&vend->pv_devs); dev != NULL; 534 dev = list_next(&vend->pv_devs, dev)) { 535 if (dev->pd_id == id) 536 return (dev); 537 } 538 539 return (NULL); 540 } 541 542 pcidb_device_t * 543 pcidb_lookup_device(pcidb_hdl_t *hdl, uint16_t vid, uint16_t did) 544 { 545 pcidb_vendor_t *vend; 546 547 vend = pcidb_lookup_vendor(hdl, vid); 548 if (vend == NULL) 549 return (NULL); 550 551 return (pcidb_lookup_device_by_vendor(vend, did)); 552 } 553 554 pcidb_device_t * 555 pcidb_device_iter(pcidb_vendor_t *vend) 556 { 557 return (list_head(&vend->pv_devs)); 558 } 559 560 pcidb_device_t * 561 pcidb_device_iter_next(pcidb_device_t *dev) 562 { 563 return (list_next(&dev->pd_vend->pv_devs, dev)); 564 } 565 566 const char * 567 pcidb_device_name(pcidb_device_t *dev) 568 { 569 return (dev->pd_name); 570 } 571 572 uint16_t 573 pcidb_device_id(pcidb_device_t *dev) 574 { 575 return (dev->pd_id); 576 } 577 578 pcidb_vendor_t * 579 pcidb_device_vendor(pcidb_device_t *dev) 580 { 581 return (dev->pd_vend); 582 } 583 584 pcidb_subvd_t * 585 pcidb_lookup_subvd_by_device(pcidb_device_t *dev, uint16_t svid, uint16_t sdid) 586 { 587 pcidb_subvd_t *sub; 588 589 assert(dev != NULL); 590 591 for (sub = list_head(&dev->pd_subs); sub != NULL; 592 sub = list_next(&dev->pd_subs, sub)) { 593 if (sub->ps_vid == svid && sub->ps_did == sdid) 594 return (sub); 595 } 596 597 return (NULL); 598 } 599 600 pcidb_subvd_t * 601 pcidb_lookup_subvd_by_vendor(pcidb_vendor_t *vend, uint16_t devid, 602 uint16_t svid, uint16_t sdid) 603 { 604 pcidb_device_t *dev; 605 606 assert(vend != NULL); 607 dev = pcidb_lookup_device_by_vendor(vend, devid); 608 if (dev == NULL) 609 return (NULL); 610 611 return (pcidb_lookup_subvd_by_device(dev, svid, sdid)); 612 } 613 614 pcidb_subvd_t * 615 pcidb_lookup_subvd(pcidb_hdl_t *hdl, uint16_t vid, uint16_t did, uint16_t svid, 616 uint16_t sdid) 617 { 618 pcidb_device_t *dev; 619 620 assert(hdl != NULL); 621 dev = pcidb_lookup_device(hdl, vid, did); 622 if (dev == NULL) 623 return (NULL); 624 625 return (pcidb_lookup_subvd_by_device(dev, svid, sdid)); 626 } 627 628 pcidb_subvd_t * 629 pcidb_subvd_iter(pcidb_device_t *dev) 630 { 631 return (list_head(&dev->pd_subs)); 632 } 633 634 pcidb_subvd_t * 635 pcidb_subvd_iter_next(pcidb_subvd_t *sub) 636 { 637 return (list_next(&sub->ps_dev->pd_subs, sub)); 638 } 639 640 const char * 641 pcidb_subvd_name(pcidb_subvd_t *sub) 642 { 643 return (sub->ps_name); 644 } 645 646 uint16_t 647 pcidb_subvd_svid(pcidb_subvd_t *sub) 648 { 649 return (sub->ps_vid); 650 } 651 652 uint16_t 653 pcidb_subvd_sdid(pcidb_subvd_t *sub) 654 { 655 return (sub->ps_did); 656 } 657 658 pcidb_device_t * 659 pcidb_subvd_device(pcidb_subvd_t *sub) 660 { 661 return (sub->ps_dev); 662 } 663 664 pcidb_vendor_t * 665 pcidb_subvd_vendor(pcidb_subvd_t *sub) 666 { 667 return (sub->ps_vend); 668 } 669 670 671 pcidb_class_t * 672 pcidb_lookup_class(pcidb_hdl_t *hdl, uint8_t code) 673 { 674 for (pcidb_class_t *class = list_head(&hdl->ph_classes); class != NULL; 675 class = list_next(&hdl->ph_classes, class)) { 676 if (class->pc_code == code) { 677 return (class); 678 } 679 } 680 681 return (NULL); 682 } 683 684 pcidb_class_t * 685 pcidb_class_iter(pcidb_hdl_t *hdl) 686 { 687 return (list_head(&hdl->ph_classes)); 688 } 689 690 pcidb_class_t * 691 pcidb_class_iter_next(pcidb_class_t *class) 692 { 693 return (list_next(&class->pc_hdl->ph_classes, class)); 694 } 695 696 const char * 697 pcidb_class_name(pcidb_class_t *class) 698 { 699 return (class->pc_name); 700 } 701 702 uint8_t 703 pcidb_class_code(pcidb_class_t *class) 704 { 705 return (class->pc_code); 706 } 707 708 pcidb_subclass_t * 709 pcidb_lookup_subclass(pcidb_hdl_t *hdl, uint8_t ccode, uint8_t subcode) 710 { 711 pcidb_class_t *class; 712 713 class = pcidb_lookup_class(hdl, ccode); 714 if (class == NULL) { 715 return (NULL); 716 } 717 718 return (pcidb_lookup_subclass_by_class(class, subcode)); 719 } 720 721 pcidb_subclass_t * 722 pcidb_lookup_subclass_by_class(pcidb_class_t *class, uint8_t code) 723 { 724 for (pcidb_subclass_t *sub = list_head(&class->pc_subclass); 725 sub != NULL; sub = list_next(&class->pc_subclass, sub)) { 726 if (sub->psc_code == code) { 727 return (sub); 728 } 729 } 730 731 return (NULL); 732 } 733 734 pcidb_subclass_t * 735 pcidb_subclass_iter(pcidb_class_t *class) 736 { 737 return (list_head(&class->pc_subclass)); 738 } 739 740 pcidb_subclass_t * 741 pcidb_subclass_iter_next(pcidb_subclass_t *sub) 742 { 743 return (list_next(&sub->psc_class->pc_subclass, sub)); 744 } 745 746 const char * 747 pcidb_subclass_name(pcidb_subclass_t *sub) 748 { 749 return (sub->psc_name); 750 } 751 752 uint8_t 753 pcidb_subclass_code(pcidb_subclass_t *sub) 754 { 755 return (sub->psc_code); 756 } 757 758 pcidb_progif_t * 759 pcidb_lookup_progif(pcidb_hdl_t *hdl, uint8_t ccode, uint8_t scode, 760 uint8_t pcode) 761 { 762 pcidb_subclass_t *sub; 763 764 sub = pcidb_lookup_subclass(hdl, ccode, scode); 765 if (sub == NULL) { 766 return (NULL); 767 } 768 769 return (pcidb_lookup_progif_by_subclass(sub, pcode)); 770 } 771 772 pcidb_progif_t * 773 pcidb_lookup_progif_by_subclass(pcidb_subclass_t *sub, uint8_t code) 774 { 775 for (pcidb_progif_t *prog = list_head(&sub->psc_progifs); prog != NULL; 776 prog = list_next(&sub->psc_progifs, prog)) { 777 if (prog->pp_code == code) { 778 return (prog); 779 } 780 } 781 782 return (NULL); 783 } 784 785 pcidb_progif_t * 786 pcidb_progif_iter(pcidb_subclass_t *sub) 787 { 788 return (list_head(&sub->psc_progifs)); 789 } 790 791 pcidb_progif_t * 792 pcidb_progif_iter_next(pcidb_progif_t *prog) 793 { 794 return (list_next(&prog->pp_subclass->psc_progifs, prog)); 795 } 796 797 const char * 798 pcidb_progif_name(pcidb_progif_t *prog) 799 { 800 return (prog->pp_name); 801 } 802 803 uint8_t 804 pcidb_progif_code(pcidb_progif_t *prog) 805 { 806 return (prog->pp_code); 807 } 808