DF-2489 / dfrig2489.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 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 | /* * dfrig2489.c -- DF-2489 verification rig * (sys/dev/disk/nata/ata-disk.c:147 and :540) * * Finding under test: * * ad_get_geometry() (ata-disk.c:462-500) copies adp->heads and * adp->sectors straight out of the device-supplied ATA IDENTIFY * words (u_int16_t param.heads / param.sectors, else-branch at * ata-disk.c:477-478; the current_* branch at :469-475 is guarded, * the raw branch is not -- and only the raw branch is taken whenever * word 53 does not report words 54-58 valid, or current_heads / * current_sectors are zero). A device reporting heads==0 or * sectors==0 (word 3 / word 6) then makes ad_attach() execute * * info.d_ncylinders = adp->total_secs/(adp->heads*adp->sectors); * ^^ ata-disk.c:147 * * and, under bootverbose, ad_describe() execute the identical * divisor * * adp->total_secs / (adp->heads * adp->sectors) * ^^ ata-disk.c:540 * * -- an integer divide by zero: x86 #DE, "Fatal trap 18: integer * divide fault", at device attach (boot or hot-plug). Nothing * between ata_getparam() (ata-all.c:590, copies IDENTIFY verbatim, * only requires a printable model byte) and ad_attach() validates * the geometry words. * * Rig design (established nata verbatim-interposition class, cf. * DF-2210 ata-national, DF-249x/DF-2496 nata): * * 1. FULL PRODUCTION FUNNEL dispatch (tests 1/2/3): the real * kernel's own ata_identify() (global, ata-all.c:683) is called * on a fabricated ata channel. It runs the REAL kmalloc'ed * ata_device, the REAL ata_getparam() (ATA_ATA_IDENTIFY served by * a hw.begin_transaction stub -- exactly the seam a QEMU IDE * disk answers), the REAL le16toh/bswap/btrim string parse, the * REAL newbus child creation (device_add_child on a channel * carrying the real "ata" devclass), bus_generic_attach, the REAL * ad_probe() and -- the point -- the RUNNING KERNEL'S OWN * ad_attach() (nm: t ad_attach), whose inlined ad_get_geometry * computes the divisor from the crafted IDENTIFY words and whose * ata-disk.c:147 idiv traps. Everything from the IDENTIFY bytes * to the fault is shipped production code; the only fabrications * are the channel HW stubs (begin_transaction / the ata_if setmode * and locking defaults / the device graph), none of which sit * between the cited division and its device-supplied input. * * 2. VERBATIM COPY dispatch (tests 4/5): the production * sys/dev/disk/nata/ata-disk.c is #included UNMODIFIED (module * plumbing neutralized, the unused global ad_devclass renamed -- * build.sh sha256-checks the file against the audited sys/ tree), * so this KLD also carries its own byte-identical ad_attach / * ad_describe / ad_get_geometry, driven directly on the same * fabricated post-getparam state (DF-2496 test22 shape). * * 3. DESCRIBE variant (test 5): fabricated ad_softc with heads==0 * (exactly what production ad_get_geometry leaves after a * heads==0 IDENTIFY) + bootverbose=1 -> ad_describe():540 traps; * on the stock kernel this site is shadowed by :147 during * attach, but it is the SAME unvalidated divisor and must be * fixed with it (a :147-only fix still panics every bootverbose * boot). * * Fabricated machine state (what a real ata channel looks like to * nata when a PIO-only disk sits on it): * * rig bus (kobj null class) <- fake_pci_dev (devclass NULL) * <- fake_ch_dev (struct ata_channel softc, devclass "ata", * state=ATA_IDLE, dma=NULL [PIO-only channel, legitimate, * ata-dma.c:153], hw.begin_transaction=rig stub, * production ata_queue_init() locks/queue) * <- [ata_identify creates the REAL ad child here] * * Tests (sysctl kern.dfrig2489.go=N; read kern.dfrig2489.status): * * 1 CONTROL, full funnel: crafted IDENTIFY with real geometry * (16383/16/63, LBA 0x2000000) -> kernel own ad_attach runs to * completion: ad0 created, describe line + bootverbose * "[33230C/16H/63S]" line, subdisk child attaches, RAID * metadata scan (reads served as zeros) finds nothing. Expect * RETURNED, no panic. * 2 THE FINDING, full funnel, heads==0 (IDENTIFY word 3 = 0): * kernel own ad_attach -> trap 18 at ata-disk.c:147. * 3 THE FINDING, full funnel, sectors==0 (word 6 = 0): same trap. * 4 THE FINDING, verbatim rig copy ad_attach, heads==0: identical * trap in the rig-compiled production copy (byte-identity). * 5 THE FINDING, verbatim rig copy ad_describe, heads==0 ad_softc, * bootverbose: trap 18 at ata-disk.c:540. * * 2/3/4/5 are FATAL (one per boot); proof = serial console panic. * Module is loaded AT BOOT via /boot/loader.conf (on this INVARIANTS * guest runtime kldload GPFs); dispatch at runtime via sysctl onto a * dedicated worker thread that holds the MP token (the boot-attach * context -- the built-in atapicam driver's attach asserts it). */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/systm.h> #include <sys/malloc.h> #include <sys/sysctl.h> #include <sys/bus.h> #include <sys/bus_private.h> /* struct bsd_device layout */ #include <sys/conf.h> /* struct cdev / si_drv1 */ #include <sys/bio.h> #include <sys/buf.h> #include <sys/device.h> /* struct dev_strategy_args */ #include <sys/callout.h> #include <sys/lock.h> #include <sys/kthread.h> #include <sys/mplock2.h> /* get_mplock: boot-attach context */ #include <sys/nata.h> /* NOTE: nata headers (ata-all.h/ata-pci.h) have NO include guards; * the production ata-disk.c below includes ata-all.h itself, so all * further nata includes happen AFTER it, exactly once. */ /* ------------------------------------------------------------------ */ /* neutralize the production file's module/driver plumbing so this KLD */ /* registers nothing that clashes with the built-in ad driver, and */ /* rename its (now unreferenced) global ad_devclass definition. */ /* The functions under test keep their production names and bodies. */ /* ------------------------------------------------------------------ */ #undef DRIVER_MODULE #undef DECLARE_MODULE #undef MODULE_DEPEND #undef MODULE_VERSION #define DRIVER_MODULE(name, busname, driver, devclass, evh, arg) /* rig */ #define DECLARE_MODULE(name, data, sub, order) /* rig */ #define MODULE_DEPEND(name, m, v1, v2, v3) /* rig */ #define MODULE_VERSION(m, v) /* rig */ /* rename the production file's global devclass definition (its only * reference, DRIVER_MODULE(ad, ata, ...), is neutralized above); the * extern in ata-disk.h and the definition in ata-disk.c rename in * lock inside this TU, avoiding a duplicate global against the * built-in kernel copy */ #define ad_devclass rig_unused_ad_devclass #include "ata-disk.c" /* <-- UNMODIFIED production code */ #undef ad_devclass #include "ata-pci.h" /* struct ata_pci_controller (once) */ #undef DRIVER_MODULE #undef DECLARE_MODULE #undef MODULE_DEPEND #undef MODULE_VERSION /* ------------------------------------------------------------------ */ /* channel HW seam: what the drive answers. IDENTIFY is served from */ /* the crafted image; data READs are served as zeros (a plain disk */ /* with no ataraid metadata); control commands succeed. Everything */ /* else (queueing, locking, completion, taskqueue, retries, status */ /* decoding) is the kernel's own ata_queue_request()/ata_start(). */ /* ------------------------------------------------------------------ */ static struct ata_params rig_params; /* desired FINAL parsed words */ static u_int8_t rig_identify_img[512]; /* device-side IDENTIFY image */ static int rig_identify_served, rig_reads_served, rig_ctl_served; /* device delivers ATA strings with each byte pair swapped; * ata_getparam() bswap()s them back (ata-all.c:631-635) */ static void rig_swap_str(u_int8_t *dst, const char *src, int len) { int i; for (i = 0; i < len; i += 2) { if (i + 1 < len) { dst[i] = src[i + 1]; dst[i + 1] = src[i]; } else { dst[i] = src[i]; } } } /* variant: 0 = sane geometry (control), 1 = heads==0, 2 = sectors==0 */ static void rig_build_params(int variant) { bzero(&rig_params, sizeof(rig_params)); rig_params.config = 0x0040; /* non-ATAPI direct disk */ rig_params.cylinders = 16383; rig_params.heads = (variant == 1) ? 0 : 16; /* IDENTIFY word 3 */ rig_params.sectors = (variant == 2) ? 0 : 63; /* IDENTIFY word 6 */ /* word 53: words 64-70 & 88 valid, 54-58 NOT valid -> forces the * unguarded else-branch of ad_get_geometry (ata-disk.c:476-480) */ rig_params.atavalid = ATA_FLAG_64_70 | ATA_FLAG_88; rig_params.apiomodes = 0x0002; /* PIO4 */ rig_params.sectors_intr = 16; /* word 47: 16 secs/int */ rig_params.lba_size_1 = 0x0000; /* word 60/61 = 0x2000000 */ rig_params.lba_size_2 = 0x0200; rig_params.version_major = 0x007e; /* ATA/ATAPI-6 */ bcopy("DF2489SERIAL00000001", rig_params.serial, 20); bcopy(variant == 0 ? "DF2489 SANE GEOMETRY" : "DF2489 ZEROGEOM DISK", rig_params.model, 20); bcopy("FW000001", rig_params.revision, 8); } static void rig_make_identify(void) { size_t off; bzero(rig_identify_img, sizeof(rig_identify_img)); bcopy(&rig_params, rig_identify_img, umin(sizeof(rig_params), sizeof(rig_identify_img))); /* strings pairwise swapped, the way the wire delivers them */ off = __offsetof(struct ata_params, serial); rig_swap_str(rig_identify_img + off, "DF2489SERIAL00000001", 20); off = __offsetof(struct ata_params, revision); rig_swap_str(rig_identify_img + off, "FW000001", 8); off = __offsetof(struct ata_params, model); rig_swap_str(rig_identify_img + off, (const char *)&rig_params.model, 40); } static int rig_begin_transaction(struct ata_request *request) { kprintf("dfrig2489: hw: cmd=0x%02x feature=0x%02x count=%u " "lba=%ju bytecount=%u flags=0x%x\n", request->u.ata.command, request->u.ata.feature, request->u.ata.count, (uintmax_t)request->u.ata.lba, request->bytecount, request->flags); if (request->flags & ATA_R_READ) { if (request->u.ata.command == ATA_ATA_IDENTIFY) { bcopy(rig_identify_img, request->data, umin(request->bytecount, sizeof(rig_identify_img))); rig_identify_served++; } else { /* plain-disk data: zeros, no ataraid magic */ bzero(request->data, request->bytecount); rig_reads_served++; } request->donecount = request->bytecount; } else { rig_ctl_served++; /* SETFEATURES / SET_MULTI accepted */ } request->status = ATA_S_READY; /* 0x40, no error bits */ request->error = 0; request->result = 0; return ATA_OP_FINISHED; } /* ------------------------------------------------------------------ */ /* fabricated channel kobj ops: the ata_if defaults, exactly what a */ /* channel without a chipset driver gets (ata_if.m CODE blocks) */ /* ------------------------------------------------------------------ */ static int rig_ata_locking(device_t dev, int mode) { struct ata_channel *ch = device_get_softc(dev); return ch->unit; /* interface DEFAULT ata_null_locking */ } static void rig_ata_setmode(device_t parent, device_t dev) { struct ata_device *atadev = device_get_softc(dev); /* interface DEFAULT ata_null_setmode (ata_if.m) */ atadev->mode = ata_limit_mode(dev, atadev->mode, ATA_PIO_MAX); kprintf("dfrig2489: rig ata setmode default -> mode=0x%02x\n", atadev->mode); } static struct kobj_method rig_ata_methods[] = { { &ata_setmode_desc, (kobjop_t)rig_ata_setmode }, { &ata_locking_desc, (kobjop_t)rig_ata_locking }, { NULL, NULL } }; static struct kobj_class rig_ata_class = { "dfrig2489_atach", rig_ata_methods, 0, NULL, 0, NULL }; static struct kobj_ops rig_ata_ops; /* null class for the fabricated pci/bus devices (never dispatched on * these paths; kobj falls back to kobj_error_method) */ static struct kobj_method rig_null_methods[] = { { NULL, NULL } }; static struct kobj_class rig_null_class = { "dfrig2489_null", rig_null_methods, 0, NULL, 0, NULL }; static struct kobj_ops rig_null_ops; static struct kobj_method rig_dummy_method = { NULL, NULL }; /* ------------------------------------------------------------------ */ /* fabricated device graph */ /* ------------------------------------------------------------------ */ static struct bsd_device fake_bus_dev, fake_pci_dev, fake_ch_dev; static struct ata_pci_controller fake_ctlr; static struct ata_channel fake_ch; static char ch_nameunit[] = "ata9f"; static char pci_nameunit[] = "pcid9f"; static int rig_prepare(int variant) { int i; devclass_t ata_dc = devclass_find("ata"); if (ata_dc == NULL) { kprintf("dfrig2489: devclass \"ata\" not found\n"); return ENXIO; } { /* preserve the lockinit'ed locks across the reset */ struct lock saved_state = fake_ch.state_mtx; struct lock saved_queue = fake_ch.queue_mtx; bzero(&fake_ch, sizeof(fake_ch)); fake_ch.state_mtx = saved_state; fake_ch.queue_mtx = saved_queue; } 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_ctlr, sizeof(fake_ctlr)); /* kobj ops caches pre-filled so kernel kobj lookups through these * devices resolve via the class method lists (DF-2210 pattern) */ for (i = 0; i < KOBJ_CACHE_SIZE; i++) { rig_ata_ops.cache[i] = &rig_dummy_method; rig_null_ops.cache[i] = &rig_dummy_method; } rig_ata_ops.cls = &rig_ata_class; rig_null_ops.cls = &rig_null_class; fake_bus_dev.ops = (kobj_ops_t)&rig_null_ops; fake_pci_dev.ops = (kobj_ops_t)&rig_null_ops; fake_ch_dev.ops = (kobj_ops_t)&rig_ata_ops; fake_pci_dev.parent = &fake_bus_dev; fake_pci_dev.softc = &fake_ctlr; fake_pci_dev.devclass = NULL; /* not pci/atapci: generic scan */ fake_pci_dev.nameunit = pci_nameunit; TAILQ_INIT(&fake_pci_dev.children); fake_ch_dev.parent = &fake_pci_dev; fake_ch_dev.softc = &fake_ch; fake_ch_dev.devclass = ata_dc; /* real "ata" bus devclass */ fake_ch_dev.unit = 0; fake_ch_dev.nameunit = ch_nameunit; TAILQ_INIT(&fake_ch_dev.children); /* the channel a PIO-only disk lives on */ fake_ch.dev = &fake_ch_dev; fake_ch.unit = 0; fake_ch.devices = ATA_ATA_MASTER; /* disk present on master */ fake_ch.state = ATA_IDLE; /* normal channel state */ fake_ch.dma = NULL; /* PIO-only channel (ata-dma.c:153) */ fake_ch.hw.begin_transaction = rig_begin_transaction; ata_queue_init(&fake_ch); /* PRODUCTION */ /* serve the crafted IDENTIFY */ rig_build_params(variant); rig_make_identify(); rig_identify_served = rig_reads_served = rig_ctl_served = 0; kprintf("dfrig2489: channel prepared: variant=%d " "(params.heads=%u params.sectors=%u cylinders=%u " "atavalid=0x%04x lba_size=0x%08x)\n", variant, rig_params.heads, rig_params.sectors, rig_params.cylinders, rig_params.atavalid, (rig_params.lba_size_2 << 16) | rig_params.lba_size_1); return 0; } /* ------------------------------------------------------------------ */ /* status buffer */ /* ------------------------------------------------------------------ */ static char rig_status_buf[8192]; static int rig_last_rc; #define P(fmt, ...) \ ksnprintf(rig_status_buf + strlen(rig_status_buf), \ sizeof(rig_status_buf) - strlen(rig_status_buf), \ fmt "\n", ## __VA_ARGS__) /* ------------------------------------------------------------------ */ /* tests 1/2/3: the FULL production funnel -- kernel's own identify, */ /* getparam parse, newbus child, ad_probe, ad_attach */ /* ------------------------------------------------------------------ */ static int rig_test_funnel(int variant, const char *tag) { int saved_bootverbose = bootverbose; int rc; rc = rig_prepare(variant); if (rc) return rc; P("== DF-2489 %s: PRODUCTION ata_identify() funnel -> " "RUNNING KERNEL own ad_attach, variant=%d (%s) ==", tag, variant, variant == 0 ? "SANE 16383/16/63" : variant == 1 ? "CRAFTED heads=0 (word 3)" : "CRAFTED sectors=0 (word 6)"); P("firing ata_identify(%p): real getparam parses the crafted " "IDENTIFY, real newbus child, real ad_probe/ad_attach ...", &fake_ch_dev); P("EXPECT%s: %s", variant == 0 ? "ED CLEAN ATTACH" : "ING FATAL TRAP 18 (integer divide fault) at ata-disk.c:147", variant == 0 ? "ad0 created, describe + bootverbose " "[C/H/S] lines on console" : "ad_attach:147 total_secs/(heads*sectors) with divisor 0"); kprintf("dfrig2489[%s]: firing PRODUCTION ata_identify() " "(variant=%d) -- kernel own ad_attach follows\n", tag, variant); bootverbose = 1; /* also exercise the :537-543 path */ ata_identify(&fake_ch_dev); /* PRODUCTION */ bootverbose = saved_bootverbose; P("ata_identify returned (no panic); IDENTIFY served=%d " "data reads served=%d control cmds served=%d", rig_identify_served, rig_reads_served, rig_ctl_served); if (variant == 0) { struct ata_device *atadev; device_t child; child = TAILQ_FIRST(&fake_ch_dev.children); atadev = child ? device_get_softc(child) : NULL; if (child && atadev && device_get_ivars(child)) { struct ad_softc *adp = device_get_ivars(child); P("control attach state: child=%p ad_softc=%p " "heads=%u sectors=%u total_secs=%ju " "(disk_info d_ncylinders would be %ju)", child, adp, adp->heads, adp->sectors, (uintmax_t)adp->total_secs, (uintmax_t)(adp->total_secs / (adp->heads * adp->sectors))); rig_last_rc = (adp->heads == 16 && adp->sectors == 63); } else { P("no ad child attached?!"); rig_last_rc = 0; } } return 0; } /* ------------------------------------------------------------------ */ /* test 4: verbatim rig copy ad_attach on the same post-getparam state */ /* ------------------------------------------------------------------ */ static struct bsd_device fake_ata_dev_b; static struct ata_device fake_atadev_b; static char b_nameunit[] = "ad9b"; static void rig_test_verbatim_attach(void) { devclass_t ad_dc = devclass_find("ad"); if (rig_prepare(1) == 0) { bzero(&fake_ata_dev_b, sizeof(fake_ata_dev_b)); bzero(&fake_atadev_b, sizeof(fake_atadev_b)); fake_ata_dev_b.parent = &fake_ch_dev; fake_ata_dev_b.softc = &fake_atadev_b; fake_ata_dev_b.ivars = NULL; /* virgin disk */ fake_ata_dev_b.devclass = ad_dc; fake_ata_dev_b.unit = 7; fake_ata_dev_b.nameunit = b_nameunit; TAILQ_INIT(&fake_ata_dev_b.children); /* exactly what production ata_getparam leaves after the * crafted heads==0 IDENTIFY (rig_params, variant 1) */ fake_atadev_b.dev = &fake_ata_dev_b; fake_atadev_b.unit = ATA_MASTER; fake_atadev_b.mode = ATA_PIO_MAX; bcopy(&rig_params, &fake_atadev_b.param, sizeof(struct ata_params)); } P("== DF-2489 go4: VERBATIM #included copy ad_attach(), " "heads=0 =="); P("ad_softc state entering ad_attach (from ad_get_geometry " "else-branch, ata-disk.c:477-478): heads=0 sectors=63 " "total_secs=0x2000000 (lba_size won, :489-490)"); P("EXPECTING FATAL TRAP 18 at ata-disk.c:147 " "`total_secs/(heads*sectors)' -- panic follows on serial"); kprintf("dfrig2489[go4]: dispatching VERBATIM COPY ad_attach() " "(heads=0) -- divisor heads*sectors=0\n"); ad_attach(&fake_ata_dev_b); /* PRODUCTION (rig copy) */ P("SURVIVED -- on a FIXED kernel this is the expected outcome " "(clamp in ad_get_geometry -> no division by zero)"); rig_last_rc = 0; } /* ------------------------------------------------------------------ */ /* test 5: verbatim rig copy ad_describe with a zero-geometry ad_softc */ /* ------------------------------------------------------------------ */ static struct bsd_device fake_ata_dev_c; static struct ata_device fake_atadev_c; static struct ad_softc fake_adsc_c; static char c_nameunit[] = "ad9c"; static void rig_test_describe(void) { int saved_bootverbose = bootverbose; rig_prepare(0); bzero(&fake_ata_dev_c, sizeof(fake_ata_dev_c)); bzero(&fake_atadev_c, sizeof(fake_atadev_c)); bzero(&fake_adsc_c, sizeof(fake_adsc_c)); fake_ata_dev_c.parent = &fake_ch_dev; fake_ata_dev_c.softc = &fake_atadev_c; fake_ata_dev_c.ivars = &fake_adsc_c; fake_ata_dev_c.unit = 0; fake_ata_dev_c.nameunit = c_nameunit; bcopy(&rig_params, &fake_atadev_c.param, sizeof(struct ata_params)); fake_atadev_c.dev = &fake_ata_dev_c; fake_atadev_c.unit = ATA_MASTER; fake_atadev_c.mode = ATA_PIO4; /* post-ad_get_geometry state of a heads==0 device */ fake_adsc_c.total_secs = 100000; fake_adsc_c.heads = 0; /* <-- crafted IDENTIFY word 3 */ fake_adsc_c.sectors = 63; fake_adsc_c.num_tags = 0; P("== DF-2489 go5: VERBATIM #included copy ad_describe(), " "bootverbose, heads=0 =="); P("ad_softc: total_secs=100000 heads=0 sectors=63 -- the state " "ad_get_geometry leaves after a heads==0 IDENTIFY"); P("EXPECTING FATAL TRAP 18 at ata-disk.c:540 " "`total_secs/(heads*sectors)' in the bootverbose path"); kprintf("dfrig2489[go5]: dispatching VERBATIM COPY ad_describe() " "(bootverbose, heads=0) -- :540 divisor heads*sectors=0\n"); bootverbose = 1; ad_describe(&fake_ata_dev_c); /* PRODUCTION (rig copy) */ bootverbose = saved_bootverbose; P("SURVIVED -- on a FIXED kernel this is the expected outcome"); rig_last_rc = 0; } /* ------------------------------------------------------------------ */ /* tests 6/7: semi-funnel diagnostics -- replicate ata_identify()'s */ /* per-device body (ata-all.c:697-716) with the param installed */ /* directly (fabricated post-getparse state), then run the REAL newbus */ /* probe/attach machinery stepwise to bracket any wedge */ /* ------------------------------------------------------------------ */ static device_t rig_child; static struct ata_device *rig_atadev; static void rig_test_semi(int variant, int direct_probe, const char *tag) { int saved_bootverbose = bootverbose; if (rig_prepare(variant) != 0) return; rig_atadev = kmalloc(sizeof(struct ata_device), M_ATA, M_INTWAIT | M_ZERO); rig_atadev->unit = ATA_MASTER; rig_child = device_add_child(&fake_ch_dev, NULL, -1); if (rig_child == NULL) { P("device_add_child failed"); kfree(rig_atadev, M_ATA); rig_atadev = NULL; return; } device_set_softc(rig_child, rig_atadev); device_quiet(rig_child); rig_atadev->dev = rig_child; rig_atadev->max_iosize = DEV_BSIZE; rig_atadev->mode = ATA_PIO_MAX; bcopy(&rig_params, &rig_atadev->param, sizeof(struct ata_params)); P("== DF-2489 %s: semi-funnel diagnostic (variant=%d) ==", tag, variant); kprintf("dfrig2489[%s]: child=%p fabricated (ata-all.c:697-716 " "body); bus_generic_probe next\n", tag, rig_child); bus_generic_probe(&fake_ch_dev); kprintf("dfrig2489[%s]: bus_generic_probe RETURNED\n", tag); bootverbose = 1; if (direct_probe) { kprintf("dfrig2489[%s]: device_probe_and_attach(child) " "next\n", tag); device_probe_and_attach(rig_child); } else { kprintf("dfrig2489[%s]: bus_generic_attach(channel) " "next\n", tag); bus_generic_attach(&fake_ch_dev); } bootverbose = saved_bootverbose; kprintf("dfrig2489[%s]: newbus attach machinery RETURNED\n", tag); P("newbus attach machinery returned; hw stats: identify=%d " "reads=%d ctl=%d", rig_identify_served, rig_reads_served, rig_ctl_served); if (device_get_ivars(rig_child)) { struct ad_softc *adp = device_get_ivars(rig_child); P("child attached: ad_softc=%p heads=%u sectors=%u " "total_secs=%ju", adp, adp->heads, adp->sectors, (uintmax_t)adp->total_secs); rig_last_rc = (adp->heads != 0 && adp->sectors != 0); } else { P("child did not attach (no ivars)"); rig_last_rc = 0; } } /* ------------------------------------------------------------------ */ /* scenario dispatch + worker thread (the sysctl handler only posts */ /* the command; the scenario runs in a dedicated kernel thread so a */ /* wedge cannot hold a sysctl/driverf-context lock) */ /* ------------------------------------------------------------------ */ static int rig_fire_cmd; /* protected by being single-writer */ static int rig_seq; static void rig_run(int cmd) { rig_status_buf[0] = '\0'; rig_last_rc = -1; switch (cmd) { case 1: rig_test_funnel(0, "go1"); break; case 2: rig_test_funnel(1, "go2"); /* fatal on stock */ break; case 3: rig_test_funnel(2, "go3"); /* fatal on stock */ break; case 4: rig_test_verbatim_attach(); /* fatal on stock */ break; case 5: rig_test_describe(); /* fatal on stock */ break; case 6: rig_test_semi(0, 0, "go6"); /* diagnostic, sane */ break; case 7: rig_test_semi(0, 1, "go7"); /* diagnostic, sane */ break; default: P("unknown test %d", cmd); rig_last_rc = -2; return; } P("last_rc=%d", rig_last_rc); } static void rig_worker(void *arg) { kprintf("dfrig2489: worker thread ready\n"); for (;;) { int cmd; tsleep(&rig_fire_cmd, 0, "dfrig2489w", 0); cmd = rig_fire_cmd; rig_fire_cmd = 0; if (cmd == 0) continue; kprintf("dfrig2489: worker firing scenario %d " "(under mp_token, like the boot attach context)\n", cmd); /* * The funnel attaches through the real newbus machinery, * which on this kernel also attaches the built-in * atapicam driver (its identify() adds a child to every * probed channel); xpt_bus_register() asserts the MP * token is held, exactly as the boot-time attach context * holds it. Take it -- this mirrors, not alters, the * production attach context. */ get_mplock(); rig_run(cmd); rel_mplock(); rig_seq++; wakeup(&rig_seq); kprintf("dfrig2489: scenario %d DONE (seq=%d)\n", cmd, rig_seq); } } 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; if (val < 1 || val > 7) return EINVAL; rig_fire_cmd = val; wakeup_one(&rig_fire_cmd); return 0; } static int rig_status_sysctl(SYSCTL_HANDLER_ARGS) { return SYSCTL_OUT(req, rig_status_buf, strlen(rig_status_buf) + 1); } static int rig_seq_sysctl(SYSCTL_HANDLER_ARGS) { int v = rig_seq; return sysctl_handle_int(oidp, &v, 0, req); } static SYSCTL_NODE(_kern, OID_AUTO, dfrig2489, CTLFLAG_RW, 0, "DF-2489 ata-disk zero-geometry rig"); SYSCTL_PROC(_kern_dfrig2489, OID_AUTO, go, CTLTYPE_INT | CTLFLAG_RW, 0, 0, rig_go_sysctl, "I", "fire DF-2489 scenario N"); SYSCTL_PROC(_kern_dfrig2489, OID_AUTO, status, CTLTYPE_STRING | CTLFLAG_RD, 0, 0, rig_status_sysctl, "A", "last DF-2489 scenario output"); SYSCTL_PROC(_kern_dfrig2489, OID_AUTO, seq, CTLTYPE_INT | CTLFLAG_RD, 0, 0, rig_seq_sysctl, "I", "completed scenario count"); /* ------------------------------------------------------------------ */ /* module (loaded at BOOT via /boot/loader.conf; runtime kldload GPFs */ /* on this INVARIANTS guest) */ /* ------------------------------------------------------------------ */ static struct thread *rig_td; static int dfrig2489_modevent(module_t mod, int type, void *data) { int error; switch (type) { case MOD_LOAD: lockinit(&fake_ch.state_mtx, "dfrig2489_st", 0, LK_CANRECURSE); lockinit(&fake_ch.queue_mtx, "dfrig2489_q", 0, LK_CANRECURSE); error = kthread_create(rig_worker, NULL, &rig_td, "dfrig2489"); if (error) return error; kprintf("dfrig2489: loaded (production ata-disk.c " "included verbatim; scenarios " "kern.dfrig2489.go=1..7)\n"); return 0; case MOD_UNLOAD: return EBUSY; /* worker thread never exits */ default: return EOPNOTSUPP; } } static moduledata_t dfrig2489_mod = { "dfrig2489", dfrig2489_modevent, 0 }; static struct mod_metadata _mod_metadata_dfrig2489 = { MDT_STRUCT_VERSION, MDT_MODULE, &dfrig2489_mod, "dfrig2489" }; DATA_SET(modmetadata_set, _mod_metadata_dfrig2489); SYSINIT(dfrig2489module, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, module_register_init, &dfrig2489_mod); |