⬢ DragonFlyBSD Kernel Audit
DF-2420 / df2420_rig.c
← back to finding ↓ download raw
/*
 * df2420_rig.c - production-state KLD rig for DF-2420 / DF-4203 verification.
 *
 * It does NOT contain any copy of ahci driver logic. It reaches the REAL
 * live driver state of ahci0 (devclass_find("ahci") -> softc -> ports) and
 * calls the REAL exported ahci_ata_get_xfer()/ahci_ata_put_xfer() symbols.
 *
 * Sysctl interface (root only):  sysctl dev.df2420rig.cmd="<command>"
 *   hold <port>        - drain ap_ccb_free completely by acquiring every
 *                        ata_xfer (this is exactly what happens on real
 *                        hardware when device ncqdepth < sc_ncmds prunes
 *                        slots, or when >ncmds CCBs are in flight).
 *   release            - put all held ata_xfers back (restores driver state)
 *   info               - dump port/slot state
 *   arm <port> <secs>  - DF-4203: force at->at_probe = ATA_PROBE_NEED_IDENT
 *                        in a loop for <secs> seconds (simulates the only
 *                        producer of NEED_IDENT, ahci_pm.c:876, which needs
 *                        SATA port-multiplier hardware the guest lacks).
 *
 * DF-2420 production-path trigger while 'hold' is active:
 *   ./ahcipoc read10        (XPT_SCSI_IO -> ahci_xpt_scsi_disk_io ->
 *                             ahci_ata_get_xfer() == NULL -> xa->flags deref)
 *   ./ahcipoc -d /dev/pass1 atapi 00 00 00 00 00 00
 *                           (XPT_SCSI_IO -> ahci_xpt_scsi_atapi_io ->
 *                             xa = ahci_ata_get_xfer(); fis = xa->fis deref)
 *
 * Build (guest): see build.sh (uses sys/conf/kmod.mk, includes the real
 * sys/dev/disk/ahci/ahci.h so all struct offsets are the production ones).
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
#include <sys/malloc.h>
#include <sys/thread.h>
#include <sys/bus.h>

#include "ahci.h"			/* real driver header */

#define MAXHELD	64

static struct ahci_softc	*rig_sc;
static struct ata_xfer		*held[MAXHELD];
static int			nheld;
static int			held_port = -1;

static struct ahci_softc *
rig_get_sc(void)
{
	devclass_t dc;

	if (rig_sc)
		return (rig_sc);
	dc = devclass_find("ahci");
	if (dc == NULL) {
		kprintf("df2420rig: no ahci devclass (driver not loaded?)\n");
		return (NULL);
	}
	rig_sc = devclass_get_softc(dc, 0);
	if (rig_sc == NULL)
		kprintf("df2420rig: ahci0 softc is NULL\n");
	return (rig_sc);
}

static int
rig_cmd(SYSCTL_HANDLER_ARGS)
{
	char cmd[128];
	struct ahci_softc *sc;
	struct ahci_port *ap;
	int error, i, port, secs;

	memset(cmd, 0, sizeof(cmd));
	error = sysctl_handle_string(oidp, cmd, sizeof(cmd), req);
	if (error != 0 || req->newptr == NULL)
		return (error);

	sc = rig_get_sc();
	if (sc == NULL)
		return (ENOENT);

	if (ksscanf(cmd, "hold %d", &port) == 1) {
		if (port < 0 || port >= sc->sc_numports)
			return (EINVAL);
		ap = sc->sc_ports[port];
		if (ap == NULL)
			return (ENOENT);
		if (nheld)
			return (EBUSY);
		for (i = 0; i < MAXHELD; ++i) {
			held[i] = ahci_ata_get_xfer(ap, NULL);
			if (held[i] == NULL)
				break;
		}
		nheld = i;
		held_port = port;
		kprintf("df2420rig: port %d drained - holding %d ata_xfers, "
		    "ap_ccb_free is now EMPTY\n", port, nheld);
	} else if (!strncmp(cmd, "release", 7)) {
		for (i = 0; i < nheld; ++i)
			ahci_ata_put_xfer(held[i]);
		kprintf("df2420rig: released %d ata_xfers (port %d)\n",
		    nheld, held_port);
		nheld = 0;
		held_port = -1;
	} else if (!strncmp(cmd, "info", 4)) {
		for (i = 0; i < sc->sc_numports; ++i) {
			ap = sc->sc_ports[i];
			if (ap == NULL)
				continue;
			kprintf("df2420rig: port %d (%s) type=%d probe=%d "
			    "ata0.type=%d probe=%d active=%08x sactive=%08x\n",
			    i, ap->ap_name, ap->ap_type, ap->ap_probe,
			    ap->ap_ata[0] ? ap->ap_ata[0]->at_type : -1,
			    ap->ap_ata[0] ? ap->ap_ata[0]->at_probe : -1,
			    ap->ap_active, ap->ap_sactive);
		}
		kprintf("df2420rig: nheld=%d held_port=%d\n", nheld, held_port);
	} else if (ksscanf(cmd, "arm %d %d", &port, &secs) == 2) {
		if (port < 0 || port >= sc->sc_numports)
			return (EINVAL);
		ap = sc->sc_ports[port];
		if (ap == NULL || ap->ap_ata[0] == NULL)
			return (ENOENT);
		kprintf("df2420rig: arming NEED_IDENT storm on port %d for "
		    "%d seconds\n", port, secs);
		for (i = 0; i < secs * 1000; ++i) {
			/*
			 * The ONLY producer of ATA_PROBE_NEED_IDENT in the
			 * production driver is ahci_pm.c:876 (PM hard reset
			 * completion), which requires port-multiplier
			 * hardware.  Force the same state here.
			 */
			if (ap->ap_ata[0]->at_probe == ATA_PROBE_GOOD)
				ap->ap_ata[0]->at_probe = ATA_PROBE_NEED_IDENT;
			tsleep(&rig_sc, 0, "arm", hz / 1000 + 1);
		}
		kprintf("df2420rig: arm storm finished (port %d probe now %d)\n",
		    port, ap->ap_ata[0]->at_probe);
	} else {
		return (EINVAL);
	}
	return (0);
}

static int
df2420rig_load(module_t mod, int what, void *arg)
{
	switch (what) {
	case MOD_LOAD:
		SYSCTL_ADD_PROC(NULL, SYSCTL_STATIC_CHILDREN(_dev),
		    OID_AUTO, "df2420rig",
		    CTLTYPE_STRING | CTLFLAG_RW, NULL, 0,
		    rig_cmd, "A", "df2420 verification rig");
		kprintf("df2420rig: loaded (sysctl dev.df2420rig.cmd)\n");
		break;
	case MOD_UNLOAD:
		if (nheld) {
			kprintf("df2420rig: refusing unload, %d xfers held\n",
			    nheld);
			return (EBUSY);
		}
		kprintf("df2420rig: unloaded\n");
		break;
	}
	return (0);
}

static moduledata_t df2420rig_mod = { "df2420rig", df2420rig_load, 0 };
DECLARE_MODULE(df2420rig, df2420rig_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);