DF-2213 / dfrig_cyx.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 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 | /* * 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); |