DF-5352 / legacy_crash.c
/* * DF-5352 crasher: legacy_intr_config() on an IRQ that is not a legacy * IRQ map (MSI-X allocated). * * ioapic_abi_legacy_intr_config() (ioapic_abi.c:987-1053) validates its * input only with KKASSERT/KASSERT, both of which are compiled out on * non-INVARIANTS (production) kernels (sys/sys/systm.h:94-122): * - KKASSERT(irq < ioapic_abi_legacy_irq_max) [ioapic_abi.c:999] * - KKASSERT(cpuid < ncpus) after the legacy search loop * [ioapic_abi.c:1005] -- the loop just falls through when no CPU has * a LEGACY map for this irq, leaving 'map' pointing at the last CPU's * (non-legacy) map. * For an MSI/MSI-X map, im_gsi == -1 (ioapic_abi.c:763-764), so * ioapic_gsi_pin(-1) -> ioapic_gsi_search(-1) -> * panic("ioapic_gsi_search: no I/O APIC") [ioapic.c:415-427]. * * This is the driver-facing ABI: any driver calling * bus_config_intr()/machintr_legacy_intr_config() with a non-legacy IRQ * panics a production kernel. Return type is void -- no error path. */ #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:116-125 */ #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_IOAPIC; static int legacy_modevent(module_t mod __unused, int type, void *unused __unused) { int irq = -1, rc, irqv[16], n = 0, i, victim = -1; switch (type) { case MOD_LOAD: /* * Grab MSI-X irqs until one lands past the legacy range * (QEMU's ioapic maps gsis 0..23 as legacy on some cpu); * a map that is MSI-X on cpu0 and not LEGACY anywhere is * what we want. */ for (i = 0; i < 16; ++i) { irq = -1; rc = MachIntrABI_IOAPIC.msix_alloc(&irq, 0); if (rc != 0) break; kprintf("legacy_crash: msix_alloc rc=%d irq=%d\n", rc, irq); if (irq >= 24) { victim = irq; break; } irqv[n++] = irq; } if (victim < 0) { kprintf("legacy_crash: no irq past legacy range, " "abort\n"); while (--n >= 0) MachIntrABI_IOAPIC.msix_release(irqv[n], 0); return 0; } kprintf("legacy_crash: calling legacy_intr_config(irq=%d, " "LEVEL, LOW); production kernel should panic in " "ioapic_gsi_search (im_gsi == -1)\n", victim); MachIntrABI_IOAPIC.legacy_intr_config(victim, INTR_TRIGGER_LEVEL, INTR_POLARITY_LOW); kprintf("legacy_crash: RETURNED WITHOUT PANIC (unexpected)\n"); while (--n >= 0) MachIntrABI_IOAPIC.msix_release(irqv[n], 0); MachIntrABI_IOAPIC.msix_release(victim, 0); return 0; case MOD_UNLOAD: return 0; default: return EOPNOTSUPP; } } static moduledata_t legacy_crash_mod = { "legacy_crash", legacy_modevent, NULL }; DECLARE_MODULE(legacy_crash, legacy_crash_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); |