/*
 * dfrig_cyx.c -- DF-2213 verification rig
 *                (sys/dev/disk/nata/chipsets/ata-cyrix.c:98)
 *
 * Finding under test:
 *
 *	ata_cyrix_setmode() programs the Cyrix 5530 PIO timing register
 *	with
 *
 *	    ATA_OUTL(ch->r_io[ATA_BMCMD_PORT].res,
 *	             0x20 + (devno << 3),
 *	             piotiming[mode & ATA_MODE_MASK]);   <-- ata-cyrix.c:98
 *
 *	where piotiming[] has FIVE elements (ata-cyrix.c:67-68) but the
 *	ATA PIO mode constants start at 0x08 (sys/sys/nata.h:203-207:
 *	ATA_PIO0=0x08 .. ATA_PIO4=0x0c) and ATA_MODE_MASK is 0x0f
 *	(sys/sys/nata.h:200).  For every real PIO mode the masked index
 *	is therefore 8..12 -- reading 3..7 elements (12..28 bytes) past
 *	the end of the 20-byte table -- and the garbage constant so read
 *	is written to the disk controller PIO timing register.  In the
 *	shipped stock kernel the elements at piotiming[8..12] are
 *	dmatiming[2] / a hole / udmatiming[0..2] (see
 *	kernel_rodata.txt), so the standard boot probe (setmode settles
 *	at ATA_PIO4) programs a UDMA2 timing (0x00911030) where a PIO4
 *	timing (0x00040010) belongs: silent wrong-timing / disk data
 *	corruption class.  Every sibling driver indexes the same shape
 *	of table with ata_mode2idx() (ata-pci.c:719-726, the idiom that
 *	subtracts ATA_PIO0: e.g. ata-national.c:102 -- the NatSemi clone
 * of this very driver).
 *
 * Real dispatch chain that reaches the handler (all production cites):
 *
 *   boot reinit / ioctl(IOCATASMODE) sys/dev/disk/nata/ata-all.c:523-526
 *	ATA_SETMODE(parent, dev)                     (newbus method, ata_if.m)
 *   -> ata_pcichannel_setmode()  sys/dev/disk/nata/ata-pci.c:855-866
 *	ctlr->setmode(dev, ATA_PIO_MAX);             <-- extracted call site
 *	if (mode >= ATA_DMA) ctlr->setmode(dev, mode);
 *   -> ata_cyrix_setmode()  chipsets/ata-cyrix.c:60
 *   -> line 98: piotiming[mode & 0x0f] with mode>=0x08  => OOB .rodata read
 *      ATA_OUTL(...) writes the OOB-sourced constant into the controller
 *      PIO timing register (0x20 + devno*8 in the BMIO port space).
 *
 * The hardware is gated (Cyrix 5530, PCI ID 0x0100780e... actually
 * ATA_CYRIX_5530 per ata-pci.h), so this rig follows the established
 * production-handler dispatch class (DF-2210 national, DF-252x trm,
 * DF-249x nata):
 *
 *  - the production file chipsets/ata-cyrix.c is #included VERBATIM
 *    from the guest /usr/src tree (build.sh verifies the file sha256
 *    against the audited sys/ tree).  The ONLY textual intervention is
 *    `#define ata_cyrix_ident rig_unused_ata_cyrix_ident`, which renames
 *    the never-called ident() entry point to avoid a duplicate global
 *    against the built-in kernel copy; ata_cyrix_setmode itself is
 *    compiled byte-for-byte as written, static, under its production
 *    name.
 *
 *  - the RUNNING KERNEL's OWN ata_cyrix_setmode (local symbol, from
 *    `nm /boot/kernel/kernel`, address injected via sysctl) is ALSO
 *    dispatched -- the shipped binary, zero recompilation.  Its OOB
 *    reads come from the shipped kernel's own .rodata (whose layout
 *    was recovered by objdump: kernel_handler_disasm.txt /
 *    kernel_rodata.txt), and the rig cross-checks the recorded
 *    register write value against the live kernel memory at
 *    tbl_base + 4*index.
 *
 *  - the channel/controller/ata-device state is exactly what the
 *    machine presents on a working Cyrix 5530 channel: ctlr->r_res1
 *    allocated (the guard at ata-cyrix.c:84 passes -- THIS file guards
 *    its derefs, unlike the National clone of DF-2210), ch->dma present
 *    (alignment/max_iosize get overridden), disk present
 *    (ch->devices = ATA_ATA_MASTER) with per-scenario identify data
 *    driving ata_limit_mode() to the PIO mode under test.  The channel
 *    is in the ATA_STALL_QUEUE state with a begin_transaction that
 *    reports the drive accepting SETXFER, so the kernel's own
 *    production ata_controlcmd()/ata_queue_request() (ata-queue.c:100-
 *    115,158-190) run unmodified.  The ATA_OUTL timing writes dispatch
 *    through the real bus_space_write_4() onto a fabricated MEM-space
 *    bus tag + handle: on DragonFly/amd64 bus_space_write_4(tag==MEM)
 *    is `*(volatile u_int32_t *)(handle + offset) = value`, so the
 *    production register writes land in the rig's 128-byte "PCI
 *    register file" (fake_bmio) and get dumped -- the garbage timing
 *    landing in the controller register, captured.
 *
 * Tests (sysctl kern.dfrig_cyx.go=N), all on a STOCK kernel:
 *
 *   1  THE FINDING, shipped kernel handler: identify PIO4/UDMA2 disk,
 *      mode=ATA_PIO_MAX (exactly the first call of the extracted call
 *      site ata-pci.c:863).  Settles at ATA_PIO4 (0x0c) -> index 12 ->
 *      OOB read = udmatiming[2] = 0x00911030 -> regfile[0x20] gets a
 *      UDMA2 timing where piotiming[4]=0x00040010 belongs.
 *   2  THE FINDING, verbatim rig copy: identical dispatch through the
 *      #included production source (byte-identity of the copy).
 *   3  RANGE FLOOR: identify PIO0-only disk -> settles ATA_PIO0 (0x08)
 *      -> index 8 -> OOB read = dmatiming[2] = 0x00002020.
 *   4  FULL extracted call site (ata-pci.c:855-866) with requested
 *      ATA_UDMA2 (a UDMA disk at boot): call 1 = the OOB PIO write
 *      above; call 2 = udmatiming[2] (IN bounds -- the UDMA branch is
 *      correctly indexed) -> contrast: both controller slots end up
 *      holding UDMA timings, the PIO one illegitimately.
 *   5  CONTROL (correct branch): WDMA2-only identify, request
 *      ATA_WDMA2 -> dmatiming[2] in bounds -> regfile[0x24] =
 *      0x00002020 -- the WDMA branch indexes correctly; the defect is
 *      specific to the PIO branch (line 98).
 *
 * After every dispatch the rig prints: the settled mode, the computed
 * index (mode & ATA_MODE_MASK), the CORRECT index (kernel-exported
 * ata_mode2idx()) and the correct value (the five source constants),
 * the captured register-file slots, and a dump of the live kernel
 * piotiming[] window [0..13] with in-bounds/OOB annotation so the
 * recorded write value is attributable to a specific OOB element.
 */

