summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorDavid Gwynne <dlg@cvs.openbsd.org>2020-06-22 04:11:38 +0000
committerDavid Gwynne <dlg@cvs.openbsd.org>2020-06-22 04:11:38 +0000
commite18f482c2f3185af258857d8cc0b11fd077c38d6 (patch)
treefe97cc1cdc2d3c023d4d69423f5ae2e2e0482ef9 /sys/dev
parent31602e4bd373067943db5dbb611a7ad3c1b4ac47 (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.c39
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, &reg) == 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;