DF-5009 / fakemic.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 | /* * fakemic.c โ synthetic dual-PHY MII MAC controller (DF-5009 PoC trigger) * * Reproduces the hardware precondition for DF-5009 without any hardware: * a single MII bus carrying two PHYs โ * * phy 0: RealTek RTL8201L (OUI 0x000020, model 0x20) -> rlphy @ instance 0 * phy 1: unassigned vendor (OUI 0x0DEADB, model 0x9) -> ukphy @ instance 1 * * On such a bus (real-world shape: 8129/8139 combo cards, IP101 boards), * when the operator selects the *sibling* PHY's media โ a perfectly * legitimate action, e.g. `ifconfig <if> media autoselect` picks the * instance-1 autoselect entry because it sits at the head of the media * list (if_media.c:128 prepends) โ every subsequent mii_tick() (1 Hz * watchdog) and every mii_pollstat() (SIOCGIFMEDIA, unprivileged) * dispatches rlphy_service() with a foreign ifm_cur, and the KASSERT at * rlphy.c:200 fires: * * panic: rlphy_service: can't isolate RealTek PHY * * on any INVARIANTS kernel โ including stock X86_64_GENERIC * (sys/config/X86_64_GENERIC:56 options INVARIANTS). * * fakemic provides exactly the register image both PHY drivers expect: * - BMCR writes are shadowed; BMCR_RESET reads back clear so the * mii_phy_reset() poll loop (mii_physubr.c:204-209) completes at once; * - BMSR advertises ANEG + 10/100 capabilities (miibus_no_phy() * at mii.c:105-116 requires media bits for a PHY to exist at all); * - PHY IDs select rlphy (explicit ID match, rlphy.c:74-78) and * ukphy (probe priority -100, ukphy.c:124). * Its softc starts with a zeroed struct ifnet so the sibling's * MII_TICK path bails out at the IFF_UP check (ukphy.c-style guard) * before doing anything else; rlphy never gets that far โ the KASSERT * at rlphy.c:200 is the first statement that touches ifm_cur. * * Usage in the guest: * make SYSDIR=/usr/src/sys # builds fakemic.ko * kldload ./fakemic.ko # attaches fakemic0 + dual-PHY miibus * sysctl -w kern.fakemic_arm=1 # admin selects sibling media; the * # watchdog tick -> kernel panic */ #include <sys/param.h> #include <sys/systm.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/bus.h> #include <sys/malloc.h> #include <sys/socket.h> #include <sys/sysctl.h> #include <net/if.h> #include <net/if_media.h> #include <net/if_var.h> #include <dev/netif/mii_layer/mii.h> #include <dev/netif/mii_layer/miivar.h> #include "miibus_if.h" /* phy 0: RealTek RTL8201L โ matches rlphys[] (rlphy.c:75) */ #define RTL8201L_ID1 0x0000 #define RTL8201L_ID2 0x8201 /* OUI 0x000020, model 0x20, rev 1 */ /* phy 1: unassigned vendor โ no mii_phydesc match -> ukphy */ #define UNKNOWN_ID1 0x37AB #define UNKNOWN_ID2 0x6C91 /* OUI 0x0DEADB, model 0x9, rev 1 */ #define FMC_BMSR (BMSR_ANEG | BMSR_100TXFDX | BMSR_100TXHDX | \ BMSR_10TFDX | BMSR_10THDX) /* * The sibling additionally advertises 100baseT4 -- a media word rlphy * never adds. ifmedia_match() ignores the instance field (ifm_mask is * seeded with IFM_IMASK at miibus_attach, mii.c:180), so "autoselect" * always resolves to the last matching entry (rlphy's). The only way an * operator can select the SIBLING PHY is via a media subtype unique to * it -- exactly the combo-board case (fiber PHY with 1000SX, T4, ...). */ #define FMC_BMSR_SIBLING (FMC_BMSR | BMSR_100T4) struct fakemic_softc { struct ifnet ifnet; /* mii_ifp; zeroed => !IFF_UP */ device_t miibus; uint16_t phy[2][8]; /* canned register image */ }; static device_t fakemic_dev; static int fakemic_armed; static void fakemic_identify(driver_t *, device_t); static int fakemic_probe(device_t); static int fakemic_attach(device_t); static int fakemic_detach(device_t); static int fakemic_readreg(device_t, int, int); static int fakemic_writereg(device_t, int, int, int); static void fakemic_statchg(device_t); static void fakemic_mediainit(device_t); static int fakemic_ifmedia_upd(struct ifnet *); static void fakemic_ifmedia_sts(struct ifnet *, struct ifmediareq *); static device_method_t fakemic_methods[] = { DEVMETHOD(device_identify, fakemic_identify), DEVMETHOD(device_probe, fakemic_probe), DEVMETHOD(device_attach, fakemic_attach), DEVMETHOD(device_detach, fakemic_detach), DEVMETHOD(miibus_readreg, fakemic_readreg), DEVMETHOD(miibus_writereg, fakemic_writereg), DEVMETHOD(miibus_statchg, fakemic_statchg), DEVMETHOD(miibus_mediainit, fakemic_mediainit), DEVMETHOD_END }; static devclass_t fakemic_devclass; static driver_t fakemic_driver = { "fakemic", fakemic_methods, sizeof(struct fakemic_softc) }; DRIVER_MODULE(fakemic, nexus, fakemic_driver, fakemic_devclass, NULL, NULL); DRIVER_MODULE(miibus, fakemic, miibus_driver, miibus_devclass, NULL, NULL); MODULE_DEPEND(fakemic, miibus, 1, 1, 1); static void fakemic_identify(driver_t *driver, device_t parent) { if (device_find_child(parent, "fakemic", -1) == NULL) BUS_ADD_CHILD(parent, parent, 0, "fakemic", -1); } static int fakemic_probe(device_t dev) { device_set_desc(dev, "synthetic dual-PHY MII controller (DF-5009)"); return (0); } static int fakemic_attach(device_t dev) { struct fakemic_softc *sc = device_get_softc(dev); struct mii_data *mii; int error; bzero(sc, sizeof(*sc)); sc->phy[0][MII_BMCR] = 0; sc->phy[0][MII_BMSR] = FMC_BMSR; sc->phy[0][MII_PHYIDR1] = RTL8201L_ID1; sc->phy[0][MII_PHYIDR2] = RTL8201L_ID2; sc->phy[0][MII_ANAR] = ANAR_CSMA; sc->phy[1][MII_BMCR] = 0; sc->phy[1][MII_BMSR] = FMC_BMSR_SIBLING; sc->phy[1][MII_PHYIDR1] = UNKNOWN_ID1; sc->phy[1][MII_PHYIDR2] = UNKNOWN_ID2; sc->phy[1][MII_ANAR] = ANAR_CSMA; error = mii_phy_probe(dev, &sc->miibus, fakemic_ifmedia_upd, fakemic_ifmedia_sts); if (error) { kprintf("fakemic: mii_phy_probe failed: %d\n", error); return (error); } mii = device_get_softc(sc->miibus); kprintf("fakemic: dual-PHY MII bus up; default media inst=%d " "subtype=0x%x (expect inst=0 => rlphy autoselect)\n", IFM_INST(mii->mii_media.ifm_cur->ifm_media), (unsigned)IFM_SUBTYPE(mii->mii_media.ifm_cur->ifm_media)); return (0); } static int fakemic_detach(device_t dev) { return (0); } static int fakemic_readreg(device_t dev, int phy, int reg) { struct fakemic_softc *sc = device_get_softc(dev); uint16_t v; if (phy < 0 || phy > 1 || reg < 0 || reg > 7) return (0xffff); /* absent */ v = sc->phy[phy][reg]; if (reg == MII_BMCR) v &= ~BMCR_RESET; /* reset completes instantly */ return (v); } static int fakemic_writereg(device_t dev, int phy, int reg, int data) { struct fakemic_softc *sc = device_get_softc(dev); if (phy < 0 || phy > 1 || reg < 0 || reg > 7) return (0); sc->phy[phy][reg] = (uint16_t)data; return (0); } static void fakemic_statchg(device_t dev) { } static void fakemic_mediainit(device_t dev) { } static int fakemic_ifmedia_upd(struct ifnet *ifp) { return (0); } static void fakemic_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) { ifmr->ifm_active = IFM_ETHER | IFM_AUTO; ifmr->ifm_status = IFM_AVALID; } /* * Arm the trigger: create fakemic0 on nexus, then emulate the operator * action "select the sibling PHY's autoselect" (ifconfig fakemic0 media * autoselect) followed by the 1-second MII watchdog tick that every * MII MAC driver issues. On an unpatched kernel this panics inside * rlphy_service(); on a patched kernel the sysctl returns to userland * and prints "mii_tick survived". */ static int fakemic_arm_sysctl(SYSCTL_HANDLER_ARGS) { int error, t = fakemic_armed; struct fakemic_softc *sc; struct mii_data *mii; error = sysctl_handle_int(oidp, &t, 0, req); if (error || req->newptr == NULL) return (error); if (t == 0 || fakemic_armed) return (0); fakemic_armed = 1; if (fakemic_dev == NULL) { devclass_t dc; device_t d; dc = devclass_find("fakemic"); if (dc == NULL) { kprintf("fakemic: no fakemic devclass (driver not loaded?)\n"); return (0); } d = devclass_get_device(dc, 0); if (d == NULL) { kprintf("fakemic: fakemic0 not attached\n"); return (0); } fakemic_dev = d; } sc = device_get_softc(fakemic_dev); mii = device_get_softc(sc->miibus); kprintf("fakemic: operator selects SIBLING PHY media " "(\"ifconfig fakemic0 media 100baseT4\")\n"); ifmedia_set(&mii->mii_media, IFM_MAKEWORD(IFM_ETHER, IFM_100_T4, 0, 1)); kprintf("fakemic: ifm_cur inst=%d; invoking MII watchdog mii_tick()\n", IFM_INST(mii->mii_media.ifm_cur->ifm_media)); mii_tick(mii); kprintf("fakemic: mii_tick survived -- rlphy KASSERT did NOT fire\n"); return (0); } SYSCTL_PROC(_kern, OID_AUTO, fakemic_arm, CTLTYPE_INT | CTLFLAG_RW, 0, 0, fakemic_arm_sysctl, "I", "arm DF-5009 dual-PHY trigger"); |