#include <sys/param.h>
#include <sys/bus.h>
#include <sys/bus_dma.h>
#include <sys/bus_resource.h>
#include <sys/callout.h>
#include <sys/endian.h>
#include <sys/kernel.h>
#include <sys/libkern.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/nata.h>
#include <sys/queue.h>
#include <sys/rman.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
#include <sys/bus_private.h>	/* struct bsd_device layout */

#include <bus/pci/pcireg.h>
#include <bus/pci/pcivar.h>

#include "ata-all.h"
#include "ata-pci.h"

/* ------------------------------------------------------------------ */
/* CHECKER NOTE: the production file below is included VERBATIM.       */
/* The single #define renames only the never-called ident() symbol so  */
/* this KLD does not collide with the built-in kernel copy; the        */
/* setmode function under test keeps its production name and body.     */
/* build.sh pins the file by sha256 before compiling.                  */
/* ------------------------------------------------------------------ */
int rig_unused_ata_cyrix_ident(device_t dev);	/* kproto */
#define ata_cyrix_ident rig_unused_ata_cyrix_ident
#include "chipsets/ata-cyrix.c"	/* <-- UNMODIFIED production code */
#undef ata_cyrix_ident

/* the five real PIO timings from the audited source (ata-cyrix.c:67-68),
 * used to (a) verify the kernel .rodata table was found and (b) show
 * the CORRECT value the controller should have received */
static const uint32_t rig_piotiming_src[5] =
    { 0x00009172, 0x00012171, 0x00020080, 0x00032010, 0x00040010 };

