diff options
author | Miod Vallat <miod@cvs.openbsd.org> | 2009-04-12 17:55:21 +0000 |
---|---|---|
committer | Miod Vallat <miod@cvs.openbsd.org> | 2009-04-12 17:55:21 +0000 |
commit | 2829c2629cd9ab9ba5ca8dd4c7a45d3e3755098e (patch) | |
tree | 57de42063cb2c852b25b6e1386c878d38a7685e1 /sys | |
parent | db8f9a5b15d531c213beb3886678c480ee33a9b5 (diff) |
Add code to prevent the MI pci code to cause IOC3 devices to freeze and
hog the bus, and also to fake a valid interrupt register. The IOC3 device
is not a PCI device at all, but pretends to be one. Except its own
registers overlap the PCI configuration space, and some flavours do not
support disabling memory space in the control register, violating the PCI
specs. Fun.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/arch/sgi/xbow/xbridge.c | 98 |
1 files changed, 91 insertions, 7 deletions
diff --git a/sys/arch/sgi/xbow/xbridge.c b/sys/arch/sgi/xbow/xbridge.c index 51548e80692..fb2dee9e403 100644 --- a/sys/arch/sgi/xbow/xbridge.c +++ b/sys/arch/sgi/xbow/xbridge.c @@ -1,4 +1,4 @@ -/* $OpenBSD: xbridge.c,v 1.5 2009/03/30 09:41:00 kettenis Exp $ */ +/* $OpenBSD: xbridge.c,v 1.6 2009/04/12 17:55:20 miod Exp $ */ /* * Copyright (c) 2008 Miodrag Vallat. @@ -36,6 +36,7 @@ #include <dev/pci/pcireg.h> #include <dev/pci/pcivar.h> +#include <dev/pci/pcidevs.h> #include <mips64/archtype.h> #include <sgi/xbow/xbow.h> @@ -344,7 +345,7 @@ pcireg_t xbridge_conf_read(void *cookie, pcitag_t tag, int offset) { struct xbridge_softc *sc = cookie; - pcireg_t data; + pcireg_t id, data; int bus, dev, fn; paddr_t pa; int s; @@ -360,9 +361,49 @@ xbridge_conf_read(void *cookie, pcitag_t tag, int offset) } else pa = sc->sc_regh + BRIDGE_PCI_CFG_SPACE + (dev << 12); - pa += (fn << 8) + offset; - if (guarded_read_4(pa, &data) != 0) - data = 0xffffffff; + /* + * IOC3 devices only implement a subset of the PCI configuration + * registers. + * Depending on which particular model we encounter, things may + * seem to work, or write access to nonexisting registers would + * completely freeze the machine. + * + * We thus check for the device type here, and handle the non + * existing registers ourselves. + */ + + if (guarded_read_4(pa + PCI_ID_REG, &id) != 0) { + splx(s); + return 0xffffffff; + } + + if (id == PCI_ID_CODE(PCI_VENDOR_SGI, PCI_PRODUCT_SGI_IOC3)) { + switch (offset) { + case PCI_ID_REG: + case PCI_COMMAND_STATUS_REG: + case PCI_CLASS_REG: + case PCI_BHLC_REG: + case PCI_MAPREG_START: + /* These registers are implemented. Go ahead. */ + id = 0; + break; + case PCI_INTERRUPT_REG: + /* This register is not implemented. Fake it. */ + data = PCI_INTERRUPT_PIN_A << PCI_INTERRUPT_PIN_SHIFT; + break; + default: + /* These registers are not implemented. */ + data = 0; + break; + } + } else + id = 0; + + if (id == 0) { + pa += (fn << 8) + offset; + if (guarded_read_4(pa, &data) != 0) + data = 0xffffffff; + } splx(s); return(data); @@ -372,6 +413,7 @@ void xbridge_conf_write(void *cookie, pcitag_t tag, int offset, pcireg_t data) { struct xbridge_softc *sc = cookie; + pcireg_t id; int bus, dev, fn; paddr_t pa; int s; @@ -387,8 +429,50 @@ xbridge_conf_write(void *cookie, pcitag_t tag, int offset, pcireg_t data) } else pa = sc->sc_regh + BRIDGE_PCI_CFG_SPACE + (dev << 12); - pa += (fn << 8) + offset; - guarded_write_4(pa, data); + /* + * IOC3 devices only implement a subset of the PCI configuration + * registers. + * Depending on which particular model we encounter, things may + * seem to work, or write access to nonexisting registers would + * completely freeze the machine. + * + * We thus check for the device type here, and handle the non + * existing registers ourselves. + */ + + if (guarded_read_4(pa + PCI_ID_REG, &id) != 0) { + splx(s); + return; + } + + if (id == PCI_ID_CODE(PCI_VENDOR_SGI, PCI_PRODUCT_SGI_IOC3)) { + switch (offset) { + case PCI_COMMAND_STATUS_REG: + /* + * Some IOC models do not support having this bit + * cleared (which is what pci_mapreg_probe() will + * do), so we set it unconditionnaly. + */ + data |= PCI_COMMAND_MEM_ENABLE; + /* FALLTHROUGH */ + case PCI_ID_REG: + case PCI_CLASS_REG: + case PCI_BHLC_REG: + case PCI_MAPREG_START: + /* These registers are implemented. Go ahead. */ + id = 0; + break; + default: + /* These registers are not implemented. */ + break; + } + } else + id = 0; + + if (id == 0) { + pa += (fn << 8) + offset; + guarded_write_4(pa, data); + } splx(s); } |