summaryrefslogtreecommitdiff
path: root/usr/src/cmd/pcieadm/pcieadm_devs.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/pcieadm/pcieadm_devs.c')
-rw-r--r--usr/src/cmd/pcieadm/pcieadm_devs.c568
1 files changed, 568 insertions, 0 deletions
diff --git a/usr/src/cmd/pcieadm/pcieadm_devs.c b/usr/src/cmd/pcieadm/pcieadm_devs.c
new file mode 100644
index 0000000000..5ca19ea1d9
--- /dev/null
+++ b/usr/src/cmd/pcieadm/pcieadm_devs.c
@@ -0,0 +1,568 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2021 Oxide Computer Company
+ */
+
+#include <err.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <ofmt.h>
+#include <strings.h>
+#include <sys/pci.h>
+
+#include "pcieadm.h"
+
+typedef struct pcieadm_show_devs {
+ pcieadm_t *psd_pia;
+ ofmt_handle_t psd_ofmt;
+ boolean_t psd_funcs;
+ int psd_nfilts;
+ char **psd_filts;
+ uint_t psd_nprint;
+} pcieadm_show_devs_t;
+
+typedef enum pcieadm_show_devs_otype {
+ PCIEADM_SDO_VID,
+ PCIEADM_SDO_DID,
+ PCIEADM_SDO_BDF,
+ PCIEADM_SDO_BDF_BUS,
+ PCIEADM_SDO_BDF_DEV,
+ PCIEADM_SDO_BDF_FUNC,
+ PCIEADM_SDO_DRIVER,
+ PCIEADM_SDO_TYPE,
+ PCIEADM_SDO_VENDOR,
+ PCIEADM_SDO_DEVICE,
+ PCIEADM_SDO_PATH,
+ PCIEADM_SDO_MAXSPEED,
+ PCIEADM_SDO_MAXWIDTH,
+ PCIEADM_SDO_CURSPEED,
+ PCIEADM_SDO_CURWIDTH,
+ PCIEADM_SDO_SUPSPEEDS
+} pcieadm_show_devs_otype_t;
+
+typedef struct pcieadm_show_devs_ofmt {
+ int psdo_vid;
+ int psdo_did;
+ uint_t psdo_bus;
+ uint_t psdo_dev;
+ uint_t psdo_func;
+ const char *psdo_path;
+ const char *psdo_vendor;
+ const char *psdo_device;
+ const char *psdo_driver;
+ int psdo_instance;
+ int psdo_mwidth;
+ int psdo_cwidth;
+ int64_t psdo_mspeed;
+ int64_t psdo_cspeed;
+ int psdo_nspeeds;
+ int64_t *psdo_sspeeds;
+} pcieadm_show_devs_ofmt_t;
+
+static uint_t
+pcieadm_speed2gen(int64_t speed)
+{
+ if (speed == 2500000000LL) {
+ return (1);
+ } else if (speed == 5000000000LL) {
+ return (2);
+ } else if (speed == 8000000000LL) {
+ return (3);
+ } else if (speed == 16000000000LL) {
+ return (4);
+ } else if (speed == 32000000000LL) {
+ return (5);
+ } else {
+ return (0);
+ }
+}
+
+static const char *
+pcieadm_speed2str(int64_t speed)
+{
+ if (speed == 2500000000LL) {
+ return ("2.5");
+ } else if (speed == 5000000000LL) {
+ return ("5.0");
+ } else if (speed == 8000000000LL) {
+ return ("8.0");
+ } else if (speed == 16000000000LL) {
+ return ("16.0");
+ } else if (speed == 32000000000LL) {
+ return ("32.0");
+ } else {
+ return (NULL);
+ }
+}
+
+static boolean_t
+pcieadm_show_devs_ofmt_cb(ofmt_arg_t *ofarg, char *buf, uint_t buflen)
+{
+ const char *str;
+ pcieadm_show_devs_ofmt_t *psdo = ofarg->ofmt_cbarg;
+ boolean_t first = B_TRUE;
+
+ switch (ofarg->ofmt_id) {
+ case PCIEADM_SDO_BDF:
+ if (snprintf(buf, buflen, "%x/%x/%x", psdo->psdo_bus,
+ psdo->psdo_dev, psdo->psdo_func) >= buflen) {
+ return (B_FALSE);
+ }
+ break;
+ case PCIEADM_SDO_BDF_BUS:
+ if (snprintf(buf, buflen, "%x", psdo->psdo_bus) >= buflen) {
+ return (B_FALSE);
+ }
+ break;
+ case PCIEADM_SDO_BDF_DEV:
+ if (snprintf(buf, buflen, "%x", psdo->psdo_dev) >= buflen) {
+ return (B_FALSE);
+ }
+ break;
+ case PCIEADM_SDO_BDF_FUNC:
+ if (snprintf(buf, buflen, "%x", psdo->psdo_func) >= buflen) {
+ return (B_FALSE);
+ }
+ break;
+ case PCIEADM_SDO_DRIVER:
+ if (psdo->psdo_driver == NULL || psdo->psdo_instance == -1) {
+ (void) snprintf(buf, buflen, "--");
+ } else if (snprintf(buf, buflen, "%s%d", psdo->psdo_driver,
+ psdo->psdo_instance) >= buflen) {
+ return (B_FALSE);
+ }
+ break;
+ case PCIEADM_SDO_PATH:
+ if (strlcat(buf, psdo->psdo_path, buflen) >= buflen) {
+ return (B_TRUE);
+ }
+ break;
+ case PCIEADM_SDO_VID:
+ if (psdo->psdo_vid == -1) {
+ (void) strlcat(buf, "--", buflen);
+ } else if (snprintf(buf, buflen, "%x", psdo->psdo_vid) >=
+ buflen) {
+ return (B_FALSE);
+ }
+ break;
+ case PCIEADM_SDO_DID:
+ if (psdo->psdo_did == -1) {
+ (void) strlcat(buf, "--", buflen);
+ } else if (snprintf(buf, buflen, "%x", psdo->psdo_did) >=
+ buflen) {
+ return (B_FALSE);
+ }
+ break;
+ case PCIEADM_SDO_VENDOR:
+ if (strlcat(buf, psdo->psdo_vendor, buflen) >= buflen) {
+ return (B_FALSE);
+ }
+ break;
+ case PCIEADM_SDO_DEVICE:
+ if (strlcat(buf, psdo->psdo_device, buflen) >= buflen) {
+ return (B_FALSE);
+ }
+ break;
+ case PCIEADM_SDO_MAXWIDTH:
+ if (psdo->psdo_mwidth <= 0) {
+ (void) strlcat(buf, "--", buflen);
+ } else if (snprintf(buf, buflen, "x%u", psdo->psdo_mwidth) >=
+ buflen) {
+ return (B_FALSE);
+ }
+ break;
+ case PCIEADM_SDO_CURWIDTH:
+ if (psdo->psdo_cwidth <= 0) {
+ (void) strlcat(buf, "--", buflen);
+ } else if (snprintf(buf, buflen, "x%u", psdo->psdo_cwidth) >=
+ buflen) {
+ return (B_FALSE);
+ }
+ break;
+ case PCIEADM_SDO_MAXSPEED:
+ str = pcieadm_speed2str(psdo->psdo_mspeed);
+ if (str == NULL) {
+ (void) strlcat(buf, "--", buflen);
+ } else if (snprintf(buf, buflen, "%s GT/s", str) >= buflen) {
+ return (B_FALSE);
+ }
+ break;
+ case PCIEADM_SDO_CURSPEED:
+ str = pcieadm_speed2str(psdo->psdo_cspeed);
+ if (str == NULL) {
+ (void) strlcat(buf, "--", buflen);
+ } else if (snprintf(buf, buflen, "%s GT/s", str) >= buflen) {
+ return (B_FALSE);
+ }
+ break;
+ case PCIEADM_SDO_SUPSPEEDS:
+ buf[0] = 0;
+ for (int i = 0; i < psdo->psdo_nspeeds; i++) {
+ const char *str;
+
+ str = pcieadm_speed2str(psdo->psdo_sspeeds[i]);
+ if (str == NULL) {
+ continue;
+ }
+
+ if (!first) {
+ if (strlcat(buf, ",", buflen) >= buflen) {
+ return (B_FALSE);
+ }
+ }
+ first = B_FALSE;
+
+ if (strlcat(buf, str, buflen) >= buflen) {
+ return (B_FALSE);
+ }
+ }
+ break;
+ case PCIEADM_SDO_TYPE:
+ if (pcieadm_speed2gen(psdo->psdo_mspeed) == 0 ||
+ psdo->psdo_mwidth == -1) {
+ if (strlcat(buf, "PCI", buflen) >= buflen) {
+ return (B_FALSE);
+ }
+ } else {
+ if (snprintf(buf, buflen, "PCIe Gen %ux%u",
+ pcieadm_speed2gen(psdo->psdo_mspeed),
+ psdo->psdo_mwidth) >= buflen) {
+ return (B_FALSE);
+ }
+ }
+ break;
+ default:
+ abort();
+ }
+ return (B_TRUE);
+}
+
+static const char *pcieadm_show_dev_fields = "bdf,type,driver,device";
+static const char *pcieadm_show_dev_speeds =
+ "bdf,driver,maxspeed,curspeed,maxwidth,curwidth,supspeeds";
+static const ofmt_field_t pcieadm_show_dev_ofmt[] = {
+ { "VID", 6, PCIEADM_SDO_VID, pcieadm_show_devs_ofmt_cb },
+ { "DID", 6, PCIEADM_SDO_DID, pcieadm_show_devs_ofmt_cb },
+ { "BDF", 8, PCIEADM_SDO_BDF, pcieadm_show_devs_ofmt_cb },
+ { "DRIVER", 15, PCIEADM_SDO_DRIVER, pcieadm_show_devs_ofmt_cb },
+ { "TYPE", 15, PCIEADM_SDO_TYPE, pcieadm_show_devs_ofmt_cb },
+ { "VENDOR", 30, PCIEADM_SDO_VENDOR, pcieadm_show_devs_ofmt_cb },
+ { "DEVICE", 30, PCIEADM_SDO_DEVICE, pcieadm_show_devs_ofmt_cb },
+ { "PATH", 30, PCIEADM_SDO_PATH, pcieadm_show_devs_ofmt_cb },
+ { "BUS", 4, PCIEADM_SDO_BDF_BUS, pcieadm_show_devs_ofmt_cb },
+ { "DEV", 4, PCIEADM_SDO_BDF_DEV, pcieadm_show_devs_ofmt_cb },
+ { "FUNC", 4, PCIEADM_SDO_BDF_FUNC, pcieadm_show_devs_ofmt_cb },
+ { "MAXSPEED", 10, PCIEADM_SDO_MAXSPEED, pcieadm_show_devs_ofmt_cb },
+ { "MAXWIDTH", 10, PCIEADM_SDO_MAXWIDTH, pcieadm_show_devs_ofmt_cb },
+ { "CURSPEED", 10, PCIEADM_SDO_CURSPEED, pcieadm_show_devs_ofmt_cb },
+ { "CURWIDTH", 10, PCIEADM_SDO_CURWIDTH, pcieadm_show_devs_ofmt_cb },
+ { "SUPSPEEDS", 20, PCIEADM_SDO_SUPSPEEDS, pcieadm_show_devs_ofmt_cb },
+ { NULL, 0, 0, NULL }
+};
+
+static boolean_t
+pcieadm_show_devs_match(pcieadm_show_devs_t *psd,
+ pcieadm_show_devs_ofmt_t *psdo)
+{
+ char dinst[128], bdf[128];
+
+ if (psd->psd_nfilts == 0) {
+ return (B_TRUE);
+ }
+
+ if (psdo->psdo_driver != NULL && psdo->psdo_instance != -1) {
+ (void) snprintf(dinst, sizeof (dinst), "%s%d",
+ psdo->psdo_driver, psdo->psdo_instance);
+ }
+ (void) snprintf(bdf, sizeof (bdf), "%x/%x/%x", psdo->psdo_bus,
+ psdo->psdo_dev, psdo->psdo_func);
+
+ for (uint_t i = 0; i < psd->psd_nfilts; i++) {
+ const char *filt = psd->psd_filts[i];
+
+ if (strcmp(filt, psdo->psdo_path) == 0) {
+ return (B_TRUE);
+ }
+
+ if (strcmp(filt, bdf) == 0) {
+ return (B_TRUE);
+ }
+
+ if (psdo->psdo_driver != NULL &&
+ strcmp(filt, psdo->psdo_driver) == 0) {
+ return (B_TRUE);
+ }
+
+ if (psdo->psdo_driver != NULL && psdo->psdo_instance != -1 &&
+ strcmp(filt, dinst) == 0) {
+ return (B_TRUE);
+ }
+
+ if (strncmp("/devices", filt, strlen("/devices")) == 0) {
+ filt += strlen("/devices");
+ }
+
+ if (strcmp(filt, psdo->psdo_path) == 0) {
+ return (B_TRUE);
+ }
+ }
+ return (B_FALSE);
+}
+
+static int
+pcieadm_show_devs_walk_cb(di_node_t node, void *arg)
+{
+ int nprop, *regs = NULL, *did, *vid, *mwidth, *cwidth;
+ int64_t *mspeed, *cspeed, *sspeeds;
+ char *path = NULL;
+ pcieadm_show_devs_t *psd = arg;
+ int ret = DI_WALK_CONTINUE;
+ pcieadm_show_devs_ofmt_t oarg;
+ pcidb_hdl_t *pcidb = psd->psd_pia->pia_pcidb;
+
+ bzero(&oarg, sizeof (oarg));
+
+ path = di_devfs_path(node);
+ if (path == NULL) {
+ err(EXIT_FAILURE, "failed to construct devfs path for node: "
+ "%s (%s)", di_node_name(node));
+ }
+
+ nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", &regs);
+ if (nprop <= 0) {
+ errx(EXIT_FAILURE, "failed to lookup regs array for %s",
+ path);
+ }
+
+ oarg.psdo_path = path;
+ oarg.psdo_bus = PCI_REG_BUS_G(regs[0]);
+ oarg.psdo_dev = PCI_REG_DEV_G(regs[0]);
+ oarg.psdo_func = PCI_REG_FUNC_G(regs[0]);
+
+ if (oarg.psdo_func != 0 && !psd->psd_funcs) {
+ goto done;
+ }
+
+ oarg.psdo_driver = di_driver_name(node);
+ oarg.psdo_instance = di_instance(node);
+
+ nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "device-id", &did);
+ if (nprop != 1) {
+ oarg.psdo_did = -1;
+ } else {
+ oarg.psdo_did = (uint16_t)*did;
+ }
+
+ nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "vendor-id", &vid);
+ if (nprop != 1) {
+ oarg.psdo_vid = -1;
+ } else {
+ oarg.psdo_vid = (uint16_t)*vid;
+ }
+
+ oarg.psdo_vendor = "--";
+ if (oarg.psdo_vid != -1) {
+ pcidb_vendor_t *vend = pcidb_lookup_vendor(pcidb,
+ oarg.psdo_vid);
+ if (vend != NULL) {
+ oarg.psdo_vendor = pcidb_vendor_name(vend);
+ }
+ }
+
+ oarg.psdo_device = "--";
+ if (oarg.psdo_vid != -1 && oarg.psdo_did != -1) {
+ pcidb_device_t *dev = pcidb_lookup_device(pcidb,
+ oarg.psdo_vid, oarg.psdo_did);
+ if (dev != NULL) {
+ oarg.psdo_device = pcidb_device_name(dev);
+
+ }
+ }
+
+ nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node,
+ "pcie-link-maximum-width", &mwidth);
+ if (nprop != 1) {
+ oarg.psdo_mwidth = -1;
+ } else {
+ oarg.psdo_mwidth = *mwidth;
+ }
+
+ nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node,
+ "pcie-link-current-width", &cwidth);
+ if (nprop != 1) {
+ oarg.psdo_cwidth = -1;
+ } else {
+ oarg.psdo_cwidth = *cwidth;
+ }
+
+ nprop = di_prop_lookup_int64(DDI_DEV_T_ANY, node,
+ "pcie-link-maximum-speed", &mspeed);
+ if (nprop != 1) {
+ oarg.psdo_mspeed = -1;
+ } else {
+ oarg.psdo_mspeed = *mspeed;
+ }
+
+ nprop = di_prop_lookup_int64(DDI_DEV_T_ANY, node,
+ "pcie-link-current-speed", &cspeed);
+ if (nprop != 1) {
+ oarg.psdo_cspeed = -1;
+ } else {
+ oarg.psdo_cspeed = *cspeed;
+ }
+
+ nprop = di_prop_lookup_int64(DDI_DEV_T_ANY, node,
+ "pcie-link-supported-speeds", &sspeeds);
+ if (nprop > 0) {
+ oarg.psdo_nspeeds = nprop;
+ oarg.psdo_sspeeds = sspeeds;
+ } else {
+ oarg.psdo_nspeeds = 0;
+ oarg.psdo_sspeeds = NULL;
+ }
+
+ if (pcieadm_show_devs_match(psd, &oarg)) {
+ ofmt_print(psd->psd_ofmt, &oarg);
+ psd->psd_nprint++;
+ }
+
+done:
+ if (path != NULL) {
+ di_devfs_path_free(path);
+ }
+
+ return (ret);
+}
+
+void
+pcieadm_show_devs_usage(FILE *f)
+{
+ (void) fprintf(f, "\tshow-devs\t[-F] [-H] [-s | -o field[,...] [-p]] "
+ "[filter...]\n");
+}
+
+static void
+pcieadm_show_devs_help(const char *fmt, ...)
+{
+ if (fmt != NULL) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ vwarnx(fmt, ap);
+ va_end(ap);
+ (void) fprintf(stderr, "\n");
+ }
+
+ (void) fprintf(stderr, "Usage: %s show-devs [-F] [-H] [-s | -o "
+ "field[,...] [-p]] [filter...]\n", pcieadm_progname);
+
+ (void) fprintf(stderr, "\nList PCI devices and functions in the "
+ "system. Each <filter> selects a set\nof devices to show and "
+ "can be a driver name, instance, /devices path, or\nb/d/f.\n\n"
+ "\t-F\t\tdo not display PCI functions\n"
+ "\t-H\t\tomit the column header\n"
+ "\t-o field\toutput fields to print\n"
+ "\t-p\t\tparsable output (requires -o)\n"
+ "\t-s\t\tlist speeds and widths\n");
+
+}
+
+int
+pcieadm_show_devs(pcieadm_t *pcip, int argc, char *argv[])
+{
+ int c;
+ uint_t flags = 0;
+ const char *fields = NULL;
+ pcieadm_show_devs_t psd;
+ pcieadm_di_walk_t walk;
+ ofmt_status_t oferr;
+ boolean_t parse = B_FALSE;
+ boolean_t speeds = B_FALSE;
+
+ /*
+ * show-devs relies solely on the devinfo snapshot we already took.
+ * Formalize our privs immediately.
+ */
+ pcieadm_init_privs(pcip);
+
+ bzero(&psd, sizeof (psd));
+ psd.psd_pia = pcip;
+ psd.psd_funcs = B_TRUE;
+
+ while ((c = getopt(argc, argv, ":FHo:ps")) != -1) {
+ switch (c) {
+ case 'F':
+ psd.psd_funcs = B_FALSE;
+ break;
+ case 'p':
+ parse = B_TRUE;
+ flags |= OFMT_PARSABLE;
+ break;
+ case 'H':
+ flags |= OFMT_NOHEADER;
+ break;
+ case 's':
+ speeds = B_TRUE;
+ break;
+ case 'o':
+ fields = optarg;
+ break;
+ case ':':
+ pcieadm_show_devs_help("option -%c requires an "
+ "argument", optopt);
+ exit(EXIT_USAGE);
+ case '?':
+ pcieadm_show_devs_help("unknown option: -%c", optopt);
+ exit(EXIT_USAGE);
+ }
+ }
+
+ if (parse && fields == NULL) {
+ errx(EXIT_USAGE, "-p requires fields specified with -o");
+ }
+
+ if (fields != NULL && speeds) {
+ errx(EXIT_USAGE, "-s cannot be used with with -o");
+ }
+
+ if (fields == NULL) {
+ if (speeds) {
+ fields = pcieadm_show_dev_speeds;
+ } else {
+ fields = pcieadm_show_dev_fields;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 0) {
+ psd.psd_nfilts = argc;
+ psd.psd_filts = argv;
+ }
+
+ oferr = ofmt_open(fields, pcieadm_show_dev_ofmt, flags, 0,
+ &psd.psd_ofmt);
+ ofmt_check(oferr, parse, psd.psd_ofmt, pcieadm_ofmt_errx, warnx);
+
+ walk.pdw_arg = &psd;
+ walk.pdw_func = pcieadm_show_devs_walk_cb;
+
+ pcieadm_di_walk(pcip, &walk);
+
+ if (psd.psd_nprint > 0) {
+ return (EXIT_SUCCESS);
+ } else {
+ return (EXIT_FAILURE);
+ }
+}