DF-2420 / df2420_rig.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | /* * 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); |