summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiod Vallat <miod@cvs.openbsd.org>2009-04-12 17:55:21 +0000
committerMiod Vallat <miod@cvs.openbsd.org>2009-04-12 17:55:21 +0000
commit2829c2629cd9ab9ba5ca8dd4c7a45d3e3755098e (patch)
tree57de42063cb2c852b25b6e1386c878d38a7685e1
parentdb8f9a5b15d531c213beb3886678c480ee33a9b5 (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.
-rw-r--r--sys/arch/sgi/xbow/xbridge.c98
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);
}