DF-5598 / veto.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 | /* * DF-5598 harness โ ichsmb detach-veto family probe. * * Target: sys/bus/smbus/ichsmb/ichsmb_pci.c:175 wires device_detach to * ichsmb_detach() (sys/bus/smbus/ichsmb/ichsmb.c:680), which IGNORES the * return of device_delete_child(dev, sc->smb) at ichsmb.c:689 and then * unconditionally runs ichsmb_release_resources() + lockuninit() and * returns 0 โ the DF-5544/5545/5562 family shape. * * DIFFERENCE under test: unlike uhci/xhci/ohci (device_delete_children() * first, swallowed), ichsmb_detach runs a CHECKED bus_generic_detach() * cascade FIRST (ichsmb.c:686), and smbus_detach() checks its own cascade * (smbus.c:110-112). The only in-tree leaf, smb_detach(), never vetoes * (smb.c:147-155). So a vetoing grandchild should be caught at * bus_generic_detach and ichsmb_detach should return EBUSY with ALL * resources intact โ the deterministic family panic should NOT reproduce. * * This KLD attaches a vetoing child directly under smbus0 (grandchild of * ichsmb0) and the driver script drives the exact kldunload path * (linker_file_unload -> driver_module_handler -> devclass_delete_driver, * subr_bus.c:897) โ the same context as PCI hot-unplug. * * sysctl hw.df5598_armed=1 detach of the child returns EBUSY (default) * sysctl hw.df5598_armed=0 child cooperates (clean-unload control run) */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/systm.h> #include <sys/bus.h> #include <sys/sysctl.h> #include <bus/smbus/smbconf.h> static device_t veto_child; static int armed = 1; SYSCTL_INT(_hw, OID_AUTO, df5598_armed, CTLFLAG_RW, &armed, 0, "DF-5598: 1 = veto child returns EBUSY from detach"); static int veto_probe(device_t dev); static int veto_attach(device_t dev); static int veto_detach(device_t dev); static device_method_t veto_methods[] = { DEVMETHOD(device_probe, veto_probe), DEVMETHOD(device_attach, veto_attach), DEVMETHOD(device_detach, veto_detach), DEVMETHOD_END }; static driver_t veto_driver = { "df5598veto", veto_methods, 0 }; static devclass_t veto_devclass; DRIVER_MODULE(df5598veto, smbus, veto_driver, veto_devclass, NULL, NULL); static int veto_probe(device_t dev) { device_set_desc(dev, "DF-5598 detach-veto child"); return (0); } static int veto_attach(device_t dev) { device_printf(dev, "armed; detach returns EBUSY while " "hw.df5598_armed=1\n"); return (0); } static int veto_detach(device_t dev) { if (armed) { device_printf(dev, "DETACH VETO: returning EBUSY -- if this " "is swallowed, ichsmb_release_resources() runs under me\n"); return (EBUSY); } device_printf(dev, "cooperating\n"); return (0); } static int df5598_load(struct module *m, int what, void *arg) { device_t bus; devclass_t dc; int error; switch (what) { case MOD_LOAD: bus = devclass_get_device(devclass_find("smbus"), 0); if (bus == NULL) { kprintf("DF5598: no smbus0; kldload ichsmb first\n"); return (ENXIO); } veto_child = device_add_child(bus, "df5598veto", -1); if (veto_child == NULL) return (ENOMEM); error = device_probe_and_attach(veto_child); if (error) { kprintf("DF5598: probe_and_attach failed: %d\n", error); return (error); } kprintf("DF5598: veto child attached under %s\n", device_get_nameunit(bus)); break; case MOD_UNLOAD: /* re-resolve: smbus0/child may already be gone (clean * ichsmb unload deletes the whole subtree) */ dc = devclass_find("df5598veto"); veto_child = dc ? devclass_get_device(dc, 0) : NULL; if (veto_child != NULL) { armed = 0; error = device_delete_child( device_get_parent(veto_child), veto_child); if (error) { kprintf("DF5598: child delete: %d\n", error); return (error); } } kprintf("DF5598: unloaded\n"); break; default: break; } return (0); } static moduledata_t df5598_mod = { "df5598", df5598_load, NULL }; DECLARE_MODULE(df5598, df5598_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); MODULE_DEPEND(df5598, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); |