/* ------------------------------------------------------------------ */
/* channel HW completion fabrication: with ch->state == ATA_STALL_QUEUE */
/* the PRODUCTION ata_queue_request() (ata-queue.c:100-115) executes   */
/* ATA_R_CONTROL requests synchronously via ch->hw.begin_transaction   */
/* and returns immediately when it reports ATA_OP_FINISHED -- no       */
/* queueing, no tsleep, no interrupt.  Our begin_transaction stands    */
/* for the drive accepting SET FEATURES / SETXFER.  With this in place */
/* even the RUNNING KERNEL's own ata_controlcmd() and                  */
/* ata_queue_request() run unmodified.                                 */
/* ------------------------------------------------------------------ */
static int rig_cmd_seen;		/* begin_transaction dispatches */

static int
rig_begin_transaction(struct ata_request *request)
{
	rig_cmd_seen++;
	request->result = 0;			/* drive accepted SETXFER */
	request->flags |= ATA_R_COMPLETED;
	return ATA_OP_FINISHED;
}

/* ------------------------------------------------------------------ */
/* fabricated controller register file: the production ATA_OUTL at     */
/* ata-cyrix.c:89/93/97 expands to bus_space_write_4(res->r_bustag,    */
/* res->r_bushandle, offset, value); with r_bustag ==                  */
/* X86_64_BUS_SPACE_MEM that is *(volatile u_int32_t *)(handle+off)    */
/* (cpu/x86_64/include/bus_dma.h:404-412), so a MEM tag + kernel       */
/* buffer handle turns the production register writes into captures.   */
/* ------------------------------------------------------------------ */
#define RIG_BMIO_NREG	32			/* 128 bytes: 0x00..0x7f */
static u_int32_t fake_bmio[RIG_BMIO_NREG];
static struct resource fake_res;		/* tag/handle carrier */

/* ------------------------------------------------------------------ */
/* fake device graph -- the exact graph nata hands the chipset driver  */
/* when a disk sits on a working Cyrix 5530 channel:                    */
/*                                                                     */
/*   rig_bus_dev (opaque parent) <- fake_pci_dev (ctlr softc, r_res1)  */
/*        <- fake_ch_dev (struct ata_channel, dma present)             */
/*        <- fake_ata_dev (struct ata_device, unit=ATA_MASTER)         */
/* ------------------------------------------------------------------ */
static struct bsd_device fake_bus_dev, fake_pci_dev, fake_ch_dev;
static struct bsd_device fake_ata_dev;
static struct ata_channel fake_ch;
static struct ata_device fake_atadev;
static struct ata_dma fake_dma;
static struct ata_pci_controller fake_ctlr;

static unsigned long rig_kern_addr;	/* kernel own handler, via nm */
static unsigned long rig_kern_tbl;	/* kernel piotiming[] base */
static int rig_armed;

static void
rig_setup_graph(void)
{
	bzero(&fake_bus_dev, sizeof(fake_bus_dev));
	bzero(&fake_pci_dev, sizeof(fake_pci_dev));
	bzero(&fake_ch_dev, sizeof(fake_ch_dev));
	bzero(&fake_ata_dev, sizeof(fake_ata_dev));
	bzero(&fake_ch, sizeof(fake_ch));
	bzero(&fake_atadev, sizeof(fake_atadev));
	bzero(&fake_ctlr, sizeof(fake_ctlr));
	bzero(&fake_bmio, sizeof(fake_bmio));
	bzero(&fake_res, sizeof(fake_res));

	/* the BMIO resource: MEM-space tag, handle = register file */
	fake_res.r_bustag = 1;		/* X86_64_BUS_SPACE_MEM */
	fake_res.r_bushandle = (bus_space_handle_t)fake_bmio;

	/* pci <- channel <- ata device, exactly as GRANDPARENT walks it */
	fake_pci_dev.parent = &fake_bus_dev;
	fake_pci_dev.softc = &fake_ctlr;
	fake_ch_dev.parent = &fake_pci_dev;
	fake_ch_dev.softc = &fake_ch;
	fake_ata_dev.parent = &fake_ch_dev;
	fake_ata_dev.softc = &fake_atadev;

	/* controller: BMIO resource allocated -- the ata-cyrix.c:84 guard
	 * passes (this file guards its derefs, unlike DF-2210 national) */
	fake_ctlr.r_res1 = &fake_res;

	/* channel: working DMA, disk present, sync CONTROL completion */
	fake_ch.dev = &fake_ch_dev;
	fake_ch.unit = 0;
	fake_ch.devices = ATA_ATA_MASTER;
	fake_ch.state = ATA_STALL_QUEUE;
	fake_ch.hw.begin_transaction = rig_begin_transaction;
	fake_ch.r_io[ATA_BMCMD_PORT].res = &fake_res;
	fake_ch.r_io[ATA_BMCMD_PORT].offset = 0;
	lockinit(&fake_ch.state_mtx, "dfrig_cyx_st", 0, LK_CANRECURSE);
	lockinit(&fake_ch.queue_mtx, "dfrig_cyx_q", 0, LK_CANRECURSE);

	/* ata device: master; identify data set per scenario */
	fake_atadev.dev = &fake_ata_dev;
	fake_atadev.unit = ATA_MASTER;
	fake_atadev.mode = 0xff;
}

