DF-5001 / miihub.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 | /* * DF-5001 hub demonstration module. * * Executes the EXACT body of miibus_mediainit() (sys/dev/netif/mii_layer/ * mii.c:228-245) against a real `struct ifmedia` whose ifm_list is EMPTY -- * the state reached at attach time when every probed PHY takes its * "no media present" branch (family: atphy DF-4930, ciphy DF-4957, * jmphy DF-4981, truephy DF-5003, plus the ~20 siblings that share the * ukphy_attach shape) and the parent NIC's mediainit method adds nothing * of its own. * * The only omitted statement is the MIIBUS_MEDIAINIT(parent) poke * (mii.c:235) -- it is the parent NIC driver's callback and is a no-op for * this demonstration (the parents that have extra media would make the * panic UNREACHABLE, so skipping it is the conservative choice: we test * the hub walk in exactly the configuration where it is dangerous). * * With an empty list the walk (mii.c:238-243) never executes its body, * `media` stays 0 (mii.c:232), and the terminal ifmedia_set(&ifm, 0) * (mii.c:244) hits DragonFly's * * ifmedia_match() == NULL -> kprintf("ifmedia_set: no match ...") * -> panic("ifmedia_set") [sys/net/if_media.c] * * hw.miihub_fire=1 -> baseline: verbatim hub walk on empty list (panics) * hw.miihub_fixcheck=1-> the FIXED hub walk (fix.diff): empty list gets an * IFM_ETHER|IFM_AUTO entry before ifmedia_set(); must * survive, and ifm_cur must be non-NULL (which is * what keeps every mii *_service()'s * `ife = mii->mii_media.ifm_cur; ife->ifm_media` * dereference safe afterwards). */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/bus.h> #include <sys/conf.h> #include <sys/sysctl.h> #include <sys/systm.h> #include <net/if.h> #include <net/if_media.h> static struct ifmedia ifm; /* stands in for mii->mii_media (mii_data) */ /* * Verbatim copy of the hub walk, mii.c:232-244. */ static void miihub_mediainit_walk(struct ifmedia *ifmp) { struct ifmedia_entry *m; int media = 0; /* mii.c:238-243 */ for (m = LIST_FIRST(&ifmp->ifm_list); m != NULL; m = LIST_NEXT(m, ifm_list)) { media = m->ifm_media; if (media == (IFM_ETHER | IFM_AUTO)) break; } /* mii.c:244 */ ifmedia_set(ifmp, media); } /* * The fix.diff shape: never call ifmedia_set() on an empty list. */ static void miihub_mediainit_fixed(struct ifmedia *ifmp) { struct ifmedia_entry *m; int media = 0; for (m = LIST_FIRST(&ifmp->ifm_list); m != NULL; m = LIST_NEXT(m, ifm_list)) { media = m->ifm_media; if (media == (IFM_ETHER | IFM_AUTO)) break; } if (m == NULL && LIST_EMPTY(&ifmp->ifm_list)) { ifmedia_add(ifmp, IFM_ETHER | IFM_AUTO, 0, NULL); media = IFM_ETHER | IFM_AUTO; } ifmedia_set(ifmp, media); } static int miihub_fire(SYSCTL_HANDLER_ARGS) { int v = 0, error; error = sysctl_handle_opaque(oidp, &v, sizeof(v), req); if (error != 0 || req->newptr == NULL) return (error); kprintf("miihub: ifmedia_init (EMPTY ifm_list -- all PHYs took the " "'no media present' branch, parent added nothing)\n"); ifmedia_init(&ifm, IFM_IMASK, NULL, NULL); kprintf("miihub: running miibus_mediainit walk verbatim (mii.c:238-244)\n"); miihub_mediainit_walk(&ifm); /* expected: panic("ifmedia_set") */ kprintf("miihub: NOT REACHED\n"); return (0); } static int miihub_fixcheck(SYSCTL_HANDLER_ARGS) { struct ifmedia ifm2; int v = 0, error; error = sysctl_handle_opaque(oidp, &v, sizeof(v), req); if (error != 0 || req->newptr == NULL) return (error); kprintf("miihub-fix: ifmedia_init (EMPTY list) + fixed hub walk " "(empty -> add IFM_ETHER|IFM_AUTO -> ifmedia_set)\n"); ifmedia_init(&ifm2, IFM_IMASK, NULL, NULL); miihub_mediainit_fixed(&ifm2); kprintf("miihub-fix: survived; ifm_cur=%p media=0x%x\n", ifm2.ifm_cur, ifm2.ifm_media); if (ifm2.ifm_cur == NULL) { kprintf("miihub-fix: FAIL -- ifm_cur still NULL\n"); return (1); } /* what every PHY service function does on the next tick (e.g. * truephy.c:185 `ife = mii->mii_media.ifm_cur; ... IFM_INST(ife->ifm_media)`) */ kprintf("miihub-fix: ifm_cur->ifm_media=0x%x (service deref safe)\n", ifm2.ifm_cur->ifm_media); ifmedia_removeall(&ifm2); return (0); } static int miihub_modevent(module_t mod, int type, void *arg) { static struct sysctl_ctx_list ctx; switch (type) { case MOD_LOAD: sysctl_ctx_init(&ctx); SYSCTL_ADD_PROC(NULL, SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "miihub_fire", CTLTYPE_INT | CTLFLAG_RW, 0, 0, miihub_fire, "I", "fire miibus_mediainit walk on empty media list (panics)"); SYSCTL_ADD_PROC(NULL, SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "miihub_fixcheck", CTLTYPE_INT | CTLFLAG_RW, 0, 0, miihub_fixcheck, "I", "replay with the fixed empty-list guard (must survive)"); kprintf("miihub: loaded; hw.miihub_fire=1 panics, " "hw.miihub_fixcheck=1 validates the fix shape\n"); return (0); case MOD_UNLOAD: sysctl_ctx_free(&ctx); return (0); } return (0); } DEV_MODULE(miihub, miihub_modevent, NULL); |