/*
 * DF-5351 crasher: msi_alloc(count=128) on the last online CPU.
 *
 * With count=128 the scan loop in ioapic_abi_msi_alloc_intern() reaches
 * i=128 and then j walks 0..127, i.e. intr = 128..255:
 *   - ioapic_irqmaps[cpuid][192..255] are read (ioapic_abi.c:1276) and
 *     written (ioapic_abi.c:1287-1293): one full row past the end of the
 *     192-entry row (the next CPU's map row).
 *   - intrs[64..127] = 192..255 is written into the caller's array
 *     (ioapic_abi.c:1295) -- past any 32-entry contract array.
 *   - msi_setup(intr, cpuid) is called for intr=192..255
 *     (ioapic_abi.c:1296): msi_intr[192..255] is an OOB read past the
 *     192-entry msi_intr[] table, and setidt(0x20+192 .. 0x20+255)
 *     overwrites the IPI IDT gates (vectors 224..255) and writes past
 *     the 256-entry per-CPU IDT (vectors 256..287).
 * Expect: panic on the next IPI (6-CPU guest fires IPIs constantly).
 * If the machine somehow survives, the printed zone[64..127] values >= 192
 * still prove the row-crossing map writes.
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <sys/machintr.h>



extern struct machintr_abi MachIntrABI_IOAPIC;

static int zone[256];

static void
row128(int cpuid)
{
	int i, rc;

	for (i = 0; i < 256; ++i)
		zone[i] = 0x7B7B7B7B;

	kprintf("msiabi_row128: msi_alloc(count=128, cpu%d) ...\n", cpuid);
	rc = MachIntrABI_IOAPIC.msi_alloc(zone, 128, cpuid);
	kprintf("msiabi_row128: rc=%d\n", rc);
	if (rc == 0) {
		kprintf("msiabi_row128: zone[0..3] =");
		for (i = 0; i < 4; ++i)
			kprintf(" %d", zone[i]);
		kprintf("\nmsiabi_row128: zone[120..127] =");
		for (i = 120; i < 128; ++i)
			kprintf(" %d", zone[i]);
		kprintf("  (values >= 192 prove writes into "
		    "ioapic_irqmaps[cpuid][192..255])\n");
		/* not reached if IPI delivery kills us first */
		MachIntrABI_IOAPIC.msi_release(zone, 128, cpuid);
		kprintf("msiabi_row128: released\n");
	}
}

static int
msiabi_modevent(module_t mod __unused, int type, void *data __unused)
{
	switch (type) {
	case MOD_LOAD:
		kprintf("msiabi_row128: loaded\n");
		row128(ncpus - 1);
		kprintf("msiabi_row128: SURVIVED (unexpected)\n");
		return 0;
	case MOD_UNLOAD:
		return 0;
	default:
		return EOPNOTSUPP;
	}
}

static moduledata_t msiabi_row128_mod = { "msiabi_row128", msiabi_modevent, NULL };
DECLARE_MODULE(msiabi_row128, msiabi_row128_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
