⬢ DragonFlyBSD Kernel Audit
DF-5603 / df5603_harness.c
← back to finding ↓ download raw
/*
 * DF-5603 - production-code KLD harness (no SD hardware required).
 *
 * Drives the stock, compiled-in mmc_switch() / mmc_switch_status()
 * helpers with a card-derived switch timeout of 0 - exactly the value
 * mmc.c computes at mmc.c:1663-1665 when a crafted (or mis-programmed)
 * eMMC reports CSD spec_vers >= 6 and EXT_CSD[EXT_CSD_GEN_CMD6_TIME]=0:
 *
 *	ivar->cmd6_time = 10 * ivar->raw_ext_csd[EXT_CSD_GEN_CMD6_TIME];
 *
 * which is 0, and then hands to the helpers at mmc.c:1672, :709, :828
 * and :836 as the "timeout" argument:
 *
 *	mmc_switch(..., ivar->cmd6_time, true);
 *	mmc_switch_status(..., ivar->cmd6_time);
 *
 * mmc_subr.c:158 / :187 enforce the "timeout != 0" contract with
 * KASSERT(...), which on DragonFly's stock X86_64_GENERIC kernel
 * (sys/config/X86_64_GENERIC:56 "options INVARIANTS") is a live
 * panic - i.e. card enumeration turns into a guaranteed kernel panic.
 *
 * The two device_t arguments are two DISTINCT real devices (brdev !=
 * reqdev) so that the helpers error-log branch - which dereferences
 * device_get_softc(brdev) as struct mmc_softc* - is never taken, and
 * the bridge dispatch (MMCBUS_WAIT_FOR_REQUEST on the parent of brdev)
 * falls back to kobj_error_method() == ENXIO (kern/subr_kobj.c:69-72),
 * making the post-KASSERT code path harmless when the KASSERT is
 * absent (patched kernel).  This keeps the harness faithful to the
 * production call shape without a physical card.
 */

#include <sys/param.h>
#include <sys/module.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/bus.h>

#include <bus/mmc/mmc_subr.h>
#include <bus/mmc/mmcreg.h>

#define	DF5603_RCA	1	/* any rca */

static device_t
df5603_pick(const char *cn, int unit)
{
	devclass_t dc;

	dc = devclass_find(cn);
	if (dc == NULL)
		return (NULL);
	return (devclass_get_device(dc, unit));
}

static int
df5603_modevent(module_t mod, int type, void *arg)
{
	device_t brdev, reqdev;
	int err;

	switch (type) {
	case MOD_LOAD:
		/* Two distinct real devices, parent classes of which do
		 * not implement mmcbus_if, so dispatch -> graceful ENXIO. */
		brdev = df5603_pick("acpi", 0);
		reqdev = df5603_pick("acpi", 1);
		if (reqdev == NULL)
			reqdev = df5603_pick("pcib", 0);
		if (reqdev == NULL)
			reqdev = df5603_pick("isa", 0);
		if (brdev == NULL || reqdev == NULL || brdev == reqdev) {
			kprintf("DF-5603: no usable device pair, aborting\n");
			return (ENXIO);
		}

		kprintf("DF-5603: calling mmc_switch(timeout=0) as mmc.c does "
		    "for a card with EXT_CSD[248]=0 ...\n");
		err = mmc_switch(brdev, reqdev, DF5603_RCA,
		    EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING,
		    EXT_CSD_HS_TIMING_HS, 0, true);
		kprintf("DF-5603: mmc_switch returned %d WITHOUT panicking "
		    "(KASSERT not compiled in or fix applied)\n", err);

		kprintf("DF-5603: calling mmc_switch_status(timeout=0) "
		    "(second KASSERT site) ...\n");
		err = mmc_switch_status(brdev, reqdev, DF5603_RCA, 0);
		kprintf("DF-5603: mmc_switch_status returned %d WITHOUT "
		    "panicking\n", err);
		break;
	default:
		break;
	}
	return (0);
}

static moduledata_t df5603_mod = {
	"df5603",
	df5603_modevent,
	NULL
};

DECLARE_MODULE(df5603, df5603_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);