1
0
mirror of https://github.com/openbsd/src.git synced 2024-12-22 16:42:56 -08:00

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@
This commit is contained in:
dlg 2020-06-22 05:54:26 +00:00
parent d5e61526fa
commit 4b96984bb8

View File

@ -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);