Index: sys/dev/pci/pci.c =================================================================== RCS file: /cvs/src/sys/dev/pci/pci.c,v retrieving revision 1.115 diff -u -p -r1.115 pci.c --- sys/dev/pci/pci.c 15 Jan 2020 14:01:19 -0000 1.115 +++ sys/dev/pci/pci.c 8 Jun 2020 03:13:05 -0000 @@ -1031,10 +1031,11 @@ pci_vpd_read(pci_chipset_tag_t pc, pcita int ofs, i, j; KASSERT(data != NULL); - KASSERT((offset + count) < 0x7fff); + if (offset + count) >= PCI_VPD_ADDRESS_MASK) + return (EINVAL); if (pci_get_capability(pc, tag, PCI_CAP_VPD, &ofs, ®) == 0) - return (1); + return (ENXIO); for (i = 0; i < count; offset += sizeof(*data), i++) { reg &= 0x0000ffff; @@ -1049,7 +1050,7 @@ pci_vpd_read(pci_chipset_tag_t pc, pcita j = 0; do { if (j++ == 20) - return (1); + return (EIO); delay(4); reg = pci_conf_read(pc, tag, ofs); } while ((reg & PCI_VPD_OPFLAG) == 0); @@ -1203,6 +1204,7 @@ pciioctl(dev_t dev, u_long cmd, caddr_t break; case PCIOCGETROMLEN: case PCIOCGETROM: + case PCIOCGETVPD: break; case PCIOCGETVGA: case PCIOCSETVGA: @@ -1366,6 +1368,35 @@ pciioctl(dev_t dev, u_long cmd, caddr_t fail: rom->pr_romlen = PCI_ROM_SIZE(mask); + break; + } + + case PCIOCGETVPD: { + struct pci_vpd_req *pv = (struct pci_vpd_req *)data; + pcireg_t *data; + size_t len; + int s; + + CTASSERT(sizeof(*data) == sizeof(*pv->pv_data)); + + data = mallocarray(pv->pv_count, sizeof(*data), M_TEMP, + M_WAITOK|M_CANFAIL); + if (data == NULL) { + error = ENOMEM; + break; + } + + s = splhigh(); + error = pci_vpd_read(pc, tag, pv->pv_offset, pv->pv_count, + data); + splx(s); + + len = pv->pv_count * sizeof(*pv->pv_data); + + if (error == 0) + error = copyout(data, pv->pv_data, len); + + free(data, M_TEMP, len); break; } Index: sys/sys/pciio.h =================================================================== RCS file: /cvs/src/sys/sys/pciio.h,v retrieving revision 1.7 diff -u -p -r1.7 pciio.h --- sys/sys/pciio.h 5 Sep 2010 18:14:33 -0000 1.7 +++ sys/sys/pciio.h 8 Jun 2020 03:13:05 -0000 @@ -54,6 +54,13 @@ struct pci_rom { char *pr_rom; }; +struct pci_vpd_req { + struct pcisel pv_sel; + int pv_offset; + int pv_count; + uint32_t *pv_data; +}; + struct pci_vga { struct pcisel pv_sel; int pv_lock; @@ -74,5 +81,6 @@ struct pci_vga { #define PCIOCGETVGA _IOWR('p', 6, struct pci_vga) #define PCIOCSETVGA _IOWR('p', 7, struct pci_vga) #define PCIOCREADMASK _IOWR('p', 8, struct pci_io) +#define PCIOCGETVPD _IOWR('p', 9, struct pci_vpd_req) #endif /* !_SYS_PCIIO_H_ */ Index: usr.sbin/pcidump/pcidump.c =================================================================== RCS file: /cvs/src/usr.sbin/pcidump/pcidump.c,v retrieving revision 1.56 diff -u -p -r1.56 pcidump.c --- usr.sbin/pcidump/pcidump.c 30 Nov 2019 14:02:29 -0000 1.56 +++ usr.sbin/pcidump/pcidump.c 8 Jun 2020 03:13:05 -0000 @@ -34,6 +34,7 @@ #include #include #include +#include #define PCIDEV "/dev/pci" @@ -52,6 +53,7 @@ int pci_read(int, int, int, u_int32_t, u 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) { @@ -511,6 +644,9 @@ dump_caplist(int bus, int dev, int func, switch (cap) { 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);