/* identify-data presets driving ata_limit_mode() to the mode under
 * test (ata-all.c ata_pmode/ata_wmode/ata_umode) */
static void
rig_identify_pio4_udma2(void)
{
	fake_atadev.param.atavalid = ATA_FLAG_64_70 | ATA_FLAG_88;
	fake_atadev.param.apiomodes = 0x02;	/* PIO4 (ata_pmode -> 0x0c) */
	fake_atadev.param.udmamodes = 0x04;	/* UDMA2 (ata_umode -> 0x42) */
	fake_atadev.param.mwdmamodes = 0x00;
	fake_atadev.param.retired_piomode = 0;
}

static void
rig_identify_pio0_only(void)
{
	/* no valid words 64-70/88, no mwdma, retired_piomode==0:
	 * ata_pmode() -> ATA_PIO0 (0x08), ata_umode() -> -1 */
	fake_atadev.param.atavalid = 0;
	fake_atadev.param.apiomodes = 0x00;
	fake_atadev.param.udmamodes = 0x00;
	fake_atadev.param.mwdmamodes = 0x00;
	fake_atadev.param.retired_piomode = 0;
}

static void
rig_identify_wdma2_only(void)
{
	fake_atadev.param.atavalid = 0;
	fake_atadev.param.apiomodes = 0x00;
	fake_atadev.param.udmamodes = 0x00;
	fake_atadev.param.mwdmamodes = 0x04;	/* WDMA2 (ata_wmode -> 0x22) */
	fake_atadev.param.retired_piomode = 0;
}

/* print the live kernel piotiming[] window with annotation */
static void
rig_dump_kernel_table(void)
{
	volatile u_int32_t *tbl = (volatile u_int32_t *)rig_kern_tbl;
	int i;

	if (rig_kern_tbl == 0)
		return;
	if (tbl[0] != rig_piotiming_src[0] || tbl[1] != rig_piotiming_src[1] ||
	    tbl[2] != rig_piotiming_src[2] || tbl[3] != rig_piotiming_src[3] ||
	    tbl[4] != rig_piotiming_src[4]) {
		kprintf("dfrig_cyx: TABLE-CHECK FAILED at 0x%lx: [0..4] != "
			"source constants\n", rig_kern_tbl);
		return;
	}
	kprintf("dfrig_cyx: kernel .rodata piotiming[] @ 0x%lx "
		"(verified: [0..4] == source constants):\n", rig_kern_tbl);
	for (i = 0; i < 14; i++) {
		const char *what;
		switch (i) {
		case 0: case 1: case 2: case 3: case 4:
			what = "IN BOUNDS (piotiming[i])"; break;
		case 5: what = "OOB: pad hole"; break;
		case 6: what = "OOB: dmatiming[0]"; break;
		case 7: what = "OOB: dmatiming[1]"; break;
		case 8: what = "OOB: dmatiming[2]  <-- PIO0 (0x08) lands here"; break;
		case 9: what = "OOB: pad hole      <-- PIO1 (0x09) lands here"; break;
		case 10: what = "OOB: udmatiming[0] <-- PIO2 (0x0a) lands here"; break;
		case 11: what = "OOB: udmatiming[1] <-- PIO3 (0x0b) lands here"; break;
		case 12: what = "OOB: udmatiming[2] <-- PIO4 (0x0c) lands here"; break;
		default: what = "OOB: unrelated driver constant"; break;
		}
		kprintf("dfrig_cyx:   [%2d] @+0x%02x = 0x%08x  %s\n",
			i, i * 4, tbl[i], what);
	}
}

