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 2025 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%d", 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%d", 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 /* 294 * We need to distinguish three different groups of things here: 295 * 296 * - Something is a PCI device. 297 * - We have a PCIe device where the link is down and therefore 298 * have no current width or speed. 299 * - We have a PCIe device which is up. 300 * 301 * A PCIe device should always have a maximum width value. This 302 * is required and therefore should be a good proxy for whether 303 * or not we have a PCIe device. 304 */ 305 if (psdo->psdo_mwidth == -1) { 306 if (strlcat(buf, "PCI", buflen) >= buflen) { 307 return (B_FALSE); 308 } 309 break; 310 } 311 312 /* 313 * If we don't have a valid link up, indicate we don't know. 314 * While the link is probably down, we don't have that as a 315 * guarantee right now. 316 */ 317 if (psdo->psdo_cspeed == -1 || psdo->psdo_cwidth == -1) { 318 if (strlcat(buf, "PCIe unknown", buflen) >= buflen) { 319 return (B_FALSE); 320 } 321 break; 322 } 323 324 if (snprintf(buf, buflen, "PCIe Gen %ux%d", 325 pcieadm_speed2gen(psdo->psdo_cspeed), 326 psdo->psdo_cwidth) >= buflen) { 327 return (B_FALSE); 328 } 329 break; 330 default: 331 abort(); 332 } 333 return (B_TRUE); 334 } 335 336 static const char *pcieadm_show_dev_fields = "bdf,type,instance,device"; 337 static const char *pcieadm_show_dev_speeds = 338 "bdf,driver,maxspeed,curspeed,maxwidth,curwidth,supspeeds"; 339 static const ofmt_field_t pcieadm_show_dev_ofmt[] = { 340 { "VID", 6, PCIEADM_SDO_VID, pcieadm_show_devs_ofmt_cb }, 341 { "DID", 6, PCIEADM_SDO_DID, pcieadm_show_devs_ofmt_cb }, 342 { "REV", 6, PCIEADM_SDO_REV, pcieadm_show_devs_ofmt_cb }, 343 { "SUBVID", 6, PCIEADM_SDO_SUBVID, pcieadm_show_devs_ofmt_cb }, 344 { "SUBSYS", 6, PCIEADM_SDO_SUBSYS, pcieadm_show_devs_ofmt_cb }, 345 { "BDF", 8, PCIEADM_SDO_BDF, pcieadm_show_devs_ofmt_cb }, 346 { "DRIVER", 15, PCIEADM_SDO_DRIVER, pcieadm_show_devs_ofmt_cb }, 347 { "INSTANCE", 15, PCIEADM_SDO_INSTANCE, pcieadm_show_devs_ofmt_cb }, 348 { "INSTNUM", 8, PCIEADM_SDO_INSTNUM, pcieadm_show_devs_ofmt_cb }, 349 { "TYPE", 15, PCIEADM_SDO_TYPE, pcieadm_show_devs_ofmt_cb }, 350 { "VENDOR", 30, PCIEADM_SDO_VENDOR, pcieadm_show_devs_ofmt_cb }, 351 { "DEVICE", 30, PCIEADM_SDO_DEVICE, pcieadm_show_devs_ofmt_cb }, 352 { "SUBVENDOR", 30, PCIEADM_SDO_SUBVENDOR, pcieadm_show_devs_ofmt_cb }, 353 { "SUBSYSTEM", 30, PCIEADM_SDO_SUBSYSTEM, pcieadm_show_devs_ofmt_cb }, 354 { "PATH", 30, PCIEADM_SDO_PATH, pcieadm_show_devs_ofmt_cb }, 355 { "BUS", 4, PCIEADM_SDO_BDF_BUS, pcieadm_show_devs_ofmt_cb }, 356 { "DEV", 4, PCIEADM_SDO_BDF_DEV, pcieadm_show_devs_ofmt_cb }, 357 { "FUNC", 4, PCIEADM_SDO_BDF_FUNC, pcieadm_show_devs_ofmt_cb }, 358 { "MAXSPEED", 10, PCIEADM_SDO_MAXSPEED, pcieadm_show_devs_ofmt_cb }, 359 { "MAXWIDTH", 10, PCIEADM_SDO_MAXWIDTH, pcieadm_show_devs_ofmt_cb }, 360 { "CURSPEED", 10, PCIEADM_SDO_CURSPEED, pcieadm_show_devs_ofmt_cb }, 361 { "CURWIDTH", 10, PCIEADM_SDO_CURWIDTH, pcieadm_show_devs_ofmt_cb }, 362 { "SUPSPEEDS", 20, PCIEADM_SDO_SUPSPEEDS, pcieadm_show_devs_ofmt_cb }, 363 { NULL, 0, 0, NULL } 364 }; 365 366 static boolean_t 367 pcieadm_show_devs_match(pcieadm_show_devs_t *psd, 368 pcieadm_show_devs_ofmt_t *psdo) 369 { 370 char dinst[128], bdf[128]; 371 372 if (psd->psd_nfilts == 0) { 373 return (B_TRUE); 374 } 375 376 if (psdo->psdo_driver != NULL && psdo->psdo_instance != -1) { 377 (void) snprintf(dinst, sizeof (dinst), "%s%d", 378 psdo->psdo_driver, psdo->psdo_instance); 379 } 380 (void) snprintf(bdf, sizeof (bdf), "%x/%x/%x", psdo->psdo_bus, 381 psdo->psdo_dev, psdo->psdo_func); 382 383 for (uint_t i = 0; i < psd->psd_nfilts; i++) { 384 const char *filt = psd->psd_filts[i]; 385 386 if (strcmp(filt, psdo->psdo_path) == 0) { 387 psd->psd_used[i] = B_TRUE; 388 return (B_TRUE); 389 } 390 391 if (strcmp(filt, bdf) == 0) { 392 psd->psd_used[i] = B_TRUE; 393 return (B_TRUE); 394 } 395 396 if (psdo->psdo_driver != NULL && 397 strcmp(filt, psdo->psdo_driver) == 0) { 398 psd->psd_used[i] = B_TRUE; 399 return (B_TRUE); 400 } 401 402 if (psdo->psdo_driver != NULL && psdo->psdo_instance != -1 && 403 strcmp(filt, dinst) == 0) { 404 psd->psd_used[i] = B_TRUE; 405 return (B_TRUE); 406 } 407 408 if (strncmp("/devices", filt, strlen("/devices")) == 0) { 409 filt += strlen("/devices"); 410 } 411 412 if (strcmp(filt, psdo->psdo_path) == 0) { 413 psd->psd_used[i] = B_TRUE; 414 return (B_TRUE); 415 } 416 } 417 return (B_FALSE); 418 } 419 420 static int 421 pcieadm_show_devs_walk_cb(di_node_t node, void *arg) 422 { 423 int nprop, *regs = NULL, *did, *vid, *mwidth, *cwidth, *rev; 424 int *subvid, *subsys; 425 int64_t *mspeed, *cspeed, *sspeeds; 426 char *path = NULL; 427 pcieadm_show_devs_t *psd = arg; 428 int ret = DI_WALK_CONTINUE; 429 char venstr[64], devstr[64], subvenstr[64], subsysstr[64]; 430 pcieadm_show_devs_ofmt_t oarg; 431 pcidb_hdl_t *pcidb = psd->psd_pia->pia_pcidb; 432 433 bzero(&oarg, sizeof (oarg)); 434 435 path = di_devfs_path(node); 436 if (path == NULL) { 437 err(EXIT_FAILURE, "failed to construct devfs path for node: " 438 "%s", di_node_name(node)); 439 } 440 441 nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", ®s); 442 if (nprop <= 0) { 443 errx(EXIT_FAILURE, "failed to lookup regs array for %s", 444 path); 445 } 446 447 oarg.psdo_path = path; 448 oarg.psdo_bus = PCI_REG_BUS_G(regs[0]); 449 oarg.psdo_dev = PCI_REG_DEV_G(regs[0]); 450 oarg.psdo_func = PCI_REG_FUNC_G(regs[0]); 451 452 if (oarg.psdo_func != 0 && !psd->psd_funcs) { 453 goto done; 454 } 455 456 oarg.psdo_driver = di_driver_name(node); 457 oarg.psdo_instance = di_instance(node); 458 459 nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "device-id", &did); 460 if (nprop != 1) { 461 oarg.psdo_did = -1; 462 } else { 463 oarg.psdo_did = (uint16_t)*did; 464 } 465 466 nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "vendor-id", &vid); 467 if (nprop != 1) { 468 oarg.psdo_vid = -1; 469 } else { 470 oarg.psdo_vid = (uint16_t)*vid; 471 } 472 473 nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "revision-id", &rev); 474 if (nprop != 1) { 475 oarg.psdo_rev = -1; 476 } else { 477 oarg.psdo_rev = (uint16_t)*rev; 478 } 479 480 nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "subsystem-vendor-id", 481 &subvid); 482 if (nprop != 1) { 483 oarg.psdo_subvid = -1; 484 } else { 485 oarg.psdo_subvid = (uint16_t)*subvid; 486 } 487 488 nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "subsystem-id", 489 &subsys); 490 if (nprop != 1) { 491 oarg.psdo_subsys = -1; 492 } else { 493 oarg.psdo_subsys = (uint16_t)*subsys; 494 } 495 496 oarg.psdo_vendor = "--"; 497 if (oarg.psdo_vid != -1) { 498 pcidb_vendor_t *vend = pcidb_lookup_vendor(pcidb, 499 oarg.psdo_vid); 500 if (vend != NULL) { 501 oarg.psdo_vendor = pcidb_vendor_name(vend); 502 } else { 503 (void) snprintf(venstr, sizeof (venstr), 504 "Unknown vendor: 0x%x", oarg.psdo_vid); 505 oarg.psdo_vendor = venstr; 506 } 507 } 508 509 oarg.psdo_device = "--"; 510 if (oarg.psdo_vid != -1 && oarg.psdo_did != -1) { 511 pcidb_device_t *dev = pcidb_lookup_device(pcidb, 512 oarg.psdo_vid, oarg.psdo_did); 513 if (dev != NULL) { 514 oarg.psdo_device = pcidb_device_name(dev); 515 } else { 516 (void) snprintf(devstr, sizeof (devstr), 517 "Unknown device: 0x%x", oarg.psdo_did); 518 oarg.psdo_device = devstr; 519 } 520 } 521 522 /* 523 * The pci.ids database organizes subsystems under devices. We look at 524 * the subsystem vendor separately because even if the device or other 525 * information is not known, we may still be able to figure out what it 526 * is. 527 */ 528 oarg.psdo_subvendor = "--"; 529 oarg.psdo_subsystem = "--"; 530 if (oarg.psdo_subvid != -1) { 531 pcidb_vendor_t *vend = pcidb_lookup_vendor(pcidb, 532 oarg.psdo_subvid); 533 if (vend != NULL) { 534 oarg.psdo_subvendor = pcidb_vendor_name(vend); 535 } else { 536 (void) snprintf(subvenstr, sizeof (subvenstr), 537 "Unknown vendor: 0x%x", oarg.psdo_vid); 538 oarg.psdo_subvendor = subvenstr; 539 } 540 } 541 542 if (oarg.psdo_vid != -1 && oarg.psdo_did != -1 && 543 oarg.psdo_subvid != -1 && oarg.psdo_subsys != -1) { 544 pcidb_subvd_t *subvd = pcidb_lookup_subvd(pcidb, oarg.psdo_vid, 545 oarg.psdo_did, oarg.psdo_subvid, oarg.psdo_subsys); 546 if (subvd != NULL) { 547 oarg.psdo_subsystem = pcidb_subvd_name(subvd); 548 } else { 549 (void) snprintf(subsysstr, sizeof (subsysstr), 550 "Unknown subsystem: 0x%x", oarg.psdo_subsys); 551 oarg.psdo_subsystem = subsysstr; 552 } 553 } 554 555 556 nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, 557 "pcie-link-maximum-width", &mwidth); 558 if (nprop != 1) { 559 oarg.psdo_mwidth = -1; 560 } else { 561 oarg.psdo_mwidth = *mwidth; 562 } 563 564 nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, 565 "pcie-link-current-width", &cwidth); 566 if (nprop != 1) { 567 oarg.psdo_cwidth = -1; 568 } else { 569 oarg.psdo_cwidth = *cwidth; 570 } 571 572 nprop = di_prop_lookup_int64(DDI_DEV_T_ANY, node, 573 "pcie-link-maximum-speed", &mspeed); 574 if (nprop != 1) { 575 oarg.psdo_mspeed = -1; 576 } else { 577 oarg.psdo_mspeed = *mspeed; 578 } 579 580 nprop = di_prop_lookup_int64(DDI_DEV_T_ANY, node, 581 "pcie-link-current-speed", &cspeed); 582 if (nprop != 1) { 583 oarg.psdo_cspeed = -1; 584 } else { 585 oarg.psdo_cspeed = *cspeed; 586 } 587 588 nprop = di_prop_lookup_int64(DDI_DEV_T_ANY, node, 589 "pcie-link-supported-speeds", &sspeeds); 590 if (nprop > 0) { 591 oarg.psdo_nspeeds = nprop; 592 oarg.psdo_sspeeds = sspeeds; 593 } else { 594 oarg.psdo_nspeeds = 0; 595 oarg.psdo_sspeeds = NULL; 596 } 597 598 if (pcieadm_show_devs_match(psd, &oarg)) { 599 ofmt_print(psd->psd_ofmt, &oarg); 600 psd->psd_nprint++; 601 } 602 603 done: 604 if (path != NULL) { 605 di_devfs_path_free(path); 606 } 607 608 return (ret); 609 } 610 611 void 612 pcieadm_show_devs_usage(FILE *f) 613 { 614 (void) fprintf(f, "\tshow-devs\t[-F] [-H] [-s | -o field[,...] [-p]] " 615 "[filter...]\n"); 616 } 617 618 static void 619 pcieadm_show_devs_help(const char *fmt, ...) 620 { 621 if (fmt != NULL) { 622 va_list ap; 623 624 va_start(ap, fmt); 625 vwarnx(fmt, ap); 626 va_end(ap); 627 (void) fprintf(stderr, "\n"); 628 } 629 630 (void) fprintf(stderr, "Usage: %s show-devs [-F] [-H] [-s | -o " 631 "field[,...] [-p]] [filter...]\n", pcieadm_progname); 632 633 (void) fprintf(stderr, "\nList PCI devices and functions in the " 634 "system. Each <filter> selects a set\nof devices to show and " 635 "can be a driver name, instance, /devices path, or\nb/d/f.\n\n" 636 "\t-F\t\tdo not display PCI functions\n" 637 "\t-H\t\tomit the column header\n" 638 "\t-o field\toutput fields to print\n" 639 "\t-p\t\tparsable output (requires -o)\n" 640 "\t-s\t\tlist speeds and widths\n\n" 641 "The following fields are supported:\n" 642 "\tvid\t\tthe PCI vendor ID in hex\n" 643 "\tdid\t\tthe PCI device ID in hex\n" 644 "\trev\t\tthe PCI device revision in hex\n" 645 "\tsubvid\t\tthe PCI subsystem vendor ID in hex\n" 646 "\tsubsys\t\tthe PCI subsystem ID in hex\n" 647 "\tvendor\t\tthe name of the PCI vendor\n" 648 "\tdevice\t\tthe name of the PCI device\n" 649 "\tsubvendor\tthe name of the PCI subsystem vendor\n" 650 "\tsubsystem\tthe name of the PCI subsystem\n" 651 "\tinstance\tthe name of this particular instance, e.g. igb0\n" 652 "\tdriver\t\tthe name of the driver attached to the device\n" 653 "\tinstnum\t\tthe instance number of a device, e.g. 2 for nvme2\n" 654 "\tpath\t\tthe /devices path of the device\n" 655 "\tbdf\t\tthe PCI bus/device/function, with values in hex\n" 656 "\tbus\t\tthe PCI bus number of the device in hex\n" 657 "\tdev\t\tthe PCI device number of the device in hex\n" 658 "\tfunc\t\tthe PCI function number of the device in hex\n" 659 "\ttype\t\ta string describing the PCIe generation and width\n" 660 "\tmaxspeed\tthe maximum supported PCIe speed of the device\n" 661 "\tcurspeed\tthe current PCIe speed of the device\n" 662 "\tmaxwidth\tthe maximum supported PCIe lane count of the device\n" 663 "\tcurwidth\tthe current lane count of the PCIe device\n" 664 "\tsupspeeds\tthe list of speeds the device supports\n"); 665 } 666 667 int 668 pcieadm_show_devs(pcieadm_t *pcip, int argc, char *argv[]) 669 { 670 int c, ret; 671 uint_t flags = 0; 672 const char *fields = NULL; 673 pcieadm_show_devs_t psd; 674 pcieadm_di_walk_t walk; 675 ofmt_status_t oferr; 676 boolean_t parse = B_FALSE; 677 boolean_t speeds = B_FALSE; 678 679 /* 680 * show-devs relies solely on the devinfo snapshot we already took. 681 * Formalize our privs immediately. 682 */ 683 pcieadm_init_privs(pcip); 684 685 bzero(&psd, sizeof (psd)); 686 psd.psd_pia = pcip; 687 psd.psd_funcs = B_TRUE; 688 689 while ((c = getopt(argc, argv, ":FHo:ps")) != -1) { 690 switch (c) { 691 case 'F': 692 psd.psd_funcs = B_FALSE; 693 break; 694 case 'p': 695 parse = B_TRUE; 696 flags |= OFMT_PARSABLE; 697 break; 698 case 'H': 699 flags |= OFMT_NOHEADER; 700 break; 701 case 's': 702 speeds = B_TRUE; 703 break; 704 case 'o': 705 fields = optarg; 706 break; 707 case ':': 708 pcieadm_show_devs_help("option -%c requires an " 709 "argument", optopt); 710 exit(EXIT_USAGE); 711 case '?': 712 pcieadm_show_devs_help("unknown option: -%c", optopt); 713 exit(EXIT_USAGE); 714 } 715 } 716 717 if (parse && fields == NULL) { 718 errx(EXIT_USAGE, "-p requires fields specified with -o"); 719 } 720 721 if (fields != NULL && speeds) { 722 errx(EXIT_USAGE, "-s cannot be used with with -o"); 723 } 724 725 if (fields == NULL) { 726 if (speeds) { 727 fields = pcieadm_show_dev_speeds; 728 } else { 729 fields = pcieadm_show_dev_fields; 730 } 731 } 732 733 argc -= optind; 734 argv += optind; 735 736 if (argc > 0) { 737 psd.psd_nfilts = argc; 738 psd.psd_filts = argv; 739 psd.psd_used = calloc(argc, sizeof (boolean_t)); 740 if (psd.psd_used == NULL) { 741 err(EXIT_FAILURE, "failed to allocate filter tracking " 742 "memory"); 743 } 744 } 745 746 oferr = ofmt_open(fields, pcieadm_show_dev_ofmt, flags, 0, 747 &psd.psd_ofmt); 748 ofmt_check(oferr, parse, psd.psd_ofmt, pcieadm_ofmt_errx, warnx); 749 750 walk.pdw_arg = &psd; 751 walk.pdw_func = pcieadm_show_devs_walk_cb; 752 753 pcieadm_di_walk(pcip, &walk); 754 755 ret = EXIT_SUCCESS; 756 for (int i = 0; i < psd.psd_nfilts; i++) { 757 if (!psd.psd_used[i]) { 758 warnx("filter '%s' did not match any devices", 759 psd.psd_filts[i]); 760 ret = EXIT_FAILURE; 761 } 762 } 763 764 if (psd.psd_nprint == 0) { 765 ret = EXIT_FAILURE; 766 } 767 768 return (ret); 769 } 770