⬢ DragonFlyBSD Kernel Audit
DF-5509 / fix.diff
← back to finding ↓ download raw
--- a/sys/bus/u4b/controller/ehci_pci.c
+++ b/sys/bus/u4b/controller/ehci_pci.c
@@ -277,6 +277,7 @@
 	ehci_softc_t *sc = device_get_softc(self);
 	int err;
 	int rid;
+	uint8_t caplen;
 
 	/* initialise some bus fields */
 	sc->sc_bus.parent = self;
@@ -323,6 +324,25 @@
 	sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res);
 	sc->sc_io_size = rman_get_size(sc->sc_io_res);
 
+	/*
+	 * Quiesce any interrupt state left behind by the BIOS or a
+	 * previous owner BEFORE bus_setup_intr() arms ehci_interrupt()
+	 * below. Until ehci_init() latches sc_offs and sc_eintrs, the
+	 * interrupt handler reads USBSTS from the wrong absolute offset
+	 * (MMIO + 0 + 0x04 == HCSPARAMS) and unconditionally exits via
+	 * "goto done" WITHOUT acknowledging, so a level-triggered
+	 * pending interrupt in the window between bus_setup_intr() and
+	 * ehci_init()'s ehci_hcreset() re-fires forever - including the
+	 * up-to-5-second BIOS handoff wait in ehci_pci_take_controller().
+	 * CAPLENGTH (low byte of capability register 0) is an absolute
+	 * offset and is therefore valid before sc_offs is initialized.
+	 */
+	caplen = bus_space_read_1(sc->sc_io_tag, sc->sc_io_hdl, 0);
+	bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl,
+	    caplen + EHCI_USBINTR, 0);
+	bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl,
+	    caplen + EHCI_USBSTS, 0xFFFFFFFF);
+
 	rid = 0;
 	sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid,
 	    RF_SHAREABLE | RF_ACTIVE);