summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan McDonald <danmcd@joyent.com>2021-04-06 13:53:57 -0400
committerDan McDonald <danmcd@joyent.com>2021-04-06 13:54:04 -0400
commit43e43e05cd35925fd83121ffa94db7aa45725448 (patch)
treed580cedef7144a44ba349d235a80496e8a21975f
parent226b670d97d873f898223fdf625d09554be198f8 (diff)
parent7687d0d8812e33aceb40697eb2a8b408c1fe7b52 (diff)
downloadillumos-joyent-43e43e05cd35925fd83121ffa94db7aa45725448.tar.gz
[illumos-gate merge]
commit 7687d0d8812e33aceb40697eb2a8b408c1fe7b52 13687 want tool for PCIe device, config space display commit 80d1a7bde98a8ab2881940a6fe6775073564f253 13359 mlxcx_update_link_state can race against mlxcx_register_mac 13370 mlxcx_intr_n doing redundant check on mleqe_event_type commit b4100263209f454c9f030b30aec0d337c7614e0e 13692 bhyve panic if vmm_drv_purge() fails commit ffb6483089015eb90be1f5e7fc2a96c9929546a6 11698 Want NVMe Hotplug Support 11699 x86 pci configurator should not fail device teardown if device is gone 11700 DDI hotplug request handler resets connection handle state before performing state change operations 11701 ldi_handle dcmd segfaults occasionally commit 8054a0e4c809d98ffb44f17b9a8b932ca2c24b2c 13695 Can't create VNICs over vioif after 13637 commit f980a4bbce3d867e2bb5e61c180593f416d181a5 13684 ld aborts when input object has no file name commit b4adc50c2ffdb6ae8e81d8be3c37ed01066fe920 13693 loader: we should support pools without features Conflicts: manifest usr/src/test/util-tests/tests/Makefile
-rw-r--r--exception_lists/copyright2
-rw-r--r--manifest2
-rw-r--r--usr/src/boot/Makefile.version2
-rw-r--r--usr/src/boot/lib/libstand/zfs/zfsimpl.c15
-rw-r--r--usr/src/cmd/Makefile1
-rw-r--r--usr/src/cmd/pcieadm/Makefile45
-rw-r--r--usr/src/cmd/pcieadm/pcieadm.c624
-rw-r--r--usr/src/cmd/pcieadm/pcieadm.h112
-rw-r--r--usr/src/cmd/pcieadm/pcieadm_cfgspace.c5046
-rw-r--r--usr/src/cmd/pcieadm/pcieadm_devs.c568
-rw-r--r--usr/src/cmd/sgs/libld/common/syms.c15
-rw-r--r--usr/src/cmd/sgs/libld/common/update.c4
-rw-r--r--usr/src/cmd/sgs/tools/SUNWonld-README1
-rw-r--r--usr/src/pkg/manifests/diagnostic-pci.mf1
-rw-r--r--usr/src/pkg/manifests/system-test-utiltest.mf17
-rw-r--r--usr/src/test/util-tests/runfiles/default.run1
-rw-r--r--usr/src/test/util-tests/tests/Makefile2
-rw-r--r--usr/src/test/util-tests/tests/pcieadm/Makefile59
-rw-r--r--usr/src/test/util-tests/tests/pcieadm/bridge-efilt-p.out2
-rw-r--r--usr/src/test/util-tests/tests/pcieadm/bridge-efilt.out10
-rw-r--r--usr/src/test/util-tests/tests/pcieadm/bridge-ht-p.out1
-rw-r--r--usr/src/test/util-tests/tests/pcieadm/bridge-ht.msi-p.out1
-rw-r--r--usr/src/test/util-tests/tests/pcieadm/bridge-ht.msi.command-p.out1
-rw-r--r--usr/src/test/util-tests/tests/pcieadm/bridge-ht.out4
-rw-r--r--usr/src/test/util-tests/tests/pcieadm/bridge.pcibin0 -> 4096 bytes
-rw-r--r--usr/src/test/util-tests/tests/pcieadm/header0-basic-L.out3
-rw-r--r--usr/src/test/util-tests/tests/pcieadm/header0-basic-LH.out2
-rw-r--r--usr/src/test/util-tests/tests/pcieadm/header0-basic-n.out3
-rw-r--r--usr/src/test/util-tests/tests/pcieadm/header0-basic.out3
-rw-r--r--usr/src/test/util-tests/tests/pcieadm/header0-parse.out2
-rw-r--r--usr/src/test/util-tests/tests/pcieadm/igb-ltr-p.out1
-rw-r--r--usr/src/test/util-tests/tests/pcieadm/igb-ltr.out11
-rw-r--r--usr/src/test/util-tests/tests/pcieadm/igb.pcibin0 -> 4096 bytes
-rw-r--r--usr/src/test/util-tests/tests/pcieadm/pcieadmtest.ksh160
-rw-r--r--usr/src/uts/common/io/mlxcx/mlxcx.c29
-rw-r--r--usr/src/uts/common/io/mlxcx/mlxcx.h3
-rw-r--r--usr/src/uts/common/io/mlxcx/mlxcx_gld.c6
-rw-r--r--usr/src/uts/common/io/mlxcx/mlxcx_intr.c60
-rw-r--r--usr/src/uts/common/io/nvme/nvme.c16
-rw-r--r--usr/src/uts/common/io/pciex/pcie.c34
-rw-r--r--usr/src/uts/common/io/vioif/vioif.c12
-rw-r--r--usr/src/uts/common/sys/pci.h5
-rw-r--r--usr/src/uts/common/sys/pcie.h9
-rw-r--r--usr/src/uts/i86pc/io/vmm/vmm_sol_dev.c8
44 files changed, 6866 insertions, 37 deletions
diff --git a/exception_lists/copyright b/exception_lists/copyright
index ef5530ac1d..95f609a69f 100644
--- a/exception_lists/copyright
+++ b/exception_lists/copyright
@@ -419,6 +419,8 @@ usr/src/test/util-tests/tests/grep_xpg4/files/test*
usr/src/test/util-tests/tests/head/*.in
usr/src/test/util-tests/tests/head/*.out
usr/src/test/util-tests/tests/libsff/*.out
+usr/src/test/util-tests/tests/pcieadm/*.out
+usr/src/test/util-tests/tests/pcieadm/*.pci
usr/src/test/zfs-tests/tests/functional/history/*Z
usr/src/test/zfs-tests/tests/functional/history/*txt
usr/src/tools/btxld/btx.h
diff --git a/manifest b/manifest
index 3390f0877f..d768184d37 100644
--- a/manifest
+++ b/manifest
@@ -10229,8 +10229,10 @@ s usr/lib/nss_user.so.1=../../lib/nss_user.so.1
f usr/lib/passmgmt 0555 root sys
f usr/lib/passwdutil.so.1 0755 root bin
d usr/lib/pci 0755 root bin
+f usr/lib/pci/pcidb 0555 root bin
f usr/lib/pci/pcidr 0555 root bin
f usr/lib/pci/pcidr_plugin.so 0755 root bin
+f usr/lib/pci/pcieadm 0555 root bin
f usr/lib/pci/pcieb 0555 root bin
f usr/lib/pfexecd 0555 root bin
d usr/lib/picl 0755 root sys
diff --git a/usr/src/boot/Makefile.version b/usr/src/boot/Makefile.version
index 656a8a3d08..88ac6f56a0 100644
--- a/usr/src/boot/Makefile.version
+++ b/usr/src/boot/Makefile.version
@@ -34,4 +34,4 @@ LOADER_VERSION = 1.1
# Use date like formatting here, YYYY.MM.DD.XX, without leading zeroes.
# The version is processed from left to right, the version number can only
# be increased.
-BOOT_VERSION = $(LOADER_VERSION)-2021.03.16.1
+BOOT_VERSION = $(LOADER_VERSION)-2021.04.03.1
diff --git a/usr/src/boot/lib/libstand/zfs/zfsimpl.c b/usr/src/boot/lib/libstand/zfs/zfsimpl.c
index 9ceb9b9794..e83a8a3983 100644
--- a/usr/src/boot/lib/libstand/zfs/zfsimpl.c
+++ b/usr/src/boot/lib/libstand/zfs/zfsimpl.c
@@ -175,10 +175,21 @@ nvlist_check_features_for_read(nvlist_t *nvl)
nv_string_t *nvp_name;
int rc;
+ /*
+ * We may have all features disabled.
+ */
rc = nvlist_find(nvl, ZPOOL_CONFIG_FEATURES_FOR_READ,
DATA_TYPE_NVLIST, NULL, &features, NULL);
- if (rc != 0)
- return (rc);
+ switch (rc) {
+ case 0:
+ break; /* Continue with checks */
+
+ case ENOENT:
+ return (0); /* All features are disabled */
+
+ default:
+ return (rc); /* Error while reading nvlist */
+ }
data = (nvs_data_t *)features->nv_data;
nvp = &data->nvl_pair; /* first pair in nvlist */
diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile
index bd426edbb5..a4bacee105 100644
--- a/usr/src/cmd/Makefile
+++ b/usr/src/cmd/Makefile
@@ -306,6 +306,7 @@ COMMON_SUBDIRS= \
pbind \
pcidb \
pcidr \
+ pcieadm \
pcieb \
pcitool \
pfexec \
diff --git a/usr/src/cmd/pcieadm/Makefile b/usr/src/cmd/pcieadm/Makefile
new file mode 100644
index 0000000000..c8b17c2a3a
--- /dev/null
+++ b/usr/src/cmd/pcieadm/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= pcieadm
+
+include ../Makefile.cmd
+include ../Makefile.cmd.64
+include ../Makefile.ctf
+
+CFLAGS += $(CCVERBOSE)
+CSTD = $(CSTD_GNU99)
+LDLIBS += -ldevinfo -lpcidb -lofmt
+OBJS = pcieadm.o pcieadm_cfgspace.o pcieadm_devs.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/pcieadm/pcieadm.c b/usr/src/cmd/pcieadm/pcieadm.c
new file mode 100644
index 0000000000..edcad6e4d8
--- /dev/null
+++ b/usr/src/cmd/pcieadm/pcieadm.c
@@ -0,0 +1,624 @@
+/*
+ * 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
+ */
+
+/*
+ * PCIe shenanigans
+ *
+ * Currently this implements several different views at seeing into PCIe devices
+ * and is designed to (hopefully) replace pcitool and be a vector for new system
+ * functionality such as dealing with multicast filtering, ACS, etc.
+ *
+ * While most subcommands have their own implementations, there are a couple of
+ * things that are worth bearing in mind:
+ *
+ * 1) Where possible, prefer the use of libofmt. In particular, having good,
+ * parsable output is important. New subcommands should strive to meet that.
+ *
+ * 2) Because we're often processing binary data (and it's good hygiene),
+ * subcommands should make sure to drop privileges as early as they can by
+ * calling pcieadm_init_privs(). More on privileges below.
+ *
+ * Privilege Management
+ * --------------------
+ *
+ * In an attempt to minimize privilege exposure, but to allow subcommands
+ * flexibility when required (e.g. show-cfgspace needs full privs to read from
+ * the kernel), we have two privilege sets that we maintain. One which is the
+ * minimial privs, which basically is a set that has stripped everything. This
+ * is 'pia_priv_min'. The second is one that allows a subcommand to add in
+ * privileges that it requires which will be left in the permitted set. These
+ * are in 'pia_priv_eff'. It's important to know that this set is always
+ * intersected with what the user actually has, so this is not meant to be a way
+ * for a caller to get more privileges than they already have.
+ *
+ * A subcommand is expected to call pcieadm_init_privs() once they have
+ * processed enough arguments that they can set an upper bound on privileges.
+ * It's worth noting that a subcommand will be executed in an already minimial
+ * environment; however, we will have already set up a libdevinfo handle for
+ * them, which should make the need to do much more not so bad.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <err.h>
+#include <libdevinfo.h>
+#include <strings.h>
+#include <sys/stat.h>
+#include <sys/pci_tools.h>
+#include <sys/pci.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/debug.h>
+#include <upanic.h>
+#include <libgen.h>
+
+#include "pcieadm.h"
+
+pcieadm_t pcieadm;
+const char *pcieadm_progname;
+
+void
+pcieadm_init_privs(pcieadm_t *pcip)
+{
+ static const char *msg = "attempted to re-initialize privileges";
+ if (pcip->pia_priv_init == NULL) {
+ upanic(msg, strlen(msg));
+ }
+
+ priv_intersect(pcip->pia_priv_init, pcip->pia_priv_eff);
+
+ if (setppriv(PRIV_SET, PRIV_PERMITTED, pcieadm.pia_priv_eff) != 0) {
+ err(EXIT_FAILURE, "failed to reduce privileges");
+ }
+
+ if (setppriv(PRIV_SET, PRIV_LIMIT, pcieadm.pia_priv_eff) != 0) {
+ err(EXIT_FAILURE, "failed to reduce privileges");
+ }
+
+ priv_freeset(pcip->pia_priv_init);
+ pcip->pia_priv_init = NULL;
+}
+
+void
+pcieadm_indent(void)
+{
+ pcieadm.pia_indent += 2;
+}
+
+void
+pcieadm_deindent(void)
+{
+ VERIFY3U(pcieadm.pia_indent, >, 0);
+ pcieadm.pia_indent -= 2;
+}
+
+void
+pcieadm_print(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (pcieadm.pia_indent > 0) {
+ (void) printf("%*s", pcieadm.pia_indent, "");
+ }
+
+ va_start(ap, fmt);
+ (void) vprintf(fmt, ap);
+ va_end(ap);
+}
+
+void
+pcieadm_ofmt_errx(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ verrx(EXIT_FAILURE, fmt, ap);
+}
+
+boolean_t
+pcieadm_di_node_is_pci(di_node_t node)
+{
+ const char *name;
+
+ name = di_node_name(node);
+ return (strncmp("pci", name, 3) == 0);
+}
+
+static int
+pcieadm_di_walk_cb(di_node_t node, void *arg)
+{
+ pcieadm_di_walk_t *walk = arg;
+
+ if (!pcieadm_di_node_is_pci(node)) {
+ return (DI_WALK_CONTINUE);
+ }
+
+ /*
+ * We create synthetic nodes for the root of PCIe tree basically
+ * functions as all the resources available for one or more bridges.
+ * When we encounter that top-level node skip it.
+ */
+ if (strcmp("pci", di_node_name(node)) == 0) {
+ return (DI_WALK_CONTINUE);
+ }
+
+ return (walk->pdw_func(node, walk->pdw_arg));
+}
+
+void
+pcieadm_di_walk(pcieadm_t *pcip, pcieadm_di_walk_t *arg)
+{
+ (void) di_walk_node(pcip->pia_root, DI_WALK_CLDFIRST, arg,
+ pcieadm_di_walk_cb);
+}
+
+/*
+ * Attempt to find the nexus that corresponds to this device. To do this, we
+ * walk up and walk the minors until we find a "reg" minor.
+ */
+void
+pcieadm_find_nexus(pcieadm_t *pia)
+{
+ di_node_t cur;
+
+ for (cur = di_parent_node(pia->pia_devi); cur != DI_NODE_NIL;
+ cur = di_parent_node(cur)) {
+ di_minor_t minor = DI_MINOR_NIL;
+
+ while ((minor = di_minor_next(cur, minor)) != DI_MINOR_NIL) {
+ if (di_minor_spectype(minor) == S_IFCHR &&
+ strcmp(di_minor_name(minor), "reg") == 0) {
+ pia->pia_nexus = cur;
+ return;
+ }
+ }
+ }
+}
+
+static int
+pcieadm_find_dip_cb(di_node_t node, void *arg)
+{
+ char *path = NULL, *driver;
+ char dinst[128], bdf[128], altbdf[128];
+ int inst, nprop, *regs;
+ pcieadm_t *pia = arg;
+
+ path = di_devfs_path(node);
+ if (path == NULL) {
+ err(EXIT_FAILURE, "failed to construct devfs path for node: "
+ "%s (%s)", di_node_name(node));
+ }
+
+ driver = di_driver_name(node);
+ inst = di_instance(node);
+ if (driver != NULL && inst != -1) {
+ (void) snprintf(dinst, sizeof (dinst), "%s%d", driver, inst);
+ }
+
+ nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", &regs);
+ if (nprop <= 0) {
+ errx(EXIT_FAILURE, "failed to lookup regs array for %s",
+ path);
+ }
+ (void) snprintf(bdf, sizeof (bdf), "%x/%x/%x", PCI_REG_BUS_G(regs[0]),
+ PCI_REG_DEV_G(regs[0]), PCI_REG_FUNC_G(regs[0]));
+ (void) snprintf(bdf, sizeof (bdf), "%02x/%02x/%02x",
+ PCI_REG_BUS_G(regs[0]), PCI_REG_DEV_G(regs[0]),
+ PCI_REG_FUNC_G(regs[0]));
+
+ if (strcmp(pia->pia_devstr, path) == 0 ||
+ strcmp(pia->pia_devstr, bdf) == 0 ||
+ strcmp(pia->pia_devstr, altbdf) == 0 ||
+ (driver != NULL && inst != -1 &&
+ strcmp(pia->pia_devstr, dinst) == 0)) {
+ if (pia->pia_devi != DI_NODE_NIL) {
+ errx(EXIT_FAILURE, "device name matched two device "
+ "nodes: %s and %s", di_node_name(pia->pia_devi),
+ di_node_name(node));
+ }
+
+ pia->pia_devi = node;
+ }
+
+ if (path != NULL) {
+ di_devfs_path_free(path);
+ }
+
+ return (DI_WALK_CONTINUE);
+}
+
+void
+pcieadm_find_dip(pcieadm_t *pcip, const char *device)
+{
+ pcieadm_di_walk_t walk;
+
+ /*
+ * If someone specifies /devices, just skip over it.
+ */
+ pcip->pia_devstr = device;
+ if (strncmp("/devices", device, strlen("/devices")) == 0) {
+ pcip->pia_devstr += strlen("/devices");
+ }
+
+ pcip->pia_devi = DI_NODE_NIL;
+ walk.pdw_arg = pcip;
+ walk.pdw_func = pcieadm_find_dip_cb;
+ pcieadm_di_walk(pcip, &walk);
+
+ if (pcip->pia_devi == DI_NODE_NIL) {
+ errx(EXIT_FAILURE, "failed to find device node %s", device);
+ }
+
+ pcip->pia_nexus = DI_NODE_NIL;
+ pcieadm_find_nexus(pcip);
+ if (pcip->pia_nexus == DI_NODE_NIL) {
+ errx(EXIT_FAILURE, "failed to find nexus for %s", device);
+ }
+}
+
+typedef struct pcieadm_cfgspace_file {
+ int pcfi_fd;
+} pcieadm_cfgspace_file_t;
+
+static boolean_t
+pcieadm_read_cfgspace_file(uint32_t off, uint8_t len, void *buf, void *arg)
+{
+ uint32_t bufoff = 0;
+ pcieadm_cfgspace_file_t *pcfi = arg;
+
+ while (len > 0) {
+ ssize_t ret = pread(pcfi->pcfi_fd, buf + bufoff, len, off);
+ if (ret < 0) {
+ err(EXIT_FAILURE, "failed to read %u bytes at %"
+ PRIu32, len, off);
+ } else if (ret == 0) {
+ warnx("hit unexpected EOF reading cfgspace from file "
+ "at offest %" PRIu32 ", still wanted to read %u "
+ "bytes", off, len);
+ return (B_FALSE);
+ } else {
+ len -= ret;
+ off += ret;
+ bufoff += ret;
+ }
+
+ }
+
+ return (B_TRUE);
+}
+
+void
+pcieadm_init_cfgspace_file(pcieadm_t *pcip, const char *path,
+ pcieadm_cfgspace_f *funcp, void **arg)
+{
+ int fd;
+ struct stat st;
+ pcieadm_cfgspace_file_t *pcfi;
+
+ if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pcip->pia_priv_eff) != 0) {
+ err(EXIT_FAILURE, "failed to raise privileges");
+ }
+
+ if ((fd = open(path, O_RDONLY)) < 0) {
+ err(EXIT_FAILURE, "failed to open input file %s", path);
+ }
+
+ if (fstat(fd, &st) != 0) {
+ err(EXIT_FAILURE, "failed to get stat information for %s",
+ path);
+ }
+
+ if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pcip->pia_priv_min) != 0) {
+ err(EXIT_FAILURE, "failed to reduce privileges");
+ }
+
+ if (S_ISDIR(st.st_mode)) {
+ errx(EXIT_FAILURE, "input file %s is a directory, unable "
+ "to read data", path);
+ }
+
+ if (S_ISLNK(st.st_mode)) {
+ errx(EXIT_FAILURE, "input file %s is a symbolic link, unable "
+ "to read data", path);
+ }
+
+ if (S_ISDOOR(st.st_mode)) {
+ errx(EXIT_FAILURE, "input file %s is a door, unable "
+ "to read data", path);
+ }
+
+ if (S_ISPORT(st.st_mode)) {
+ errx(EXIT_FAILURE, "input file %s is an event port, unable "
+ "to read data", path);
+ }
+
+ /*
+ * Assume if we were given a FIFO, character/block device, socket, or
+ * something else that it's probably fine.
+ */
+ pcfi = calloc(1, sizeof (*pcfi));
+ if (pcfi == NULL) {
+ err(EXIT_FAILURE, "failed to allocate memory for reading "
+ "cfgspace data from a file");
+ }
+
+ pcfi->pcfi_fd = fd;
+ *arg = pcfi;
+ *funcp = pcieadm_read_cfgspace_file;
+}
+
+void
+pcieadm_fini_cfgspace_file(void *arg)
+{
+ pcieadm_cfgspace_file_t *pcfi = arg;
+ VERIFY0(close(pcfi->pcfi_fd));
+ free(pcfi);
+}
+
+typedef struct pcieadm_cfgspace_kernel {
+ pcieadm_t *pck_pci;
+ int pck_fd;
+ uint8_t pck_bus;
+ uint8_t pck_dev;
+ uint8_t pck_func;
+} pcieadm_cfgspace_kernel_t;
+
+static boolean_t
+pcieadm_read_cfgspace_kernel(uint32_t off, uint8_t len, void *buf, void *arg)
+{
+ pcieadm_cfgspace_kernel_t *pck = arg;
+ pcieadm_t *pcip = pck->pck_pci;
+ pcitool_reg_t pci_reg;
+
+ bzero(&pci_reg, sizeof (pci_reg));
+ pci_reg.user_version = PCITOOL_VERSION;
+ pci_reg.bus_no = pck->pck_bus;
+ pci_reg.dev_no = pck->pck_dev;
+ pci_reg.func_no = pck->pck_func;
+ pci_reg.barnum = 0;
+ pci_reg.offset = off;
+ pci_reg.acc_attr = PCITOOL_ACC_ATTR_ENDN_LTL;
+
+ switch (len) {
+ case 1:
+ pci_reg.acc_attr += PCITOOL_ACC_ATTR_SIZE_1;
+ break;
+ case 2:
+ pci_reg.acc_attr += PCITOOL_ACC_ATTR_SIZE_2;
+ break;
+ case 4:
+ pci_reg.acc_attr += PCITOOL_ACC_ATTR_SIZE_4;
+ break;
+ case 8:
+ pci_reg.acc_attr += PCITOOL_ACC_ATTR_SIZE_8;
+ break;
+ default:
+ errx(EXIT_FAILURE, "asked to read invalid size from kernel: %u",
+ len);
+ }
+
+ if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pcip->pia_priv_eff) != 0) {
+ err(EXIT_FAILURE, "failed to raise privileges");
+ }
+
+ if (ioctl(pck->pck_fd, PCITOOL_DEVICE_GET_REG, &pci_reg) != 0) {
+ err(EXIT_FAILURE, "failed to read device offset 0x%x", off);
+ }
+
+ if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pcip->pia_priv_min) != 0) {
+ err(EXIT_FAILURE, "failed to reduce privileges");
+ }
+
+ switch (len) {
+ case 1:
+ *(uint8_t *)buf = (uint8_t)pci_reg.data;
+ break;
+ case 2:
+ *(uint16_t *)buf = (uint16_t)pci_reg.data;
+ break;
+ case 4:
+ *(uint32_t *)buf = (uint32_t)pci_reg.data;
+ break;
+ case 8:
+ *(uint64_t *)buf = (uint64_t)pci_reg.data;
+ break;
+ }
+
+ return (B_TRUE);
+}
+
+void
+pcieadm_init_cfgspace_kernel(pcieadm_t *pcip, pcieadm_cfgspace_f *funcp,
+ void **arg)
+{
+ char *nexus_base;
+ char nexus_reg[PATH_MAX];
+ int fd, nregs, *regs;
+ pcieadm_cfgspace_kernel_t *pck;
+
+ if ((nexus_base = di_devfs_path(pcip->pia_nexus)) == NULL) {
+ err(EXIT_FAILURE, "failed to get path to nexus node");
+ }
+
+ if (snprintf(nexus_reg, sizeof (nexus_reg), "/devices%s:reg",
+ nexus_base) >= sizeof (nexus_reg)) {
+ errx(EXIT_FAILURE, "failed to construct nexus path, path "
+ "overflow");
+ }
+ free(nexus_base);
+
+ if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pcip->pia_priv_eff) != 0) {
+ err(EXIT_FAILURE, "failed to raise privileges");
+ }
+
+ if ((fd = open(nexus_reg, O_RDONLY)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", nexus_reg);
+ }
+
+ if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pcip->pia_priv_min) != 0) {
+ err(EXIT_FAILURE, "failed to reduce privileges");
+ }
+
+ nregs = di_prop_lookup_ints(DDI_DEV_T_ANY, pcip->pia_devi, "reg",
+ &regs);
+ if (nregs <= 0) {
+ errx(EXIT_FAILURE, "failed to lookup regs array for %s",
+ pcip->pia_devstr);
+ }
+
+ pck = calloc(1, sizeof (pcieadm_cfgspace_kernel_t));
+ if (pck == NULL) {
+ err(EXIT_FAILURE, "failed to allocate memory for reading "
+ "kernel cfgspace data");
+ }
+
+ pck->pck_pci = pcip;
+ pck->pck_fd = fd;
+ pck->pck_bus = PCI_REG_BUS_G(regs[0]);
+ pck->pck_dev = PCI_REG_DEV_G(regs[0]);
+ pck->pck_func = PCI_REG_FUNC_G(regs[0]);
+
+ *funcp = pcieadm_read_cfgspace_kernel;
+ *arg = pck;
+}
+
+void
+pcieadm_fini_cfgspace_kernel(void *arg)
+{
+ pcieadm_cfgspace_kernel_t *pck = arg;
+
+ VERIFY0(close(pck->pck_fd));
+ free(pck);
+}
+
+static const pcieadm_cmdtab_t pcieadm_cmds[] = {
+ { "save-cfgspace", pcieadm_save_cfgspace, pcieadm_save_cfgspace_usage },
+ { "show-cfgspace", pcieadm_show_cfgspace, pcieadm_show_cfgspace_usage },
+ { "show-devs", pcieadm_show_devs, pcieadm_show_devs_usage },
+ { NULL }
+};
+
+static void
+pcieadm_usage(const char *format, ...)
+{
+ uint_t cmd;
+
+ if (format != NULL) {
+ va_list ap;
+
+ va_start(ap, format);
+ vwarnx(format, ap);
+ va_end(ap);
+ }
+
+ (void) fprintf(stderr, "usage: %s <subcommand> <args> ...\n\n",
+ pcieadm_progname);
+
+ for (cmd = 0; pcieadm_cmds[cmd].pct_name != NULL; cmd++) {
+ if (pcieadm_cmds[cmd].pct_use != NULL) {
+ pcieadm_cmds[cmd].pct_use(stderr);
+ }
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ uint_t cmd;
+
+ pcieadm_progname = basename(argv[0]);
+
+ if (argc < 2) {
+ pcieadm_usage("missing required sub-command");
+ exit(EXIT_USAGE);
+ }
+
+ for (cmd = 0; pcieadm_cmds[cmd].pct_name != NULL; cmd++) {
+ if (strcmp(pcieadm_cmds[cmd].pct_name, argv[1]) == 0) {
+ break;
+ }
+ }
+
+ if (pcieadm_cmds[cmd].pct_name == NULL) {
+ pcieadm_usage("unknown sub-command: %s", argv[1]);
+ exit(EXIT_USAGE);
+ }
+ argc -= 2;
+ argv += 2;
+ optind = 0;
+ pcieadm.pia_cmdtab = &pcieadm_cmds[cmd];
+
+ /*
+ * Set up common things that all of pcieadm needs before dispatching to
+ * a specific sub-command.
+ */
+ pcieadm.pia_pcidb = pcidb_open(PCIDB_VERSION);
+ if (pcieadm.pia_pcidb == NULL) {
+ err(EXIT_FAILURE, "failed to open PCI ID database");
+ }
+
+ pcieadm.pia_root = di_init("/", DINFOCPYALL);
+ if (pcieadm.pia_root == DI_NODE_NIL) {
+ err(EXIT_FAILURE, "failed to initialize devinfo tree");
+ }
+
+ /*
+ * Set up privileges now that we have already opened our core libraries.
+ * We first set up the minimum actual privilege set that we use while
+ * running. We next set up a second privilege set that has additional
+ * privileges that are intersected with the users actual privileges and
+ * are appended to by the underlying command backends.
+ */
+ if ((pcieadm.pia_priv_init = priv_allocset()) == NULL) {
+ err(EXIT_FAILURE, "failed to allocate privilege set");
+ }
+
+ if (getppriv(PRIV_EFFECTIVE, pcieadm.pia_priv_init) != 0) {
+ err(EXIT_FAILURE, "failed to get current privileges");
+ }
+
+ if ((pcieadm.pia_priv_min = priv_allocset()) == NULL) {
+ err(EXIT_FAILURE, "failed to allocate privilege set");
+ }
+
+ if ((pcieadm.pia_priv_eff = priv_allocset()) == NULL) {
+ err(EXIT_FAILURE, "failed to allocate privilege set");
+ }
+
+ /*
+ * Note, PRIV_FILE_READ is not removed from the basic set so that way we
+ * can still open libraries that are required due to lazy loading.
+ */
+ priv_basicset(pcieadm.pia_priv_min);
+ VERIFY0(priv_delset(pcieadm.pia_priv_min, PRIV_FILE_LINK_ANY));
+ VERIFY0(priv_delset(pcieadm.pia_priv_min, PRIV_PROC_INFO));
+ VERIFY0(priv_delset(pcieadm.pia_priv_min, PRIV_PROC_SESSION));
+ VERIFY0(priv_delset(pcieadm.pia_priv_min, PRIV_PROC_FORK));
+ VERIFY0(priv_delset(pcieadm.pia_priv_min, PRIV_NET_ACCESS));
+ VERIFY0(priv_delset(pcieadm.pia_priv_min, PRIV_FILE_WRITE));
+ VERIFY0(priv_delset(pcieadm.pia_priv_min, PRIV_PROC_EXEC));
+ VERIFY0(priv_delset(pcieadm.pia_priv_min, PRIV_PROC_EXEC));
+
+ priv_copyset(pcieadm.pia_priv_min, pcieadm.pia_priv_eff);
+ priv_intersect(pcieadm.pia_priv_init, pcieadm.pia_priv_eff);
+
+ if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pcieadm.pia_priv_min) != 0) {
+ err(EXIT_FAILURE, "failed to reduce privileges");
+ }
+
+ return (pcieadm.pia_cmdtab->pct_func(&pcieadm, argc, argv));
+}
diff --git a/usr/src/cmd/pcieadm/pcieadm.h b/usr/src/cmd/pcieadm/pcieadm.h
new file mode 100644
index 0000000000..b5d44ef970
--- /dev/null
+++ b/usr/src/cmd/pcieadm/pcieadm.h
@@ -0,0 +1,112 @@
+/*
+ * 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
+ */
+
+#ifndef _PCIEADM_H
+#define _PCIEADM_H
+
+/*
+ * Common definitions for pcieadm(1M).
+ */
+
+#include <libdevinfo.h>
+#include <pcidb.h>
+#include <priv.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct pcieadm pcieadm_t;
+
+typedef struct pcieadm_cmdtab {
+ const char *pct_name;
+ int (*pct_func)(pcieadm_t *, int, char **);
+ void (*pct_use)(FILE *);
+} pcieadm_cmdtab_t;
+
+struct pcieadm {
+ uint_t pia_indent;
+ di_node_t pia_root;
+ const char *pia_devstr;
+ di_node_t pia_devi;
+ di_node_t pia_nexus;
+ pcidb_hdl_t *pia_pcidb;
+ const pcieadm_cmdtab_t *pia_cmdtab;
+ priv_set_t *pia_priv_init;
+ priv_set_t *pia_priv_min;
+ priv_set_t *pia_priv_eff;
+};
+
+typedef struct {
+ void *pdw_arg;
+ int (*pdw_func)(di_node_t, void *);
+} pcieadm_di_walk_t;
+
+/*
+ * Config space related
+ */
+typedef boolean_t (*pcieadm_cfgspace_f)(uint32_t, uint8_t, void *, void *);
+
+/*
+ * Utilities
+ */
+extern void pcieadm_di_walk(pcieadm_t *, pcieadm_di_walk_t *);
+extern void pcieadm_init_cfgspace_kernel(pcieadm_t *, pcieadm_cfgspace_f *,
+ void **);
+extern void pcieadm_fini_cfgspace_kernel(void *);
+extern void pcieadm_init_cfgspace_file(pcieadm_t *, const char *,
+ pcieadm_cfgspace_f *, void **);
+extern void pcieadm_fini_cfgspace_file(void *);
+extern void pcieadm_find_nexus(pcieadm_t *);
+extern void pcieadm_find_dip(pcieadm_t *, const char *);
+extern boolean_t pcieadm_di_node_is_pci(di_node_t);
+
+/*
+ * Output related
+ */
+extern const char *pcieadm_progname;
+extern void pcieadm_indent(void);
+extern void pcieadm_deindent(void);
+extern void pcieadm_print(const char *, ...);
+extern void pcieadm_ofmt_errx(const char *, ...);
+
+/*
+ * Command tabs
+ */
+extern int pcieadm_save_cfgspace(pcieadm_t *, int, char *[]);
+extern void pcieadm_save_cfgspace_usage(FILE *);
+extern int pcieadm_show_cfgspace(pcieadm_t *, int, char *[]);
+extern void pcieadm_show_cfgspace_usage(FILE *);
+extern int pcieadm_show_devs(pcieadm_t *, int, char *[]);
+extern void pcieadm_show_devs_usage(FILE *);
+
+#define EXIT_USAGE 2
+
+/*
+ * Privilege related. Note there are no centralized functions around raising and
+ * lowering privs as that unfortunately makes ROPs more easy to execute.
+ */
+extern void pcieadm_init_privs(pcieadm_t *);
+
+/*
+ * XXX Maybe not here:
+ */
+#define BITX(u, h, l) (((u) >> (l)) & ((1LU << ((h) - (l) + 1LU)) - 1LU))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PCIEADM_H */
diff --git a/usr/src/cmd/pcieadm/pcieadm_cfgspace.c b/usr/src/cmd/pcieadm/pcieadm_cfgspace.c
new file mode 100644
index 0000000000..50d98c5ec9
--- /dev/null
+++ b/usr/src/cmd/pcieadm/pcieadm_cfgspace.c
@@ -0,0 +1,5046 @@
+/*
+ * 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
+ */
+
+/*
+ * This file contains logic to walk and print a large chunk of configuration
+ * space and many of the capabilities. There are multiple sub-commands that
+ * vector into the same logic (e.g. 'save-cfgspace' and 'show-cfgspace'). In
+ * general, there are a few major goals with this bit of code:
+ *
+ * o Every field should strive to be parsable and therefore selectable for
+ * output. This drove the idea that every field has both a short name and a
+ * human name. The short name is a dot-delineated name. When in parsable
+ * mode, the name will always refer to a single field. However, for
+ * convenience for humans, when not trying to be parsable, we show the
+ * parents in the tree. That is if you specify something like
+ * 'pcie.linkcap.maxspeed', in parsable mode you'll only get that; however,
+ * in non-parsable mode, you'll get an indication of the capability and
+ * register that field was in.
+ *
+ * o Related to the above, parsable mode always outputs a raw, uninterpreted
+ * value. This was done on purpose. Some fields require interpreting multiple
+ * registers to have meaning and long strings aren't always the most useful.
+ *
+ * o Every field isn't always pretty printed. This was generally just a
+ * decision based upon the field itself and how much work it'd be to fit it
+ * into the framework we have. In general, the ones we're mostly guilty of
+ * doing this with are related to cases where there's a scaling value in a
+ * subsequent register. If you find yourself wanting this, feel free to add
+ * it.
+ *
+ * o Currently designated vendor-specific capabilities aren't included here (or
+ * any specific vendor-specific capabilities for that matter). If they are
+ * added, they should follow the same angle of using a name to represent a
+ * sub-capability as we did with HyperTransport.
+ */
+
+#include <err.h>
+#include <strings.h>
+#include <sys/sysmacros.h>
+#include <sys/pci.h>
+#include <sys/pcie.h>
+#include <sys/debug.h>
+#include <ofmt.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "pcieadm.h"
+
+typedef enum pcieadm_cfgspace_op {
+ PCIEADM_CFGSPACE_OP_PRINT,
+ PCIEADM_CFGSPACE_OP_WRITE
+} pcieadm_cfgspace_op_t;
+
+typedef enum piceadm_cfgspace_flag {
+ PCIEADM_CFGSPACE_F_PARSE = 1 << 0,
+ PCIEADM_CFGSPACE_F_SHORT = 1 << 1,
+} pcieadm_cfgspace_flags_t;
+
+typedef enum pcieadm_cfgspace_otype {
+ PCIEADM_CFGSPACE_OT_SHORT,
+ PCIEADM_CFGSPACE_OT_HUMAN,
+ PCIEADM_CFGSPACE_OT_VALUE
+} pcieadm_cfgsapce_otype_t;
+
+typedef struct pcieadm_cfgspace_ofmt {
+ const char *pco_base;
+ const char *pco_short;
+ const char *pco_human;
+ uint64_t pco_value;
+ const char *pco_strval;
+} pcieadm_cfgspace_ofmt_t;
+
+typedef enum pcieadm_regdef_val {
+ PRDV_STRVAL,
+ PRDV_BITFIELD,
+ PRDV_HEX
+} pcieadm_regdef_val_t;
+
+typedef struct pcieadm_regdef_addend {
+ uint8_t pra_shift;
+ int64_t pra_addend;
+} pcieadm_regdef_addend_t;
+
+typedef struct pcieadm_regdef {
+ uint8_t prd_lowbit;
+ uint8_t prd_hibit;
+ const char *prd_short;
+ const char *prd_human;
+ pcieadm_regdef_val_t prd_valtype;
+ union {
+ /*
+ * Enough space for up to an 8-bit fields worth of values
+ * (though we expect most to be sparse).
+ */
+ const char *prdv_strval[128];
+ pcieadm_regdef_addend_t prdv_hex;
+ } prd_val;
+} pcieadm_regdef_t;
+
+typedef struct pcieadm_unitdef {
+ const char *pcd_unit;
+ uint32_t pcd_mult;
+} pcieadm_unitdef_t;
+
+typedef struct pcieadm_strmap {
+ const char *psr_str;
+ uint64_t psr_val;
+} pcieadm_strmap_t;
+
+typedef struct pcieadm_cfgspace_filter {
+ const char *pcf_string;
+ size_t pcf_len;
+ boolean_t pcf_used;
+} pcieadm_cfgspace_filter_t;
+
+typedef struct pcieadm_strfilt {
+ struct pcieadm_strfilt *pstr_next;
+ const char *pstr_str;
+ char pstr_curgen[256];
+} pcieadm_strfilt_t;
+
+/*
+ * Data is sized to be large enough that we can hold all of PCIe extended
+ * configuration space.
+ */
+typedef union pcieadm_cfgspace_data {
+ uint8_t pcb_u8[PCIE_CONF_HDR_SIZE];
+ uint32_t pcb_u32[PCIE_CONF_HDR_SIZE / 4];
+} pcieadm_cfgspace_data_t;
+
+typedef struct pcieadm_cfgspace_walk {
+ pcieadm_t *pcw_pcieadm;
+ pcieadm_cfgspace_op_t pcw_op;
+ uint32_t pcw_valid;
+ pcieadm_cfgspace_data_t *pcw_data;
+ uint16_t pcw_capoff;
+ uint32_t pcw_caplen;
+ int pcw_outfd;
+ uint_t pcw_dtype;
+ uint_t pcw_nlanes;
+ uint_t pcw_pcietype;
+ uint_t pcw_nfilters;
+ pcieadm_cfgspace_filter_t *pcw_filters;
+ pcieadm_cfgspace_flags_t pcw_flags;
+ ofmt_handle_t pcw_ofmt;
+ pcieadm_strfilt_t *pcw_filt;
+} pcieadm_cfgspace_walk_t;
+
+void
+pcieadm_strfilt_pop(pcieadm_cfgspace_walk_t *walkp)
+{
+ pcieadm_strfilt_t *filt;
+
+ VERIFY3P(walkp->pcw_filt, !=, NULL);
+ filt = walkp->pcw_filt;
+ walkp->pcw_filt = filt->pstr_next;
+ free(filt);
+}
+
+void
+pcieadm_strfilt_push(pcieadm_cfgspace_walk_t *walkp, const char *str)
+{
+ pcieadm_strfilt_t *filt;
+ size_t len;
+
+ filt = calloc(1, sizeof (*filt));
+ if (filt == NULL) {
+ errx(EXIT_FAILURE, "failed to allocate memory for string "
+ "filter");
+ }
+
+ filt->pstr_str = str;
+ if (walkp->pcw_filt == NULL) {
+ len = strlcat(filt->pstr_curgen, str,
+ sizeof (filt->pstr_curgen));
+ } else {
+ len = snprintf(filt->pstr_curgen, sizeof (filt->pstr_curgen),
+ "%s.%s", walkp->pcw_filt->pstr_curgen, str);
+ filt->pstr_next = walkp->pcw_filt;
+ }
+
+ if (len >= sizeof (filt->pstr_curgen)) {
+ errx(EXIT_FAILURE, "overflowed internal string buffer "
+ "appending %s", str);
+ }
+
+ walkp->pcw_filt = filt;
+}
+
+static boolean_t
+pcieadm_cfgspace_filter(pcieadm_cfgspace_walk_t *walkp, const char *str)
+{
+ char buf[1024];
+ size_t len;
+
+ if (walkp->pcw_nfilters == 0) {
+ return (B_TRUE);
+ }
+
+ if (str == NULL) {
+ return (B_FALSE);
+ }
+
+ if (walkp->pcw_filt != NULL) {
+ len = snprintf(buf, sizeof (buf), "%s.%s",
+ walkp->pcw_filt->pstr_curgen, str);
+ } else {
+ len = snprintf(buf, sizeof (buf), "%s", str);
+ }
+
+ if (len >= sizeof (buf)) {
+ abort();
+ }
+
+ for (uint_t i = 0; i < walkp->pcw_nfilters; i++) {
+ if (strcmp(buf, walkp->pcw_filters[i].pcf_string) == 0) {
+ walkp->pcw_filters[i].pcf_used = B_TRUE;
+ return (B_TRUE);
+ }
+
+ /*
+ * If we're in non-parsable mode, we want to do a little bit
+ * more in a few cases. We want to make sure that we print the
+ * parents of more-specific entries. That is, if someone
+ * specified 'header.command.serr', then we want to print
+ * 'header', and 'header.command'. Similarly, if someone
+ * specifies an individual field, we want to print all of its
+ * subfields, that is asking for 'header.command', really gets
+ * that and all of 'header.command.*'.
+ */
+ if ((walkp->pcw_flags & PCIEADM_CFGSPACE_F_PARSE) != 0) {
+ continue;
+ }
+
+ if (len >= walkp->pcw_filters[i].pcf_len) {
+ if (strncmp(buf, walkp->pcw_filters[i].pcf_string,
+ walkp->pcw_filters[i].pcf_len) == 0 &&
+ buf[walkp->pcw_filters[i].pcf_len] == '.') {
+ return (B_TRUE);
+ }
+ } else {
+ if (strncmp(buf, walkp->pcw_filters[i].pcf_string,
+ len) == 0 &&
+ walkp->pcw_filters[i].pcf_string[len] == '.') {
+ return (B_TRUE);
+ }
+ }
+ }
+
+ return (B_FALSE);
+}
+
+static boolean_t
+pcieadm_cfgspace_ofmt_cb(ofmt_arg_t *ofarg, char *buf, uint_t buflen)
+{
+ pcieadm_cfgspace_ofmt_t *pco = ofarg->ofmt_cbarg;
+
+ switch (ofarg->ofmt_id) {
+ case PCIEADM_CFGSPACE_OT_SHORT:
+ if (snprintf(buf, buflen, "%s.%s", pco->pco_base,
+ pco->pco_short) >= buflen) {
+ return (B_FALSE);
+ }
+ break;
+ case PCIEADM_CFGSPACE_OT_HUMAN:
+ if (strlcpy(buf, pco->pco_human, buflen) >= buflen) {
+ return (B_FALSE);
+ }
+ break;
+ case PCIEADM_CFGSPACE_OT_VALUE:
+ if (pco->pco_strval != NULL) {
+ if (strlcpy(buf, pco->pco_strval, buflen) >= buflen) {
+ return (B_FALSE);
+ }
+ } else {
+ if (snprintf(buf, buflen, "0x%" PRIx64,
+ pco->pco_value) >= buflen) {
+ return (B_FALSE);
+ }
+ }
+ break;
+ default:
+ abort();
+ }
+
+ return (B_TRUE);
+}
+
+
+static const ofmt_field_t pcieadm_cfgspace_ofmt[] = {
+ { "SHORT", 30, PCIEADM_CFGSPACE_OT_SHORT, pcieadm_cfgspace_ofmt_cb },
+ { "HUMAN", 30, PCIEADM_CFGSPACE_OT_HUMAN, pcieadm_cfgspace_ofmt_cb },
+ { "VALUE", 20, PCIEADM_CFGSPACE_OT_VALUE, pcieadm_cfgspace_ofmt_cb },
+ { NULL, 0, 0, NULL }
+};
+
+static void
+pcieadm_cfgspace_print_parse(pcieadm_cfgspace_walk_t *walkp,
+ const char *sname, const char *human, uint64_t value)
+{
+ pcieadm_cfgspace_ofmt_t pco;
+
+ VERIFY3P(walkp->pcw_filt, !=, NULL);
+ pco.pco_base = walkp->pcw_filt->pstr_curgen;
+ pco.pco_short = sname;
+ pco.pco_human = human;
+ pco.pco_value = value;
+ pco.pco_strval = NULL;
+ ofmt_print(walkp->pcw_ofmt, &pco);
+}
+
+typedef struct pcieadm_cfgspace_print pcieadm_cfgspace_print_t;
+typedef void (*pcieadm_cfgspace_print_f)(pcieadm_cfgspace_walk_t *,
+ pcieadm_cfgspace_print_t *, void *);
+
+struct pcieadm_cfgspace_print {
+ uint8_t pcp_off;
+ uint8_t pcp_len;
+ const char *pcp_short;
+ const char *pcp_human;
+ pcieadm_cfgspace_print_f pcp_print;
+ void *pcp_arg;
+};
+
+static void
+pcieadm_field_printf(pcieadm_cfgspace_walk_t *walkp, const char *shortf,
+ const char *humanf, uint64_t val, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (!pcieadm_cfgspace_filter(walkp, shortf))
+ return;
+
+ if (walkp->pcw_ofmt != NULL) {
+ pcieadm_cfgspace_print_parse(walkp, shortf, humanf, val);
+ return;
+ }
+
+ if (walkp->pcw_pcieadm->pia_indent > 0) {
+ (void) printf("%*s", walkp->pcw_pcieadm->pia_indent, "");
+ }
+
+ if ((walkp->pcw_flags & PCIEADM_CFGSPACE_F_SHORT) != 0) {
+ (void) printf("|--> %s (%s.%s): ", humanf,
+ walkp->pcw_filt->pstr_curgen, shortf);
+ } else {
+ (void) printf("|--> %s: ", humanf);
+ }
+
+ va_start(ap, fmt);
+ (void) vprintf(fmt, ap);
+ va_end(ap);
+
+}
+
+static void
+pcieadm_cfgspace_printf(pcieadm_cfgspace_walk_t *walkp,
+ pcieadm_cfgspace_print_t *print, uint64_t val, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (!pcieadm_cfgspace_filter(walkp, print->pcp_short))
+ return;
+
+ if (walkp->pcw_ofmt != NULL) {
+ pcieadm_cfgspace_print_parse(walkp, print->pcp_short,
+ print->pcp_human, val);
+ return;
+ }
+
+ if (walkp->pcw_pcieadm->pia_indent > 0) {
+ (void) printf("%*s", walkp->pcw_pcieadm->pia_indent, "");
+ }
+
+ if ((walkp->pcw_flags & PCIEADM_CFGSPACE_F_SHORT) != 0) {
+ (void) printf("%s (%s.%s): ", print->pcp_human,
+ walkp->pcw_filt->pstr_curgen, print->pcp_short);
+ } else {
+ (void) printf("%s: ", print->pcp_human);
+ }
+
+ va_start(ap, fmt);
+ (void) vprintf(fmt, ap);
+ va_end(ap);
+}
+
+static void
+pcieadm_cfgspace_puts(pcieadm_cfgspace_walk_t *walkp,
+ pcieadm_cfgspace_print_t *print, const char *str)
+{
+ if (!pcieadm_cfgspace_filter(walkp, print->pcp_short))
+ return;
+
+ if (walkp->pcw_ofmt != NULL) {
+ pcieadm_cfgspace_ofmt_t pco;
+
+ VERIFY3P(walkp->pcw_filt, !=, NULL);
+ pco.pco_base = walkp->pcw_filt->pstr_curgen;
+ pco.pco_short = print->pcp_short;
+ pco.pco_human = print->pcp_human;
+ pco.pco_strval = str;
+ ofmt_print(walkp->pcw_ofmt, &pco);
+ return;
+ }
+
+ if (walkp->pcw_pcieadm->pia_indent > 0) {
+ (void) printf("%*s", walkp->pcw_pcieadm->pia_indent, "");
+ }
+
+ if ((walkp->pcw_flags & PCIEADM_CFGSPACE_F_SHORT) != 0) {
+ (void) printf("%s (%s.%s): %s\n", print->pcp_human,
+ walkp->pcw_filt->pstr_curgen, print->pcp_short, str);
+ } else {
+ (void) printf("%s: %s\n", print->pcp_human, str);
+ }
+}
+
+static uint64_t
+pcieadm_cfgspace_extract(pcieadm_cfgspace_walk_t *walkp,
+ pcieadm_cfgspace_print_t *print)
+{
+ uint32_t val = 0;
+
+ VERIFY3U(print->pcp_len, <=, 8);
+ VERIFY3U(print->pcp_off + print->pcp_len + walkp->pcw_capoff, <=,
+ walkp->pcw_valid);
+ for (uint8_t i = print->pcp_len; i > 0; i--) {
+ val <<= 8;
+ val |= walkp->pcw_data->pcb_u8[walkp->pcw_capoff +
+ print->pcp_off + i - 1];
+ }
+
+ return (val);
+}
+
+static uint16_t
+pcieadm_cfgspace_extract_u16(pcieadm_cfgspace_walk_t *walkp,
+ pcieadm_cfgspace_print_t *print)
+{
+ VERIFY(print->pcp_len == 2);
+ return ((uint16_t)pcieadm_cfgspace_extract(walkp, print));
+}
+
+static void
+pcieadm_cfgspace_print_unit(pcieadm_cfgspace_walk_t *walkp,
+ pcieadm_cfgspace_print_t *print, void *arg)
+{
+ pcieadm_unitdef_t *unit = arg;
+ uint64_t rawval = pcieadm_cfgspace_extract(walkp, print);
+ uint64_t val = rawval;
+
+ if (unit->pcd_mult > 1) {
+ val *= unit->pcd_mult;
+ }
+ pcieadm_cfgspace_printf(walkp, print, rawval, "0x%" PRIx64 " %s%s\n",
+ val, unit->pcd_unit, val != 1 ? "s" : "");
+}
+
+static void
+pcieadm_cfgspace_print_regdef(pcieadm_cfgspace_walk_t *walkp,
+ pcieadm_cfgspace_print_t *print, void *arg)
+{
+ pcieadm_regdef_t *regdef = arg;
+ uint64_t val = pcieadm_cfgspace_extract(walkp, print);
+
+ pcieadm_cfgspace_printf(walkp, print, val, "0x%" PRIx64 "\n", val);
+
+ pcieadm_indent();
+ pcieadm_strfilt_push(walkp, print->pcp_short);
+
+ for (regdef = arg; regdef->prd_short != NULL; regdef++) {
+ uint32_t nbits = regdef->prd_hibit - regdef->prd_lowbit + 1UL;
+ uint32_t bitmask = (1UL << nbits) - 1UL;
+ uint64_t regval = (val >> regdef->prd_lowbit) & bitmask;
+ const char *strval;
+ uint64_t actval;
+
+ if (!pcieadm_cfgspace_filter(walkp, regdef->prd_short)) {
+ continue;
+ }
+
+ switch (regdef->prd_valtype) {
+ case PRDV_STRVAL:
+ strval = regdef->prd_val.prdv_strval[regval];
+ if (strval == NULL) {
+ strval = "reserved";
+ }
+ pcieadm_field_printf(walkp, regdef->prd_short,
+ regdef->prd_human, regval, "%s (0x%" PRIx64 ")\n",
+ strval, regval << regdef->prd_lowbit);
+ break;
+ case PRDV_HEX:
+ actval = regval;
+ if (regdef->prd_val.prdv_hex.pra_shift > 0) {
+ actval <<= regdef->prd_val.prdv_hex.pra_shift;
+ }
+ actval += regdef->prd_val.prdv_hex.pra_addend;
+
+ pcieadm_field_printf(walkp, regdef->prd_short,
+ regdef->prd_human, regval, "0x% " PRIx64 "\n",
+ actval);
+ break;
+ case PRDV_BITFIELD:
+ pcieadm_field_printf(walkp, regdef->prd_short,
+ regdef->prd_human, regval, "0x%" PRIx64 "\n",
+ regval << regdef->prd_lowbit);
+
+ if (walkp->pcw_ofmt == NULL) {
+ pcieadm_indent();
+ for (uint32_t i = 0; i < nbits; i++) {
+ if (((1 << i) & regval) == 0)
+ continue;
+ pcieadm_print("|--> %s (0x%x)\n",
+ regdef->prd_val.prdv_strval[i],
+ 1UL << (i + regdef->prd_lowbit));
+ }
+ pcieadm_deindent();
+ }
+ break;
+ }
+ }
+
+ pcieadm_strfilt_pop(walkp);
+ pcieadm_deindent();
+}
+
+static void
+pcieadm_cfgspace_print_strmap(pcieadm_cfgspace_walk_t *walkp,
+ pcieadm_cfgspace_print_t *print, void *arg)
+{
+ pcieadm_strmap_t *strmap = arg;
+ uint64_t val = pcieadm_cfgspace_extract(walkp, print);
+ const char *str = "reserved";
+
+ for (uint_t i = 0; strmap[i].psr_str != NULL; i++) {
+ if (strmap[i].psr_val == val) {
+ str = strmap[i].psr_str;
+ break;
+ }
+ }
+
+ pcieadm_cfgspace_printf(walkp, print, val, "0x%x -- %s\n", val, str);
+}
+
+static void
+pcieadm_cfgspace_print_hex(pcieadm_cfgspace_walk_t *walkp,
+ pcieadm_cfgspace_print_t *print, void *arg)
+{
+ uint64_t val = pcieadm_cfgspace_extract(walkp, print);
+
+ pcieadm_cfgspace_printf(walkp, print, val, "0x%" PRIx64 "\n", val);
+}
+
+static void
+pcieadm_cfgspace_print_vendor(pcieadm_cfgspace_walk_t *walkp,
+ pcieadm_cfgspace_print_t *print, void *arg)
+{
+ pcidb_vendor_t *vend;
+ uint16_t vid = pcieadm_cfgspace_extract_u16(walkp, print);
+
+ vend = pcidb_lookup_vendor(walkp->pcw_pcieadm->pia_pcidb, vid);
+ if (vend != NULL) {
+ pcieadm_cfgspace_printf(walkp, print, vid, "0x%x -- %s\n", vid,
+ pcidb_vendor_name(vend));
+ } else {
+ pcieadm_cfgspace_printf(walkp, print, vid, "0x%x\n", vid);
+ }
+}
+
+static void
+pcieadm_cfgspace_print_device(pcieadm_cfgspace_walk_t *walkp,
+ pcieadm_cfgspace_print_t *print, void *arg)
+{
+ pcidb_device_t *dev;
+ uint16_t did = pcieadm_cfgspace_extract_u16(walkp, print);
+ uint16_t vid = walkp->pcw_data->pcb_u8[PCI_CONF_VENID] +
+ (walkp->pcw_data->pcb_u8[PCI_CONF_VENID + 1] << 8);
+
+ dev = pcidb_lookup_device(walkp->pcw_pcieadm->pia_pcidb, vid, did);
+ if (dev != NULL) {
+ pcieadm_cfgspace_printf(walkp, print, did, "0x%x -- %s\n", did,
+ pcidb_device_name(dev));
+ } else {
+ pcieadm_cfgspace_printf(walkp, print, did, "0x%x\n", did);
+ }
+}
+
+/*
+ * To print out detailed information about a subsystem vendor or device, we need
+ * all of the information about the vendor and device due to the organization of
+ * the PCI IDs db.
+ */
+static void
+pcieadm_cfgspace_print_subid(pcieadm_cfgspace_walk_t *walkp,
+ pcieadm_cfgspace_print_t *print, void *arg)
+{
+ uint16_t vid = walkp->pcw_data->pcb_u8[PCI_CONF_VENID] +
+ (walkp->pcw_data->pcb_u8[PCI_CONF_VENID + 1] << 8);
+ uint16_t did = walkp->pcw_data->pcb_u8[PCI_CONF_DEVID] +
+ (walkp->pcw_data->pcb_u8[PCI_CONF_DEVID + 1] << 8);
+ uint16_t svid = walkp->pcw_data->pcb_u8[PCI_CONF_SUBVENID] +
+ (walkp->pcw_data->pcb_u8[PCI_CONF_SUBVENID + 1] << 8);
+ uint16_t sdid = walkp->pcw_data->pcb_u8[PCI_CONF_SUBSYSID] +
+ (walkp->pcw_data->pcb_u8[PCI_CONF_SUBSYSID + 1] << 8);
+ uint16_t val = pcieadm_cfgspace_extract_u16(walkp, print);
+ boolean_t isvendor = print->pcp_off == PCI_CONF_SUBVENID;
+
+ if (isvendor) {
+ pcidb_vendor_t *vend;
+ vend = pcidb_lookup_vendor(walkp->pcw_pcieadm->pia_pcidb,
+ svid);
+ if (vend != NULL) {
+ pcieadm_cfgspace_printf(walkp, print, val,
+ "0x%x -- %s\n", val, pcidb_vendor_name(vend));
+ } else {
+ pcieadm_cfgspace_printf(walkp, print, val,
+ "0x%x\n", val);
+ }
+ } else {
+ pcidb_subvd_t *subvd;
+ subvd = pcidb_lookup_subvd(walkp->pcw_pcieadm->pia_pcidb, vid,
+ did, svid, sdid);
+ if (subvd != NULL) {
+ pcieadm_cfgspace_printf(walkp, print, val,
+ "0x%x -- %s\n", val, pcidb_subvd_name(subvd));
+ } else {
+ pcieadm_cfgspace_printf(walkp, print, val, "0x%x\n",
+ val);
+ }
+ }
+}
+
+/*
+ * The variable natures of BARs is a pain. This makes printing this out and the
+ * fields all a bit gross.
+ */
+static void
+pcieadm_cfgspace_print_bars(pcieadm_cfgspace_walk_t *walkp,
+ pcieadm_cfgspace_print_t *print, void *arg)
+{
+ uint32_t *barp = &walkp->pcw_data->pcb_u32[(walkp->pcw_capoff +
+ print->pcp_off) / 4];
+ char barname[32];
+ const char *typestrs[2] = { "Memory Space", "I/O Space" };
+
+ for (uint_t i = 0; i < print->pcp_len / 4; i++) {
+ uint_t type;
+ (void) snprintf(barname, sizeof (barname), "%s%u",
+ print->pcp_short, i);
+
+ type = barp[i] & PCI_BASE_SPACE_M;
+
+ if (pcieadm_cfgspace_filter(walkp, barname) &&
+ walkp->pcw_ofmt == NULL) {
+ if ((walkp->pcw_flags & PCIEADM_CFGSPACE_F_SHORT) !=
+ 0) {
+ pcieadm_print("%s %u (%s.%s)\n",
+ print->pcp_human, i,
+ walkp->pcw_filt->pstr_curgen, barname);
+ } else {
+ pcieadm_print("%s %u\n", print->pcp_human, i);
+ }
+ }
+
+ pcieadm_strfilt_push(walkp, barname);
+ pcieadm_indent();
+
+ pcieadm_field_printf(walkp, "space", "Space", type,
+ "%s (0x%x)\n", typestrs[type], type);
+
+ if (type == PCI_BASE_SPACE_IO) {
+ uint32_t addr = barp[i] & PCI_BASE_IO_ADDR_M;
+
+ pcieadm_field_printf(walkp, "addr", "Address", addr,
+ "0x%" PRIx32 "\n", addr);
+ } else {
+ uint8_t type, pre;
+ uint64_t addr;
+ const char *locstr;
+
+ type = barp[i] & PCI_BASE_TYPE_M;
+ pre = barp[i] & PCI_BASE_PREF_M;
+ addr = barp[i] & PCI_BASE_M_ADDR_M;
+
+ if (type == PCI_BASE_TYPE_ALL) {
+ addr += (uint64_t)barp[i+1] << 32;
+ i++;
+ }
+
+ pcieadm_field_printf(walkp, "addr", "Address", addr,
+ "0x%" PRIx64 "\n", addr);
+
+ switch (type) {
+ case PCI_BASE_TYPE_MEM:
+ locstr = "32-bit";
+ break;
+ case PCI_BASE_TYPE_LOW:
+ locstr = "Sub-1 MiB";
+ break;
+ case PCI_BASE_TYPE_ALL:
+ locstr = "64-bit";
+ break;
+ case PCI_BASE_TYPE_RES:
+ default:
+ locstr = "Reserved";
+ break;
+ }
+
+ pcieadm_field_printf(walkp, "addr", "Address", addr,
+ "%s (0x%x)\n", locstr, type >> 1);
+ pcieadm_field_printf(walkp, "prefetch", "Prefetchable",
+ pre != 0, "%s (0x%x)\n", pre != 0 ? "yes" : "no",
+ pre != 0);
+ }
+
+ pcieadm_deindent();
+ pcieadm_strfilt_pop(walkp);
+ }
+}
+
+static void
+pcieadm_cfgspace_print_ecv(pcieadm_cfgspace_walk_t *walkp,
+ pcieadm_cfgspace_print_t *print, void *arg)
+{
+ uint16_t bitlen, nwords;
+
+ if (BITX(walkp->pcw_data->pcb_u8[walkp->pcw_capoff + 4], 5, 5) == 0) {
+ return;
+ }
+
+ bitlen = walkp->pcw_data->pcb_u8[walkp->pcw_capoff + 5];
+ if (bitlen == 0) {
+ bitlen = 256;
+ }
+
+ nwords = bitlen / 32;
+ if ((bitlen % 8) != 0) {
+ nwords++;
+ }
+
+ for (uint16_t i = 0; i < nwords; i++) {
+ char tshort[32], thuman[128];
+ pcieadm_cfgspace_print_t p;
+
+ (void) snprintf(tshort, sizeof (tshort), "ecv%u", i);
+ (void) snprintf(thuman, sizeof (thuman), "Egress Control "
+ "Vector %u", i);
+ p.pcp_off = print->pcp_off + i * 4;
+ p.pcp_len = 4;
+ p.pcp_short = tshort;
+ p.pcp_human = thuman;
+ p.pcp_print = pcieadm_cfgspace_print_hex;
+ p.pcp_arg = NULL;
+
+ p.pcp_print(walkp, &p, p.pcp_arg);
+ }
+}
+
+static void
+pcieadm_cfgspace_print_dpa_paa(pcieadm_cfgspace_walk_t *walkp,
+ pcieadm_cfgspace_print_t *print, void *arg)
+{
+ uint8_t nents;
+
+ nents = BITX(walkp->pcw_data->pcb_u8[walkp->pcw_capoff + 4], 4, 0) + 1;
+ if (nents == 0) {
+ return;
+ }
+
+ for (uint8_t i = 0; i < nents; i++) {
+ char tshort[32], thuman[128];
+ pcieadm_cfgspace_print_t p;
+
+ (void) snprintf(tshort, sizeof (tshort), "%s%u",
+ print->pcp_short, i);
+ (void) snprintf(thuman, sizeof (thuman), "%s %u",
+ print->pcp_human, i);
+
+ p.pcp_off = print->pcp_off + i;
+ p.pcp_len = 1;
+ p.pcp_short = tshort;
+ p.pcp_human = thuman;
+ p.pcp_print = pcieadm_cfgspace_print_hex;
+ p.pcp_arg = NULL;
+
+ p.pcp_print(walkp, &p, p.pcp_arg);
+ }
+}
+
+/*
+ * Config Space Header Table Definitions
+ */
+static pcieadm_regdef_t pcieadm_regdef_command[] = {
+ { 0, 0, "io", "I/O Space", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 1, 1, "mem", "Memory Space", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 2, 2, "bus", "Bus Master", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 3, 3, "spec", "Special Cycle", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 4, 4, "mwi", "Memory Write and Invalidate", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 5, 5, "vga", "VGA Palette Snoop", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 6, 6, "per", "Parity Error Response", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 7, 7, "idsel", "IDSEL Stepping/Wait Cycle Control", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 8, 8, "serr", "SERR# Enable", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } }, },
+ { 9, 9, "fbtx", "Fast Back-to-Back Transactions", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } }, },
+ { 10, 10, "intx", "Interrupt X", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "enabled", "disabled" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_status[] = {
+ { 0, 0, "imm", "Immediate Readiness", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } }, },
+ { 3, 3, "istat", "Interrupt Status", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "not pending", "pending" } }, },
+ { 4, 4, "capsup", "Capabilities List", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } }, },
+ { 5, 5, "66mhz", "66 MHz Capable", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } }, },
+ { 7, 7, "fbtxcap", "Fast Back-to-Back Capable", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } }, },
+ { 8, 8, "mdperr", "Master Data Parity Error", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no error", "error detected" } }, },
+ { 9, 10, "devsel", "DEVSEL# Timing", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "fast", "medium", "slow",
+ "reserved" } } },
+ { 11, 11, "sta", "Signaled Target Abort", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 12, 12, "rta", "Received Target Abort", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 13, 13, "rma", "Received Master Abort", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 14, 14, "sse", "Signaled System Error", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 15, 15, "dpe", "Detected Parity Error", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { -1, -1, NULL }
+};
+
+/*
+ * It might be interesting to translate these into numbers at a future point.
+ */
+static pcieadm_regdef_t pcieadm_regdef_class[] = {
+ { 16, 23, "class", "Class Code", PRDV_HEX },
+ { 7, 15, "sclass", "Sub-Class Code", PRDV_HEX },
+ { 0, 7, "pi", "Programming Interface", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_bridge_iobase[] = {
+ { 0, 3, "cap", "Addressing Capability", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "16-bit", "32-bit" } } },
+ { 4, 7, "base", "Base", PRDV_HEX,
+ .prd_val = { .prdv_hex = { 12 } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_bridge_iolim[] = {
+ { 0, 3, "cap", "Addressing Capability", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "16-bit", "32-bit" } } },
+ { 4, 7, "limit", "Limit", PRDV_HEX,
+ .prd_val = { .prdv_hex = { 12, 0xfff } } },
+ { -1, -1, NULL }
+};
+
+
+static pcieadm_regdef_t pcieadm_regdef_bridgests[] = {
+ { 5, 5, "66mhz", "66 MHz", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 7, 7, "fastb2b", "Fast Back-to-Back Transactions", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 8, 8, "mdperr", "Master Data Parity Error", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no error", "error detected" } } },
+ { 9, 10, "devsel", "DEVSEL# Timing", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "fast", "medium", "slow" } } },
+ { 11, 11, "sta", "Signaled Target Abort", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no abort", "aborted" } } },
+ { 12, 12, "rta", "Received Target Abort", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no abort", "aborted" } } },
+ { 13, 13, "rma", "Received Master Abort", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no abort", "aborted" } } },
+ { 14, 14, "rsyserr", "Received System Error", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no error", "error received" } } },
+ { 15, 15, "dperr", "Detected Parity Error", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no error", "error detected" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_bridge_membase[] = {
+ { 4, 16, "base", "Base", PRDV_HEX,
+ .prd_val = { .prdv_hex = { 20 } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_bridge_memlim[] = {
+ { 4, 16, "limit", "Limit", PRDV_HEX,
+ .prd_val = { .prdv_hex = { 20, 0xfffff } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_bridge_pfbase[] = {
+ { 0, 3, "cap", "Addressing Capability", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "32-bit", "64-bit" } } },
+ { 4, 16, "base", "Base", PRDV_HEX,
+ .prd_val = { .prdv_hex = { 20 } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_bridge_pflim[] = {
+ { 0, 3, "cap", "Addressing Capability", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "32-bit", "64-bit" } } },
+ { 4, 16, "limit", "Limit", PRDV_HEX,
+ .prd_val = { .prdv_hex = { 20, 0xfffff } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_bridge_ctl[] = {
+ { 0, 0, "perrresp", "Parity Error Response", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 1, 1, "serr", "SERR#", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 2, 2, "isa", "ISA", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 3, 3, "vga", "VGA", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 4, 4, "vgadec", "VGA 16-bit Decode", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "10-bit", "16-bit" } } },
+ { 5, 5, "mabort", "Master Abort", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 6, 6, "secrst", "Secondary Bus Reset", PRDV_HEX },
+ { 7, 7, "fastb2b", "Fast Back-to-Back Transactions", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 8, 8, "pridisc", "Primary Discard Timer", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "2^15 cycles", "2^10 cycles" } } },
+ { 9, 9, "secdisc", "Secondary Discard Timer", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "2^15 cycles", "2^10 cycles" } } },
+ { 10, 10, "disctimer", "Discard Timer Error", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 11, 11, "discserr", "Discard Timer SERR#", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_unitdef_t pcieadm_unitdef_cache = {
+ "byte", 4
+};
+
+static pcieadm_unitdef_t pcieadm_unitdef_latreg = { "cycle" };
+
+static pcieadm_regdef_t pcieadm_regdef_header[] = {
+ { 0, 6, "layout", "Header Layout", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "Device", "Bridge", "PC Card" } } },
+ { 7, 7, "mfd", "Multi-Function Device", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_bist[] = {
+ { 0, 3, "code", "Completion Code", PRDV_HEX },
+ { 6, 6, "start", "Start BIST", PRDV_HEX },
+ { 7, 7, "cap", "BIST Capable", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_exprom[] = {
+ { 0, 0, "enable", "Enable", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 11, 31, "addr", "Base Address", PRDV_HEX,
+ .prd_val = { .prdv_hex = { 21 } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_strmap_t pcieadm_strmap_ipin[] = {
+ { "none", 0 },
+ { "INTA", PCI_INTA },
+ { "INTB", PCI_INTB },
+ { "INTC", PCI_INTC },
+ { "INTD", PCI_INTD },
+ { NULL }
+};
+
+
+static pcieadm_cfgspace_print_t pcieadm_cfgspace_type0[] = {
+ { 0x0, 2, "vendor", "Vendor ID", pcieadm_cfgspace_print_vendor },
+ { 0x2, 2, "device", "Device ID", pcieadm_cfgspace_print_device },
+ { 0x4, 2, "command", "Command", pcieadm_cfgspace_print_regdef,
+ pcieadm_regdef_command },
+ { 0x6, 2, "status", "Status", pcieadm_cfgspace_print_regdef,
+ pcieadm_regdef_status },
+ { 0x8, 1, "revision", "Revision ID", pcieadm_cfgspace_print_hex },
+ { 0x9, 3, "class", "Class Code", pcieadm_cfgspace_print_regdef,
+ pcieadm_regdef_class },
+ { 0xc, 1, "cache", "Cache Line Size", pcieadm_cfgspace_print_unit,
+ &pcieadm_unitdef_cache },
+ { 0xd, 1, "latency", "Latency Timer", pcieadm_cfgspace_print_unit,
+ &pcieadm_unitdef_latreg },
+ { 0xe, 1, "type", "Header Type", pcieadm_cfgspace_print_regdef,
+ pcieadm_regdef_header },
+ { 0xf, 1, "bist", "BIST", pcieadm_cfgspace_print_regdef,
+ pcieadm_regdef_bist },
+ { 0x10, 24, "bar", "Base Address Register",
+ pcieadm_cfgspace_print_bars },
+ { 0x28, 4, "cis", "Cardbus CIS Pointer", pcieadm_cfgspace_print_hex },
+ { 0x2c, 2, "subvid", "Subsystem Vendor ID",
+ pcieadm_cfgspace_print_subid },
+ { 0x2e, 2, "subdev", "Subsystem Device ID",
+ pcieadm_cfgspace_print_subid },
+ { 0x30, 4, "rom", "Expansion ROM", pcieadm_cfgspace_print_regdef,
+ pcieadm_regdef_exprom },
+ { 0x34, 1, "cap", "Capabilities Pointer", pcieadm_cfgspace_print_hex },
+ { 0x3c, 1, "iline", "Interrupt Line", pcieadm_cfgspace_print_hex },
+ { 0x3d, 1, "ipin", "Interrupt Pin", pcieadm_cfgspace_print_strmap,
+ pcieadm_strmap_ipin },
+ { 0x3e, 1, "gnt", "Min_Gnt", pcieadm_cfgspace_print_hex },
+ { 0x3f, 1, "lat", "Min_Lat", pcieadm_cfgspace_print_hex },
+ { -1, -1, NULL }
+};
+
+static pcieadm_cfgspace_print_t pcieadm_cfgspace_type1[] = {
+ { 0x0, 2, "vendor", "Vendor ID", pcieadm_cfgspace_print_vendor },
+ { 0x2, 2, "device", "Device ID", pcieadm_cfgspace_print_device },
+ { 0x4, 2, "command", "Command", pcieadm_cfgspace_print_regdef,
+ pcieadm_regdef_command },
+ { 0x6, 2, "status", "Status", pcieadm_cfgspace_print_regdef,
+ pcieadm_regdef_status },
+ { 0x8, 1, "revision", "Revision ID", pcieadm_cfgspace_print_hex },
+ { 0x9, 3, "class", "Class Code", pcieadm_cfgspace_print_regdef,
+ pcieadm_regdef_class },
+ { 0xc, 1, "cache", "Cache Line Size", pcieadm_cfgspace_print_unit,
+ &pcieadm_unitdef_cache },
+ { 0xd, 1, "latency", "Latency Timer", pcieadm_cfgspace_print_unit,
+ &pcieadm_unitdef_latreg },
+ { 0xe, 1, "type", "Header Type", pcieadm_cfgspace_print_regdef,
+ pcieadm_regdef_header },
+ { 0xf, 1, "bist", "BIST", pcieadm_cfgspace_print_regdef,
+ pcieadm_regdef_bist },
+ { 0x10, 8, "bar", "Base Address Register",
+ pcieadm_cfgspace_print_bars },
+ { PCI_BCNF_PRIBUS, 1, "pribus", "Primary Bus Number",
+ pcieadm_cfgspace_print_hex },
+ { PCI_BCNF_SECBUS, 1, "secbus", "Secondary Bus Number",
+ pcieadm_cfgspace_print_hex },
+ { PCI_BCNF_SUBBUS, 1, "subbus", "Subordinate Bus Number",
+ pcieadm_cfgspace_print_hex },
+ { PCI_BCNF_LATENCY_TIMER, 1, "latency2", "Secondary Latency timer",
+ pcieadm_cfgspace_print_unit, &pcieadm_unitdef_latreg },
+ { PCI_BCNF_IO_BASE_LOW, 1, "iobase", "I/O Base Low",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_bridge_iobase },
+ { PCI_BCNF_IO_LIMIT_LOW, 1, "iolimit", "I/O Limit Low",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_bridge_iolim },
+ { PCI_BCNF_SEC_STATUS, 2, "status2", "Secondary Status",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_bridgests },
+ { PCI_BCNF_MEM_BASE, 2, "membase", "Memory Base",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_bridge_membase },
+ { PCI_BCNF_MEM_LIMIT, 2, "memlimit", "Memory Limit",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_bridge_memlim },
+ { PCI_BCNF_PF_BASE_LOW, 2, "pfbase", "Prefetchable Memory Base",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_bridge_pfbase },
+ { PCI_BCNF_PF_LIMIT_LOW, 2, "pflimit", "Prefetchable Memory Limit",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_bridge_pflim },
+ { PCI_BCNF_PF_BASE_HIGH, 4, "pfbasehi",
+ "Prefetchable Base Upper 32 bits",
+ pcieadm_cfgspace_print_hex },
+ { PCI_BCNF_PF_LIMIT_HIGH, 4, "pflimihi",
+ "Prefetchable Limit Upper 32 bits",
+ pcieadm_cfgspace_print_hex },
+ { PCI_BCNF_IO_BASE_HI, 2, "iobasehi", "I/O Base Upper 16 bits",
+ pcieadm_cfgspace_print_hex },
+ { PCI_BCNF_IO_LIMIT_HI, 2, "iobasehi", "I/O Limit Upper 16 bits",
+ pcieadm_cfgspace_print_hex },
+ { PCI_BCNF_CAP_PTR, 1, "cap", "Capabilities Pointer",
+ pcieadm_cfgspace_print_hex },
+ { PCI_BCNF_ROM, 4, "rom", "Expansion ROM",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_exprom },
+ { PCI_BCNF_ILINE, 1, "iline", "Interrupt Line",
+ pcieadm_cfgspace_print_hex },
+ { PCI_BCNF_IPIN, 1, "ipin", "Interrupt Pin",
+ pcieadm_cfgspace_print_strmap, pcieadm_strmap_ipin },
+ { PCI_BCNF_BCNTRL, 2, "bctl", "Bridge Control",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_bridge_ctl },
+ { -1, -1, NULL }
+};
+
+static pcieadm_cfgspace_print_t pcieadm_cfgspace_unknown[] = {
+ { 0x0, 2, "vendor", "Vendor ID", pcieadm_cfgspace_print_vendor },
+ { 0x2, 2, "device", "Device ID", pcieadm_cfgspace_print_device },
+ { 0x8, 1, "revision", "Revision ID", pcieadm_cfgspace_print_hex },
+ { 0xe, 1, "type", "Header Type", pcieadm_cfgspace_print_regdef,
+ pcieadm_regdef_header },
+ { -1, -1, NULL }
+};
+
+/*
+ * Power Management Capability Version 3. Note versions two and three seem to be
+ * the same, but are used to indicate compliance to different revisions of the
+ * PCI power management specification.
+ */
+static pcieadm_regdef_t pcieadm_regdef_pmcap[] = {
+ { 0, 2, "vers", "Version", PRDV_HEX },
+ { 3, 3, "clock", "PME Clock", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "not required", "required" } } },
+ { 4, 4, "irrd0", "Immediate Readiness on Return to D0", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 5, 5, "dsi", "Device Specific Initialization", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 6, 8, "auxcur", "Auxiliary Current", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "0", "55 mA", "100 mA", "160 mA",
+ "220 mA", "270 mA", "320 mA", "375 mA" } } },
+ { 9, 9, "d1", "D1", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 10, 10, "d2", "D2", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 11, 15, "pme", "PME Support", PRDV_BITFIELD,
+ .prd_val = { .prdv_strval = { "D0", "D1", "D2", "D3hot",
+ "D3cold" } } },
+ { -1, -1, NULL }
+};
+
+
+static pcieadm_cfgspace_print_t pcieadm_cap_pcipm_v3[] = {
+ { PCI_PMCAP, 2, "pmcap", "Power Management Capabilities",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pmcap },
+ { -1, -1, NULL }
+};
+
+/*
+ * PCI Bridge Subsystem Capability
+ */
+static pcieadm_cfgspace_print_t pcieadm_cap_bridge_subsys[] = {
+ { 0x4, 2, "subvid", "Subsystem Vendor ID", pcieadm_cfgspace_print_hex },
+ { 0x6, 2, "subdev", "Subsystem Device ID", pcieadm_cfgspace_print_hex },
+ { -1, -1, NULL }
+};
+
+/*
+ * MSI Capability
+ */
+static pcieadm_regdef_t pcieadm_regdef_msictrl[] = {
+ { 0, 0, "enable", "MSI Enable", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 1, 3, "mmsgcap", "Multiple Message Capable", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "1 vector", "2 vectors",
+ "4 vectors", "8 vectors", "16 vectors", "32 vectors" } } },
+ { 4, 6, "mmsgen", "Multiple Message Enabled", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "1 vector", "2 vectors",
+ "4 vectors", "8 vectors", "16 vectors", "32 vectors" } } },
+ { 7, 7, "addr64", "64-bit Address Capable", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 8, 8, "pvm", "Per-Vector Masking Capable", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 9, 9, "extmdcap", "Extended Message Data Capable",
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 10, 10, "extmden", "extended Message Data Enable",
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_cfgspace_print_t pcieadm_cap_msi_32[] = {
+ { PCI_MSI_CTRL, 2, "ctrl", "Message Control",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_msictrl },
+ { PCI_MSI_ADDR_OFFSET, 4, "addr", "Message Address",
+ pcieadm_cfgspace_print_hex },
+ { PCI_MSI_32BIT_DATA, 2, "data", "Message Data",
+ pcieadm_cfgspace_print_hex },
+ { -1, -1, NULL }
+};
+
+static pcieadm_cfgspace_print_t pcieadm_cap_msi_32ext[] = {
+ { PCI_MSI_CTRL, 2, "ctrl", "Message Control",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_msictrl },
+ { PCI_MSI_ADDR_OFFSET, 4, "addr", "Message Address",
+ pcieadm_cfgspace_print_hex },
+ { PCI_MSI_32BIT_DATA, 2, "data", "Message Data",
+ pcieadm_cfgspace_print_hex },
+ { PCI_MSI_32BIT_EXTDATA, 2, "extdata", "Extended Message Data",
+ pcieadm_cfgspace_print_hex },
+ { -1, -1, NULL }
+};
+
+static pcieadm_cfgspace_print_t pcieadm_cap_msi_32pvm[] = {
+ { PCI_MSI_CTRL, 2, "ctrl", "Message Control",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_msictrl },
+ { PCI_MSI_ADDR_OFFSET, 4, "addr", "Message Address",
+ pcieadm_cfgspace_print_hex },
+ { PCI_MSI_32BIT_DATA, 2, "data", "Message Data",
+ pcieadm_cfgspace_print_hex },
+ { PCI_MSI_32BIT_EXTDATA, 2, "extdata", "Extended Message Data",
+ pcieadm_cfgspace_print_hex },
+ { PCI_MSI_32BIT_MASK, 4, "mask", "Mask Bits",
+ pcieadm_cfgspace_print_hex },
+ { PCI_MSI_32BIT_PENDING, 4, "pend", "Pending Bits",
+ pcieadm_cfgspace_print_hex },
+ { -1, -1, NULL }
+};
+
+static pcieadm_cfgspace_print_t pcieadm_cap_msi_64[] = {
+ { PCI_MSI_CTRL, 2, "ctrl", "Message Control",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_msictrl },
+ { PCI_MSI_ADDR_OFFSET, 4, "addr", "Message Address",
+ pcieadm_cfgspace_print_hex },
+ { PCI_MSI_64BIT_ADDR, 4, "upadd", "Upper Message Address",
+ pcieadm_cfgspace_print_hex },
+ { PCI_MSI_64BIT_DATA, 2, "data", "Message Data",
+ pcieadm_cfgspace_print_hex },
+ { -1, -1, NULL }
+};
+
+static pcieadm_cfgspace_print_t pcieadm_cap_msi_64ext[] = {
+ { PCI_MSI_CTRL, 2, "ctrl", "Message Control",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_msictrl },
+ { PCI_MSI_ADDR_OFFSET, 4, "addr", "Message Address",
+ pcieadm_cfgspace_print_hex },
+ { PCI_MSI_64BIT_ADDR, 4, "upadd", "Upper Message Address",
+ pcieadm_cfgspace_print_hex },
+ { PCI_MSI_64BIT_DATA, 2, "data", "Message Data",
+ pcieadm_cfgspace_print_hex },
+ { PCI_MSI_64BIT_EXTDATA, 2, "extdata", "Extended Message Data",
+ pcieadm_cfgspace_print_hex },
+ { -1, -1, NULL }
+};
+
+static pcieadm_cfgspace_print_t pcieadm_cap_msi_64pvm[] = {
+ { PCI_MSI_CTRL, 2, "ctrl", "Message Control",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_msictrl },
+ { PCI_MSI_ADDR_OFFSET, 4, "addr", "Message Address",
+ pcieadm_cfgspace_print_hex },
+ { PCI_MSI_64BIT_ADDR, 4, "upadd", "Upper Message Address",
+ pcieadm_cfgspace_print_hex },
+ { PCI_MSI_64BIT_DATA, 2, "data", "Message Data",
+ pcieadm_cfgspace_print_hex },
+ { PCI_MSI_64BIT_EXTDATA, 2, "extdata", "Extended Message Data",
+ pcieadm_cfgspace_print_hex },
+ { PCI_MSI_64BIT_MASKBITS, 4, "mask", "Mask Bits",
+ pcieadm_cfgspace_print_hex },
+ { PCI_MSI_64BIT_PENDING, 4, "pend", "Pending Bits",
+ pcieadm_cfgspace_print_hex },
+ { -1, -1, NULL }
+};
+
+/*
+ * MSI-X Capability
+ */
+static pcieadm_regdef_t pcieadm_regdef_msixctrl[] = {
+ { 0, 10, "size", "Table Size", PRDV_HEX,
+ .prd_val = { .prdv_hex = { 0, 1 } } },
+ { 14, 14, "mask", "Function Mask", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unmasked", "masked" } } },
+ { 15, 15, "enable", "MSI-X Enable", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_msixtable[] = {
+ { 0, 2, "bir", "Table BIR", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "BAR 0", "BAR 1", "BAR 2", "BAR 3",
+ "BAR 4", "BAR 5" } } },
+ { 3, 31, "offset", "Table Offset", PRDV_HEX,
+ .prd_val = { .prdv_hex = { 3 } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_msixpba[] = {
+ { 0, 2, "bir", "PBA BIR", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "BAR 0", "BAR 1", "BAR 2", "BAR 3",
+ "BAR 4", "BAR 5" } } },
+ { 3, 31, "offset", "PBA Offset", PRDV_HEX,
+ .prd_val = { .prdv_hex = { 3 } } },
+ { -1, -1, NULL }
+};
+
+
+static pcieadm_cfgspace_print_t pcieadm_cap_msix[] = {
+ { PCI_MSIX_CTRL, 2, "ctrl", "Control Register",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_msixctrl },
+ { PCI_MSIX_TBL_OFFSET, 4, "table", "Table Offset",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_msixtable },
+ { PCI_MSIX_PBA_OFFSET, 4, "pba", "PBA Offset",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_msixpba },
+ { -1, -1, NULL }
+};
+
+/*
+ * PCI Express Capability
+ */
+static pcieadm_regdef_t pcieadm_regdef_pcie_cap[] = {
+ { 0, 3, "vers", "Version", PRDV_HEX },
+ { 4, 7, "type", "Device/Port Type", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "PCIe Endpoint",
+ "Legacy PCIe Endpoint", NULL, NULL,
+ "Root Port of PCIe Root Complex",
+ "Upstream Port of PCIe Switch",
+ "Downstream Port of PCIe Switch",
+ "PCIe to PCI/PCI-X Bridge",
+ "PCI/PCI-x to PCIe Bridge",
+ "RCiEP",
+ "Root Complex Event Collector" } } },
+ { 8, 8, "slot", "Slot Implemented", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "No", "Yes" } } },
+ { 9, 13, "intno", "Interrupt Message Number", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_pcie_devcap[] = {
+ { 0, 2, "mps", "Max Payload Size Supported", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "128 bytes", "256 bytes",
+ "512 bytes", "1024 byes", "2048 bytes", "4096 bytes" } } },
+ { 3, 4, "pfunc", "Phantom Functions Supported", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "No", "1-bit", "2-bits",
+ "3-bits" } } },
+ { 5, 5, "exttag", "Extended Tag Field", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "5-bit", "8-bit" } } },
+ { 6, 8, "l0slat", "L0s Acceptable Latency", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "64 ns", "128 ns", "256 ns",
+ "512 ns", "1 us", "2 us", "4 us", "No limit" } } },
+ { 9, 11, "l1lat", "L1 Acceptable Latency", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "1 us", "2 us", "4 us", "8 us",
+ "16 us", "32 us", "64 us", "No limit" } } },
+ { 15, 15, "rber", "Role Based Error Reporting", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 16, 16, "errcor", "ERR_COR Subclass", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 18, 25, "csplv", "Captured Slot Power Limit", PRDV_HEX },
+ { 26, 27, "cspls", "Captured Slot Power Limit Scale", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "1.0x", "0.1x", "0.01x",
+ "0.001x" } } },
+ { 28, 28, "flr", "Function Level Reset", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_pcie_devctl[] = {
+ { 0, 0, "corerr", "Correctable Error Reporting", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 1, 1, "nferr", "Non-Fatal Error Reporting", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 2, 2, "ferr", "Fatal Error Reporting", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 3, 3, "unsupreq", "Unsupported Request Reporting", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 4, 4, "relord", "Relaxed Ordering", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 5, 7, "mps", "Max Payload Size", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "128 bytes", "256 bytes",
+ "512 bytes", "1024 byes", "2048 bytes", "4096 bytes" } } },
+ { 8, 8, "exttag", "Extended Tag Field", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 9, 9, "pfunc", "Phantom Functions", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 9, 9, "auxpm", "Aux Power PM", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 11, 11, "nosnoop", "No Snoop", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 12, 14, "mrrs", "Max Read Request Size", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "128 bytes", "256 bytes",
+ "512 bytes", "1024 byes", "2048 bytes", "4096 bytes" } } },
+ { 15, 15, "bcrflr", "Bridge Configuration Retry / Function Level Reset",
+ PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_pcie_devsts[] = {
+ { 0, 0, "corerr", "Correctable Error Detected", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 1, 1, "nferr", "Non-Fatal Error Detected", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 2, 2, "ferr", "Fatal Error Detected", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 3, 3, "unsupreq", "Unsupported Request Detected", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 4, 4, "auxpm", "AUX Power Detected", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 5, 5, "txpend", "Transactions Pending", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 6, 6, "eprd", "Emergency Power Reduction Detected", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_pcie_linkcap[] = {
+ { 0, 3, "maxspeed", "Maximum Link Speed", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { NULL, "2.5 GT/s", "5.0 GT/s",
+ "8.0 GT/s", "16.0 GT/s", "32.0 GT/s" } } },
+ { 4, 9, "maxwidth", "Maximum Link Width", PRDV_HEX },
+ { 10, 11, "aspm", "ASPM Support", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "None", "L0s", "L1", "L0s/L1" } } },
+ { 12, 14, "l0slat", "L0s Exit Latency", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "<64ns", "64-128ns", "128-256ns",
+ "256-512ns", "512ns-1us", "1-2us", "2-4us", ">4us" } } },
+ { 15, 17, "l1lat", "L1 Exit Latency", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "<1us", "1-2us", "2-4us", "4-8us",
+ "8-16us", "16-32us" "32-64us", ">64us" } } },
+ { 18, 18, "clockpm", "Clock Power Management", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 19, 19, "supdown", "Surprise Down Error Reporting", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 20, 20, "dlact", "Data Link Layer Active Reporting", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 21, 21, "linkbw", "Link Bandwidth Notification Capability",
+ PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 22, 22, "aspmcomp", "ASPM Optionality Compliance", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "not compliant", "compliant" } } },
+ { 24, 31, "portno", "Port Number", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_pcie_linkctl[] = {
+ { 0, 1, "aspmctl", "ASPM Control", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "None", "L0s", "L1", "L0s/L1" } } },
+ { 3, 3, "rcb", "Read Completion Boundary", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "64 byte", "128 byte" } } },
+ { 4, 4, "disable", "Link Disable", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "not force disabled",
+ "force disabled" } } },
+ { 5, 5, "retrain", "Retrain Link", PRDV_HEX },
+ { 6, 6, "ccc", "Common Clock Configuration", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "asynchronous", "common" } } },
+ { 7, 7, "extsync", "Extended Sync", PRDV_HEX,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 8, 8, "clkpm", "Clock Power Management", PRDV_HEX,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 9, 9, "hwawd", "Hardware Autonomous Width", PRDV_HEX,
+ .prd_val = { .prdv_strval = { "enabled", "disabled" } } },
+ { 10, 10, "linkbwint", "Link Bandwidth Management Interrupt", PRDV_HEX,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 11, 11, "linkabwint", "Link Autonomous Bandwidth Interrupt", PRDV_HEX,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 14, 15, "drs", "DRS Signaling Control", PRDV_HEX,
+ .prd_val = { .prdv_strval = { "not reported", "Interrupt enabled",
+ "DRS->FRS enabled" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_pcie_linksts[] = {
+ { 0, 3, "speed", "Link Speed", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { NULL, "2.5 GT/s", "5.0 GT/s",
+ "8.0 GT/s", "16.0 GT/s", "32.0 GT/s" } } },
+ { 4, 9, "width", "Link Width", PRDV_HEX },
+ { 11, 11, "training", "Link Training", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 12, 12, "slotclk", "Slot Clock Configuration", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "asynchronous", "common" } } },
+ { 13, 13, "dllact", "Data Link Layer Link Active", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 14, 14, "linkbw", "Link Bandwidth Management Status", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no change", "change occurred" } } },
+ { 15, 15, "linkabw", "Link Autonomous Bandwidth Status", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no change", "change occurred" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_pcie_slotcap[] = {
+ { 0, 0, "attnbtn", "Attention Button Present", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 1, 1, "pwrctrl", "Power Controller Present", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 2, 2, "mrlsen", "MRL Sensor Present", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 3, 3, "attnind", "Attention Indicator Present", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 4, 4, "powind", "Power Indicator Present", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 5, 5, "hpsup", "Hot-Plug Surprise", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 6, 6, "hpcap", "Hot-Plug Capable ", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 7, 14, "slotplv", "Slot Power Limit Value", PRDV_HEX },
+ { 15, 16, "slotpls", "Slot Power Limit Scale", PRDV_HEX },
+ { 17, 17, "emi", "Electromechanical Interlock Present",
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 18, 18, "ncc", "No Command Completed", PRDV_HEX,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 19, 31, "slotno", "Physical Slot Number", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_pcie_slotctl[] = {
+ { 0, 0, "attnbtn", "Attention Button Pressed", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 1, 1, "powflt", "Power Fault Detected", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 2, 2, "mrlsen", "MRL Sensor Changed", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 3, 3, "presdet", "Presence Detect Changed", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 4, 4, "ccmpltint", "Command Complete Interrupt", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "Enabled" } } },
+ { 5, 5, "hpi", "Hot Plug Interrupt Enable", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 6, 7, "attnind", "Attention Indicator Control", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { NULL, "on", "blink", "off" } } },
+ { 8, 9, "powin", "Power Indicator Control", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { NULL, "on", "blink", "off" } } },
+ { 10, 10, "pwrctrl", "Power Controller Control", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "power on", "power off" } } },
+ { 11, 11, "emi", "Electromechanical Interlock Control", PRDV_HEX },
+ { 12, 12, "dll", "Data Link Layer State Changed", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 13, 13, "autopowdis", "Auto Slot Power Limit", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "enabled", "disabled" } } },
+ { 14, 14, "ibpddis", "In-Band PD", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "enabled", "disabled" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_pcie_slotsts[] = {
+ { 0, 0, "attnbtn", "Attention Button Pressed", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 1, 1, "powflt", "Power Fault Detected", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 2, 2, "mrlsen", "MRL Sensor Changed", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 3, 3, "presdet", "Presence Detect Changed", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 4, 4, "ccmplt", "Command Complete", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 5, 5, "mrlsen", "MRL Sensor State", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "closed", "open" } } },
+ { 6, 6, "presdet", "Presence Detect State", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "not present", "present" } } },
+ { 7, 7, "emi", "Electromechanical Interlock", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disengaged", "engaged" } } },
+ { 8, 8, "dll", "Data Link Layer State Changed", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_pcie_rootcap[] = {
+ { 0, 0, "syscorerr", "System Error on Correctable Error", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 1, 1, "sysnonftl", "System Error on Non-Fatal Error", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 2, 2, "sysfatal", "System Error on Fatal Error", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 3, 3, "pmeie", "PME Interrupt", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 4, 4, "crssw", "CRS Software Visibility", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_pcie_rootctl[] = {
+ { 0, 0, "crssw", "CRS Software Visibility", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_pcie_rootsts[] = {
+ { 0, 15, "pmereqid", "PME Requester ID", PRDV_HEX },
+ { 16, 16, "pmests", "PME Status", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "deasserted", "asserted" } } },
+ { 17, 17, "pmepend", "PME Pending", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_pcie_devcap2[] = {
+ { 0, 3, "cmpto", "Completion Timeout Ranges Supported", PRDV_BITFIELD,
+ .prd_val = { .prdv_strval = { "50us-10ms", "10ms-250ms",
+ "250ms-4s", "4s-64s" } } },
+ { 4, 4, "cmptodis", "Completion Timeout Disable", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 5, 5, "ari", "ARI Forwarding", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 6, 6, "atomroute", "AtomicOp Routing", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 7, 7, "atom32", "32-bit AtomicOp Completer", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 8, 8, "atom64", "64-bit AtomicOp Completer", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 9, 9, "cas128", "128-bit CAS Completer", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 10, 10, "norelord", "No Ro-enabld PR-PR Passing", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 11, 11, "ltr", "LTR Mechanism", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 12, 13, "tph", "TPH Completer", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "TPH supported",
+ NULL, "TPH and Extended TPH supported" } } },
+ { 14, 15, "lncls", "LN System CLS", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported",
+ "LN with 64-byte cachelines", "LN with 128-byte cachelines" } } },
+ { 16, 16, "tag10comp", "10-bit Tag Completer", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 17, 17, "tag10req", "10-bit Tag Requester", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 18, 19, "obff", "OBFF", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "Message Signaling",
+ "WAKE# Signaling", "WAKE# and Message Signaling" } } },
+ { 20, 20, "extfmt", "Extended Fmt Field Supported", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 21, 21, "eetlp", "End-End TLP Prefix Supported", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 22, 23, "maxeetlp", "Max End-End TLP Prefixes", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "4", "1", "2", "3" } } },
+ { 24, 25, "empr", "Emergency Power Reduction", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported",
+ "supported, device-specific",
+ "supported, from factor or device-specific" } } },
+ { 21, 21, "emprinit",
+ "Emergency Power Reduction Initialization Required", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 31, 31, "frs", "Function Readiness Status", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_pcie_devctl2[] = {
+ { 0, 3, "cmpto", "Completion Timeout", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "50us-50ms", "50us-100us",
+ "1ms-10ms", NULL, NULL, "16ms-55ms", "65ms-210ms", NULL, NULL,
+ "260ms-900ms", "1s-3.5s", NULL, NULL, "4s-13s", "17s-64s" } } },
+ { 4, 4, "cmptodis", "Completion Timeout Disabled", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "not disabled", "disabled" } } },
+ { 5, 5, "ari", "ARI Forwarding", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 6, 6, "atomreq", "AtomicOp Requester", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 7, 7, "atomblock", "AtomicOp Egress Blocking", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unblocked", "blocked" } } },
+ { 8, 8, "idoreq", "ID-Based Ordering Request", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 9, 9, "idocomp", "ID-Based Ordering Completion", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 10, 10, "ltr", "LTR Mechanism", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 11, 11, "empowred", "Emergency Power Reduction", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "not requested", "requested" } } },
+ { 12, 12, "tag10req", "10-bit Tag Requester", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 13, 14, "obff", "OBFF", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "message signaling - A",
+ "message signaling - B", "WAKE# signaling" } } },
+ { 15, 15, "eetlpblk", "End-End TLP Prefix Blocking", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unblocked", "blocked" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_pcie_devsts2[] = {
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_pcie_linkcap2[] = {
+ { 1, 7, "supspeeds", "Supported Link Speeds", PRDV_BITFIELD,
+ .prd_val = { .prdv_strval = { "2.5 GT/s", "5.0 GT/s", "8.0 GT/s",
+ "16.0 GT/s", "32.0 GT/s" } } },
+ { 8, 8, "crosslink", "Crosslink", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 9, 15, "skposgen", "Lower SKP OS Generation Supported Speeds Vector",
+ PRDV_BITFIELD,
+ .prd_val = { .prdv_strval = { "2.5 GT/s", "5.0 GT/s", "8.0 GT/s",
+ "16.0 GT/s", "32.0 GT/s" } } },
+ { 16, 22, "skposrecv", "Lower SKP OS Reception Supported Speeds Vector",
+ PRDV_BITFIELD,
+ .prd_val = { .prdv_strval = { "2.5 GT/s", "5.0 GT/s", "8.0 GT/s",
+ "16.0 GT/s", "32.0 GT/s" } } },
+ { 23, 23, "retimedet", "Retimer Presence Detect Supported", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 24, 24, "retime2det", "Two Retimers Presence Detect Supported",
+ PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 31, 31, "drs", "Device Readiness Status", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_pcie_linkctl2[] = {
+ { 0, 3, "targspeed", "Target Link Speed", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { NULL, "2.5 GT/s", "5.0 GT/s",
+ "8.0 GT/s", "16.0 GT/s", "32.0 GT/s" } } },
+ { 4, 4, "comp", "Enter Compliance", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 5, 5, "hwautosp", "Hardware Autonomous Speed Disable", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "not disabled", "disabled" } } },
+ { 6, 6, "seldeemph", "Selectable De-emphasis", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "-6 dB", "-3.5 dB" } } },
+ { 7, 9, "txmarg", "TX Margin", PRDV_HEX },
+ { 10, 10, "modcomp", "Enter Modified Compliance", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 11, 11, "compsos", "Compliance SOS",
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 12, 15, "compemph", "Compliance Preset/De-emphasis", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_pcie_linksts2[] = {
+ { 0, 0, "curdeemph", "Current De-emphasis Level", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "-6 dB", "-3.5 dB" } } },
+ { 1, 1, "eq8comp", "Equalization 8.0 GT/s Complete", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 2, 2, "eq8p1comp", "Equalization 8.0 GT/s Phase 1", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsuccessful", "successful" } } },
+ { 3, 3, "eq8p2comp", "Equalization 8.0 GT/s Phase 2", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsuccessful", "successful" } } },
+ { 4, 4, "eq8p3comp", "Equalization 8.0 GT/s Phase 3", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsuccessful", "successful" } } },
+ { 5, 5, "linkeq8req", "Link Equalization Request 8.0 GT/s", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "not requested", "requested" } } },
+ { 6, 6, "retimedet", "Retimer Presence Detected", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 7, 7, "retime2det", "Two Retimers Presence Detected", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 8, 9, "crosslink", "Crosslink Resolution", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "upstream port",
+ "downstream port", "incomplete" } } },
+ { 12, 14, "dscomppres", "Downstream Component Presence", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "link down - undetermined",
+ "link down - not present", "link down - present", NULL,
+ "link up - present", "link up - present and DRS" } } },
+ { 15, 15, "drsrx", "DRS Message Received", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_pcie_slotcap2[] = {
+ { 0, 0, "ibpddis", "In-Band PD Disable", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_pcie_slotctl2[] = {
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_pcie_slotsts2[] = {
+ { -1, -1, NULL }
+};
+
+
+static pcieadm_cfgspace_print_t pcieadm_cap_pcie_v1[] = {
+ { PCIE_PCIECAP, 2, "cap", "Capability Register",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_cap },
+ { PCIE_DEVCAP, 4, "devcap", "Device Capabilities",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_devcap },
+ { PCIE_DEVSTS, 2, "devsts", "Device Status",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_devsts },
+ { PCIE_LINKCAP, 4, "linkcap", "Link Capabilities",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_linkcap },
+ { PCIE_LINKCTL, 2, "linkctl", "Link Control",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_linkctl },
+ { PCIE_LINKSTS, 2, "linksts", "Link Status",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_linksts },
+ { PCIE_SLOTCAP, 4, "slotcap", "Slot Capabilities",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_slotcap },
+ { PCIE_SLOTCTL, 2, "slotctl", "Slot Control",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_slotctl },
+ { PCIE_SLOTSTS, 2, "slotsts", "Slot Status",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_slotsts },
+ { PCIE_ROOTCTL, 2, "rootctl", "Root control",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_rootctl },
+ { PCIE_ROOTCAP, 2, "rootcap", "Root Capabilities",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_rootcap },
+ { PCIE_ROOTSTS, 4, "rootsts", "Root Status",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_rootsts },
+ { -1, -1, NULL }
+};
+
+static pcieadm_cfgspace_print_t pcieadm_cap_pcie_v2[] = {
+ { PCIE_PCIECAP, 2, "cap", "Capability Register",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_cap },
+ { PCIE_DEVCAP, 4, "devcap", "Device Capabilities",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_devcap },
+ { PCIE_DEVCTL, 2, "devctl", "Device Control",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_devctl },
+ { PCIE_DEVSTS, 2, "devsts", "Device Status",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_devsts },
+ { PCIE_LINKCAP, 4, "linkcap", "Link Capabilities",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_linkcap },
+ { PCIE_LINKCTL, 2, "linkctl", "Link Control",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_linkctl },
+ { PCIE_LINKSTS, 2, "linksts", "Link Status",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_linksts },
+ { PCIE_SLOTCAP, 4, "slotcap", "Slot Capabilities",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_slotcap },
+ { PCIE_SLOTCTL, 2, "slotctl", "Slot Control",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_slotctl },
+ { PCIE_SLOTSTS, 2, "slotsts", "Slot Status",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_slotsts },
+ { PCIE_ROOTCTL, 2, "rootctl", "Root Control",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_rootctl },
+ { PCIE_ROOTCAP, 2, "rootcap", "Root Capabilities",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_rootcap },
+ { PCIE_ROOTSTS, 4, "rootsts", "Root Status",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_rootsts },
+ { PCIE_DEVCAP2, 4, "devcap2", "Device Capabilities 2",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_devcap2 },
+ { PCIE_DEVCTL2, 2, "devctl2", "Device Control 2",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_devctl2 },
+ { PCIE_DEVSTS2, 2, "devsts2", "Device Status 2",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_devsts2 },
+ { PCIE_LINKCAP2, 4, "linkcap2", "Link Capabilities 2",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_linkcap2 },
+ { PCIE_LINKCTL2, 2, "linkctl2", "Link Control 2",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_linkctl2 },
+ { PCIE_LINKSTS2, 2, "linksts2", "Link Status 2",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_linksts2 },
+ { PCIE_SLOTCAP2, 4, "slotcap2", "Slot Capabilities 2",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_slotcap2 },
+ { PCIE_SLOTCTL2, 2, "slotctl2", "Slot Control 2",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_slotctl2 },
+ { PCIE_SLOTSTS2, 2, "slotsts2", "Slot Status 2",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_slotsts2 },
+ { -1, -1, NULL }
+};
+
+/*
+ * PCIe Extended Capability Header
+ */
+static pcieadm_regdef_t pcieadm_regdef_pcie_caphdr[] = {
+ { 0, 15, "capid", "Capability ID", PRDV_HEX },
+ { 16, 19, "version", "Capability Version", PRDV_HEX },
+ { 20, 32, "offset", "Next Capability Offset", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+/*
+ * VPD Capability
+ */
+static pcieadm_regdef_t pcieadm_regdef_vpd_addr[] = {
+ { 0, 14, "addr", "VPD Address", PRDV_HEX },
+ { 15, 15, "flag", "Flag", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_cfgspace_print_t pcieadm_cap_vpd[] = {
+ { 0x2, 2, "addr", "VPD Address Register",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_vpd_addr },
+ { 0x4, 4, "data", "VPD Data", pcieadm_cfgspace_print_hex },
+ { -1, -1, NULL }
+};
+
+/*
+ * SATA Capability per AHCI 1.3.1
+ */
+static pcieadm_regdef_t pcieadm_regdef_sata_cr0[] = {
+ { 0, 3, "minrev", "Minor Revision", PRDV_HEX },
+ { 4, 7, "majrev", "Major Revision", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_sata_cr1[] = {
+ { 0, 3, "bar", "BAR Location", PRDV_HEX,
+ .prd_val = { .prdv_hex = { 2 } } },
+ { 4, 23, "offset", "BAR Offset", PRDV_HEX,
+ .prd_val = { .prdv_hex = { 2 } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_cfgspace_print_t pcieadm_cap_sata[] = {
+ { 0x2, 2, "satacr0", "SATA Capability Register 0",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_sata_cr0 },
+ { 0x4, 4, "satacr1", "SATA Capability Register 1",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_sata_cr1 },
+ { -1, -1, NULL }
+};
+
+/*
+ * Debug Capability per EHCI
+ */
+static pcieadm_regdef_t pcieadm_regdef_debug[] = {
+ { 0, 12, "offset", "BAR Offset", PRDV_HEX },
+ { 13, 15, "bar", "BAR Location ", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { NULL, "BAR 0", "BAR 1", "BAR 2",
+ "BAR 3", "BAR 4", "BAR 5" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_cfgspace_print_t pcieadm_cap_debug[] = {
+ { 0x2, 2, "port", "Debug Port",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_debug },
+ { -1, -1, NULL }
+};
+
+/*
+ * AER Capability
+ */
+static pcieadm_regdef_t pcieadm_regdef_aer_ue[] = {
+ { 4, 4, "dlp", "Data Link Protocol Error", PRDV_HEX },
+ { 5, 5, "sde", "Surprise Down Error", PRDV_HEX },
+ { 12, 12, "ptlp", "Poisoned TLP Received", PRDV_HEX },
+ { 13, 13, "fcp", "Flow Control Protocol Error", PRDV_HEX },
+ { 14, 14, "cto", "Completion Timeout", PRDV_HEX },
+ { 15, 15, "cab", "Completion Abort", PRDV_HEX },
+ { 16, 16, "unco", "Unexpected Completion", PRDV_HEX },
+ { 17, 17, "rxov", "Receiver Overflow", PRDV_HEX },
+ { 18, 18, "maltlp", "Malformed TLP", PRDV_HEX },
+ { 19, 19, "ecrc", "ECRC Error", PRDV_HEX },
+ { 20, 20, "usuprx", "Unsupported Request Error", PRDV_HEX },
+ { 21, 21, "acs", "ACS Violation", PRDV_HEX },
+ { 22, 22, "ueint", "Uncorrectable Internal Error", PRDV_HEX },
+ { 23, 23, "mcbtlp", "MC Blocked TLP", PRDV_HEX },
+ { 24, 24, "atoomeb", "AtomicOp Egress Blocked", PRDV_HEX },
+ { 25, 25, "tlppb", "TLP Prefix Blocked Error", PRDV_HEX },
+ { 26, 26, "ptlpeb", "Poisoned TLP Egress Blocked", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_aer_ce[] = {
+ { 0, 0, "rxerr", "Receiver Error", PRDV_HEX },
+ { 6, 6, "badtlp", "Bad TLP", PRDV_HEX },
+ { 7, 7, "baddllp", "Bad DLLP", PRDV_HEX },
+ { 8, 8, "replayro", "REPLAY_NUM Rollover", PRDV_HEX },
+ { 12, 12, "rtto", "Replay timer Timeout", PRDV_HEX },
+ { 13, 13, "advnfe", "Advisory Non-Fatal Error", PRDV_HEX },
+ { 14, 14, "ceint", "Correctable Internal Error", PRDV_HEX },
+ { 15, 15, "headlov", "Header Log Overflow", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_aer_ctrl[] = {
+ { 0, 4, "feptr", "First Error Pointer", PRDV_HEX },
+ { 5, 5, "ecgencap", "ECRC Generation Capable", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 6, 6, "ecgenen", "ECRC Generation Enable", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 7, 7, "ecchkcap", "ECRC Check Capable", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 8, 8, "ecchken", "ECRC Check Enable", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_aer_rootcom[] = {
+ { 0, 0, "corerr", "Correctable Error Reporting", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 1, 1, "nferr", "Non-Fatal Error Reporting", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 2, 2, "faterr", "Fatal Error Reporting", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_aer_rootsts[] = {
+ { 0, 0, "errcor", "ERR_COR Received", PRDV_HEX },
+ { 1, 1, "merrcor", "Multiple ERR_COR Received", PRDV_HEX },
+ { 2, 2, "errfnf", "ERR_FATAL/NONFATAL Received", PRDV_HEX },
+ { 3, 3, "merrfnf", "Multiple ERR_FATAL/NONFATAL Received", PRDV_HEX },
+ { 4, 4, "fuefat", "First Uncorrectable Fatal", PRDV_HEX },
+ { 5, 5, "nferrrx", "Non-Fatal Error Messages Received", PRDV_HEX },
+ { 6, 6, "faterrx", "Fatal Error Messages Received", PRDV_HEX },
+ { 7, 8, "errcorsc", "ERR_COR Subclass", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "ECS Legacy", "ECS SIG_SFW",
+ "ECS SIG_OS", "ECS Extended" } } },
+ { 27, 31, "inum", "Advanced Error Interrupt Message", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_aer_esi[] = {
+ { 0, 15, "errcorr", "ERR_COR Source", PRDV_HEX },
+ { 16, 31, "errfnf", "ERR_FATAL/NONFATAL Source", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_aer_secue[] = {
+ { 0, 0, "taosc", "Target-Abort on Split Completion", PRDV_HEX },
+ { 1, 1, "maosc", "Master-Abort on Split Completion", PRDV_HEX },
+ { 2, 2, "rxta", "Received Target-Abort", PRDV_HEX },
+ { 3, 3, "rxma", "Received Master-Abort", PRDV_HEX },
+ { 5, 5, "unsce", "Unexpected Split Completion Error", PRDV_HEX },
+ { 6, 6, "uescmd", "Uncorrectable Split Completion Message Data Error",
+ PRDV_HEX },
+ { 7, 7, "uede", "Uncorrectable Data Error", PRDV_HEX },
+ { 8, 8, "ueattre", "Uncorrectable Attribute Error", PRDV_HEX },
+ { 9, 9, "ueaddre", "Uncorrectable Address Error", PRDV_HEX },
+ { 10, 10, "dtdte", "Delayed Transaction Discard Timer Expired",
+ PRDV_HEX },
+ { 11, 11, "perr", "PERR# Assertion", PRDV_HEX },
+ { 12, 12, "serr", "SERR# Assertion", PRDV_HEX },
+ { 13, 13, "internal", "Internal Bridge Error", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_aer_secctl[] = {
+ { 0, 4, "feptr", "Secondary Uncorrectable First Error Pointer",
+ PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_cfgspace_print_t pcieadm_cap_aer_v1[] = {
+ { PCIE_AER_CAP, 4, "caphdr", "Capability Header",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr },
+ { PCIE_AER_UCE_STS, 4, "uestatus", "Uncorrectable Error Status",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ue },
+ { PCIE_AER_UCE_MASK, 4, "uemask", "Uncorrectable Error Mask",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ue },
+ { PCIE_AER_UCE_SERV, 4, "ueserv", "Uncorrectable Error Severity",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ue },
+ { PCIE_AER_CE_STS, 4, "cestatus", "Correctable Error Status",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ce },
+ { PCIE_AER_CE_MASK, 4, "cemask", "Correctable Error Mask",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ce },
+ { PCIE_AER_CTL, 4, "ctrl", "Advanced Error Capabilities and Control",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ctrl },
+ { PCIE_AER_HDR_LOG + 4, 4, "hl0", "Header Log 0",
+ pcieadm_cfgspace_print_hex },
+ { PCIE_AER_HDR_LOG + 8, 4, "hl1", "Header Log 1",
+ pcieadm_cfgspace_print_hex },
+ { PCIE_AER_HDR_LOG + 12, 4, "hl2", "Header Log 2",
+ pcieadm_cfgspace_print_hex },
+ { PCIE_AER_HDR_LOG + 12, 4, "hl3", "Header Log 3",
+ pcieadm_cfgspace_print_hex },
+ { PCIE_AER_CTL, 4, "rootcmd", "Root Error Command",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_rootcom },
+ { PCIE_AER_RE_STS, 4, "rootsts", "Root Error Status",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_rootsts },
+ { PCIE_AER_CE_SRC_ID, 4, "esi", "Error Source Identification",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_esi },
+ { -1, -1, NULL }
+};
+
+static pcieadm_cfgspace_print_t pcieadm_cap_aer_v2[] = {
+ { PCIE_AER_CAP, 4, "caphdr", "Capability Header",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr },
+ { PCIE_AER_UCE_STS, 4, "uestatus", "Uncorrectable Error Status",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ue },
+ { PCIE_AER_UCE_MASK, 4, "uemask", "Uncorrectable Error Mask",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ue },
+ { PCIE_AER_UCE_SERV, 4, "ueserv", "Uncorrectable Error Severity",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ue },
+ { PCIE_AER_CE_STS, 4, "cestatus", "Correctable Error Status",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ce },
+ { PCIE_AER_CE_MASK, 4, "cemask", "Correctable Error Mask",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ce },
+ { PCIE_AER_CTL, 4, "ctrl", "Advanced Error Capabilities and Control",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ctrl },
+ { PCIE_AER_HDR_LOG + 4, 4, "hl0", "Header Log 0",
+ pcieadm_cfgspace_print_hex },
+ { PCIE_AER_HDR_LOG + 8, 4, "hl1", "Header Log 1",
+ pcieadm_cfgspace_print_hex },
+ { PCIE_AER_HDR_LOG + 12, 4, "hl2", "Header Log 2",
+ pcieadm_cfgspace_print_hex },
+ { PCIE_AER_HDR_LOG + 12, 4, "hl3", "Header Log 3",
+ pcieadm_cfgspace_print_hex },
+ { PCIE_AER_CTL, 4, "rootcmd", "Root Error Command",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_rootcom },
+ { PCIE_AER_RE_STS, 4, "rootsts", "Root Error Status",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_rootsts },
+ { PCIE_AER_TLP_PRE_LOG, 4, "tlplog0", "TLP Prefix Log 0",
+ pcieadm_cfgspace_print_hex },
+ { PCIE_AER_TLP_PRE_LOG + 4, 4, "tlplog1", "TLP Prefix Log 1",
+ pcieadm_cfgspace_print_hex },
+ { PCIE_AER_TLP_PRE_LOG + 8, 4, "tlplog2", "TLP Prefix Log 2",
+ pcieadm_cfgspace_print_hex },
+ { PCIE_AER_TLP_PRE_LOG + 12, 4, "tlplog3", "TLP Prefix Log 3",
+ pcieadm_cfgspace_print_hex },
+ { -1, -1, NULL }
+};
+
+static pcieadm_cfgspace_print_t pcieadm_cap_aer_bridge[] = {
+ { PCIE_AER_CAP, 4, "caphdr", "Capability Header",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr },
+ { PCIE_AER_UCE_STS, 4, "uestatus", "Uncorrectable Error Status",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ue },
+ { PCIE_AER_UCE_MASK, 4, "uemask", "Uncorrectable Error Mask",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ue },
+ { PCIE_AER_UCE_SERV, 4, "ueserv", "Uncorrectable Error Severity",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ue },
+ { PCIE_AER_CE_STS, 4, "cestatus", "Correctable Error Status",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ce },
+ { PCIE_AER_CE_MASK, 4, "cemask", "Correctable Error Mask",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ce },
+ { PCIE_AER_CTL, 4, "ctrl", "Advanced Error Capabilities and Control",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_ctrl },
+ { PCIE_AER_HDR_LOG + 4, 4, "hl0", "Header Log 0",
+ pcieadm_cfgspace_print_hex },
+ { PCIE_AER_HDR_LOG + 8, 4, "hl1", "Header Log 1",
+ pcieadm_cfgspace_print_hex },
+ { PCIE_AER_HDR_LOG + 12, 4, "hl2", "Header Log 2",
+ pcieadm_cfgspace_print_hex },
+ { PCIE_AER_HDR_LOG + 12, 4, "hl3", "Header Log 3",
+ pcieadm_cfgspace_print_hex },
+ { PCIE_AER_CTL, 4, "rootcmd", "Root Error Command",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_rootcom },
+ { PCIE_AER_RE_STS, 4, "rootsts", "Root Error Status",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_rootsts },
+ { PCIE_AER_CE_SRC_ID, 4, "esi", "Error Source Identification",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_esi },
+ { PCIE_AER_SUCE_STS, 4, "secuests",
+ "Secondary Uncorrectable Error Status",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_secue },
+ { PCIE_AER_SUCE_MASK, 4, "secuests",
+ "Secondary Uncorrectable Error Mask",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_secue },
+ { PCIE_AER_SUCE_SERV, 4, "secuests",
+ "Secondary Uncorrectable Error Severity",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_secue },
+ { PCIE_AER_SCTL, 4, "secctrl",
+ "Secondary Error Capabilityes and Control",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_aer_secctl },
+ { PCIE_AER_SHDR_LOG, 4, "shl0", "Secondary Header Log 0",
+ pcieadm_cfgspace_print_hex },
+ { PCIE_AER_SHDR_LOG + 4, 4, "shl1", "Secondary Header Log 1",
+ pcieadm_cfgspace_print_hex },
+ { PCIE_AER_SHDR_LOG + 8, 4, "shl1", "Secondary Header Log 2",
+ pcieadm_cfgspace_print_hex },
+ { PCIE_AER_SHDR_LOG + 12, 4, "shl1", "Secondary Header Log 3",
+ pcieadm_cfgspace_print_hex },
+ { -1, -1, NULL }
+};
+
+/*
+ * Secondary PCI Express Extended Capability
+ */
+static pcieadm_regdef_t pcieadm_regdef_pcie2_linkctl3[] = {
+ { 0, 0, "peq", "Perform Equalization", PRDV_HEX },
+ { 1, 1, "leqrie", "Link Equalization Request Interrupt Enable",
+ PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 9, 15, "elskpos", "Enable Lower SKP OS Generation Vector",
+ PRDV_BITFIELD,
+ .prd_val = { .prdv_strval = { "2.5 GT/s", "5.0 GT/s", "8.0 GT/s",
+ "16.0 GT/s", "32.0 GT/s" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_pcie2_linkeq[] = {
+ { 0, 3, "dstxpre", "Downstream Port 8.0 GT/s Transmitter Preset",
+ PRDV_HEX },
+ { 4, 6, "dstxhint", "Downstream Port 8.0 GT/s Receiver Hint",
+ PRDV_HEX },
+ { 8, 11, "ustxpre", "Upstream Port 8.0 GT/s Transmitter Preset",
+ PRDV_HEX },
+ { 12, 14, "ustxhint", "Upstream Port 8.0 GT/s Receiver Hint",
+ PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static void
+pcieadm_cfgspace_print_laneq(pcieadm_cfgspace_walk_t *walkp,
+ pcieadm_cfgspace_print_t *print, void *arg)
+{
+ if (walkp->pcw_nlanes == 0) {
+ warnx("failed to capture lane count, but somehow have "
+ "secondary PCIe cap");
+ return;
+ }
+
+ for (uint_t i = 0; i < walkp->pcw_nlanes; i++) {
+ char eqshort[32], eqhuman[128];
+ pcieadm_cfgspace_print_t p;
+
+ (void) snprintf(eqshort, sizeof (eqshort), "lane%u", i);
+ (void) snprintf(eqhuman, sizeof (eqhuman), "Lane %u EQ Control",
+ i);
+ p.pcp_off = print->pcp_off + i * 2;
+ p.pcp_len = 2;
+ p.pcp_short = eqshort;
+ p.pcp_human = eqhuman;
+ p.pcp_print = pcieadm_cfgspace_print_regdef;
+ p.pcp_arg = pcieadm_regdef_pcie2_linkeq;
+
+ p.pcp_print(walkp, &p, p.pcp_arg);
+ }
+}
+
+static pcieadm_cfgspace_print_t pcieadm_cap_pcie2[] = {
+ { 0x0, 4, "caphdr", "Capability Header",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr },
+ { 0x4, 4, "linkctl3", "Link Control 3",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie2_linkctl3 },
+ { 0x8, 4, "laneerr", "Lane Error Status", pcieadm_cfgspace_print_hex },
+ { 0xc, 2, "eqctl", "Lane Equalization Control",
+ pcieadm_cfgspace_print_laneq },
+ { -1, -1, NULL }
+};
+
+/*
+ * Access Control Services
+ */
+static pcieadm_regdef_t pcieadm_regdef_acs_cap[] = {
+ { 0, 0, "srcvd", "ACS Source Validation", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 1, 1, "tranblk", "ACS Transaction Blocking", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 2, 2, "p2prr", "ACS P2P Request Redirect", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 3, 3, "p2pcr", "ACS P2P Completion Redirect", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 4, 4, "upfwd", "ACS Upstream Forwarding", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 5, 5, "p2pegctl", "ACS P2P Egress Control", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 6, 6, "dtp2p", "ACS Direct Translated P2P", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 7, 7, "enhcap", "ACS Enhanced Capability", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 8, 15, "ecvsz", "Egress Control Vector Size", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_acs_ctl[] = {
+ { 0, 0, "srcvd", "ACS Source Validation", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 1, 1, "tranblk", "ACS Transaction Blocking", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 2, 2, "p2prr", "ACS P2P Request Redirect", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 3, 3, "p2pcr", "ACS P2P Completion Redirect", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 4, 4, "upfwd", "ACS Upstream Forwarding", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 5, 5, "p2pegctl", "ACS P2P Egress Control", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 6, 6, "dtp2p", "ACS Direct Translated P2P", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 7, 7, "iorb", "ACS I/O Request Blocking", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 8, 9, "dspmta", "ACS DSP Memory Target Access Control", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "Direct Request access",
+ "Request blocking", "Request redirect" } } },
+ { 10, 11, "uspmta", "ACS USP Memory Target Access Control", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "Direct Request access",
+ "Request blocking", "Request redirect" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_cfgspace_print_t pcieadm_cap_acs[] = {
+ { 0x0, 4, "caphdr", "Capability Header",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr },
+ { 0x4, 2, "cap", "ACS Capability",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_acs_cap },
+ { 0x6, 2, "ctl", "ACS Control",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_acs_ctl },
+ { 0x8, 4, "ecv", "Egress Control Vector", pcieadm_cfgspace_print_ecv },
+ { -1, -1, NULL }
+};
+
+/*
+ * L1 PM Substates
+ */
+static pcieadm_regdef_t pcieadm_regdef_l1pm_cap[] = {
+ { 0, 0, "pcil1.2", "PCI-PM L1.2", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 1, 1, "pcil1.1", "PCI-PM L1.1", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 2, 2, "aspml1.2", "ASPM L1.2", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 3, 3, "aspml1.1", "ASPM L1.1", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 4, 4, "l1pmsub", "L1 PM Substates", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 5, 5, "linkact", "Link Activation", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 8, 15, "pcmrt", "Port Common_Mode_Restore_Time", PRDV_HEX },
+ { 16, 17, "poscale", "Port T_POWER_ON Scale", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "2 us", "10 us", "100 us" } } },
+ { 19, 23, "portpo", "Port T_POWER_ON Value", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_l1pm_ctl1[] = {
+ { 0, 0, "pcil1.2", "PCI-PM L1.2", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 1, 1, "pcil1.1", "PCI-PM L1.1", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 2, 2, "aspml1.2", "ASPM L1.2", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 3, 3, "aspml1.1", "ASPM L1.1", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 4, 4, "laie", "Link Activation Interrupt Enable", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 5, 5, "lactl", "Link Activation Control", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 8, 15, "cmrt", "Common_Mode_Restore_Time", PRDV_HEX },
+ { 16, 25, "ltrl1.2", "LTR L1.2 Threshold Value", PRDV_HEX },
+ { 29, 31, "ltrl1.2s", "LTR L1.2 Threshold Scale", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "1 ns", "32 ns", "1024 ns",
+ "32,768 ns", "1,048,576 ns", "33,554,432 ns" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_l1pm_ctl2[] = {
+ { 0, 1, "poscale", "T_POWER_ON Scale", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "2 us", "10 us", "100 us" } } },
+ { 3, 7, "portpo", "T_POWER_ON Value", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_l1pm_sts[] = {
+ { 0, 0, "la", "Link Activation", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+
+static pcieadm_cfgspace_print_t pcieadm_cap_l1pm_v1[] = {
+ { 0x0, 4, "caphdr", "Capability Header",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr },
+ { 0x4, 4, "caps", "L1 PM Substates Capabilities",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_l1pm_cap },
+ { 0x8, 4, "ctl1", "L1 PM Substates Control 1",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_l1pm_ctl1 },
+ { 0xc, 4, "ctl1", "L1 PM Substates Control 2",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_l1pm_ctl2 },
+ { -1, -1, NULL }
+};
+
+static pcieadm_cfgspace_print_t pcieadm_cap_l1pm_v2[] = {
+ { 0x0, 4, "caphdr", "Capability Header",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr },
+ { 0x4, 4, "caps", "L1 PM Substates Capabilities",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_l1pm_cap },
+ { 0x8, 4, "ctl1", "L1 PM Substates Control 1",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_l1pm_ctl1 },
+ { 0xc, 4, "ctl1", "L1 PM Substates Control 2",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_l1pm_ctl2 },
+ { 0x10, 4, "sts", "L1 PM Substates Status",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_l1pm_sts },
+ { -1, -1, NULL }
+};
+
+/*
+ * Latency Tolerance Reporting (LTR)
+ */
+static pcieadm_regdef_t pcieadm_regdef_ltr[] = {
+ { 0, 9, "latval", "Latency Value", PRDV_HEX },
+ { 10, 12, "latscale", "Latency Scale", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "1 ns", "32 ns", "1024 ns",
+ "32,768 ns", "1,048,576 ns", "33,554,432 ns" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_cfgspace_print_t pcieadm_cap_ltr[] = {
+ { 0x0, 4, "caphdr", "Capability Header",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr },
+ { 0x4, 2, "snoop", "Max Snoop Latency",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ltr },
+ { 0x6, 2, "snoop", "Max No-Snoop Latency",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ltr },
+ { -1, -1, NULL }
+};
+
+/*
+ * Alternative Routing ID
+ */
+static pcieadm_regdef_t pcieadm_regdef_ari_cap[] = {
+ { 0, 0, "mfvcfg", "MFVC Function Groups", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 1, 1, "acsfg", "ACS Function Groups", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 8, 15, "nfunc", "Next Function Number", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_ari_ctl[] = {
+ { 0, 0, "mfvcfg", "MFVC Function Groups", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 1, 1, "acsfg", "ACS Function Groups", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 4, 6, "fgrp", "Function Group", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_cfgspace_print_t pcieadm_cap_ari[] = {
+ { 0x0, 4, "caphdr", "Capability Header",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr },
+ { 0x4, 2, "cap", "ARI Capability",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ari_cap },
+ { 0x6, 2, "ctl", "ARI Control",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ari_ctl },
+ { -1, -1, NULL }
+};
+
+/*
+ * PASID
+ */
+static pcieadm_regdef_t pcieadm_regdef_pasid_cap[] = {
+ { 1, 1, "exec", "Execution Permission", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 2, 2, "priv", "Privileged Mode", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 8, 12, "width", "Max PASID Width", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_pasid_ctl[] = {
+ { 0, 0, "pasid", "PASID", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 1, 1, "exec", "Execution Permission", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 2, 2, "priv", "Privileged Mode", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { -1, -1, NULL }
+};
+
+
+static pcieadm_cfgspace_print_t pcieadm_cap_pasid[] = {
+ { 0x0, 4, "caphdr", "Capability Header",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr },
+ { 0x4, 2, "cap", "PASID Capability",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pasid_cap },
+ { 0x6, 2, "ctl", "PASID Control",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pasid_ctl },
+ { -1, -1, NULL }
+};
+
+/*
+ * "Advanced Features"
+ */
+static pcieadm_regdef_t pcieadm_regdef_af_cap[] = {
+ { 0, 0, "tp", "Transactions Pending", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 1, 1, "flr", "Function Level Reset", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_af_ctl[] = {
+ { 0, 0, "flr", "Function Level Reset", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_af_sts[] = {
+ { 0, 0, "tp", "Transactions Pending", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "none pending", "pending" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_cfgspace_print_t pcieadm_cap_af[] = {
+ { 0x2, 2, "cap", "AF Capabilities",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_af_cap },
+ { 0x4, 1, "ctl", "AF Control",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_af_ctl },
+ { 0x5, 1, "sts", "AF Status",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_af_sts },
+ { -1, -1, NULL }
+};
+
+/*
+ * Multicast
+ */
+static pcieadm_regdef_t pcieadm_regdef_mcast_cap[] = {
+ { 0, 5, "maxgrp", "Max Group", PRDV_HEX,
+ .prd_val = { .prdv_hex = { 0, 1 } } },
+ { 8, 13, "winsize", "Window Size (raw)", PRDV_HEX },
+ { 15, 15, "ecrc", "ECRC Regeneration", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_mcast_ctl[] = {
+ { 0, 5, "numgrp", "Number of Groups", PRDV_HEX,
+ .prd_val = { .prdv_hex = { 0, 1 } } },
+ { 15, 15, "enable", "Enable", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_mcast_base[] = {
+ { 0, 5, "index", "Multicast Index Position", PRDV_HEX },
+ { 12, 63, "addr", "Base Address", PRDV_HEX,
+ .prd_val = { .prdv_hex = { 12 } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_mcast_overlay[] = {
+ { 0, 5, "size", "Overlay Size (raw)", PRDV_HEX },
+ { 6, 63, "addr", "Overlay Base Address", PRDV_HEX,
+ .prd_val = { .prdv_hex = { 6 } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_cfgspace_print_t pcieadm_cap_mcast[] = {
+ { 0x0, 4, "caphdr", "Capability Header",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr },
+ { 0x4, 2, "cap", "Multicast Capability",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_mcast_cap },
+ { 0x6, 2, "ctl", "Multicast Control",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_mcast_ctl },
+ { 0x8, 8, "base", "Multicast Base Address",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_mcast_base },
+ { 0x10, 8, "rx", "Multicast Receive", pcieadm_cfgspace_print_hex },
+ { 0x18, 8, "block", "Multicast Block All", pcieadm_cfgspace_print_hex },
+ { 0x20, 8, "blockun", "Multicast Block Untranslated",
+ pcieadm_cfgspace_print_hex },
+ { 0x28, 8, "overlay", "Multicast Overlay BAR",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_mcast_overlay },
+ { -1, -1, NULL }
+};
+
+/*
+ * Various vendor extensions
+ */
+static pcieadm_regdef_t pcieadm_regdef_vsec[] = {
+ { 0, 15, "id", "ID", PRDV_HEX },
+ { 16, 19, "rev", "Revision", PRDV_HEX },
+ { 20, 31, "len", "Length", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_cfgspace_print_t pcieadm_cap_vs[] = {
+ { 0x2, 2, "length", "Length", pcieadm_cfgspace_print_hex },
+ { -1, -1, NULL }
+};
+
+static pcieadm_cfgspace_print_t pcieadm_cap_vsec[] = {
+ { 0x0, 4, "caphdr", "Capability Header",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr },
+ { 0x4, 4, "header", "Vendor-Specific Header",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_vsec },
+ { -1, -1, NULL }
+};
+
+/*
+ * Data Link Feature
+ */
+static pcieadm_regdef_t pcieadm_regdef_dlf_cap[] = {
+ { 0, 0, "lsfc", "Local Scaled Flow Control", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 31, 31, "dlex", "Data Link Exchange", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_dlf_sts[] = {
+ { 0, 0, "rsfc", "Remote Scaled Flow Control", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 31, 31, "valid", "Remote Data Link Feature Valid", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "invalid", "valid" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_cfgspace_print_t pcieadm_cap_dlf[] = {
+ { 0x0, 4, "caphdr", "Capability Header",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr },
+ { 0x4, 4, "cap", "Data Link Feature Capabilities",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_dlf_cap },
+ { 0x8, 4, "sts", "Data Link Feature Status",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_dlf_sts },
+ { -1, -1, NULL }
+};
+
+/*
+ * 16.0 GT/s cap
+ */
+static pcieadm_regdef_t pcieadm_regdef_16g_cap[] = {
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_16g_ctl[] = {
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_16g_sts[] = {
+ { 0, 0, "eqcomp", "Equalization 16.0 GT/s Complete", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "incomplete", "complete" } } },
+ { 1, 1, "eqp1", "Equalization 16.0 GT/s Phase 1", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "incomplete", "complete" } } },
+ { 2, 2, "eqp2", "Equalization 16.0 GT/s Phase 2", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "incomplete", "complete" } } },
+ { 3, 3, "eqp3", "Equalization 16.0 GT/s Phase 3", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "incomplete", "complete" } } },
+ { 4, 4, "req", "Link Equalization Request 16.0 GT/s", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_16g_eq[] = {
+ { 0, 3, "dstxpre", "Downstream Port 16.0 GT/s Transmitter Preset",
+ PRDV_HEX },
+ { 4, 7, "ustxpre", "Upstream Port 16.0 GT/s Transmitter Preset",
+ PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static void
+pcieadm_cfgspace_print_16geq(pcieadm_cfgspace_walk_t *walkp,
+ pcieadm_cfgspace_print_t *print, void *arg)
+{
+ if (walkp->pcw_nlanes == 0) {
+ warnx("failed to capture lane count, but somehow have "
+ "secondary PCIe cap");
+ return;
+ }
+
+ for (uint_t i = 0; i < walkp->pcw_nlanes; i++) {
+ char eqshort[32], eqhuman[128];
+ pcieadm_cfgspace_print_t p;
+
+ (void) snprintf(eqshort, sizeof (eqshort), "lane%u", i);
+ (void) snprintf(eqhuman, sizeof (eqhuman), "Lane %u EQ Control",
+ i);
+ p.pcp_off = print->pcp_off + i * 1;
+ p.pcp_len = 1;
+ p.pcp_short = eqshort;
+ p.pcp_human = eqhuman;
+ p.pcp_print = pcieadm_cfgspace_print_regdef;
+ p.pcp_arg = pcieadm_regdef_16g_eq;
+
+ p.pcp_print(walkp, &p, p.pcp_arg);
+ }
+}
+
+static pcieadm_cfgspace_print_t pcieadm_cap_16g[] = {
+ { 0x0, 4, "caphdr", "Capability Header",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr },
+ { 0x4, 4, "cap", "16.0 GT/s Capabilities",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_16g_cap },
+ { 0x8, 4, "ctl", "16.0 GT/s Control",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_16g_ctl },
+ { 0xc, 4, "sts", "16.0 GT/s Status",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_16g_sts },
+ { 0x10, 4, "ldpmis", "16.0 GT/s Local Data Parity Mismatch",
+ pcieadm_cfgspace_print_hex },
+ { 0x14, 4, "frpmis", "16.0 GT/s First Retimer Data Parity Mismatch",
+ pcieadm_cfgspace_print_hex },
+ { 0x18, 4, "srpmis", "16.0 GT/s Second Retimer Data Parity Mismatch",
+ pcieadm_cfgspace_print_hex },
+ { 0x1c, 4, "rsvd", "16.0 GT/s Second Retimer Data Parity Mismatch",
+ pcieadm_cfgspace_print_hex },
+ { 0x20, 1, "eqctl", "16.0 GT/s EQ Control",
+ pcieadm_cfgspace_print_16geq },
+ { -1, -1, NULL }
+};
+
+/*
+ * Receiver Margining
+ */
+static pcieadm_regdef_t pcieadm_regdef_margin_cap[] = {
+ { 0, 0, "sw", "Margining uses Driver Software", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_margin_sts[] = {
+ { 0, 0, "ready", "Margining Ready", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 1, 1, "sw", "Margining Software Ready", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_margin_lane[] = {
+ { 0, 2, "rxno", "Receiver Number", PRDV_HEX },
+ { 3, 5, "type", "Margin Type", PRDV_HEX },
+ { 6, 6, "model", "Usage Model", PRDV_HEX },
+ { 8, 15, "payload", "Margin Payload", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static void
+pcieadm_cfgspace_print_margin(pcieadm_cfgspace_walk_t *walkp,
+ pcieadm_cfgspace_print_t *print, void *arg)
+{
+ if (walkp->pcw_nlanes == 0) {
+ warnx("failed to capture lane count, but somehow have "
+ "lane margining capability");
+ return;
+ }
+
+ for (uint_t i = 0; i < walkp->pcw_nlanes; i++) {
+ char mshort[32], mhuman[128];
+ pcieadm_cfgspace_print_t p;
+
+ (void) snprintf(mshort, sizeof (mshort), "lane%uctl", i);
+ (void) snprintf(mhuman, sizeof (mhuman), "Lane %u Margining "
+ "Control", i);
+ p.pcp_off = print->pcp_off + i * 4;
+ p.pcp_len = 2;
+ p.pcp_short = mshort;
+ p.pcp_human = mhuman;
+ p.pcp_print = pcieadm_cfgspace_print_regdef;
+ p.pcp_arg = pcieadm_regdef_margin_lane;
+
+ p.pcp_print(walkp, &p, p.pcp_arg);
+
+ (void) snprintf(mshort, sizeof (mshort), "lane%usts", i);
+ (void) snprintf(mhuman, sizeof (mhuman), "Lane %u Margining "
+ "Status", i);
+ p.pcp_off = print->pcp_off + 2 + i * 4;
+ p.pcp_len = 2;
+ p.pcp_short = mshort;
+ p.pcp_human = mhuman;
+ p.pcp_print = pcieadm_cfgspace_print_regdef;
+ p.pcp_arg = pcieadm_regdef_margin_lane;
+
+ p.pcp_print(walkp, &p, p.pcp_arg);
+ }
+}
+
+static pcieadm_cfgspace_print_t pcieadm_cap_margin[] = {
+ { 0x0, 4, "caphdr", "Capability Header",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr },
+ { 0x4, 2, "cap", "Margining Port Capabilities",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_margin_cap },
+ { 0x6, 2, "sts", "Margining Port Status",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_margin_sts },
+ { 0x8, 4, "lane", "Margining Lane", pcieadm_cfgspace_print_margin },
+ { -1, -1, NULL }
+};
+
+/*
+ * Serial Number Capability
+ */
+static void
+pcieadm_cfgspace_print_sn(pcieadm_cfgspace_walk_t *walkp,
+ pcieadm_cfgspace_print_t *print, void *arg)
+{
+ char sn[64];
+ uint16_t off = walkp->pcw_capoff + print->pcp_off;
+
+ (void) snprintf(sn, sizeof (sn),
+ "%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x",
+ walkp->pcw_data->pcb_u8[off + 7], walkp->pcw_data->pcb_u8[off + 6],
+ walkp->pcw_data->pcb_u8[off + 5], walkp->pcw_data->pcb_u8[off + 4],
+ walkp->pcw_data->pcb_u8[off + 3], walkp->pcw_data->pcb_u8[off + 2],
+ walkp->pcw_data->pcb_u8[off + 1], walkp->pcw_data->pcb_u8[off]);
+
+ pcieadm_cfgspace_puts(walkp, print, sn);
+}
+
+static pcieadm_cfgspace_print_t pcieadm_cap_sn[] = {
+ { 0x0, 4, "caphdr", "Capability Header",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr },
+ { 0x4, 8, "sn", "Serial Number", pcieadm_cfgspace_print_sn },
+ { -1, -1, NULL }
+};
+
+/*
+ * TLP Processing Hints (TPH)
+ */
+static pcieadm_regdef_t pcieadm_regdef_tph_cap[] = {
+ { 0, 0, "nost", "No ST Mode", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 1, 1, "ivec", "Interrupt Vector Mode", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 2, 2, "dev", "Device Specific Mode", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 8, 8, "exttph", "Extended TPH Requester", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 9, 10, "loc", "ST Table Location", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "Not Present",
+ "In Capability Structure", "MSI-X" } } },
+ { 16, 26, "size", "ST Table Size", PRDV_HEX, { .prdv_hex = { 0, 1 } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_tph_ctl[] = {
+ { 0, 2, "mode", "ST Mode Select", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "No ST", "Interrupt Vector",
+ "Device Specific" } } },
+ { 8, 9, "en", "TPH Requester", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "Not Permitted", "TPH", NULL,
+ "TPH and Extended TPH" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_tph_st[] = {
+ { 0, 7, "low", "ST Lower", PRDV_HEX },
+ { 8, 15, "up", "ST Upper", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+/*
+ * The TPH ST table is only conditionally present in the capability. So we need
+ * to read the TPH capability register and then check if the table location and
+ * size are set here.
+ */
+static void
+pcieadm_cfgspace_print_tphst(pcieadm_cfgspace_walk_t *walkp,
+ pcieadm_cfgspace_print_t *print, void *arg)
+{
+ uint_t nents;
+ uint32_t tphcap = walkp->pcw_data->pcb_u32[(walkp->pcw_capoff + 4) / 4];
+
+ if (BITX(tphcap, 10, 9) != 1) {
+ return;
+ }
+
+ nents = BITX(tphcap, 26, 16) + 1;
+ for (uint_t i = 0; i < nents; i++) {
+ char tshort[32], thuman[128];
+ pcieadm_cfgspace_print_t p;
+
+ (void) snprintf(tshort, sizeof (tshort), "st%u", i);
+ (void) snprintf(thuman, sizeof (thuman), "ST Table %u",
+ i);
+ p.pcp_off = print->pcp_off + i * 2;
+ p.pcp_len = 2;
+ p.pcp_short = tshort;
+ p.pcp_human = thuman;
+ p.pcp_print = pcieadm_cfgspace_print_regdef;
+ p.pcp_arg = pcieadm_regdef_tph_st;
+
+ p.pcp_print(walkp, &p, p.pcp_arg);
+ }
+}
+
+static pcieadm_cfgspace_print_t pcieadm_cap_tph[] = {
+ { 0x0, 4, "caphdr", "Capability Header",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr },
+ { 0x4, 4, "cap", "TPH Requester Capability",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_tph_cap },
+ { 0x8, 4, "ctl", "TPH Requester Control",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_tph_ctl },
+ { 0xc, 2, "table", "ST Table", pcieadm_cfgspace_print_tphst },
+ { -1, -1, NULL }
+};
+
+/*
+ * SR-IOV
+ */
+static pcieadm_regdef_t pcieadm_regdef_sriov_cap[] = {
+ { 0, 0, "migration", "Migration", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 1, 1, "ari", "ARI Capable Hierarchy Preserved", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unpreserved", "preserved" } } },
+ { 2, 2, "vf10b", "VF 10-bit Tag Requester", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unpreserved", "preserved" } } },
+ { 21, 31, "inum", "VF Migration Interrupt Message Number", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_sriov_ctl[] = {
+ { 0, 0, "vf", "VF", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 1, 1, "vfm", "VF Migration", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 2, 2, "vfmi", "VF Migration Interrupt", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 3, 3, "ari", "ARI Capable Hierarchy", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 4, 4, "vf10b", "VF 10-bit Tag Requester", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_sriov_sts[] = {
+ { 0, 0, "vfm", "VF Migration", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "none", "requested" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_sriov_pgsup[] = {
+ { 0, 31, "pgsz", "Supported Page Sizes", PRDV_BITFIELD,
+ .prd_val = { .prdv_strval = { "4 KB", "8 KB", "16 KB", "32 KB",
+ "64 KB", "128 KB", "256 KB", "512 KB", "1 MB", "2 MB", "4 MB",
+ "8 MB", "16 MB", "32 MB", "64 MB", "128 MB", "256 MB", "512 MB",
+ "1 GB", "2 GB", "4 GB", "8 GB", "16 GB", "32 GB", "64 GB",
+ "128 GB", "256 GB", "512 GB", "1 TB", "2 TB", "4 TB", "8 TB" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_sriov_pgen[] = {
+ { 0, 31, "pgsz", "System Page Sizes", PRDV_BITFIELD,
+ .prd_val = { .prdv_strval = { "4 KB", "8 KB", "16 KB", "32 KB",
+ "64 KB", "128 KB", "256 KB", "512 KB", "1 MB", "2 MB", "4 MB",
+ "8 MB", "16 MB", "32 MB", "64 MB", "128 MB", "256 MB", "512 MB",
+ "1 GB", "2 GB", "4 GB", "8 GB", "16 GB", "32 GB", "64 GB",
+ "128 GB", "256 GB", "512 GB", "1 TB", "2 TB", "4 TB", "8 TB" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_sriov_mig[] = {
+ { 0, 2, "bir", "VF Migration State BIR", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "BAR 0", "BAR 1", "BAR 2", "BAR 3",
+ "BAR 4", "BAR 5" } } },
+ { 3, 31, "offset", "VF Migration State Offset", PRDV_HEX,
+ .prd_val = { .prdv_hex = { 3 } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_cfgspace_print_t pcieadm_cap_sriov[] = {
+ { 0x0, 4, "caphdr", "Capability Header",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr },
+ { 0x4, 4, "cap", "SR-IOV Capabilities",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_sriov_cap },
+ { 0x8, 2, "ctl", "SR-IOV Control",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_sriov_ctl },
+ { 0xa, 2, "sts", "SR-IOV Status",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_sriov_sts },
+ { 0xc, 2, "initvfs", "Initial VFs", pcieadm_cfgspace_print_hex },
+ { 0xe, 2, "totvfs", "Total VFs", pcieadm_cfgspace_print_hex },
+ { 0x10, 2, "numvfs", "Number VFs", pcieadm_cfgspace_print_hex },
+ { 0x12, 1, "dep", "Function Dependency Link",
+ pcieadm_cfgspace_print_hex },
+ { 0x14, 2, "offset", "First VF Offset", pcieadm_cfgspace_print_hex },
+ { 0x16, 2, "stride", "VF Stride", pcieadm_cfgspace_print_hex },
+ { 0x1a, 2, "devid", "VF Device ID", pcieadm_cfgspace_print_hex },
+ { 0x1c, 4, "pgsz", "Supported Page Sizes",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_sriov_pgsup },
+ { 0x20, 4, "pgsz", "System Page Sizes",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_sriov_pgen },
+ { 0x24, 24, "vfbar", "Virtual Base Address Register",
+ pcieadm_cfgspace_print_bars },
+ { 0x3c, 4, "migration", "VF Migration State Array",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_sriov_mig },
+ { -1, -1, NULL }
+};
+
+/*
+ * PCI-X
+ */
+static pcieadm_regdef_t pcieadm_regdef_pcix_dev_ctl[] = {
+ { 0, 0, "dper", "Data Parity Error Recovery", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 1, 1, "ro", "Relaxed Ordering", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 2, 3, "maxread", "Maximum Memory Read Byte Count", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "512 bytes", "1024 bytes",
+ "2048 byes", "4096 bytes" } } },
+ { 4, 6, "maxsplit", "Maximum Outstanding Split Transactions",
+ PRDV_STRVAL, .prd_val = { .prdv_strval = { "1", "2", "3", "4", "8",
+ "12", "16", "32" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_pcix_dev_sts[] = {
+ { 0, 2, "func", "Function Number", PRDV_HEX },
+ { 3, 7, "dev", "Device Number", PRDV_HEX },
+ { 8, 15, "bus", "Bus Number", PRDV_HEX },
+ { 16, 16, "64bit", "64-bit Device", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported (32-bit)",
+ "supported" } } },
+ { 17, 17, "133mhz", "133 MHz Capable", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported (66 MHz)",
+ "supported" } } },
+ { 18, 18, "spcodis", "Split Completion Discarded", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 19, 19, "unspco", "Unexpected Split Completion", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 20, 20, "complex", "Device Complexity", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "simple", "bridge" } } },
+ { 21, 22, "maxread", "Designed Maximum Memory Read Byte Count",
+ PRDV_STRVAL, .prd_val = { .prdv_strval = { "512 bytes",
+ "1024 bytes", "2048 byes", "4096 bytes" } } },
+ { 23, 25, "maxsplit", "Designed Maximum Outstanding Split Transactions",
+ PRDV_STRVAL, .prd_val = { .prdv_strval = { "1", "2", "3", "4", "8",
+ "12", "16", "32" } } },
+ { 26, 28, "maxcread", "Designed Maximum Cumulative Read Size",
+ PRDV_STRVAL, .prd_val = { .prdv_strval = { "8/1KB", "16/2KB",
+ "32/4KB", "64/8KB", "128/16KB", "256/32KB", "512/64KB",
+ "1024/128KB" } } },
+ { 29, 29, "rxspcoer", "Received Split Completion Error Message",
+ PRDV_STRVAL, .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_pcix_sec_sts[] = {
+ { 0, 0, "64bit", "64-bit Device", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported (32-bit)",
+ "supported" } } },
+ { 1, 1, "133mhz", "133 MHz Capable", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported (66 MHz)",
+ "supported" } } },
+ { 2, 2, "spcodis", "Split Completion Discarded", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 3, 3, "unspco", "Unexpected Split Completion", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 4, 4, "spcoor", "Split Completion Overrun", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 5, 5, "sprde", "Split Request Delayed", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 6, 8, "freq", "Secondary Clock Frequency", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "conventional", "66 MHz", "100 Mhz",
+ "133 MHz" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_pcix_bridge_sts[] = {
+ { 0, 2, "func", "Function Number", PRDV_HEX },
+ { 3, 7, "dev", "Device Number", PRDV_HEX },
+ { 8, 15, "bus", "Bus Number", PRDV_HEX },
+ { 16, 16, "64bit", "64-bit Device", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported (32-bit)",
+ "supported" } } },
+ { 17, 17, "133mhz", "133 MHz Capable", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported (66 MHz)",
+ "supported" } } },
+ { 18, 18, "spcodis", "Split Completion Discarded", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 19, 19, "unspco", "Unexpected Split Completion", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 20, 20, "spcoor", "Split Completion Overrun", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 21, 21, "sprde", "Split Request Delayed", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_pcix_bridge_split[] = {
+ { 0, 15, "cap", "Split Transaction Capacity", PRDV_HEX },
+ { 16, 31, "limit", "Split Transaction Commitment Limit", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_cfgspace_print_t pcieadm_cap_pcix_dev[] = {
+ { 0x2, 2, "ctl", "PCI-X Command",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcix_dev_ctl },
+ { 0x4, 4, "sts", "PCI-X Status",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcix_dev_sts },
+ { -1, -1, NULL }
+};
+
+static pcieadm_cfgspace_print_t pcieadm_cap_pcix_bridge[] = {
+ { 0x2, 2, "secsts", "PCI-X Secondary Status",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcix_sec_sts },
+ { 0x4, 4, "sts", "PCI-X Bridge Status",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcix_bridge_sts },
+ { 0x8, 4, "ussplit", "Upstream Split Transaction",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcix_bridge_split },
+ { 0x8, 4, "dssplit", "Downstream Split Transaction",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcix_bridge_split },
+ { -1, -1, NULL }
+};
+
+/*
+ * Dynamic Power Allocation
+ */
+static pcieadm_regdef_t pcieadm_regdef_dpa_cap[] = {
+ { 0, 4, "substates", "Substate Max", PRDV_HEX,
+ { .prdv_hex = { 0, 1 } } },
+ { 8, 9, "tlu", "Transition Latency Unit", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "1 ms", "10 ms", "100 ms" } } },
+ { 12, 13, "pas", "Power Allocation Scale", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "10.0x", "1.0x", "0.1x",
+ "0.01x" } } },
+ { 16, 23, "tlv0", "Transition Latency Value 0", PRDV_HEX },
+ { 24, 31, "tlv0", "Transition Latency Value 1", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_dpa_sts[] = {
+ { 0, 4, "substate", "Substate Status", PRDV_HEX },
+ { 8, 8, "ctlen", "Substate Control Enabled", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_dpa_ctl[] = {
+ { 0, 4, "substate", "Substate Control", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_cfgspace_print_t pcieadm_cap_dpa[] = {
+ { 0x0, 4, "caphdr", "Capability Header",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr },
+ { 0x4, 4, "cap", "DPA Capability",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_dpa_cap },
+ { 0x8, 4, "lat", "DPA Latency Indicator", pcieadm_cfgspace_print_hex },
+ { 0xc, 2, "sts", "DPA Status",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_dpa_sts },
+ { 0xe, 2, "sts", "DPA Control",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_dpa_ctl },
+ { 0x10, 1, "paa", "DPA Power Allocation Array",
+ pcieadm_cfgspace_print_dpa_paa },
+ { -1, -1, NULL }
+};
+
+/*
+ * Power Budgeting
+ */
+static pcieadm_regdef_t pcieadm_regdef_powbudg_data[] = {
+ { 0, 7, "base", "Base Power", PRDV_HEX },
+ { 8, 9, "scale", "Data Scale", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "1.0x", "0.1x", "0.01x",
+ "0.001x" } } },
+ { 10, 12, "pmsub", "PM Substate", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "Default", "Device Specific",
+ "Device Specific", "Device Specific", "Device Specific",
+ "Device Specific", "Device Specific", "Device Specific" } } },
+ { 13, 14, "pmstate", "PM State", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "D0", "D1", "D2", "D3" } } },
+ { 15, 17, "type", "Type", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "PME Aux", "Axiliary", "Idle",
+ "Sustained", "Sustained - EPRS", "Maximum - EPRS", NULL,
+ "Maximum" } } },
+ { 18, 20, "rail", "Power Rail", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "Power (12V)", "Power (3.3V)",
+ "Power (1.5V or 1.8V)", NULL, NULL, NULL, NULL, "Thermal" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_powbudg_cap[] = {
+ { 0, 0, "sa", "System Allocated", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { -1, -1, NULL }
+};
+
+
+static pcieadm_cfgspace_print_t pcieadm_cap_powbudg[] = {
+ { 0x0, 4, "caphdr", "Capability Header",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr },
+ { 0x4, 1, "sel", "Data Select", pcieadm_cfgspace_print_hex },
+ { 0x8, 4, "data", "Data Regiser", pcieadm_cfgspace_print_regdef,
+ pcieadm_regdef_powbudg_data },
+ { 0xc, 0x1, "cap", "Power Budget Capability",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_powbudg_cap },
+ { -1, -1, NULL }
+};
+
+/*
+ * Precision Time Management
+ */
+static pcieadm_regdef_t pcieadm_regdef_ptm_cap[] = {
+ { 0, 0, "req", "PTM Requester", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 1, 1, "resp", "PTM Responder", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 2, 2, "root", "PTM Root", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 3, 3, "eptm", "ePTM", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 8, 15, "gran", "Local Clock Granularity", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_ptm_ctl[] = {
+ { 0, 0, "en", "PTM Enable", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 1, 1, "root", "Root Select", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 8, 15, "gran", "Effective Granularity", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_cfgspace_print_t pcieadm_cap_info_ptm[] = {
+ { 0x0, 4, "caphdr", "Capability Header",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr },
+ { 0x4, 4, "cap", "PTM Capability",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ptm_cap },
+ { 0x8, 4, "cap", "PTM Control",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ptm_ctl },
+ { -1, -1, NULL }
+};
+
+/*
+ * Address Translation Services (ATS)
+ */
+static pcieadm_regdef_t pcieadm_regdef_ats_cap[] = {
+ { 0, 4, "invqd", "Invalidate Queue Depth", PRDV_HEX },
+ { 5, 5, "pgalign", "Page Aligned Request", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "not required", "required" } } },
+ { 6, 6, "glbinv", "Global Invalidate", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 7, 7, "relo", "Relaxed Ordering", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_ats_ctl[] = {
+ { 0, 4, "stu", "Smallest Translation Unit", PRDV_HEX },
+ { 15, 15, "en", "Enable", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_cfgspace_print_t pcieadm_cap_ats[] = {
+ { 0x0, 4, "caphdr", "Capability Header",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr },
+ { 0x4, 2, "cap", "ATS Capability",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ats_cap },
+ { 0x6, 2, "cap", "ATS Control",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ats_ctl },
+ { -1, -1, NULL }
+};
+
+/*
+ * Page Request
+ */
+static pcieadm_regdef_t pcieadm_regdef_pgreq_ctl[] = {
+ { 0, 0, "en", "Enable", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 1, 1, "reset", "Reset", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_pgreq_sts[] = {
+ { 0, 0, "rf", "Response Failure", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 1, 1, "uprgi", "Unexpected Page Request Group Index", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 8, 8, "stopped", "Stopped", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 15, 15, "prgrpreq", "PRG Response PASID", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "not required", "required" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_cfgspace_print_t pcieadm_cap_pgreq[] = {
+ { 0x0, 4, "caphdr", "Capability Header",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr },
+ { 0x4, 2, "ctl", "Page Request Control",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pgreq_ctl },
+ { 0x6, 2, "ctl", "Page Request Status",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pgreq_sts },
+ { 0x8, 4, "cap", "Outstanding Page Request Capacity",
+ pcieadm_cfgspace_print_hex },
+ { 0xc, 4, "alloc", "Outstanding Page Request Allocation",
+ pcieadm_cfgspace_print_hex },
+ { -1, -1, NULL }
+};
+
+/*
+ * NULL Capability
+ */
+static pcieadm_cfgspace_print_t pcieadm_cap_null[] = {
+ { 0x0, 4, "caphdr", "Capability Header",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr },
+ { -1, -1, NULL }
+};
+
+/*
+ * Downstream Port Containment
+ */
+static pcieadm_regdef_t pcieadm_regdef_dpc_cap[] = {
+ { 0, 4, "inum", "DPC Interrupt Message Number", PRDV_HEX },
+ { 5, 5, "rpext", "Root Port Extensions", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 6, 6, "ptlpeb", "Poisoned TLP Egress Blocking", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 7, 7, "swtrig", "Software Triggering", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 8, 11, "logsz", "RP PIO Log Size", PRDV_HEX },
+ { 12, 12, "errcorr", "DL_Active ERR_COR Signaling", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_dpc_ctl[] = {
+ { 0, 1, "trigger", "DPC Trigger", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled, fatal",
+ "enabled, non-fatal" } } },
+ { 2, 2, "comp", "Completion Control", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "Completer Abort",
+ "Unsupported Request" } } },
+ { 3, 3, "intr", "Interrupt",
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 4, 4, "errcor", "ERR_COR",
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 5, 5, "ptlpeb", "Poisoned TLP Egress Blocking", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 6, 6, "swtrig", "Software Trigger", PRDV_HEX },
+ { 7, 7, "corerr", "DL_Active ERR_COR",
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 8, 8, "sigsfw", "SIG_SFW",
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_dpc_sts[] = {
+ { 0, 0, "trigger", "Trigger Status", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "not triggered", "triggered" } } },
+ { 1, 2, "reason", "Trigger Reason", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unmasked uncorrectable",
+ "ERR_NONFATAL received", "ERR_FATAL received",
+ "see extension" } } },
+ { 3, 3, "istatus", "Interrupt Status", PRDV_HEX },
+ { 4, 4, "rpbusy", "RP Busy", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "no", "yes" } } },
+ { 5, 6, "extreason", "Trigger Reason Extension", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "RP PIO", "Software Trigger" } } },
+ { 8, 12, "feptr", "RP PIO, First Error Pointer", PRDV_HEX },
+ { 13, 13, "sigsfw", "SIG_SFW Status", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_dpc_rppio_bits[] = {
+ { 0, 0, "cfgur", "Configuration Request UR Completion", PRDV_HEX },
+ { 1, 1, "cfgca", "Configuration Request CA Completion", PRDV_HEX },
+ { 2, 2, "cfgcto", "Configuration Request Completion Timeout",
+ PRDV_HEX },
+ { 8, 8, "iour", "I/O UR Completion", PRDV_HEX },
+ { 9, 9, "ioca", "I/O CA Completion", PRDV_HEX },
+ { 10, 10, "iocto", "I/O Completion Timeout", PRDV_HEX },
+ { 8, 8, "memur", "Memory UR Completion", PRDV_HEX },
+ { 9, 9, "memca", "Memory CA Completion", PRDV_HEX },
+ { 10, 10, "memcto", "Memory Completion Timeout", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static void
+pcieadm_cfgspace_print_dpc_rppio(pcieadm_cfgspace_walk_t *walkp,
+ pcieadm_cfgspace_print_t *print, void *arg)
+{
+ uint32_t cap = walkp->pcw_data->pcb_u32[(walkp->pcw_capoff + 4) / 4];
+
+ if (BITX(cap, 5, 5) == 0) {
+ return;
+ }
+
+ pcieadm_cfgspace_print_regdef(walkp, print, arg);
+}
+
+static void
+pcieadm_cfgspace_print_dpc_piohead(pcieadm_cfgspace_walk_t *walkp,
+ pcieadm_cfgspace_print_t *print, void *arg)
+{
+ uint32_t cap = walkp->pcw_data->pcb_u32[(walkp->pcw_capoff + 4) / 4];
+ uint32_t nwords = BITX(cap, 11, 8);
+
+ if (BITX(cap, 5, 5) == 0 || nwords < 4) {
+ return;
+ }
+
+ pcieadm_cfgspace_print_hex(walkp, print, NULL);
+}
+
+static void
+pcieadm_cfgspace_print_dpc_impspec(pcieadm_cfgspace_walk_t *walkp,
+ pcieadm_cfgspace_print_t *print, void *arg)
+{
+ uint32_t cap = walkp->pcw_data->pcb_u32[(walkp->pcw_capoff + 4) / 4];
+ uint32_t nwords = BITX(cap, 11, 8);
+
+ if (BITX(cap, 5, 5) == 0 || nwords < 5) {
+ return;
+ }
+
+ pcieadm_cfgspace_print_hex(walkp, print, NULL);
+}
+
+static void
+pcieadm_cfgspace_print_dpc_tlplog(pcieadm_cfgspace_walk_t *walkp,
+ pcieadm_cfgspace_print_t *print, void *arg)
+{
+ uint32_t cap = walkp->pcw_data->pcb_u32[(walkp->pcw_capoff + 4) / 4];
+ int32_t nwords = BITX(cap, 11, 8);
+
+ if (nwords == 0 || BITX(cap, 5, 5) == 0) {
+ return;
+ }
+
+ if (nwords <= 9) {
+ nwords -= 5;
+ } else {
+ nwords -= 4;
+ }
+
+ for (int32_t i = 0; i < nwords; i++) {
+ char tlpshort[32], tlphuman[128];
+ pcieadm_cfgspace_print_t p;
+
+ (void) snprintf(tlpshort, sizeof (tlpshort), "%s%u",
+ print->pcp_short, i);
+ (void) snprintf(tlphuman, sizeof (tlphuman), "%s %u",
+ print->pcp_human, i);
+ p.pcp_off = print->pcp_off + i * 4;
+ p.pcp_len = 4;
+ p.pcp_short = tlpshort;
+ p.pcp_human = tlphuman;
+ p.pcp_print = pcieadm_cfgspace_print_hex;
+ p.pcp_arg = NULL;
+
+ p.pcp_print(walkp, &p, p.pcp_arg);
+ }
+}
+
+static pcieadm_cfgspace_print_t pcieadm_cap_dpc[] = {
+ { 0x0, 4, "caphdr", "Capability Header",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr },
+ { 0x4, 2, "cap", "DPC Capability",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_dpc_cap },
+ { 0x6, 2, "ctl", "DPC Control",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_dpc_ctl },
+ { 0x8, 2, "sts", "DPC Status",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_dpc_sts },
+ { 0xa, 2, "srcid", "DPC Error Source ID",
+ pcieadm_cfgspace_print_hex },
+ { 0x10, 4, "rppiosts", "RP PIO Status",
+ pcieadm_cfgspace_print_dpc_rppio, pcieadm_regdef_dpc_rppio_bits },
+ { 0x14, 4, "rppiomask", "RP PIO Mask ID",
+ pcieadm_cfgspace_print_dpc_rppio, pcieadm_regdef_dpc_rppio_bits },
+ { 0x14, 4, "rppiosev", "RP PIO Severity",
+ pcieadm_cfgspace_print_dpc_rppio, pcieadm_regdef_dpc_rppio_bits },
+ { 0x18, 4, "rppiose", "RP PIO SysError",
+ pcieadm_cfgspace_print_dpc_rppio, pcieadm_regdef_dpc_rppio_bits },
+ { 0x1c, 4, "rppioex", "RP PIO Exception",
+ pcieadm_cfgspace_print_dpc_rppio, pcieadm_regdef_dpc_rppio_bits },
+ { 0x20, 4, "rppiohl0", "RP PIO Header Log 0",
+ pcieadm_cfgspace_print_dpc_piohead },
+ { 0x24, 4, "rppiohl1", "RP PIO Header Log 1",
+ pcieadm_cfgspace_print_dpc_piohead },
+ { 0x28, 4, "rppiohl2", "RP PIO Header Log 2",
+ pcieadm_cfgspace_print_dpc_piohead },
+ { 0x2c, 4, "rppiohl3", "RP PIO Header Log 3",
+ pcieadm_cfgspace_print_dpc_piohead },
+ { 0x30, 4, "impspec", "RP PIO ImpSpec Log",
+ pcieadm_cfgspace_print_dpc_impspec },
+ { 0x34, 16, "tlplog", "RP PIO TLP Prefix Log",
+ pcieadm_cfgspace_print_dpc_tlplog },
+ { -1, -1, NULL }
+};
+
+/*
+ * Virtual Channel Capability
+ */
+static pcieadm_regdef_t pcieadm_regdef_vc_cap1[] = {
+ { 0, 2, "count", "Extended VC Count", PRDV_HEX },
+ { 4, 6, "lpcount", "Low Priority Extended VC Count", PRDV_HEX },
+ { 8, 9, "refclk", "Reference Clock", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "100ns" } } },
+ { 10, 11, "patsz", "Port Arbitration Table Size", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "1 bit", "2 bits", "4 bits",
+ "8 bits" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_vc_cap2[] = {
+ { 0, 7, "arbcap", "VC Arbitration Capability", PRDV_BITFIELD,
+ .prd_val = { .prdv_strval = { "hardware fixed",
+ "32 phase weighted round robin", "64 phase weighted round robin",
+ "128 phase weighted round robin" } } },
+ { 24, 31, "offset", "VC Arbitration Table Offset", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_vc_ctl[] = {
+ { 0, 0, "loadtbl", "Load VC Arbitration Table", PRDV_HEX },
+ { 1, 3, "arbtype", "VC Arbitration Select", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "hardware fixed",
+ "32 phase weighted round robin", "64 phase weighted round robin",
+ "128 phase weighted round robin" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_vc_sts[] = {
+ { 0, 0, "table", "VC Arbitration Table Status", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_vc_rsrccap[] = {
+ { 0, 7, "arbcap", "Port Arbitration Capability", PRDV_BITFIELD,
+ .prd_val = { .prdv_strval = { "hardware fixed",
+ "32 phase weighted round robin", "64 phase weighted round robin",
+ "128 phase weighted round robin",
+ "128 phase time-based weighted round robin",
+ "256 phase weighted round robin" } } },
+ { 14, 14, "aps", "Advanced Packet Switching", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 15, 15, "rstx", "Reject Snoop Transactions", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 16, 22, "nslots", "Maximum Time Slots", PRDV_HEX,
+ { .prdv_hex = { 0, 1 } } },
+ { 24, 31, "offset", "VC Arbitration Table Offset", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_vc_rsrcctl[] = {
+ { 0, 7, "tcmap", "TC/VC Map", PRDV_HEX },
+ { 16, 16, "loadtbl", "Load VC Arbitration Table", PRDV_HEX },
+ { 17, 19, "arbtype", "Port Arbitration Select", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "hardware fixed",
+ "32 phase weighted round robin", "64 phase weighted round robin",
+ "128 phase weighted round robin",
+ "128 phase time-based weighted round robin",
+ "256 phase weighted round robin" } } },
+ { 24, 26, "vcid", "VC ID", PRDV_HEX },
+ { 31, 31, "en", "VC Enable",
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_vc_rsrcsts[] = {
+ { 0, 0, "table", "Port Arbitration Table Status", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static void
+pcieadm_cfgspace_print_vc_rsrc(pcieadm_cfgspace_walk_t *walkp,
+ pcieadm_cfgspace_print_t *print, void *arg)
+{
+ uint32_t cap = walkp->pcw_data->pcb_u32[(walkp->pcw_capoff + 4) / 4];
+ uint32_t nents = BITX(cap, 2, 0) + 1;
+
+ for (uint32_t i = 0; i < nents; i++) {
+ char vcshort[32], vchuman[128];
+ pcieadm_cfgspace_print_t p;
+
+ (void) snprintf(vcshort, sizeof (vcshort), "rsrccap%u", i);
+ (void) snprintf(vchuman, sizeof (vchuman), "VC Resource %u "
+ "Capability", i);
+ p.pcp_off = print->pcp_off + i * 0x10;
+ p.pcp_len = 4;
+ p.pcp_short = vcshort;
+ p.pcp_human = vchuman;
+ p.pcp_print = pcieadm_cfgspace_print_regdef;
+ p.pcp_arg = pcieadm_regdef_vc_rsrccap;
+
+ p.pcp_print(walkp, &p, p.pcp_arg);
+
+ (void) snprintf(vcshort, sizeof (vcshort), "rsrcctl%u", i);
+ (void) snprintf(vchuman, sizeof (vchuman), "VC Resource %u "
+ "Control", i);
+ p.pcp_off = print->pcp_off + i * 0x10 + 4;
+ p.pcp_len = 4;
+ p.pcp_short = vcshort;
+ p.pcp_human = vchuman;
+ p.pcp_print = pcieadm_cfgspace_print_regdef;
+ p.pcp_arg = pcieadm_regdef_vc_rsrcctl;
+
+ p.pcp_print(walkp, &p, p.pcp_arg);
+
+ (void) snprintf(vcshort, sizeof (vcshort), "rsrcsts%u", i);
+ (void) snprintf(vchuman, sizeof (vchuman), "VC Resource %u "
+ "Status", i);
+ p.pcp_off = print->pcp_off + i * 0x10 + 0xa;
+ p.pcp_len = 2;
+ p.pcp_short = vcshort;
+ p.pcp_human = vchuman;
+ p.pcp_print = pcieadm_cfgspace_print_regdef;
+ p.pcp_arg = pcieadm_regdef_vc_rsrcsts;
+
+ p.pcp_print(walkp, &p, p.pcp_arg);
+ }
+}
+
+static pcieadm_cfgspace_print_t pcieadm_cap_vc[] = {
+ { 0x0, 4, "caphdr", "Capability Header",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_pcie_caphdr },
+ { 0x4, 4, "cap1", "Port VC Capability 1",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_vc_cap1 },
+ { 0x8, 4, "cap2", "Port VC Capability 2",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_vc_cap2 },
+ { 0xc, 2, "ctl", "Port VC Control",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_vc_ctl },
+ { 0xe, 2, "sts", "Port VC Status",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_vc_sts },
+ { 0x10, 12, "vcrec", "VC Resource", pcieadm_cfgspace_print_vc_rsrc },
+ { -1, -1, NULL }
+};
+
+/*
+ * HyperTransport
+ */
+static pcieadm_cfgspace_print_t pcieadm_cap_ht_intr[] = {
+ { 0x2, 1, "index", "Interrupt Discovery Index",
+ pcieadm_cfgspace_print_hex },
+ { 0x4, 4, "dataport", "Interrupt Dataport",
+ pcieadm_cfgspace_print_hex },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_ht_command_pri[] = {
+ { 0, 4, "unitid", "Base Unit ID", PRDV_HEX },
+ { 5, 9, "count", "Unit Count", PRDV_HEX },
+ { 10, 10, "host", "Master Host", PRDV_HEX },
+ { 11, 11, "dir", "Default Direction", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "towards host",
+ "away from host" } } },
+ { 12, 12, "drop", "Drop on Uninitialized Link", PRDV_HEX },
+ { 13, 15, "cap", "Capability ID", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_ht_command_sec[] = {
+ { 0, 0, "reset", "Warm Reset", PRDV_HEX },
+ { 1, 1, "de", "Double Ended", PRDV_HEX },
+ { 2, 6, "devno", "Device Number", PRDV_HEX },
+ { 7, 7, "chain", "Chain Side", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "from host", "from chain" } } },
+ { 8, 8, "hide", "Host Hide", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "visible", "hidden" } } },
+ { 10, 10, "target", "Act as Target", PRDV_HEX },
+ { 11, 11, "eocerr", "Host Inbound End of Chain Error", PRDV_HEX },
+ { 12, 12, "drop", "Drop on Uninitialized Link", PRDV_HEX },
+ { 13, 15, "cap", "Capability ID", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_ht_linkctl[] = {
+ { 0, 0, "srcid", "Source ID", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 1, 1, "cfl", "CRC Flood", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 2, 2, "cst", "CRC Start Test", PRDV_HEX },
+ { 3, 3, "cfer", "CRC Force Error", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 4, 4, "linkfail", "Link Failure", PRDV_HEX },
+ { 5, 5, "initcmp", "Initialization Complete", PRDV_HEX },
+ { 6, 6, "eoc", "End of Chain", PRDV_HEX },
+ { 7, 7, "txoff", "Transmitter Off", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "transmitter on",
+ "transmitter off" } } },
+ { 8, 11, "crcerr", "CRC Error", PRDV_HEX },
+ { 12, 12, "isoc", "Isochronous Flow Control", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 13, 13, "ls", "LDTSTOP# Tristate", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 14, 14, "extctl", "Extended CTL Time", PRDV_HEX },
+ { 15, 15, "64b", "64-bit Addressing", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_ht_linkcfg[] = {
+ { 0, 2, "maxin", "Maximum Link Width In", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "8 bits", "16 bits", NULL, "32 bits",
+ "2 bits", "4 bits", NULL, "not connected" } } },
+ { 3, 3, "dwfcinsup", "Doubleword Flow Control In", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 4, 6, "maxout", "Maximum Link Width Out", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "8 bits", "16 bits", NULL, "32 bits",
+ "2 bits", "4 bits", NULL, "not connected" } } },
+ { 7, 7, "dwfcoutsup", "Doubleword Flow Control Out", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 8, 10, "linkin", "Link Width In", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "8 bits", "16 bits", NULL, "32 bits",
+ "2 bits", "4 bits", NULL, "not connected" } } },
+ { 11, 11, "dwfcin", "Doubleword Flow Control In", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 12, 14, "linkout", "Link Width Out", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "8 bits", "16 bits", NULL, "32 bits",
+ "2 bits", "4 bits", NULL, "not connected" } } },
+ { 15, 15, "dwfcout", "Doubleword Flow Control Out", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_ht_rev[] = {
+ { 0, 4, "minor", "Minor Revision", PRDV_HEX },
+ { 5, 7, "major", "Major Revision", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_ht_linkfreq[] = {
+ { 0, 4, "freq", "Link Frequency", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "200 MHz", "300 MHz", "400 MHz",
+ "500 MHz", "600 MHz", "800 MHz", "1000 MHz", "1200 MHz", "1400 MHz",
+ "1600 MHz", "1800 MHz", "2000 MHz", "2200 MHz", "2400 MHz",
+ "2600 MHz", "Vendor Specfic" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_ht_linkerr[] = {
+ { 4, 4, "prot", "Protocol Error", PRDV_HEX },
+ { 5, 5, "over", "Overflow Error", PRDV_HEX },
+ { 6, 6, "eoc", "End of Chain Error", PRDV_HEX },
+ { 7, 7, "ctl", "CTL Timeout", PRDV_HEX },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_ht_linkcap[] = {
+ { 0, 15, "freq", "Link Frequency", PRDV_BITFIELD,
+ .prd_val = { .prdv_strval = { "200 MHz", "300 MHz", "400 MHz",
+ "500 MHz", "600 MHz", "800 MHz", "1000 MHz", "1200 MHz", "1400 MHz",
+ "1600 MHz", "1800 MHz", "2000 MHz", "2200 MHz", "2400 MHz",
+ "2600 MHz", "Vendor Specfic" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_ht_feature[] = {
+ { 0, 0, "isofc", "Isochronous Flow Control", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 1, 1, "ls", "LDTSTOP#", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 2, 2, "crct", "CRC Test Mode", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 3, 3, "ectl", "Extended CTL Time", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "not required", "required" } } },
+ { 4, 4, "64b", "64-bit Addressing", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 5, 5, "unitid", "UnitID Reorder", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "enabled", "disabled" } } },
+ { 6, 6, "srcid", "Source Identification Extension", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "not required", "required" } } },
+ { 8, 8, "extreg", "Extended Register Set", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "unsupported", "supported" } } },
+ { 9, 9, "uscfg", "Upstream Configuration", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_ht_error[] = {
+ { 0, 0, "protfl", "Protocol Error Flood", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 1, 1, "ovfl", "Overflow Error Flood", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 2, 2, "protf", "Protocol Error Fatal", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 3, 3, "ovf", "Overflow Error Fatal", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 4, 4, "eocf", "End of Chain Fatal Error", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 5, 5, "respf", "Response Error Fatal", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 6, 6, "crcf", "CRC Error Fatal", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 7, 7, "sysf", "System Error Fatal", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 8, 8, "chain", "Chain Fail", PRDV_HEX },
+ { 9, 9, "resp", "Response Error", PRDV_HEX },
+ { 10, 10, "protnf", "Protocol Error Non-Fatal", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 11, 11, "ovfnf", "Overflow Error Non-Fatal", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 12, 12, "eocnf", "End of Chain Error Non-Fatal", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 13, 13, "respnf", "Response Error Non-Fatal", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 14, 14, "crcnf", "CRC Error Non-Fatal", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 15, 15, "sysnf", "System Error Non-Fatal", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_ht_memory[] = {
+ { 0, 8, "base", "Memory Base Upper 8 Bits", PRDV_HEX,
+ .prd_val = { .prdv_hex = { 32 } } },
+ { 9, 15, "limit", "Memory Limit Upper 8 Bits", PRDV_HEX,
+ .prd_val = { .prdv_hex = { 32 } } },
+ { -1, -1, NULL }
+};
+
+static pcieadm_cfgspace_print_t pcieadm_cap_ht_pri[] = {
+ { 0x2, 2, "command", "Command",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_command_pri },
+ { 0x4, 2, "linkctl0", "Link Control 0",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_linkctl },
+ { 0x6, 2, "linkcfg0", "Link Configuration 0",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_linkcfg },
+ { 0x8, 2, "linkctl1", "Link Control 1",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_linkctl },
+ { 0xa, 2, "linkcfg1", "Link Configuration 1",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_linkcfg },
+ { 0xc, 1, "rev", "Revision",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_rev },
+ { 0xd, 1, "linkfreq0", "Link Frequency 0",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_linkfreq },
+ { 0xd, 1, "linkerr0", "Link Error 0",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_linkerr },
+ { 0xe, 2, "linkfcap0", "Link Frequency Cap 0",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_linkcap },
+ { 0x10, 1, "feature", "Feature Capability",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_feature },
+ { 0x11, 1, "linkfreq1", "Link Frequency 1",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_linkfreq },
+ { 0x11, 1, "linkerr1", "Link Error 1",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_linkerr },
+ { 0x12, 2, "linkfcap1", "Link Frequency Cap 1",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_linkcap },
+ { 0x14, 2, "scratch", "Enumeration Scratchpad",
+ pcieadm_cfgspace_print_hex },
+ { 0x16, 2, "error", "Error Handling",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_error },
+ { 0x18, 2, "memory", "Memory",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_memory },
+ { 0x1a, 1, "bus", "Bus Number", pcieadm_cfgspace_print_hex },
+ { -1, -1, NULL }
+};
+
+static pcieadm_cfgspace_print_t pcieadm_cap_ht_sec[] = {
+ { 0x2, 2, "command", "Command",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_command_sec },
+ { 0x4, 2, "linkctl", "Link Control",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_linkctl },
+ { 0x6, 2, "linkcfg", "Link Configuration",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_linkcfg },
+ { 0x8, 1, "rev", "Revision",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_rev },
+ { 0x9, 1, "linkfreq", "Link Frequency 0",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_linkfreq },
+ { 0x9, 1, "linkerr", "Link Error 0",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_linkerr },
+ { 0xa, 2, "linkfcap", "Link Frequency Cap 0",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_linkcap },
+ { 0xc, 2, "feature", "Feature Capability",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_feature },
+ { 0x10, 2, "scratch", "Enumeration Scratchpad",
+ pcieadm_cfgspace_print_hex },
+ { 0x12, 2, "error", "Error Handling",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_error },
+ { 0x14, 2, "memory", "Memory",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_memory },
+ { -1, -1, NULL }
+};
+
+static pcieadm_regdef_t pcieadm_regdef_ht_msi[] = {
+ { 0, 0, "en", "Enable", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { 1, 1, "fixed", "Fixed", PRDV_STRVAL,
+ .prd_val = { .prdv_strval = { "disabled", "enabled" } } },
+ { -1, -1, NULL }
+};
+
+static void
+pcieadm_cfgspace_print_ht_msi_addr(pcieadm_cfgspace_walk_t *walkp,
+ pcieadm_cfgspace_print_t *print, void *arg)
+{
+ uint8_t fixed = walkp->pcw_data->pcb_u8[walkp->pcw_capoff + 2];
+
+ if (BITX(fixed, 1, 1) != 0)
+ return;
+
+ pcieadm_cfgspace_print_hex(walkp, print, arg);
+}
+
+static pcieadm_cfgspace_print_t pcieadm_cap_ht_msi[] = {
+ { 0x2, 2, "command", "Command",
+ pcieadm_cfgspace_print_regdef, pcieadm_regdef_ht_msi },
+ { 0x4, 8, "address", "MSI Address",
+ pcieadm_cfgspace_print_ht_msi_addr },
+ { -1, -1, NULL }
+};
+
+/*
+ * Capability related tables
+ */
+typedef struct pcieadm_cap_vers {
+ uint32_t ppr_vers;
+ uint32_t ppr_len;
+ pcieadm_cfgspace_print_t *ppr_print;
+} pcieadm_cap_vers_t;
+
+typedef struct pcieadm_subcap {
+ const char *psub_short;
+ const char *psub_human;
+} pcieadm_subcap_t;
+
+typedef struct pcieadm_pci_cap pcieadm_pci_cap_t;
+
+typedef void (*pcieadm_cap_info_f)(pcieadm_cfgspace_walk_t *,
+ const pcieadm_pci_cap_t *, uint32_t, const pcieadm_cap_vers_t **,
+ uint32_t *, const pcieadm_subcap_t **);
+
+struct pcieadm_pci_cap {
+ uint32_t ppc_id;
+ const char *ppc_short;
+ const char *ppc_human;
+ pcieadm_cap_info_f ppc_info;
+ pcieadm_cap_vers_t ppc_vers[4];
+};
+
+/*
+ * Capability version determinations.
+ */
+
+static void
+pcieadm_cap_info_fixed(pcieadm_cfgspace_walk_t *walkp,
+ const pcieadm_pci_cap_t *cap, uint32_t off,
+ const pcieadm_cap_vers_t **versp, uint32_t *lenp,
+ const pcieadm_subcap_t **subcap)
+{
+ *versp = &cap->ppc_vers[0];
+ *lenp = cap->ppc_vers[0].ppr_len;
+ *subcap = NULL;
+}
+
+static void
+pcieadm_cap_info_vers(pcieadm_cfgspace_walk_t *walkp,
+ const pcieadm_pci_cap_t *cap, uint32_t off,
+ const pcieadm_cap_vers_t **versp, uint32_t *lenp,
+ const pcieadm_subcap_t **subcap)
+{
+ uint8_t vers;
+
+ *subcap = NULL;
+ vers = walkp->pcw_data->pcb_u8[off + 2] & 0xf;
+ for (uint32_t i = 0; i < ARRAY_SIZE(cap->ppc_vers); i++) {
+ if (vers == cap->ppc_vers[i].ppr_vers &&
+ cap->ppc_vers[i].ppr_vers != 0) {
+ *versp = &cap->ppc_vers[i];
+ *lenp = cap->ppc_vers[i].ppr_len;
+ return;
+ }
+ }
+
+ *versp = NULL;
+ *lenp = 0;
+}
+
+/*
+ * The PCI Power Management capability uses a 3-bit version ID as opposed to the
+ * standard 4-bit version.
+ */
+static void
+pcieadm_cap_info_pcipm(pcieadm_cfgspace_walk_t *walkp,
+ const pcieadm_pci_cap_t *cap, uint32_t off,
+ const pcieadm_cap_vers_t **versp, uint32_t *lenp,
+ const pcieadm_subcap_t **subcap)
+{
+ uint8_t vers;
+
+ *subcap = NULL;
+ vers = walkp->pcw_data->pcb_u8[off + 2] & 0x7;
+ for (uint32_t i = 0; i < ARRAY_SIZE(cap->ppc_vers); i++) {
+ if (vers == cap->ppc_vers[i].ppr_vers) {
+ *versp = &cap->ppc_vers[i];
+ *lenp = cap->ppc_vers[i].ppr_len;
+ return;
+ }
+ }
+
+ *versp = NULL;
+ *lenp = 0;
+}
+
+/*
+ * The length of the MSI capability depends on bits in its control field. As
+ * such we use a custom function to extract the length and treat each of these
+ * variants as thought it were a different version.
+ */
+static pcieadm_cap_vers_t pcieadm_cap_vers_msi_32 = {
+ 0, 0xa, pcieadm_cap_msi_32
+};
+
+static pcieadm_cap_vers_t pcieadm_cap_vers_msi_32ext = {
+ 0, 0xc, pcieadm_cap_msi_32ext
+};
+
+static pcieadm_cap_vers_t pcieadm_cap_vers_msi_64 = {
+ 0, 0xe, pcieadm_cap_msi_64
+};
+
+static pcieadm_cap_vers_t pcieadm_cap_vers_msi_64ext = {
+ 0, 0x10, pcieadm_cap_msi_64ext
+};
+
+static pcieadm_cap_vers_t pcieadm_cap_vers_msi_32pvm = {
+ 0, 0x14, pcieadm_cap_msi_32pvm
+};
+
+static pcieadm_cap_vers_t pcieadm_cap_vers_msi_64pvm = {
+ 0, 0x18, pcieadm_cap_msi_64pvm
+};
+
+static void
+pcieadm_cap_info_msi(pcieadm_cfgspace_walk_t *walkp,
+ const pcieadm_pci_cap_t *cap, uint32_t off,
+ const pcieadm_cap_vers_t **versp, uint32_t *lenp,
+ const pcieadm_subcap_t **subcap)
+{
+ uint16_t ctrl;
+ boolean_t addr64, pvm, ext;
+
+ *subcap = NULL;
+ ctrl = walkp->pcw_data->pcb_u8[off + 2] |
+ (walkp->pcw_data->pcb_u8[off + 3] << 8);
+ if (ctrl == PCI_EINVAL16) {
+ warnx("failed to read MSI Message Control register");
+ *lenp = 0;
+ *versp = NULL;
+ return;
+ }
+
+ /*
+ * The MSI capability has three main things that control its size.
+ * 64-bit addressing adds 4 bytes. Per-Vector Masking adds 8 bytes and
+ * causes the Extended data addressing piece to always be present.
+ * Therefore we check first for pvm as it implies evt, effectively.
+ */
+ addr64 = (ctrl & PCI_MSI_64BIT_MASK) != 0;
+ pvm = (ctrl & PCI_MSI_PVM_MASK) != 0;
+ ext = (ctrl & PCI_MSI_EMD_MASK) != 0;
+
+ if (pvm && addr64) {
+ *versp = &pcieadm_cap_vers_msi_64pvm;
+ } else if (pvm) {
+ *versp = &pcieadm_cap_vers_msi_32pvm;
+ } else if (addr64 && ext) {
+ *versp = &pcieadm_cap_vers_msi_64ext;
+ } else if (addr64) {
+ *versp = &pcieadm_cap_vers_msi_64;
+ } else if (ext) {
+ *versp = &pcieadm_cap_vers_msi_32ext;
+ } else {
+ *versp = &pcieadm_cap_vers_msi_32;
+ }
+
+ *lenp = (*versp)->ppr_len;
+}
+
+/*
+ * The AER Capability is technically different for PCIe-PCI bridges. If we find
+ * that device type here, then we need to use a different version information
+ * rather than the actual set defined with the device (which have changed over
+ * time).
+ */
+static const pcieadm_cap_vers_t pcieadm_cap_vers_aer_bridge = {
+ 1, 0x4c, pcieadm_cap_aer_bridge
+};
+
+static void
+pcieadm_cap_info_aer(pcieadm_cfgspace_walk_t *walkp,
+ const pcieadm_pci_cap_t *cap, uint32_t off,
+ const pcieadm_cap_vers_t **versp, uint32_t *lenp,
+ const pcieadm_subcap_t **subcap)
+{
+ if (walkp->pcw_dtype == PCIE_PCIECAP_DEV_TYPE_PCIE2PCI) {
+ uint8_t vers;
+
+ *subcap = NULL;
+ vers = walkp->pcw_data->pcb_u8[off + 2] & 0xf;
+ if (vers != pcieadm_cap_vers_aer_bridge.ppr_vers) {
+ warnx("encountered PCIe to PCI bridge with unknown "
+ "AER capability version: %u", vers);
+ *lenp = 0;
+ *versp = NULL;
+ return;
+ }
+ *lenp = pcieadm_cap_vers_aer_bridge.ppr_len;
+ *versp = &pcieadm_cap_vers_aer_bridge;
+ }
+
+ return (pcieadm_cap_info_vers(walkp, cap, off, versp, lenp, subcap));
+}
+
+/*
+ * The PCI-X capability varies depending on the header type of the device.
+ * Therefore we simply use the device type to figure out what to do.
+ */
+static pcieadm_cap_vers_t pcieadm_cap_vers_pcix_dev = {
+ 0, 0x8, pcieadm_cap_pcix_dev
+};
+
+static pcieadm_cap_vers_t pcieadm_cap_vers_pcix_bridge = {
+ 0, 0x10, pcieadm_cap_pcix_bridge
+};
+
+static void
+pcieadm_cap_info_pcix(pcieadm_cfgspace_walk_t *walkp,
+ const pcieadm_pci_cap_t *cap, uint32_t off,
+ const pcieadm_cap_vers_t **versp, uint32_t *lenp,
+ const pcieadm_subcap_t **subcap)
+{
+
+ *subcap = NULL;
+ switch (walkp->pcw_dtype) {
+ case PCI_HEADER_ZERO:
+ *versp = &pcieadm_cap_vers_pcix_dev;
+ break;
+ case PCI_HEADER_ONE:
+ *versp = &pcieadm_cap_vers_pcix_bridge;
+ break;
+ default:
+ warnx("encountered PCI-X capability with unsupported device "
+ "type: 0x%x\n", walkp->pcw_dtype);
+ *lenp = 0;
+ *versp = NULL;
+ return;
+ }
+
+ *lenp = (*versp)->ppr_len;
+}
+
+typedef struct pcieadm_cap_ht {
+ uint32_t pch_capid;
+ pcieadm_subcap_t pch_subcap;
+ pcieadm_cap_vers_t pch_vers;
+} pcieadm_cap_ht_t;
+
+static pcieadm_cap_ht_t pcieadm_ht_cap_pri = {
+ 0x00, { "pri", "Primary" }, { 0, 0x1c, pcieadm_cap_ht_pri }
+};
+
+static pcieadm_cap_ht_t pcieadm_ht_cap_sec = {
+ 0x01, { "sec", "Secondary" }, { 0, 0x18, pcieadm_cap_ht_sec }
+};
+
+static pcieadm_cap_ht_t pcieadm_ht_caps[] = {
+ { 0x08, { "switch", "Switch" } },
+ { 0x10, { "intr", "Interrupt Discovery and Configuration" },
+ { 0, 8, pcieadm_cap_ht_intr } },
+ { 0x11, { "rev", "Revision ID" } },
+ { 0x12, { "unitid", "UnitID Clumping" } },
+ { 0x13, { "extcfg", "Extended Configuration Space Access" } },
+ { 0x14, { "addrmap", "Address Mapping" } },
+ { 0x15, { "msi", "MSI Mapping" },
+ { 0, 4, pcieadm_cap_ht_msi } },
+ { 0x16, { "dir", "DirectRoute" } },
+ { 0x17, { "vcset", "VCSet" } },
+ { 0x18, { "retry", "Retry Mode" } },
+ { 0x19, { "x86", "X86 Encoding" } },
+ { 0x1a, { "gen3", "Gen3" } },
+ { 0x1b, { "fle", "Function-Level Extension" } },
+ { 0x1c, { "pm", "Power Management" } },
+ { UINT32_MAX, NULL },
+};
+
+static void
+pcieadm_cap_info_ht(pcieadm_cfgspace_walk_t *walkp,
+ const pcieadm_pci_cap_t *cap, uint32_t off,
+ const pcieadm_cap_vers_t **versp, uint32_t *lenp,
+ const pcieadm_subcap_t **subcap)
+{
+ uint32_t base = walkp->pcw_data->pcb_u32[off / 4];
+ uint32_t caplo = BITX(base, 31, 29);
+ pcieadm_cap_ht_t *htcap = NULL;
+
+ *versp = NULL;
+ *lenp = 0;
+ *subcap = NULL;
+
+ if (caplo > 1) {
+ uint32_t capid = BITX(base, 31, 27);
+
+ for (uint32_t i = 0; pcieadm_ht_caps[i].pch_capid != UINT32_MAX;
+ i++) {
+ if (capid == pcieadm_ht_caps[i].pch_capid) {
+ htcap = &pcieadm_ht_caps[i];
+ break;
+ }
+ }
+ } else if (caplo == 0) {
+ htcap = &pcieadm_ht_cap_pri;
+ } else if (caplo == 1) {
+ htcap = &pcieadm_ht_cap_sec;
+ }
+
+ if (htcap == NULL) {
+ warnx("encountered unknown HyperTransport Capability 0x%x",
+ BITX(base, 31, 27));
+ return;
+ }
+
+ *subcap = &htcap->pch_subcap;
+ if (htcap->pch_vers.ppr_print != NULL) {
+ *versp = &htcap->pch_vers;
+ *lenp = htcap->pch_vers.ppr_len;
+ }
+}
+
+pcieadm_pci_cap_t pcieadm_pci_caps[] = {
+ { PCI_CAP_ID_PM, "pcipm", "PCI Power Management",
+ pcieadm_cap_info_pcipm, { { 2, 8, pcieadm_cap_pcipm_v3 },
+ { 3, 8, pcieadm_cap_pcipm_v3 } } },
+ { PCI_CAP_ID_AGP, "agp", "Accelerated Graphics Port" },
+ { PCI_CAP_ID_VPD, "vpd", "Vital Product Data", pcieadm_cap_info_fixed,
+ { { 0, 8, pcieadm_cap_vpd } } },
+ { PCI_CAP_ID_SLOT_ID, "slot", "Slot Identification" },
+ { PCI_CAP_ID_MSI, "msi", "Message Signaled Interrupts",
+ pcieadm_cap_info_msi },
+ { PCI_CAP_ID_cPCI_HS, "cpci", "CompactPCI Hot Swap" },
+ { PCI_CAP_ID_PCIX, "pcix", "PCI-X", pcieadm_cap_info_pcix },
+ { PCI_CAP_ID_HT, "ht", "HyperTransport", pcieadm_cap_info_ht },
+ { PCI_CAP_ID_VS, "vs", "Vendor Specific", pcieadm_cap_info_fixed,
+ { { 0, 3, pcieadm_cap_vs } } },
+ { PCI_CAP_ID_DEBUG_PORT, "dbg", "Debug Port", pcieadm_cap_info_fixed,
+ { { 0, 4, pcieadm_cap_debug } } },
+ { PCI_CAP_ID_cPCI_CRC, "cpcicrc",
+ "CompactPCI Central Resource Control" },
+ { PCI_CAP_ID_PCI_HOTPLUG, "pcihp", "PCI Hot-Plug" },
+ { PCI_CAP_ID_P2P_SUBSYS, "bdgsub", "PCI Bridge Subsystem Vendor ID",
+ pcieadm_cap_info_fixed, { 0, 8, pcieadm_cap_bridge_subsys } },
+ { PCI_CAP_ID_AGP_8X, "agp8x", "AGP 8x" },
+ { PCI_CAP_ID_SECURE_DEV, "secdev", "Secure Device" },
+ { PCI_CAP_ID_PCI_E, "pcie", "PCI Express", pcieadm_cap_info_vers,
+ { { 1, 0x24, pcieadm_cap_pcie_v1 },
+ { 2, 0x3c, pcieadm_cap_pcie_v2 } } },
+ { PCI_CAP_ID_MSI_X, "msix", "MSI-X", pcieadm_cap_info_fixed,
+ { { 0, 12, pcieadm_cap_msix } } },
+ { PCI_CAP_ID_SATA, "sata", "Serial ATA Configuration",
+ pcieadm_cap_info_fixed, { { 0, 8, pcieadm_cap_sata } } },
+ /*
+ * Note, the AF feature doesn't have a version but encodes a length in
+ * the version field, so we cheat and use that.
+ */
+ { PCI_CAP_ID_FLR, "af", "Advanced Features", pcieadm_cap_info_vers,
+ { { 6, 6, pcieadm_cap_af } } },
+ { PCI_CAP_ID_EA, "ea", "Enhanced Allocation" },
+ { PCI_CAP_ID_FPB, "fpb", "Flattening Portal Bridge" }
+};
+
+pcieadm_pci_cap_t pcieadm_pcie_caps[] = {
+ { 0, "null", "NULL Capability", pcieadm_cap_info_fixed,
+ { { 0, 0x4, pcieadm_cap_null } } },
+ { PCIE_EXT_CAP_ID_AER, "aer", "Advanced Error Reporting",
+ pcieadm_cap_info_aer, { { 1, 0x38, pcieadm_cap_aer_v1 },
+ { 2, 0x48, pcieadm_cap_aer_v2 } } },
+ { PCIE_EXT_CAP_ID_VC, "vc", "Virtual Channel", pcieadm_cap_info_vers,
+ { { 0x1, 0x1c, pcieadm_cap_vc } } },
+ { PCIE_EXT_CAP_ID_SER, "sn", "Serial Number", pcieadm_cap_info_vers,
+ { { 1, 0xc, pcieadm_cap_sn } } },
+ { PCIE_EXT_CAP_ID_PWR_BUDGET, "powbudg", "Power Budgeting",
+ pcieadm_cap_info_vers, { { 1, 0x10, pcieadm_cap_powbudg } } },
+ { PCIE_EXT_CAP_ID_RC_LINK_DECL, "rcld",
+ "Root Complex Link Declaration" },
+ { PCIE_EXT_CAP_ID_RC_INT_LINKCTRL, "rcilc",
+ "Root Complex Internal Link Control" },
+ { PCIE_EXT_CAP_ID_RC_EVNT_CEA, "rcecea",
+ "Root Complex Event Collector Endpoint Aggregation" },
+ { PCIE_EXT_CAP_ID_MFVC, "mfvc", "Multi-Function Virtual Channel" },
+ { PCIE_EXT_CAP_ID_VC_WITH_MFVC, "vcwmfvc", "Virtual Channel with MFVC",
+ pcieadm_cap_info_vers, { { 0x1, 0x1c, pcieadm_cap_vc } } },
+ { PCIE_EXT_CAP_ID_RCRB, "rcrb", "Root Complex Register Block" },
+ { PCIE_EXT_CAP_ID_VS, "vsec", "Vendor Specific Extended Capability",
+ pcieadm_cap_info_vers, { { 1, 0x8, pcieadm_cap_vsec } } },
+ { PCIE_EXT_CAP_ID_CAC, "cac", "Configuration Access Correlation" },
+ { PCIE_EXT_CAP_ID_ACS, "acs", "Access Control Services",
+ pcieadm_cap_info_vers, { { 1, 0x8, pcieadm_cap_acs } } },
+ { PCIE_EXT_CAP_ID_ARI, "ari", "Alternative Routing-ID Interpretation",
+ pcieadm_cap_info_vers, { { 1, 0x8, pcieadm_cap_ari } } },
+ { PCIE_EXT_CAP_ID_ATS, "ats", "Access Translation Services",
+ pcieadm_cap_info_vers, { { 1, 0x8, pcieadm_cap_ats } } },
+ { PCIE_EXT_CAP_ID_SRIOV, "sriov", "Single Root I/O Virtualization",
+ pcieadm_cap_info_vers, { { 1, 0x40, pcieadm_cap_sriov } } },
+ { PCIE_EXT_CAP_ID_MRIOV, "mriov", "Multi-Root I/O Virtualization" },
+ { PCIE_EXT_CAP_ID_MULTICAST, "mcast", "Multicast",
+ pcieadm_cap_info_vers, { { 1, 0x30, pcieadm_cap_mcast } } },
+ { PCIE_EXT_CAP_ID_PGREQ, "pgreq", "Page Request",
+ pcieadm_cap_info_vers, { { 1, 0x10, pcieadm_cap_pgreq } } },
+ { PCIE_EXT_CAP_ID_EA, "ea", "Enhanced Allocation" },
+ { PCIE_EXT_CAP_ID_RESIZE_BAR, "rbar", "Resizable Bar" },
+ { PCIE_EXT_CAP_ID_DPA, "dpa", "Dynamic Power Allocation",
+ pcieadm_cap_info_vers, { { 1, 0x10, pcieadm_cap_dpa } } },
+ { PCIE_EXT_CAP_ID_TPH_REQ, "tph", "TPH Requester",
+ pcieadm_cap_info_vers, { { 1, 0xc, pcieadm_cap_tph } } },
+ { PCIE_EXT_CAP_ID_LTR, "ltr", "Latency Tolerance Reporting",
+ pcieadm_cap_info_vers, { { 1, 0x8, pcieadm_cap_ltr } } },
+ { PCIE_EXT_CAP_ID_PCIE2, "pcie2", "Secondary PCI Express",
+ pcieadm_cap_info_vers, { { 1, 0xc, pcieadm_cap_pcie2 } } },
+ { PCIE_EXT_CAP_ID_PASID, "pasid", "Process Address Space ID",
+ pcieadm_cap_info_vers, { { 1, 0x8, pcieadm_cap_pasid } } },
+ { PCIE_EXT_CAP_ID_LNR, "lnr", "LN Requester" },
+ { PCIE_EXT_CAP_ID_DPC, "dpc", "Downstream Port Containment",
+ pcieadm_cap_info_vers, { { 1, 0x30, pcieadm_cap_dpc } } },
+ { PCIE_EXT_CAP_ID_L1PM, "l1pm", "L1 PM Substates",
+ pcieadm_cap_info_vers, { { 1, 0x10, pcieadm_cap_l1pm_v1 },
+ { 2, 0x14, pcieadm_cap_l1pm_v2 } } },
+ { PCIE_EXT_CAP_ID_PTM, "ptm", "Precision Time Management",
+ pcieadm_cap_info_vers, { { 1, 0xc, pcieadm_cap_info_ptm } } },
+ { PCIE_EXT_CAP_ID_FRS, "frs", "FRS Queueing" },
+ { PCIE_EXT_CAP_ID_RTR, "trt", "Readiness Time Reporting" },
+ /*
+ * When we encounter a designated vendor specificaiton, in particular,
+ * for CXL, we'll want to set ppc_subcap so we can use reasonable
+ * filtering.
+ */
+ { PCIE_EXT_CAP_ID_DVS, "dvsec",
+ "Designated Vendor-Specific Extended Capability" },
+ { PCIE_EXT_CAP_ID_VFRBAR, "vfrbar", "Virtual Function Resizable BAR" },
+ { PCIE_EXT_CAP_ID_DLF, "dlf", "Data Link Feature",
+ pcieadm_cap_info_vers, { { 1, 0xc, pcieadm_cap_dlf } } },
+ { PCIE_EXT_CAP_ID_PL16GT, "pl16g", "Physical Layer 16.0 GT/s",
+ pcieadm_cap_info_vers, { { 1, 0x22, pcieadm_cap_16g } } },
+ { PCIE_EXT_CAP_ID_LANE_MARGIN, "margin",
+ "Lane Margining at the Receiver", pcieadm_cap_info_vers,
+ { { 1, 0x8, pcieadm_cap_margin } } },
+ { PCIE_EXT_CAP_ID_HIEARCHY_ID, "hierid", "Hierarchy ID" },
+ { PCIE_EXT_CAP_ID_NPEM, "npem", "Native PCIe Enclosure Management" },
+ { PCIE_EXT_CAP_ID_PL32GT, "pl32g", "Physical Layer 32.0 GT/s" },
+ { PCIE_EXT_CAP_ID_AP, "ap", "Alternative Protocol" },
+ { PCIE_EXT_CAP_ID_SFI, "sfi", "System Firmware Intermediary" }
+};
+
+static const pcieadm_pci_cap_t *
+pcieadm_cfgspace_match_cap(uint32_t capid, boolean_t pcie)
+{
+ uint_t ncaps;
+ pcieadm_pci_cap_t *caps;
+
+ if (pcie) {
+ ncaps = ARRAY_SIZE(pcieadm_pcie_caps);
+ caps = pcieadm_pcie_caps;
+ } else {
+ ncaps = ARRAY_SIZE(pcieadm_pci_caps);
+ caps = pcieadm_pci_caps;
+ }
+
+ for (uint_t i = 0; i < ncaps; i++) {
+ if (caps[i].ppc_id == capid) {
+ return (&caps[i]);
+ }
+ }
+
+ return (NULL);
+}
+
+static void
+pcieadm_cfgspace_print_cap(pcieadm_cfgspace_walk_t *walkp, uint_t capid,
+ const pcieadm_pci_cap_t *cap_info, const pcieadm_cap_vers_t *vers_info,
+ const pcieadm_subcap_t *subcap)
+{
+ boolean_t filter = B_FALSE;
+
+ /*
+ * If we don't recognize the capability, print out the ID if we're not
+ * filtering and not in parsable mode.
+ */
+ if (cap_info == NULL) {
+ if (walkp->pcw_ofmt == NULL &&
+ pcieadm_cfgspace_filter(walkp, NULL)) {
+ warnx("encountered unknown capability ID 0x%x "
+ "unable to print or list", capid);
+ pcieadm_print("Unknown Capability (0x%x)\n", capid);
+ }
+ return;
+ }
+
+ /*
+ * Check to see if we should print this and in particular, if there's
+ * both a capability or subcapability, we need to try and match both.
+ * The reason that the calls to check the filters are conditioned on
+ * pcw_ofmt is that when we're in parsable mode, we cannot match a
+ * top-level capability since it's an arbitrary number of fields.
+ */
+ if (walkp->pcw_ofmt == NULL) {
+ filter = pcieadm_cfgspace_filter(walkp, cap_info->ppc_short);
+ }
+ pcieadm_strfilt_push(walkp, cap_info->ppc_short);
+ if (subcap != NULL) {
+ if (walkp->pcw_ofmt == NULL) {
+ boolean_t subfilt = pcieadm_cfgspace_filter(walkp,
+ subcap->psub_short);
+ filter = subfilt || filter;
+ }
+ pcieadm_strfilt_push(walkp, subcap->psub_short);
+ }
+
+
+ if (walkp->pcw_ofmt == NULL && filter) {
+ if ((walkp->pcw_flags & PCIEADM_CFGSPACE_F_SHORT) != 0) {
+ if (subcap != NULL) {
+ pcieadm_print("%s Capability - %s (%s) "
+ "(0x%x)\n", cap_info->ppc_human,
+ subcap->psub_human,
+ walkp->pcw_filt->pstr_curgen, capid);
+ } else {
+ pcieadm_print("%s Capability (%s) (0x%x)\n",
+ cap_info->ppc_human,
+ walkp->pcw_filt->pstr_curgen, capid);
+ }
+ } else {
+ if (subcap != NULL) {
+ pcieadm_print("%s Capability - %s (0x%x)\n",
+ cap_info->ppc_human, subcap->psub_human,
+ capid);
+ } else {
+ pcieadm_print("%s Capability (0x%x)\n",
+ cap_info->ppc_human, capid);
+ }
+ }
+ }
+
+ if (vers_info != NULL) {
+ pcieadm_cfgspace_print_t *print;
+
+ pcieadm_indent();
+ for (print = vers_info->ppr_print;
+ print->pcp_short != NULL; print++) {
+ VERIFY3P(print->pcp_print, !=, NULL);
+ print->pcp_print(walkp, print,
+ print->pcp_arg);
+ }
+ pcieadm_deindent();
+ } else {
+ if (subcap != NULL) {
+ warnx("Unable to print or list %s - %s (no support or "
+ "missing version info)", cap_info->ppc_human,
+ subcap->psub_human);
+ } else {
+ warnx("Unable to print or list %s (no support or "
+ "missing version info)", cap_info->ppc_human);
+ }
+ }
+
+ if (subcap != NULL) {
+ pcieadm_strfilt_pop(walkp);
+ }
+ pcieadm_strfilt_pop(walkp);
+}
+
+static void
+pcieadm_cfgspace_write(int fd, const uint8_t *source, size_t len)
+{
+ size_t off = 0;
+
+ while (len > 0) {
+ ssize_t ret = write(fd, source + off, len - off);
+ if (ret < 0) {
+ err(EXIT_FAILURE, "failed to write config space to "
+ "output file");
+ }
+
+ off += ret;
+ len -= ret;
+ }
+}
+
+void
+pcieadm_cfgspace(pcieadm_t *pcip, pcieadm_cfgspace_op_t op,
+ pcieadm_cfgspace_f readf, int fd, void *readarg, uint_t nfilts,
+ pcieadm_cfgspace_filter_t *filters, pcieadm_cfgspace_flags_t flags,
+ ofmt_handle_t ofmt)
+{
+ uint_t type;
+ uint16_t cap;
+ pcieadm_cfgspace_data_t data;
+ pcieadm_cfgspace_walk_t walk;
+ const char *headstr, *headshort;
+ pcieadm_cfgspace_print_t *header;
+ boolean_t capsup = B_FALSE, extcfg = B_FALSE;
+ uint_t ncaps;
+
+ walk.pcw_pcieadm = pcip;
+ walk.pcw_op = op;
+ walk.pcw_data = &data;
+ walk.pcw_outfd = fd;
+ walk.pcw_capoff = 0;
+ walk.pcw_nlanes = 0;
+ walk.pcw_nfilters = nfilts;
+ walk.pcw_filters = filters;
+ walk.pcw_flags = flags;
+ walk.pcw_ofmt = ofmt;
+ walk.pcw_filt = NULL;
+
+ /*
+ * Start by reading all of the basic 40-byte config space header in one
+ * fell swoop.
+ */
+ for (uint32_t i = 0; i < PCI_CAP_PTR_OFF / 4; i++) {
+ if (!readf(i * 4, 4, &data.pcb_u32[i], readarg)) {
+ errx(EXIT_FAILURE, "failed to read offset %u from "
+ "configuration space", i * 4);
+ }
+ }
+ walk.pcw_valid = PCI_CAP_PTR_OFF;
+ walk.pcw_caplen = PCI_CAP_PTR_OFF;
+
+ /*
+ * Grab the information from the header that we need to figure out what
+ * kind of device this is, how to print it, if there are any
+ * capabilities, and go from there.
+ */
+ type = data.pcb_u8[PCI_CONF_HEADER] & PCI_HEADER_TYPE_M;
+ switch (type) {
+ case PCI_HEADER_ZERO:
+ headstr = "Type 0 Header";
+ headshort = "header0";
+ header = pcieadm_cfgspace_type0;
+ capsup = (data.pcb_u8[PCI_CONF_STAT] & PCI_STAT_CAP) != 0;
+ break;
+ case PCI_HEADER_ONE:
+ headstr = "Type 1 Header";
+ headshort = "header1";
+ header = pcieadm_cfgspace_type1;
+ capsup = (data.pcb_u8[PCI_CONF_STAT] & PCI_STAT_CAP) != 0;
+ break;
+ case PCI_HEADER_TWO:
+ default:
+ headstr = "Unknown Header";
+ headshort = "headerX";
+ header = pcieadm_cfgspace_unknown;
+ warnx("unsupported PCI header type: 0x%x, output limited to "
+ "data configuration space");
+ }
+
+ walk.pcw_dtype = type;
+
+ if (op == PCIEADM_CFGSPACE_OP_WRITE) {
+ pcieadm_cfgspace_write(fd, &data.pcb_u8[0], PCI_CAP_PTR_OFF);
+ } else if (op == PCIEADM_CFGSPACE_OP_PRINT) {
+ pcieadm_cfgspace_print_t *print;
+
+ if (walk.pcw_ofmt == NULL &&
+ pcieadm_cfgspace_filter(&walk, headshort)) {
+ if ((flags & PCIEADM_CFGSPACE_F_SHORT) != 0) {
+ pcieadm_print("Device %s -- %s (%s)\n",
+ pcip->pia_devstr, headstr, headshort);
+ } else {
+ pcieadm_print("Device %s -- %s\n",
+ pcip->pia_devstr, headstr);
+ }
+ }
+
+ pcieadm_strfilt_push(&walk, headshort);
+ pcieadm_indent();
+ for (print = header; print->pcp_short != NULL; print++) {
+ print->pcp_print(&walk, print, print->pcp_arg);
+ }
+ pcieadm_deindent();
+ pcieadm_strfilt_pop(&walk);
+ }
+
+
+ if (!capsup) {
+ return;
+ }
+
+ for (uint32_t i = PCI_CAP_PTR_OFF / 4; i < PCI_CONF_HDR_SIZE / 4; i++) {
+ if (!readf(i * 4, 4, &data.pcb_u32[i], readarg)) {
+ errx(EXIT_FAILURE, "failed to read offset %u from "
+ "configuration space", i * 4);
+ }
+ }
+ walk.pcw_valid = PCIE_EXT_CAP;
+ VERIFY3P(walk.pcw_filt, ==, NULL);
+
+ if (op == PCIEADM_CFGSPACE_OP_WRITE) {
+ pcieadm_cfgspace_write(fd, &data.pcb_u8[PCI_CAP_PTR_OFF],
+ PCI_CONF_HDR_SIZE - PCI_CAP_PTR_OFF);
+ }
+
+ ncaps = 0;
+ cap = data.pcb_u8[PCI_CONF_CAP_PTR];
+ while (cap != 0 && cap != PCI_EINVAL8) {
+ const pcieadm_pci_cap_t *cap_info;
+ const pcieadm_cap_vers_t *vers_info = NULL;
+ const pcieadm_subcap_t *subcap = NULL;
+ uint8_t cap_id, nextcap;
+ uint32_t read_len = 0;
+
+ /*
+ * The PCI specification requires that the caller mask off the
+ * bottom two bits. Always check for an invalid value (all 1s)
+ * before this.
+ */
+ cap &= PCI_CAP_PTR_MASK;
+ cap_id = data.pcb_u8[cap + PCI_CAP_ID];
+ nextcap = data.pcb_u8[cap + PCI_CAP_NEXT_PTR];
+ cap_info = pcieadm_cfgspace_match_cap(cap_id, B_FALSE);
+ if (cap_info != NULL && cap_info->ppc_info != NULL) {
+ cap_info->ppc_info(&walk, cap_info, cap, &vers_info,
+ &read_len, &subcap);
+ }
+
+ walk.pcw_caplen = read_len;
+ walk.pcw_capoff = cap;
+
+ if (cap_id == PCI_CAP_ID_PCI_E) {
+ extcfg = B_TRUE;
+ if (walk.pcw_valid != 0) {
+ walk.pcw_pcietype = data.pcb_u8[cap +
+ PCIE_PCIECAP] & PCIE_PCIECAP_DEV_TYPE_MASK;
+ walk.pcw_nlanes = (data.pcb_u8[cap +
+ PCIE_LINKCAP] & 0xf0) >> 4;
+ walk.pcw_nlanes |= (data.pcb_u8[cap +
+ PCIE_LINKCAP + 1] & 0x01) << 4;
+ } else {
+ walk.pcw_pcietype = UINT_MAX;
+ }
+ }
+
+ if (op == PCIEADM_CFGSPACE_OP_PRINT) {
+ pcieadm_cfgspace_print_cap(&walk, cap_id, cap_info,
+ vers_info, subcap);
+ }
+
+ cap = nextcap;
+ ncaps++;
+ if (ncaps >= PCI_CAP_MAX_PTR) {
+ errx(EXIT_FAILURE, "encountered more PCI capabilities "
+ "than fit in configuration space");
+ }
+ }
+
+ if (!extcfg) {
+ return;
+ }
+
+ for (uint_t i = PCIE_EXT_CAP / 4; i < PCIE_CONF_HDR_SIZE / 4; i++) {
+ if (!readf(i * 4, 4, &data.pcb_u32[i], readarg)) {
+ errx(EXIT_FAILURE, "failed to read offset %u from "
+ "configuration space", i * 4);
+ }
+ }
+ walk.pcw_valid = PCIE_CONF_HDR_SIZE;
+
+ if (op == PCIEADM_CFGSPACE_OP_WRITE) {
+ pcieadm_cfgspace_write(fd, &data.pcb_u8[PCIE_EXT_CAP],
+ PCIE_CONF_HDR_SIZE - PCIE_EXT_CAP);
+ return;
+ }
+
+ cap = PCIE_EXT_CAP;
+ ncaps = 0;
+ while (cap != 0 && cap != PCI_EINVAL16) {
+ uint16_t cap_id, nextcap;
+ const pcieadm_pci_cap_t *cap_info;
+ const pcieadm_cap_vers_t *vers_info = NULL;
+ const pcieadm_subcap_t *subcap = NULL;
+ uint32_t read_len = 0;
+
+ /*
+ * PCIe has the same masking as PCI. Note, sys/pcie.h currently
+ * has PCIE_EXT_CAP_NEXT_PTR_MASK as 0xfff, instead of the
+ * below. This should be switched to PCIE_EXT_CAP_NEXT_PTR_MASK
+ * when the kernel headers are fixed.
+ */
+ cap &= 0xffc;
+
+ /*
+ * While this seems duplicative of the loop condition, a device
+ * without capabilities indicates it with a zero for the first
+ * cap.
+ */
+ if (data.pcb_u32[cap / 4] == 0 ||
+ data.pcb_u32[cap / 4] == PCI_EINVAL32)
+ break;
+
+ cap_id = data.pcb_u32[cap / 4] & PCIE_EXT_CAP_ID_MASK;
+ nextcap = (data.pcb_u32[cap / 4] >>
+ PCIE_EXT_CAP_NEXT_PTR_SHIFT) & PCIE_EXT_CAP_NEXT_PTR_MASK;
+
+ cap_info = pcieadm_cfgspace_match_cap(cap_id, B_TRUE);
+ if (cap_info != NULL && cap_info->ppc_info != NULL) {
+ cap_info->ppc_info(&walk, cap_info, cap, &vers_info,
+ &read_len, &subcap);
+ }
+
+ walk.pcw_caplen = read_len;
+ walk.pcw_capoff = cap;
+
+ if (op == PCIEADM_CFGSPACE_OP_PRINT) {
+ pcieadm_cfgspace_print_cap(&walk, cap_id, cap_info,
+ vers_info, subcap);
+ }
+
+ cap = nextcap;
+ ncaps++;
+ if (ncaps >= PCIE_EXT_CAP_MAX_PTR) {
+ errx(EXIT_FAILURE, "encountered more PCI capabilities "
+ "than fit in configuration space");
+ }
+ }
+}
+
+void
+pcieadm_show_cfgspace_usage(FILE *f)
+{
+ (void) fprintf(f, "\tshow-cfgspace\t[-L] [-n] [-H] -d device | -f file "
+ "[filter...]\n");
+ (void) fprintf(f, "\tshow-cfgspace\t-p -o field[,...] [-H] -d device | "
+ "-f file [filter...]\n");
+}
+
+static void
+pcieadm_show_cfgspace_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-cfgspace [-L] [-n] [-H] -d "
+ "device | -f file [filter...]\n", pcieadm_progname);
+ (void) fprintf(stderr, " %s show-cfgspace -p -o field[,...] "
+ "[-H] -d device | -f file\n\t\t\t [filter...]\n",
+ pcieadm_progname);
+
+ (void) fprintf(stderr, "\nPrint and decode PCI configuration space "
+ "data from a device or file. Each\n<filter> selects a given "
+ "capability, sub-capability, register, or field to print.\n\n"
+ "\t-d device\tread data from the specified device (driver instance,"
+ "\n\t\t\t/devices path, or b/d/f)\n"
+ "\t-f file\t\tread data from the specified file\n"
+ "\t-L\t\tlist printable fields\n"
+ "\t-n\t\tshow printable short names\n"
+ "\t-H\t\tomit the column header (for -L and -p)\n"
+ "\t-p\t\tparsable output (requires -o)\n"
+ "\t-o field\toutput fields to print (required for -p)\n");
+}
+
+int
+pcieadm_show_cfgspace(pcieadm_t *pcip, int argc, char *argv[])
+{
+ int c, ret;
+ pcieadm_cfgspace_f readf;
+ void *readarg;
+ boolean_t list = B_FALSE, parse = B_FALSE;
+ const char *device = NULL, *file = NULL, *fields = NULL;
+ uint_t nfilts = 0;
+ pcieadm_cfgspace_filter_t *filts = NULL;
+ pcieadm_cfgspace_flags_t flags = 0;
+ uint_t oflags = 0;
+ ofmt_handle_t ofmt = NULL;
+
+ while ((c = getopt(argc, argv, ":HLd:f:o:np")) != -1) {
+ switch (c) {
+ case 'd':
+ device = optarg;
+ break;
+ case 'L':
+ list = B_TRUE;
+ break;
+ case 'f':
+ file = optarg;
+ break;
+ case 'p':
+ parse = B_TRUE;
+ flags |= PCIEADM_CFGSPACE_F_PARSE;
+ oflags |= OFMT_PARSABLE;
+ break;
+ case 'n':
+ flags |= PCIEADM_CFGSPACE_F_SHORT;
+ break;
+ case 'H':
+ oflags |= OFMT_NOHEADER;
+ break;
+ case 'o':
+ fields = optarg;
+ break;
+ case ':':
+ pcieadm_show_cfgspace_help("Option -%c requires an "
+ "argument", optopt);
+ exit(EXIT_USAGE);
+ case '?':
+ default:
+ pcieadm_show_cfgspace_help("unknown option: -%c",
+ optopt);
+ exit(EXIT_USAGE);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (device == NULL && file == NULL) {
+ pcieadm_show_cfgspace_help("one of -d or -f must be specified");
+ exit(EXIT_USAGE);
+ }
+
+ if (device != NULL && file != NULL) {
+ pcieadm_show_cfgspace_help("only one of -d and -f must be "
+ "specified");
+ exit(EXIT_USAGE);
+ }
+
+ if (parse && fields == NULL) {
+ pcieadm_show_cfgspace_help("-p requires fields specified with "
+ "-o");
+ exit(EXIT_USAGE);
+ }
+
+ if (!parse && fields != NULL) {
+ pcieadm_show_cfgspace_help("-o can only be used with -p");
+ exit(EXIT_USAGE);
+ }
+
+ if ((oflags & OFMT_NOHEADER) && !(list || parse)) {
+ pcieadm_show_cfgspace_help("-H must be used with either -L or "
+ "-p");
+ exit(EXIT_USAGE);
+ }
+
+ if ((flags & PCIEADM_CFGSPACE_F_SHORT) && (list || parse)) {
+ pcieadm_show_cfgspace_help("-n cannot be used with either -L "
+ "or -p");
+ exit(EXIT_USAGE);
+ }
+
+ if (list && parse != 0) {
+ pcieadm_show_cfgspace_help("-L and -p cannot be used together");
+ exit(EXIT_USAGE);
+ }
+
+ if (list && fields != NULL) {
+ pcieadm_show_cfgspace_help("-L and -o cannot be used together");
+ exit(EXIT_USAGE);
+ }
+
+ if (list) {
+ fields = "short,human";
+ }
+
+ if (argc > 0) {
+ nfilts = argc;
+ filts = calloc(nfilts, sizeof (pcieadm_cfgspace_filter_t));
+
+ for (int i = 0; i < argc; i++) {
+ filts[i].pcf_string = argv[i];
+ filts[i].pcf_len = strlen(argv[i]);
+ }
+ }
+
+ if (list || parse) {
+ ofmt_status_t oferr;
+ oferr = ofmt_open(fields, pcieadm_cfgspace_ofmt, oflags, 0,
+ &ofmt);
+ ofmt_check(oferr, parse, ofmt, pcieadm_ofmt_errx, warnx);
+ }
+
+ /*
+ * Initialize privileges that we require. For reading from the kernel
+ * we require all privileges. For a file, we just intersect with things
+ * that would allow someone to read from any file.
+ */
+ if (device != NULL) {
+ /*
+ * We need full privileges if reading from a device,
+ * unfortunately.
+ */
+ priv_fillset(pcip->pia_priv_eff);
+ } else {
+ VERIFY0(priv_addset(pcip->pia_priv_eff, PRIV_FILE_DAC_READ));
+ VERIFY0(priv_addset(pcip->pia_priv_eff, PRIV_FILE_DAC_SEARCH));
+ }
+ pcieadm_init_privs(pcip);
+
+ if (device != NULL) {
+ pcieadm_find_dip(pcip, device);
+ pcieadm_init_cfgspace_kernel(pcip, &readf, &readarg);
+ } else {
+ pcip->pia_devstr = file;
+ pcieadm_init_cfgspace_file(pcip, file, &readf, &readarg);
+ }
+ pcieadm_cfgspace(pcip, PCIEADM_CFGSPACE_OP_PRINT, readf, -1, readarg,
+ nfilts, filts, flags, ofmt);
+ if (device != NULL) {
+ pcieadm_fini_cfgspace_kernel(readarg);
+ } else {
+ pcieadm_fini_cfgspace_file(readarg);
+ }
+
+ ofmt_close(ofmt);
+ ret = EXIT_SUCCESS;
+ for (uint_t i = 0; i < nfilts; i++) {
+ if (!filts[i].pcf_used) {
+ warnx("filter '%s' did not match any fields",
+ filts[i].pcf_string);
+ ret = EXIT_FAILURE;
+ }
+ }
+
+ return (ret);
+}
+
+typedef struct pcieadm_save_cfgspace {
+ pcieadm_t *psc_pci;
+ int psc_dirfd;
+ uint_t psc_nsaved;
+ int psc_ret;
+} pcieadm_save_cfgspace_t;
+
+static int
+pcieadm_save_cfgspace_cb(di_node_t devi, void *arg)
+{
+ int fd, nregs, *regs;
+ pcieadm_save_cfgspace_t *psc = arg;
+ pcieadm_cfgspace_f readf;
+ void *readarg;
+ char fname[128];
+
+ psc->psc_pci->pia_devstr = di_node_name(devi);
+ psc->psc_pci->pia_devi = devi;
+ psc->psc_pci->pia_nexus = DI_NODE_NIL;
+ pcieadm_find_nexus(psc->psc_pci);
+ if (psc->psc_pci->pia_nexus == DI_NODE_NIL) {
+ warnx("failed to find nexus for %s", di_node_name(devi));
+ psc->psc_ret = EXIT_FAILURE;
+ return (DI_WALK_CONTINUE);
+ }
+
+ nregs = di_prop_lookup_ints(DDI_DEV_T_ANY, devi, "reg", &regs);
+ if (nregs <= 0) {
+ warnx("failed to lookup regs array for %s",
+ psc->psc_pci->pia_devstr);
+ psc->psc_ret = EXIT_FAILURE;
+ return (DI_WALK_CONTINUE);
+ }
+
+ (void) snprintf(fname, sizeof (fname), "%02x-%02x-%02x.pci",
+ PCI_REG_BUS_G(regs[0]), PCI_REG_DEV_G(regs[0]),
+ PCI_REG_FUNC_G(regs[0]));
+
+ if ((fd = openat(psc->psc_dirfd, fname, O_WRONLY | O_TRUNC | O_CREAT,
+ 0666)) < 0) {
+ warn("failed to create output file %s", fname);
+ psc->psc_ret = EXIT_FAILURE;
+ return (DI_WALK_CONTINUE);
+ }
+
+ pcieadm_init_cfgspace_kernel(psc->psc_pci, &readf, &readarg);
+ pcieadm_cfgspace(psc->psc_pci, PCIEADM_CFGSPACE_OP_WRITE, readf, fd,
+ readarg, 0, NULL, 0, NULL);
+ pcieadm_fini_cfgspace_kernel(readarg);
+
+ if (close(fd) != 0) {
+ warn("failed to close output fd for %s", fname);
+ psc->psc_ret = EXIT_FAILURE;
+ } else {
+ psc->psc_nsaved++;
+ }
+
+ return (DI_WALK_CONTINUE);
+}
+
+void
+pcieadm_save_cfgspace_usage(FILE *f)
+{
+ (void) fprintf(f, "\tsave-devs\t-d device output-file\n");
+ (void) fprintf(f, "\tsave-devs\t-a output-directory\n");
+}
+
+static void
+pcieadm_save_cfgspace_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 save-cfgspace -d device "
+ "output-file\n", pcieadm_progname);
+ (void) fprintf(stderr, " %s save-cfgspace -a "
+ "output-directory\n", pcieadm_progname);
+
+ (void) fprintf(stderr, "\nSave PCI configuration space data from a "
+ "device to a file or\nsave all devices to a specified directory."
+ "\n\n"
+ "\t-a\t\tsave data from all devices\n"
+ "\t-d device\tread data from the specified device (driver instance,"
+ "\n\t\t\t/devices path, or b/d/f)\n");
+}
+
+int
+pcieadm_save_cfgspace(pcieadm_t *pcip, int argc, char *argv[])
+{
+ int c;
+ pcieadm_cfgspace_f readf;
+ void *readarg;
+ const char *device = NULL;
+ boolean_t do_all = B_FALSE;
+
+ while ((c = getopt(argc, argv, ":ad:")) != -1) {
+ switch (c) {
+ case 'a':
+ do_all = B_TRUE;
+ break;
+ case 'd':
+ device = optarg;
+ break;
+ case ':':
+ pcieadm_save_cfgspace_help("Option -%c requires an "
+ "argument", optopt);
+ exit(EXIT_USAGE);
+ case '?':
+ default:
+ pcieadm_save_cfgspace_help("unknown option: -%c",
+ optopt);
+ exit(EXIT_USAGE);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (device == NULL && !do_all) {
+ pcieadm_save_cfgspace_help("missing required -d option to "
+ "indicate device to dump");
+ exit(EXIT_USAGE);
+ }
+
+ if (argc != 1) {
+ pcieadm_save_cfgspace_help("missing required output path");
+ exit(EXIT_USAGE);
+ }
+
+ /*
+ * For reading from devices, we need to full privileges, unfortunately.
+ */
+ priv_fillset(pcip->pia_priv_eff);
+ pcieadm_init_privs(pcip);
+
+ if (!do_all) {
+ int fd;
+
+ pcieadm_find_dip(pcip, device);
+
+ if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pcip->pia_priv_eff) !=
+ 0) {
+ err(EXIT_FAILURE, "failed to raise privileges");
+ }
+
+ if ((fd = open(argv[0], O_WRONLY | O_CREAT | O_TRUNC, 0666)) <
+ 0) {
+ err(EXIT_FAILURE, "failed to open output file %s",
+ argv[0]);
+ }
+
+ if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pcip->pia_priv_min) !=
+ 0) {
+ err(EXIT_FAILURE, "failed to reduce privileges");
+ }
+
+ pcieadm_init_cfgspace_kernel(pcip, &readf, &readarg);
+ pcieadm_cfgspace(pcip, PCIEADM_CFGSPACE_OP_WRITE, readf, fd,
+ readarg, 0, NULL, 0, NULL);
+ pcieadm_fini_cfgspace_kernel(readarg);
+
+ if (close(fd) != 0) {
+ err(EXIT_FAILURE, "failed to close output file "
+ "descriptor");
+ }
+
+ return (EXIT_SUCCESS);
+ } else {
+ pcieadm_save_cfgspace_t psc;
+ pcieadm_di_walk_t walk;
+
+ if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pcip->pia_priv_eff) !=
+ 0) {
+ err(EXIT_FAILURE, "failed to raise privileges");
+ }
+
+ if ((psc.psc_dirfd = open(argv[0], O_RDONLY | O_DIRECTORY)) <
+ 0) {
+ err(EXIT_FAILURE, "failed to open output directory %s",
+ argv[0]);
+ }
+
+ if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pcip->pia_priv_min) !=
+ 0) {
+ err(EXIT_FAILURE, "failed to reduce privileges");
+ }
+
+ psc.psc_nsaved = 0;
+ psc.psc_ret = EXIT_SUCCESS;
+ psc.psc_pci = pcip;
+
+ walk.pdw_arg = &psc;
+ walk.pdw_func = pcieadm_save_cfgspace_cb;
+ pcieadm_di_walk(pcip, &walk);
+
+ VERIFY0(close(psc.psc_dirfd));
+
+ if (psc.psc_nsaved == 0) {
+ warnx("failed to save any PCI devices");
+ return (EXIT_FAILURE);
+ }
+
+ pcieadm_print("successfully saved %u devices to %s\n",
+ psc.psc_nsaved, argv[0]);
+ return (psc.psc_ret);
+ }
+}
diff --git a/usr/src/cmd/pcieadm/pcieadm_devs.c b/usr/src/cmd/pcieadm/pcieadm_devs.c
new file mode 100644
index 0000000000..5ca19ea1d9
--- /dev/null
+++ b/usr/src/cmd/pcieadm/pcieadm_devs.c
@@ -0,0 +1,568 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2021 Oxide Computer Company
+ */
+
+#include <err.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <ofmt.h>
+#include <strings.h>
+#include <sys/pci.h>
+
+#include "pcieadm.h"
+
+typedef struct pcieadm_show_devs {
+ pcieadm_t *psd_pia;
+ ofmt_handle_t psd_ofmt;
+ boolean_t psd_funcs;
+ int psd_nfilts;
+ char **psd_filts;
+ uint_t psd_nprint;
+} pcieadm_show_devs_t;
+
+typedef enum pcieadm_show_devs_otype {
+ PCIEADM_SDO_VID,
+ PCIEADM_SDO_DID,
+ PCIEADM_SDO_BDF,
+ PCIEADM_SDO_BDF_BUS,
+ PCIEADM_SDO_BDF_DEV,
+ PCIEADM_SDO_BDF_FUNC,
+ PCIEADM_SDO_DRIVER,
+ PCIEADM_SDO_TYPE,
+ PCIEADM_SDO_VENDOR,
+ PCIEADM_SDO_DEVICE,
+ PCIEADM_SDO_PATH,
+ PCIEADM_SDO_MAXSPEED,
+ PCIEADM_SDO_MAXWIDTH,
+ PCIEADM_SDO_CURSPEED,
+ PCIEADM_SDO_CURWIDTH,
+ PCIEADM_SDO_SUPSPEEDS
+} pcieadm_show_devs_otype_t;
+
+typedef struct pcieadm_show_devs_ofmt {
+ int psdo_vid;
+ int psdo_did;
+ uint_t psdo_bus;
+ uint_t psdo_dev;
+ uint_t psdo_func;
+ const char *psdo_path;
+ const char *psdo_vendor;
+ const char *psdo_device;
+ const char *psdo_driver;
+ int psdo_instance;
+ int psdo_mwidth;
+ int psdo_cwidth;
+ int64_t psdo_mspeed;
+ int64_t psdo_cspeed;
+ int psdo_nspeeds;
+ int64_t *psdo_sspeeds;
+} pcieadm_show_devs_ofmt_t;
+
+static uint_t
+pcieadm_speed2gen(int64_t speed)
+{
+ if (speed == 2500000000LL) {
+ return (1);
+ } else if (speed == 5000000000LL) {
+ return (2);
+ } else if (speed == 8000000000LL) {
+ return (3);
+ } else if (speed == 16000000000LL) {
+ return (4);
+ } else if (speed == 32000000000LL) {
+ return (5);
+ } else {
+ return (0);
+ }
+}
+
+static const char *
+pcieadm_speed2str(int64_t speed)
+{
+ if (speed == 2500000000LL) {
+ return ("2.5");
+ } else if (speed == 5000000000LL) {
+ return ("5.0");
+ } else if (speed == 8000000000LL) {
+ return ("8.0");
+ } else if (speed == 16000000000LL) {
+ return ("16.0");
+ } else if (speed == 32000000000LL) {
+ return ("32.0");
+ } else {
+ return (NULL);
+ }
+}
+
+static boolean_t
+pcieadm_show_devs_ofmt_cb(ofmt_arg_t *ofarg, char *buf, uint_t buflen)
+{
+ const char *str;
+ pcieadm_show_devs_ofmt_t *psdo = ofarg->ofmt_cbarg;
+ boolean_t first = B_TRUE;
+
+ switch (ofarg->ofmt_id) {
+ case PCIEADM_SDO_BDF:
+ if (snprintf(buf, buflen, "%x/%x/%x", psdo->psdo_bus,
+ psdo->psdo_dev, psdo->psdo_func) >= buflen) {
+ return (B_FALSE);
+ }
+ break;
+ case PCIEADM_SDO_BDF_BUS:
+ if (snprintf(buf, buflen, "%x", psdo->psdo_bus) >= buflen) {
+ return (B_FALSE);
+ }
+ break;
+ case PCIEADM_SDO_BDF_DEV:
+ if (snprintf(buf, buflen, "%x", psdo->psdo_dev) >= buflen) {
+ return (B_FALSE);
+ }
+ break;
+ case PCIEADM_SDO_BDF_FUNC:
+ if (snprintf(buf, buflen, "%x", psdo->psdo_func) >= buflen) {
+ return (B_FALSE);
+ }
+ break;
+ case PCIEADM_SDO_DRIVER:
+ if (psdo->psdo_driver == NULL || psdo->psdo_instance == -1) {
+ (void) snprintf(buf, buflen, "--");
+ } else if (snprintf(buf, buflen, "%s%d", psdo->psdo_driver,
+ psdo->psdo_instance) >= buflen) {
+ return (B_FALSE);
+ }
+ break;
+ case PCIEADM_SDO_PATH:
+ if (strlcat(buf, psdo->psdo_path, buflen) >= buflen) {
+ return (B_TRUE);
+ }
+ break;
+ case PCIEADM_SDO_VID:
+ if (psdo->psdo_vid == -1) {
+ (void) strlcat(buf, "--", buflen);
+ } else if (snprintf(buf, buflen, "%x", psdo->psdo_vid) >=
+ buflen) {
+ return (B_FALSE);
+ }
+ break;
+ case PCIEADM_SDO_DID:
+ if (psdo->psdo_did == -1) {
+ (void) strlcat(buf, "--", buflen);
+ } else if (snprintf(buf, buflen, "%x", psdo->psdo_did) >=
+ buflen) {
+ return (B_FALSE);
+ }
+ break;
+ case PCIEADM_SDO_VENDOR:
+ if (strlcat(buf, psdo->psdo_vendor, buflen) >= buflen) {
+ return (B_FALSE);
+ }
+ break;
+ case PCIEADM_SDO_DEVICE:
+ if (strlcat(buf, psdo->psdo_device, buflen) >= buflen) {
+ return (B_FALSE);
+ }
+ break;
+ case PCIEADM_SDO_MAXWIDTH:
+ if (psdo->psdo_mwidth <= 0) {
+ (void) strlcat(buf, "--", buflen);
+ } else if (snprintf(buf, buflen, "x%u", psdo->psdo_mwidth) >=
+ buflen) {
+ return (B_FALSE);
+ }
+ break;
+ case PCIEADM_SDO_CURWIDTH:
+ if (psdo->psdo_cwidth <= 0) {
+ (void) strlcat(buf, "--", buflen);
+ } else if (snprintf(buf, buflen, "x%u", psdo->psdo_cwidth) >=
+ buflen) {
+ return (B_FALSE);
+ }
+ break;
+ case PCIEADM_SDO_MAXSPEED:
+ str = pcieadm_speed2str(psdo->psdo_mspeed);
+ if (str == NULL) {
+ (void) strlcat(buf, "--", buflen);
+ } else if (snprintf(buf, buflen, "%s GT/s", str) >= buflen) {
+ return (B_FALSE);
+ }
+ break;
+ case PCIEADM_SDO_CURSPEED:
+ str = pcieadm_speed2str(psdo->psdo_cspeed);
+ if (str == NULL) {
+ (void) strlcat(buf, "--", buflen);
+ } else if (snprintf(buf, buflen, "%s GT/s", str) >= buflen) {
+ return (B_FALSE);
+ }
+ break;
+ case PCIEADM_SDO_SUPSPEEDS:
+ buf[0] = 0;
+ for (int i = 0; i < psdo->psdo_nspeeds; i++) {
+ const char *str;
+
+ str = pcieadm_speed2str(psdo->psdo_sspeeds[i]);
+ if (str == NULL) {
+ continue;
+ }
+
+ if (!first) {
+ if (strlcat(buf, ",", buflen) >= buflen) {
+ return (B_FALSE);
+ }
+ }
+ first = B_FALSE;
+
+ if (strlcat(buf, str, buflen) >= buflen) {
+ return (B_FALSE);
+ }
+ }
+ break;
+ case PCIEADM_SDO_TYPE:
+ if (pcieadm_speed2gen(psdo->psdo_mspeed) == 0 ||
+ psdo->psdo_mwidth == -1) {
+ if (strlcat(buf, "PCI", buflen) >= buflen) {
+ return (B_FALSE);
+ }
+ } else {
+ if (snprintf(buf, buflen, "PCIe Gen %ux%u",
+ pcieadm_speed2gen(psdo->psdo_mspeed),
+ psdo->psdo_mwidth) >= buflen) {
+ return (B_FALSE);
+ }
+ }
+ break;
+ default:
+ abort();
+ }
+ return (B_TRUE);
+}
+
+static const char *pcieadm_show_dev_fields = "bdf,type,driver,device";
+static const char *pcieadm_show_dev_speeds =
+ "bdf,driver,maxspeed,curspeed,maxwidth,curwidth,supspeeds";
+static const ofmt_field_t pcieadm_show_dev_ofmt[] = {
+ { "VID", 6, PCIEADM_SDO_VID, pcieadm_show_devs_ofmt_cb },
+ { "DID", 6, PCIEADM_SDO_DID, pcieadm_show_devs_ofmt_cb },
+ { "BDF", 8, PCIEADM_SDO_BDF, pcieadm_show_devs_ofmt_cb },
+ { "DRIVER", 15, PCIEADM_SDO_DRIVER, pcieadm_show_devs_ofmt_cb },
+ { "TYPE", 15, PCIEADM_SDO_TYPE, pcieadm_show_devs_ofmt_cb },
+ { "VENDOR", 30, PCIEADM_SDO_VENDOR, pcieadm_show_devs_ofmt_cb },
+ { "DEVICE", 30, PCIEADM_SDO_DEVICE, pcieadm_show_devs_ofmt_cb },
+ { "PATH", 30, PCIEADM_SDO_PATH, pcieadm_show_devs_ofmt_cb },
+ { "BUS", 4, PCIEADM_SDO_BDF_BUS, pcieadm_show_devs_ofmt_cb },
+ { "DEV", 4, PCIEADM_SDO_BDF_DEV, pcieadm_show_devs_ofmt_cb },
+ { "FUNC", 4, PCIEADM_SDO_BDF_FUNC, pcieadm_show_devs_ofmt_cb },
+ { "MAXSPEED", 10, PCIEADM_SDO_MAXSPEED, pcieadm_show_devs_ofmt_cb },
+ { "MAXWIDTH", 10, PCIEADM_SDO_MAXWIDTH, pcieadm_show_devs_ofmt_cb },
+ { "CURSPEED", 10, PCIEADM_SDO_CURSPEED, pcieadm_show_devs_ofmt_cb },
+ { "CURWIDTH", 10, PCIEADM_SDO_CURWIDTH, pcieadm_show_devs_ofmt_cb },
+ { "SUPSPEEDS", 20, PCIEADM_SDO_SUPSPEEDS, pcieadm_show_devs_ofmt_cb },
+ { NULL, 0, 0, NULL }
+};
+
+static boolean_t
+pcieadm_show_devs_match(pcieadm_show_devs_t *psd,
+ pcieadm_show_devs_ofmt_t *psdo)
+{
+ char dinst[128], bdf[128];
+
+ if (psd->psd_nfilts == 0) {
+ return (B_TRUE);
+ }
+
+ if (psdo->psdo_driver != NULL && psdo->psdo_instance != -1) {
+ (void) snprintf(dinst, sizeof (dinst), "%s%d",
+ psdo->psdo_driver, psdo->psdo_instance);
+ }
+ (void) snprintf(bdf, sizeof (bdf), "%x/%x/%x", psdo->psdo_bus,
+ psdo->psdo_dev, psdo->psdo_func);
+
+ for (uint_t i = 0; i < psd->psd_nfilts; i++) {
+ const char *filt = psd->psd_filts[i];
+
+ if (strcmp(filt, psdo->psdo_path) == 0) {
+ return (B_TRUE);
+ }
+
+ if (strcmp(filt, bdf) == 0) {
+ return (B_TRUE);
+ }
+
+ if (psdo->psdo_driver != NULL &&
+ strcmp(filt, psdo->psdo_driver) == 0) {
+ return (B_TRUE);
+ }
+
+ if (psdo->psdo_driver != NULL && psdo->psdo_instance != -1 &&
+ strcmp(filt, dinst) == 0) {
+ return (B_TRUE);
+ }
+
+ if (strncmp("/devices", filt, strlen("/devices")) == 0) {
+ filt += strlen("/devices");
+ }
+
+ if (strcmp(filt, psdo->psdo_path) == 0) {
+ return (B_TRUE);
+ }
+ }
+ return (B_FALSE);
+}
+
+static int
+pcieadm_show_devs_walk_cb(di_node_t node, void *arg)
+{
+ int nprop, *regs = NULL, *did, *vid, *mwidth, *cwidth;
+ int64_t *mspeed, *cspeed, *sspeeds;
+ char *path = NULL;
+ pcieadm_show_devs_t *psd = arg;
+ int ret = DI_WALK_CONTINUE;
+ pcieadm_show_devs_ofmt_t oarg;
+ pcidb_hdl_t *pcidb = psd->psd_pia->pia_pcidb;
+
+ bzero(&oarg, sizeof (oarg));
+
+ path = di_devfs_path(node);
+ if (path == NULL) {
+ err(EXIT_FAILURE, "failed to construct devfs path for node: "
+ "%s (%s)", di_node_name(node));
+ }
+
+ nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", &regs);
+ if (nprop <= 0) {
+ errx(EXIT_FAILURE, "failed to lookup regs array for %s",
+ path);
+ }
+
+ oarg.psdo_path = path;
+ oarg.psdo_bus = PCI_REG_BUS_G(regs[0]);
+ oarg.psdo_dev = PCI_REG_DEV_G(regs[0]);
+ oarg.psdo_func = PCI_REG_FUNC_G(regs[0]);
+
+ if (oarg.psdo_func != 0 && !psd->psd_funcs) {
+ goto done;
+ }
+
+ oarg.psdo_driver = di_driver_name(node);
+ oarg.psdo_instance = di_instance(node);
+
+ nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "device-id", &did);
+ if (nprop != 1) {
+ oarg.psdo_did = -1;
+ } else {
+ oarg.psdo_did = (uint16_t)*did;
+ }
+
+ nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "vendor-id", &vid);
+ if (nprop != 1) {
+ oarg.psdo_vid = -1;
+ } else {
+ oarg.psdo_vid = (uint16_t)*vid;
+ }
+
+ oarg.psdo_vendor = "--";
+ if (oarg.psdo_vid != -1) {
+ pcidb_vendor_t *vend = pcidb_lookup_vendor(pcidb,
+ oarg.psdo_vid);
+ if (vend != NULL) {
+ oarg.psdo_vendor = pcidb_vendor_name(vend);
+ }
+ }
+
+ oarg.psdo_device = "--";
+ if (oarg.psdo_vid != -1 && oarg.psdo_did != -1) {
+ pcidb_device_t *dev = pcidb_lookup_device(pcidb,
+ oarg.psdo_vid, oarg.psdo_did);
+ if (dev != NULL) {
+ oarg.psdo_device = pcidb_device_name(dev);
+
+ }
+ }
+
+ nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node,
+ "pcie-link-maximum-width", &mwidth);
+ if (nprop != 1) {
+ oarg.psdo_mwidth = -1;
+ } else {
+ oarg.psdo_mwidth = *mwidth;
+ }
+
+ nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node,
+ "pcie-link-current-width", &cwidth);
+ if (nprop != 1) {
+ oarg.psdo_cwidth = -1;
+ } else {
+ oarg.psdo_cwidth = *cwidth;
+ }
+
+ nprop = di_prop_lookup_int64(DDI_DEV_T_ANY, node,
+ "pcie-link-maximum-speed", &mspeed);
+ if (nprop != 1) {
+ oarg.psdo_mspeed = -1;
+ } else {
+ oarg.psdo_mspeed = *mspeed;
+ }
+
+ nprop = di_prop_lookup_int64(DDI_DEV_T_ANY, node,
+ "pcie-link-current-speed", &cspeed);
+ if (nprop != 1) {
+ oarg.psdo_cspeed = -1;
+ } else {
+ oarg.psdo_cspeed = *cspeed;
+ }
+
+ nprop = di_prop_lookup_int64(DDI_DEV_T_ANY, node,
+ "pcie-link-supported-speeds", &sspeeds);
+ if (nprop > 0) {
+ oarg.psdo_nspeeds = nprop;
+ oarg.psdo_sspeeds = sspeeds;
+ } else {
+ oarg.psdo_nspeeds = 0;
+ oarg.psdo_sspeeds = NULL;
+ }
+
+ if (pcieadm_show_devs_match(psd, &oarg)) {
+ ofmt_print(psd->psd_ofmt, &oarg);
+ psd->psd_nprint++;
+ }
+
+done:
+ if (path != NULL) {
+ di_devfs_path_free(path);
+ }
+
+ return (ret);
+}
+
+void
+pcieadm_show_devs_usage(FILE *f)
+{
+ (void) fprintf(f, "\tshow-devs\t[-F] [-H] [-s | -o field[,...] [-p]] "
+ "[filter...]\n");
+}
+
+static void
+pcieadm_show_devs_help(const char *fmt, ...)
+{
+ if (fmt != NULL) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ vwarnx(fmt, ap);
+ va_end(ap);
+ (void) fprintf(stderr, "\n");
+ }
+
+ (void) fprintf(stderr, "Usage: %s show-devs [-F] [-H] [-s | -o "
+ "field[,...] [-p]] [filter...]\n", pcieadm_progname);
+
+ (void) fprintf(stderr, "\nList PCI devices and functions in the "
+ "system. Each <filter> selects a set\nof devices to show and "
+ "can be a driver name, instance, /devices path, or\nb/d/f.\n\n"
+ "\t-F\t\tdo not display PCI functions\n"
+ "\t-H\t\tomit the column header\n"
+ "\t-o field\toutput fields to print\n"
+ "\t-p\t\tparsable output (requires -o)\n"
+ "\t-s\t\tlist speeds and widths\n");
+
+}
+
+int
+pcieadm_show_devs(pcieadm_t *pcip, int argc, char *argv[])
+{
+ int c;
+ uint_t flags = 0;
+ const char *fields = NULL;
+ pcieadm_show_devs_t psd;
+ pcieadm_di_walk_t walk;
+ ofmt_status_t oferr;
+ boolean_t parse = B_FALSE;
+ boolean_t speeds = B_FALSE;
+
+ /*
+ * show-devs relies solely on the devinfo snapshot we already took.
+ * Formalize our privs immediately.
+ */
+ pcieadm_init_privs(pcip);
+
+ bzero(&psd, sizeof (psd));
+ psd.psd_pia = pcip;
+ psd.psd_funcs = B_TRUE;
+
+ while ((c = getopt(argc, argv, ":FHo:ps")) != -1) {
+ switch (c) {
+ case 'F':
+ psd.psd_funcs = B_FALSE;
+ break;
+ case 'p':
+ parse = B_TRUE;
+ flags |= OFMT_PARSABLE;
+ break;
+ case 'H':
+ flags |= OFMT_NOHEADER;
+ break;
+ case 's':
+ speeds = B_TRUE;
+ break;
+ case 'o':
+ fields = optarg;
+ break;
+ case ':':
+ pcieadm_show_devs_help("option -%c requires an "
+ "argument", optopt);
+ exit(EXIT_USAGE);
+ case '?':
+ pcieadm_show_devs_help("unknown option: -%c", optopt);
+ exit(EXIT_USAGE);
+ }
+ }
+
+ if (parse && fields == NULL) {
+ errx(EXIT_USAGE, "-p requires fields specified with -o");
+ }
+
+ if (fields != NULL && speeds) {
+ errx(EXIT_USAGE, "-s cannot be used with with -o");
+ }
+
+ if (fields == NULL) {
+ if (speeds) {
+ fields = pcieadm_show_dev_speeds;
+ } else {
+ fields = pcieadm_show_dev_fields;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 0) {
+ psd.psd_nfilts = argc;
+ psd.psd_filts = argv;
+ }
+
+ oferr = ofmt_open(fields, pcieadm_show_dev_ofmt, flags, 0,
+ &psd.psd_ofmt);
+ ofmt_check(oferr, parse, psd.psd_ofmt, pcieadm_ofmt_errx, warnx);
+
+ walk.pdw_arg = &psd;
+ walk.pdw_func = pcieadm_show_devs_walk_cb;
+
+ pcieadm_di_walk(pcip, &walk);
+
+ if (psd.psd_nprint > 0) {
+ return (EXIT_SUCCESS);
+ } else {
+ return (EXIT_FAILURE);
+ }
+}
diff --git a/usr/src/cmd/sgs/libld/common/syms.c b/usr/src/cmd/sgs/libld/common/syms.c
index a40f8ae5cd..ff073c9aa7 100644
--- a/usr/src/cmd/sgs/libld/common/syms.c
+++ b/usr/src/cmd/sgs/libld/common/syms.c
@@ -1718,8 +1718,9 @@ ld_sym_validate(Ofl_desc *ofl)
sym->st_name) && (st_insert(ofl->ofl_strtab,
sdp->sd_name) == -1))
return (S_ERROR);
- if (allow_ldynsym && sym->st_name &&
- ldynsym_symtype[type]) {
+ if (allow_ldynsym && ldynsym_symtype[type] &&
+ ((sym->st_name != 0) ||
+ (type == STT_FILE))) {
ofl->ofl_dynscopecnt++;
if (st_insert(ofl->ofl_dynstrtab,
sdp->sd_name) == -1)
@@ -2452,8 +2453,14 @@ ld_sym_process(Is_desc *isc, Ifl_desc *ifl, Ofl_desc *ofl)
sdp->sd_name) == -1))
return (S_ERROR);
- if (allow_ldynsym && sym->st_name &&
- ldynsym_symtype[type]) {
+ /*
+ * STT_FILE symbols must always remain, to
+ * maintain the ordering semantics of symbol
+ * tables.
+ */
+ if (allow_ldynsym && ldynsym_symtype[type] &&
+ ((sym->st_name != 0) ||
+ (type == STT_FILE))) {
ofl->ofl_dynlocscnt++;
if (st_insert(ofl->ofl_dynstrtab,
sdp->sd_name) == -1)
diff --git a/usr/src/cmd/sgs/libld/common/update.c b/usr/src/cmd/sgs/libld/common/update.c
index d61d4cfcd5..8ef1be8343 100644
--- a/usr/src/cmd/sgs/libld/common/update.c
+++ b/usr/src/cmd/sgs/libld/common/update.c
@@ -702,9 +702,11 @@ update_osym(Ofl_desc *ofl)
enter_in_symtab = symtab &&
(!(ofl->ofl_flags & FLG_OF_REDLSYM) ||
sdp->sd_move);
- enter_in_ldynsym = ldynsym && sdp->sd_name &&
+ enter_in_ldynsym = ldynsym &&
+ ((sym->st_name != 0) || (type == STT_FILE)) &&
ldynsym_symtype[type] &&
!(ofl->ofl_flags & FLG_OF_REDLSYM);
+
_symshndx = NULL;
if (enter_in_symtab) {
diff --git a/usr/src/cmd/sgs/tools/SUNWonld-README b/usr/src/cmd/sgs/tools/SUNWonld-README
index a414a6bfe8..d29de0aa81 100644
--- a/usr/src/cmd/sgs/tools/SUNWonld-README
+++ b/usr/src/cmd/sgs/tools/SUNWonld-README
@@ -1670,3 +1670,4 @@ Bugid Risk Synopsis
11057 hidden undefined weak symbols should not leave relocations
11067 debug statistics crash ld(1) when -z allextract
13481 ld(1) should skip GCC local aliases when building symsort sections
+13684 ld aborts when input object has no file name
diff --git a/usr/src/pkg/manifests/diagnostic-pci.mf b/usr/src/pkg/manifests/diagnostic-pci.mf
index c666862ebd..a2a1610726 100644
--- a/usr/src/pkg/manifests/diagnostic-pci.mf
+++ b/usr/src/pkg/manifests/diagnostic-pci.mf
@@ -23,4 +23,5 @@ dir path=usr group=sys
dir path=usr/lib
dir path=usr/lib/pci
file path=usr/lib/pci/pcidb mode=0555
+file path=usr/lib/pci/pcieadm 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 963821dff2..914154281f 100644
--- a/usr/src/pkg/manifests/system-test-utiltest.mf
+++ b/usr/src/pkg/manifests/system-test-utiltest.mf
@@ -77,6 +77,7 @@ dir path=opt/util-tests/tests/mdb/format
dir path=opt/util-tests/tests/mdb/options
dir path=opt/util-tests/tests/mdb/typedef
dir path=opt/util-tests/tests/mergeq
+dir path=opt/util-tests/tests/pci
dir path=opt/util-tests/tests/sed
dir path=opt/util-tests/tests/sed/regress.multitest.out
dir path=opt/util-tests/tests/sleep
@@ -1686,7 +1687,23 @@ 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/pci/bridge-efilt-p.out mode=0444
+file path=opt/util-tests/tests/pci/bridge-efilt.out mode=0444
+file path=opt/util-tests/tests/pci/bridge-ht-p.out mode=0444
+file path=opt/util-tests/tests/pci/bridge-ht.msi-p.out mode=0444
+file path=opt/util-tests/tests/pci/bridge-ht.msi.command-p.out mode=0444
+file path=opt/util-tests/tests/pci/bridge-ht.out mode=0444
+file path=opt/util-tests/tests/pci/bridge.pci mode=0444
+file path=opt/util-tests/tests/pci/header0-basic-L.out mode=0444
+file path=opt/util-tests/tests/pci/header0-basic-LH.out mode=0444
+file path=opt/util-tests/tests/pci/header0-basic-n.out mode=0444
+file path=opt/util-tests/tests/pci/header0-basic.out mode=0444
+file path=opt/util-tests/tests/pci/header0-parse.out mode=0444
+file path=opt/util-tests/tests/pci/igb-ltr-p.out mode=0444
+file path=opt/util-tests/tests/pci/igb-ltr.out mode=0444
+file path=opt/util-tests/tests/pci/igb.pci mode=0444
file path=opt/util-tests/tests/pcidbtest mode=0555
+file path=opt/util-tests/tests/pcieadmtest 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
diff --git a/usr/src/test/util-tests/runfiles/default.run b/usr/src/test/util-tests/runfiles/default.run
index c4ea9ed8e1..9b8ba57fbf 100644
--- a/usr/src/test/util-tests/runfiles/default.run
+++ b/usr/src/test/util-tests/runfiles/default.run
@@ -88,3 +88,4 @@ tests = ['custr_remove', 'custr_trunc']
tests = ['sed_addr', 'multi_test']
[/opt/util-tests/tests/pcidbtest]
+[/opt/util-tests/tests/pcieadmtest]
diff --git a/usr/src/test/util-tests/tests/Makefile b/usr/src/test/util-tests/tests/Makefile
index 77a20d6059..1d73f5dc94 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 += bunyan libcustr find mdb sed head pcidb
+SUBDIRS += bunyan libcustr find mdb sed head pcidb pcieadm
include $(SRC)/test/Makefile.com
diff --git a/usr/src/test/util-tests/tests/pcieadm/Makefile b/usr/src/test/util-tests/tests/pcieadm/Makefile
new file mode 100644
index 0000000000..59a995f0ea
--- /dev/null
+++ b/usr/src/test/util-tests/tests/pcieadm/Makefile
@@ -0,0 +1,59 @@
+#
+# 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
+ROOTOPTPCI = $(ROOT)/opt/util-tests/tests/pci
+PROG = pcieadmtest
+DATAFILES = bridge.pci igb.pci \
+ header0-basic.out \
+ header0-basic-L.out \
+ header0-basic-LH.out \
+ header0-basic-n.out \
+ header0-parse.out \
+ igb-ltr.out \
+ igb-ltr-p.out \
+ bridge-ht.out \
+ bridge-ht-p.out \
+ bridge-ht.msi-p.out \
+ bridge-ht.msi.command-p.out \
+ bridge-efilt.out \
+ bridge-efilt-p.out
+
+ROOTPROG = $(PROG:%=$(ROOTOPTPKG)/%)
+ROOTDATA = $(DATAFILES:%=$(ROOTOPTPCI)/%)
+$(ROOTDATA) := FILEMODE = 0444
+
+all:
+
+install: $(ROOTDATA) $(ROOTPROG)
+
+clobber: clean
+
+clean:
+
+$(ROOTOPTPKG):
+ $(INS.dir)
+
+$(ROOTOPTPCI): $(ROOTOPTPKG)
+ $(INS.dir)
+
+$(ROOTOPTPCI)/%: % $(ROOTOPTPCI)
+ $(INS.file)
+
+$(ROOTOPTPKG)/%: %.ksh $(ROOTOPTPKG)
+ $(INS.rename)
diff --git a/usr/src/test/util-tests/tests/pcieadm/bridge-efilt-p.out b/usr/src/test/util-tests/tests/pcieadm/bridge-efilt-p.out
new file mode 100644
index 0000000000..63e630294d
--- /dev/null
+++ b/usr/src/test/util-tests/tests/pcieadm/bridge-efilt-p.out
@@ -0,0 +1,2 @@
+pcie.linksts:0x7043
+pcieadm: filter 'atelier' did not match any fields
diff --git a/usr/src/test/util-tests/tests/pcieadm/bridge-efilt.out b/usr/src/test/util-tests/tests/pcieadm/bridge-efilt.out
new file mode 100644
index 0000000000..f3b2bf7680
--- /dev/null
+++ b/usr/src/test/util-tests/tests/pcieadm/bridge-efilt.out
@@ -0,0 +1,10 @@
+pcieadm: filter 'atelier' did not match any fields
+PCI Express Capability (0x10)
+ Link Status: 0x7043
+ |--> Link Speed: 8.0 GT/s (0x3)
+ |--> Link Width: 0x4
+ |--> Link Training: no (0x0)
+ |--> Slot Clock Configuration: common (0x1000)
+ |--> Data Link Layer Link Active: yes (0x2000)
+ |--> Link Bandwidth Management Status: change occurred (0x4000)
+ |--> Link Autonomous Bandwidth Status: no change (0x0)
diff --git a/usr/src/test/util-tests/tests/pcieadm/bridge-ht-p.out b/usr/src/test/util-tests/tests/pcieadm/bridge-ht-p.out
new file mode 100644
index 0000000000..09ead24746
--- /dev/null
+++ b/usr/src/test/util-tests/tests/pcieadm/bridge-ht-p.out
@@ -0,0 +1 @@
+pcieadm: filter 'ht' did not match any fields
diff --git a/usr/src/test/util-tests/tests/pcieadm/bridge-ht.msi-p.out b/usr/src/test/util-tests/tests/pcieadm/bridge-ht.msi-p.out
new file mode 100644
index 0000000000..2cd1fe59bf
--- /dev/null
+++ b/usr/src/test/util-tests/tests/pcieadm/bridge-ht.msi-p.out
@@ -0,0 +1 @@
+pcieadm: filter 'ht.msi' did not match any fields
diff --git a/usr/src/test/util-tests/tests/pcieadm/bridge-ht.msi.command-p.out b/usr/src/test/util-tests/tests/pcieadm/bridge-ht.msi.command-p.out
new file mode 100644
index 0000000000..095e12d8a1
--- /dev/null
+++ b/usr/src/test/util-tests/tests/pcieadm/bridge-ht.msi.command-p.out
@@ -0,0 +1 @@
+0xa803:ht.msi.command
diff --git a/usr/src/test/util-tests/tests/pcieadm/bridge-ht.out b/usr/src/test/util-tests/tests/pcieadm/bridge-ht.out
new file mode 100644
index 0000000000..ba2899a347
--- /dev/null
+++ b/usr/src/test/util-tests/tests/pcieadm/bridge-ht.out
@@ -0,0 +1,4 @@
+HyperTransport Capability - MSI Mapping (0x8)
+ Command: 0xa803
+ |--> Enable: enabled (0x1)
+ |--> Fixed: enabled (0x2)
diff --git a/usr/src/test/util-tests/tests/pcieadm/bridge.pci b/usr/src/test/util-tests/tests/pcieadm/bridge.pci
new file mode 100644
index 0000000000..f85ffa4f6e
--- /dev/null
+++ b/usr/src/test/util-tests/tests/pcieadm/bridge.pci
Binary files differ
diff --git a/usr/src/test/util-tests/tests/pcieadm/header0-basic-L.out b/usr/src/test/util-tests/tests/pcieadm/header0-basic-L.out
new file mode 100644
index 0000000000..c52c00d5cc
--- /dev/null
+++ b/usr/src/test/util-tests/tests/pcieadm/header0-basic-L.out
@@ -0,0 +1,3 @@
+SHORT HUMAN
+header0.vendor Vendor ID
+header0.device Device ID
diff --git a/usr/src/test/util-tests/tests/pcieadm/header0-basic-LH.out b/usr/src/test/util-tests/tests/pcieadm/header0-basic-LH.out
new file mode 100644
index 0000000000..5dda4c444b
--- /dev/null
+++ b/usr/src/test/util-tests/tests/pcieadm/header0-basic-LH.out
@@ -0,0 +1,2 @@
+header0.vendor Vendor ID
+header0.device Device ID
diff --git a/usr/src/test/util-tests/tests/pcieadm/header0-basic-n.out b/usr/src/test/util-tests/tests/pcieadm/header0-basic-n.out
new file mode 100644
index 0000000000..874bb0db55
--- /dev/null
+++ b/usr/src/test/util-tests/tests/pcieadm/header0-basic-n.out
@@ -0,0 +1,3 @@
+Device /dev/stdin -- Type 0 Header (header0)
+ Vendor ID (header0.vendor): 0x8086 -- Intel Corporation
+ Device ID (header0.device): 0x1521 -- I350 Gigabit Network Connection
diff --git a/usr/src/test/util-tests/tests/pcieadm/header0-basic.out b/usr/src/test/util-tests/tests/pcieadm/header0-basic.out
new file mode 100644
index 0000000000..93e067f828
--- /dev/null
+++ b/usr/src/test/util-tests/tests/pcieadm/header0-basic.out
@@ -0,0 +1,3 @@
+Device /dev/stdin -- Type 0 Header
+ Vendor ID: 0x8086 -- Intel Corporation
+ Device ID: 0x1521 -- I350 Gigabit Network Connection
diff --git a/usr/src/test/util-tests/tests/pcieadm/header0-parse.out b/usr/src/test/util-tests/tests/pcieadm/header0-parse.out
new file mode 100644
index 0000000000..d56cf26062
--- /dev/null
+++ b/usr/src/test/util-tests/tests/pcieadm/header0-parse.out
@@ -0,0 +1,2 @@
+header0.vendor:0x8086
+header0.device:0x1521
diff --git a/usr/src/test/util-tests/tests/pcieadm/igb-ltr-p.out b/usr/src/test/util-tests/tests/pcieadm/igb-ltr-p.out
new file mode 100644
index 0000000000..c6a9d1bce0
--- /dev/null
+++ b/usr/src/test/util-tests/tests/pcieadm/igb-ltr-p.out
@@ -0,0 +1 @@
+pcieadm: filter 'ltr' did not match any fields
diff --git a/usr/src/test/util-tests/tests/pcieadm/igb-ltr.out b/usr/src/test/util-tests/tests/pcieadm/igb-ltr.out
new file mode 100644
index 0000000000..f6a4f26cbd
--- /dev/null
+++ b/usr/src/test/util-tests/tests/pcieadm/igb-ltr.out
@@ -0,0 +1,11 @@
+Latency Tolerance Reporting Capability (0x18)
+ Capability Header: 0x1d010018
+ |--> Capability ID: 0x18
+ |--> Capability Version: 0x1
+ |--> Next Capability Offset: 0x1d0
+ Max Snoop Latency: 0x0
+ |--> Latency Value: 0x0
+ |--> Latency Scale: 1 ns (0x0)
+ Max No-Snoop Latency: 0x0
+ |--> Latency Value: 0x0
+ |--> Latency Scale: 1 ns (0x0)
diff --git a/usr/src/test/util-tests/tests/pcieadm/igb.pci b/usr/src/test/util-tests/tests/pcieadm/igb.pci
new file mode 100644
index 0000000000..bcee63f32d
--- /dev/null
+++ b/usr/src/test/util-tests/tests/pcieadm/igb.pci
Binary files differ
diff --git a/usr/src/test/util-tests/tests/pcieadm/pcieadmtest.ksh b/usr/src/test/util-tests/tests/pcieadm/pcieadmtest.ksh
new file mode 100644
index 0000000000..d68cf0b7cc
--- /dev/null
+++ b/usr/src/test/util-tests/tests/pcieadm/pcieadmtest.ksh
@@ -0,0 +1,160 @@
+#!/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
+
+pcieadm_arg0="$(basename $0)"
+pcieadm_prog="/usr/lib/pci/pcieadm"
+pcieadm_data="$(dirname $0)/pci"
+pcieadm_exit=0
+pcieadm_tmpfile="/tmp/pcieadmtest.$$"
+
+warn()
+{
+ typeset msg="$*"
+ [[ -z "$msg" ]] && msg="failed"
+ echo "TEST FAILED: $pcieadm_arg0: $msg" >&2
+ pcieadm_exit=1
+}
+
+pcieadm_bad_args()
+{
+ if $pcieadm_prog $@ 2>/dev/null 1>/dev/null; then
+ warn "should have failed with args "$@", but passed"
+ return
+ fi
+
+ printf "TEST PASSED: invalid arguments %s\n" "$*"
+}
+
+pcieadm_validate_output()
+{
+ typeset input="$pcieadm_data/$1"
+ shift
+ typeset outfile="$pcieadm_data/$1"
+ shift
+ typeset expexit=$1
+ shift
+
+ $pcieadm_prog $@ <$input >"$pcieadm_tmpfile" 2>&1
+ if (( $? != expexit)); then
+ warn "$@: mismatched exit status, found $?, expected $expexit"
+ fi
+
+ if ! diff $outfile $pcieadm_tmpfile; then
+ warn "$@: output mismatched"
+ else
+ printf "TEST PASSED: %s\n" "$*"
+ fi
+}
+
+
+if [[ -n $PCIEADM ]]; then
+ pcieadm_prog=$PCIEADM
+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
+
+if [[ ! -d $pcieadm_data ]]; then
+ printf "failed to find data directory %s\n" "$pcieadm_data" >&2
+ exit 1
+fi
+
+#
+# First work through bad options.
+#
+pcieadm_bad_args
+pcieadm_bad_args -d
+pcieadm_bad_args foobar
+pcieadm_bad_args save-cfgspace
+pcieadm_bad_args save-cfgspace -a
+pcieadm_bad_args save-cfgspace -d
+pcieadm_bad_args save-cfgspace -d final
+pcieadm_bad_args save-cfgspace -a -d fantasy
+pcieadm_bad_args show-devs -h
+pcieadm_bad_args show-devs -p
+pcieadm_bad_args show-devs -s -o
+pcieadm_bad_args show-cfgspace
+pcieadm_bad_args show-cfgspace -d -H
+pcieadm_bad_args show-cfgspace -d
+pcieadm_bad_args show-cfgspace -f
+pcieadm_bad_args show-cfgspace -h
+pcieadm_bad_args show-cfgspace -L
+pcieadm_bad_args show-cfgspace -L -n -f "$pcieadm_data/igb.pci"
+pcieadm_bad_args show-cfgspace -L -p -f "$pcieadm_data/igb.pci"
+pcieadm_bad_args show-cfgspace -p -f "$pcieadm_data/igb.pci"
+pcieadm_bad_args show-cfgspace -o foo -f "$pcieadm_data/igb.pci"
+pcieadm_bad_args show-cfgspace -L -o foo -f "$pcieadm_data/igb.pci"
+
+#
+# Test different output cases
+#
+pcieadm_validate_output igb.pci header0-basic.out 0 \
+ show-cfgspace -f /dev/stdin header0.vendor header0.device
+pcieadm_validate_output igb.pci header0-basic-L.out 0 \
+ show-cfgspace -L -f /dev/stdin header0.vendor header0.device
+pcieadm_validate_output igb.pci header0-basic-n.out 0 \
+ show-cfgspace -n -f /dev/stdin header0.vendor header0.device
+pcieadm_validate_output igb.pci header0-basic-LH.out 0 \
+ show-cfgspace -L -H -f /dev/stdin header0.vendor header0.device
+
+#
+# Specific filter behavior. We want to validate the following:
+#
+# o An inexact filter (e.g. a cap or subcap) matches in human mode,
+# but not parsable.
+# o An exact filter will show its contents in human mode, but not
+# parsable.
+# o A missing filter causes to exit non-zero, but still show what we
+# found with other filters or because of a prefix match.
+#
+pcieadm_validate_output igb.pci igb-ltr.out 0 \
+ show-cfgspace -f /dev/stdin ltr
+pcieadm_validate_output igb.pci igb-ltr-p.out 1 \
+ show-cfgspace -p -o short,value -f /dev/stdin ltr
+pcieadm_validate_output igb.pci header0-parse.out 0 \
+ show-cfgspace -p -o short,value -f /dev/stdin header0.vendor header0.device
+pcieadm_validate_output bridge.pci bridge-ht.out 0 \
+ show-cfgspace -f /dev/stdin ht
+pcieadm_validate_output bridge.pci bridge-ht.out 0 \
+ show-cfgspace -f /dev/stdin ht.msi
+pcieadm_validate_output bridge.pci bridge-ht.out 0 \
+ show-cfgspace -f /dev/stdin ht.msi.command
+pcieadm_validate_output bridge.pci bridge-ht-p.out 1 \
+ show-cfgspace -p -o value,short -f /dev/stdin ht
+pcieadm_validate_output bridge.pci bridge-ht.msi-p.out 1 \
+ show-cfgspace -p -o value,short -f /dev/stdin ht.msi
+pcieadm_validate_output bridge.pci bridge-ht.msi.command-p.out 0 \
+ show-cfgspace -p -o value,short -f /dev/stdin ht.msi.command
+pcieadm_validate_output bridge.pci bridge-efilt.out 1 \
+ show-cfgspace -f /dev/stdin pcie.linksts atelier
+pcieadm_validate_output bridge.pci bridge-efilt-p.out 1 \
+ show-cfgspace -p -o short,value -f /dev/stdin pcie.linksts atelier
+
+if (( pcieadm_exit == 0 )); then
+ printf "All tests passed successfully!\n"
+fi
+
+rm -f "$pcieadm_tmpfile"
+exit $pcieadm_exit
diff --git a/usr/src/uts/common/io/mlxcx/mlxcx.c b/usr/src/uts/common/io/mlxcx/mlxcx.c
index 9aae5244de..f74d093b9c 100644
--- a/usr/src/uts/common/io/mlxcx/mlxcx.c
+++ b/usr/src/uts/common/io/mlxcx/mlxcx.c
@@ -10,7 +10,7 @@
*/
/*
- * Copyright 2020, The University of Queensland
+ * Copyright 2021, The University of Queensland
* Copyright (c) 2018, Joyent, Inc.
* Copyright 2020 RackTop Systems, Inc.
*/
@@ -2365,12 +2365,28 @@ mlxcx_setup_eq(mlxcx_t *mlxp, uint_t vec, uint64_t events)
return (B_FALSE);
}
mleq->mleq_state |= MLXCX_EQ_INTR_ENABLED;
+ mleq->mleq_state |= MLXCX_EQ_ATTACHING;
mlxcx_arm_eq(mlxp, mleq);
mutex_exit(&mleq->mleq_mtx);
return (B_TRUE);
}
+static void
+mlxcx_eq_set_attached(mlxcx_t *mlxp)
+{
+ uint_t vec;
+ mlxcx_event_queue_t *mleq;
+
+ for (vec = 0; vec < mlxp->mlx_intr_count; ++vec) {
+ mleq = &mlxp->mlx_eqs[vec];
+
+ mutex_enter(&mleq->mleq_mtx);
+ mleq->mleq_state &= ~MLXCX_EQ_ATTACHING;
+ mutex_exit(&mleq->mleq_mtx);
+ }
+}
+
static boolean_t
mlxcx_setup_async_eqs(mlxcx_t *mlxp)
{
@@ -2764,7 +2780,10 @@ mlxcx_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
* Set up asynchronous event queue which handles control type events
* like PAGE_REQUEST and CMD completion events.
*
- * This will enable and arm the interrupt on EQ 0.
+ * This will enable and arm the interrupt on EQ 0. Note that only page
+ * reqs and cmd completions will be handled until we call
+ * mlxcx_eq_set_attached further down (this way we don't need an extra
+ * set of locks over the mlxcx_t sub-structs not allocated yet)
*/
if (!mlxcx_setup_async_eqs(mlxp)) {
goto err;
@@ -2891,6 +2910,12 @@ mlxcx_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
}
mlxp->mlx_attach |= MLXCX_ATTACH_MAC_HDL;
+ /*
+ * This tells the interrupt handlers they can start processing events
+ * other than cmd completions and page requests.
+ */
+ mlxcx_eq_set_attached(mlxp);
+
return (DDI_SUCCESS);
err:
diff --git a/usr/src/uts/common/io/mlxcx/mlxcx.h b/usr/src/uts/common/io/mlxcx/mlxcx.h
index e28fe89806..68da65765f 100644
--- a/usr/src/uts/common/io/mlxcx/mlxcx.h
+++ b/usr/src/uts/common/io/mlxcx/mlxcx.h
@@ -10,7 +10,7 @@
*/
/*
- * Copyright 2020, The University of Queensland
+ * Copyright 2021, The University of Queensland
* Copyright (c) 2018, Joyent, Inc.
* Copyright 2020 RackTop Systems, Inc.
*/
@@ -318,6 +318,7 @@ typedef enum {
MLXCX_EQ_INTR_ENABLED = 1 << 5, /* ddi_intr_enable()'d */
MLXCX_EQ_INTR_ACTIVE = 1 << 6, /* 'rupt handler running */
MLXCX_EQ_INTR_QUIESCE = 1 << 7, /* 'rupt handler to quiesce */
+ MLXCX_EQ_ATTACHING = 1 << 8, /* mlxcx_attach still running */
} mlxcx_eventq_state_t;
typedef struct mlxcx_bf {
diff --git a/usr/src/uts/common/io/mlxcx/mlxcx_gld.c b/usr/src/uts/common/io/mlxcx/mlxcx_gld.c
index 941eb0f9e7..2c41f4ddeb 100644
--- a/usr/src/uts/common/io/mlxcx/mlxcx_gld.c
+++ b/usr/src/uts/common/io/mlxcx/mlxcx_gld.c
@@ -10,7 +10,7 @@
*/
/*
- * Copyright (c) 2020, the University of Queensland
+ * Copyright (c) 2021, the University of Queensland
* Copyright 2020 RackTop Systems, Inc.
*/
@@ -1493,6 +1493,8 @@ mlxcx_register_mac(mlxcx_t *mlxp)
VERIFY3U(mlxp->mlx_nports, ==, 1);
port = &mlxp->mlx_ports[0];
+ mutex_enter(&port->mlp_mtx);
+
mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
mac->m_driver = mlxp;
mac->m_dip = mlxp->mlx_dip;
@@ -1510,6 +1512,8 @@ mlxcx_register_mac(mlxcx_t *mlxp)
}
mac_free(mac);
+ mutex_exit(&port->mlp_mtx);
+
mlxcx_update_link_state(mlxp, port);
return (ret == 0);
diff --git a/usr/src/uts/common/io/mlxcx/mlxcx_intr.c b/usr/src/uts/common/io/mlxcx/mlxcx_intr.c
index 53ea4d683e..e2f5141171 100644
--- a/usr/src/uts/common/io/mlxcx/mlxcx_intr.c
+++ b/usr/src/uts/common/io/mlxcx/mlxcx_intr.c
@@ -10,7 +10,7 @@
*/
/*
- * Copyright (c) 2020, the University of Queensland
+ * Copyright (c) 2021, the University of Queensland
* Copyright 2020 RackTop Systems, Inc.
* Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
*/
@@ -422,7 +422,9 @@ mlxcx_update_link_state(mlxcx_t *mlxp, mlxcx_port_t *port)
default:
ls = LINK_STATE_UNKNOWN;
}
- mac_link_update(mlxp->mlx_mac_hdl, ls);
+
+ if (mlxp->mlx_mac_hdl != NULL)
+ mac_link_update(mlxp->mlx_mac_hdl, ls);
mutex_exit(&port->mlp_mtx);
}
@@ -762,10 +764,16 @@ mlxcx_intr_async(caddr_t arg, caddr_t arg2)
DTRACE_PROBE2(event, mlxcx_t *, mlxp, mlxcx_eventq_ent_t *,
ent);
+ /*
+ * Handle events which can be processed while we're still in
+ * mlxcx_attach(). Everything on the mlxcx_t which these events
+ * use must be allocated and set up prior to the call to
+ * mlxcx_setup_async_eqs().
+ */
switch (ent->mleqe_event_type) {
case MLXCX_EVENT_CMD_COMPLETION:
mlxcx_cmd_completion(mlxp, ent);
- break;
+ continue;
case MLXCX_EVENT_PAGE_REQUEST:
func = from_be16(ent->mleqe_page_request.
mled_page_request_function_id);
@@ -783,7 +791,7 @@ mlxcx_intr_async(caddr_t arg, caddr_t arg2)
mutex_exit(&param->mla_mtx);
mlxcx_warn(mlxp, "Unexpected page request "
"whilst another is pending");
- break;
+ continue;
}
param->mla_pages.mlp_npages =
(int32_t)from_be32(ent->mleqe_page_request.
@@ -795,7 +803,20 @@ mlxcx_intr_async(caddr_t arg, caddr_t arg2)
taskq_dispatch_ent(mlxp->mlx_async_tq, mlxcx_pages_task,
param, 0, &param->mla_tqe);
- break;
+ continue;
+ }
+
+ /*
+ * All other events should be ignored while in attach.
+ */
+ mutex_enter(&mleq->mleq_mtx);
+ if (mleq->mleq_state & MLXCX_EQ_ATTACHING) {
+ mutex_exit(&mleq->mleq_mtx);
+ continue;
+ }
+ mutex_exit(&mleq->mleq_mtx);
+
+ switch (ent->mleqe_event_type) {
case MLXCX_EVENT_PORT_STATE:
portn = get_bits8(
ent->mleqe_port_state.mled_port_state_port_num,
@@ -1055,17 +1076,30 @@ mlxcx_intr_n(caddr_t arg, caddr_t arg2)
}
mleq->mleq_badintrs = 0;
+ mutex_enter(&mleq->mleq_mtx);
ASSERT(mleq->mleq_state & MLXCX_EQ_ARMED);
mleq->mleq_state &= ~MLXCX_EQ_ARMED;
+#if defined(DEBUG)
+ /*
+ * If we're still in mlxcx_attach and an intr_n fired, something really
+ * weird is going on. This shouldn't happen in the absence of a driver
+ * or firmware bug, so in the interests of minimizing branches in this
+ * function this check is under DEBUG.
+ */
+ if (mleq->mleq_state & MLXCX_EQ_ATTACHING) {
+ mutex_exit(&mleq->mleq_mtx);
+ mlxcx_warn(mlxp, "intr_n (%u) fired during attach, disabling "
+ "vector", mleq->mleq_intr_index);
+ mlxcx_fm_ereport(mlxp, DDI_FM_DEVICE_INVAL_STATE);
+ ddi_fm_service_impact(mlxp->mlx_dip, DDI_SERVICE_LOST);
+ (void) ddi_intr_disable(mlxp->mlx_intr_handles[
+ mleq->mleq_intr_index]);
+ goto done;
+ }
+#endif
+ mutex_exit(&mleq->mleq_mtx);
for (; ent != NULL; ent = mlxcx_eq_next(mleq)) {
- if (ent->mleqe_event_type != MLXCX_EVENT_COMPLETION) {
- mlxcx_fm_ereport(mlxp, DDI_FM_DEVICE_INVAL_STATE);
- ddi_fm_service_impact(mlxp->mlx_dip, DDI_SERVICE_LOST);
- (void) ddi_intr_disable(mlxp->mlx_intr_handles[
- mleq->mleq_intr_index]);
- goto done;
- }
ASSERT3U(ent->mleqe_event_type, ==, MLXCX_EVENT_COMPLETION);
probe.mlcq_num =
@@ -1075,7 +1109,7 @@ mlxcx_intr_n(caddr_t arg, caddr_t arg2)
mutex_exit(&mleq->mleq_mtx);
if (mlcq == NULL)
- continue;
+ goto update_eq;
mlwq = mlcq->mlcq_wq;
diff --git a/usr/src/uts/common/io/nvme/nvme.c b/usr/src/uts/common/io/nvme/nvme.c
index 89debf9b07..8215adaed6 100644
--- a/usr/src/uts/common/io/nvme/nvme.c
+++ b/usr/src/uts/common/io/nvme/nvme.c
@@ -1103,6 +1103,22 @@ nvme_submit_cmd_common(nvme_qpair_t *qp, nvme_cmd_t *cmd)
cmd->nc_completed = B_FALSE;
/*
+ * Now that we hold the queue pair lock, we must check whether or not
+ * the controller has been listed as dead (e.g. was removed due to
+ * hotplug). This is necessary as otherwise we could race with
+ * nvme_remove_callback(). Because this has not been enqueued, we don't
+ * call nvme_unqueue_cmd(), which is why we must manually decrement the
+ * semaphore.
+ */
+ if (cmd->nc_nvme->n_dead) {
+ taskq_dispatch_ent(qp->nq_cq->ncq_cmd_taskq, cmd->nc_callback,
+ cmd, TQ_NOSLEEP, &cmd->nc_tqent);
+ sema_v(&qp->nq_sema);
+ mutex_exit(&qp->nq_mutex);
+ return;
+ }
+
+ /*
* Try to insert the cmd into the active cmd array at the nq_next_cmd
* slot. If the slot is already occupied advance to the next slot and
* try again. This can happen for long running commands like async event
diff --git a/usr/src/uts/common/io/pciex/pcie.c b/usr/src/uts/common/io/pciex/pcie.c
index 35a0190be7..df6b2d189b 100644
--- a/usr/src/uts/common/io/pciex/pcie.c
+++ b/usr/src/uts/common/io/pciex/pcie.c
@@ -22,6 +22,7 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2019 Joyent, Inc.
+ * Copyright 2021 Oxide Computer Company
*/
#include <sys/sysmacros.h>
@@ -1189,11 +1190,18 @@ pcie_capture_speeds(dev_info_t *dip)
uint16_t vers, status;
uint32_t cap, cap2, ctl2;
pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
+ dev_info_t *rcdip;
if (!PCIE_IS_PCIE(bus_p))
return;
- vers = PCIE_CAP_GET(16, bus_p, PCIE_PCIECAP);
+ rcdip = pcie_get_rc_dip(dip);
+ if (bus_p->bus_cfg_hdl == NULL) {
+ vers = pci_cfgacc_get16(rcdip, bus_p->bus_bdf,
+ bus_p->bus_pcie_off + PCIE_PCIECAP);
+ } else {
+ vers = PCIE_CAP_GET(16, bus_p, PCIE_PCIECAP);
+ }
if (vers == PCI_EINVAL16)
return;
vers &= PCIE_PCIECAP_VER_MASK;
@@ -1207,10 +1215,17 @@ pcie_capture_speeds(dev_info_t *dip)
ctl2 = 0;
break;
case PCIE_PCIECAP_VER_2_0:
- cap2 = PCIE_CAP_GET(32, bus_p, PCIE_LINKCAP2);
+ if (bus_p->bus_cfg_hdl == NULL) {
+ cap2 = pci_cfgacc_get32(rcdip, bus_p->bus_bdf,
+ bus_p->bus_pcie_off + PCIE_LINKCAP2);
+ ctl2 = pci_cfgacc_get16(rcdip, bus_p->bus_bdf,
+ bus_p->bus_pcie_off + PCIE_LINKCTL2);
+ } else {
+ cap2 = PCIE_CAP_GET(32, bus_p, PCIE_LINKCAP2);
+ ctl2 = PCIE_CAP_GET(16, bus_p, PCIE_LINKCTL2);
+ }
if (cap2 == PCI_EINVAL32)
cap2 = 0;
- ctl2 = PCIE_CAP_GET(16, bus_p, PCIE_LINKCTL2);
if (ctl2 == PCI_EINVAL16)
ctl2 = 0;
break;
@@ -1219,8 +1234,15 @@ pcie_capture_speeds(dev_info_t *dip)
return;
}
- status = PCIE_CAP_GET(16, bus_p, PCIE_LINKSTS);
- cap = PCIE_CAP_GET(32, bus_p, PCIE_LINKCAP);
+ if (bus_p->bus_cfg_hdl == NULL) {
+ status = pci_cfgacc_get16(rcdip, bus_p->bus_bdf,
+ bus_p->bus_pcie_off + PCIE_LINKSTS);
+ cap = pci_cfgacc_get32(rcdip, bus_p->bus_bdf,
+ bus_p->bus_pcie_off + PCIE_LINKCAP);
+ } else {
+ status = PCIE_CAP_GET(16, bus_p, PCIE_LINKSTS);
+ cap = PCIE_CAP_GET(32, bus_p, PCIE_LINKCAP);
+ }
if (status == PCI_EINVAL16 || cap == PCI_EINVAL32)
return;
@@ -1659,6 +1681,8 @@ initial_done:
pcie_init_plat(dip);
+ pcie_capture_speeds(dip);
+
final_done:
PCIE_DBG("Add %s(dip 0x%p, bdf 0x%x, secbus 0x%x)\n",
diff --git a/usr/src/uts/common/io/vioif/vioif.c b/usr/src/uts/common/io/vioif/vioif.c
index 822dbfa8b7..368af5381d 100644
--- a/usr/src/uts/common/io/vioif/vioif.c
+++ b/usr/src/uts/common/io/vioif/vioif.c
@@ -765,7 +765,17 @@ vioif_m_setpromisc(void *arg, boolean_t on)
uint8_t val = on ? 1 : 0;
if (!vif->vif_has_ctrlq_rx) {
- return (ENOTSUP);
+ /*
+ * While most hypervisors support the control queue, bhyve
+ * (or more specifically viona) on illumos currently does not.
+ *
+ * Until that support is added to viona, we pretend
+ * the request always succeeds to match the historic behavior
+ * of the illumos vioif driver. Once that support has been
+ * added to viona, we should do the correct thing and return
+ * ENOTSUP
+ */
+ return (0);
}
return (vioif_ctrlq_req(vif, VIRTIO_NET_CTRL_RX,
diff --git a/usr/src/uts/common/sys/pci.h b/usr/src/uts/common/sys/pci.h
index d62d19c3a5..5dd6762ab5 100644
--- a/usr/src/uts/common/sys/pci.h
+++ b/usr/src/uts/common/sys/pci.h
@@ -621,6 +621,8 @@ extern "C" {
#define PCI_CAP_ID_MSI_X 0x11 /* MSI-X supported */
#define PCI_CAP_ID_SATA 0x12 /* SATA Data/Index Config supported */
#define PCI_CAP_ID_FLR 0x13 /* Function Level Reset supported */
+#define PCI_CAP_ID_EA 0x14 /* Enhanced Allocation */
+#define PCI_CAP_ID_FPB 0x15 /* Flattening Portal Bridge */
/*
* Capability next entry pointer values
@@ -909,13 +911,16 @@ typedef struct pcix_attr {
#define PCI_MSI_CTRL 0x02 /* MSI control register, 2 bytes */
#define PCI_MSI_ADDR_OFFSET 0x04 /* MSI 32-bit msg address, 4 bytes */
#define PCI_MSI_32BIT_DATA 0x08 /* MSI 32-bit msg data, 2 bytes */
+#define PCI_MSI_32BIT_EXTDATA 0x0A /* MSI 32-bit msg ext data, 2 bytes */
#define PCI_MSI_32BIT_MASK 0x0C /* MSI 32-bit mask bits, 4 bytes */
#define PCI_MSI_32BIT_PENDING 0x10 /* MSI 32-bit pending bits, 4 bytes */
/*
* PCI Message Signalled Interrupts (MSI) capability entry offsets for 64-bit
*/
+#define PCI_MSI_64BIT_ADDR 0x08 /* MSI 64-bit upper address, 4 bytes */
#define PCI_MSI_64BIT_DATA 0x0C /* MSI 64-bit msg data, 2 bytes */
+#define PCI_MSI_64BIT_EXTDATA 0x0E /* MSI 64-bit msg ext data, 2 bytes */
#define PCI_MSI_64BIT_MASKBITS 0x10 /* MSI 64-bit mask bits, 4 bytes */
#define PCI_MSI_64BIT_PENDING 0x14 /* MSI 64-bit pending bits, 4 bytes */
diff --git a/usr/src/uts/common/sys/pcie.h b/usr/src/uts/common/sys/pcie.h
index e8f91a1390..840c31a328 100644
--- a/usr/src/uts/common/sys/pcie.h
+++ b/usr/src/uts/common/sys/pcie.h
@@ -536,6 +536,7 @@ extern "C" {
#define PCIE_EXT_CAP_NEXT_PTR_MASK 0xFFF
#define PCIE_EXT_CAP_NEXT_PTR_NULL 0x0
+#define PCIE_EXT_CAP_MAX_PTR 0x3c0 /* max. number of caps */
/*
* PCI-Express Enhanced Capability Identifier Values
@@ -559,6 +560,7 @@ extern "C" {
#define PCIE_EXT_CAP_ID_SRIOV 0x10 /* Single Root I/O Virt. */
#define PCIE_EXT_CAP_ID_MRIOV 0x11 /* Multi Root I/O Virt. */
#define PCIE_EXT_CAP_ID_MULTICAST 0x12 /* Multicast Services */
+#define PCIE_EXT_CAP_ID_PGREQ 0x13 /* Page Request */
#define PCIE_EXT_CAP_ID_EA 0x14 /* Enhanced Allocation */
#define PCIE_EXT_CAP_ID_RESIZE_BAR 0x15 /* Resizable BAR */
#define PCIE_EXT_CAP_ID_DPA 0x16 /* Dynamic Power Allocation */
@@ -573,11 +575,15 @@ extern "C" {
#define PCIE_EXT_CAP_ID_FRS 0x21 /* Function Ready Stat. Queue */
#define PCIE_EXT_CAP_ID_RTR 0x22 /* Readiness Time Reporting */
#define PCIE_EXT_CAP_ID_DVS 0x23 /* Designated Vendor-Specific */
+#define PCIE_EXT_CAP_ID_VFRBAR 0x24 /* VF Resizable BAR */
#define PCIE_EXT_CAP_ID_DLF 0x25 /* Data Link Feature */
-#define PCIE_EXT_CAP_ID_PL16GTE 0x26 /* Physical Layer 16.0 GT/s */
+#define PCIE_EXT_CAP_ID_PL16GT 0x26 /* Physical Layer 16.0 GT/s */
#define PCIE_EXT_CAP_ID_LANE_MARGIN 0x27 /* Lane Margining */
#define PCIE_EXT_CAP_ID_HIEARCHY_ID 0x28 /* Hierarchy ID */
#define PCIE_EXT_CAP_ID_NPEM 0x29 /* Native PCIe Enclosure Mgmt */
+#define PCIE_EXT_CAP_ID_PL32GT 0x2A /* Physical Layer 32.0 GT/s */
+#define PCIE_EXT_CAP_ID_AP 0x2B /* Alternate Protocol */
+#define PCIE_EXT_CAP_ID_SFI 0x2C /* Sys. Firmware Intermediary */
/*
* PCI-Express Advanced Error Reporting Extended Capability Offsets
@@ -596,6 +602,7 @@ extern "C" {
#define PCIE_AER_RE_STS 0x30 /* Root Error Status */
#define PCIE_AER_CE_SRC_ID 0x34 /* Error Source ID */
#define PCIE_AER_ERR_SRC_ID 0x36 /* Error Source ID */
+#define PCIE_AER_TLP_PRE_LOG 0x38 /* TLP Prefix Log */
/* Bridges Only */
#define PCIE_AER_SUCE_STS 0x2c /* Secondary UCE Status */
diff --git a/usr/src/uts/i86pc/io/vmm/vmm_sol_dev.c b/usr/src/uts/i86pc/io/vmm/vmm_sol_dev.c
index 9267befd19..e668ee35a6 100644
--- a/usr/src/uts/i86pc/io/vmm/vmm_sol_dev.c
+++ b/usr/src/uts/i86pc/io/vmm/vmm_sol_dev.c
@@ -1873,14 +1873,14 @@ vmm_do_vm_destroy_locked(vmm_softc_t *sc, boolean_t clean_zsd,
*hma_release = B_FALSE;
- if (clean_zsd) {
- vmm_zsd_rem_vm(sc);
- }
-
if (vmm_drv_purge(sc) != 0) {
return (EINTR);
}
+ if (clean_zsd) {
+ vmm_zsd_rem_vm(sc);
+ }
+
/* Clean up devmem entries */
vmmdev_devmem_purge(sc);