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
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.
13 * Copyright 2023 Oxide Computer Company
25 typedef struct pcieadm_show_devs
{
27 ofmt_handle_t psd_ofmt
;
33 } pcieadm_show_devs_t
;
35 typedef enum pcieadm_show_devs_otype
{
51 PCIEADM_SDO_SUBVENDOR
,
52 PCIEADM_SDO_SUBSYSTEM
,
59 } pcieadm_show_devs_otype_t
;
61 typedef struct pcieadm_show_devs_ofmt
{
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
;
82 int64_t *psdo_sspeeds
;
83 } pcieadm_show_devs_ofmt_t
;
86 pcieadm_speed2gen(int64_t speed
)
88 if (speed
== 2500000000LL) {
90 } else if (speed
== 5000000000LL) {
92 } else if (speed
== 8000000000LL) {
94 } else if (speed
== 16000000000LL) {
96 } else if (speed
== 32000000000LL) {
104 pcieadm_speed2str(int64_t speed
)
106 if (speed
== 2500000000LL) {
108 } else if (speed
== 5000000000LL) {
110 } else if (speed
== 8000000000LL) {
112 } else if (speed
== 16000000000LL) {
114 } else if (speed
== 32000000000LL) {
122 pcieadm_show_devs_ofmt_cb(ofmt_arg_t
*ofarg
, char *buf
, uint_t buflen
)
125 pcieadm_show_devs_ofmt_t
*psdo
= ofarg
->ofmt_cbarg
;
126 boolean_t first
= B_TRUE
;
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
) {
135 case PCIEADM_SDO_BDF_BUS
:
136 if (snprintf(buf
, buflen
, "%x", psdo
->psdo_bus
) >= buflen
) {
140 case PCIEADM_SDO_BDF_DEV
:
141 if (snprintf(buf
, buflen
, "%x", psdo
->psdo_dev
) >= buflen
) {
145 case PCIEADM_SDO_BDF_FUNC
:
146 if (snprintf(buf
, buflen
, "%x", psdo
->psdo_func
) >= buflen
) {
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
) {
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
) {
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
) >=
173 case PCIEADM_SDO_PATH
:
174 if (strlcat(buf
, psdo
->psdo_path
, buflen
) >= buflen
) {
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
) >=
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
) >=
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
) >=
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
) >=
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
) >=
218 case PCIEADM_SDO_VENDOR
:
219 if (strlcat(buf
, psdo
->psdo_vendor
, buflen
) >= buflen
) {
223 case PCIEADM_SDO_DEVICE
:
224 if (strlcat(buf
, psdo
->psdo_device
, buflen
) >= buflen
) {
228 case PCIEADM_SDO_SUBVENDOR
:
229 if (strlcat(buf
, psdo
->psdo_subvendor
, buflen
) >= buflen
) {
233 case PCIEADM_SDO_SUBSYSTEM
:
234 if (strlcat(buf
, psdo
->psdo_subsystem
, buflen
) >= buflen
) {
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
) >=
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
) >=
254 case PCIEADM_SDO_MAXSPEED
:
255 str
= pcieadm_speed2str(psdo
->psdo_mspeed
);
257 (void) strlcat(buf
, "--", buflen
);
258 } else if (snprintf(buf
, buflen
, "%s GT/s", str
) >= buflen
) {
262 case PCIEADM_SDO_CURSPEED
:
263 str
= pcieadm_speed2str(psdo
->psdo_cspeed
);
265 (void) strlcat(buf
, "--", buflen
);
266 } else if (snprintf(buf
, buflen
, "%s GT/s", str
) >= buflen
) {
270 case PCIEADM_SDO_SUPSPEEDS
:
272 for (int i
= 0; i
< psdo
->psdo_nspeeds
; i
++) {
275 str
= pcieadm_speed2str(psdo
->psdo_sspeeds
[i
]);
281 if (strlcat(buf
, ",", buflen
) >= buflen
) {
287 if (strlcat(buf
, str
, buflen
) >= buflen
) {
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
) {
299 if (snprintf(buf
, buflen
, "PCIe Gen %ux%u",
300 pcieadm_speed2gen(psdo
->psdo_cspeed
),
301 psdo
->psdo_cwidth
) >= buflen
) {
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
},
343 pcieadm_show_devs_match(pcieadm_show_devs_t
*psd
,
344 pcieadm_show_devs_ofmt_t
*psdo
)
346 char dinst
[128], bdf
[128];
348 if (psd
->psd_nfilts
== 0) {
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
);
356 (void) snprintf(bdf
, sizeof (bdf
), "%x/%x/%x", psdo
->psdo_bus
,
357 psdo
->psdo_dev
, psdo
->psdo_func
);
359 for (uint_t i
= 0; i
< psd
->psd_nfilts
; i
++) {
360 const char *filt
= psd
->psd_filts
[i
];
362 if (strcmp(filt
, psdo
->psdo_path
) == 0) {
363 psd
->psd_used
[i
] = B_TRUE
;
367 if (strcmp(filt
, bdf
) == 0) {
368 psd
->psd_used
[i
] = B_TRUE
;
372 if (psdo
->psdo_driver
!= NULL
&&
373 strcmp(filt
, psdo
->psdo_driver
) == 0) {
374 psd
->psd_used
[i
] = B_TRUE
;
378 if (psdo
->psdo_driver
!= NULL
&& psdo
->psdo_instance
!= -1 &&
379 strcmp(filt
, dinst
) == 0) {
380 psd
->psd_used
[i
] = B_TRUE
;
384 if (strncmp("/devices", filt
, strlen("/devices")) == 0) {
385 filt
+= strlen("/devices");
388 if (strcmp(filt
, psdo
->psdo_path
) == 0) {
389 psd
->psd_used
[i
] = B_TRUE
;
397 pcieadm_show_devs_walk_cb(di_node_t node
, void *arg
)
399 int nprop
, *regs
= NULL
, *did
, *vid
, *mwidth
, *cwidth
, *rev
;
400 int *subvid
, *subsys
;
401 int64_t *mspeed
, *cspeed
, *sspeeds
;
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
;
409 bzero(&oarg
, sizeof (oarg
));
411 path
= di_devfs_path(node
);
413 err(EXIT_FAILURE
, "failed to construct devfs path for node: "
414 "%s", di_node_name(node
));
417 nprop
= di_prop_lookup_ints(DDI_DEV_T_ANY
, node
, "reg", ®s
);
419 errx(EXIT_FAILURE
, "failed to lookup regs array for %s",
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]);
428 if (oarg
.psdo_func
!= 0 && !psd
->psd_funcs
) {
432 oarg
.psdo_driver
= di_driver_name(node
);
433 oarg
.psdo_instance
= di_instance(node
);
435 nprop
= di_prop_lookup_ints(DDI_DEV_T_ANY
, node
, "device-id", &did
);
439 oarg
.psdo_did
= (uint16_t)*did
;
442 nprop
= di_prop_lookup_ints(DDI_DEV_T_ANY
, node
, "vendor-id", &vid
);
446 oarg
.psdo_vid
= (uint16_t)*vid
;
449 nprop
= di_prop_lookup_ints(DDI_DEV_T_ANY
, node
, "revision-id", &rev
);
453 oarg
.psdo_rev
= (uint16_t)*rev
;
456 nprop
= di_prop_lookup_ints(DDI_DEV_T_ANY
, node
, "subsystem-vendor-id",
459 oarg
.psdo_subvid
= -1;
461 oarg
.psdo_subvid
= (uint16_t)*subvid
;
464 nprop
= di_prop_lookup_ints(DDI_DEV_T_ANY
, node
, "subsystem-id",
467 oarg
.psdo_subsys
= -1;
469 oarg
.psdo_subsys
= (uint16_t)*subsys
;
472 oarg
.psdo_vendor
= "--";
473 if (oarg
.psdo_vid
!= -1) {
474 pcidb_vendor_t
*vend
= pcidb_lookup_vendor(pcidb
,
477 oarg
.psdo_vendor
= pcidb_vendor_name(vend
);
479 (void) snprintf(venstr
, sizeof (venstr
),
480 "Unknown vendor: 0x%x", oarg
.psdo_vid
);
481 oarg
.psdo_vendor
= venstr
;
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
);
490 oarg
.psdo_device
= pcidb_device_name(dev
);
492 (void) snprintf(devstr
, sizeof (devstr
),
493 "Unknown device: 0x%x", oarg
.psdo_did
);
494 oarg
.psdo_device
= devstr
;
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
504 oarg
.psdo_subvendor
= "--";
505 oarg
.psdo_subsystem
= "--";
506 if (oarg
.psdo_subvid
!= -1) {
507 pcidb_vendor_t
*vend
= pcidb_lookup_vendor(pcidb
,
510 oarg
.psdo_subvendor
= pcidb_vendor_name(vend
);
512 (void) snprintf(subvenstr
, sizeof (subvenstr
),
513 "Unknown vendor: 0x%x", oarg
.psdo_vid
);
514 oarg
.psdo_subvendor
= subvenstr
;
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
);
523 oarg
.psdo_subsystem
= pcidb_subvd_name(subvd
);
525 (void) snprintf(subsysstr
, sizeof (subsysstr
),
526 "Unknown subsystem: 0x%x", oarg
.psdo_subsys
);
527 oarg
.psdo_subsystem
= subsysstr
;
532 nprop
= di_prop_lookup_ints(DDI_DEV_T_ANY
, node
,
533 "pcie-link-maximum-width", &mwidth
);
535 oarg
.psdo_mwidth
= -1;
537 oarg
.psdo_mwidth
= *mwidth
;
540 nprop
= di_prop_lookup_ints(DDI_DEV_T_ANY
, node
,
541 "pcie-link-current-width", &cwidth
);
543 oarg
.psdo_cwidth
= -1;
545 oarg
.psdo_cwidth
= *cwidth
;
548 nprop
= di_prop_lookup_int64(DDI_DEV_T_ANY
, node
,
549 "pcie-link-maximum-speed", &mspeed
);
551 oarg
.psdo_mspeed
= -1;
553 oarg
.psdo_mspeed
= *mspeed
;
556 nprop
= di_prop_lookup_int64(DDI_DEV_T_ANY
, node
,
557 "pcie-link-current-speed", &cspeed
);
559 oarg
.psdo_cspeed
= -1;
561 oarg
.psdo_cspeed
= *cspeed
;
564 nprop
= di_prop_lookup_int64(DDI_DEV_T_ANY
, node
,
565 "pcie-link-supported-speeds", &sspeeds
);
567 oarg
.psdo_nspeeds
= nprop
;
568 oarg
.psdo_sspeeds
= sspeeds
;
570 oarg
.psdo_nspeeds
= 0;
571 oarg
.psdo_sspeeds
= NULL
;
574 if (pcieadm_show_devs_match(psd
, &oarg
)) {
575 ofmt_print(psd
->psd_ofmt
, &oarg
);
581 di_devfs_path_free(path
);
588 pcieadm_show_devs_usage(FILE *f
)
590 (void) fprintf(f
, "\tshow-devs\t[-F] [-H] [-s | -o field[,...] [-p]] "
595 pcieadm_show_devs_help(const char *fmt
, ...)
603 (void) fprintf(stderr
, "\n");
606 (void) fprintf(stderr
, "Usage: %s show-devs [-F] [-H] [-s | -o "
607 "field[,...] [-p]] [filter...]\n", pcieadm_progname
);
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");
644 pcieadm_show_devs(pcieadm_t
*pcip
, int argc
, char *argv
[])
648 const char *fields
= NULL
;
649 pcieadm_show_devs_t psd
;
650 pcieadm_di_walk_t walk
;
652 boolean_t parse
= B_FALSE
;
653 boolean_t speeds
= B_FALSE
;
656 * show-devs relies solely on the devinfo snapshot we already took.
657 * Formalize our privs immediately.
659 pcieadm_init_privs(pcip
);
661 bzero(&psd
, sizeof (psd
));
663 psd
.psd_funcs
= B_TRUE
;
665 while ((c
= getopt(argc
, argv
, ":FHo:ps")) != -1) {
668 psd
.psd_funcs
= B_FALSE
;
672 flags
|= OFMT_PARSABLE
;
675 flags
|= OFMT_NOHEADER
;
684 pcieadm_show_devs_help("option -%c requires an "
688 pcieadm_show_devs_help("unknown option: -%c", optopt
);
693 if (parse
&& fields
== NULL
) {
694 errx(EXIT_USAGE
, "-p requires fields specified with -o");
697 if (fields
!= NULL
&& speeds
) {
698 errx(EXIT_USAGE
, "-s cannot be used with with -o");
701 if (fields
== NULL
) {
703 fields
= pcieadm_show_dev_speeds
;
705 fields
= pcieadm_show_dev_fields
;
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 "
722 oferr
= ofmt_open(fields
, pcieadm_show_dev_ofmt
, flags
, 0,
724 ofmt_check(oferr
, parse
, psd
.psd_ofmt
, pcieadm_ofmt_errx
, warnx
);
727 walk
.pdw_func
= pcieadm_show_devs_walk_cb
;
729 pcieadm_di_walk(pcip
, &walk
);
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",
740 if (psd
.psd_nprint
== 0) {