/* post-dispatch report: index used vs correct, register file dump */
static void
rig_report(const char *tag, int requested)
{
	int settled = fake_atadev.mode;
	int idx = settled & ATA_MODE_MASK;
	int cidx = (settled >= 0) ? ata_mode2idx(settled) : -1;
	u_int32_t pio_slot = fake_bmio[0x20 / 4];
	u_int32_t dma_slot = fake_bmio[0x24 / 4];
	u_int32_t oob_src = 0;

	kprintf("dfrig_cyx[%s]: requested mode=0x%02x settled mode=0x%02x "
		"controlcmd_seen=%d\n", tag, requested, settled, rig_cmd_seen);
	kprintf("dfrig_cyx[%s]: INDEX USED  = mode & ATA_MODE_MASK = 0x%02x & "
		"0x0f = %d   (piotiming[] has 5 elements: 0..4)%s\n",
		tag, settled, idx, (idx >= 5 && settled < ATA_WDMA0) ?
		"  <<< OUT OF BOUNDS" : "");
	if (settled < ATA_WDMA0) {
		kprintf("dfrig_cyx[%s]: INDEX RIGHT = ata_mode2idx(mode)   = "
			"%d   (the idiom every sibling uses)\n", tag, cidx);
		if (cidx >= 0 && cidx <= 4)
			kprintf("dfrig_cyx[%s]: CORRECT VALUE piotiming[%d] = "
				"0x%08x (source constant)\n", tag, cidx,
				rig_piotiming_src[cidx]);
	}
	kprintf("dfrig_cyx[%s]: controller register file (fabricated BMIO "
		"MEM space):\n", tag);
	kprintf("dfrig_cyx[%s]:   reg 0x20 (PIO timing, devno 0)  = 0x%08x\n",
		tag, pio_slot);
	kprintf("dfrig_cyx[%s]:   reg 0x24 (DMA timing, devno 0)  = 0x%08x\n",
		tag, dma_slot);
	if (rig_kern_tbl && settled < ATA_WDMA0 && idx >= 5) {
		oob_src = ((volatile u_int32_t *)rig_kern_tbl)[idx];
		kprintf("dfrig_cyx[%s]: OOB SOURCE kernel_mem[piotiming+%d*4] = "
			"0x%08x%s\n", tag, idx, oob_src,
			(oob_src == pio_slot) ?
			"  == reg 0x20 write  (OOB read CONFIRMED)" :
			"  (MISMATCH vs reg 0x20?!)");
	}
}

/* one direct dispatch of the handler under test */
static void
rig_dispatch(int via_kernel, int identify, int mode, const char *tag)
{
	/* production ata-dmainit defaults (ata-dma.c:75-83) so the run
	 * shows the chipset OVERRIDING them, as on real hardware */
	bzero(&fake_dma, sizeof(fake_dma));
	fake_dma.alignment = 2;
	fake_dma.max_iosize = 128 * DEV_BSIZE;
	fake_ch.dma = &fake_dma;
	fake_atadev.mode = 0xff;
	bzero(&fake_atadev.param, sizeof(fake_atadev.param));
	rig_cmd_seen = 0;
	bzero(fake_bmio, sizeof(fake_bmio));

	if (identify == 0)
		rig_identify_pio4_udma2();
	else if (identify == 1)
		rig_identify_pio0_only();
	else
		rig_identify_wdma2_only();

	kprintf("dfrig_cyx[%s]: BEFORE dispatch: ch->dma=%p ctlr->r_res1=%p "
		"(guard ata-cyrix.c:84 passes) atadev->unit=%d\n",
		tag, fake_ch.dma, fake_ctlr.r_res1, fake_atadev.unit);
	kprintf("dfrig_cyx[%s]: dispatching production ata_cyrix_setmode"
		"(dev, 0x%02x) %s\n", tag, mode,
		via_kernel ? "(RUNNING KERNEL own copy)" :
		"(verbatim #included copy)");

	if (via_kernel)
		((void (*)(device_t, int))rig_kern_addr)(&fake_ata_dev, mode);
	else
		ata_cyrix_setmode(&fake_ata_dev, mode);

	kprintf("dfrig_cyx[%s]: RETURNED: ch->dma->alignment=%u "
		"max_iosize=%u\n", tag, fake_dma.alignment,
		fake_dma.max_iosize);
	rig_report(tag, mode);
}

/* extracted call site: ata_pcichannel_setmode(), ata-pci.c:855-866,
 * verbatim extraction (static in the kernel, cannot be linked) */
static void
rig_extracted_callsite(device_t dev, int mode)
{
	struct ata_pci_controller *ctlr = device_get_softc(GRANDPARENT(dev));

	ctlr->setmode(dev, ATA_PIO_MAX);
	if (mode >= ATA_DMA)
		ctlr->setmode(dev, mode);
}

