1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2023 Oxide Computer Company 14 */ 15 16 #include <err.h> 17 #include <stdio.h> 18 #include <unistd.h> 19 #include <ofmt.h> 20 #include <strings.h> 21 #include <sys/pci.h> 22 23 #include "pcieadm.h" 24 25 typedef struct pcieadm_show_devs { 26 pcieadm_t *psd_pia; 27 ofmt_handle_t psd_ofmt; 28 boolean_t psd_funcs; 29 int psd_nfilts; 30 char **psd_filts; 31 boolean_t *psd_used; 32 uint_t psd_nprint; 33 } pcieadm_show_devs_t; 34 35 typedef enum pcieadm_show_devs_otype { 36 PCIEADM_SDO_VID, 37 PCIEADM_SDO_DID, 38 PCIEADM_SDO_REV, 39 PCIEADM_SDO_SUBVID, 40 PCIEADM_SDO_SUBSYS, 41 PCIEADM_SDO_BDF, 42 PCIEADM_SDO_BDF_BUS, 43 PCIEADM_SDO_BDF_DEV, 44 PCIEADM_SDO_BDF_FUNC, 45 PCIEADM_SDO_DRIVER, 46 PCIEADM_SDO_INSTANCE, 47 PCIEADM_SDO_INSTNUM, 48 PCIEADM_SDO_TYPE, 49 PCIEADM_SDO_VENDOR, 50 PCIEADM_SDO_DEVICE, 51 PCIEADM_SDO_SUBVENDOR, 52 PCIEADM_SDO_SUBSYSTEM, 53 PCIEADM_SDO_PATH, 54 PCIEADM_SDO_MAXSPEED, 55 PCIEADM_SDO_MAXWIDTH, 56 PCIEADM_SDO_CURSPEED, 57 PCIEADM_SDO_CURWIDTH, 58 PCIEADM_SDO_SUPSPEEDS 59 } pcieadm_show_devs_otype_t; 60 61 typedef struct pcieadm_show_devs_ofmt { 62 int psdo_vid; 63 int psdo_did; 64 int psdo_rev; 65 int psdo_subvid; 66 int psdo_subsys; 67 uint_t psdo_bus; 68 uint_t psdo_dev; 69 uint_t psdo_func; 70 const char *psdo_path; 71 const char *psdo_vendor; 72 const char *psdo_device; 73 const char *psdo_subvendor; 74 const char *psdo_subsystem; 75 const char *psdo_driver; 76 int psdo_instance; 77 int psdo_mwidth; 78 int psdo_cwidth; 79 int64_t psdo_mspeed; 80 int64_t psdo_cspeed; 81 int psdo_nspeeds; 82 int64_t *psdo_sspeeds; 83 } pcieadm_show_devs_ofmt_t; 84 85 static uint_t 86 pcieadm_speed2gen(int64_t speed) 87 { 88 if (speed == 2500000000LL) { 89 return (1); 90 } else if (speed == 5000000000LL) { 91 return (2); 92 } else if (speed == 8000000000LL) { 93 return (3); 94 } else if (speed == 16000000000LL) { 95 return (4); 96 } else if (speed == 32000000000LL) { 97 return (5); 98 } else { 99 return (0); 100 } 101 } 102 103 static const char * 104 pcieadm_speed2str(int64_t speed) 105 { 106 if (speed == 2500000000LL) { 107 return ("2.5"); 108 } else if (speed == 5000000000LL) { 109 return ("5.0"); 110 } else if (speed == 8000000000LL) { 111 return ("8.0"); 112 } else if (speed == 16000000000LL) { 113 return ("16.0"); 114 } else if (speed == 32000000000LL) { 115 return ("32.0"); 116 } else { 117 return (NULL); 118 } 119 } 120 121 static boolean_t 122 pcieadm_show_devs_ofmt_cb(ofmt_arg_t *ofarg, char *buf, uint_t buflen) 123 { 124 const char *str; 125 pcieadm_show_devs_ofmt_t *psdo = ofarg->ofmt_cbarg; 126 boolean_t first = B_TRUE; 127 128 switch (ofarg->ofmt_id) { 129 case PCIEADM_SDO_BDF: 130 if (snprintf(buf, buflen, "%x/%x/%x", psdo->psdo_bus, 131 psdo->psdo_dev, psdo->psdo_func) >= buflen) { 132 return (B_FALSE); 133 } 134 break; 135 case PCIEADM_SDO_BDF_BUS: 136 if (snprintf(buf, buflen, "%x", psdo->psdo_bus) >= buflen) { 137 return (B_FALSE); 138 } 139 break; 140 case PCIEADM_SDO_BDF_DEV: 141 if (snprintf(buf, buflen, "%x", psdo->psdo_dev) >= buflen) { 142 return (B_FALSE); 143 } 144 break; 145 case PCIEADM_SDO_BDF_FUNC: 146 if (snprintf(buf, buflen, "%x", psdo->psdo_func) >= buflen) { 147 return (B_FALSE); 148 } 149 break; 150 case PCIEADM_SDO_INSTANCE: 151 if (psdo->psdo_driver == NULL || psdo->psdo_instance == -1) { 152 (void) snprintf(buf, buflen, "--"); 153 } else if (snprintf(buf, buflen, "%s%d", psdo->psdo_driver, 154 psdo->psdo_instance) >= buflen) { 155 return (B_FALSE); 156 } 157 break; 158 case PCIEADM_SDO_DRIVER: 159 if (psdo->psdo_driver == NULL) { 160 (void) snprintf(buf, buflen, "--"); 161 } else if (strlcpy(buf, psdo->psdo_driver, buflen) >= buflen) { 162 return (B_FALSE); 163 } 164 break; 165 case PCIEADM_SDO_INSTNUM: 166 if (psdo->psdo_instance == -1) { 167 (void) snprintf(buf, buflen, "--"); 168 } else if (snprintf(buf, buflen, "%d", psdo->psdo_instance) >= 169 buflen) { 170 return (B_FALSE); 171 } 172 break; 173 case PCIEADM_SDO_PATH: 174 if (strlcat(buf, psdo->psdo_path, buflen) >= buflen) { 175 return (B_TRUE); 176 } 177 break; 178 case PCIEADM_SDO_VID: 179 if (psdo->psdo_vid == -1) { 180 (void) strlcat(buf, "--", buflen); 181 } else if (snprintf(buf, buflen, "%x", psdo->psdo_vid) >= 182 buflen) { 183 return (B_FALSE); 184 } 185 break; 186 case PCIEADM_SDO_DID: 187 if (psdo->psdo_did == -1) { 188 (void) strlcat(buf, "--", buflen); 189 } else if (snprintf(buf, buflen, "%x", psdo->psdo_did) >= 190 buflen) { 191 return (B_FALSE); 192 } 193 break; 194 case PCIEADM_SDO_REV: 195 if (psdo->psdo_rev == -1) { 196 (void) strlcat(buf, "--", buflen); 197 } else if (snprintf(buf, buflen, "%x", psdo->psdo_rev) >= 198 buflen) { 199 return (B_FALSE); 200 } 201 break; 202 case PCIEADM_SDO_SUBVID: 203 if (psdo->psdo_subvid == -1) { 204 (void) strlcat(buf, "--", buflen); 205 } else if (snprintf(buf, buflen, "%x", psdo->psdo_subvid) >= 206 buflen) { 207 return (B_FALSE); 208 } 209 break; 210 case PCIEADM_SDO_SUBSYS: 211 if (psdo->psdo_subsys == -1) { 212 (void) strlcat(buf, "--", buflen); 213 } else if (snprintf(buf, buflen, "%x", psdo->psdo_subsys) >= 214 buflen) { 215 return (B_FALSE); 216 } 217 break; 218 case PCIEADM_SDO_VENDOR: 219 if (strlcat(buf, psdo->psdo_vendor, buflen) >= buflen) { 220 return (B_FALSE); 221 } 222 break; 223 case PCIEADM_SDO_DEVICE: 224 if (strlcat(buf, psdo->psdo_device, buflen) >= buflen) { 225 return (B_FALSE); 226 } 227 break; 228 case PCIEADM_SDO_SUBVENDOR: 229 if (strlcat(buf, psdo->psdo_subvendor, buflen) >= buflen) { 230 return (B_FALSE); 231 } 232 break; 233 case PCIEADM_SDO_SUBSYSTEM: 234 if (strlcat(buf, psdo->psdo_subsystem, buflen) >= buflen) { 235 return (B_FALSE); 236 } 237 break; 238 case PCIEADM_SDO_MAXWIDTH: 239 if (psdo->psdo_mwidth <= 0) { 240 (void) strlcat(buf, "--", buflen); 241 } else if (snprintf(buf, buflen, "x%u", psdo->psdo_mwidth) >= 242 buflen) { 243 return (B_FALSE); 244 } 245 break; 246 case PCIEADM_SDO_CURWIDTH: 247 if (psdo->psdo_cwidth <= 0) { 248 (void) strlcat(buf, "--", buflen); 249 } else if (snprintf(buf, buflen, "x%u", psdo->psdo_cwidth) >= 250 buflen) { 251 return (B_FALSE); 252 } 253 break; 254 case PCIEADM_SDO_MAXSPEED: 255 str = pcieadm_speed2str(psdo->psdo_mspeed); 256 if (str == NULL) { 257 (void) strlcat(buf, "--", buflen); 258 } else if (snprintf(buf, buflen, "%s GT/s", str) >= buflen) { 259 return (B_FALSE); 260 } 261 break; 262 case PCIEADM_SDO_CURSPEED: 263 str = pcieadm_speed2str(psdo->psdo_cspeed); 264 if (str == NULL) { 265 (void) strlcat(buf, "--", buflen); 266 } else if (snprintf(buf, buflen, "%s GT/s", str) >= buflen) { 267 return (B_FALSE); 268 } 269 break; 270 case PCIEADM_SDO_SUPSPEEDS: 271 buf[0] = 0; 272 for (int i = 0; i < psdo->psdo_nspeeds; i++) { 273 const char *str; 274 275 str = pcieadm_speed2str(psdo->psdo_sspeeds[i]); 276 if (str == NULL) { 277 continue; 278 } 279 280 if (!first) { 281 if (strlcat(buf, ",", buflen) >= buflen) { 282 return (B_FALSE); 283 } 284 } 285 first = B_FALSE; 286 287 if (strlcat(buf, str, buflen) >= buflen) { 288 return (B_FALSE); 289 } 290 } 291 break; 292 case PCIEADM_SDO_TYPE: 293 if (pcieadm_speed2gen(psdo->psdo_cspeed) == 0 || 294 psdo->psdo_mwidth == -1) { 295 if (strlcat(buf, "PCI", buflen) >= buflen) { 296 return (B_FALSE); 297 } 298 } else { 299 if (snprintf(buf, buflen, "PCIe Gen %ux%u", 300 pcieadm_speed2gen(psdo->psdo_cspeed), 301 psdo->psdo_cwidth) >= buflen) { 302 return (B_FALSE); 303 } 304 } 305 break; 306 default: 307 abort(); 308 } 309 return (B_TRUE); 310 } 311 312 static const char *pcieadm_show_dev_fields = "bdf,type,instance,device"; 313 static const char *pcieadm_show_dev_speeds = 314 "bdf,driver,maxspeed,curspeed,maxwidth,curwidth,supspeeds"; 315 static const ofmt_field_t pcieadm_show_dev_ofmt[] = { 316 { "VID", 6, PCIEADM_SDO_VID, pcieadm_show_devs_ofmt_cb }, 317 { "DID", 6, PCIEADM_SDO_DID, pcieadm_show_devs_ofmt_cb }, 318 { "REV", 6, PCIEADM_SDO_REV, pcieadm_show_devs_ofmt_cb }, 319 { "SUBVID", 6, PCIEADM_SDO_SUBVID, pcieadm_show_devs_ofmt_cb }, 320 { "SUBSYS", 6, PCIEADM_SDO_SUBSYS, pcieadm_show_devs_ofmt_cb }, 321 { "BDF", 8, PCIEADM_SDO_BDF, pcieadm_show_devs_ofmt_cb }, 322 { "DRIVER", 15, PCIEADM_SDO_DRIVER, pcieadm_show_devs_ofmt_cb }, 323 { "INSTANCE", 15, PCIEADM_SDO_INSTANCE, pcieadm_show_devs_ofmt_cb }, 324 { "INSTNUM", 8, PCIEADM_SDO_INSTNUM, pcieadm_show_devs_ofmt_cb }, 325 { "TYPE", 15, PCIEADM_SDO_TYPE, pcieadm_show_devs_ofmt_cb }, 326 { "VENDOR", 30, PCIEADM_SDO_VENDOR, pcieadm_show_devs_ofmt_cb }, 327 { "DEVICE", 30, PCIEADM_SDO_DEVICE, pcieadm_show_devs_ofmt_cb }, 328 { "SUBVENDOR", 30, PCIEADM_SDO_SUBVENDOR, pcieadm_show_devs_ofmt_cb }, 329 { "SUBSYSTEM", 30, PCIEADM_SDO_SUBSYSTEM, pcieadm_show_devs_ofmt_cb }, 330 { "PATH", 30, PCIEADM_SDO_PATH, pcieadm_show_devs_ofmt_cb }, 331 { "BUS", 4, PCIEADM_SDO_BDF_BUS, pcieadm_show_devs_ofmt_cb }, 332 { "DEV", 4, PCIEADM_SDO_BDF_DEV, pcieadm_show_devs_ofmt_cb }, 333 { "FUNC", 4, PCIEADM_SDO_BDF_FUNC, pcieadm_show_devs_ofmt_cb }, 334 { "MAXSPEED", 10, PCIEADM_SDO_MAXSPEED, pcieadm_show_devs_ofmt_cb }, 335 { "MAXWIDTH", 10, PCIEADM_SDO_MAXWIDTH, pcieadm_show_devs_ofmt_cb }, 336 { "CURSPEED", 10, PCIEADM_SDO_CURSPEED, pcieadm_show_devs_ofmt_cb }, 337 { "CURWIDTH", 10, PCIEADM_SDO_CURWIDTH, pcieadm_show_devs_ofmt_cb }, 338 { "SUPSPEEDS", 20, PCIEADM_SDO_SUPSPEEDS, pcieadm_show_devs_ofmt_cb }, 339 { NULL, 0, 0, NULL } 340 }; 341 342 static boolean_t 343 pcieadm_show_devs_match(pcieadm_show_devs_t *psd, 344 pcieadm_show_devs_ofmt_t *psdo) 345 { 346 char dinst[128], bdf[128]; 347 348 if (psd->psd_nfilts == 0) { 349 return (B_TRUE); 350 } 351 352 if (psdo->psdo_driver != NULL && psdo->psdo_instance != -1) { 353 (void) snprintf(dinst, sizeof (dinst), "%s%d", 354 psdo->psdo_driver, psdo->psdo_instance); 355 } 356 (void) snprintf(bdf, sizeof (bdf), "%x/%x/%x", psdo->psdo_bus, 357 psdo->psdo_dev, psdo->psdo_func); 358 359 for (uint_t i = 0; i < psd->psd_nfilts; i++) { 360 const char *filt = psd->psd_filts[i]; 361 362 if (strcmp(filt, psdo->psdo_path) == 0) { 363 psd->psd_used[i] = B_TRUE; 364 return (B_TRUE); 365 } 366 367 if (strcmp(filt, bdf) == 0) { 368 psd->psd_used[i] = B_TRUE; 369 return (B_TRUE); 370 } 371 372 if (psdo->psdo_driver != NULL && 373 strcmp(filt, psdo->psdo_driver) == 0) { 374 psd->psd_used[i] = B_TRUE; 375 return (B_TRUE); 376 } 377 378 if (psdo->psdo_driver != NULL && psdo->psdo_instance != -1 && 379 strcmp(filt, dinst) == 0) { 380 psd->psd_used[i] = B_TRUE; 381 return (B_TRUE); 382 } 383 384 if (strncmp("/devices", filt, strlen("/devices")) == 0) { 385 filt += strlen("/devices"); 386 } 387 388 if (strcmp(filt, psdo->psdo_path) == 0) { 389 psd->psd_used[i] = B_TRUE; 390 return (B_TRUE); 391 } 392 } 393 return (B_FALSE); 394 } 395 396 static int 397 pcieadm_show_devs_walk_cb(di_node_t node, void *arg) 398 { 399 int nprop, *regs = NULL, *did, *vid, *mwidth, *cwidth, *rev; 400 int *subvid, *subsys; 401 int64_t *mspeed, *cspeed, *sspeeds; 402 char *path = NULL; 403 pcieadm_show_devs_t *psd = arg; 404 int ret = DI_WALK_CONTINUE; 405 char venstr[64], devstr[64], subvenstr[64], subsysstr[64]; 406 pcieadm_show_devs_ofmt_t oarg; 407 pcidb_hdl_t *pcidb = psd->psd_pia->pia_pcidb; 408 409 bzero(&oarg, sizeof (oarg)); 410 411 path = di_devfs_path(node); 412 if (path == NULL) { 413 err(EXIT_FAILURE, "failed to construct devfs path for node: " 414 "%s", di_node_name(node)); 415 } 416 417 nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", ®s); 418 if (nprop <= 0) { 419 errx(EXIT_FAILURE, "failed to lookup regs array for %s", 420 path); 421 } 422 423 oarg.psdo_path = path; 424 oarg.psdo_bus = PCI_REG_BUS_G(regs[0]); 425 oarg.psdo_dev = PCI_REG_DEV_G(regs[0]); 426 oarg.psdo_func = PCI_REG_FUNC_G(regs[0]); 427 428 if (oarg.psdo_func != 0 && !psd->psd_funcs) { 429 goto done; 430 } 431 432 oarg.psdo_driver = di_driver_name(node); 433 oarg.psdo_instance = di_instance(node); 434 435 nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "device-id", &did); 436 if (nprop != 1) { 437 oarg.psdo_did = -1; 438 } else { 439 oarg.psdo_did = (uint16_t)*did; 440 } 441 442 nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "vendor-id", &vid); 443 if (nprop != 1) { 444 oarg.psdo_vid = -1; 445 } else { 446 oarg.psdo_vid = (uint16_t)*vid; 447 } 448 449 nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "revision-id", &rev); 450 if (nprop != 1) { 451 oarg.psdo_rev = -1; 452 } else { 453 oarg.psdo_rev = (uint16_t)*rev; 454 } 455 456 nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "subsystem-vendor-id", 457 &subvid); 458 if (nprop != 1) { 459 oarg.psdo_subvid = -1; 460 } else { 461 oarg.psdo_subvid = (uint16_t)*subvid; 462 } 463 464 nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "subsystem-id", 465 &subsys); 466 if (nprop != 1) { 467 oarg.psdo_subsys = -1; 468 } else { 469 oarg.psdo_subsys = (uint16_t)*subsys; 470 } 471 472 oarg.psdo_vendor = "--"; 473 if (oarg.psdo_vid != -1) { 474 pcidb_vendor_t *vend = pcidb_lookup_vendor(pcidb, 475 oarg.psdo_vid); 476 if (vend != NULL) { 477 oarg.psdo_vendor = pcidb_vendor_name(vend); 478 } else { 479 (void) snprintf(venstr, sizeof (venstr), 480 "Unknown vendor: 0x%x", oarg.psdo_vid); 481 oarg.psdo_vendor = venstr; 482 } 483 } 484 485 oarg.psdo_device = "--"; 486 if (oarg.psdo_vid != -1 && oarg.psdo_did != -1) { 487 pcidb_device_t *dev = pcidb_lookup_device(pcidb, 488 oarg.psdo_vid, oarg.psdo_did); 489 if (dev != NULL) { 490 oarg.psdo_device = pcidb_device_name(dev); 491 } else { 492 (void) snprintf(devstr, sizeof (devstr), 493 "Unknown device: 0x%x", oarg.psdo_did); 494 oarg.psdo_device = devstr; 495 } 496 } 497 498 /* 499 * The pci.ids database organizes subsystems under devices. We look at 500 * the subsystem vendor separately because even if the device or other 501 * information is not known, we may still be able to figure out what it 502 * is. 503 */ 504 oarg.psdo_subvendor = "--"; 505 oarg.psdo_subsystem = "--"; 506 if (oarg.psdo_subvid != -1) { 507 pcidb_vendor_t *vend = pcidb_lookup_vendor(pcidb, 508 oarg.psdo_subvid); 509 if (vend != NULL) { 510 oarg.psdo_subvendor = pcidb_vendor_name(vend); 511 } else { 512 (void) snprintf(subvenstr, sizeof (subvenstr), 513 "Unknown vendor: 0x%x", oarg.psdo_vid); 514 oarg.psdo_subvendor = subvenstr; 515 } 516 } 517 518 if (oarg.psdo_vid != -1 && oarg.psdo_did != -1 && 519 oarg.psdo_subvid != -1 && oarg.psdo_subsys != -1) { 520 pcidb_subvd_t *subvd = pcidb_lookup_subvd(pcidb, oarg.psdo_vid, 521 oarg.psdo_did, oarg.psdo_subvid, oarg.psdo_subsys); 522 if (subvd != NULL) { 523 oarg.psdo_subsystem = pcidb_subvd_name(subvd); 524 } else { 525 (void) snprintf(subsysstr, sizeof (subsysstr), 526 "Unknown subsystem: 0x%x", oarg.psdo_subsys); 527 oarg.psdo_subsystem = subsysstr; 528 } 529 } 530 531 532 nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, 533 "pcie-link-maximum-width", &mwidth); 534 if (nprop != 1) { 535 oarg.psdo_mwidth = -1; 536 } else { 537 oarg.psdo_mwidth = *mwidth; 538 } 539 540 nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, 541 "pcie-link-current-width", &cwidth); 542 if (nprop != 1) { 543 oarg.psdo_cwidth = -1; 544 } else { 545 oarg.psdo_cwidth = *cwidth; 546 } 547 548 nprop = di_prop_lookup_int64(DDI_DEV_T_ANY, node, 549 "pcie-link-maximum-speed", &mspeed); 550 if (nprop != 1) { 551 oarg.psdo_mspeed = -1; 552 } else { 553 oarg.psdo_mspeed = *mspeed; 554 } 555 556 nprop = di_prop_lookup_int64(DDI_DEV_T_ANY, node, 557 "pcie-link-current-speed", &cspeed); 558 if (nprop != 1) { 559 oarg.psdo_cspeed = -1; 560 } else { 561 oarg.psdo_cspeed = *cspeed; 562 } 563 564 nprop = di_prop_lookup_int64(DDI_DEV_T_ANY, node, 565 "pcie-link-supported-speeds", &sspeeds); 566 if (nprop > 0) { 567 oarg.psdo_nspeeds = nprop; 568 oarg.psdo_sspeeds = sspeeds; 569 } else { 570 oarg.psdo_nspeeds = 0; 571 oarg.psdo_sspeeds = NULL; 572 } 573 574 if (pcieadm_show_devs_match(psd, &oarg)) { 575 ofmt_print(psd->psd_ofmt, &oarg); 576 psd->psd_nprint++; 577 } 578 579 done: 580 if (path != NULL) { 581 di_devfs_path_free(path); 582 } 583 584 return (ret); 585 } 586 587 void 588 pcieadm_show_devs_usage(FILE *f) 589 { 590 (void) fprintf(f, "\tshow-devs\t[-F] [-H] [-s | -o field[,...] [-p]] " 591 "[filter...]\n"); 592 } 593 594 static void 595 pcieadm_show_devs_help(const char *fmt, ...) 596 { 597 if (fmt != NULL) { 598 va_list ap; 599 600 va_start(ap, fmt); 601 vwarnx(fmt, ap); 602 va_end(ap); 603 (void) fprintf(stderr, "\n"); 604 } 605 606 (void) fprintf(stderr, "Usage: %s show-devs [-F] [-H] [-s | -o " 607 "field[,...] [-p]] [filter...]\n", pcieadm_progname); 608 609 (void) fprintf(stderr, "\nList PCI devices and functions in the " 610 "system. Each <filter> selects a set\nof devices to show and " 611 "can be a driver name, instance, /devices path, or\nb/d/f.\n\n" 612 "\t-F\t\tdo not display PCI functions\n" 613 "\t-H\t\tomit the column header\n" 614 "\t-o field\toutput fields to print\n" 615 "\t-p\t\tparsable output (requires -o)\n" 616 "\t-s\t\tlist speeds and widths\n\n" 617 "The following fields are supported:\n" 618 "\tvid\t\tthe PCI vendor ID in hex\n" 619 "\tdid\t\tthe PCI device ID in hex\n" 620 "\trev\t\tthe PCI device revision in hex\n" 621 "\tsubvid\t\tthe PCI subsystem vendor ID in hex\n" 622 "\tsubsys\t\tthe PCI subsystem ID in hex\n" 623 "\tvendor\t\tthe name of the PCI vendor\n" 624 "\tdevice\t\tthe name of the PCI device\n" 625 "\tsubvendor\t\tthe name of the PCI subsystem vendor\n" 626 "\tsubsystem\t\tthe name of the PCI subsystem\n" 627 "\tinstance\tthe name of this particular instance, e.g. igb0\n" 628 "\tdriver\t\tthe name of the driver attached to the device\n" 629 "\tinstnum\t\tthe instance number of a device, e.g. 2 for nvme2\n" 630 "\tpath\t\tthe /devices path of the device\n" 631 "\tbdf\t\tthe PCI bus/device/function, with values in hex\n" 632 "\tbus\t\tthe PCI bus number of the device in hex\n" 633 "\tdev\t\tthe PCI device number of the device in hex\n" 634 "\tfunc\t\tthe PCI function number of the device in hex\n" 635 "\ttype\t\ta string describing the PCIe generation and width\n" 636 "\tmaxspeed\tthe maximum supported PCIe speed of the device\n" 637 "\tcurspeed\tthe current PCIe speed of the device\n" 638 "\tmaxwidth\tthe maximum supported PCIe lane count of the device\n" 639 "\tcurwidth\tthe current lane count of the PCIe device\n" 640 "\tsupspeeds\tthe list of speeds the device supports\n"); 641 } 642 643 int 644 pcieadm_show_devs(pcieadm_t *pcip, int argc, char *argv[]) 645 { 646 int c, ret; 647 uint_t flags = 0; 648 const char *fields = NULL; 649 pcieadm_show_devs_t psd; 650 pcieadm_di_walk_t walk; 651 ofmt_status_t oferr; 652 boolean_t parse = B_FALSE; 653 boolean_t speeds = B_FALSE; 654 655 /* 656 * show-devs relies solely on the devinfo snapshot we already took. 657 * Formalize our privs immediately. 658 */ 659 pcieadm_init_privs(pcip); 660 661 bzero(&psd, sizeof (psd)); 662 psd.psd_pia = pcip; 663 psd.psd_funcs = B_TRUE; 664 665 while ((c = getopt(argc, argv, ":FHo:ps")) != -1) { 666 switch (c) { 667 case 'F': 668 psd.psd_funcs = B_FALSE; 669 break; 670 case 'p': 671 parse = B_TRUE; 672 flags |= OFMT_PARSABLE; 673 break; 674 case 'H': 675 flags |= OFMT_NOHEADER; 676 break; 677 case 's': 678 speeds = B_TRUE; 679 break; 680 case 'o': 681 fields = optarg; 682 break; 683 case ':': 684 pcieadm_show_devs_help("option -%c requires an " 685 "argument", optopt); 686 exit(EXIT_USAGE); 687 case '?': 688 pcieadm_show_devs_help("unknown option: -%c", optopt); 689 exit(EXIT_USAGE); 690 } 691 } 692 693 if (parse && fields == NULL) { 694 errx(EXIT_USAGE, "-p requires fields specified with -o"); 695 } 696 697 if (fields != NULL && speeds) { 698 errx(EXIT_USAGE, "-s cannot be used with with -o"); 699 } 700 701 if (fields == NULL) { 702 if (speeds) { 703 fields = pcieadm_show_dev_speeds; 704 } else { 705 fields = pcieadm_show_dev_fields; 706 } 707 } 708 709 argc -= optind; 710 argv += optind; 711 712 if (argc > 0) { 713 psd.psd_nfilts = argc; 714 psd.psd_filts = argv; 715 psd.psd_used = calloc(argc, sizeof (boolean_t)); 716 if (psd.psd_used == NULL) { 717 err(EXIT_FAILURE, "failed to allocate filter tracking " 718 "memory"); 719 } 720 } 721 722 oferr = ofmt_open(fields, pcieadm_show_dev_ofmt, flags, 0, 723 &psd.psd_ofmt); 724 ofmt_check(oferr, parse, psd.psd_ofmt, pcieadm_ofmt_errx, warnx); 725 726 walk.pdw_arg = &psd; 727 walk.pdw_func = pcieadm_show_devs_walk_cb; 728 729 pcieadm_di_walk(pcip, &walk); 730 731 ret = EXIT_SUCCESS; 732 for (int i = 0; i < psd.psd_nfilts; i++) { 733 if (!psd.psd_used[i]) { 734 warnx("filter '%s' did not match any devices", 735 psd.psd_filts[i]); 736 ret = EXIT_FAILURE; 737 } 738 } 739 740 if (psd.psd_nprint == 0) { 741 ret = EXIT_FAILURE; 742 } 743 744 return (ret); 745 } 746