diff options
Diffstat (limited to 'usr/src/cmd/pcieadm/pcieadm_devs.c')
| -rw-r--r-- | usr/src/cmd/pcieadm/pcieadm_devs.c | 568 |
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", ®s); + 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); + } +} |