/* full-call-site scenario: two ctlr->setmode dispatches as at boot */
static void
rig_dispatch_full(int via_kernel, const char *tag)
{
	bzero(&fake_dma, sizeof(fake_dma));
	fake_dma.alignment = 2;
	fake_dma.max_iosize = 128 * DEV_BSIZE;
	fake_ch.dma = &fake_dma;
	fake_atadev.mode = ATA_UDMA2;	/* requested mode, as at boot */
	bzero(&fake_atadev.param, sizeof(fake_atadev.param));
	rig_identify_pio4_udma2();
	rig_cmd_seen = 0;
	bzero(fake_bmio, sizeof(fake_bmio));
	fake_ctlr.setmode = via_kernel ?
		(void (*)(device_t, int))rig_kern_addr : ata_cyrix_setmode;

	kprintf("dfrig_cyx[%s]: BEFORE: requested mode=ATA_UDMA2 -- full "
		"extracted call site ata-pci.c:855-866 "
		"(call1 = ctlr->setmode(dev, ATA_PIO_MAX), "
		"call2 = ctlr->setmode(dev, 0x42))\n", tag);

	rig_extracted_callsite(&fake_ata_dev, fake_atadev.mode);

	kprintf("dfrig_cyx[%s]: RETURNED: alignment=%u max_iosize=%u "
		"controlcmd_seen=%d (2 SETXFER = normal)\n",
		tag, fake_dma.alignment, fake_dma.max_iosize, rig_cmd_seen);
	rig_report(tag, ATA_UDMA2);
}

/* ------------------------------------------------------------------ */
/* sysctl plumbing                                                     */
/* ------------------------------------------------------------------ */
static int
rig_go_sysctl(SYSCTL_HANDLER_ARGS)
{
	int val = 0, error;

	error = sysctl_handle_int(oidp, &val, 0, req);
	if (error || !req->newptr)
		return error;

	rig_armed = 1;
	rig_dump_kernel_table();
	switch (val) {
	case 1:
		rig_dispatch(1, 0, ATA_PIO_MAX, "go1");
		break;
	case 2:
		rig_dispatch(0, 0, ATA_PIO_MAX, "go2");
		break;
	case 3:
		rig_dispatch(1, 1, ATA_PIO_MAX, "go3");
		break;
	case 4:
		rig_dispatch_full(1, "go4");
		break;
	case 5:
		rig_dispatch(1, 2, ATA_WDMA2, "go5");
		break;
	default:
		return EINVAL;
	}
	return 0;
}

static SYSCTL_NODE(_kern, OID_AUTO, dfrig_cyx, CTLFLAG_RW, 0,
		   "DF-2213 ata-cyrix rig");

SYSCTL_ULONG(_kern_dfrig_cyx, OID_AUTO, addr, CTLFLAG_RW, &rig_kern_addr, 0,
	     "address of the kernel own ata_cyrix_setmode (from nm)");

SYSCTL_ULONG(_kern_dfrig_cyx, OID_AUTO, tbl, CTLFLAG_RW, &rig_kern_tbl, 0,
	     "address of the kernel .rodata piotiming[] table "
	     "(from objdump of the shipped handler)");

SYSCTL_PROC(_kern_dfrig_cyx, OID_AUTO, go, CTLTYPE_INT | CTLFLAG_RW,
	    0, 0, rig_go_sysctl, "I", "fire a scenario");

/* ------------------------------------------------------------------ */
/* module                                                              */
/* ------------------------------------------------------------------ */
static int
dfrig_cyx_load(module_t mod, int what, void *arg)
{
	switch (what) {
	case MOD_LOAD:
		rig_setup_graph();
		kprintf("dfrig_cyx: DF-2213 rig loaded "
			"(production chipsets/ata-cyrix.c:98 included "
			"verbatim; kernel handler addr=0x%lx tbl=0x%lx)\n",
			rig_kern_addr, rig_kern_tbl);
		return 0;
	case MOD_UNLOAD:
		if (rig_armed)
			return EBUSY;
		lockuninit(&fake_ch.state_mtx);
		lockuninit(&fake_ch.queue_mtx);
		return 0;
	default:
		return 0;
	}
}

static moduledata_t dfrig_cyx_mod = { "dfrig_cyx", dfrig_cyx_load, 0 };
DECLARE_MODULE(dfrig_cyx, dfrig_cyx_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);
