summaryrefslogtreecommitdiffstats
path: root/sys/dev/pci/pci.c
diff options
context:
space:
mode:
authorkettenis <kettenis@openbsd.org>2009-07-26 13:21:18 +0000
committerkettenis <kettenis@openbsd.org>2009-07-26 13:21:18 +0000
commitda18e9e3d702dc07a1feb63c7e2b81e042c97813 (patch)
treef6404890c1bc28514f3792f3bdd09ca8ddd2d6fc /sys/dev/pci/pci.c
parentIf we recognize a specific mouse model where we need to override locator (diff)
downloadwireguard-openbsd-da18e9e3d702dc07a1feb63c7e2b81e042c97813.tar.xz
wireguard-openbsd-da18e9e3d702dc07a1feb63c7e2b81e042c97813.zip
Add PCIOCGETROM, and ioctl(2) to read PCI ROMs.
ok miod@, marco@
Diffstat (limited to 'sys/dev/pci/pci.c')
-rw-r--r--sys/dev/pci/pci.c93
1 files changed, 80 insertions, 13 deletions
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c
index 43c67efc0d7..f5a869d820d 100644
--- a/sys/dev/pci/pci.c
+++ b/sys/dev/pci/pci.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pci.c,v 1.66 2009/07/25 12:51:41 miod Exp $ */
+/* $OpenBSD: pci.c,v 1.67 2009/07/26 13:21:18 kettenis Exp $ */
/* $NetBSD: pci.c,v 1.31 1997/06/06 23:48:04 thorpej Exp $ */
/*
@@ -785,7 +785,9 @@ pciclose(dev_t dev, int flag, int devtype, struct proc *p)
int
pciioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
+ struct pcisel *sel = (struct pcisel *)data;
struct pci_io *io;
+ struct pci_rom *rom;
int i, error;
pcitag_t tag;
struct pci_softc *pci = NULL;
@@ -798,21 +800,16 @@ pciioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
if (!(flag & FWRITE))
return EPERM;
break;
+ case PCIOCGETROM:
+ break;
default:
return ENOTTY;
}
- io = (struct pci_io *)data;
-
- PCIDEBUG(("pciioctl cmd %s",
- cmd == PCIOCREAD ? "pciocread" : "pciocwrite"));
- PCIDEBUG((" bus %d dev %d func %d reg %x\n", io->pi_sel.pc_bus,
- io->pi_sel.pc_dev, io->pi_sel.pc_func, io->pi_reg));
-
for (i = 0; i < pci_cd.cd_ndevs; i++) {
pci = pci_cd.cd_devs[i];
if (pci != NULL && pci->sc_domain == minor(dev) &&
- pci->sc_bus == io->pi_sel.pc_bus)
+ pci->sc_bus == sel->pc_bus)
break;
}
if (i >= pci_cd.cd_ndevs)
@@ -820,16 +817,16 @@ pciioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
/* Check bounds */
if (pci->sc_bus >= 256 ||
- io->pi_sel.pc_dev >= pci_bus_maxdevs(pci->sc_pc, pci->sc_bus) ||
- io->pi_sel.pc_func >= 8)
+ sel->pc_dev >= pci_bus_maxdevs(pci->sc_pc, pci->sc_bus) ||
+ sel->pc_func >= 8)
return EINVAL;
pc = pci->sc_pc;
- tag = pci_make_tag(pc, io->pi_sel.pc_bus, io->pi_sel.pc_dev,
- io->pi_sel.pc_func);
+ tag = pci_make_tag(pc, sel->pc_bus, sel->pc_dev, sel->pc_func);
switch (cmd) {
case PCIOCREAD:
+ io = (struct pci_io *)data;
switch (io->pi_width) {
case 4:
/* Make sure the register is properly aligned */
@@ -845,6 +842,7 @@ pciioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
break;
case PCIOCWRITE:
+ io = (struct pci_io *)data;
switch (io->pi_width) {
case 4:
/* Make sure the register is properly aligned */
@@ -858,6 +856,75 @@ pciioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
break;
}
break;
+
+ case PCIOCGETROM:
+ {
+ pcireg_t addr, mask, bhlc;
+ bus_space_handle_t h;
+ bus_size_t len, off;
+ char buf[256];
+ int s;
+
+ bhlc = pci_conf_read(pc, tag, PCI_BHLC_REG);
+ if (PCI_HDRTYPE_TYPE(bhlc) != 0)
+ return (ENODEV);
+
+ s = splhigh();
+ addr = pci_conf_read(pc, tag, PCI_ROM_REG);
+ pci_conf_write(pc, tag, PCI_ROM_REG, ~PCI_ROM_ENABLE);
+ mask = pci_conf_read(pc, tag, PCI_ROM_REG);
+ pci_conf_write(pc, tag, PCI_ROM_REG, addr);
+ splx(s);
+
+ /*
+ * Section 6.2.5.2 `Expansion ROM Base Addres Register',
+ *
+ * tells us that only the upper 21 bits are writable.
+ * This means that the size of a ROM must be a
+ * multiple of 2 KB. So reading the ROM in chunks of
+ * 256 bytes should work just fine.
+ */
+ if ((PCI_ROM_ADDR(addr) == 0 ||
+ PCI_ROM_SIZE(mask) % sizeof(buf)) != 0)
+ return (ENODEV);
+
+ rom = (struct pci_rom *)data;
+ if (rom->pr_romlen < PCI_ROM_SIZE(mask)) {
+ error = ENOMEM;
+ goto fail;
+ }
+
+ error = bus_space_map(pci->sc_memt, PCI_ROM_ADDR(addr),
+ PCI_ROM_SIZE(mask), 0, &h);
+ if (error)
+ goto fail;
+
+ off = 0;
+ len = PCI_ROM_SIZE(mask);
+ while (len > 0 && error == 0) {
+ s = splhigh();
+ pci_conf_write(pc, tag, PCI_ROM_REG,
+ addr | PCI_ROM_ENABLE);
+ bus_space_read_region_1(pci->sc_memt, h, off,
+ buf, sizeof(buf));
+ pci_conf_write(pc, tag, PCI_ROM_REG, addr);
+ splx(s);
+
+ error = copyout(buf, rom->pr_rom + off, sizeof(buf));
+ off += sizeof(buf);
+ len -= sizeof(buf);
+ }
+
+ bus_space_unmap(pci->sc_memt, h, PCI_ROM_SIZE(mask));
+
+ fail:
+ rom->pr_romlen = PCI_ROM_SIZE(mask);
+ break;
+ }
+
+ default:
+ error = ENOTTY;
+ break;
}
return (error);