summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Mustacchi <rm@fingolfin.org>2021-03-05 10:25:21 -0800
committerRobert Mustacchi <rm@fingolfin.org>2021-03-24 11:22:58 -0700
commit8a37ae750765321f9d9de63763676fa7280d93da (patch)
tree9813e8103e48b11517a06b3de26717d097c7e9e1
parent5ad6a0f49b59cb4bd38eab045ce9894881e9d862 (diff)
downloadillumos-joyent-8a37ae750765321f9d9de63763676fa7280d93da.tar.gz
13630 libpcidb could know about class codes
Reviewed by: C Fraire <cfraire@me.com> Reviewed by: Patrick Mooney <pmooney@pfmooney.com> Reviewed by: Andy Fiddaman <andy@omnios.org> Reviewed by: Rich Lowe <richlowe@richlowe.net> Approved by: Rich Lowe <richlowe@richlowe.net>
-rw-r--r--usr/src/cmd/Makefile1
-rw-r--r--usr/src/cmd/pcidb/Makefile45
-rw-r--r--usr/src/cmd/pcidb/pcidb.c884
-rw-r--r--usr/src/lib/libpcidb/Makefile.com7
-rw-r--r--usr/src/lib/libpcidb/common/mapfile-vers48
-rw-r--r--usr/src/lib/libpcidb/common/pcidb.c701
-rw-r--r--usr/src/lib/libpcidb/common/pcidb.h30
-rw-r--r--usr/src/pkg/manifests/diagnostic-pci.mf26
-rw-r--r--usr/src/pkg/manifests/system-test-utiltest.mf2
-rw-r--r--usr/src/test/util-tests/runfiles/default.run2
-rw-r--r--usr/src/test/util-tests/tests/Makefile2
-rw-r--r--usr/src/test/util-tests/tests/pcidb/Makefile36
-rw-r--r--usr/src/test/util-tests/tests/pcidb/pcidbtest.ksh223
13 files changed, 1762 insertions, 245 deletions
diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile
index 7bac5c58ce..3bc0bf2af4 100644
--- a/usr/src/cmd/Makefile
+++ b/usr/src/cmd/Makefile
@@ -304,6 +304,7 @@ COMMON_SUBDIRS= \
passwd \
pathchk \
pbind \
+ pcidb \
pcidr \
pcieb \
pcitool \
diff --git a/usr/src/cmd/pcidb/Makefile b/usr/src/cmd/pcidb/Makefile
new file mode 100644
index 0000000000..41a5737c7a
--- /dev/null
+++ b/usr/src/cmd/pcidb/Makefile
@@ -0,0 +1,45 @@
+#
+# 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
+#
+
+PROG= pcidb
+
+include ../Makefile.cmd
+include ../Makefile.cmd.64
+include ../Makefile.ctf
+
+CFLAGS += $(CCVERBOSE)
+CSTD = $(CSTD_GNU99)
+LDLIBS += -lpcidb -lofmt
+OBJS = pcidb.o
+ROOTCMDDIR = $(ROOTLIB)/pci
+
+.KEEP_STATE:
+
+$(PROG): $(OBJS)
+ $(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+%.o: %.c
+ $(COMPILE.c) $<
+ $(POST_PROCESS_O)
+
+all: $(PROG)
+
+install: all $(ROOTCMD)
+
+clean:
+ $(RM) $(OBJS)
+
+include ../Makefile.targ
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);
+}
diff --git a/usr/src/lib/libpcidb/Makefile.com b/usr/src/lib/libpcidb/Makefile.com
index 43a327423b..96ff944ad5 100644
--- a/usr/src/lib/libpcidb/Makefile.com
+++ b/usr/src/lib/libpcidb/Makefile.com
@@ -24,7 +24,7 @@
LIBRARY = libpcidb.a
VERS = .1
-OBJECTS = pcidb.o
+OBJECTS = pcidb.o list.o
include ../../Makefile.lib
@@ -33,11 +33,16 @@ LIBS = $(DYNLIB)
SRCDIR = ../common
LDLIBS += -lc
+CSTD = $(CSTD_GNU99)
.KEEP_STATE:
all: $(LIBS)
+pics/%.o: $(SRC)/common/list/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
+
include ../../Makefile.targ
diff --git a/usr/src/lib/libpcidb/common/mapfile-vers b/usr/src/lib/libpcidb/common/mapfile-vers
index 509778b0e8..8c17896938 100644
--- a/usr/src/lib/libpcidb/common/mapfile-vers
+++ b/usr/src/lib/libpcidb/common/mapfile-vers
@@ -20,6 +20,7 @@
#
#
# Copyright (c) 2012 Joyent, Inc. All rights reserved.
+# Copyright 2021 Oxide Computer Company
#
#
@@ -40,30 +41,47 @@ $mapfile_version 2
SYMBOL_VERSION SUNWprivate {
global:
- pcidb_open;
+ pcidb_class_code;
+ pcidb_class_iter_next;
+ pcidb_class_iter;
+ pcidb_class_name;
pcidb_close;
- pcidb_lookup_vendor;
- pcidb_vendor_iter;
- pcidb_vendor_iter_next;
- pcidb_vendor_name;
- pcidb_vendor_id;
- pcidb_lookup_device;
- pcidb_lookup_device_by_vendor;
- pcidb_device_iter;
+ pcidb_device_id;
pcidb_device_iter_next;
+ pcidb_device_iter;
pcidb_device_name;
- pcidb_device_id;
pcidb_device_vendor;
+ pcidb_lookup_class;
+ pcidb_lookup_device_by_vendor;
+ pcidb_lookup_device;
+ pcidb_lookup_progif_by_subclass;
+ pcidb_lookup_progif;
+ pcidb_lookup_subclass_by_class;
+ pcidb_lookup_subclass;
+ pcidb_lookup_subvd_by_device;
+ pcidb_lookup_subvd_by_vendor;
pcidb_lookup_subvd;
- pcidb_lookup_subvd_by_vendor;
- pcidb_lookup_subvd_by_device;
- pcidb_subvd_iter;
+ pcidb_lookup_vendor;
+ pcidb_open;
+ pcidb_progif_code;
+ pcidb_progif_iter_next;
+ pcidb_progif_iter;
+ pcidb_progif_name;
+ pcidb_subclass_code;
+ pcidb_subclass_iter_next;
+ pcidb_subclass_iter;
+ pcidb_subclass_name;
+ pcidb_subvd_device;
pcidb_subvd_iter_next;
+ pcidb_subvd_iter;
pcidb_subvd_name;
- pcidb_subvd_svid;
pcidb_subvd_sdid;
- pcidb_subvd_device;
+ pcidb_subvd_svid;
pcidb_subvd_vendor;
+ pcidb_vendor_id;
+ pcidb_vendor_iter_next;
+ pcidb_vendor_iter;
+ pcidb_vendor_name;
local:
*;
};
diff --git a/usr/src/lib/libpcidb/common/pcidb.c b/usr/src/lib/libpcidb/common/pcidb.c
index 37bfc75f06..bac836f256 100644
--- a/usr/src/lib/libpcidb/common/pcidb.c
+++ b/usr/src/lib/libpcidb/common/pcidb.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2012, Joyent, Inc. All rights reserved.
+ * Copyright 2021 Oxide Computer Company
*/
/*
@@ -57,201 +58,232 @@
#include <string.h>
#include <assert.h>
#include <unistd.h>
+#include <stddef.h>
+#include <sys/list.h>
#include "pcidb.h"
#define PCI_NAME_MAX 256
#define PCI_READLINE 1024
-/* Forward declarations */
-struct pcidb_vendor;
-struct pcidb_device;
-struct pcidb_subvd;
+struct pcidb_progif {
+ char pp_name[PCI_NAME_MAX];
+ list_node_t pp_link;
+ pcidb_subclass_t *pp_subclass;
+ uint8_t pp_code;
+};
+
+struct pcidb_subclass {
+ char psc_name[PCI_NAME_MAX];
+ list_node_t psc_link;
+ list_t psc_progifs;
+ pcidb_class_t *psc_class;
+ uint8_t psc_code;
+};
+
+struct pcidb_class {
+ char pc_name[PCI_NAME_MAX];
+ list_node_t pc_link;
+ list_t pc_subclass;
+ pcidb_hdl_t *pc_hdl;
+ uint8_t pc_code;
+};
struct pcidb_subvd {
- uint16_t ps_vid;
- uint16_t ps_did;
- char ps_name[PCI_NAME_MAX];
- struct pcidb_subvd *ps_prev;
- struct pcidb_subvd *ps_next;
- struct pcidb_device *ps_dev;
- struct pcidb_vendor *ps_vend;
+ uint16_t ps_vid;
+ uint16_t ps_did;
+ char ps_name[PCI_NAME_MAX];
+ list_node_t ps_link;
+ pcidb_device_t *ps_dev;
+ pcidb_vendor_t *ps_vend;
};
struct pcidb_device {
- uint16_t pd_id;
- char pd_name[PCI_NAME_MAX];
- struct pcidb_subvd *pd_sstart;
- struct pcidb_subvd *pd_send;
- struct pcidb_device *pd_next;
- struct pcidb_device *pd_prev;
- struct pcidb_vendor *pd_vend;
+ uint16_t pd_id;
+ char pd_name[PCI_NAME_MAX];
+ list_t pd_subs;
+ list_node_t pd_link;
+ pcidb_vendor_t *pd_vend;
};
struct pcidb_vendor {
- uint16_t pv_id;
- char pv_name[PCI_NAME_MAX];
- struct pcidb_device *pv_dstart;
- struct pcidb_device *pv_dend;
- struct pcidb_vendor *pv_prev;
- struct pcidb_vendor *pv_next;
+ uint16_t pv_id;
+ char pv_name[PCI_NAME_MAX];
+ list_t pv_devs;
+ list_node_t pv_link;
+ pcidb_hdl_t *pv_hdl;
};
struct pcidb_hdl {
- pcidb_vendor_t *ph_vstart;
- pcidb_vendor_t *ph_vend;
+ list_t ph_vendors;
+ list_t ph_classes;
};
typedef enum pcidb_parse {
+ PDB_INIT,
PDB_VENDOR,
PDB_DEVICE,
- PDB_SUBDEV
+ PDB_SUBDEV,
+ PDB_CLASS,
+ PDB_SUBCLASS,
+ PDB_PROGIF
} pcidb_parse_t;
static const char *pci_db = "/usr/share/hwdata/pci.ids";
-static void
-pcihdl_add_vendor(pcidb_hdl_t *hdl, pcidb_vendor_t *v)
-{
- if (hdl->ph_vstart == NULL && hdl->ph_vend == NULL) {
- hdl->ph_vstart = v;
- hdl->ph_vend = v;
- v->pv_prev = NULL;
- v->pv_next = NULL;
- } else {
- v->pv_prev = hdl->ph_vend;
- v->pv_next = NULL;
- hdl->ph_vend->pv_next = v;
- hdl->ph_vend = v;
- }
-}
-
static pcidb_vendor_t *
parse_vendor(char *buf, pcidb_hdl_t *hdl)
{
- pcidb_vendor_t *v;
- size_t len;
+ pcidb_vendor_t *vend;
- v = malloc(sizeof (pcidb_vendor_t));
- if (v == NULL)
+ vend = malloc(sizeof (pcidb_vendor_t));
+ if (vend == NULL)
return (NULL);
- pcihdl_add_vendor(hdl, v);
- v->pv_dstart = NULL;
- v->pv_dend = NULL;
+ list_create(&vend->pv_devs, sizeof (pcidb_device_t),
+ offsetof(pcidb_device_t, pd_link));
+ vend->pv_hdl = hdl;
+ list_insert_tail(&hdl->ph_vendors, vend);
buf[4] = '\0';
- v->pv_id = strtol(buf, NULL, 16);
+ vend->pv_id = strtol(buf, NULL, 16);
buf += 6;
- len = strlen(buf);
- if (buf[len-1] == '\n')
- buf[len-1] = '\0';
- (void) strlcpy(v->pv_name, buf, PCI_NAME_MAX);
+ (void) strlcpy(vend->pv_name, buf, PCI_NAME_MAX);
- return (v);
-}
-
-static void
-insert_device(pcidb_vendor_t *v, pcidb_device_t *d)
-{
- d->pd_vend = v;
- if (v->pv_dstart == NULL && v->pv_dend == NULL) {
- v->pv_dstart = d;
- v->pv_dend = d;
- d->pd_next = NULL;
- d->pd_prev = NULL;
- } else {
- d->pd_prev = v->pv_dend;
- d->pd_next = NULL;
- v->pv_dend->pd_next = d;
- v->pv_dend = d;
- }
+ return (vend);
}
static pcidb_device_t *
-parse_device(char *buf, pcidb_vendor_t *v)
+parse_device(char *buf, pcidb_vendor_t *vend)
{
- pcidb_device_t *d;
- size_t len;
+ pcidb_device_t *dev;
- d = malloc(sizeof (pcidb_device_t));
- if (d == NULL)
- return (d);
+ dev = malloc(sizeof (pcidb_device_t));
+ if (dev == NULL)
+ return (dev);
- d->pd_sstart = NULL;
- d->pd_send = NULL;
- insert_device(v, d);
+ list_create(&dev->pd_subs, sizeof (pcidb_subvd_t),
+ offsetof(pcidb_subvd_t, ps_link));
+ dev->pd_vend = vend;
+ list_insert_tail(&vend->pv_devs, dev);
buf++;
buf[4] = '\0';
- d->pd_id = strtol(buf, NULL, 16);
+ dev->pd_id = strtol(buf, NULL, 16);
buf += 6;
- len = strlen(buf);
- if (buf[len-1] == '\n')
- buf[len-1] = '\0';
-
- (void) strlcpy(d->pd_name, buf, PCI_NAME_MAX);
- return (d);
-}
-
-static void
-insert_subdev(pcidb_device_t *d, pcidb_subvd_t *s)
-{
- s->ps_dev = d;
- s->ps_vend = d->pd_vend;
- if (d->pd_sstart == NULL) {
- d->pd_sstart = s;
- d->pd_send = s;
- s->ps_prev = NULL;
- s->ps_next = NULL;
- } else {
- s->ps_prev = d->pd_send;
- s->ps_next = NULL;
- d->pd_send->ps_next = s;
- d->pd_send = s;
- }
+
+ (void) strlcpy(dev->pd_name, buf, PCI_NAME_MAX);
+ return (dev);
}
static pcidb_subvd_t *
-parse_subdev(char *buf, pcidb_device_t *d)
+parse_subdev(char *buf, pcidb_device_t *dev)
{
- pcidb_subvd_t *s;
- size_t len;
+ pcidb_subvd_t *sub;
- s = malloc(sizeof (pcidb_subvd_t));
- if (s == NULL)
+ sub = malloc(sizeof (pcidb_subvd_t));
+ if (sub == NULL)
return (NULL);
- insert_subdev(d, s);
+
+ sub->ps_dev = dev;
+ sub->ps_vend = dev->pd_vend;
+ list_insert_tail(&dev->pd_subs, sub);
buf += 2;
buf[4] = '\0';
- s->ps_vid = strtol(buf, NULL, 16);
+ sub->ps_vid = strtol(buf, NULL, 16);
buf += 5;
buf[4] = '\0';
- s->ps_did = strtol(buf, NULL, 16);
+ sub->ps_did = strtol(buf, NULL, 16);
buf += 6;
- len = strlen(buf);
- if (buf[len-1] == '\n')
- buf[len-1] = '\0';
+ (void) strlcpy(sub->ps_name, buf, PCI_NAME_MAX);
+
+ return (sub);
+}
+
+static pcidb_class_t *
+pcidb_parse_class(char *buf, pcidb_hdl_t *hdl)
+{
+ pcidb_class_t *class;
+
+ class = malloc(sizeof (pcidb_class_t));
+ if (class == NULL)
+ return (NULL);
+
+ list_create(&class->pc_subclass, sizeof (pcidb_subclass_t),
+ offsetof(pcidb_subclass_t, psc_link));
+ class->pc_hdl = hdl;
+ list_insert_tail(&hdl->ph_classes, class);
+
+ buf += 2;
+ buf[3] = '\0';
+ class->pc_code = strtol(buf, NULL, 16);
+ buf += 4;
+ (void) strlcpy(class->pc_name, buf, PCI_NAME_MAX);
+
+ return (class);
+}
+
+static pcidb_subclass_t *
+pcidb_parse_subclass(char *buf, pcidb_class_t *class)
+{
+ pcidb_subclass_t *sub;
+
+ sub = malloc(sizeof (pcidb_subclass_t));
+ if (sub == NULL)
+ return (NULL);
+
+ list_create(&sub->psc_progifs, sizeof (pcidb_progif_t),
+ offsetof(pcidb_progif_t, pp_link));
+ sub->psc_class = class;
+ list_insert_tail(&class->pc_subclass, sub);
+
+ buf++;
+ buf[3] = '\0';
+ sub->psc_code = strtol(buf, NULL, 16);
+ buf += 4;
+ (void) strlcpy(sub->psc_name, buf, PCI_NAME_MAX);
+
+ return (sub);
+}
+
+static pcidb_progif_t *
+pcidb_parse_progif(char *buf, pcidb_subclass_t *sub)
+{
+ pcidb_progif_t *prog;
+
+ prog = malloc(sizeof (pcidb_progif_t));
+ if (prog == NULL) {
+ return (NULL);
+ }
+
+ prog->pp_subclass = sub;
+ list_insert_tail(&sub->psc_progifs, prog);
- (void) strlcpy(s->ps_name, buf, PCI_NAME_MAX);
+ buf += 2;
+ buf[3] = '\0';
+ prog->pp_code = strtol(buf, NULL, 16);
+ buf += 4;
+ (void) strlcpy(prog->pp_name, buf, PCI_NAME_MAX);
- return (s);
+ return (prog);
}
static int
readline(FILE *f, char *buf, size_t len)
{
for (;;) {
+ char *c;
+
if (fgets(buf, len, f) == NULL)
return (-1);
- if (buf[0] == 'C')
- return (-1);
+ if ((c = strchr(buf, '\n')) != NULL)
+ *c = '\0';
- if (buf[0] != '#' && buf[0] != '\n')
+ if (buf[0] != '#' && buf[0] != '\0')
return (0);
}
}
@@ -259,12 +291,15 @@ readline(FILE *f, char *buf, size_t len)
static int
parse_db(FILE *f, pcidb_hdl_t *hdl)
{
- char buf[1024];
- pcidb_vendor_t *v = NULL;
- pcidb_device_t *d = NULL;
- pcidb_parse_t state = PDB_VENDOR;
+ pcidb_vendor_t *vend = NULL;
+ pcidb_device_t *dev = NULL;
+ pcidb_class_t *class = NULL;
+ pcidb_subclass_t *sub = NULL;
+ pcidb_parse_t state = PDB_INIT;
for (;;) {
+ char buf[1024];
+
errno = 0;
if (readline(f, buf, sizeof (buf)) != 0) {
if (errno != 0)
@@ -275,15 +310,26 @@ parse_db(FILE *f, pcidb_hdl_t *hdl)
newstate:
switch (state) {
+ case PDB_INIT:
+ vend = NULL;
+ dev = NULL;
+ class = NULL;
+ sub = NULL;
+ if (buf[0] == 'C') {
+ state = PDB_CLASS;
+ } else {
+ state = PDB_VENDOR;
+ }
+ goto newstate;
case PDB_VENDOR:
- v = parse_vendor(buf, hdl);
- if (v == NULL)
- return (0);
+ vend = parse_vendor(buf, hdl);
+ if (vend == NULL)
+ return (-1);
state = PDB_DEVICE;
- continue;
+ break;
case PDB_DEVICE:
if (buf[0] != '\t') {
- state = PDB_VENDOR;
+ state = PDB_INIT;
goto newstate;
}
@@ -292,14 +338,14 @@ newstate:
goto newstate;
}
- assert(v != NULL);
- d = parse_device(buf, v);
- if (d == NULL)
+ assert(vend != NULL);
+ dev = parse_device(buf, vend);
+ if (dev == NULL)
return (0);
- continue;
+ break;
case PDB_SUBDEV:
if (buf[0] != '\t') {
- state = PDB_VENDOR;
+ state = PDB_INIT;
goto newstate;
}
@@ -309,8 +355,48 @@ newstate:
}
assert(buf[0] == '\t' && buf[1] == '\t');
- assert(d != NULL);
- (void) parse_subdev(buf, d);
+ assert(dev != NULL);
+ if (parse_subdev(buf, dev) == NULL) {
+ return (-1);
+ }
+ break;
+ case PDB_CLASS:
+ class = pcidb_parse_class(buf, hdl);
+ state = PDB_SUBCLASS;
+ break;
+ case PDB_SUBCLASS:
+ if (buf[0] != '\t') {
+ state = PDB_INIT;
+ goto newstate;
+ }
+
+ if (buf[1] == '\t') {
+ state = PDB_PROGIF;
+ goto newstate;
+ }
+
+ assert(class != NULL);
+ sub = pcidb_parse_subclass(buf, class);
+ if (sub == NULL) {
+ return (-1);
+ }
+ break;
+ case PDB_PROGIF:
+ if (buf[0] != '\t') {
+ state = PDB_INIT;
+ goto newstate;
+ }
+
+ if (buf[0] == '\t' && buf[1] != '\t') {
+ state = PDB_SUBCLASS;
+ goto newstate;
+ }
+
+ assert(sub != NULL);
+ if (pcidb_parse_progif(buf, sub) == NULL) {
+ return (-1);
+ }
+ break;
}
}
}
@@ -330,8 +416,10 @@ pcidb_open(int version)
if (h == NULL)
return (NULL);
- h->ph_vstart = NULL;
- h->ph_vend = NULL;
+ list_create(&h->ph_vendors, sizeof (pcidb_vendor_t),
+ offsetof(pcidb_vendor_t, pv_link));
+ list_create(&h->ph_classes, sizeof (pcidb_class_t),
+ offsetof(pcidb_class_t, pc_link));
f = fopen(pci_db, "rF");
if (f == NULL) {
@@ -342,7 +430,6 @@ pcidb_open(int version)
if (parse_db(f, h) < 0) {
(void) fclose(f);
pcidb_close(h);
- free(h);
return (NULL);
}
@@ -352,36 +439,51 @@ pcidb_open(int version)
}
void
-pcidb_close(pcidb_hdl_t *h)
+pcidb_close(pcidb_hdl_t *hdl)
{
- pcidb_vendor_t *v, *tv;
+ pcidb_vendor_t *vend;
+ pcidb_class_t *class;
- pcidb_device_t *d, *td;
- pcidb_subvd_t *s, *ts;
-
- if (h == NULL)
+ if (hdl == NULL)
return;
- v = h->ph_vstart;
- while (v != NULL) {
- d = v->pv_dstart;
- while (d != NULL) {
- s = d->pd_sstart;
- while (s != NULL) {
- ts = s;
- s = s->ps_next;
- free(ts);
+ while ((vend = list_remove_head(&hdl->ph_vendors)) != NULL) {
+ pcidb_device_t *dev;
+
+ while ((dev = list_remove_head(&vend->pv_devs)) != NULL) {
+ pcidb_subvd_t *sub;
+
+ while ((sub = list_remove_head(&dev->pd_subs)) !=
+ NULL) {
+ free(sub);
+ }
+ list_destroy(&dev->pd_subs);
+ free(dev);
+ }
+ list_destroy(&vend->pv_devs);
+ free(vend);
+ }
+ list_destroy(&hdl->ph_vendors);
+
+ while ((class = list_remove_head(&hdl->ph_classes)) != NULL) {
+ pcidb_subclass_t *sub;
+
+ while ((sub = list_remove_head(&class->pc_subclass)) != NULL) {
+ pcidb_progif_t *prog;
+
+ while ((prog = list_remove_head(&sub->psc_progifs)) !=
+ NULL) {
+ free(prog);
}
- td = d;
- d = d->pd_next;
- free(td);
+ list_destroy(&sub->psc_progifs);
+ free(sub);
}
- tv = v;
- v = v->pv_next;
- free(tv);
+ list_destroy(&class->pc_subclass);
+ free(class);
}
+ list_destroy(&hdl->ph_classes);
- free(h);
+ free(hdl);
}
pcidb_vendor_t *
@@ -389,7 +491,8 @@ pcidb_lookup_vendor(pcidb_hdl_t *hdl, uint16_t id)
{
pcidb_vendor_t *v;
- for (v = hdl->ph_vstart; v != NULL; v = v->pv_next) {
+ for (v = list_head(&hdl->ph_vendors); v != NULL;
+ v = list_next(&hdl->ph_vendors, v)) {
if (v->pv_id == id)
return (v);
}
@@ -398,165 +501,307 @@ pcidb_lookup_vendor(pcidb_hdl_t *hdl, uint16_t id)
}
const char *
-pcidb_vendor_name(pcidb_vendor_t *v)
+pcidb_vendor_name(pcidb_vendor_t *vend)
{
- return (v->pv_name);
+ return (vend->pv_name);
}
uint16_t
-pcidb_vendor_id(pcidb_vendor_t *v)
+pcidb_vendor_id(pcidb_vendor_t *vend)
{
- return (v->pv_id);
+ return (vend->pv_id);
}
pcidb_vendor_t *
-pcidb_vendor_iter(pcidb_hdl_t *h)
+pcidb_vendor_iter(pcidb_hdl_t *hdl)
{
- return (h->ph_vstart);
+ return (list_head(&hdl->ph_vendors));
}
pcidb_vendor_t *
-pcidb_vendor_iter_next(pcidb_vendor_t *v)
+pcidb_vendor_iter_next(pcidb_vendor_t *vend)
{
- assert(v != NULL);
- return (v->pv_next);
+ assert(vend != NULL);
+ return (list_next(&vend->pv_hdl->ph_vendors, vend));
}
pcidb_device_t *
-pcidb_lookup_device_by_vendor(pcidb_vendor_t *v, uint16_t id)
+pcidb_lookup_device_by_vendor(pcidb_vendor_t *vend, uint16_t id)
{
- pcidb_device_t *d;
- assert(v != NULL);
+ assert(vend != NULL);
- for (d = v->pv_dstart; d != NULL; d = d->pd_next)
- if (d->pd_id == id)
- return (d);
+ for (pcidb_device_t *dev = list_head(&vend->pv_devs); dev != NULL;
+ dev = list_next(&vend->pv_devs, dev)) {
+ if (dev->pd_id == id)
+ return (dev);
+ }
return (NULL);
}
pcidb_device_t *
-pcidb_lookup_device(pcidb_hdl_t *h, uint16_t vid, uint16_t did)
+pcidb_lookup_device(pcidb_hdl_t *hdl, uint16_t vid, uint16_t did)
{
- pcidb_vendor_t *v;
+ pcidb_vendor_t *vend;
- v = pcidb_lookup_vendor(h, vid);
- if (v == NULL)
+ vend = pcidb_lookup_vendor(hdl, vid);
+ if (vend == NULL)
return (NULL);
- return (pcidb_lookup_device_by_vendor(v, did));
+ return (pcidb_lookup_device_by_vendor(vend, did));
}
pcidb_device_t *
-pcidb_device_iter(pcidb_vendor_t *v)
+pcidb_device_iter(pcidb_vendor_t *vend)
{
- return (v->pv_dstart);
+ return (list_head(&vend->pv_devs));
}
pcidb_device_t *
-pcidb_device_iter_next(pcidb_device_t *d)
+pcidb_device_iter_next(pcidb_device_t *dev)
{
- return (d->pd_next);
+ return (list_next(&dev->pd_vend->pv_devs, dev));
}
const char *
-pcidb_device_name(pcidb_device_t *d)
+pcidb_device_name(pcidb_device_t *dev)
{
- return (d->pd_name);
+ return (dev->pd_name);
}
uint16_t
-pcidb_device_id(pcidb_device_t *d)
+pcidb_device_id(pcidb_device_t *dev)
{
- return (d->pd_id);
+ return (dev->pd_id);
}
pcidb_vendor_t *
-pcidb_device_vendor(pcidb_device_t *d)
+pcidb_device_vendor(pcidb_device_t *dev)
{
- return (d->pd_vend);
+ return (dev->pd_vend);
}
pcidb_subvd_t *
-pcidb_lookup_subvd_by_device(pcidb_device_t *d, uint16_t svid, uint16_t sdid)
+pcidb_lookup_subvd_by_device(pcidb_device_t *dev, uint16_t svid, uint16_t sdid)
{
- pcidb_subvd_t *s;
+ pcidb_subvd_t *sub;
- assert(d != NULL);
+ assert(dev != NULL);
- for (s = d->pd_sstart; s != NULL; s = s->ps_next)
- if (s->ps_vid == svid && s->ps_did == sdid)
- return (s);
+ for (sub = list_head(&dev->pd_subs); sub != NULL;
+ sub = list_next(&dev->pd_subs, sub)) {
+ if (sub->ps_vid == svid && sub->ps_did == sdid)
+ return (sub);
+ }
return (NULL);
}
pcidb_subvd_t *
-pcidb_lookup_subvd_by_vendor(pcidb_vendor_t *v, uint16_t devid, uint16_t svid,
- uint16_t sdid)
+pcidb_lookup_subvd_by_vendor(pcidb_vendor_t *vend, uint16_t devid,
+ uint16_t svid, uint16_t sdid)
{
- pcidb_device_t *d;
+ pcidb_device_t *dev;
- assert(v != NULL);
- d = pcidb_lookup_device_by_vendor(v, devid);
- if (d == NULL)
+ assert(vend != NULL);
+ dev = pcidb_lookup_device_by_vendor(vend, devid);
+ if (dev == NULL)
return (NULL);
- return (pcidb_lookup_subvd_by_device(d, svid, sdid));
+ return (pcidb_lookup_subvd_by_device(dev, svid, sdid));
}
pcidb_subvd_t *
-pcidb_lookup_subvd(pcidb_hdl_t *h, uint16_t vid, uint16_t did, uint16_t svid,
+pcidb_lookup_subvd(pcidb_hdl_t *hdl, uint16_t vid, uint16_t did, uint16_t svid,
uint16_t sdid)
{
- pcidb_device_t *d;
+ pcidb_device_t *dev;
- assert(h != NULL);
- d = pcidb_lookup_device(h, vid, did);
- if (d == NULL)
+ assert(hdl != NULL);
+ dev = pcidb_lookup_device(hdl, vid, did);
+ if (dev == NULL)
return (NULL);
- return (pcidb_lookup_subvd_by_device(d, svid, sdid));
+ return (pcidb_lookup_subvd_by_device(dev, svid, sdid));
}
pcidb_subvd_t *
-pcidb_subvd_iter(pcidb_device_t *d)
+pcidb_subvd_iter(pcidb_device_t *dev)
{
- return (d->pd_sstart);
+ return (list_head(&dev->pd_subs));
}
pcidb_subvd_t *
-pcidb_subvd_iter_next(pcidb_subvd_t *s)
+pcidb_subvd_iter_next(pcidb_subvd_t *sub)
{
- return (s->ps_next);
+ return (list_next(&sub->ps_dev->pd_subs, sub));
}
const char *
-pcidb_subvd_name(pcidb_subvd_t *s)
+pcidb_subvd_name(pcidb_subvd_t *sub)
{
- return (s->ps_name);
+ return (sub->ps_name);
}
uint16_t
-pcidb_subvd_svid(pcidb_subvd_t *s)
+pcidb_subvd_svid(pcidb_subvd_t *sub)
{
- return (s->ps_vid);
+ return (sub->ps_vid);
}
uint16_t
-pcidb_subvd_sdid(pcidb_subvd_t *s)
+pcidb_subvd_sdid(pcidb_subvd_t *sub)
{
- return (s->ps_did);
+ return (sub->ps_did);
}
pcidb_device_t *
-pcidb_subvd_device(pcidb_subvd_t *s)
+pcidb_subvd_device(pcidb_subvd_t *sub)
{
- return (s->ps_dev);
+ return (sub->ps_dev);
}
pcidb_vendor_t *
-pcidb_subvd_vendor(pcidb_subvd_t *s)
+pcidb_subvd_vendor(pcidb_subvd_t *sub)
+{
+ return (sub->ps_vend);
+}
+
+
+pcidb_class_t *
+pcidb_lookup_class(pcidb_hdl_t *hdl, uint8_t code)
+{
+ for (pcidb_class_t *class = list_head(&hdl->ph_classes); class != NULL;
+ class = list_next(&hdl->ph_classes, class)) {
+ if (class->pc_code == code) {
+ return (class);
+ }
+ }
+
+ return (NULL);
+}
+
+pcidb_class_t *
+pcidb_class_iter(pcidb_hdl_t *hdl)
+{
+ return (list_head(&hdl->ph_classes));
+}
+
+pcidb_class_t *
+pcidb_class_iter_next(pcidb_class_t *class)
+{
+ return (list_next(&class->pc_hdl->ph_classes, class));
+}
+
+const char *
+pcidb_class_name(pcidb_class_t *class)
+{
+ return (class->pc_name);
+}
+
+uint8_t
+pcidb_class_code(pcidb_class_t *class)
+{
+ return (class->pc_code);
+}
+
+pcidb_subclass_t *
+pcidb_lookup_subclass(pcidb_hdl_t *hdl, uint8_t ccode, uint8_t subcode)
+{
+ pcidb_class_t *class;
+
+ class = pcidb_lookup_class(hdl, ccode);
+ if (class == NULL) {
+ return (NULL);
+ }
+
+ return (pcidb_lookup_subclass_by_class(class, subcode));
+}
+
+pcidb_subclass_t *
+pcidb_lookup_subclass_by_class(pcidb_class_t *class, uint8_t code)
+{
+ for (pcidb_subclass_t *sub = list_head(&class->pc_subclass);
+ sub != NULL; sub = list_next(&class->pc_subclass, sub)) {
+ if (sub->psc_code == code) {
+ return (sub);
+ }
+ }
+
+ return (NULL);
+}
+
+pcidb_subclass_t *
+pcidb_subclass_iter(pcidb_class_t *class)
+{
+ return (list_head(&class->pc_subclass));
+}
+
+pcidb_subclass_t *
+pcidb_subclass_iter_next(pcidb_subclass_t *sub)
+{
+ return (list_next(&sub->psc_class->pc_subclass, sub));
+}
+
+const char *
+pcidb_subclass_name(pcidb_subclass_t *sub)
+{
+ return (sub->psc_name);
+}
+
+uint8_t
+pcidb_subclass_code(pcidb_subclass_t *sub)
+{
+ return (sub->psc_code);
+}
+
+pcidb_progif_t *
+pcidb_lookup_progif(pcidb_hdl_t *hdl, uint8_t ccode, uint8_t scode,
+ uint8_t pcode)
+{
+ pcidb_subclass_t *sub;
+
+ sub = pcidb_lookup_subclass(hdl, ccode, scode);
+ if (sub == NULL) {
+ return (NULL);
+ }
+
+ return (pcidb_lookup_progif_by_subclass(sub, pcode));
+}
+
+pcidb_progif_t *
+pcidb_lookup_progif_by_subclass(pcidb_subclass_t *sub, uint8_t code)
+{
+ for (pcidb_progif_t *prog = list_head(&sub->psc_progifs); prog != NULL;
+ prog = list_next(&sub->psc_progifs, prog)) {
+ if (prog->pp_code == code) {
+ return (prog);
+ }
+ }
+
+ return (NULL);
+}
+
+pcidb_progif_t *
+pcidb_progif_iter(pcidb_subclass_t *sub)
+{
+ return (list_head(&sub->psc_progifs));
+}
+
+pcidb_progif_t *
+pcidb_progif_iter_next(pcidb_progif_t *prog)
+{
+ return (list_next(&prog->pp_subclass->psc_progifs, prog));
+}
+
+const char *
+pcidb_progif_name(pcidb_progif_t *prog)
+{
+ return (prog->pp_name);
+}
+
+uint8_t
+pcidb_progif_code(pcidb_progif_t *prog)
{
- return (s->ps_vend);
+ return (prog->pp_code);
}
diff --git a/usr/src/lib/libpcidb/common/pcidb.h b/usr/src/lib/libpcidb/common/pcidb.h
index a11e4d90f1..0cf0047b26 100644
--- a/usr/src/lib/libpcidb/common/pcidb.h
+++ b/usr/src/lib/libpcidb/common/pcidb.h
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2012, Joyent, Inc. All rights reserved.
+ * Copyright 2021 Oxide Computer Company
*/
/*
@@ -41,6 +42,9 @@ typedef struct pcidb_hdl pcidb_hdl_t;
typedef struct pcidb_vendor pcidb_vendor_t;
typedef struct pcidb_device pcidb_device_t;
typedef struct pcidb_subvd pcidb_subvd_t;
+typedef struct pcidb_class pcidb_class_t;
+typedef struct pcidb_subclass pcidb_subclass_t;
+typedef struct pcidb_progif pcidb_progif_t;
extern pcidb_hdl_t *pcidb_open(int);
extern void pcidb_close(pcidb_hdl_t *);
@@ -77,6 +81,32 @@ extern uint16_t pcidb_subvd_sdid(pcidb_subvd_t *);
extern pcidb_device_t *pcidb_subvd_device(pcidb_subvd_t *);
extern pcidb_vendor_t *pcidb_subvd_vendor(pcidb_subvd_t *);
+extern pcidb_class_t *pcidb_lookup_class(pcidb_hdl_t *, uint8_t);
+extern pcidb_class_t *pcidb_class_iter(pcidb_hdl_t *);
+extern pcidb_class_t *pcidb_class_iter_next(pcidb_class_t *);
+
+extern const char *pcidb_class_name(pcidb_class_t *);
+extern uint8_t pcidb_class_code(pcidb_class_t *);
+
+extern pcidb_subclass_t *pcidb_lookup_subclass(pcidb_hdl_t *, uint8_t, uint8_t);
+extern pcidb_subclass_t *pcidb_lookup_subclass_by_class(pcidb_class_t *,
+ uint8_t);
+extern pcidb_subclass_t *pcidb_subclass_iter(pcidb_class_t *);
+extern pcidb_subclass_t *pcidb_subclass_iter_next(pcidb_subclass_t *);
+
+extern const char *pcidb_subclass_name(pcidb_subclass_t *);
+extern uint8_t pcidb_subclass_code(pcidb_subclass_t *);
+
+extern pcidb_progif_t *pcidb_lookup_progif(pcidb_hdl_t *, uint8_t, uint8_t,
+ uint8_t);
+extern pcidb_progif_t *pcidb_lookup_progif_by_subclass(pcidb_subclass_t *,
+ uint8_t);
+extern pcidb_progif_t *pcidb_progif_iter(pcidb_subclass_t *);
+extern pcidb_progif_t *pcidb_progif_iter_next(pcidb_progif_t *);
+
+extern const char *pcidb_progif_name(pcidb_progif_t *);
+extern uint8_t pcidb_progif_code(pcidb_progif_t *);
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/pkg/manifests/diagnostic-pci.mf b/usr/src/pkg/manifests/diagnostic-pci.mf
new file mode 100644
index 0000000000..c666862ebd
--- /dev/null
+++ b/usr/src/pkg/manifests/diagnostic-pci.mf
@@ -0,0 +1,26 @@
+#
+# 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
+#
+
+set name=pkg.fmri value=pkg:/diagnostic/pci@$(PKGVERS)
+set name=pkg.description value="PCI Utilities"
+set name=pkg.summary value="PCI Utilities"
+set name=info.classification \
+ value="org.opensolaris.category.2008:Applications/System Utilities"
+set name=variant.arch value=$(ARCH)
+dir path=usr group=sys
+dir path=usr/lib
+dir path=usr/lib/pci
+file path=usr/lib/pci/pcidb mode=0555
+license lic_CDDL license=lic_CDDL
diff --git a/usr/src/pkg/manifests/system-test-utiltest.mf b/usr/src/pkg/manifests/system-test-utiltest.mf
index 96a71c9d33..0e68abbe97 100644
--- a/usr/src/pkg/manifests/system-test-utiltest.mf
+++ b/usr/src/pkg/manifests/system-test-utiltest.mf
@@ -1675,6 +1675,7 @@ file path=opt/util-tests/tests/mdb/typedef/tst.union.mdb mode=0444
file path=opt/util-tests/tests/mdb/typedef/tst.union.mdb.out mode=0444
file path=opt/util-tests/tests/mergeq/mqt mode=0555
file path=opt/util-tests/tests/mergeq/wqt mode=0555
+file path=opt/util-tests/tests/pcidbtest mode=0555
file path=opt/util-tests/tests/printf_test mode=0555
file path=opt/util-tests/tests/sed/multi_test mode=0555
file path=opt/util-tests/tests/sed/regress.multitest.out/1.1 mode=0444
@@ -1823,6 +1824,7 @@ license usr/src/test/util-tests/tests/demangle/THIRDPARTYLICENSE.rust \
license=usr/src/test/util-tests/tests/demangle/THIRDPARTYLICENSE.rust
license usr/src/test/util-tests/tests/sed/bsd/THIRDPARTYLICENSE \
license=usr/src/test/util-tests/tests/sed/bsd/THIRDPARTYLICENSE
+depend fmri=diagnostic/pci type=require
depend fmri=locale/de type=require
depend fmri=system/library/iconv/utf-8 type=require
depend fmri=system/test/testrunner type=require
diff --git a/usr/src/test/util-tests/runfiles/default.run b/usr/src/test/util-tests/runfiles/default.run
index 83e55604de..578fa8b6c7 100644
--- a/usr/src/test/util-tests/runfiles/default.run
+++ b/usr/src/test/util-tests/runfiles/default.run
@@ -81,3 +81,5 @@ tests = ['custr_remove', 'custr_trunc']
[/opt/util-tests/tests/sed]
tests = ['sed_addr', 'multi_test']
+
+[/opt/util-tests/tests/pcidbtest]
diff --git a/usr/src/test/util-tests/tests/Makefile b/usr/src/test/util-tests/tests/Makefile
index 46c29b416c..41d692ff1b 100644
--- a/usr/src/test/util-tests/tests/Makefile
+++ b/usr/src/test/util-tests/tests/Makefile
@@ -20,6 +20,6 @@
SUBDIRS = date dis dladm iconv libnvpair_json libsff printf xargs grep_xpg4
SUBDIRS += demangle mergeq workq chown ctf smbios libjedec awk make sleep
-SUBDIRS += libcustr find mdb sed head
+SUBDIRS += libcustr find mdb sed head pcidb
include $(SRC)/test/Makefile.com
diff --git a/usr/src/test/util-tests/tests/pcidb/Makefile b/usr/src/test/util-tests/tests/pcidb/Makefile
new file mode 100644
index 0000000000..362f26a277
--- /dev/null
+++ b/usr/src/test/util-tests/tests/pcidb/Makefile
@@ -0,0 +1,36 @@
+#
+# 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 $(SRC)/cmd/Makefile.cmd
+include $(SRC)/test/Makefile.com
+
+ROOTOPTPKG = $(ROOT)/opt/util-tests/tests
+PROG = pcidbtest
+
+ROOTPROG = $(PROG:%=$(ROOTOPTPKG)/%)
+
+all:
+
+install: $(ROOTPROG)
+
+clobber: clean
+
+clean:
+
+$(ROOTOPTPKG):
+ $(INS.dir)
+
+$(ROOTOPTPKG)/%: %.ksh $(ROOTOPTPKG)
+ $(INS.rename)
diff --git a/usr/src/test/util-tests/tests/pcidb/pcidbtest.ksh b/usr/src/test/util-tests/tests/pcidb/pcidbtest.ksh
new file mode 100644
index 0000000000..6a3cfb2edd
--- /dev/null
+++ b/usr/src/test/util-tests/tests/pcidb/pcidbtest.ksh
@@ -0,0 +1,223 @@
+#!/usr/bin/ksh
+#
+#
+# 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
+#
+
+unalias -a
+set -o pipefail
+
+pcidb_arg0="$(basename $0)"
+pcidb_prog="/usr/lib/pci/pcidb"
+pcidb_exit=0
+
+warn()
+{
+ typeset msg="$*"
+ [[ -z "$msg" ]] && msg="failed"
+ echo "TEST FAILED: $pcidb_arg0: $msg" >&2
+}
+
+#
+# The following is intended to catch bad filters.
+#
+pcidb_bad_filter()
+{
+ typeset filt="$1"
+
+ if $pcidb_prog $filt 2>/dev/null; then
+ warn "invalid filter $filt erroneously worked"
+ pcidb_exit=1
+ return
+ fi
+
+ printf "TEST PASSED: invalid filter %s\n" "$filt"
+
+}
+
+pcidb_bad_args()
+{
+ if $pcidb_prog $@ 2>/dev/null 1>/dev/null; then
+ warn "should have failed with args "$@", but passed"
+ pcidb_exit=1
+ return
+ fi
+
+ printf "TEST PASSED: invalid arguments %s\n" "$*"
+}
+
+pcidb_match()
+{
+ typeset output
+ typeset match="$1"
+ shift
+
+ output=$($pcidb_prog $@)
+ if (( $? != 0)); then
+ warn "failed to run pcidb with args: $@"
+ pcidb_exit=1
+ return
+ fi
+
+ if [[ "$output" != "$match" ]]; then
+ warn "output mismatch with args: $@\n found: $output\n" \
+ "expected: $match"
+ pcidb_exit=1
+ return
+ fi
+
+ printf "TEST PASSED: successfully matched against %s\n" "$*"
+}
+
+if [[ -n $PCIDB ]]; then
+ pcidb_prog=$PCIDB
+fi
+
+#
+# Before we begin execution, set up the environment such that we have a
+# standard locale and that umem will help us catch mistakes.
+#
+export LC_ALL=C.UTF-8
+export LD_PRELOAD=libumem.so
+export UMEM_DEBUG=default
+
+#
+# Validate that filters match either exactly one or at least one line of
+# output using parsable mode. When we match more than one entry, we
+# don't try to assert the count because we expect that it will actually
+# change over time.
+#
+exp="1de
+8086"
+pcidb_match "$exp" -v -p -o vid pci8086 pci1de
+pcidb_match "Advanced Micro Devices, Inc. [AMD]" -v -p -o vendor pci1022
+pcidb_match "1af4:1044:Virtio RNG" -p -o vid,did,device pci1af4,1044
+pcidb_match "Dell:HBA330 Adapter" -s -p -o subvendor,subsystem \
+ pci1000,97.1028,1f45
+pcidb_match "c:3:30:XHCI" -i -p -o bcc,scc,pi,interface pciclass,0c0330
+pcidb_match "I2O" -S -p -o subclass pciexclass,0e
+pcidb_match "Ethernet 1Gb 2-port 368i Adapter" -s -p -o subsystem pci1590,216,s
+
+#
+# We should get no output when we specify a class or device filter and
+# use a different table or we have an over specified filter.
+#
+pcidb_match "" -d pciclass,03
+pcidb_match "" -S pci1000
+pcidb_match "" -v pci8086,1234
+pcidb_match "" -c pciclass,010802
+
+#
+# Run through filter parsing
+#
+pcidb_bad_filter "foo"
+pcidb_bad_filter ";ffvi"
+pcidb_bad_filter "12345"
+pcidb_bad_filter "pc8086"
+pcidb_bad_filter "pciqwer"
+pcidb_bad_filter "pci12345"
+pcidb_bad_filter "pci8086,"
+pcidb_bad_filter "pci8086,locke"
+pcidb_bad_filter "pci8086sigh"
+pcidb_bad_filter "pci8086,p"
+pcidb_bad_filter "pci8086,12345"
+pcidb_bad_filter "pci8086,1234zz"
+pcidb_bad_filter "pci8086,1234."
+pcidb_bad_filter "pci8086,1234,"
+pcidb_bad_filter "pci8086,1234,b"
+pcidb_bad_filter "pci8086,1234,8"
+pcidb_bad_filter "pci8086,1234,wat"
+pcidb_bad_filter "pci8086,1234.terra"
+pcidb_bad_filter "pci8086,1234.terra,celes"
+pcidb_bad_filter "pci8086,1234.fffff"
+pcidb_bad_filter "pci8086,1234.abcd,"
+pcidb_bad_filter "pci8086,1234.abcd."
+pcidb_bad_filter "pci8086,1234.abcdqr"
+pcidb_bad_filter "pci8086,1234.abcd,2,p"
+pcidb_bad_filter "pci8086,1234.abcd,2000000000"
+pcidb_bad_filter "pci8086,1234.abcd,kefka"
+pcidb_bad_filter "pci8086,1234.abcd,34ultros"
+pcidb_bad_filter "pciexqwer"
+pcidb_bad_filter "pciex12345"
+pcidb_bad_filter "pciex8086,"
+pcidb_bad_filter "pciex8086,locke"
+pcidb_bad_filter "pciex8086sigh"
+pcidb_bad_filter "pciex8086,p"
+pcidb_bad_filter "pciex8086,12345"
+pcidb_bad_filter "pciex8086,1234zz"
+pcidb_bad_filter "pciex8086,1234."
+pcidb_bad_filter "pciex8086,1234,"
+pcidb_bad_filter "pciex8086,1234,b"
+pcidb_bad_filter "pciex8086,1234,8"
+pcidb_bad_filter "pciex8086,1234,wat"
+pcidb_bad_filter "pciex8086,1234.terra"
+pcidb_bad_filter "pciex8086,1234.terra,celes"
+pcidb_bad_filter "pciex8086,1234.fffff"
+pcidb_bad_filter "pciex8086,1234.abcd,"
+pcidb_bad_filter "pciex8086,1234.abcd."
+pcidb_bad_filter "pciex8086,1234.abcdqr"
+pcidb_bad_filter "pciex8086,1234.abcd,2,p"
+pcidb_bad_filter "pciex8086,1234.abcd,2000000000"
+pcidb_bad_filter "pciex8086,1234.abcd,kefka"
+pcidb_bad_filter "pciex8086,1234.abcd,34ultros"
+pcidb_bad_filter "pciclas"
+pcidb_bad_filter "pciclassedgar"
+pcidb_bad_filter "pciclass,sabin"
+pcidb_bad_filter "pciclass,0"
+pcidb_bad_filter "pciclass,013"
+pcidb_bad_filter "pciclass,01345"
+pcidb_bad_filter "pciclass,0134567"
+pcidb_bad_filter "pciclass,01,"
+pcidb_bad_filter "pciclass,010,"
+pcidb_bad_filter "pciclass,010aa,"
+pcidb_bad_filter "pciclass,0102as"
+pcidb_bad_filter "pciclass,0102.as"
+pcidb_bad_filter "pciclass,0102@as"
+pcidb_bad_filter "pciclass,010298aa"
+pcidb_bad_filter "pciclass,010298,"
+pcidb_bad_filter "pciclass,010298!"
+pcidb_bad_filter "pciclass,010298!shadow"
+pcidb_bad_filter "pciexclas"
+pcidb_bad_filter "pciexclassedgar"
+pcidb_bad_filter "pciexclass,sabin"
+pcidb_bad_filter "pciexclass,0"
+pcidb_bad_filter "pciexclass,013"
+pcidb_bad_filter "pciexclass,01345"
+pcidb_bad_filter "pciexclass,0134567"
+pcidb_bad_filter "pciexclass,01,"
+pcidb_bad_filter "pciexclass,010,"
+pcidb_bad_filter "pciexclass,010aa,"
+pcidb_bad_filter "pciexclass,0102as"
+pcidb_bad_filter "pciexclass,0102.as"
+pcidb_bad_filter "pciexclass,0102@as"
+pcidb_bad_filter "pciexclass,010298aa"
+pcidb_bad_filter "pciexclass,010298,"
+pcidb_bad_filter "pciexclass,010298!"
+pcidb_bad_filter "pciexclass,010298!shadow"
+
+#
+# Verify that if we ask for bad columns we error
+#
+pcidb_bad_args -p
+pcidb_bad_args -o
+pcidb_bad_args -o -p
+pcidb_bad_args -p -o terra
+pcidb_bad_args -p -o subclass -v
+pcidb_bad_args -v -d -c
+
+if (( pcidb_exit == 0 )); then
+ printf "All tests passed successfully!\n"
+fi
+
+exit $pcidb_exit