/*
 * gmuxobs.c -- kernel observer for DF-5744.
 *
 * Watches COM1 scratch register (I/O port 0x3ff). The 16550 SCR byte is
 * fully readable/writable and has ZERO functional effect, which makes it a
 * perfect side-effect-free canary for an out-of-bounds port write.
 *
 * Rig geometry (SSDT): one gmux device with a 4-byte _CRS at base 0x333.
 * apple_gmux attach then does (production code, apple-gmux.c):
 *   gmux_read8  VERSION_{MAJOR,MINOR,RELEASE}  -> bus_read_1(res, 0x04..0x06)
 *                                               (3 OOB reads past 4-byte res)
 *   -> all 0xff -> gmux_is_indexed() (apple-gmux.c:346-362):
 *        bus_write_1(res, 0xcc, 0xaa)   == outb(0x333+0xcc=0x3ff, 0xaa)  <-- CANARY
 *        bus_write_1(res, 0xcd, 0x55)   == outb(0x400, 0x55)
 *        bus_write_1(res, 0xce, 0x00)   == outb(0x401, 0x00)
 *   -> val = 0xaa | (inb(0x400) << 8) != 0x55aa -> "not indexed" -> ENODEV
 *
 * If SCR(0x3ff) reads 0xaa after `kldload apple_gmux`, the production
 * driver demonstrably wrote 0xcc bytes past its 4-byte rman allocation.
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
#include <machine/cpufunc.h>

static int	gmuxobs_scr(SYSCTL_HANDLER_ARGS);

SYSCTL_PROC(_hw, OID_AUTO, gmuxobs_scr, CTLTYPE_INT | CTLFLAG_RD,
	    NULL, 0, gmuxobs_scr, "I", "current COM1 SCR (port 0x3ff) value");

static int
gmuxobs_scr(SYSCTL_HANDLER_ARGS)
{
	int v = inb(0x3ff);

	return (sysctl_handle_int(oidp, &v, 0, req));
}

static int
gmuxobs_handler(struct module *m, int what, void *arg)
{
	switch (what) {
	case MOD_LOAD:
		kprintf("gmuxobs: COM1 SCR (0x3ff) at module load = 0x%02x\n",
		    inb(0x3ff));
		break;
	case MOD_UNLOAD:
		kprintf("gmuxobs: COM1 SCR (0x3ff) at module unload = 0x%02x\n",
		    inb(0x3ff));
		break;
	}
	return (0);
}

static moduledata_t gmuxobs_mod = { "gmuxobs", gmuxobs_handler, NULL };
DECLARE_MODULE(gmuxobs, gmuxobs_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
