summaryrefslogtreecommitdiff
path: root/usr.sbin/pcidump
diff options
context:
space:
mode:
authorDavid Gwynne <dlg@cvs.openbsd.org>2020-06-22 05:54:27 +0000
committerDavid Gwynne <dlg@cvs.openbsd.org>2020-06-22 05:54:27 +0000
commit0d7afcdc848eac24430d3edfb0de9a419edcad33 (patch)
treeae5026e95208f2cae6ef394804f5710b88a8c4f7 /usr.sbin/pcidump
parent32aa60057c38f20864af3859b9a71afb303a3004 (diff)
a first cut at requesting and parsing vpd info.
reading vpd stuff is useful when you're trying to get support information about a pci device, eg, if you want a serial number, or firmware versions, or specific part name or number, it's likely available via vpd. also, im sick of having the diff in my tree. this relies on the new PCIOCGETVPD ioctl i just committed to the kernel. it's a very quick and dirty implementation, hopefully someone will pick it up and polish it a bit. tested by hrvoje popovski on a variety of cards ok jmatthew@
Diffstat (limited to 'usr.sbin/pcidump')
-rw-r--r--usr.sbin/pcidump/pcidump.c138
1 files changed, 137 insertions, 1 deletions
diff --git a/usr.sbin/pcidump/pcidump.c b/usr.sbin/pcidump/pcidump.c
index fd8034d9136..1b239b732da 100644
--- a/usr.sbin/pcidump/pcidump.c
+++ b/usr.sbin/pcidump/pcidump.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pcidump.c,v 1.56 2019/11/30 14:02:29 mestre Exp $ */
+/* $OpenBSD: pcidump.c,v 1.57 2020/06/22 05:54:26 dlg Exp $ */
/*
* Copyright (c) 2006, 2007 David Gwynne <loki@animata.net>
@@ -34,6 +34,7 @@
#include <string.h>
#include <unistd.h>
#include <limits.h>
+#include <vis.h>
#define PCIDEV "/dev/pci"
@@ -52,6 +53,7 @@ int pci_read(int, int, int, u_int32_t, u_int32_t *);
int pci_readmask(int, int, int, u_int32_t, u_int32_t *);
void dump_bars(int, int, int, int);
void dump_caplist(int, int, int, u_int8_t);
+void dump_vpd(int, int, int);
void dump_pci_powerstate(int, int, int, uint8_t);
void dump_pcie_linkspeed(int, int, int, uint8_t);
void dump_pcie_devserial(int, int, int, uint16_t);
@@ -335,6 +337,137 @@ probe(int bus, int dev, int func)
return (0);
}
+int
+print_bytes(const uint8_t *buf, size_t len)
+{
+ char dst[8];
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ vis(dst, buf[i], VIS_TAB|VIS_NL, 0);
+ printf("%s", dst);
+ }
+ printf("\n");
+
+ return (0);
+}
+
+int
+print_vpd(const uint8_t *buf, size_t len)
+{
+ const struct pci_vpd *vpd;
+ char key0[8];
+ char key1[8];
+ size_t vlen, i;
+
+ while (len > 0) {
+ if (len < sizeof(*vpd))
+ return (1);
+
+ vpd = (const struct pci_vpd *)buf;
+ vis(key0, vpd->vpd_key0, VIS_TAB|VIS_NL, 0);
+ vis(key1, vpd->vpd_key1, VIS_TAB|VIS_NL, 0);
+ vlen = vpd->vpd_len;
+
+ printf("\t\t %s%s: ", key0, key1);
+
+ buf += sizeof(*vpd);
+ len -= sizeof(*vpd);
+
+ if (len < vlen)
+ return (1);
+ print_bytes(buf, vlen);
+
+ buf += vlen;
+ len -= vlen;
+ }
+
+ return (0);
+}
+
+void
+dump_vpd(int bus, int dev, int func)
+{
+ struct pci_vpd_req io;
+ uint32_t data[64]; /* XXX this can be up to 32k of data */
+ uint8_t *buf = (uint8_t *)data;
+ size_t len = sizeof(data);
+ size_t i;
+
+ bzero(&io, sizeof(io));
+ io.pv_sel.pc_bus = bus;
+ io.pv_sel.pc_dev = dev;
+ io.pv_sel.pc_func = func;
+ io.pv_offset = 0;
+ io.pv_count = nitems(data);
+ io.pv_data = data;
+
+ if (ioctl(pcifd, PCIOCGETVPD, &io) == -1)
+ warn("PCIOCGETVPD");
+
+ do {
+ uint8_t vpd = *buf;
+ uint8_t type;
+ size_t hlen, vlen;
+ int (*print)(const uint8_t *, size_t) = print_bytes;
+
+ if (PCI_VPDRES_ISLARGE(vpd)) {
+ struct pci_vpd_largeres *res;
+ type = PCI_VPDRES_LARGE_NAME(vpd);
+
+ switch (type) {
+ case PCI_VPDRES_TYPE_IDENTIFIER_STRING:
+ printf("\t\tProduct Name: ");
+ break;
+ case PCI_VPDRES_TYPE_VPD:
+ print = print_vpd;
+ break;
+ default:
+ printf("%02x: ", type);
+ break;
+ }
+
+ if (len < sizeof(*res))
+ goto trunc;
+ res = (struct pci_vpd_largeres *)buf;
+
+ hlen = sizeof(*res);
+ vlen = ((size_t)res->vpdres_len_msb << 8) |
+ (size_t)res->vpdres_len_lsb;
+ } else { /* small */
+ type = PCI_VPDRES_SMALL_NAME(vpd);
+ if (type == PCI_VPDRES_TYPE_END_TAG)
+ break;
+
+ printf("\t\t");
+ switch (type) {
+ case PCI_VPDRES_TYPE_COMPATIBLE_DEVICE_ID:
+ case PCI_VPDRES_TYPE_VENDOR_DEFINED:
+ default:
+ printf("%02x", type);
+ break;
+ }
+
+ hlen = sizeof(vpd);
+ vlen = PCI_VPDRES_SMALL_LENGTH(vpd);
+ }
+ buf += hlen;
+ len -= hlen;
+
+ if (len < vlen)
+ goto trunc;
+ (*print)(buf, vlen);
+
+ buf += vlen;
+ len -= vlen;
+ } while (len > 0);
+
+ return;
+trunc:
+ /* i have spent too much time in tcpdump - dlg */
+ printf("[|vpd]\n");
+}
+
void
dump_pci_powerstate(int bus, int dev, int func, uint8_t ptr)
{
@@ -512,6 +645,9 @@ dump_caplist(int bus, int dev, int func, u_int8_t ptr)
case PCI_CAP_PWRMGMT:
dump_pci_powerstate(bus, dev, func, ptr);
break;
+ case PCI_CAP_VPD:
+ dump_vpd(bus, dev, func);
+ break;
case PCI_CAP_PCIEXPRESS:
dump_pcie_linkspeed(bus, dev, func, ptr);
dump_pcie_enhanced_caplist(bus, dev, func);