diff options
author | David Gwynne <dlg@cvs.openbsd.org> | 2020-06-22 04:11:38 +0000 |
---|---|---|
committer | David Gwynne <dlg@cvs.openbsd.org> | 2020-06-22 04:11:38 +0000 |
commit | e18f482c2f3185af258857d8cc0b11fd077c38d6 (patch) | |
tree | fe97cc1cdc2d3c023d4d69423f5ae2e2e0482ef9 /sys/dev | |
parent | 31602e4bd373067943db5dbb611a7ad3c1b4ac47 (diff) |
let userland read vpd info from a pci device.
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.
the vpd info is not accessed as bytes read from a capability, but
is read via a register in the capability. the same register also
supports updating or writing vpd info, which sounds like a bad idea
to let userland have raw access to.
this adds an ioctl so that userland can ask the kernel to read via
the vpd register on its behalf. this ensures that the only access
is read access, and it's sanity checked.
tested by hrvoje popovski on many devices.
ok jmatthew@
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/pci/pci.c | 39 |
1 files changed, 35 insertions, 4 deletions
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c index 68cda5a80a9..4ac66e627c9 100644 --- a/sys/dev/pci/pci.c +++ b/sys/dev/pci/pci.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pci.c,v 1.116 2020/06/17 01:43:04 dlg Exp $ */ +/* $OpenBSD: pci.c,v 1.117 2020/06/22 04:11:37 dlg Exp $ */ /* $NetBSD: pci.c,v 1.31 1997/06/06 23:48:04 thorpej Exp $ */ /* @@ -1031,10 +1031,11 @@ pci_vpd_read(pci_chipset_tag_t pc, pcitag_t tag, int offset, int count, 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, pcitag_t tag, int offset, int count, 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 data, int flag, struct proc *p) break; case PCIOCGETROMLEN: case PCIOCGETROM: + case PCIOCGETVPD: break; case PCIOCGETVGA: case PCIOCSETVGA: @@ -1369,6 +1371,35 @@ pciioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 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; + } + case PCIOCGETVGA: { struct pci_vga *vga = (struct pci_vga *)data; |