summaryrefslogtreecommitdiff
path: root/usr/src/cmd/pcidb/pcidb.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/pcidb/pcidb.c')
-rw-r--r--usr/src/cmd/pcidb/pcidb.c884
1 files changed, 884 insertions, 0 deletions
diff --git a/usr/src/cmd/pcidb/pcidb.c b/usr/src/cmd/pcidb/pcidb.c
new file mode 100644
index 0000000000..7a0785c7ae
--- /dev/null
+++ b/usr/src/cmd/pcidb/pcidb.c
@@ -0,0 +1,884 @@
+/*
+ * 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
+ */
+
+/*
+ * A tool to interface with the pci.ids database driven by libpcidb.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <pcidb.h>
+#include <err.h>
+#include <libgen.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <ofmt.h>
+#include <errno.h>
+#include <sys/debug.h>
+#include <priv.h>
+
+#define EXIT_USAGE 2
+
+static char *pcidb_progname;
+
+typedef enum {
+ PCIDB_MODE_UNKNOWN,
+ PCIDB_MODE_LIST,
+ PCIDB_MODE_SEARCH,
+ PCIDB_MODE_LOOKUP
+} pcidb_mode_t;
+
+typedef enum {
+ PCIDB_TABLE_NONE,
+ PCIDB_TABLE_VENDOR,
+ PCIDB_TABLE_DEVICE,
+ PCIDB_TABLE_SUBSYSTEM,
+ PCIDB_TABLE_CLASS,
+ PCIDB_TABLE_SUBCLASS,
+ PCIDB_TABLE_PROGIF
+} pcidb_table_t;
+
+typedef enum {
+ PCIDB_OFMT_VID,
+ PCIDB_OFMT_VENSTR,
+ PCIDB_OFMT_DID,
+ PCIDB_OFMT_DEVSTR,
+ PCIDB_OFMT_SVID,
+ PCIDB_OFMT_SDID,
+ PCIDB_OFMT_SUBVENSTR,
+ PCIDB_OFMT_SUBSYSSTR,
+ PCIDB_OFMT_BCC,
+ PCIDB_OFMT_CLASSSTR,
+ PCIDB_OFMT_SCC,
+ PCIDB_OFMT_SUBCLASSSTR,
+ PCIDB_OFMT_PI,
+ PCIDB_OFMT_PROGIFSTR
+} pcidb_ofmt_t;
+
+typedef struct pcidb_filter {
+ uint32_t pft_vend;
+ uint32_t pft_dev;
+ uint32_t pft_subven;
+ uint32_t pft_subdev;
+ uint32_t pft_class;
+ uint32_t pft_subclass;
+ uint32_t pft_progif;
+} pcidb_filter_t;
+
+#define PCIDB_NOFILTER UINT32_MAX
+
+typedef struct pcidb_walk {
+ pcidb_hdl_t *pw_hdl;
+ ofmt_handle_t pw_ofmt;
+ pcidb_vendor_t *pw_vendor;
+ pcidb_device_t *pw_device;
+ pcidb_subvd_t *pw_subvd;
+ pcidb_class_t *pw_class;
+ pcidb_subclass_t *pw_subclass;
+ pcidb_progif_t *pw_progif;
+ boolean_t pw_strcase;
+ uint_t pw_nfilters;
+ pcidb_filter_t *pw_filters;
+} pcidb_walk_t;
+
+static boolean_t
+pcidb_write_vendor(ofmt_arg_t *ofarg, char *buf, uint_t buflen)
+{
+ pcidb_walk_t *walk = ofarg->ofmt_cbarg;
+
+ VERIFY(walk->pw_vendor != NULL);
+ switch (ofarg->ofmt_id) {
+ case PCIDB_OFMT_VID:
+ (void) snprintf(buf, buflen, "%x",
+ pcidb_vendor_id(walk->pw_vendor));
+ break;
+ case PCIDB_OFMT_VENSTR:
+ (void) strlcpy(buf, pcidb_vendor_name(walk->pw_vendor), buflen);
+ break;
+ default:
+ abort();
+ }
+ return (B_TRUE);
+}
+
+static boolean_t
+pcidb_write_device(ofmt_arg_t *ofarg, char *buf, uint_t buflen)
+{
+ pcidb_walk_t *walk = ofarg->ofmt_cbarg;
+
+ VERIFY(walk->pw_device != NULL);
+ switch (ofarg->ofmt_id) {
+ case PCIDB_OFMT_DID:
+ (void) snprintf(buf, buflen, "%x",
+ pcidb_device_id(walk->pw_device));
+ break;
+ case PCIDB_OFMT_DEVSTR:
+ (void) strlcpy(buf, pcidb_device_name(walk->pw_device), buflen);
+ break;
+ default:
+ abort();
+ }
+ return (B_TRUE);
+}
+
+static boolean_t
+pcidb_write_subsystem(ofmt_arg_t *ofarg, char *buf, uint_t buflen)
+{
+ pcidb_walk_t *walk = ofarg->ofmt_cbarg;
+ pcidb_vendor_t *vendor;
+
+ VERIFY(walk->pw_subvd != NULL);
+ switch (ofarg->ofmt_id) {
+ case PCIDB_OFMT_SVID:
+ (void) snprintf(buf, buflen, "%x",
+ pcidb_subvd_svid(walk->pw_subvd));
+ break;
+ case PCIDB_OFMT_SDID:
+ (void) snprintf(buf, buflen, "%x",
+ pcidb_subvd_sdid(walk->pw_subvd));
+ break;
+ case PCIDB_OFMT_SUBSYSSTR:
+ (void) strlcpy(buf, pcidb_subvd_name(walk->pw_subvd), buflen);
+ break;
+ case PCIDB_OFMT_SUBVENSTR:
+ vendor = pcidb_lookup_vendor(walk->pw_hdl,
+ pcidb_subvd_svid(walk->pw_subvd));
+ if (vendor == NULL) {
+ return (B_FALSE);
+ }
+ (void) strlcpy(buf, pcidb_vendor_name(vendor), buflen);
+ break;
+ default:
+ abort();
+ }
+ return (B_TRUE);
+}
+
+static boolean_t
+pcidb_write_class(ofmt_arg_t *ofarg, char *buf, uint_t buflen)
+{
+ pcidb_walk_t *walk = ofarg->ofmt_cbarg;
+
+ VERIFY(walk->pw_class != NULL);
+ switch (ofarg->ofmt_id) {
+ case PCIDB_OFMT_BCC:
+ (void) snprintf(buf, buflen, "%x",
+ pcidb_class_code(walk->pw_class));
+ break;
+ case PCIDB_OFMT_CLASSSTR:
+ (void) strlcpy(buf, pcidb_class_name(walk->pw_class), buflen);
+ break;
+ default:
+ abort();
+ }
+ return (B_TRUE);
+}
+
+static boolean_t
+pcidb_write_subclass(ofmt_arg_t *ofarg, char *buf, uint_t buflen)
+{
+ pcidb_walk_t *walk = ofarg->ofmt_cbarg;
+
+ VERIFY(walk->pw_subclass != NULL);
+ switch (ofarg->ofmt_id) {
+ case PCIDB_OFMT_SCC:
+ (void) snprintf(buf, buflen, "%x",
+ pcidb_subclass_code(walk->pw_subclass));
+ break;
+ case PCIDB_OFMT_SUBCLASSSTR:
+ (void) strlcpy(buf, pcidb_subclass_name(walk->pw_subclass),
+ buflen);
+ break;
+ default:
+ abort();
+ }
+ return (B_TRUE);
+}
+
+static boolean_t
+pcidb_write_progif(ofmt_arg_t *ofarg, char *buf, uint_t buflen)
+{
+ pcidb_walk_t *walk = ofarg->ofmt_cbarg;
+
+ VERIFY(walk->pw_progif != NULL);
+ switch (ofarg->ofmt_id) {
+ case PCIDB_OFMT_PI:
+ (void) snprintf(buf, buflen, "%x",
+ pcidb_progif_code(walk->pw_progif));
+ break;
+ case PCIDB_OFMT_PROGIFSTR:
+ (void) strlcpy(buf, pcidb_progif_name(walk->pw_progif),
+ buflen);
+ break;
+ default:
+ abort();
+ }
+ return (B_TRUE);
+}
+
+static const char *pcidb_vendor_fields = "vid,vendor";
+static const ofmt_field_t pcidb_vendor_ofmt[] = {
+ { "VID", 8, PCIDB_OFMT_VID, pcidb_write_vendor },
+ { "VENDOR", 30, PCIDB_OFMT_VENSTR, pcidb_write_vendor },
+ { NULL, 0, 0, NULL }
+};
+
+static const char *pcidb_device_fields = "vid,did,vendor,device";
+static const ofmt_field_t pcidb_device_ofmt[] = {
+ { "VID", 8, PCIDB_OFMT_VID, pcidb_write_vendor },
+ { "VENDOR", 30, PCIDB_OFMT_VENSTR, pcidb_write_vendor },
+ { "DID", 8, PCIDB_OFMT_DID, pcidb_write_device },
+ { "DEVICE", 30, PCIDB_OFMT_DEVSTR, pcidb_write_device },
+ { NULL, 0, 0, NULL }
+};
+
+static const char *pcidb_subsystem_fields = "vid,did,svid,sdid,subsystem";
+static const ofmt_field_t pcidb_subsystem_ofmt[] = {
+ { "VID", 8, PCIDB_OFMT_VID, pcidb_write_vendor },
+ { "VENDOR", 30, PCIDB_OFMT_VENSTR, pcidb_write_vendor },
+ { "DID", 8, PCIDB_OFMT_DID, pcidb_write_device },
+ { "DEVICE", 30, PCIDB_OFMT_DEVSTR, pcidb_write_device },
+ { "SVID", 8, PCIDB_OFMT_SVID, pcidb_write_subsystem },
+ { "SDID", 8, PCIDB_OFMT_SDID, pcidb_write_subsystem },
+ { "SUBSYSTEM", 30, PCIDB_OFMT_SUBSYSSTR, pcidb_write_subsystem },
+ { "SUBVENDOR", 30, PCIDB_OFMT_SUBVENSTR, pcidb_write_subsystem },
+ { NULL, 0, 0, NULL }
+};
+
+static const char *pcidb_class_fields = "bcc,class";
+static const ofmt_field_t pcidb_class_ofmt[] = {
+ { "BCC", 6, PCIDB_OFMT_BCC, pcidb_write_class },
+ { "CLASS", 30, PCIDB_OFMT_CLASSSTR, pcidb_write_class },
+ { NULL, 0, 0, NULL }
+};
+
+static const char *pcidb_subclass_fields = "bcc,scc,class,subclass";
+static const ofmt_field_t pcidb_subclass_ofmt[] = {
+ { "BCC", 6, PCIDB_OFMT_BCC, pcidb_write_class },
+ { "CLASS", 30, PCIDB_OFMT_CLASSSTR, pcidb_write_class },
+ { "SCC", 6, PCIDB_OFMT_SCC, pcidb_write_subclass },
+ { "SUBCLASS", 30, PCIDB_OFMT_SUBCLASSSTR, pcidb_write_subclass },
+ { NULL, 0, 0, NULL }
+};
+
+static const char *pcidb_progif_fields = "bcc,scc,pi,subclass,interface";
+static const ofmt_field_t pcidb_progif_ofmt[] = {
+ { "BCC", 6, PCIDB_OFMT_BCC, pcidb_write_class },
+ { "CLASS", 30, PCIDB_OFMT_CLASSSTR, pcidb_write_class },
+ { "SCC", 6, PCIDB_OFMT_SCC, pcidb_write_subclass },
+ { "SUBCLASS", 30, PCIDB_OFMT_SUBCLASSSTR, pcidb_write_subclass },
+ { "PI", 6, PCIDB_OFMT_PI, pcidb_write_progif },
+ { "INTERFACE", 30, PCIDB_OFMT_PROGIFSTR, pcidb_write_progif },
+ { NULL, 0, 0, NULL }
+};
+
+static void
+pcidb_ofmt_errx(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ verrx(EXIT_FAILURE, fmt, ap);
+}
+
+static boolean_t
+pcidb_filter_match(pcidb_walk_t *walk)
+{
+ if (walk->pw_nfilters == 0) {
+ return (B_TRUE);
+ }
+
+ for (uint_t i = 0; i < walk->pw_nfilters; i++) {
+ const pcidb_filter_t *filt = &walk->pw_filters[i];
+ if (filt->pft_vend != PCIDB_NOFILTER &&
+ (walk->pw_vendor == NULL ||
+ filt->pft_vend != pcidb_vendor_id(walk->pw_vendor))) {
+ continue;
+ }
+
+ if (filt->pft_dev != PCIDB_NOFILTER &&
+ (walk->pw_device == NULL ||
+ filt->pft_dev != pcidb_device_id(walk->pw_device))) {
+ continue;
+ }
+
+ if (filt->pft_subven != PCIDB_NOFILTER &&
+ (walk->pw_subvd == NULL ||
+ filt->pft_subven != pcidb_subvd_svid(walk->pw_subvd))) {
+ continue;
+ }
+
+ if (filt->pft_subdev != PCIDB_NOFILTER &&
+ (walk->pw_subvd == NULL ||
+ filt->pft_subdev != pcidb_subvd_sdid(walk->pw_subvd))) {
+ continue;
+ }
+
+ if (filt->pft_class != PCIDB_NOFILTER &&
+ (walk->pw_class == NULL ||
+ filt->pft_class != pcidb_class_code(walk->pw_class))) {
+ continue;
+ }
+
+ if (filt->pft_subclass != PCIDB_NOFILTER &&
+ (walk->pw_subclass == NULL ||
+ filt->pft_subclass !=
+ pcidb_subclass_code(walk->pw_subclass))) {
+ continue;
+ }
+
+ if (filt->pft_progif != PCIDB_NOFILTER &&
+ (walk->pw_progif == NULL ||
+ filt->pft_progif != pcidb_progif_code(walk->pw_progif))) {
+ continue;
+ }
+
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
+static void
+pcidb_walk_vendors(pcidb_walk_t *walk)
+{
+ pcidb_hdl_t *hdl = walk->pw_hdl;
+
+ for (pcidb_vendor_t *vend = pcidb_vendor_iter(hdl); vend != NULL;
+ vend = pcidb_vendor_iter_next(vend)) {
+ walk->pw_vendor = vend;
+ if (!pcidb_filter_match(walk))
+ continue;
+ ofmt_print(walk->pw_ofmt, walk);
+ }
+}
+
+static void
+pcidb_walk_devices(pcidb_walk_t *walk)
+{
+ pcidb_hdl_t *hdl = walk->pw_hdl;
+
+ for (pcidb_vendor_t *vend = pcidb_vendor_iter(hdl); vend != NULL;
+ vend = pcidb_vendor_iter_next(vend)) {
+ walk->pw_vendor = vend;
+ for (pcidb_device_t *dev = pcidb_device_iter(vend); dev != NULL;
+ dev = pcidb_device_iter_next(dev)) {
+ walk->pw_device = dev;
+ if (!pcidb_filter_match(walk))
+ continue;
+ ofmt_print(walk->pw_ofmt, walk);
+ }
+ }
+}
+
+static void
+pcidb_walk_subsystems(pcidb_walk_t *walk)
+{
+ pcidb_hdl_t *hdl = walk->pw_hdl;
+
+ for (pcidb_vendor_t *vend = pcidb_vendor_iter(hdl); vend != NULL;
+ vend = pcidb_vendor_iter_next(vend)) {
+ walk->pw_vendor = vend;
+ for (pcidb_device_t *dev = pcidb_device_iter(vend); dev != NULL;
+ dev = pcidb_device_iter_next(dev)) {
+ walk->pw_device = dev;
+ for (pcidb_subvd_t *sub = pcidb_subvd_iter(dev);
+ sub != NULL; sub = pcidb_subvd_iter_next(sub)) {
+ walk->pw_subvd = sub;
+ if (!pcidb_filter_match(walk))
+ continue;
+ ofmt_print(walk->pw_ofmt, walk);
+ }
+ }
+
+ }
+}
+
+static void
+pcidb_walk_classes(pcidb_walk_t *walk)
+{
+ for (pcidb_class_t *class = pcidb_class_iter(walk->pw_hdl);
+ class != NULL; class = pcidb_class_iter_next(class)) {
+ walk->pw_class = class;
+ if (!pcidb_filter_match(walk))
+ continue;
+ ofmt_print(walk->pw_ofmt, walk);
+ }
+}
+
+static void
+pcidb_walk_subclasses(pcidb_walk_t *walk)
+{
+ for (pcidb_class_t *class = pcidb_class_iter(walk->pw_hdl);
+ class != NULL; class = pcidb_class_iter_next(class)) {
+ walk->pw_class = class;
+ for (pcidb_subclass_t *sub = pcidb_subclass_iter(class);
+ sub != NULL; sub = pcidb_subclass_iter_next(sub)) {
+ walk->pw_subclass = sub;
+ if (!pcidb_filter_match(walk))
+ continue;
+ ofmt_print(walk->pw_ofmt, walk);
+ }
+ }
+}
+
+static void
+pcidb_walk_progifs(pcidb_walk_t *walk)
+{
+ for (pcidb_class_t *class = pcidb_class_iter(walk->pw_hdl);
+ class != NULL; class = pcidb_class_iter_next(class)) {
+ walk->pw_class = class;
+ for (pcidb_subclass_t *sub = pcidb_subclass_iter(class);
+ sub != NULL; sub = pcidb_subclass_iter_next(sub)) {
+ walk->pw_subclass = sub;
+ for (pcidb_progif_t *progif = pcidb_progif_iter(sub);
+ progif != NULL;
+ progif = pcidb_progif_iter_next(progif)) {
+ walk->pw_progif = progif;
+ if (!pcidb_filter_match(walk))
+ continue;
+ ofmt_print(walk->pw_ofmt, walk);
+ }
+ }
+ }
+}
+
+static void
+pcidb_parse_class_filter(pcidb_filter_t *filter, char *arg, const char *orig)
+{
+ size_t len;
+ unsigned long val;
+ char *eptr;
+
+ filter->pft_vend = filter->pft_dev = PCIDB_NOFILTER;
+ filter->pft_subven = filter->pft_subdev = PCIDB_NOFILTER;
+
+ len = strlen(arg);
+ if (len != 2 && len != 4 && len != 6) {
+ errx(EXIT_FAILURE, "invalid class filter: '%s': bad length",
+ orig);
+ }
+
+ errno = 0;
+ val = strtoul(arg, &eptr, 16);
+ if (errno != 0 || *eptr != '\0') {
+ errx(EXIT_FAILURE, "invalid class filter: '%s': failed to "
+ "parse hex string", orig);
+ }
+
+ if (len == 6) {
+ filter->pft_progif = val & 0xff;
+ val = val >> 8;
+ } else {
+ filter->pft_progif = PCIDB_NOFILTER;
+ }
+
+ if (len >= 4) {
+ filter->pft_subclass = val & 0xff;
+ val = val >> 8;
+ } else {
+ filter->pft_subclass = PCIDB_NOFILTER;
+ }
+
+ filter->pft_class = val & 0xff;
+}
+
+static void
+pcidb_parse_device_filter(pcidb_filter_t *filter, char *arg, const char *orig)
+{
+ unsigned long val;
+ uint32_t primary, secondary;
+ char *eptr;
+
+ filter->pft_vend = filter->pft_dev = PCIDB_NOFILTER;
+ filter->pft_subven = filter->pft_subdev = PCIDB_NOFILTER;
+ filter->pft_class = filter->pft_subclass = PCIDB_NOFILTER;
+ filter->pft_progif = PCIDB_NOFILTER;
+
+ errno = 0;
+ val = strtoul(arg, &eptr, 16);
+ if (errno != 0 || (*eptr != '\0' && *eptr != ',')) {
+ errx(EXIT_FAILURE, "invalid device filter: '%s': failed to "
+ "parse hex string", orig);
+ }
+
+ if (val > UINT16_MAX) {
+ errx(EXIT_FAILURE, "invalid id: %x is larger than 0xffff", val);
+ }
+
+ primary = (uint32_t)val;
+ if (*eptr == '\0') {
+ filter->pft_vend = primary;
+ return;
+ } else if (strcmp(eptr, ",s") == 0) {
+ filter->pft_subven = primary;
+ return;
+ } else if (eptr[1] == '\0') {
+ errx(EXIT_FAILURE, "invalid device filter: '%s': filter "
+ "terminated early", arg);
+ }
+
+ arg = eptr + 1;
+ val = strtoul(arg, &eptr, 16);
+ if (errno != 0 || (*eptr != '\0' && *eptr != ',' && *eptr != '.')) {
+ errx(EXIT_FAILURE, "invalid device filter: '%s': failed to "
+ "parse hex string at %s", orig, arg);
+ }
+
+ if (val > UINT16_MAX) {
+ errx(EXIT_FAILURE, "invalid id: %x is larger than 0xffff", val);
+ }
+
+ secondary = (uint32_t)val;
+ if (*eptr == '\0') {
+ filter->pft_vend = primary;
+ filter->pft_dev = secondary;
+ return;
+ } else if (eptr[1] == '\0') {
+ errx(EXIT_FAILURE, "invalid device filter: '%s': filter "
+ "terminated early", arg);
+ }
+
+ if (*eptr == ',') {
+ if (eptr[1] == 'p' && eptr[2] == '\0') {
+ filter->pft_vend = primary;
+ filter->pft_dev = secondary;
+ return;
+ }
+ if (eptr[1] == 's' && eptr[2] == '\0') {
+ filter->pft_subven = primary;
+ filter->pft_subdev = secondary;
+ return;
+ }
+ errx(EXIT_FAILURE, "invalid device filter: '%s': invalid "
+ "trailing comma at %s, expected either ,p or ,s",
+ orig, eptr);
+ }
+
+ filter->pft_vend = primary;
+ filter->pft_dev = secondary;
+
+ arg = eptr + 1;
+ errno = 0;
+ val = strtoul(arg, &eptr, 16);
+ if (errno != 0 || (*eptr != '\0' && *eptr != ',')) {
+ errx(EXIT_FAILURE, "invalid device filter: '%s': failed to "
+ "parse hex string at %s", orig, arg);
+ }
+
+ if (val > UINT16_MAX) {
+ errx(EXIT_FAILURE, "invalid id: %x is larger than 0xffff", val);
+ }
+
+ filter->pft_subven = (uint32_t)val;
+ if (*eptr == '\0') {
+ return;
+ } else if (eptr[1] == '\0') {
+ errx(EXIT_FAILURE, "invalid device filter: '%s': filter "
+ "terminated early", arg);
+ }
+
+ arg = eptr + 1;
+ errno = 0;
+ val = strtoul(arg, &eptr, 16);
+ if (errno != 0 || *eptr != '\0') {
+ errx(EXIT_FAILURE, "invalid device filter: '%s': failed to "
+ "parse hex string at %s", orig, arg);
+ }
+
+ if (val > UINT16_MAX) {
+ errx(EXIT_FAILURE, "invalid id: %x is larger than 0xffff", val);
+ }
+
+ filter->pft_subdev = (uint32_t)val;
+}
+
+
+/*
+ * Process a series of alias style ways of indicating numeric filters. Use the
+ * basic alias format for now.
+ */
+static void
+pcidb_process_filters(int argc, char *argv[], pcidb_walk_t *walkp)
+{
+ if (argc <= 0) {
+ walkp->pw_nfilters = 0;
+ return;
+ }
+
+ walkp->pw_nfilters = argc;
+ walkp->pw_filters = calloc(walkp->pw_nfilters, sizeof (pcidb_filter_t));
+ if (walkp->pw_filters == NULL) {
+ err(EXIT_FAILURE, "failed to allocate memory for filters");
+ }
+
+ for (int i = 0; i < argc; i++) {
+ char *str = strdup(argv[i]);
+
+ if (str == NULL) {
+ errx(EXIT_FAILURE, "failed to duplicate string %s",
+ argv[i]);
+ }
+
+ if (strncmp(str, "pciexclass,", 11) == 0) {
+ pcidb_parse_class_filter(&walkp->pw_filters[i],
+ str + 11, argv[i]);
+ } else if (strncmp(str, "pciclass,", 9) == 0) {
+ pcidb_parse_class_filter(&walkp->pw_filters[i], str + 9,
+ argv[i]);
+ } else if (strncmp(str, "pciex", 5) == 0) {
+ pcidb_parse_device_filter(&walkp->pw_filters[i],
+ str + 5, argv[i]);
+ } else if (strncmp(str, "pci", 3) == 0) {
+ pcidb_parse_device_filter(&walkp->pw_filters[i],
+ str + 3, argv[i]);
+ } else {
+ errx(EXIT_FAILURE, "invalid filter string: %s", str);
+ }
+
+ free(str);
+ }
+}
+
+static void
+pcidb_drop_privs(void)
+{
+ priv_set_t *curprivs, *targprivs;
+
+ if ((curprivs = priv_allocset()) == NULL) {
+ err(EXIT_FAILURE, "failed to allocate privilege set to drop "
+ "privs");
+ }
+
+ if (getppriv(PRIV_EFFECTIVE, curprivs) != 0) {
+ err(EXIT_FAILURE, "failed to get current privileges");
+ }
+
+ if ((targprivs = priv_allocset()) == NULL) {
+ err(EXIT_FAILURE, "failed to allocate privilege set to drop "
+ "privs");
+ }
+
+ /*
+ * Set our privileges to the minimum required. Because stdout will have
+ * already been opened, all we need is the ability to read files from
+ * basic privileges. We opt to keep FILE_DAC_READ if the caller has it
+ * just in case there is something weird about the location of the
+ * pci.ids files.
+ */
+ priv_basicset(targprivs);
+ VERIFY0(priv_delset(targprivs, PRIV_FILE_LINK_ANY));
+ VERIFY0(priv_delset(targprivs, PRIV_PROC_INFO));
+ VERIFY0(priv_delset(targprivs, PRIV_PROC_SESSION));
+ VERIFY0(priv_delset(targprivs, PRIV_PROC_FORK));
+ VERIFY0(priv_delset(targprivs, PRIV_NET_ACCESS));
+ VERIFY0(priv_delset(targprivs, PRIV_FILE_WRITE));
+ VERIFY0(priv_delset(targprivs, PRIV_PROC_EXEC));
+ VERIFY0(priv_addset(targprivs, PRIV_FILE_DAC_READ));
+
+ priv_intersect(curprivs, targprivs);
+
+ if (setppriv(PRIV_SET, PRIV_EFFECTIVE, targprivs) != 0) {
+ err(EXIT_FAILURE, "failed to reduce privileges");
+ }
+
+ priv_freeset(curprivs);
+ priv_freeset(targprivs);
+}
+
+static int
+pcidb_usage(const char *fmt, ...)
+{
+ if (fmt != NULL) {
+ va_list ap;
+
+ (void) fprintf(stderr, "%s: ", pcidb_progname);
+ va_start(ap, fmt);
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void) fprintf(stderr, "\n");
+ }
+
+ (void) fprintf(stderr, "usage: %s [-v|-d|-s|-c|-S|-i] [-H]"
+ "[[-p] [-o <field>[,...]] [<filter>]\n\n"
+ "\t-v\t\tshow vendor table\n"
+ "\t-d\t\tshow device table\n"
+ "\t-s\t\tshow subsystem table\n"
+ "\t-c\t\tshow class table\n"
+ "\t-S\t\tshow subclass table\n"
+ "\t-i\t\tshow programming interface table\n"
+ "\t-H\t\tdo not output column headers\n"
+ "\t-p\t\toutput in parsable form\n"
+ "\t-o field\toutput only specified fields\n\n"
+ "filters take the form of PCI aliases, e.g. pci8086,1522, "
+ "pci1028,1f44,s, or\n"
+ "pciex1022,1480.1462,7c37. Classes can be specified in a similar "
+ "way, e.g.\npciclass,010802 or pciclass,0403.\n", pcidb_progname);
+
+ return (EXIT_USAGE);
+}
+
+int
+main(int argc, char *argv[])
+{
+ pcidb_hdl_t *hdl;
+ int c;
+ uint_t tablecnt = 0;
+ pcidb_table_t table = PCIDB_TABLE_NONE;
+ boolean_t parse = B_FALSE, strcase = B_FALSE;
+ const char *fields = NULL;
+ const char *ofmt_fields_str = NULL;
+ const ofmt_field_t *ofmt_fields = NULL;
+ ofmt_handle_t ofmt;
+ ofmt_status_t oferr;
+ uint_t flags = 0;
+ pcidb_walk_t walk;
+
+ bzero(&walk, sizeof (walk));
+ pcidb_progname = basename(argv[0]);
+
+ pcidb_drop_privs();
+
+ while ((c = getopt(argc, argv, ":vdscSipo:hH")) != -1) {
+ switch (c) {
+ case 'v':
+ tablecnt++;
+ table = PCIDB_TABLE_VENDOR;
+ break;
+ case 'd':
+ tablecnt++;
+ table = PCIDB_TABLE_DEVICE;
+ break;
+ case 's':
+ tablecnt++;
+ table = PCIDB_TABLE_SUBSYSTEM;
+ break;
+ case 'c':
+ tablecnt++;
+ table = PCIDB_TABLE_CLASS;
+ break;
+ case 'S':
+ tablecnt++;
+ table = PCIDB_TABLE_SUBCLASS;
+ break;
+ case 'i':
+ tablecnt++;
+ table = PCIDB_TABLE_PROGIF;
+ break;
+ case 'p':
+ parse = B_TRUE;
+ flags |= OFMT_PARSABLE;
+ break;
+ case 'o':
+ fields = optarg;
+ break;
+ case 'h':
+ return (pcidb_usage(NULL));
+ case 'H':
+ flags |= OFMT_NOHEADER;
+ break;
+ case ':':
+ return (pcidb_usage("Option -%c requires an argument",
+ optopt));
+ case '?':
+ return (pcidb_usage("unknown option: -%c", optopt));
+ }
+ }
+
+ if (tablecnt > 1) {
+ errx(EXIT_USAGE, "more than one table specified, only one of "
+ "-v, -d, -s, -c, -S, and -i may be specified");
+ }
+
+ if (parse && fields == NULL) {
+ errx(EXIT_USAGE, "-p requires fields specified with -o");
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ pcidb_process_filters(argc, argv, &walk);
+
+ switch (table) {
+ case PCIDB_TABLE_VENDOR:
+ ofmt_fields = pcidb_vendor_ofmt;
+ ofmt_fields_str = pcidb_vendor_fields;
+ break;
+ case PCIDB_TABLE_NONE:
+ case PCIDB_TABLE_DEVICE:
+ ofmt_fields = pcidb_device_ofmt;
+ ofmt_fields_str = pcidb_device_fields;
+ break;
+ case PCIDB_TABLE_SUBSYSTEM:
+ ofmt_fields = pcidb_subsystem_ofmt;
+ ofmt_fields_str = pcidb_subsystem_fields;
+ break;
+ case PCIDB_TABLE_CLASS:
+ ofmt_fields = pcidb_class_ofmt;
+ ofmt_fields_str = pcidb_class_fields;
+ break;
+ case PCIDB_TABLE_SUBCLASS:
+ ofmt_fields = pcidb_subclass_ofmt;
+ ofmt_fields_str = pcidb_subclass_fields;
+ break;
+ case PCIDB_TABLE_PROGIF:
+ ofmt_fields = pcidb_progif_ofmt;
+ ofmt_fields_str = pcidb_progif_fields;
+ break;
+ }
+
+ if (fields == NULL) {
+ fields = ofmt_fields_str;
+ }
+
+ oferr = ofmt_open(fields, ofmt_fields, flags, 0, &ofmt);
+ ofmt_check(oferr, parse, ofmt, pcidb_ofmt_errx, warnx);
+
+ hdl = pcidb_open(PCIDB_VERSION);
+ if (hdl == NULL) {
+ err(EXIT_FAILURE, "failed to initialize PCI IDs database");
+ }
+
+ walk.pw_hdl = hdl;
+ walk.pw_ofmt = ofmt;
+ walk.pw_strcase = strcase;
+
+ switch (table) {
+ case PCIDB_TABLE_VENDOR:
+ pcidb_walk_vendors(&walk);
+ break;
+ case PCIDB_TABLE_NONE:
+ case PCIDB_TABLE_DEVICE:
+ pcidb_walk_devices(&walk);
+ break;
+ case PCIDB_TABLE_SUBSYSTEM:
+ pcidb_walk_subsystems(&walk);
+ break;
+ case PCIDB_TABLE_CLASS:
+ pcidb_walk_classes(&walk);
+ break;
+ case PCIDB_TABLE_SUBCLASS:
+ pcidb_walk_subclasses(&walk);
+ break;
+ case PCIDB_TABLE_PROGIF:
+ pcidb_walk_progifs(&walk);
+ break;
+ }
+
+ ofmt_close(ofmt);
+ pcidb_close(hdl);
+ return (EXIT_SUCCESS);
+}