/*
 * DF-3866 — kernel-side layout proof for the emu10kx ihandler[-1] OOB.
 *
 * This module does NOT touch hardware. It re-declares, verbatim, the
 * field prefix of `struct emu_sc_info` from sys/dev/sound/pci/emu10kx.c
 * lines 307..334 (everything up to and including ihandler[]), using the
 * guest kernel's real types (struct lock, struct sbuf, device_t,
 * bus_space_tag_t, struct cdev, struct resource). Compiling it against
 * the running kernel ABI proves where ihandler[-1] lands.
 *
 * Build:  see build.sh   Run: kldload ./lay.ko; dmesg | grep DF3866
 */
#include <sys/param.h>
#include <sys/types.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/rman.h>
#include <sys/sbuf.h>
#include <sys/queue.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/sysctl.h>
#include <sys/conf.h>

#define EMU_MAX_IRQ_CONSUMERS 32	/* emu10kx.c:250 */

/* verbatim from emu10kx.c:300-305 */
struct emu_intr_handler {
	void*		softc;
	uint32_t	intr_mask;
	uint32_t	inte_mask;
	uint32_t(*irq_func) (void *softc, uint32_t irq);
};

/*
 * Verbatim field-for-field prefix of `struct emu_sc_info`
 * (emu10kx.c:307-334), same order, same types.
 */
struct proof_sc {
	struct lock	lock;
	struct lock	rw;		/* Hardware exclusive access lock */

	device_t	dev;
	device_t	pcm[6];		/* RT_COUNT */
	device_t	midi[2];
	uint32_t	type;
	uint32_t	rev;

	bus_space_tag_t	st;
	bus_space_handle_t sh;

	struct cdev	*cdev;		/* /dev/emu10kx character device */
	struct lock	emu10kx_lock;
	int		emu10kx_isopen;
	struct sbuf	emu10kx_sbuf;
	int		emu10kx_bufptr;

	struct resource	*reg;
	struct resource	*irq;
	void 		*ih;

	struct emu_intr_handler ihandler[EMU_MAX_IRQ_CONSUMERS];
};

static int
lay_modevent(module_t mod __unused, int cmd, void *data __unused)
{
	size_t oh, oreg, oirq, oih, sz;

	if (cmd != MOD_LOAD)
		return (0);

	sz  = sizeof(struct emu_intr_handler);
	oh  = __offsetof(struct proof_sc, ihandler);
	oreg= __offsetof(struct proof_sc, reg);
	oirq= __offsetof(struct proof_sc, irq);
	oih = __offsetof(struct proof_sc, ih);

	kprintf("DF3866: sizeof(struct emu_intr_handler)=%zu\n", sz);
	kprintf("DF3866: offsetof(reg)=%zu irq=%zu ih=%zu ihandler=%zu\n",
	    oreg, oirq, oih, oh);
	kprintf("DF3866: ihandler[-1] spans [%zu,%zu)\n", oh - sz, oh);
	if (oh - sz == oreg && oh - 16 == oirq && oh - 8 == oih)
		kprintf("DF3866: PROVEN: ihandler[-1] overlays {reg, irq, ih} "
		    "-> emu_intr_unregister(-1) NULLs sc->reg, sc->irq, sc->ih "
		    "and smears the high/low dwords of the irq pointer\n");
	else
		kprintf("DF3866: layout differs from expectation "
		    "(prefix struct divergence?)\n");
	return (0);
}

static moduledata_t lay_mod = { "lay", lay_modevent, NULL };
DECLARE_MODULE(lay, lay_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);
