/*
 * DF-5465 evidence: icu_abi_legacy_intr_config() (icu_abi.c:366-397) is
 * the driver-facing ABI behind bus_config_intr() /
 * machintr_legacy_intr_config() (sys/sys/machintr.h:103).  Its only
 * 'irq' validation is
 *     KKASSERT(irq >= 0 && irq < IDT_HWI_VECTORS);   [icu_abi.c:373]
 * KKASSERT compiles to nothing on non-INVARIANTS (production) kernels
 * (sys/sys/systm.h:94-122); stock GENERIC ships without INVARIANTS.
 * The function returns void -- there is no error path.
 *
 * Step 1 (silent OOB write): legacy_intr_config(1000, EDGE, HIGH):
 *   map = &icu_irqmaps[0][1000]   [icu_abi.c:374]  -- 16000 bytes past
 *   the 192-entry row: still inside the [MAXCPU][192] 2-D array (lands
 *   in cpu5's map row), so nothing faults; icu_abi.c:379
 *   'map->im_flags |= ICU_IMF_CONF' (RMW write) and :389
 *   'map->im_trig = trig' silently corrupt another CPU's free-MSI map,
 *   then elcr_write_trigger(1000, ...) runs with a wild irq
 *   (elcr.c:70-92, '1 << irq' UB shift, outb to the ELCR port).
 *
 * Step 2 (wild index): legacy_intr_config(-100000000, EDGE, HIGH):
 *   &icu_irqmaps[0][-100000000] is ~1.5 GB below the array -- below
 *   KERNBASE, unmapped -> the RMW at icu_abi.c:379 takes a kernel-mode
 *   page fault -> panic.
 *
 * On an INVARIANTS kernel step 1 already panics at the KKASSERT
 * (icu_abi.c:373) -- either way a driver bug is promoted to a kernel
 * panic; on production kernels it is a silent OOB write primitive at a
 * driver-chosen 16-byte-strided offset plus, for wild values, a fault.
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <sys/machintr.h>
/* enum intr_trigger/polarity values from sys/bus.h */
#define INTR_TRIGGER_CONFORM 0
#define INTR_TRIGGER_EDGE    1
#define INTR_TRIGGER_LEVEL   2
#define INTR_POLARITY_CONFORM 0
#define INTR_POLARITY_HIGH   1
#define INTR_POLARITY_LOW    2

extern struct machintr_abi MachIntrABI_ICU;

static int
icu_legacy_modevent(module_t mod __unused, int type,
    void *unused __unused)
{
	switch (type) {
	case MOD_LOAD:
		kprintf("icu_legacy_probe: step1 legacy_intr_config(1000, "
		    "EDGE, HIGH): silent OOB write at icu_irqmaps[0][1000] "
		    "(+16000 bytes, inside the 2-D array -- no fault)\n");
		MachIntrABI_ICU.legacy_intr_config(1000,
		    INTR_TRIGGER_EDGE, INTR_POLARITY_HIGH);
		kprintf("icu_legacy_probe: step1 RETURNED WITHOUT ERROR "
		    "(void) -- OOB write done, guest still alive\n");

		kprintf("icu_legacy_probe: step2 legacy_intr_config"
		    "(-100000000, EDGE, HIGH): wild RMW ~1.5GB below "
		    "icu_irqmaps -> expect kernel page fault panic at "
		    "icu_abi.c:379\n");
		MachIntrABI_ICU.legacy_intr_config(-100000000,
		    INTR_TRIGGER_EDGE, INTR_POLARITY_HIGH);
		kprintf("icu_legacy_probe: step2 RETURNED WITHOUT PANIC "
		    "(unexpected on a production kernel)\n");
		return 0;
	case MOD_UNLOAD:
		return 0;
	default:
		return EOPNOTSUPP;
	}
}

static moduledata_t icu_legacy_probe_mod = {
	"icu_legacy_probe", icu_legacy_modevent, NULL
};
DECLARE_MODULE(icu_legacy_probe, icu_legacy_probe_mod,
    SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
MODULE_VERSION(icu_legacy_probe, 1);
