diff options
| author | 2009-07-26 13:21:18 +0000 | |
|---|---|---|
| committer | 2009-07-26 13:21:18 +0000 | |
| commit | da18e9e3d702dc07a1feb63c7e2b81e042c97813 (patch) | |
| tree | f6404890c1bc28514f3792f3bdd09ca8ddd2d6fc /sys/dev/pci/pci.c | |
| parent | If we recognize a specific mouse model where we need to override locator (diff) | |
| download | wireguard-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.c | 93 |
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); |
