| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/08/25 19:18 | flow | patch-triage |
2mArgs:null Results: {
"EnableConfigs": [
"ECONET_FE_SOC_PHY"
],
"FocusSymbols": [
"en751221_fephy_probe",
"en751221_fephy_config_init"
],
"KMSANReasoning": "The patch introduces a new Ethernet PHY driver for EcoNet 10/100 SoCs. The driver primarily interacts with hardware registers via MDIO (using phylib functions like phy_write_paged and phy_read_paged) and performs calibration routines. All allocated structures (e.g., mtk_socphy_priv and the shared package data) are zero-initialized using devm_kzalloc or standard phylib functions. Stack variables like cal_complete_ctx are also explicitly zero-initialized. The patch does not introduce new user-space APIs, netlink attributes, ioctls, or complex data structures that could leak uninitialized memory to user space or cause uninitialized memory-dependent control flow. Any potential memory safety issues (such as out-of-bounds accesses or use-after-free) would be effectively caught by KASAN. Therefore, a dedicated KMSAN fuzzing session is not justified.",
"NeedsKMSAN": false,
"Reasoning": "The patch introduces a new PHY driver for EcoNet 10/100 SoC Ethernet PHYs (`drivers/net/phy/mediatek/econet-fe-soc.c`). PHY drivers can be reached in virtualized environments by emulating an MDIO bus. For example, syzkaller can emulate a USB network adapter (such as `smsc95xx`) via the `vhci` driver. When the emulated USB device is probed, it registers an MDIO bus and scans for PHYs by sending MDIO read requests (which translate to USB control messages). By returning the specific PHY ID (`0x03a29414`) during the MDIO scan, the MDIO bus will instantiate a PHY device with this ID, triggering the probe and initialization functions of the `econet-fe-soc` PHY driver. Therefore, the code is reachable and worth fuzzing.",
"WorthFuzzing": true
} |
| 1/1 | 2026/08/25 19:18 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 447bbc436dec24504c070330c6d47b7919316814\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Tue Aug 25 19:18:41 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/MAINTAINERS b/MAINTAINERS\nindex 460cb72688450..7ca3d8d08c693 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -9253,6 +9253,12 @@ W:\thttps://linuxtv.org\n Q:\thttp://patchwork.linuxtv.org/project/linux-media/list/\n F:\tdrivers/media/dvb-frontends/ec100*\n \n+ECONET ETHERNET PHY DRIVER\n+M:\tCaleb James DeLisle \u003ccjd@cjdns.fr\u003e\n+L:\tnetdev@vger.kernel.org\n+S:\tMaintained\n+F:\tdrivers/net/phy/mediatek/econet-fe-soc.c\n+\n ECONET MIPS PLATFORM\n M:\tCaleb James DeLisle \u003ccjd@cjdns.fr\u003e\n L:\tlinux-mips@vger.kernel.org\ndiff --git a/drivers/net/phy/mediatek/Kconfig b/drivers/net/phy/mediatek/Kconfig\nindex 3b9cf82c0cb28..64abf5b695ab1 100644\n--- a/drivers/net/phy/mediatek/Kconfig\n+++ b/drivers/net/phy/mediatek/Kconfig\n@@ -1,4 +1,19 @@\n # SPDX-License-Identifier: GPL-2.0-only\n+\n+config ECONET_FE_SOC_PHY\n+\ttristate \"EcoNet 10/100 SoC Ethernet PHYs\"\n+\tdepends on ECONET || COMPILE_TEST\n+\tselect MTK_NET_PHYLIB\n+\tselect PHY_PACKAGE\n+\thelp\n+\t Supports EcoNet SoC built-in 10/100 Ethernet PHYs.\n+\n+\t Include support for built-in Ethernet PHYs which are present in\n+\t EN7526C based SoCs (including EN7526F). It can also work with\n+\t legacy EN7512 FE PHYs which identify as 03a2.9412, but since this\n+\t ID is used by MEDIATEK_GE_PHY, users wanting to force this driver\n+\t must override the ID to 03a2.9414 in the device tree.\n+\n config MEDIATEK_2P5GE_PHY\n \ttristate \"MediaTek 2.5Gb Ethernet PHYs\"\n \tdepends on (ARM64 \u0026\u0026 ARCH_MEDIATEK) || COMPILE_TEST\ndiff --git a/drivers/net/phy/mediatek/Makefile b/drivers/net/phy/mediatek/Makefile\nindex 6f9cacf7f9065..71239e92aa581 100644\n--- a/drivers/net/phy/mediatek/Makefile\n+++ b/drivers/net/phy/mediatek/Makefile\n@@ -1,4 +1,5 @@\n # SPDX-License-Identifier: GPL-2.0\n+obj-$(CONFIG_ECONET_FE_SOC_PHY)\t\t+= econet-fe-soc.o\n obj-$(CONFIG_MEDIATEK_2P5GE_PHY)\t+= mtk-2p5ge.o\n obj-$(CONFIG_MEDIATEK_FE_SOC_PHY)\t+= mtk-fe-soc.o\n obj-$(CONFIG_MEDIATEK_GE_PHY)\t\t+= mtk-ge.o\ndiff --git a/drivers/net/phy/mediatek/econet-fe-soc.c b/drivers/net/phy/mediatek/econet-fe-soc.c\nnew file mode 100644\nindex 0000000000000..c5dbb32a5d1ab\n--- /dev/null\n+++ b/drivers/net/phy/mediatek/econet-fe-soc.c\n@@ -0,0 +1,632 @@\n+// SPDX-License-Identifier: GPL-2.0+\n+#include \u003clinux/bits.h\u003e\n+#include \u003clinux/module.h\u003e\n+#include \u003clinux/phy.h\u003e\n+#include \u003clinux/types.h\u003e\n+\n+#include \"../phylib.h\"\n+#include \"mtk.h\"\n+\n+/*\n+ * An older version of this PHY hardware uses the ID 03a2.9412.\n+ * This driver does not match on it because it collides with\n+ * MTK_GPHY_ID_MT7530, so users wanting to use this driver should\n+ * override the PHY ID in the device tree to 03a2.9414.\n+ */\n+#define ECONET_FEPHY_ID_EN7526C\t\t0x03a29414\n+\n+#define ECONET_PG_G1\t\t\t0x1000\n+#define ECONET_PG_G2\t\t\t0x2000\n+#define ECONET_PG_G3\t\t\t0x3000\n+#define ECONET_PG_G4\t\t\t0x4000\n+#define ECONET_PG_G5\t\t\t0x5000\n+#define ECONET_PG_G6\t\t\t0x6000\n+#define ECONET_PG_G7\t\t\t0x7000\n+\n+#define ECONET_PG_L0\t\t\t0x8000\n+#define ECONET_PG_L1\t\t\t0x9000\n+#define ECONET_PG_L2\t\t\t0xa000\n+#define ECONET_PG_L3\t\t\t0xb000\n+#define ECONET_PG_L4\t\t\t0xc000\n+\n+/* 4 ports (8,9,10,11) or 3 ports (9,10,11) */\n+#define ECONET_LEGACY_PORT0_ADDR\t8\n+#define ECONET_EN7526C_PORT0_ADDR\t9\n+\n+/*\n+ * Calibration polling does not complete until ECONET_CAL_MIN_CYCLES\n+ * consecutive \"done\" results are received. See: poll_cal_complete\n+ */\n+#define ECONET_CAL_MIN_CYCLES\t\t10\n+\n+/*\n+ * Microseconds per calibration poll cycle, calibration takes approximately\n+ * ECONET_CAL_CYCLE_US * ECONET_CAL_MIN_CYCLES. Total setup time is usually\n+ * less than 150x this number.\n+ */\n+#define ECONET_CAL_CYCLE_US\t\t10\n+\n+/* Total time allowed for calibration polling before error is returned. */\n+#define ECONET_CAL_TIMEOUT_US\t\t100000\n+\n+/*\n+ * Note: These register definitions were written without the benefit of the\n+ * hardware specification, so names are given only where the meaning is\n+ * fairly obvious.\n+ */\n+\n+/* G7R24 */\n+#define ECONET_CAL_TYPE_MASK\t\tGENMASK(14, 12)\n+#define ECONET_CAL_TYPE_R50\t\tFIELD_PREP(ECONET_CAL_TYPE_MASK, 0x6)\n+#define ECONET_CAL_TYPE_TXOS\t\tFIELD_PREP(ECONET_CAL_TYPE_MASK, 0x3)\n+#define ECONET_CAL_TYPE_TXAMP\t\tFIELD_PREP(ECONET_CAL_TYPE_MASK, 0x7)\n+#define ECONET_R50_ZCAL_MASK\t\tGENMASK(11, 6)\n+#define ECONET_CALIN_EN7526C\t\tBIT(4)\n+#define ECONET_CAL_DONE_EN7526C\t\tBIT(1)\n+#define ECONET_CAL_OUT_EN7526C\t\tBIT(0)\n+\n+/* L4R23 */\n+#define ECONET_CALIN_LEGACY\t\tBIT(2)\n+#define ECONET_CAL_DONE_LEGACY\t\tBIT(6)\n+#define ECONET_CAL_OUT_LEGACY\t\tBIT(4)\n+\n+#define ECONET_DAC_IN_2V\t\t0x0f0\n+\n+/* L4R17 */\n+#define ECONET_TXOS_SIGN\t\tBIT(13)\n+#define ECONET_TXOS_MAG\t\t\tGENMASK(12, 8)\n+\n+#define ECONET_TXOS_DEFAULT\t\t0\n+#define ECONET_TXOS_MAX\t\t\t31\n+#define ECONET_TXOS_MIN\t\t\t-31\n+\n+#define ECONET_R50_ZCAL_DEFAULT\t\t0x20\n+#define ECONET_R50_ZCAL_MAX\t\t0x3f\n+\n+/* L4R22 */\n+#define ECONET_R50_TXCAL_MASK\t\tGENMASK(15, 8)\n+#define ECONET_R50_RXCAL_MASK\t\tGENMASK(7, 0)\n+\n+#define ECONET_TXAMP_DEFAULT\t\t0x12\n+#define ECONET_TXAMP_MAX\t\t0x3f\n+\n+/* ZCAL compensation table per chip, per phy, RX + TX */\n+struct compensation {\n+\ts8 zcal_tx;\n+\ts8 zcal_rx;\n+\ts8 txos;\n+\ts8 amp;\n+};\n+\n+static const struct compensation en751221_comp[2][4] = {\n+\t{\n+\t\t/* Legacy */\n+\t\t{ .zcal_tx = 2, .zcal_rx = -4, .txos = 0, .amp = 1 }, /* 8 */\n+\t\t{ .zcal_tx = 2, .zcal_rx = -4, .txos = 1, .amp = 2 }, /* 9 */\n+\t\t{ .zcal_tx = 2, .zcal_rx = -4, .txos = 1, .amp = 1 }, /* 10 */\n+\t\t{ .zcal_tx = -2, .zcal_rx = -8, .txos = 1, .amp = 1 }, /* 11 */\n+\t},\n+\t{\n+\t\t/* EN7526C */\n+\t\t{ .zcal_tx = 3, .zcal_rx = 6, .txos = 1, .amp = 0 }, /* 9 */\n+\t\t{ .zcal_tx = 2, .zcal_rx = 5, .txos = 1, .amp = -1 }, /* 10 */\n+\t\t{ .zcal_tx = 6, .zcal_rx = 6, .txos = 1, .amp = -1 }, /* 11 */\n+\t\t{ }, /* Unused */\n+\t}\n+};\n+\n+static const u8 zcal_to_r50ohm[64] = {\n+\t127, 127, 127, 127, 127, 127, 126, 123, 120, 117, 114, 112, 110, 107, 105, 103,\n+\t101, 99, 97, 79, 77, 75, 74, 72, 70, 69, 67, 66, 65, 47, 46, 45,\n+\t 43, 42, 41, 40, 39, 38, 37, 36, 34, 34, 33, 32, 15, 14, 13, 12,\n+\t 11, 10, 10, 9, 8, 7, 7, 6, 5, 4, 4, 3, 2, 2, 1, 1\n+};\n+\n+struct econet_socphy_shared {\n+\tstruct phy_device *phydev_p0;\n+};\n+\n+static struct compensation get_ctab(struct phy_device *phydev)\n+{\n+\tstruct econet_socphy_shared *shared = phy_package_get_priv(phydev);\n+\tu8 phy_offset;\n+\n+\tphy_offset = phydev-\u003emdio.addr - shared-\u003ephydev_p0-\u003emdio.addr;\n+\n+\tif (WARN_ON_ONCE(phy_offset \u003e= ARRAY_SIZE(en751221_comp[0])))\n+\t\treturn (struct compensation) {};\n+\n+\treturn en751221_comp[phydev-\u003ephy_id == ECONET_FEPHY_ID_EN7526C][phy_offset];\n+}\n+\n+static void set_calin_flag(struct phy_device *phydev, bool enabled)\n+{\n+\tstruct econet_socphy_shared *shared = phy_package_get_priv(phydev);\n+\tu16 set;\n+\n+\tif (phydev-\u003ephy_id == ECONET_FEPHY_ID_EN7526C) {\n+\t\t/* G7R24 is shared so phy_modify_paged is required */\n+\t\tset = enabled ? ECONET_CALIN_EN7526C : 0;\n+\t\tphy_modify_paged(phydev, ECONET_PG_G7, 24,\n+\t\t\t\t ECONET_CALIN_EN7526C, set);\n+\t} else {\n+\t\tset = enabled ? ECONET_CALIN_LEGACY : 0;\n+\t\tphy_write_paged(shared-\u003ephydev_p0, ECONET_PG_L4, 23, set);\n+\t}\n+}\n+\n+struct cal_complete_ctx {\n+\tu16 consecutive_trues\t: 15;\n+\tbool started\t\t: 1;\n+\tbool observed_false\t: 1;\n+\tu16 aborted_tries\t: 7;\n+};\n+\n+/**\n+ * poll_cal_complete - poll for calibration completion.\n+ *\n+ * Because some legacy chips are known to be unreliable (false positives),\n+ * reference code adds 10,000us of fixed delay per calibration cycle.\n+ * Practically, there are about 10-50 calibration cycles for each of the\n+ * three tunables, so adding a significant fixed delay is costly.\n+ *\n+ * This implementation instead starts with CALIN disabled, polls for\n+ * done status false, then enables CALIN and polls until a true done\n+ * status is recorded ECONET_CAL_MIN_CYCLES times consecutively.\n+ *\n+ * @returns:\n+ * \u003c 0 on error\n+ * 0 not yet complete\n+ * 1 complete false\n+ * 2 complete true\n+ */\n+static int poll_cal_complete(struct phy_device *phydev,\n+\t\t\t struct cal_complete_ctx *ctx)\n+{\n+\tstruct econet_socphy_shared *shared = phy_package_get_priv(phydev);\n+\tint done_mask;\n+\tint out_mask;\n+\tint ret;\n+\n+\tif (phydev-\u003ephy_id == ECONET_FEPHY_ID_EN7526C) {\n+\t\tret = phy_read_paged(phydev, ECONET_PG_G7, 24);\n+\t\tdone_mask = ECONET_CAL_DONE_EN7526C;\n+\t\tout_mask = ECONET_CAL_OUT_EN7526C;\n+\t} else {\n+\t\tret = phy_read_paged(shared-\u003ephydev_p0, ECONET_PG_L4, 23);\n+\t\tdone_mask = ECONET_CAL_DONE_LEGACY;\n+\t\tout_mask = ECONET_CAL_OUT_LEGACY;\n+\t}\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\n+\tif ((ret \u0026 done_mask) == 0) {\n+\t\tctx-\u003eobserved_false = true;\n+\t\tctx-\u003eaborted_tries += (ctx-\u003econsecutive_trues \u003e 0);\n+\t\tctx-\u003econsecutive_trues = 0;\n+\n+\t\tif (!ctx-\u003estarted) {\n+\t\t\tset_calin_flag(phydev, true);\n+\t\t\tctx-\u003estarted = true;\n+\t\t\treturn poll_cal_complete(phydev, ctx);\n+\t\t}\n+\n+\t\treturn 0;\n+\t}\n+\n+\tctx-\u003econsecutive_trues++;\n+\n+\tif (ctx-\u003econsecutive_trues \u003c ECONET_CAL_MIN_CYCLES || !ctx-\u003eobserved_false)\n+\t\treturn 0;\n+\n+\tif (ctx-\u003eaborted_tries)\n+\t\tphydev_warn(phydev, \"Calibration cycle %d false positives\\n\",\n+\t\t\t ctx-\u003eaborted_tries);\n+\n+\treturn ((ret \u0026 out_mask) != 0) ? 2 : 1;\n+}\n+\n+/**\n+ * poll_cal_complete - check for calibration register\n+ * @returns: -error or 1 if calibration result is true.\n+ */\n+static int en751221_fephy_cal_cycle(struct phy_device *phydev)\n+{\n+\tstruct cal_complete_ctx cctx = {};\n+\tint out;\n+\tint ret;\n+\n+\tset_calin_flag(phydev, false);\n+\n+\tret = read_poll_timeout(poll_cal_complete, out, out != 0,\n+\t\t\t\tECONET_CAL_CYCLE_US, ECONET_CAL_TIMEOUT_US,\n+\t\t\t\tfalse, phydev, \u0026cctx);\n+\n+\tif (ret \u003c 0) {\n+\t\tphydev_err(phydev, \"Calibration timeout %d (%d / %d / %d / %d)\\n\",\n+\t\t\t ret, cctx.consecutive_trues, cctx.observed_false,\n+\t\t\t cctx.aborted_tries, cctx.started);\n+\t} else if (out \u003c 0) {\n+\t\tphydev_err(phydev, \"Calibration error %d\\n\", out);\n+\t\tret = out;\n+\t} else {\n+\t\tret = out \u003e 1;\n+\t}\n+\n+\tset_calin_flag(phydev, false);\n+\treturn ret;\n+}\n+\n+static int en751221_fephy_r50(struct phy_device *phydev)\n+{\n+\tstruct econet_socphy_shared *shared = phy_package_get_priv(phydev);\n+\tstruct compensation ctab = get_ctab(phydev);\n+\tint zcal_sz = ARRAY_SIZE(zcal_to_r50ohm);\n+\tu8 rg_zcal_ctrl = ECONET_R50_ZCAL_DEFAULT;\n+\tint initial_comp_out;\n+\tint polarity = 0;\n+\tint comp_out;\n+\tint ret = 0;\n+\tu16 rxcal;\n+\tu16 txcal;\n+\n+\tif (phydev-\u003ephy_id == ECONET_FEPHY_ID_EN7526C)\n+\t\tphy_write_paged(phydev, ECONET_PG_G7, 24, ECONET_CAL_TYPE_R50);\n+\telse\n+\t\tphy_write_paged(shared-\u003ephydev_p0, ECONET_PG_L3, 25, 0xc000);\n+\n+\tphy_modify_paged(phydev, ECONET_PG_L3, 25, 0x1000, 0x1000);\n+\n+\tfor (;;) {\n+\t\tif (phydev-\u003ephy_id == ECONET_FEPHY_ID_EN7526C)\n+\t\t\tphy_modify_paged(phydev, ECONET_PG_G7, 24,\n+\t\t\t\t\t ECONET_R50_ZCAL_MASK,\n+\t\t\t\t\t FIELD_PREP(ECONET_R50_ZCAL_MASK,\n+\t\t\t\t\t\t rg_zcal_ctrl));\n+\t\telse\n+\t\t\tphy_write_paged(shared-\u003ephydev_p0, ECONET_PG_L3, 26,\n+\t\t\t\t\trg_zcal_ctrl);\n+\n+\t\tcomp_out = en751221_fephy_cal_cycle(phydev);\n+\t\tif (comp_out \u003c 0) {\n+\t\t\tret = comp_out;\n+\t\t\tgoto out;\n+\t\t}\n+\n+\t\tif (polarity == 0) {\n+\t\t\t/* First cycle */\n+\t\t\tinitial_comp_out = comp_out;\n+\t\t\tpolarity = comp_out ? -1 : 1;\n+\t\t} else if (initial_comp_out != comp_out) {\n+\t\t\t/* Found */\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\trg_zcal_ctrl += polarity;\n+\n+\t\tif (rg_zcal_ctrl \u003e ECONET_R50_ZCAL_MAX) {\n+\t\t\tret = 1;\n+\t\t\tgoto out;\n+\t\t}\n+\t}\n+\n+\trxcal = max(0, min(zcal_sz - 1, ctab.zcal_rx + rg_zcal_ctrl));\n+\ttxcal = max(0, min(zcal_sz - 1, ctab.zcal_tx + rg_zcal_ctrl));\n+\n+\tphy_write_paged(phydev, ECONET_PG_L4, 22,\n+\t\t\tFIELD_PREP(ECONET_R50_TXCAL_MASK, txcal) |\n+\t\t\tFIELD_PREP(ECONET_R50_RXCAL_MASK, rxcal));\n+\n+out:\n+\t/* Zero out registers */\n+\tphy_write_paged(phydev, ECONET_PG_L3, 25, 0x0000);\n+\n+\tif (phydev-\u003ephy_id == ECONET_FEPHY_ID_EN7526C)\n+\t\tphy_write_paged(phydev, ECONET_PG_G7, 24, 0x0000);\n+\telse\n+\t\tphy_write_paged(shared-\u003ephydev_p0, ECONET_PG_L3, 25, 0xc000);\n+\n+\tphy_write_paged(phydev, ECONET_PG_L3, 25, 0x0000);\n+\n+\treturn ret;\n+}\n+\n+static int en751221_fephy_tx_offset(struct phy_device *phydev)\n+{\n+\tstruct econet_socphy_shared *shared = phy_package_get_priv(phydev);\n+\tstruct compensation ctab = get_ctab(phydev);\n+\tint initial_comp_out;\n+\tint polarity = 0;\n+\tint offset = ECONET_TXOS_DEFAULT;\n+\tu16 offset_bin;\n+\tint comp_out;\n+\tint ret = 0;\n+\n+\tphy_write_paged(phydev, ECONET_PG_L0, 0, 0x2100);\n+\tphy_write_paged(phydev, ECONET_PG_L0, 26, 0x5200); /* fix MDI */\n+\n+\tif (phydev-\u003ephy_id == ECONET_FEPHY_ID_EN7526C) {\n+\t\tphy_write_paged(phydev, ECONET_PG_G7, 24, ECONET_CAL_TYPE_TXOS);\n+\t\tphy_write_paged(phydev, ECONET_PG_L3, 25, 0x0400);\n+\t} else {\n+\t\tphy_write_paged(shared-\u003ephydev_p0, ECONET_PG_L3, 25, 0x4800);\n+\t\tphy_write_paged(phydev, ECONET_PG_L3, 25, 0x4c00);\n+\t}\n+\n+\tphy_modify_paged(phydev, ECONET_PG_G4, 21, 0x0800, 0x0800);\n+\n+\tphy_modify_paged(phydev, ECONET_PG_L0, 30, 0x02c0, 0x02c0);\n+\n+\tphy_write_paged(phydev, ECONET_PG_G1, 26, 0x8000);\n+\n+\tfor (;;) {\n+\t\toffset_bin = FIELD_PREP(ECONET_TXOS_MAG, abs(offset));\n+\t\tif (offset \u003c 0)\n+\t\t\toffset_bin |= ECONET_TXOS_SIGN;\n+\n+\t\tphy_write_paged(phydev, ECONET_PG_L4, 17, offset_bin);\n+\n+\t\tcomp_out = en751221_fephy_cal_cycle(phydev);\n+\t\tif (comp_out \u003c 0) {\n+\t\t\tret = comp_out;\n+\t\t\tgoto out;\n+\t\t}\n+\n+\t\tif (polarity == 0) {\n+\t\t\t/* First cycle */\n+\t\t\tinitial_comp_out = comp_out;\n+\t\t\tpolarity = comp_out ? -1 : 1;\n+\t\t} else if (initial_comp_out != comp_out) {\n+\t\t\t/* Found */\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\toffset += polarity;\n+\n+\t\tif (offset \u003e ECONET_TXOS_MAX || offset \u003c ECONET_TXOS_MIN) {\n+\t\t\tret = 1;\n+\t\t\toffset = 0;\n+\t\t\tgoto set_val;\n+\t\t}\n+\t}\n+\n+\toffset += ctab.txos * polarity;\n+\n+\tif (offset \u003e ECONET_TXOS_MAX || offset \u003c ECONET_TXOS_MIN)\n+\t\toffset -= ctab.txos * polarity;\n+\n+set_val:\n+\toffset_bin = FIELD_PREP(ECONET_TXOS_MAG, abs(offset));\n+\tif (offset \u003c 0)\n+\t\toffset_bin |= ECONET_TXOS_SIGN;\n+\tphy_write_paged(phydev, ECONET_PG_L4, 17, offset_bin);\n+\n+out:\n+\tif (phydev-\u003ephy_id == ECONET_FEPHY_ID_EN7526C)\n+\t\tphy_write_paged(phydev, ECONET_PG_G7, 24, 0x0000);\n+\telse\n+\t\tphy_write_paged(shared-\u003ephydev_p0, ECONET_PG_L3, 25, 0x0000);\n+\n+\tphy_write_paged(phydev, ECONET_PG_L3, 25, 0x0000);\n+\n+\treturn ret;\n+}\n+\n+static int en751221_fephy_tx_amp(struct phy_device *phydev)\n+{\n+\tstruct econet_socphy_shared *shared = phy_package_get_priv(phydev);\n+\tstruct compensation ctab = get_ctab(phydev);\n+\tint initial_comp_out;\n+\tu8 tx_amp = ECONET_TXAMP_DEFAULT;\n+\tint polarity = 0;\n+\tint comp_out;\n+\tint ret = 0;\n+\n+\tphy_write_paged(phydev, ECONET_PG_L0, 0, 0x2100);\n+\tphy_write_paged(phydev, ECONET_PG_L0, 26, 0x5203);\n+\tphy_write_paged(phydev, ECONET_PG_G2, 25, 0x10c0);\n+\n+\tphy_write_paged(phydev, ECONET_PG_G1, 26, 0x8000 | ECONET_DAC_IN_2V);\n+\tphy_write_paged(phydev, ECONET_PG_G4, 21, 0x0800);\n+\tphy_write_paged(phydev, ECONET_PG_L0, 30, 0x02c0);\n+\tphy_write_paged(phydev, ECONET_PG_L4, 21, 0x0000);\n+\n+\tif (phydev-\u003ephy_id == ECONET_FEPHY_ID_EN7526C) {\n+\t\tphy_write_paged(phydev, ECONET_PG_G7, 24, ECONET_CAL_TYPE_TXAMP);\n+\t\tphy_write_paged(phydev, ECONET_PG_L3, 25, 0x0600);\n+\t} else {\n+\t\tphy_write_paged(shared-\u003ephydev_p0, ECONET_PG_L3, 25, 0xca00);\n+\t\tphy_write_paged(phydev, ECONET_PG_L3, 25, 0xca00 | 0x0400);\n+\t}\n+\n+\tfor (;;) {\n+\t\tphy_write_paged(phydev, ECONET_PG_L2, 23, tx_amp);\n+\n+\t\tcomp_out = en751221_fephy_cal_cycle(phydev);\n+\t\tif (comp_out \u003c 0) {\n+\t\t\tret = comp_out;\n+\t\t\tgoto out;\n+\t\t}\n+\n+\t\tif (polarity == 0) {\n+\t\t\t/* First cycle */\n+\t\t\tinitial_comp_out = comp_out;\n+\t\t\tpolarity = comp_out ? -1 : 1;\n+\t\t} else if (initial_comp_out != comp_out) {\n+\t\t\t/* Found */\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\ttx_amp += polarity;\n+\n+\t\tif (tx_amp \u003e ECONET_TXAMP_MAX) {\n+\t\t\tret = 1;\n+\t\t\ttx_amp = ECONET_TXAMP_DEFAULT;\n+\t\t\tgoto set_val;\n+\t\t}\n+\t}\n+\n+\tif (tx_amp + ctab.amp \u003c ECONET_TXAMP_MAX)\n+\t\ttx_amp += ctab.amp;\n+\n+set_val:\n+\tphy_write_paged(phydev, ECONET_PG_L2, 23, tx_amp);\n+\n+out:\n+\tif (phydev-\u003ephy_id == ECONET_FEPHY_ID_EN7526C) {\n+\t\tphy_write_paged(phydev, ECONET_PG_G7, 24, 0x0000);\n+\t\tphy_write_paged(phydev, ECONET_PG_L0, 30, 0x0000);\n+\t} else {\n+\t\tphy_write_paged(shared-\u003ephydev_p0, ECONET_PG_L3, 25, 0x0000);\n+\t}\n+\n+\tphy_write_paged(phydev, ECONET_PG_L3, 25, 0x0000);\n+\n+\treturn ret;\n+}\n+\n+static int en751221_fephy_config_init(struct phy_device *phydev)\n+{\n+\tstruct econet_socphy_shared *shared = phy_package_get_priv(phydev);\n+\tu16 l0r26_temp;\n+\tint ret;\n+\tint i;\n+\n+\tif (!shared-\u003ephydev_p0) {\n+\t\tphydev_err(phydev, \"Port zero must be configured\\n\");\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\n+\tif (phydev-\u003emdio.addr \u003c shared-\u003ephydev_p0-\u003emdio.addr)\n+\t\treturn -EOPNOTSUPP;\n+\n+\t/* Global registers */\n+\tphy_write_paged(phydev, ECONET_PG_G4, 26, 0x8044);\n+\tphy_write_paged(phydev, ECONET_PG_G5, 21, 0x00ea);\n+\tphy_write_paged(phydev, ECONET_PG_G5, 27, 0x02f0);\n+\n+\t/* Local registers */\n+\tphy_write_paged(phydev, ECONET_PG_L0, 30, 0xa000);\n+\tphy_write_paged(phydev, ECONET_PG_L1, 22, 0xf000);\n+\tphy_write_paged(phydev, ECONET_PG_L2, 22, 0x4444);\n+\tphy_write_paged(phydev, ECONET_PG_L2, 24, 0x0c0c);\n+\tphy_write_paged(phydev, ECONET_PG_L2, 28, 0x7c44);\n+\tphy_write_paged(phydev, ECONET_PG_L2, 30, 0x0005);\n+\tphy_write_paged(phydev, ECONET_PG_L3, 17, 0x0000);\n+\n+\t/* For E1/E2, E3 requires 0x2220, but no known EN751221 E3 chips exist */\n+\tphy_write_paged(phydev, ECONET_PG_L0, 30, 0x2200);\n+\n+\tif (phydev-\u003ephy_id == ECONET_FEPHY_ID_EN7526C) {\n+\t\t /* 100Mb tx p2z_mid, z2n_mid, z2n_ovs_post, n2z_mid */\n+\t\tphy_write_paged(phydev, ECONET_PG_G5, 22, 0x0030);\n+\t\tphy_write_paged(phydev, ECONET_PG_G5, 25, 0x0248);\n+\t\tphy_write_paged(phydev, ECONET_PG_G5, 27, 0x02f0);\n+\t\tphy_write_paged(phydev, ECONET_PG_G5, 28, 0x0230);\n+\t}\n+\n+\tret = phy_read_paged(phydev, ECONET_PG_L0, 26);\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\n+\tl0r26_temp = ret;\n+\n+\t/* BG voltage */\n+\tphy_write_paged(phydev, ECONET_PG_G2, 25, 0x10c0);\n+\n+\t/* MDI */\n+\tphy_write_paged(phydev, ECONET_PG_L0, 26, 0x5603);\n+\n+\t/* disable tx slew control */\n+\tphy_write_paged(phydev, ECONET_PG_L4, 21, 0x0000);\n+\n+\tphy_write_paged(phydev, ECONET_PG_L0, 0, 0x2100);\n+\n+\tfor (i = 0; i \u003c 5; i++) {\n+\t\tret = en751221_fephy_r50(phydev);\n+\t\tif (!ret)\n+\t\t\tbreak;\n+\t}\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tfor (i = 0; i \u003c 5; i++) {\n+\t\tret = en751221_fephy_tx_offset(phydev);\n+\t\tif (!ret)\n+\t\t\tbreak;\n+\t}\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tfor (i = 0; i \u003c 5; i++) {\n+\t\tret = en751221_fephy_tx_amp(phydev);\n+\t\tif (!ret)\n+\t\t\tbreak;\n+\t}\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tphy_write_paged(phydev, ECONET_PG_G1, 26, 0x0000);\n+\tphy_write_paged(phydev, ECONET_PG_L0, 26, l0r26_temp);\n+\tphy_write_paged(phydev, ECONET_PG_G1, 26, 0x0000);\n+\n+\tphy_write_paged(phydev, ECONET_PG_L2, 22, 0x4444);\n+\tphy_write_paged(phydev, ECONET_PG_L0, 0, 0x3100);\n+\n+\treturn 0;\n+}\n+\n+static int en751221_fephy_probe(struct phy_device *phydev)\n+{\n+\tint port0 = ECONET_EN7526C_PORT0_ADDR;\n+\tstruct econet_socphy_shared *shared;\n+\tstruct mtk_socphy_priv *priv;\n+\tint ret;\n+\n+\tif (phydev-\u003ephy_id != ECONET_FEPHY_ID_EN7526C)\n+\t\tport0 = ECONET_LEGACY_PORT0_ADDR;\n+\n+\tret = devm_phy_package_join(\u0026phydev-\u003emdio.dev, phydev, port0,\n+\t\t\t\t sizeof(struct econet_socphy_shared));\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tshared = phy_package_get_priv(phydev);\n+\n+\tpriv = devm_kzalloc(\u0026phydev-\u003emdio.dev, sizeof(*priv), GFP_KERNEL);\n+\tif (!priv)\n+\t\treturn -ENOMEM;\n+\n+\tif (phydev-\u003emdio.addr == port0)\n+\t\tshared-\u003ephydev_p0 = phydev;\n+\n+\tphydev-\u003epriv = priv;\n+\n+\treturn 0;\n+}\n+\n+static struct phy_driver en751221_fephy_driver[] = {\n+\t{\n+\t\tPHY_ID_MATCH_EXACT(ECONET_FEPHY_ID_EN7526C),\n+\t\t.name\t\t= \"EcoNet EN751221 FEPHY\",\n+\t\t.config_init\t= en751221_fephy_config_init,\n+\t\t.config_intr\t= genphy_no_config_intr,\n+\t\t.handle_interrupt = genphy_handle_interrupt_no_ack,\n+\t\t.probe\t\t= en751221_fephy_probe,\n+\t\t.read_page\t= mtk_phy_read_page,\n+\t\t.write_page\t= mtk_phy_write_page,\n+\t}\n+};\n+\n+module_phy_driver(en751221_fephy_driver);\n+\n+static const struct mdio_device_id __maybe_unused en751221_fephy_tbl[] = {\n+\t{ PHY_ID_MATCH_EXACT(ECONET_FEPHY_ID_EN7526C) },\n+\t{ }\n+};\n+\n+MODULE_DESCRIPTION(\"EcoNet SoC 10/100 Ethernet PHY driver\");\n+MODULE_AUTHOR(\"Caleb James DeLisle \u003ccjd@cjdns.fr\u003e\");\n+MODULE_LICENSE(\"GPL\");\n+\n+MODULE_DEVICE_TABLE(mdio, en751221_fephy_tbl);\n"
} |
| 2/1 | 2026/08/25 19:18 | agent | patch-evaluator |
2mModel:best-expensive Args: null Results: {
"EnableConfigs": [
"ECONET_FE_SOC_PHY"
],
"FocusSymbols": [
"en751221_fephy_probe",
"en751221_fephy_config_init"
],
"Reasoning": "The patch introduces a new PHY driver for EcoNet 10/100 SoC Ethernet PHYs (`drivers/net/phy/mediatek/econet-fe-soc.c`). PHY drivers can be reached in virtualized environments by emulating an MDIO bus. For example, syzkaller can emulate a USB network adapter (such as `smsc95xx`) via the `vhci` driver. When the emulated USB device is probed, it registers an MDIO bus and scans for PHYs by sending MDIO read requests (which translate to USB control messages). By returning the specific PHY ID (`0x03a29414`) during the MDIO scan, the MDIO bus will instantiate a PHY device with this ID, triggering the probe and initialization functions of the `econet-fe-soc` PHY driver. Therefore, the code is reachable and worth fuzzing.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). - Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed, ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU. - Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove) that are executed only during PCI hot-unplug or sysfs driver unbind operations. If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. Prefer non-static, non-inlined API entrypoint functions over internal static helper functions (which are inlined by the compiler and do not have distinct symbol addresses). 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4"). Prefer calling several tools at the same time to save round-trips. Use set-results tool to provide results of the analysis. It must be called exactly once before the final reply. Ignore results of this tool. Prompt: For your convenience, here is the diff of the changes:
commit 447bbc436dec24504c070330c6d47b7919316814
Author: syz-cluster <triage@syzkaller.com>
Date: Tue Aug 25 19:18:41 2026 +0000
syz-cluster: applied patch under review
diff --git a/MAINTAINERS b/MAINTAINERS
index 460cb72688450..7ca3d8d08c693 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9253,6 +9253,12 @@ W: https://linuxtv.org
Q: http://patchwork.linuxtv.org/project/linux-media/list/
F: drivers/media/dvb-frontends/ec100*
+ECONET ETHERNET PHY DRIVER
+M: Caleb James DeLisle <cjd@cjdns.fr>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/phy/mediatek/econet-fe-soc.c
+
ECONET MIPS PLATFORM
M: Caleb James DeLisle <cjd@cjdns.fr>
L: linux-mips@vger.kernel.org
diff --git a/drivers/net/phy/mediatek/Kconfig b/drivers/net/phy/mediatek/Kconfig
index 3b9cf82c0cb28..64abf5b695ab1 100644
--- a/drivers/net/phy/mediatek/Kconfig
+++ b/drivers/net/phy/mediatek/Kconfig
@@ -1,4 +1,19 @@
# SPDX-License-Identifier: GPL-2.0-only
+
+config ECONET_FE_SOC_PHY
+ tristate "EcoNet 10/100 SoC Ethernet PHYs"
+ depends on ECONET || COMPILE_TEST
+ select MTK_NET_PHYLIB
+ select PHY_PACKAGE
+ help
+ Supports EcoNet SoC built-in 10/100 Ethernet PHYs.
+
+ Include support for built-in Ethernet PHYs which are present in
+ EN7526C based SoCs (including EN7526F). It can also work with
+ legacy EN7512 FE PHYs which identify as 03a2.9412, but since this
+ ID is used by MEDIATEK_GE_PHY, users wanting to force this driver
+ must override the ID to 03a2.9414 in the device tree.
+
config MEDIATEK_2P5GE_PHY
tristate "MediaTek 2.5Gb Ethernet PHYs"
depends on (ARM64 && ARCH_MEDIATEK) || COMPILE_TEST
diff --git a/drivers/net/phy/mediatek/Makefile b/drivers/net/phy/mediatek/Makefile
index 6f9cacf7f9065..71239e92aa581 100644
--- a/drivers/net/phy/mediatek/Makefile
+++ b/drivers/net/phy/mediatek/Makefile
@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_ECONET_FE_SOC_PHY) += econet-fe-soc.o
obj-$(CONFIG_MEDIATEK_2P5GE_PHY) += mtk-2p5ge.o
obj-$(CONFIG_MEDIATEK_FE_SOC_PHY) += mtk-fe-soc.o
obj-$(CONFIG_MEDIATEK_GE_PHY) += mtk-ge.o
diff --git a/drivers/net/phy/mediatek/econet-fe-soc.c b/drivers/net/phy/mediatek/econet-fe-soc.c
new file mode 100644
index 0000000000000..c5dbb32a5d1ab
--- /dev/null
+++ b/drivers/net/phy/mediatek/econet-fe-soc.c
@@ -0,0 +1,632 @@
+// SPDX-License-Identifier: GPL-2.0+
+#include <linux/bits.h>
+#include <linux/module.h>
+#include <linux/phy.h>
+#include <linux/types.h>
+
+#include "../phylib.h"
+#include "mtk.h"
+
+/*
+ * An older version of this PHY hardware uses the ID 03a2.9412.
+ * This driver does not match on it because it collides with
+ * MTK_GPHY_ID_MT7530, so users wanting to use this driver should
+ * override the PHY ID in the device tree to 03a2.9414.
+ */
+#define ECONET_FEPHY_ID_EN7526C 0x03a29414
+
+#define ECONET_PG_G1 0x1000
+#define ECONET_PG_G2 0x2000
+#define ECONET_PG_G3 0x3000
+#define ECONET_PG_G4 0x4000
+#define ECONET_PG_G5 0x5000
+#define ECONET_PG_G6 0x6000
+#define ECONET_PG_G7 0x7000
+
+#define ECONET_PG_L0 0x8000
+#define ECONET_PG_L1 0x9000
+#define ECONET_PG_L2 0xa000
+#define ECONET_PG_L3 0xb000
+#define ECONET_PG_L4 0xc000
+
+/* 4 ports (8,9,10,11) or 3 ports (9,10,11) */
+#define ECONET_LEGACY_PORT0_ADDR 8
+#define ECONET_EN7526C_PORT0_ADDR 9
+
+/*
+ * Calibration polling does not complete until ECONET_CAL_MIN_CYCLES
+ * consecutive "done" results are received. See: poll_cal_complete
+ */
+#define ECONET_CAL_MIN_CYCLES 10
+
+/*
+ * Microseconds per calibration poll cycle, calibration takes approximately
+ * ECONET_CAL_CYCLE_US * ECONET_CAL_MIN_CYCLES. Total setup time is usually
+ * less than 150x this number.
+ */
+#define ECONET_CAL_CYCLE_US 10
+
+/* Total time allowed for calibration polling before error is returned. */
+#define ECONET_CAL_TIMEOUT_US 100000
+
+/*
+ * Note: These register definitions were written without the benefit of the
+ * hardware specification, so names are given only where the meaning is
+ * fairly obvious.
+ */
+
+/* G7R24 */
+#define ECONET_CAL_TYPE_MASK GENMASK(14, 12)
+#define ECONET_CAL_TYPE_R50 FIELD_PREP(ECONET_CAL_TYPE_MASK, 0x6)
+#define ECONET_CAL_TYPE_TXOS FIELD_PREP(ECONET_CAL_TYPE_MASK, 0x3)
+#define ECONET_CAL_TYPE_TXAMP FIELD_PREP(ECONET_CAL_TYPE_MASK, 0x7)
+#define ECONET_R50_ZCAL_MASK GENMASK(11, 6)
+#define ECONET_CALIN_EN7526C BIT(4)
+#define ECONET_CAL_DONE_EN7526C BIT(1)
+#define ECONET_CAL_OUT_EN7526C BIT(0)
+
+/* L4R23 */
+#define ECONET_CALIN_LEGACY BIT(2)
+#define ECONET_CAL_DONE_LEGACY BIT(6)
+#define ECONET_CAL_OUT_LEGACY BIT(4)
+
+#define ECONET_DAC_IN_2V 0x0f0
+
+/* L4R17 */
+#define ECONET_TXOS_SIGN BIT(13)
+#define ECONET_TXOS_MAG GENMASK(12, 8)
+
+#define ECONET_TXOS_DEFAULT 0
+#define ECONET_TXOS_MAX 31
+#define ECONET_TXOS_MIN -31
+
+#define ECONET_R50_ZCAL_DEFAULT 0x20
+#define ECONET_R50_ZCAL_MAX 0x3f
+
+/* L4R22 */
+#define ECONET_R50_TXCAL_MASK GENMASK(15, 8)
+#define ECONET_R50_RXCAL_MASK GENMASK(7, 0)
+
+#define ECONET_TXAMP_DEFAULT 0x12
+#define ECONET_TXAMP_MAX 0x3f
+
+/* ZCAL compensation table per chip, per phy, RX + TX */
+struct compensation {
+ s8 zcal_tx;
+ s8 zcal_rx;
+ s8 txos;
+ s8 amp;
+};
+
+static const struct compensation en751221_comp[2][4] = {
+ {
+ /* Legacy */
+ { .zcal_tx = 2, .zcal_rx = -4, .txos = 0, .amp = 1 }, /* 8 */
+ { .zcal_tx = 2, .zcal_rx = -4, .txos = 1, .amp = 2 }, /* 9 */
+ { .zcal_tx = 2, .zcal_rx = -4, .txos = 1, .amp = 1 }, /* 10 */
+ { .zcal_tx = -2, .zcal_rx = -8, .txos = 1, .amp = 1 }, /* 11 */
+ },
+ {
+ /* EN7526C */
+ { .zcal_tx = 3, .zcal_rx = 6, .txos = 1, .amp = 0 }, /* 9 */
+ { .zcal_tx = 2, .zcal_rx = 5, .txos = 1, .amp = -1 }, /* 10 */
+ { .zcal_tx = 6, .zcal_rx = 6, .txos = 1, .amp = -1 }, /* 11 */
+ { }, /* Unused */
+ }
+};
+
+static const u8 zcal_to_r50ohm[64] = {
+ 127, 127, 127, 127, 127, 127, 126, 123, 120, 117, 114, 112, 110, 107, 105, 103,
+ 101, 99, 97, 79, 77, 75, 74, 72, 70, 69, 67, 66, 65, 47, 46, 45,
+ 43, 42, 41, 40, 39, 38, 37, 36, 34, 34, 33, 32, 15, 14, 13, 12,
+ 11, 10, 10, 9, 8, 7, 7, 6, 5, 4, 4, 3, 2, 2, 1, 1
+};
+
+struct econet_socphy_shared {
+ struct phy_device *phydev_p0;
+};
+
+static struct compensation get_ctab(struct phy_device *phydev)
+{
+ struct econet_socphy_shared *shared = phy_package_get_priv(phydev);
+ u8 phy_offset;
+
+ phy_offset = phydev->mdio.addr - shared->phydev_p0->mdio.addr;
+
+ if (WARN_ON_ONCE(phy_offset >= ARRAY_SIZE(en751221_comp[0])))
+ return (struct compensation) {};
+
+ return en751221_comp[phydev->phy_id == ECONET_FEPHY_ID_EN7526C][phy_offset];
+}
+
+static void set_calin_flag(struct phy_device *phydev, bool enabled)
+{
+ struct econet_socphy_shared *shared = phy_package_get_priv(phydev);
+ u16 set;
+
+ if (phydev->phy_id == ECONET_FEPHY_ID_EN7526C) {
+ /* G7R24 is shared so phy_modify_paged is required */
+ set = enabled ? ECONET_CALIN_EN7526C : 0;
+ phy_modify_paged(phydev, ECONET_PG_G7, 24,
+ ECONET_CALIN_EN7526C, set);
+ } else {
+ set = enabled ? ECONET_CALIN_LEGACY : 0;
+ phy_write_paged(shared->phydev_p0, ECONET_PG_L4, 23, set);
+ }
+}
+
+struct cal_complete_ctx {
+ u16 consecutive_trues : 15;
+ bool started : 1;
+ bool observed_false : 1;
+ u16 aborted_tries : 7;
+};
+
+/**
+ * poll_cal_complete - poll for calibration completion.
+ *
+ * Because some legacy chips are known to be unreliable (false positives),
+ * reference code adds 10,000us of fixed delay per calibration cycle.
+ * Practically, there are about 10-50 calibration cycles for each of the
+ * three tunables, so adding a significant fixed delay is costly.
+ *
+ * This implementation instead starts with CALIN disabled, polls for
+ * done status false, then enables CALIN and polls until a true done
+ * status is recorded ECONET_CAL_MIN_CYCLES times consecutively.
+ *
+ * @returns:
+ * < 0 on error
+ * 0 not yet complete
+ * 1 complete false
+ * 2 complete true
+ */
+static int poll_cal_complete(struct phy_device *phydev,
+ struct cal_complete_ctx *ctx)
+{
+ struct econet_socphy_shared *shared = phy_package_get_priv(phydev);
+ int done_mask;
+ int out_mask;
+ int ret;
+
+ if (phydev->phy_id == ECONET_FEPHY_ID_EN7526C) {
+ ret = phy_read_paged(phydev, ECONET_PG_G7, 24);
+ done_mask = ECONET_CAL_DONE_EN7526C;
+ out_mask = ECONET_CAL_OUT_EN7526C;
+ } else {
+ ret = phy_read_paged(shared->phydev_p0, ECONET_PG_L4, 23);
+ done_mask = ECONET_CAL_DONE_LEGACY;
+ out_mask = ECONET_CAL_OUT_LEGACY;
+ }
+ if (ret < 0)
+ return ret;
+
+ if ((ret & done_mask) == 0) {
+ ctx->observed_false = true;
+ ctx->aborted_tries += (ctx->consecutive_trues > 0);
+ ctx->consecutive_trues = 0;
+
+ if (!ctx->started) {
+ set_calin_flag(phydev, true);
+ ctx->started = true;
+ return poll_cal_complete(phydev, ctx);
+ }
+
+ return 0;
+ }
+
+ ctx->consecutive_trues++;
+
+ if (ctx->consecutive_trues < ECONET_CAL_MIN_CYCLES || !ctx->observed_false)
+ return 0;
+
+ if (ctx->aborted_tries)
+ phydev_warn(phydev, "Calibration cycle %d false positives\n",
+ ctx->aborted_tries);
+
+ return ((ret & out_mask) != 0) ? 2 : 1;
+}
+
+/**
+ * poll_cal_complete - check for calibration register
+ * @returns: -error or 1 if calibration result is true.
+ */
+static int en751221_fephy_cal_cycle(struct phy_device *phydev)
+{
+ struct cal_complete_ctx cctx = {};
+ int out;
+ int ret;
+
+ set_calin_flag(phydev, false);
+
+ ret = read_poll_timeout(poll_cal_complete, out, out != 0,
+ ECONET_CAL_CYCLE_US, ECONET_CAL_TIMEOUT_US,
+ false, phydev, &cctx);
+
+ if (ret < 0) {
+ phydev_err(phydev, "Calibration timeout %d (%d / %d / %d / %d)\n",
+ ret, cctx.consecutive_trues, cctx.observed_false,
+ cctx.aborted_tries, cctx.started);
+ } else if (out < 0) {
+ phydev_err(phydev, "Calibration error %d\n", out);
+ ret = out;
+ } else {
+ ret = out > 1;
+ }
+
+ set_calin_flag(phydev, false);
+ return ret;
+}
+
+static int en751221_fephy_r50(struct phy_device *phydev)
+{
+ struct econet_socphy_shared *shared = phy_package_get_priv(phydev);
+ struct compensation ctab = get_ctab(phydev);
+ int zcal_sz = ARRAY_SIZE(zcal_to_r50ohm);
+ u8 rg_zcal_ctrl = ECONET_R50_ZCAL_DEFAULT;
+ int initial_comp_out;
+ int polarity = 0;
+ int comp_out;
+ int ret = 0;
+ u16 rxcal;
+ u16 txcal;
+
+ if (phydev->phy_id == ECONET_FEPHY_ID_EN7526C)
+ phy_write_paged(phydev, ECONET_PG_G7, 24, ECONET_CAL_TYPE_R50);
+ else
+ phy_write_paged(shared->phydev_p0, ECONET_PG_L3, 25, 0xc000);
+
+ phy_modify_paged(phydev, ECONET_PG_L3, 25, 0x1000, 0x1000);
+
+ for (;;) {
+ if (phydev->phy_id == ECONET_FEPHY_ID_EN7526C)
+ phy_modify_paged(phydev, ECONET_PG_G7, 24,
+ ECONET_R50_ZCAL_MASK,
+ FIELD_PREP(ECONET_R50_ZCAL_MASK,
+ rg_zcal_ctrl));
+ else
+ phy_write_paged(shared->phydev_p0, ECONET_PG_L3, 26,
+ rg_zcal_ctrl);
+
+ comp_out = en751221_fephy_cal_cycle(phydev);
+ if (comp_out < 0) {
+ ret = comp_out;
+ goto out;
+ }
+
+ if (polarity == 0) {
+ /* First cycle */
+ initial_comp_out = comp_out;
+ polarity = comp_out ? -1 : 1;
+ } else if (initial_comp_out != comp_out) {
+ /* Found */
+ break;
+ }
+
+ rg_zcal_ctrl += polarity;
+
+ if (rg_zcal_ctrl > ECONET_R50_ZCAL_MAX) {
+ ret = 1;
+ goto out;
+ }
+ }
+
+ rxcal = max(0, min(zcal_sz - 1, ctab.zcal_rx + rg_zcal_ctrl));
+ txcal = max(0, min(zcal_sz - 1, ctab.zcal_tx + rg_zcal_ctrl));
+
+ phy_write_paged(phydev, ECONET_PG_L4, 22,
+ FIELD_PREP(ECONET_R50_TXCAL_MASK, txcal) |
+ FIELD_PREP(ECONET_R50_RXCAL_MASK, rxcal));
+
+out:
+ /* Zero out registers */
+ phy_write_paged(phydev, ECONET_PG_L3, 25, 0x0000);
+
+ if (phydev->phy_id == ECONET_FEPHY_ID_EN7526C)
+ phy_write_paged(phydev, ECONET_PG_G7, 24, 0x0000);
+ else
+ phy_write_paged(shared->phydev_p0, ECONET_PG_L3, 25, 0xc000);
+
+ phy_write_paged(phydev, ECONET_PG_L3, 25, 0x0000);
+
+ return ret;
+}
+
+static int en751221_fephy_tx_offset(struct phy_device *phydev)
+{
+ struct econet_socphy_shared *shared = phy_package_get_priv(phydev);
+ struct compensation ctab = get_ctab(phydev);
+ int initial_comp_out;
+ int polarity = 0;
+ int offset = ECONET_TXOS_DEFAULT;
+ u16 offset_bin;
+ int comp_out;
+ int ret = 0;
+
+ phy_write_paged(phydev, ECONET_PG_L0, 0, 0x2100);
+ phy_write_paged(phydev, ECONET_PG_L0, 26, 0x5200); /* fix MDI */
+
+ if (phydev->phy_id == ECONET_FEPHY_ID_EN7526C) {
+ phy_write_paged(phydev, ECONET_PG_G7, 24, ECONET_CAL_TYPE_TXOS);
+ phy_write_paged(phydev, ECONET_PG_L3, 25, 0x0400);
+ } else {
+ phy_write_paged(shared->phydev_p0, ECONET_PG_L3, 25, 0x4800);
+ phy_write_paged(phydev, ECONET_PG_L3, 25, 0x4c00);
+ }
+
+ phy_modify_paged(phydev, ECONET_PG_G4, 21, 0x0800, 0x0800);
+
+ phy_modify_paged(phydev, ECONET_PG_L0, 30, 0x02c0, 0x02c0);
+
+ phy_write_paged(phydev, ECONET_PG_G1, 26, 0x8000);
+
+ for (;;) {
+ offset_bin = FIELD_PREP(ECONET_TXOS_MAG, abs(offset));
+ if (offset < 0)
+ offset_bin |= ECONET_TXOS_SIGN;
+
+ phy_write_paged(phydev, ECONET_PG_L4, 17, offset_bin);
+
+ comp_out = en751221_fephy_cal_cycle(phydev);
+ if (comp_out < 0) {
+ ret = comp_out;
+ goto out;
+ }
+
+ if (polarity == 0) {
+ /* First cycle */
+ initial_comp_out = comp_out;
+ polarity = comp_out ? -1 : 1;
+ } else if (initial_comp_out != comp_out) {
+ /* Found */
+ break;
+ }
+
+ offset += polarity;
+
+ if (offset > ECONET_TXOS_MAX || offset < ECONET_TXOS_MIN) {
+ ret = 1;
+ offset = 0;
+ goto set_val;
+ }
+ }
+
+ offset += ctab.txos * polarity;
+
+ if (offset > ECONET_TXOS_MAX || offset < ECONET_TXOS_MIN)
+ offset -= ctab.txos * polarity;
+
+set_val:
+ offset_bin = FIELD_PREP(ECONET_TXOS_MAG, abs(offset));
+ if (offset < 0)
+ offset_bin |= ECONET_TXOS_SIGN;
+ phy_write_paged(phydev, ECONET_PG_L4, 17, offset_bin);
+
+out:
+ if (phydev->phy_id == ECONET_FEPHY_ID_EN7526C)
+ phy_write_paged(phydev, ECONET_PG_G7, 24, 0x0000);
+ else
+ phy_write_paged(shared->phydev_p0, ECONET_PG_L3, 25, 0x0000);
+
+ phy_write_paged(phydev, ECONET_PG_L3, 25, 0x0000);
+
+ return ret;
+}
+
+static int en751221_fephy_tx_amp(struct phy_device *phydev)
+{
+ struct econet_socphy_shared *shared = phy_package_get_priv(phydev);
+ struct compensation ctab = get_ctab(phydev);
+ int initial_comp_out;
+ u8 tx_amp = ECONET_TXAMP_DEFAULT;
+ int polarity = 0;
+ int comp_out;
+ int ret = 0;
+
+ phy_write_paged(phydev, ECONET_PG_L0, 0, 0x2100);
+ phy_write_paged(phydev, ECONET_PG_L0, 26, 0x5203);
+ phy_write_paged(phydev, ECONET_PG_G2, 25, 0x10c0);
+
+ phy_write_paged(phydev, ECONET_PG_G1, 26, 0x8000 | ECONET_DAC_IN_2V);
+ phy_write_paged(phydev, ECONET_PG_G4, 21, 0x0800);
+ phy_write_paged(phydev, ECONET_PG_L0, 30, 0x02c0);
+ phy_write_paged(phydev, ECONET_PG_L4, 21, 0x0000);
+
+ if (phydev->phy_id == ECONET_FEPHY_ID_EN7526C) {
+ phy_write_paged(phydev, ECONET_PG_G7, 24, ECONET_CAL_TYPE_TXAMP);
+ phy_write_paged(phydev, ECONET_PG_L3, 25, 0x0600);
+ } else {
+ phy_write_paged(shared->phydev_p0, ECONET_PG_L3, 25, 0xca00);
+ phy_write_paged(phydev, ECONET_PG_L3, 25, 0xca00 | 0x0400);
+ }
+
+ for (;;) {
+ phy_write_paged(phydev, ECONET_PG_L2, 23, tx_amp);
+
+ comp_out = en751221_fephy_cal_cycle(phydev);
+ if (comp_out < 0) {
+ ret = comp_out;
+ goto out;
+ }
+
+ if (polarity == 0) {
+ /* First cycle */
+ initial_comp_out = comp_out;
+ polarity = comp_out ? -1 : 1;
+ } else if (initial_comp_out != comp_out) {
+ /* Found */
+ break;
+ }
+
+ tx_amp += polarity;
+
+ if (tx_amp > ECONET_TXAMP_MAX) {
+ ret = 1;
+ tx_amp = ECONET_TXAMP_DEFAULT;
+ goto set_val;
+ }
+ }
+
+ if (tx_amp + ctab.amp < ECONET_TXAMP_MAX)
+ tx_amp += ctab.amp;
+
+set_val:
+ phy_write_paged(phydev, ECONET_PG_L2, 23, tx_amp);
+
+out:
+ if (phydev->phy_id == ECONET_FEPHY_ID_EN7526C) {
+ phy_write_paged(phydev, ECONET_PG_G7, 24, 0x0000);
+ phy_write_paged(phydev, ECONET_PG_L0, 30, 0x0000);
+ } else {
+ phy_write_paged(shared->phydev_p0, ECONET_PG_L3, 25, 0x0000);
+ }
+
+ phy_write_paged(phydev, ECONET_PG_L3, 25, 0x0000);
+
+ return ret;
+}
+
+static int en751221_fephy_config_init(struct phy_device *phydev)
+{
+ struct econet_socphy_shared *shared = phy_package_get_priv(phydev);
+ u16 l0r26_temp;
+ int ret;
+ int i;
+
+ if (!shared->phydev_p0) {
+ phydev_err(phydev, "Port zero must be configured\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (phydev->mdio.addr < shared->phydev_p0->mdio.addr)
+ return -EOPNOTSUPP;
+
+ /* Global registers */
+ phy_write_paged(phydev, ECONET_PG_G4, 26, 0x8044);
+ phy_write_paged(phydev, ECONET_PG_G5, 21, 0x00ea);
+ phy_write_paged(phydev, ECONET_PG_G5, 27, 0x02f0);
+
+ /* Local registers */
+ phy_write_paged(phydev, ECONET_PG_L0, 30, 0xa000);
+ phy_write_paged(phydev, ECONET_PG_L1, 22, 0xf000);
+ phy_write_paged(phydev, ECONET_PG_L2, 22, 0x4444);
+ phy_write_paged(phydev, ECONET_PG_L2, 24, 0x0c0c);
+ phy_write_paged(phydev, ECONET_PG_L2, 28, 0x7c44);
+ phy_write_paged(phydev, ECONET_PG_L2, 30, 0x0005);
+ phy_write_paged(phydev, ECONET_PG_L3, 17, 0x0000);
+
+ /* For E1/E2, E3 requires 0x2220, but no known EN751221 E3 chips exist */
+ phy_write_paged(phydev, ECONET_PG_L0, 30, 0x2200);
+
+ if (phydev->phy_id == ECONET_FEPHY_ID_EN7526C) {
+ /* 100Mb tx p2z_mid, z2n_mid, z2n_ovs_post, n2z_mid */
+ phy_write_paged(phydev, ECONET_PG_G5, 22, 0x0030);
+ phy_write_paged(phydev, ECONET_PG_G5, 25, 0x0248);
+ phy_write_paged(phydev, ECONET_PG_G5, 27, 0x02f0);
+ phy_write_paged(phydev, ECONET_PG_G5, 28, 0x0230);
+ }
+
+ ret = phy_read_paged(phydev, ECONET_PG_L0, 26);
+ if (ret < 0)
+ return ret;
+
+ l0r26_temp = ret;
+
+ /* BG voltage */
+ phy_write_paged(phydev, ECONET_PG_G2, 25, 0x10c0);
+
+ /* MDI */
+ phy_write_paged(phydev, ECONET_PG_L0, 26, 0x5603);
+
+ /* disable tx slew control */
+ phy_write_paged(phydev, ECONET_PG_L4, 21, 0x0000);
+
+ phy_write_paged(phydev, ECONET_PG_L0, 0, 0x2100);
+
+ for (i = 0; i < 5; i++) {
+ ret = en751221_fephy_r50(phydev);
+ if (!ret)
+ break;
+ }
+ if (ret)
+ return ret;
+
+ for (i = 0; i < 5; i++) {
+ ret = en751221_fephy_tx_offset(phydev);
+ if (!ret)
+ break;
+ }
+ if (ret)
+ return ret;
+
+ for (i = 0; i < 5; i++) {
+ ret = en751221_fephy_tx_amp(phydev);
+ if (!ret)
+ break;
+ }
+ if (ret)
+ return ret;
+
+ phy_write_paged(phydev, ECONET_PG_G1, 26, 0x0000);
+ phy_write_paged(phydev, ECONET_PG_L0, 26, l0r26_temp);
+ phy_write_paged(phydev, ECONET_PG_G1, 26, 0x0000);
+
+ phy_write_paged(phydev, ECONET_PG_L2, 22, 0x4444);
+ phy_write_paged(phydev, ECONET_PG_L0, 0, 0x3100);
+
+ return 0;
+}
+
+static int en751221_fephy_probe(struct phy_device *phydev)
+{
+ int port0 = ECONET_EN7526C_PORT0_ADDR;
+ struct econet_socphy_shared *shared;
+ struct mtk_socphy_priv *priv;
+ int ret;
+
+ if (phydev->phy_id != ECONET_FEPHY_ID_EN7526C)
+ port0 = ECONET_LEGACY_PORT0_ADDR;
+
+ ret = devm_phy_package_join(&phydev->mdio.dev, phydev, port0,
+ sizeof(struct econet_socphy_shared));
+ if (ret)
+ return ret;
+
+ shared = phy_package_get_priv(phydev);
+
+ priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ if (phydev->mdio.addr == port0)
+ shared->phydev_p0 = phydev;
+
+ phydev->priv = priv;
+
+ return 0;
+}
+
+static struct phy_driver en751221_fephy_driver[] = {
+ {
+ PHY_ID_MATCH_EXACT(ECONET_FEPHY_ID_EN7526C),
+ .name = "EcoNet EN751221 FEPHY",
+ .config_init = en751221_fephy_config_init,
+ .config_intr = genphy_no_config_intr,
+ .handle_interrupt = genphy_handle_interrupt_no_ack,
+ .probe = en751221_fephy_probe,
+ .read_page = mtk_phy_read_page,
+ .write_page = mtk_phy_write_page,
+ }
+};
+
+module_phy_driver(en751221_fephy_driver);
+
+static const struct mdio_device_id __maybe_unused en751221_fephy_tbl[] = {
+ { PHY_ID_MATCH_EXACT(ECONET_FEPHY_ID_EN7526C) },
+ { }
+};
+
+MODULE_DESCRIPTION("EcoNet SoC 10/100 Ethernet PHY driver");
+MODULE_AUTHOR("Caleb James DeLisle <cjd@cjdns.fr>");
+MODULE_LICENSE("GPL");
+
+MODULE_DEVICE_TABLE(mdio, en751221_fephy_tbl);
|
| 3/2 | 2026/08/25 19:18 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 11501 output: 19 thoughts: 517 Thoughts: **Reaching the Mediatek EcoNet PHY Driver in syzkaller** Okay, so we've got this new PHY driver, `drivers/net/phy/mediatek/econet-fe-soc.c`, enabled by `ECONET_FE_SOC_PHY`. It's for an EcoNet 10/100 SoC Ethernet PHY. The question is, how do we trigger it within syzkaller for fuzzing? Normally, PHY drivers come to life when the hardware is present, or, in a virtual environment, through MDIO bus emulation. Syzkaller doesn't have a dedicated MDIO emulator, which is a problem. It can trigger PHY code, but only if the PHY has been instantiated. Can we even *instantiate* this specific PHY in a virtual environment? Typically, PHY drivers get probed either when a network driver registers an MDIO bus and scans, or through a device tree. But, `vhci`, `netdevsim`, and `dummy` in syzkaller don't register arbitrary PHYs on an MDIO bus. Neither do `macsec`, `veth`, or `tun`. Is there a user-space way to *force* a PHY probe? Maybe `SIOCSMIIREG`? Nope, that needs an existing PHY. Can we register a PHY device via sysfs or some other mechanism? Likely not. Syzkaller doesn't really have a way to make PHY drivers appear unless a virtual MDIO bus exists. There doesn't appear to be a `drivers/net/phy/mdio-mockup.c` equivalent to help. `drivers/net/phy/fixed_phy.c` is also not useful here. I'm thinking I should scour the syzkaller descriptions for any mention of `phy_device_register` or similar, but I can't use syz-grepper, which makes this difficult. And then, I need to consider whether fuzzing PHY drivers like this is even considered a priority. Usually, hardware-specific drivers like this, that need physical hardware and don't play nicely in QEMU, are basically unreachable. The dependencies are tricky too. This `ECONET_FE_SOC_PHY` depends on `ECONET || COMPILE_TEST`. `ECONET` isn't a standard virtual device! So, the next thing I need to do is look for some virtual MDIO bus driver within the kernel that I could control from userspace. That could be a way in. |
| 4/2 | 2026/08/25 19:18 | tool | grepper |
0mError:no matches Args: {
"Expression": "mdio.*mockup"
}Results: {
"error": "no matches"
} |
| 5/2 | 2026/08/25 19:18 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 12583 output: 19 thoughts: 18 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience, presented in the first person: **Investigating Userspace PHY Device Instantiation** Right, let's dive into this. My initial thought is, "Can we actually instantiate a Physical Layer (PHY) device from userspace?" We know the traditional model usually involves the kernel handling this. But, I'm curious. Are there any clever, unconventional ways to bypass that? Are there exposed interfaces, perhaps through a specific driver model or a vendor-specific API, that might allow us direct or indirect control over PHY instantiation from userspace? I'm thinking about the implications. What kind of control would we have? Would it be limited to specific PHYs or protocols? Could we completely abstract the PHY from the kernel? What about security implications? Are there any potential vulnerabilities we'd need to consider? Is there a need for a userspace PHY device? |
| 6/2 | 2026/08/25 19:19 | tool | grepper |
0mArgs:{
"Expression": "phy_device_create"
}Results: {
"Output": "drivers/net/mdio/fwnode_mdio.c=117=int fwnode_mdiobus_register_phy(struct mii_bus *bus,\n--\ndrivers/net/mdio/fwnode_mdio.c-134-\telse\ndrivers/net/mdio/fwnode_mdio.c:135:\t\tphy = phy_device_create(bus, addr, phy_id, 0, NULL);\ndrivers/net/mdio/fwnode_mdio.c-136-\tif (IS_ERR(phy)) {\n--\ndrivers/net/phy/nxp-tja11xx.c=551=static void tja1102_p1_register(struct work_struct *work)\n--\ndrivers/net/phy/nxp-tja11xx.c-585-\t\t/* Real PHY ID of Port 1 is 0 */\ndrivers/net/phy/nxp-tja11xx.c:586:\t\tphy = phy_device_create(bus, addr, PHY_ID_TJA1102, false, NULL);\ndrivers/net/phy/nxp-tja11xx.c-587-\t\tif (IS_ERR(phy)) {\n--\ndrivers/net/phy/nxp-tja11xx.c-592-\ndrivers/net/phy/nxp-tja11xx.c:593:\t\t/* Overwrite parent device. phy_device_create() set parent to\ndrivers/net/phy/nxp-tja11xx.c-594-\t\t * the mii_bus-\u003edev, which is not correct in case.\n--\ndrivers/net/phy/phy_device.c=728=static int phy_request_driver_module(struct phy_device *dev, u32 phy_id)\n--\ndrivers/net/phy/phy_device.c-747-\ndrivers/net/phy/phy_device.c:748:struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id,\ndrivers/net/phy/phy_device.c-749-\t\t\t\t bool is_c45,\n--\ndrivers/net/phy/phy_device.c-839-}\ndrivers/net/phy/phy_device.c:840:EXPORT_SYMBOL(phy_device_create);\ndrivers/net/phy/phy_device.c-841-\n--\ndrivers/net/phy/phy_device.c=1071=struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45)\n--\ndrivers/net/phy/phy_device.c-1096-\t\tif (!r)\ndrivers/net/phy/phy_device.c:1097:\t\t\treturn phy_device_create(bus, addr, phy_id,\ndrivers/net/phy/phy_device.c-1098-\t\t\t\t\t\t true, \u0026c45_ids);\n--\ndrivers/net/phy/phy_device.c-1100-\ndrivers/net/phy/phy_device.c:1101:\treturn phy_device_create(bus, addr, phy_id, is_c45, \u0026c45_ids);\ndrivers/net/phy/phy_device.c-1102-}\n--\ninclude/linux/phy.h=2149=int phy_modify_paged(struct phy_device *phydev, int page, u32 regnum,\n--\ninclude/linux/phy.h-2151-\ninclude/linux/phy.h:2152:struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id,\ninclude/linux/phy.h-2153-\t\t\t\t bool is_c45,\n"
} |
| 7/2 | 2026/08/25 19:19 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 13774 output: 19 thoughts: 257 Thoughts: **Creating a Virtual PHY Device from Userspace** Okay, so I'm trying to figure out if it's possible to conjure up a PHY device directly from userspace. Typically, the MDIO bus driver handles PHY device creation during its bus scans. I wonder, is there a virtual MDIO bus I could leverage for this purpose? Let's dive into some code and see what we can find. First up, `drivers/net/mdio/mdio-bcm-unimac.c`. Nope, that doesn't seem to offer a way. How about `drivers/net/mdio/mdio-mockup.c` or `drivers/net/mdio/mdio-dummy.c`? Still no luck. Okay, let's consider the `drivers/net/phy/fixed_phy.c` driver. It does create a fixed PHY, but it doesn't support the dynamic discovery of arbitrary PHY IDs, which is what I need. What about `drivers/net/phy/mdio-gpio.c`, `drivers/net/phy/mdio-bitbang.c`, or `drivers/net/phy/mdio-mux.c`? Those don't look promising either. So, the real question is, can I somehow trigger `phy_device_create` with a custom `phy_id` from userspace? My intuition points me towards something like netlink. Maybe I can find a hook in `net/core/rtnetlink.c` or perhaps within the `net/ethtool/` directory that would allow me to get this done? Time to start digging! |
| 8/2 | 2026/08/25 19:19 | tool | grepper |
0mArgs:{
"Expression": "phy_device_create"
}Results: {
"Output": "drivers/net/mdio/fwnode_mdio.c=117=int fwnode_mdiobus_register_phy(struct mii_bus *bus,\n--\ndrivers/net/mdio/fwnode_mdio.c-134-\telse\ndrivers/net/mdio/fwnode_mdio.c:135:\t\tphy = phy_device_create(bus, addr, phy_id, 0, NULL);\ndrivers/net/mdio/fwnode_mdio.c-136-\tif (IS_ERR(phy)) {\n--\ndrivers/net/phy/nxp-tja11xx.c=551=static void tja1102_p1_register(struct work_struct *work)\n--\ndrivers/net/phy/nxp-tja11xx.c-585-\t\t/* Real PHY ID of Port 1 is 0 */\ndrivers/net/phy/nxp-tja11xx.c:586:\t\tphy = phy_device_create(bus, addr, PHY_ID_TJA1102, false, NULL);\ndrivers/net/phy/nxp-tja11xx.c-587-\t\tif (IS_ERR(phy)) {\n--\ndrivers/net/phy/nxp-tja11xx.c-592-\ndrivers/net/phy/nxp-tja11xx.c:593:\t\t/* Overwrite parent device. phy_device_create() set parent to\ndrivers/net/phy/nxp-tja11xx.c-594-\t\t * the mii_bus-\u003edev, which is not correct in case.\n--\ndrivers/net/phy/phy_device.c=728=static int phy_request_driver_module(struct phy_device *dev, u32 phy_id)\n--\ndrivers/net/phy/phy_device.c-747-\ndrivers/net/phy/phy_device.c:748:struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id,\ndrivers/net/phy/phy_device.c-749-\t\t\t\t bool is_c45,\n--\ndrivers/net/phy/phy_device.c-839-}\ndrivers/net/phy/phy_device.c:840:EXPORT_SYMBOL(phy_device_create);\ndrivers/net/phy/phy_device.c-841-\n--\ndrivers/net/phy/phy_device.c=1071=struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45)\n--\ndrivers/net/phy/phy_device.c-1096-\t\tif (!r)\ndrivers/net/phy/phy_device.c:1097:\t\t\treturn phy_device_create(bus, addr, phy_id,\ndrivers/net/phy/phy_device.c-1098-\t\t\t\t\t\t true, \u0026c45_ids);\n--\ndrivers/net/phy/phy_device.c-1100-\ndrivers/net/phy/phy_device.c:1101:\treturn phy_device_create(bus, addr, phy_id, is_c45, \u0026c45_ids);\ndrivers/net/phy/phy_device.c-1102-}\n--\ninclude/linux/phy.h=2149=int phy_modify_paged(struct phy_device *phydev, int page, u32 regnum,\n--\ninclude/linux/phy.h-2151-\ninclude/linux/phy.h:2152:struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id,\ninclude/linux/phy.h-2153-\t\t\t\t bool is_c45,\n"
} |
| 9/2 | 2026/08/25 19:19 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 15359 output: 19 thoughts: 294 Thoughts: **My Line of Reasoning** Okay, so I'm thinking about how to trigger `fwnode_mdiobus_register_phy`. That function's called during device tree or ACPI parsing. The obvious question is: can I inject a device tree overlay from userspace? Hmm, probably not directly. Syzkaller doesn't typically handle arbitrary DT overlays unless there's a specific pseudo-syscall for that, and I don't recall one for this purpose. Alright, pivot. What about `get_phy_device`? That gets called by `mdiobus_scan`. And `mdiobus_scan` is, in turn, called by `__mdiobus_register` when the MDIO bus is being scanned. The real question is: is there an MDIO bus that could be *registered* from userspace? Let's rule out the obvious: `drivers/net/dummy.c`—nope, no MDIO bus there. Same with `drivers/net/macsec.c` and `drivers/net/tun.c`—those are dead ends. Now, let's explore `drivers/net/usb/`. Aha, some USB network adapters *do* have MDIO buses! This is promising. The question is if syzkaller is using `vhci` to emulate USB network adapters? If so, then it *could* theoretically emulate the MDIO bus as well! So, I need to check which USB network adapter drivers register an MDIO bus and, critically, allow the bus to be scanned. I'll need to look at specific drivers like `drivers/net/usb/smsc75xx.c` and `drivers/net/usb/lan78xx.c` to see if they fit the bill. |
| 10/2 | 2026/08/25 19:19 | tool | grepper |
0mArgs:{
"Expression": "mdiobus_register"
}Results: {
"Output": "\nFull output is too long, showing 500 out of 1138 lines.\nUse more precise expression if possible.\n\nDocumentation/driver-api/driver-model/devres.rst=348=MDIO\n--\nDocumentation/driver-api/driver-model/devres.rst-350- devm_mdiobus_alloc_size()\nDocumentation/driver-api/driver-model/devres.rst:351: devm_mdiobus_register()\nDocumentation/driver-api/driver-model/devres.rst:352: devm_of_mdiobus_register()\nDocumentation/driver-api/driver-model/devres.rst-353-\n--\nDocumentation/firmware-guide/acpi/dsd/motorcomm-yt8xxx-phy.rst=12=device (the same objects that are registered via\nDocumentation/firmware-guide/acpi/dsd/motorcomm-yt8xxx-phy.rst:13:``fwnode_mdiobus_register_phy()``). MAC-side connection properties such as\nDocumentation/firmware-guide/acpi/dsd/motorcomm-yt8xxx-phy.rst-14-``phy-handle`` and ``phy-mode`` are documented in [acpi-mdio-phy]_.\n--\nDocumentation/firmware-guide/acpi/dsd/phy.rst=7=The PHYs on an MDIO bus [phy] are probed and registered using\nDocumentation/firmware-guide/acpi/dsd/phy.rst:8:fwnode_mdiobus_register_phy().\nDocumentation/firmware-guide/acpi/dsd/phy.rst-9-\n--\nDocumentation/networking/phy.rst=40=registered as a distinct device.\n--\nDocumentation/networking/phy.rst-56- driver needs, setup the mii_bus structure, and register with the PAL using\nDocumentation/networking/phy.rst:57: mdiobus_register. Similarly, there's a remove function to undo all of\nDocumentation/networking/phy.rst-58- that (use mdiobus_unregister).\n--\narch/powerpc/platforms/82xx/ep8248e.c=109=static int ep8248e_mdio_probe(struct platform_device *ofdev)\n--\narch/powerpc/platforms/82xx/ep8248e.c-132-\narch/powerpc/platforms/82xx/ep8248e.c:133:\tret = of_mdiobus_register(bus, ofdev-\u003edev.of_node);\narch/powerpc/platforms/82xx/ep8248e.c-134-\tif (ret)\n--\narch/powerpc/platforms/pasemi/gpio_mdio.c=207=static int gpio_mdio_probe(struct platform_device *ofdev)\n--\narch/powerpc/platforms/pasemi/gpio_mdio.c-243-\narch/powerpc/platforms/pasemi/gpio_mdio.c:244:\terr = of_mdiobus_register(new_bus, np);\narch/powerpc/platforms/pasemi/gpio_mdio.c-245-\n--\ndrivers/net/dsa/bcm_sf2.c=607=static int bcm_sf2_mdio_register(struct dsa_switch *ds)\n--\ndrivers/net/dsa/bcm_sf2.c-683-\ndrivers/net/dsa/bcm_sf2.c:684:\terr = mdiobus_register(priv-\u003euser_mii_bus);\ndrivers/net/dsa/bcm_sf2.c-685-\tif (err)\n--\ndrivers/net/dsa/lantiq/lantiq_gswip_common.c=187=static int gswip_mdio(struct gswip_priv *priv)\n--\ndrivers/net/dsa/lantiq/lantiq_gswip_common.c-213-\ndrivers/net/dsa/lantiq/lantiq_gswip_common.c:214:\terr = devm_of_mdiobus_register(dev, bus, mdio_np);\ndrivers/net/dsa/lantiq/lantiq_gswip_common.c-215-\n--\ndrivers/net/dsa/microchip/ksz9477.c=321=static int ksz9477_pcs_create(struct ksz_device *dev)\n--\ndrivers/net/dsa/microchip/ksz9477.c-341-\ndrivers/net/dsa/microchip/ksz9477.c:342:\tret = devm_mdiobus_register(dev-\u003edev, bus);\ndrivers/net/dsa/microchip/ksz9477.c-343-\tif (ret)\n--\ndrivers/net/dsa/microchip/ksz_common.c=2321=int ksz_mdio_register(struct ksz_device *dev)\n--\ndrivers/net/dsa/microchip/ksz_common.c-2392-\ndrivers/net/dsa/microchip/ksz_common.c:2393:\tret = devm_of_mdiobus_register(ds-\u003edev, bus, mdio_np);\ndrivers/net/dsa/microchip/ksz_common.c-2394-\tif (ret) {\n--\ndrivers/net/dsa/microchip/lan937x_main.c=685=static int lan937x_mdio_register(struct ksz_device *dev)\n--\ndrivers/net/dsa/microchip/lan937x_main.c-755-\ndrivers/net/dsa/microchip/lan937x_main.c:756:\tret = devm_of_mdiobus_register(ds-\u003edev, bus, mdio_np);\ndrivers/net/dsa/microchip/lan937x_main.c-757-\tif (ret)\n--\ndrivers/net/dsa/mt7530.c=2409=mt7530_setup_mdio(struct mt7530_priv *priv)\n--\ndrivers/net/dsa/mt7530.c-2444-\ndrivers/net/dsa/mt7530.c:2445:\tret = devm_of_mdiobus_register(dev, bus, mnp);\ndrivers/net/dsa/mt7530.c-2446-\tif (ret) {\n--\ndrivers/net/dsa/mt7628.c=242=static int mt7628_setup_internal_mdio(struct dsa_switch *ds)\n--\ndrivers/net/dsa/mt7628.c-261-\ndrivers/net/dsa/mt7628.c:262:\treturn devm_mdiobus_register(dev, bus);\ndrivers/net/dsa/mt7628.c-263-}\n--\ndrivers/net/dsa/mv88e6xxx/chip.c=3834=static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip,\n--\ndrivers/net/dsa/mv88e6xxx/chip.c-3887-\ndrivers/net/dsa/mv88e6xxx/chip.c:3888:\terr = of_mdiobus_register(bus, np);\ndrivers/net/dsa/mv88e6xxx/chip.c-3889-\tif (err) {\n--\ndrivers/net/dsa/mxl862xx/mxl862xx.c=267=static int mxl862xx_setup_mdio(struct dsa_switch *ds)\n--\ndrivers/net/dsa/mxl862xx/mxl862xx.c-292-\ndrivers/net/dsa/mxl862xx/mxl862xx.c:293:\tret = devm_of_mdiobus_register(dev, bus, mdio_np);\ndrivers/net/dsa/mxl862xx/mxl862xx.c-294-\tof_node_put(mdio_np);\n--\ndrivers/net/dsa/netc/netc_main.c=149=static int netc_port_create_emdio_bus(struct netc_port *np,\n--\ndrivers/net/dsa/netc/netc_main.c-179-\ndrivers/net/dsa/netc/netc_main.c:180:\terr = devm_of_mdiobus_register(dev, bus, node);\ndrivers/net/dsa/netc/netc_main.c-181-\tif (err)\n--\ndrivers/net/dsa/ocelot/felix_vsc9959.c=957=static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot)\n--\ndrivers/net/dsa/ocelot/felix_vsc9959.c-1013-\t/* Needed in order to initialize the bus mutex lock */\ndrivers/net/dsa/ocelot/felix_vsc9959.c:1014:\trc = mdiobus_register(bus);\ndrivers/net/dsa/ocelot/felix_vsc9959.c-1015-\tif (rc \u003c 0) {\n--\ndrivers/net/dsa/ocelot/seville_vsc9953.c=878=static int vsc9953_mdio_bus_alloc(struct ocelot *ocelot)\n--\ndrivers/net/dsa/ocelot/seville_vsc9953.c-903-\t/* Needed in order to initialize the bus mutex lock */\ndrivers/net/dsa/ocelot/seville_vsc9953.c:904:\trc = devm_of_mdiobus_register(dev, bus, NULL);\ndrivers/net/dsa/ocelot/seville_vsc9953.c-905-\tif (rc \u003c 0) {\n--\ndrivers/net/dsa/qca/ar9331.c=357=static int ar9331_sw_mbus_init(struct ar9331_sw_priv *priv)\n--\ndrivers/net/dsa/qca/ar9331.c-381-\ndrivers/net/dsa/qca/ar9331.c:382:\tret = devm_of_mdiobus_register(dev, mbus, mnp);\ndrivers/net/dsa/qca/ar9331.c-383-\tof_node_put(mnp);\n--\ndrivers/net/dsa/qca/qca8k-8xxx.c=947=qca8k_mdio_register(struct qca8k_priv *priv)\n--\ndrivers/net/dsa/qca/qca8k-8xxx.c-986-\ndrivers/net/dsa/qca/qca8k-8xxx.c:987:\tret = devm_of_mdiobus_register(dev, bus, mdio);\ndrivers/net/dsa/qca/qca8k-8xxx.c-988-\n--\ndrivers/net/dsa/realtek/rtl83xx.c=74=int rtl83xx_setup_user_mdio(struct dsa_switch *ds)\n--\ndrivers/net/dsa/realtek/rtl83xx.c-99-\ndrivers/net/dsa/realtek/rtl83xx.c:100:\tret = devm_of_mdiobus_register(priv-\u003edev, bus, mdio_np);\ndrivers/net/dsa/realtek/rtl83xx.c-101-\tif (ret) {\n--\ndrivers/net/dsa/rzn1_a5psw.c=1117=static int a5psw_probe_mdio(struct a5psw *a5psw, struct device_node *node)\n--\ndrivers/net/dsa/rzn1_a5psw.c-1143-\ndrivers/net/dsa/rzn1_a5psw.c:1144:\treturn devm_of_mdiobus_register(dev, bus, node);\ndrivers/net/dsa/rzn1_a5psw.c-1145-}\n--\ndrivers/net/dsa/sja1105/sja1105.h=307=void sja1105_frame_memory_partitioning(struct sja1105_private *priv);\n--\ndrivers/net/dsa/sja1105/sja1105.h-309-/* From sja1105_mdio.c */\ndrivers/net/dsa/sja1105/sja1105.h:310:int sja1105_mdiobus_register(struct dsa_switch *ds);\ndrivers/net/dsa/sja1105/sja1105.h-311-void sja1105_mdiobus_unregister(struct dsa_switch *ds);\n--\ndrivers/net/dsa/sja1105/sja1105_main.c=3026=static int sja1105_setup(struct dsa_switch *ds)\n--\ndrivers/net/dsa/sja1105/sja1105_main.c-3067-\ndrivers/net/dsa/sja1105/sja1105_main.c:3068:\trc = sja1105_mdiobus_register(ds);\ndrivers/net/dsa/sja1105/sja1105_main.c-3069-\tif (rc \u003c 0) {\n--\ndrivers/net/dsa/sja1105/sja1105_mdio.c=258=static int sja1105_mdiobus_base_tx_register(struct sja1105_private *priv,\n--\ndrivers/net/dsa/sja1105/sja1105_mdio.c-287-\ndrivers/net/dsa/sja1105/sja1105_mdio.c:288:\trc = of_mdiobus_register(bus, np);\ndrivers/net/dsa/sja1105/sja1105_mdio.c-289-\tif (rc) {\n--\ndrivers/net/dsa/sja1105/sja1105_mdio.c=312=static int sja1105_mdiobus_base_t1_register(struct sja1105_private *priv,\n--\ndrivers/net/dsa/sja1105/sja1105_mdio.c-343-\ndrivers/net/dsa/sja1105/sja1105_mdio.c:344:\trc = of_mdiobus_register(bus, np);\ndrivers/net/dsa/sja1105/sja1105_mdio.c-345-\tif (rc) {\n--\ndrivers/net/dsa/sja1105/sja1105_mdio.c=368=static int sja1105_mdiobus_pcs_register(struct sja1105_private *priv)\n--\ndrivers/net/dsa/sja1105/sja1105_mdio.c-395-\ndrivers/net/dsa/sja1105/sja1105_mdio.c:396:\trc = mdiobus_register(bus);\ndrivers/net/dsa/sja1105/sja1105_mdio.c-397-\tif (rc) {\n--\ndrivers/net/dsa/sja1105/sja1105_mdio.c=439=static void sja1105_mdiobus_pcs_unregister(struct sja1105_private *priv)\n--\ndrivers/net/dsa/sja1105/sja1105_mdio.c-458-\ndrivers/net/dsa/sja1105/sja1105_mdio.c:459:int sja1105_mdiobus_register(struct dsa_switch *ds)\ndrivers/net/dsa/sja1105/sja1105_mdio.c-460-{\n--\ndrivers/net/dsa/yt921x.c=595=yt921x_mbus_int_init(struct yt921x_priv *priv, struct device_node *mnp)\n--\ndrivers/net/dsa/yt921x.c-612-\ndrivers/net/dsa/yt921x.c:613:\tres = devm_of_mdiobus_register(dev, mbus, mnp);\ndrivers/net/dsa/yt921x.c-614-\tif (res)\n--\ndrivers/net/dsa/yt921x.c=726=yt921x_mbus_ext_init(struct yt921x_priv *priv, struct device_node *mnp)\n--\ndrivers/net/dsa/yt921x.c-743-\ndrivers/net/dsa/yt921x.c:744:\tres = devm_of_mdiobus_register(dev, mbus, mnp);\ndrivers/net/dsa/yt921x.c-745-\tif (res)\n--\ndrivers/net/ethernet/8390/ax88796.c=459=static int ax_mii_init(struct net_device *dev)\n--\ndrivers/net/ethernet/8390/ax88796.c-478-\ndrivers/net/ethernet/8390/ax88796.c:479:\terr = mdiobus_register(ax-\u003emii_bus);\ndrivers/net/ethernet/8390/ax88796.c-480-\tif (err)\n--\ndrivers/net/ethernet/actions/owl-emac.c=1321=static int owl_emac_mdio_init(struct net_device *netdev)\n--\ndrivers/net/ethernet/actions/owl-emac.c-1345-\ndrivers/net/ethernet/actions/owl-emac.c:1346:\tret = devm_of_mdiobus_register(dev, priv-\u003emii, mdio_node);\ndrivers/net/ethernet/actions/owl-emac.c-1347-\n--\ndrivers/net/ethernet/adi/adin1110.c=509=static int adin1110_register_mdiobus(struct adin1110_priv *priv,\n--\ndrivers/net/ethernet/adi/adin1110.c-529-\ndrivers/net/ethernet/adi/adin1110.c:530:\tret = devm_mdiobus_register(dev, mii_bus);\ndrivers/net/ethernet/adi/adin1110.c-531-\tif (ret)\n--\ndrivers/net/ethernet/adi/adin1140.c=617=static int adin1140_mdio_register(struct adin1140_priv *priv,\n--\ndrivers/net/ethernet/adi/adin1140.c-636-\ndrivers/net/ethernet/adi/adin1140.c:637:\treturn devm_mdiobus_register(\u0026spidev-\u003edev, priv-\u003emdiobus);\ndrivers/net/ethernet/adi/adin1140.c-638-}\n--\ndrivers/net/ethernet/aeroflex/greth.c=1290=static int greth_mdio_init(struct greth_private *greth)\n--\ndrivers/net/ethernet/aeroflex/greth.c-1306-\ndrivers/net/ethernet/aeroflex/greth.c:1307:\tret = mdiobus_register(greth-\u003emdio);\ndrivers/net/ethernet/aeroflex/greth.c-1308-\tif (ret) {\n--\ndrivers/net/ethernet/agere/et131x.c=3931=static int et131x_pci_setup(struct pci_dev *pdev,\n--\ndrivers/net/ethernet/agere/et131x.c-4026-\ndrivers/net/ethernet/agere/et131x.c:4027:\trc = mdiobus_register(adapter-\u003emii_bus);\ndrivers/net/ethernet/agere/et131x.c-4028-\tif (rc \u003c 0) {\n--\ndrivers/net/ethernet/altera/altera_tse_main.c=121=static int altera_tse_mdio_create(struct net_device *dev, unsigned int id)\n--\ndrivers/net/ethernet/altera/altera_tse_main.c-157-\ndrivers/net/ethernet/altera/altera_tse_main.c:158:\tret = of_mdiobus_register(mdio, mdio_node);\ndrivers/net/ethernet/altera/altera_tse_main.c-159-\tif (ret != 0) {\n--\ndrivers/net/ethernet/amd/au1000_eth.c=1061=static int au1000_probe(struct platform_device *pdev)\n--\ndrivers/net/ethernet/amd/au1000_eth.c-1221-\ndrivers/net/ethernet/amd/au1000_eth.c:1222:\terr = mdiobus_register(aup-\u003emii_bus);\ndrivers/net/ethernet/amd/au1000_eth.c-1223-\tif (err) {\n--\ndrivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c=3428=static int xgbe_phy_init(struct xgbe_prv_data *pdata)\n--\ndrivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c-3717-\tsnprintf(mii-\u003eid, sizeof(mii-\u003eid), \"%s\", dev_name(pdata-\u003edev));\ndrivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c:3718:\tret = mdiobus_register(mii);\ndrivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c-3719-\tif (ret) {\ndrivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c:3720:\t\tdev_err(pdata-\u003edev, \"mdiobus_register failed\\n\");\ndrivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c-3721-\t\treturn ret;\n--\ndrivers/net/ethernet/apm/xgene-v2/mdio.c=98=int xge_mdio_config(struct net_device *ndev)\n--\ndrivers/net/ethernet/apm/xgene-v2/mdio.c-118-\tmdio_bus-\u003ephy_mask = 0x1;\ndrivers/net/ethernet/apm/xgene-v2/mdio.c:119:\tret = mdiobus_register(mdio_bus);\ndrivers/net/ethernet/apm/xgene-v2/mdio.c-120-\tif (ret)\n--\ndrivers/net/ethernet/apm/xgene/xgene_enet_hw.c=837=int xgene_enet_phy_connect(struct net_device *ndev)\n--\ndrivers/net/ethernet/apm/xgene/xgene_enet_hw.c-887-\ndrivers/net/ethernet/apm/xgene/xgene_enet_hw.c:888:static int xgene_mdiobus_register(struct xgene_enet_pdata *pdata,\ndrivers/net/ethernet/apm/xgene/xgene_enet_hw.c-889-\t\t\t\t struct mii_bus *mdio)\n--\ndrivers/net/ethernet/apm/xgene/xgene_enet_hw.c-912-\ndrivers/net/ethernet/apm/xgene/xgene_enet_hw.c:913:\t\tret = of_mdiobus_register(mdio, mdio_np);\ndrivers/net/ethernet/apm/xgene/xgene_enet_hw.c-914-\t\tof_node_put(mdio_np);\n--\ndrivers/net/ethernet/apm/xgene/xgene_enet_hw.c-921-\t/* Register the MDIO bus */\ndrivers/net/ethernet/apm/xgene/xgene_enet_hw.c:922:\tret = mdiobus_register(mdio);\ndrivers/net/ethernet/apm/xgene/xgene_enet_hw.c-923-\tif (ret)\n--\ndrivers/net/ethernet/apm/xgene/xgene_enet_hw.c=939=int xgene_enet_mdio_config(struct xgene_enet_pdata *pdata)\n--\ndrivers/net/ethernet/apm/xgene/xgene_enet_hw.c-957-\ndrivers/net/ethernet/apm/xgene/xgene_enet_hw.c:958:\tret = xgene_mdiobus_register(pdata, mdio_bus);\ndrivers/net/ethernet/apm/xgene/xgene_enet_hw.c-959-\tif (ret) {\n--\ndrivers/net/ethernet/arc/emac_mdio.c=131=int arc_mdio_probe(struct arc_emac_priv *priv)\n--\ndrivers/net/ethernet/arc/emac_mdio.c-172-\ndrivers/net/ethernet/arc/emac_mdio.c:173:\terror = of_mdiobus_register(bus, mdio_node);\ndrivers/net/ethernet/arc/emac_mdio.c-174-\tof_node_put(mdio_node);\n--\ndrivers/net/ethernet/asix/ax88796c_main.c=966=static int ax88796c_probe(struct spi_device *spi)\n--\ndrivers/net/ethernet/asix/ax88796c_main.c-1010-\ndrivers/net/ethernet/asix/ax88796c_main.c:1011:\tret = devm_mdiobus_register(\u0026spi-\u003edev, ax_local-\u003emdiobus);\ndrivers/net/ethernet/asix/ax88796c_main.c-1012-\tif (ret \u003c 0) {\n--\ndrivers/net/ethernet/atheros/ag71xx.c=687=static int ag71xx_mdio_probe(struct ag71xx *ag)\n--\ndrivers/net/ethernet/atheros/ag71xx.c-727-\tmnp = of_get_child_by_name(np, \"mdio\");\ndrivers/net/ethernet/atheros/ag71xx.c:728:\terr = devm_of_mdiobus_register(dev, mii_bus, mnp);\ndrivers/net/ethernet/atheros/ag71xx.c-729-\tof_node_put(mnp);\n--\ndrivers/net/ethernet/broadcom/b44.c=2234=static int b44_register_phy_one(struct b44 *bp)\n--\ndrivers/net/ethernet/broadcom/b44.c-2259-\ndrivers/net/ethernet/broadcom/b44.c:2260:\terr = mdiobus_register(mii_bus);\ndrivers/net/ethernet/broadcom/b44.c-2261-\tif (err) {\n--\ndrivers/net/ethernet/broadcom/bcm63xx_enet.c=1713=static int bcm_enet_probe(struct platform_device *pdev)\n--\ndrivers/net/ethernet/broadcom/bcm63xx_enet.c-1829-\ndrivers/net/ethernet/broadcom/bcm63xx_enet.c:1830:\t\tret = mdiobus_register(bus);\ndrivers/net/ethernet/broadcom/bcm63xx_enet.c-1831-\t\tif (ret) {\n--\ndrivers/net/ethernet/broadcom/bgmac-bcma-mdio.c=211=struct mii_bus *bcma_mdio_mii_register(struct bgmac *bgmac)\n--\ndrivers/net/ethernet/broadcom/bgmac-bcma-mdio.c-235-\ndrivers/net/ethernet/broadcom/bgmac-bcma-mdio.c:236:\terr = of_mdiobus_register(mii_bus, np);\ndrivers/net/ethernet/broadcom/bgmac-bcma-mdio.c-237-\tof_node_put(np);\n--\ndrivers/net/ethernet/broadcom/genet/bcmmii.c=193=int bcmgenet_mii_config(struct net_device *dev, bool init)\n--\ndrivers/net/ethernet/broadcom/genet/bcmmii.c-233-\t\tphy_name = \"external RvMII\";\ndrivers/net/ethernet/broadcom/genet/bcmmii.c:234:\t\t/* of_mdiobus_register took care of reading the 'max-speed'\ndrivers/net/ethernet/broadcom/genet/bcmmii.c-235-\t\t * PHY property for us, effectively limiting the PHY supported\n--\ndrivers/net/ethernet/broadcom/sb1250-mac.c=2157=static int sbmac_init(struct platform_device *pldev, long long base)\n--\ndrivers/net/ethernet/broadcom/sb1250-mac.c-2227-\t */\ndrivers/net/ethernet/broadcom/sb1250-mac.c:2228:\terr = mdiobus_register(sc-\u003emii_bus);\ndrivers/net/ethernet/broadcom/sb1250-mac.c-2229-\tif (err) {\n--\ndrivers/net/ethernet/broadcom/tg3.c=1505=static int tg3_mdio_init(struct tg3 *tp)\n--\ndrivers/net/ethernet/broadcom/tg3.c-1557-\ndrivers/net/ethernet/broadcom/tg3.c:1558:\ti = mdiobus_register(tp-\u003emdio_bus);\ndrivers/net/ethernet/broadcom/tg3.c-1559-\tif (i) {\n--\ndrivers/net/ethernet/cadence/macb_main.c=1023=static int macb_mii_probe(struct net_device *netdev)\n--\ndrivers/net/ethernet/cadence/macb_main.c-1096-\ndrivers/net/ethernet/cadence/macb_main.c:1097:static int macb_mdiobus_register(struct macb *bp, struct device_node *mdio_np)\ndrivers/net/ethernet/cadence/macb_main.c-1098-{\n--\ndrivers/net/ethernet/cadence/macb_main.c-1104-\tif (mdio_np)\ndrivers/net/ethernet/cadence/macb_main.c:1105:\t\treturn of_mdiobus_register(bp-\u003emii_bus, mdio_np);\ndrivers/net/ethernet/cadence/macb_main.c-1106-\n--\ndrivers/net/ethernet/cadence/macb_main.c-1118-\ndrivers/net/ethernet/cadence/macb_main.c:1119:\t\t\treturn of_mdiobus_register(bp-\u003emii_bus, np);\ndrivers/net/ethernet/cadence/macb_main.c-1120-\t\t}\ndrivers/net/ethernet/cadence/macb_main.c-1121-\ndrivers/net/ethernet/cadence/macb_main.c:1122:\treturn mdiobus_register(bp-\u003emii_bus);\ndrivers/net/ethernet/cadence/macb_main.c-1123-}\n--\ndrivers/net/ethernet/cadence/macb_main.c=1125=static int macb_mii_init(struct macb *bp)\n--\ndrivers/net/ethernet/cadence/macb_main.c-1158-\ndrivers/net/ethernet/cadence/macb_main.c:1159:\terr = macb_mdiobus_register(bp, mdio_np);\ndrivers/net/ethernet/cadence/macb_main.c-1160-\tif (err)\n--\ndrivers/net/ethernet/davicom/dm9051.c=1107=static int dm9051_mdio_register(struct board_info *db)\n--\ndrivers/net/ethernet/davicom/dm9051.c-1124-\ndrivers/net/ethernet/davicom/dm9051.c:1125:\tret = devm_mdiobus_register(\u0026spi-\u003edev, db-\u003emdiobus);\ndrivers/net/ethernet/davicom/dm9051.c-1126-\tif (ret)\n--\ndrivers/net/ethernet/engleder/tsnep_main.c=2423=static int tsnep_mdio_init(struct tsnep_adapter *adapter)\n--\ndrivers/net/ethernet/engleder/tsnep_main.c-2454-\ndrivers/net/ethernet/engleder/tsnep_main.c:2455:\tretval = of_mdiobus_register(adapter-\u003emdiobus, np);\ndrivers/net/ethernet/engleder/tsnep_main.c-2456-\n--\ndrivers/net/ethernet/ethoc.c=1024=static int ethoc_probe(struct platform_device *pdev)\n--\ndrivers/net/ethernet/ethoc.c-1207-\ndrivers/net/ethernet/ethoc.c:1208:\tret = mdiobus_register(priv-\u003emdio);\ndrivers/net/ethernet/ethoc.c-1209-\tif (ret) {\n--\ndrivers/net/ethernet/faraday/ftgmac100.c=1734=static int ftgmac100_setup_mdio(struct net_device *netdev)\n--\ndrivers/net/ethernet/faraday/ftgmac100.c-1769-\ndrivers/net/ethernet/faraday/ftgmac100.c:1770:\terr = devm_of_mdiobus_register(priv-\u003edev, priv-\u003emii_bus, mdio_np);\ndrivers/net/ethernet/faraday/ftgmac100.c-1771-\tof_node_put(mdio_np);\n--\ndrivers/net/ethernet/freescale/enetc/enetc_pci_mdio.c=36=static int enetc_pci_mdio_probe(struct pci_dev *pdev,\n--\ndrivers/net/ethernet/freescale/enetc/enetc_pci_mdio.c-90-\ndrivers/net/ethernet/freescale/enetc/enetc_pci_mdio.c:91:\terr = of_mdiobus_register(bus, dev-\u003eof_node);\ndrivers/net/ethernet/freescale/enetc/enetc_pci_mdio.c-92-\tif (err)\n--\ndrivers/net/ethernet/freescale/enetc/enetc_pf_common.c=266=static int enetc_mdio_probe(struct enetc_pf *pf, struct device_node *np)\n--\ndrivers/net/ethernet/freescale/enetc/enetc_pf_common.c-292-\ndrivers/net/ethernet/freescale/enetc/enetc_pf_common.c:293:\terr = of_mdiobus_register(bus, np);\ndrivers/net/ethernet/freescale/enetc/enetc_pf_common.c-294-\tif (err)\n--\ndrivers/net/ethernet/freescale/enetc/enetc_pf_common.c=308=static int enetc_imdio_create(struct enetc_pf *pf)\n--\ndrivers/net/ethernet/freescale/enetc/enetc_pf_common.c-342-\ndrivers/net/ethernet/freescale/enetc/enetc_pf_common.c:343:\terr = mdiobus_register(bus);\ndrivers/net/ethernet/freescale/enetc/enetc_pf_common.c-344-\tif (err) {\n--\ndrivers/net/ethernet/freescale/fec_main.c=3042=static int fec_enet_mii_init(struct platform_device *pdev)\n--\ndrivers/net/ethernet/freescale/fec_main.c-3160-\ndrivers/net/ethernet/freescale/fec_main.c:3161:\terr = of_mdiobus_register(fep-\u003emii_bus, node);\ndrivers/net/ethernet/freescale/fec_main.c-3162-\tif (err)\n--\ndrivers/net/ethernet/freescale/fec_mpc52xx_phy.c=65=static int mpc52xx_fec_mdio_probe(struct platform_device *of)\n--\ndrivers/net/ethernet/freescale/fec_mpc52xx_phy.c-105-\ndrivers/net/ethernet/freescale/fec_mpc52xx_phy.c:106:\terr = of_mdiobus_register(bus, np);\ndrivers/net/ethernet/freescale/fec_mpc52xx_phy.c-107-\tif (err)\n--\ndrivers/net/ethernet/freescale/fs_enet/mii-bitbang.c=149=static int fs_enet_mdio_probe(struct platform_device *ofdev)\n--\ndrivers/net/ethernet/freescale/fs_enet/mii-bitbang.c-175-\ndrivers/net/ethernet/freescale/fs_enet/mii-bitbang.c:176:\tret = of_mdiobus_register(new_bus, ofdev-\u003edev.of_node);\ndrivers/net/ethernet/freescale/fs_enet/mii-bitbang.c-177-\tif (ret)\n--\ndrivers/net/ethernet/freescale/fs_enet/mii-fec.c=97=static int fs_enet_mdio_probe(struct platform_device *ofdev)\n--\ndrivers/net/ethernet/freescale/fs_enet/mii-fec.c-166-\ndrivers/net/ethernet/freescale/fs_enet/mii-fec.c:167:\tret = of_mdiobus_register(new_bus, ofdev-\u003edev.of_node);\ndrivers/net/ethernet/freescale/fs_enet/mii-fec.c-168-\tif (ret)\n--\ndrivers/net/ethernet/freescale/fsl_pq_mdio.c=409=static int fsl_pq_mdio_probe(struct platform_device *pdev)\n--\ndrivers/net/ethernet/freescale/fsl_pq_mdio.c-493-\ndrivers/net/ethernet/freescale/fsl_pq_mdio.c:494:\terr = of_mdiobus_register(new_bus, np);\ndrivers/net/ethernet/freescale/fsl_pq_mdio.c-495-\tif (err) {\n--\ndrivers/net/ethernet/freescale/xgmac_mdio.c=371=static int xgmac_mdio_probe(struct platform_device *pdev)\n--\ndrivers/net/ethernet/freescale/xgmac_mdio.c-426-\tif (is_of_node(fwnode))\ndrivers/net/ethernet/freescale/xgmac_mdio.c:427:\t\tret = of_mdiobus_register(bus, to_of_node(fwnode));\ndrivers/net/ethernet/freescale/xgmac_mdio.c-428-\telse if (is_acpi_node(fwnode))\ndrivers/net/ethernet/freescale/xgmac_mdio.c:429:\t\tret = acpi_mdiobus_register(bus, fwnode);\ndrivers/net/ethernet/freescale/xgmac_mdio.c-430-\telse\n--\ndrivers/net/ethernet/hisilicon/hibmcge/hbg_mdio.c=266=int hbg_mdio_init(struct hbg_priv *priv)\n--\ndrivers/net/ethernet/hisilicon/hibmcge/hbg_mdio.c-291-\ndrivers/net/ethernet/hisilicon/hibmcge/hbg_mdio.c:292:\tret = devm_mdiobus_register(dev, mdio_bus);\ndrivers/net/ethernet/hisilicon/hibmcge/hbg_mdio.c-293-\tif (ret)\n--\ndrivers/net/ethernet/hisilicon/hix5hd2_gmac.c=1094=static int hix5hd2_dev_probe(struct platform_device *pdev)\n--\ndrivers/net/ethernet/hisilicon/hix5hd2_gmac.c-1185-\ndrivers/net/ethernet/hisilicon/hix5hd2_gmac.c:1186:\tret = of_mdiobus_register(bus, node);\ndrivers/net/ethernet/hisilicon/hix5hd2_gmac.c-1187-\tif (ret)\n--\ndrivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c=123=int hclge_mac_mdio_config(struct hclge_dev *hdev)\n--\ndrivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c-154-\tmdio_bus-\u003ephy_mask = ~(1U \u003c\u003c mac-\u003ephy_addr);\ndrivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c:155:\tret = mdiobus_register(mdio_bus);\ndrivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c-156-\tif (ret) {\n--\ndrivers/net/ethernet/hisilicon/hns_mdio.c=495=static int hns_mdio_probe(struct platform_device *pdev)\n--\ndrivers/net/ethernet/hisilicon/hns_mdio.c-583-\ndrivers/net/ethernet/hisilicon/hns_mdio.c:584:\t\tret = of_mdiobus_register(new_bus, pdev-\u003edev.of_node);\ndrivers/net/ethernet/hisilicon/hns_mdio.c-585-\t} else if (is_acpi_node(pdev-\u003edev.fwnode)) {\n--\ndrivers/net/ethernet/hisilicon/hns_mdio.c-592-\t\t/* Register the MDIO bus */\ndrivers/net/ethernet/hisilicon/hns_mdio.c:593:\t\tret = mdiobus_register(new_bus);\ndrivers/net/ethernet/hisilicon/hns_mdio.c-594-\t} else {\n--\ndrivers/net/ethernet/ibm/emac/core.c=2560=static int emac_dt_mdio_probe(struct emac_instance *dev)\n--\ndrivers/net/ethernet/ibm/emac/core.c-2584-\tsnprintf(bus-\u003eid, MII_BUS_ID_SIZE, \"%s\", dev-\u003eofdev-\u003ename);\ndrivers/net/ethernet/ibm/emac/core.c:2585:\tres = devm_of_mdiobus_register(\u0026dev-\u003eofdev-\u003edev, bus, mii_np);\ndrivers/net/ethernet/ibm/emac/core.c-2586-\tif (res) {\n--\ndrivers/net/ethernet/intel/ixgbe/ixgbe_phy.c=1026=int ixgbe_mii_bus_init(struct ixgbe_hw *hw)\n--\ndrivers/net/ethernet/intel/ixgbe/ixgbe_phy.c-1088-\tadapter-\u003emii_bus = bus;\ndrivers/net/ethernet/intel/ixgbe/ixgbe_phy.c:1089:\treturn mdiobus_register(bus);\ndrivers/net/ethernet/intel/ixgbe/ixgbe_phy.c-1090-}\n--\ndrivers/net/ethernet/lantiq_etop.c=385=ltq_etop_mdio_init(struct net_device *dev)\n--\ndrivers/net/ethernet/lantiq_etop.c-402-\t\t priv-\u003epdev-\u003ename, priv-\u003epdev-\u003eid);\ndrivers/net/ethernet/lantiq_etop.c:403:\tif (mdiobus_register(priv-\u003emii_bus)) {\ndrivers/net/ethernet/lantiq_etop.c-404-\t\terr = -ENXIO;\n--\ndrivers/net/ethernet/marvell/mvmdio.c=283=static int orion_mdio_probe(struct platform_device *pdev)\n--\ndrivers/net/ethernet/marvell/mvmdio.c-381-\t/* For the platforms not supporting DT/ACPI fall-back\ndrivers/net/ethernet/marvell/mvmdio.c:382:\t * to mdiobus_register via of_mdiobus_register.\ndrivers/net/ethernet/marvell/mvmdio.c-383-\t */\ndrivers/net/ethernet/marvell/mvmdio.c-384-\tif (is_acpi_node(pdev-\u003edev.fwnode))\ndrivers/net/ethernet/marvell/mvmdio.c:385:\t\tret = acpi_mdiobus_register(bus, pdev-\u003edev.fwnode);\ndrivers/net/ethernet/marvell/mvmdio.c-386-\telse\ndrivers/net/ethernet/marvell/mvmdio.c:387:\t\tret = of_mdiobus_register(bus, pdev-\u003edev.of_node);\ndrivers/net/ethernet/marvell/mvmdio.c-388-\tif (ret \u003c 0) {\n--\ndrivers/net/ethernet/marvell/pxa168_eth.c=1387=static int pxa168_eth_probe(struct platform_device *pdev)\n--\ndrivers/net/ethernet/marvell/pxa168_eth.c-1504-\tpep-\u003esmi_bus-\u003ephy_mask = 0xffffffff;\ndrivers/net/ethernet/marvell/pxa168_eth.c:1505:\terr = mdiobus_register(pep-\u003esmi_bus);\ndrivers/net/ethernet/marvell/pxa168_eth.c-1506-\tif (err)\n--\ndrivers/net/ethernet/mediatek/mtk_eth_soc.c=998=static int mtk_mdio_init(struct mtk_eth *eth)\n--\ndrivers/net/ethernet/mediatek/mtk_eth_soc.c-1037-\tdev_dbg(eth-\u003edev, \"MDC is running on %d Hz\\n\", MDC_MAX_FREQ / eth-\u003emdc_divider);\ndrivers/net/ethernet/mediatek/mtk_eth_soc.c:1038:\tret = of_mdiobus_register(eth-\u003emii_bus, mii_np);\ndrivers/net/ethernet/mediatek/mtk_eth_soc.c-1039-\n--\ndrivers/net/ethernet/mediatek/mtk_star_emac.c=1422=static int mtk_star_mdio_init(struct net_device *ndev)\n--\ndrivers/net/ethernet/mediatek/mtk_star_emac.c-1447-\ndrivers/net/ethernet/mediatek/mtk_star_emac.c:1448:\tret = devm_of_mdiobus_register(dev, priv-\u003emii, mdio_node);\ndrivers/net/ethernet/mediatek/mtk_star_emac.c-1449-\n--\ndrivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_mdio.c=287=int mlxbf_gige_mdio_probe(struct platform_device *pdev, struct mlxbf_gige *priv)\n--\ndrivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_mdio.c-332-\ndrivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_mdio.c:333:\tret = mdiobus_register(priv-\u003emdiobus);\ndrivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_mdio.c-334-\tif (ret)\n--\ndrivers/net/ethernet/meta/fbnic/fbnic_mdio.c=253=int fbnic_mdiobus_create(struct fbnic_dev *fbd)\n--\ndrivers/net/ethernet/meta/fbnic/fbnic_mdio.c-272-\n\n"
} |
| 11/2 | 2026/08/25 19:19 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 28919 output: 31 thoughts: 56 Thoughts: **USB Ethernet Driver Investigation** Okay, so I'm thinking about these USB Ethernet drivers, particularly `smsc75xx.c` and `lan78xx.c`. And a quick glance at their code suggests they're using `mdiobus_register`. Right, that's interesting. It means they're interacting with an MDIO bus. Let's see how prevalent that is within USB networking drivers. Let me do a quick grep for `mdiobus_register` within the `drivers/net/usb/` directory. That should give me a good overview of which other drivers are using this method and potentially provide some insight into the overall architecture. I expect there'll be a few more, it's not unusual to use that in this context. |
| 12/2 | 2026/08/25 19:19 | tool | grepper |
0mArgs:{
"Expression": "mdiobus_register",
"PathPrefix": "drivers/net/usb/"
}Results: {
"Output": "drivers/net/usb/asix_devices.c=669=static int ax88772_init_mdio(struct usbnet *dev)\n--\ndrivers/net/usb/asix_devices.c-686-\ndrivers/net/usb/asix_devices.c:687:\tret = mdiobus_register(priv-\u003emdio);\ndrivers/net/usb/asix_devices.c-688-\tif (ret) {\n--\ndrivers/net/usb/ax88172a.c=61=static int ax88172a_init_mdio(struct usbnet *dev)\n--\ndrivers/net/usb/ax88172a.c-79-\ndrivers/net/usb/ax88172a.c:80:\tret = mdiobus_register(priv-\u003emdio);\ndrivers/net/usb/ax88172a.c-81-\tif (ret) {\n--\ndrivers/net/usb/lan78xx.c=2091=static int lan78xx_mdio_init(struct lan78xx_net *dev)\n--\ndrivers/net/usb/lan78xx.c-2121-\tnode = of_get_child_by_name(dev-\u003eudev-\u003edev.of_node, \"mdio\");\ndrivers/net/usb/lan78xx.c:2122:\tret = of_mdiobus_register(dev-\u003emdiobus, node);\ndrivers/net/usb/lan78xx.c-2123-\tof_node_put(node);\n--\ndrivers/net/usb/smsc95xx.c=1147=static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)\n--\ndrivers/net/usb/smsc95xx.c-1241-\ndrivers/net/usb/smsc95xx.c:1242:\tret = mdiobus_register(pdata-\u003emdiobus);\ndrivers/net/usb/smsc95xx.c-1243-\tif (ret) {\n"
} |
| 13/2 | 2026/08/25 19:19 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 29708 output: 17 thoughts: 1644 Thoughts: **Reaching the ECONET FEPHY Driver via Syzkaller**
Okay, so I've been digging into how we can get at this `econet-fe-soc` driver through syzkaller. The game plan is clear: these network device drivers, like `smsc95xx`, `lan78xx`, `asix_devices`, and `ax88172a`, they register an MDIO bus. That MDIO bus, when registered, does a scan for PHYs. This scan is the key. The scanning process relies on reading PHY registers using the MDIO bus read function. Since syzkaller can emulate USB devices through vhci, it can control the data returned by those reads.
Now, if syzkaller feeds a specific PHY ID during the MDIO scan – `0x03a29414`, which is `ECONET_FEPHY_ID_EN7526C` – the MDIO bus will create a PHY device with that ID. The kernel will then try to bind a driver for that particular PHY ID. If `CONFIG_ECONET_FE_SOC_PHY` is enabled, the `econet-fe-soc` driver will match and its probe function will execute. This kicks off the whole network driver chain, attaching the PHY and starting the PHY state machine, calling stuff like `config_init` and `read_status`. So, yes, the `econet-fe-soc` driver is *absolutely* reachable from syzkaller via USB vhci.
Alright, let's verify everything. The driver in question is `drivers/net/phy/mediatek/econet-fe-soc.c`. The PHY ID is indeed `0x03a29414`. Looking at the driver, it defines a PHY driver table:
```c
static struct phy_driver en751221_fephy_driver[] = {
{
PHY_ID_MATCH_EXACT(ECONET_FEPHY_ID_EN7526C),
.name = "EcoNet EN751221 FEPHY",
.config_init = en751221_fephy_config_init,
.config_intr = genphy_no_config_intr,
.handle_interrupt = genphy_handle_interrupt_no_ack,
.probe = en751221_fephy_probe,
.read_page = mtk_phy_read_page,
.write_page = mtk_phy_write_page,
}
};
```
So it looks good. Now, this `en751221_fephy_probe` is interesting. Looking at that function:
```c
static int en751221_fephy_probe(struct phy_device *phydev)
{
int port0 = ECONET_EN7526C_PORT0_ADDR;
struct econet_socphy_shared *shared;
struct mtk_socphy_priv *priv;
int ret;
if (phydev->phy_id != ECONET_FEPHY_ID_EN7526C)
port0 = ECONET_LEGACY_PORT0_ADDR;
ret = devm_phy_package_join(&phydev->mdio.dev, phydev, port0,
sizeof(struct econet_socphy_shared));
if (ret)
return ret;
shared = phy_package_get_priv(phydev);
priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
if (phydev->mdio.addr == port0)
shared->phydev_p0 = phydev;
phydev->priv = priv;
return 0;
}
```
And then `en751221_fephy_config_init` is:
```c
static int en751221_fephy_config_init(struct phy_device *phydev)
{
struct econet_socphy_shared *shared = phy_package_get_priv(phydev);
u16 l0r26_temp;
int ret;
int i;
if (!shared->phydev_p0) {
phydev_err(phydev, "Port zero must be configured\n");
return -E
|
| 14/2 | 2026/08/25 19:19 | tool | grepper |
0mArgs:{
"Expression": "config ECONET"
}Results: {
"Output": "arch/mips/Kconfig=358=config MACH_DECSTATION\n--\narch/mips/Kconfig-394-\narch/mips/Kconfig:395:config ECONET\narch/mips/Kconfig-396-\tbool \"EcoNet MIPS family\"\n--\ndrivers/clocksource/Kconfig=71=config DW_APB_TIMER_OF\n--\ndrivers/clocksource/Kconfig-75-\ndrivers/clocksource/Kconfig:76:config ECONET_EN751221_TIMER\ndrivers/clocksource/Kconfig-77-\tbool \"EcoNet EN751221 High Precision Timer\" if COMPILE_TEST\n--\ndrivers/irqchip/Kconfig=195=config DW_APB_ICTL\n--\ndrivers/irqchip/Kconfig-199-\ndrivers/irqchip/Kconfig:200:config ECONET_EN751221_INTC\ndrivers/irqchip/Kconfig-201-\tbool\n--\ndrivers/net/phy/mediatek/Kconfig-2-\ndrivers/net/phy/mediatek/Kconfig:3:config ECONET_FE_SOC_PHY\ndrivers/net/phy/mediatek/Kconfig-4-\ttristate \"EcoNet 10/100 SoC Ethernet PHYs\"\n"
} |
| 15/2 | 2026/08/25 19:19 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 32730 output: 38 thoughts: 828 Thoughts: **My Analysis of the EcoNet Driver for Syzkaller Testing** Okay, so I'm looking at this `ECONET` thing, which appears to be an architecture-specific component (`arch/mips/Kconfig`). The driver itself seems to hinge on either `ECONET` being enabled, or, crucially for my purposes, `COMPILE_TEST`. Excellent, syzkaller leverages `COMPILE_TEST`, so I can directly enable `ECONET_FE_SOC_PHY`. I briefly wondered about a dependency on `MTK_NET_PHYLIB`, but it *selects* it, so `ECONET_FE_SOC_PHY` is the key. No other configs appear necessary, just that one. Now I need to drill down into the driver's implementation, specifically the functions in `drivers/net/phy/mediatek/econet-fe-soc.c`. The driver defines `en751221_fephy_driver`, and its critical entry points are `en751221_fephy_config_init` and `en751221_fephy_probe`. I'll want to keep an eye on those. Let's add them to my "FocusSymbols" list. Hmm, I see that the patch touches `MAINTAINERS` and the `Makefile` as well, and adds `econet-fe-soc.c` – standard stuff. The crucial question: can I realistically reach this code within a QEMU environment, and specifically through syzkaller? The answer, thankfully, is yes! Syzkaller uses `vhci` to emulate USB devices. I'm thinking of a USB network adapter like `smsc95xx`. The `smsc95xx` driver registers an MDIO bus during its bind process. This bus then scans for PHYs by reading MDIO registers, via USB control messages. Here's the real power: Syzkaller intercepts these messages from the emulated USB device. So, if syzkaller is configured to return the PHY ID `0x03a29414` (at, say, address 9) when queried by the MDIO bus, the PHY driver core will create a PHY device, and the PHY driver core will match this ID with `en751221_fephy_driver`. It calls `en751221_fephy_probe`, which is a good starting point. Later, when the network interface is activated, `phy_start` will trigger `en751221_fephy_config_init`. This is where the magic happens – a series of MDIO reads and writes (via `phy_read_paged`, `phy_write_paged`, etc.) – all of which translate to USB control messages that syzkaller can intercept and manipulate! Therefore, the entire calibration logic in `en751221_fephy_config_init` is fully within my control. I can fuzz it extensively with syzkaller. Just need to confirm that `smsc95xx` doesn't filter out PHYs – all looks good! |
| 16/2 | 2026/08/25 19:20 | tool | grepper |
0mError:no matches Args: {
"Expression": "phy_device_create",
"PathPrefix": "drivers/net/usb/smsc95xx.c"
}Results: {
"error": "no matches"
} |
| 17/2 | 2026/08/25 19:20 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 34285 output: 37 thoughts: 49 Thoughts: **Analyzing the MDIO Bus Registration** Okay, so the `smsc95xx` driver kicks things off by registering an MDIO bus using `mdiobus_register`. Right, that's the standard way to do it. Now, let's dive deeper. I need to understand how this registration process works in detail. Time to crack open `drivers/net/phy/mdio_bus.c` and take a look at the `mdiobus_register` function itself. I want to see what's happening under the hood – what structures are being initialized, which callbacks are being set up, and how this bus is made available to other PHY drivers. Let's get to the nitty-gritty. |
| 18/2 | 2026/08/25 19:20 | tool | grepper |
0mError:no matches Args: {
"Expression": "mdiobus_scan",
"PathPrefix": "drivers/net/phy/mdio_bus.c"
}Results: {
"error": "no matches"
} |
| 19/2 | 2026/08/25 19:20 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 34539 output: 19 thoughts: 26 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **MDIO Bus Scan Location Check** Okay, so I'm digging into this MDIO bus initialization, and my immediate thought is a sanity check. Let me just confirm... `mdiobus_scan`... that's *right* where I'd expect it to be, nestled in the `drivers/net/phy/mdio_bus.c` file. No surprises there. Good. Makes sense. Just confirming the code base is structured as I would anticipate. |
| 20/2 | 2026/08/25 19:20 | tool | grepper |
0mArgs:{
"Expression": "mdiobus_scan"
}Results: {
"Output": "drivers/net/ethernet/marvell/pxa168_eth.c=958=static int pxa168_init_phy(struct net_device *dev)\n--\ndrivers/net/ethernet/marvell/pxa168_eth.c-967-\ndrivers/net/ethernet/marvell/pxa168_eth.c:968:\tphy = mdiobus_scan_c22(pep-\u003esmi_bus, pep-\u003ephy_addr);\ndrivers/net/ethernet/marvell/pxa168_eth.c-969-\tif (IS_ERR(phy))\n--\ndrivers/net/mdio/mdio-bcm-unimac.c=130=static int unimac_mdio_write(struct mii_bus *bus, int phy_id,\n--\ndrivers/net/mdio/mdio-bcm-unimac.c-163- *\ndrivers/net/mdio/mdio-bcm-unimac.c:164: * bus-\u003ereset is invoked before mdiobus_scan during mdiobus_register and is\ndrivers/net/mdio/mdio-bcm-unimac.c-165- * therefore the right location to stick that workaround. Since we do not want\n--\ndrivers/net/phy/mdio_bus_provider.c=396=static void of_mdiobus_link_mdiodev(struct mii_bus *bus,\n--\ndrivers/net/phy/mdio_bus_provider.c-407-\ndrivers/net/phy/mdio_bus_provider.c:408:static struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr, bool c45)\ndrivers/net/phy/mdio_bus_provider.c-409-{\n--\ndrivers/net/phy/mdio_bus_provider.c-447-/**\ndrivers/net/phy/mdio_bus_provider.c:448: * mdiobus_scan_c22 - scan one address on a bus for C22 MDIO devices.\ndrivers/net/phy/mdio_bus_provider.c-449- * @bus: mii_bus to scan\n--\ndrivers/net/phy/mdio_bus_provider.c-458- */\ndrivers/net/phy/mdio_bus_provider.c:459:struct phy_device *mdiobus_scan_c22(struct mii_bus *bus, int addr)\ndrivers/net/phy/mdio_bus_provider.c-460-{\ndrivers/net/phy/mdio_bus_provider.c:461:\treturn mdiobus_scan(bus, addr, false);\ndrivers/net/phy/mdio_bus_provider.c-462-}\ndrivers/net/phy/mdio_bus_provider.c:463:EXPORT_SYMBOL(mdiobus_scan_c22);\ndrivers/net/phy/mdio_bus_provider.c-464-\ndrivers/net/phy/mdio_bus_provider.c-465-/**\ndrivers/net/phy/mdio_bus_provider.c:466: * mdiobus_scan_c45 - scan one address on a bus for C45 MDIO devices.\ndrivers/net/phy/mdio_bus_provider.c-467- * @bus: mii_bus to scan\n--\ndrivers/net/phy/mdio_bus_provider.c-476- */\ndrivers/net/phy/mdio_bus_provider.c:477:static struct phy_device *mdiobus_scan_c45(struct mii_bus *bus, int addr)\ndrivers/net/phy/mdio_bus_provider.c-478-{\ndrivers/net/phy/mdio_bus_provider.c:479:\treturn mdiobus_scan(bus, addr, true);\ndrivers/net/phy/mdio_bus_provider.c-480-}\ndrivers/net/phy/mdio_bus_provider.c-481-\ndrivers/net/phy/mdio_bus_provider.c:482:static int mdiobus_scan_bus_c22(struct mii_bus *bus)\ndrivers/net/phy/mdio_bus_provider.c-483-{\n--\ndrivers/net/phy/mdio_bus_provider.c-489-\ndrivers/net/phy/mdio_bus_provider.c:490:\t\t\tphydev = mdiobus_scan_c22(bus, i);\ndrivers/net/phy/mdio_bus_provider.c-491-\t\t\tif (IS_ERR(phydev) \u0026\u0026 (PTR_ERR(phydev) != -ENODEV))\n--\ndrivers/net/phy/mdio_bus_provider.c-497-\ndrivers/net/phy/mdio_bus_provider.c:498:static int mdiobus_scan_bus_c45(struct mii_bus *bus)\ndrivers/net/phy/mdio_bus_provider.c-499-{\n--\ndrivers/net/phy/mdio_bus_provider.c-509-\ndrivers/net/phy/mdio_bus_provider.c:510:\t\t\tphydev = mdiobus_scan_c45(bus, i);\ndrivers/net/phy/mdio_bus_provider.c-511-\t\t\tif (IS_ERR(phydev) \u0026\u0026 (PTR_ERR(phydev) != -ENODEV))\n--\ndrivers/net/phy/mdio_bus_provider.c=553=int __mdiobus_register(struct mii_bus *bus, struct module *owner)\n--\ndrivers/net/phy/mdio_bus_provider.c-632-\tif (bus-\u003eread) {\ndrivers/net/phy/mdio_bus_provider.c:633:\t\terr = mdiobus_scan_bus_c22(bus);\ndrivers/net/phy/mdio_bus_provider.c-634-\t\tif (err)\n--\ndrivers/net/phy/mdio_bus_provider.c-640-\tif (!prevent_c45_scan \u0026\u0026 bus-\u003eread_c45) {\ndrivers/net/phy/mdio_bus_provider.c:641:\t\terr = mdiobus_scan_bus_c45(bus);\ndrivers/net/phy/mdio_bus_provider.c-642-\t\tif (err)\n--\ninclude/linux/phy.h=462=struct mii_bus *mdio_find_bus(const char *mdio_name);\ninclude/linux/phy.h:463:struct phy_device *mdiobus_scan_c22(struct mii_bus *bus, int addr);\ninclude/linux/phy.h-464-\n"
} |
| 21/2 | 2026/08/25 19:20 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 36403 output: 37 thoughts: 661 Thoughts: **My Analysis of a Potential Fuzzing Target**
Okay, so here's the breakdown of what I've got. I've realized that the `__mdiobus_register` function, and the chain of calls stemming from it—`mdiobus_scan_bus_c22`, `mdiobus_scan_c22`, `mdiobus_scan`, `get_phy_device`, and finally `phy_device_create`—provides a potential entry point. Specifically, drivers registering an MDIO bus without Device Tree information, like `smsc95xx`, will trigger a bus scan and device creation based on PHY IDs obtained from the MDIO read function.
Now, because syzkaller can emulate `smsc95xx` via `vhci`, it can inject a specific PHY ID during that scan – in this case, `0x03a29414`. This should cause the `econet-fe-soc` PHY driver to be probed, and its associated code to be executed. So, given that, I'm confident that this code path is reachable and therefore, worth fuzzing.
My next step is to identify the critical functions within that driver. I've examined the driver definition and it looks like I'm dealing with this:
```c
static struct phy_driver en751221_fephy_driver[] = {
{
PHY_ID_MATCH_EXACT(ECONET_FEPHY_ID_EN7526C),
.name = "EcoNet EN751221 FEPHY",
.config_init = en751221_fephy_config_init,
.config_intr = genphy_no_config_intr,
.handle_interrupt = genphy_handle_interrupt_no_ack,
.probe = en751221_fephy_probe,
.read_page = mtk_phy_read_page,
.write_page = mtk_phy_write_page,
}
};
```
I initially identified `en751221_fephy_config_init` and `en751221_fephy_probe` as the most likely candidates. However, I want to double-check that these functions don't just call static functions that will be inlined, because I prefer non-static, non-inlined API entrypoint functions. Sure enough, `en751221_fephy_config_init` calls several static helper functions: `en751221_fephy_r50`, `en751221_fephy_tx_offset`, and `en751221_fephy_tx_amp`. Thus, I will stick with just `en751221_fephy_config_init` and `en751221_fephy_probe`.
Finally, since `mtk_phy_read_page` and `mtk_phy_write_page` appear in the driver definition, I want to confirm where they are defined. They are not defined in the current file, so I need to check either `mtk.h` or `mtk.c` to see if they're relevant, and how to reach them.
|
| 22/2 | 2026/08/25 19:20 | tool | grepper |
0mArgs:{
"Expression": "mtk_phy_read_page",
"PathPrefix": "drivers/net/phy/mediatek/"
}Results: {
"Output": "drivers/net/phy/mediatek/econet-fe-soc.c=608=static struct phy_driver en751221_fephy_driver[] = {\n--\ndrivers/net/phy/mediatek/econet-fe-soc.c-615-\t\t.probe\t\t= en751221_fephy_probe,\ndrivers/net/phy/mediatek/econet-fe-soc.c:616:\t\t.read_page\t= mtk_phy_read_page,\ndrivers/net/phy/mediatek/econet-fe-soc.c-617-\t\t.write_page\t= mtk_phy_write_page,\n--\ndrivers/net/phy/mediatek/mtk-2p5ge.c=379=static struct phy_driver mtk_2p5gephy_driver[] = {\n--\ndrivers/net/phy/mediatek/mtk-2p5ge.c-390-\t\t.resume = genphy_resume,\ndrivers/net/phy/mediatek/mtk-2p5ge.c:391:\t\t.read_page = mtk_phy_read_page,\ndrivers/net/phy/mediatek/mtk-2p5ge.c-392-\t\t.write_page = mtk_phy_write_page,\n--\ndrivers/net/phy/mediatek/mtk-ge-soc.c=1495=static struct phy_driver mtk_socphy_driver[] = {\n--\ndrivers/net/phy/mediatek/mtk-ge-soc.c-1504-\t\t.resume\t\t= genphy_resume,\ndrivers/net/phy/mediatek/mtk-ge-soc.c:1505:\t\t.read_page\t= mtk_phy_read_page,\ndrivers/net/phy/mediatek/mtk-ge-soc.c-1506-\t\t.write_page\t= mtk_phy_write_page,\n--\ndrivers/net/phy/mediatek/mtk-ge-soc.c-1521-\t\t.resume\t\t= genphy_resume,\ndrivers/net/phy/mediatek/mtk-ge-soc.c:1522:\t\t.read_page\t= mtk_phy_read_page,\ndrivers/net/phy/mediatek/mtk-ge-soc.c-1523-\t\t.write_page\t= mtk_phy_write_page,\n--\ndrivers/net/phy/mediatek/mtk-ge.c=99=static struct phy_driver mtk_gephy_driver[] = {\n--\ndrivers/net/phy/mediatek/mtk-ge.c-110-\t\t.resume\t\t= genphy_resume,\ndrivers/net/phy/mediatek/mtk-ge.c:111:\t\t.read_page\t= mtk_phy_read_page,\ndrivers/net/phy/mediatek/mtk-ge.c-112-\t\t.write_page\t= mtk_phy_write_page,\n--\ndrivers/net/phy/mediatek/mtk-ge.c-124-\t\t.resume\t\t= genphy_resume,\ndrivers/net/phy/mediatek/mtk-ge.c:125:\t\t.read_page\t= mtk_phy_read_page,\ndrivers/net/phy/mediatek/mtk-ge.c-126-\t\t.write_page\t= mtk_phy_write_page,\n--\ndrivers/net/phy/mediatek/mtk-phy-lib.c=84=EXPORT_SYMBOL_GPL(__mtk_tr_clr_bits);\ndrivers/net/phy/mediatek/mtk-phy-lib.c-85-\ndrivers/net/phy/mediatek/mtk-phy-lib.c:86:int mtk_phy_read_page(struct phy_device *phydev)\ndrivers/net/phy/mediatek/mtk-phy-lib.c-87-{\n--\ndrivers/net/phy/mediatek/mtk-phy-lib.c-89-}\ndrivers/net/phy/mediatek/mtk-phy-lib.c:90:EXPORT_SYMBOL_GPL(mtk_phy_read_page);\ndrivers/net/phy/mediatek/mtk-phy-lib.c-91-\n--\ndrivers/net/phy/mediatek/mtk.h=81=void __mtk_tr_clr_bits(struct phy_device *phydev, u8 ch_addr, u8 node_addr,\n--\ndrivers/net/phy/mediatek/mtk.h-83-\ndrivers/net/phy/mediatek/mtk.h:84:int mtk_phy_read_page(struct phy_device *phydev);\ndrivers/net/phy/mediatek/mtk.h-85-int mtk_phy_write_page(struct phy_device *phydev, int page);\n"
} |
| 23/2 | 2026/08/25 19:20 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 39053 output: 37 thoughts: 153 Thoughts: **Navigating the Mediatek PHY Library** Okay, so I'm digging into `mtk-phy-lib.c`. I need to figure out the enabling configurations for this driver. First, let's look at `EnableConfigs`. Alright, looks like `CONFIG_ECONET_FE_SOC_PHY` is the key. It's dependent on `ECONET || COMPILE_TEST`, which simplifies things. Basically, I just need `ECONET_FE_SOC_PHY` enabled. Now, a quick sanity check. Does this thing depend on either `MDIO_DEVICE` or `PHYLIB`? Aha, it looks like `PHYLIB` is selected by `MTK_NET_PHYLIB`, which, in turn, is selected by `ECONET_FE_SOC_PHY`. Got it! Wait a minute, `MTK_NET_PHYLIB` is actually a config option itself. I'd better double-check the `drivers/net/phy/mediatek/Kconfig` file to be completely certain about its dependencies and settings. |
| 24/2 | 2026/08/25 19:20 | tool | grepper |
0mArgs:{
"Expression": "config MTK_NET_PHYLIB",
"PathPrefix": "drivers/net/phy/mediatek/"
}Results: {
"Output": "drivers/net/phy/mediatek/Kconfig=48=config MEDIATEK_GE_SOC_PHY\n--\ndrivers/net/phy/mediatek/Kconfig-66-\ndrivers/net/phy/mediatek/Kconfig:67:config MTK_NET_PHYLIB\ndrivers/net/phy/mediatek/Kconfig-68-\ttristate\n"
} |
| 25/2 | 2026/08/25 19:20 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 39572 output: 260 thoughts: 608 Thoughts: **Reasoning for Fuzzing the EcoNet PHY Driver** Okay, so I've been looking at this patch and, basically, it adds a new PHY driver specifically for those EcoNet 10/100 SoC Ethernet PHYs, right? The key here is reachability in a virtualized environment – and that's where things get interesting from a fuzzing perspective. I know from experience that PHY drivers *can* be reached even in these virtualized setups because we can emulate an MDIO bus. Think about how syzkaller can emulate a USB network adapter, like `smsc95xx`, using `vhci`. When that emulated USB device gets probed, it sets up an MDIO bus and starts scanning for PHYs. Now, here's the clever bit: during that MDIO scan (where it's essentially sending MDIO read requests as USB control messages), if we return the specific PHY ID, in this case `0x03a29414`, the MDIO bus will instantiate a PHY device with *that* ID. That's the trigger! That will then kick off the `econet-fe-soc` PHY driver's probe and initialization functions. Bingo! Code execution. So, this patch isn't just introducing a new driver; it's opening a potential fuzzing target. We know the code is reachable, and that it makes fuzzing worthwhile. I know I want to keep an eye on `en751221_fephy_probe` and `en751221_fephy_config_init` functions as entry points. I've looked at the config options, `ECONET_FE_SOC_PHY` is the crucial one to enable. I double-checked that `PHY_PACKAGE` is selected, and `MTK_NET_PHYLIB` is also selected, but `ECONET_FE_SOC_PHY` is sufficient. I confirmed that `ECONET_FE_SOC_PHY` is tristate, so it's the only one we really need. No need to list `USB_NET_SMSC95XX`, as syzkaller usually has that one enabled already. I'm confident in the approach and have documented the reasoning. I will `set-results`. |
| 26/2 | 2026/08/25 19:20 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"ECONET_FE_SOC_PHY"
],
"FocusSymbols": [
"en751221_fephy_probe",
"en751221_fephy_config_init"
],
"Reasoning": "The patch introduces a new PHY driver for EcoNet 10/100 SoC Ethernet PHYs (`drivers/net/phy/mediatek/econet-fe-soc.c`). PHY drivers can be reached in virtualized environments by emulating an MDIO bus. For example, syzkaller can emulate a USB network adapter (such as `smsc95xx`) via the `vhci` driver. When the emulated USB device is probed, it registers an MDIO bus and scans for PHYs by sending MDIO read requests (which translate to USB control messages). By returning the specific PHY ID (`0x03a29414`) during the MDIO scan, the MDIO bus will instantiate a PHY device with this ID, triggering the probe and initialization functions of the `econet-fe-soc` PHY driver. Therefore, the code is reachable and worth fuzzing.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"ECONET_FE_SOC_PHY"
],
"FocusSymbols": [
"en751221_fephy_probe",
"en751221_fephy_config_init"
],
"Reasoning": "The patch introduces a new PHY driver for EcoNet 10/100 SoC Ethernet PHYs (`drivers/net/phy/mediatek/econet-fe-soc.c`). PHY drivers can be reached in virtualized environments by emulating an MDIO bus. For example, syzkaller can emulate a USB network adapter (such as `smsc95xx`) via the `vhci` driver. When the emulated USB device is probed, it registers an MDIO bus and scans for PHYs by sending MDIO read requests (which translate to USB control messages). By returning the specific PHY ID (`0x03a29414`) during the MDIO scan, the MDIO bus will instantiate a PHY device with this ID, triggering the probe and initialization functions of the `econet-fe-soc` PHY driver. Therefore, the code is reachable and worth fuzzing.",
"WorthFuzzing": true
} |
| 27/1 | 2026/08/25 19:20 | action | If |
0mArgs:{
"WorthFuzzing": true
}Results: null |
| 28/2 | 2026/08/25 19:20 | agent | kmsan-evaluator |
0mModel:best-expensive Args: null Results: {
"KMSANReasoning": "The patch introduces a new Ethernet PHY driver for EcoNet 10/100 SoCs. The driver primarily interacts with hardware registers via MDIO (using phylib functions like phy_write_paged and phy_read_paged) and performs calibration routines. All allocated structures (e.g., mtk_socphy_priv and the shared package data) are zero-initialized using devm_kzalloc or standard phylib functions. Stack variables like cal_complete_ctx are also explicitly zero-initialized. The patch does not introduce new user-space APIs, netlink attributes, ioctls, or complex data structures that could leak uninitialized memory to user space or cause uninitialized memory-dependent control flow. Any potential memory safety issues (such as out-of-bounds accesses or use-after-free) would be effectively caught by KASAN. Therefore, a dedicated KMSAN fuzzing session is not justified.",
"NeedsKMSAN": false
}Instruction: You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.
CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
a comprehensive suite of debugging tools and sanitizers, including KASAN
(out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
(locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
or page allocations) and kernel-to-user memory info-leaks.
Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.
Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.
Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
attributes, ioctl output arguments, socket options, or BPF buffers) where fields
or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
uninitialized bytes of existing buffers.
Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
dereferences, locking deadlocks, or use-after-free bugs (these are already caught
by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.
Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt: For your convenience, here is the diff of the changes:
commit 447bbc436dec24504c070330c6d47b7919316814
Author: syz-cluster <triage@syzkaller.com>
Date: Tue Aug 25 19:18:41 2026 +0000
syz-cluster: applied patch under review
diff --git a/MAINTAINERS b/MAINTAINERS
index 460cb72688450..7ca3d8d08c693 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9253,6 +9253,12 @@ W: https://linuxtv.org
Q: http://patchwork.linuxtv.org/project/linux-media/list/
F: drivers/media/dvb-frontends/ec100*
+ECONET ETHERNET PHY DRIVER
+M: Caleb James DeLisle <cjd@cjdns.fr>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/phy/mediatek/econet-fe-soc.c
+
ECONET MIPS PLATFORM
M: Caleb James DeLisle <cjd@cjdns.fr>
L: linux-mips@vger.kernel.org
diff --git a/drivers/net/phy/mediatek/Kconfig b/drivers/net/phy/mediatek/Kconfig
index 3b9cf82c0cb28..64abf5b695ab1 100644
--- a/drivers/net/phy/mediatek/Kconfig
+++ b/drivers/net/phy/mediatek/Kconfig
@@ -1,4 +1,19 @@
# SPDX-License-Identifier: GPL-2.0-only
+
+config ECONET_FE_SOC_PHY
+ tristate "EcoNet 10/100 SoC Ethernet PHYs"
+ depends on ECONET || COMPILE_TEST
+ select MTK_NET_PHYLIB
+ select PHY_PACKAGE
+ help
+ Supports EcoNet SoC built-in 10/100 Ethernet PHYs.
+
+ Include support for built-in Ethernet PHYs which are present in
+ EN7526C based SoCs (including EN7526F). It can also work with
+ legacy EN7512 FE PHYs which identify as 03a2.9412, but since this
+ ID is used by MEDIATEK_GE_PHY, users wanting to force this driver
+ must override the ID to 03a2.9414 in the device tree.
+
config MEDIATEK_2P5GE_PHY
tristate "MediaTek 2.5Gb Ethernet PHYs"
depends on (ARM64 && ARCH_MEDIATEK) || COMPILE_TEST
diff --git a/drivers/net/phy/mediatek/Makefile b/drivers/net/phy/mediatek/Makefile
index 6f9cacf7f9065..71239e92aa581 100644
--- a/drivers/net/phy/mediatek/Makefile
+++ b/drivers/net/phy/mediatek/Makefile
@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_ECONET_FE_SOC_PHY) += econet-fe-soc.o
obj-$(CONFIG_MEDIATEK_2P5GE_PHY) += mtk-2p5ge.o
obj-$(CONFIG_MEDIATEK_FE_SOC_PHY) += mtk-fe-soc.o
obj-$(CONFIG_MEDIATEK_GE_PHY) += mtk-ge.o
diff --git a/drivers/net/phy/mediatek/econet-fe-soc.c b/drivers/net/phy/mediatek/econet-fe-soc.c
new file mode 100644
index 0000000000000..c5dbb32a5d1ab
--- /dev/null
+++ b/drivers/net/phy/mediatek/econet-fe-soc.c
@@ -0,0 +1,632 @@
+// SPDX-License-Identifier: GPL-2.0+
+#include <linux/bits.h>
+#include <linux/module.h>
+#include <linux/phy.h>
+#include <linux/types.h>
+
+#include "../phylib.h"
+#include "mtk.h"
+
+/*
+ * An older version of this PHY hardware uses the ID 03a2.9412.
+ * This driver does not match on it because it collides with
+ * MTK_GPHY_ID_MT7530, so users wanting to use this driver should
+ * override the PHY ID in the device tree to 03a2.9414.
+ */
+#define ECONET_FEPHY_ID_EN7526C 0x03a29414
+
+#define ECONET_PG_G1 0x1000
+#define ECONET_PG_G2 0x2000
+#define ECONET_PG_G3 0x3000
+#define ECONET_PG_G4 0x4000
+#define ECONET_PG_G5 0x5000
+#define ECONET_PG_G6 0x6000
+#define ECONET_PG_G7 0x7000
+
+#define ECONET_PG_L0 0x8000
+#define ECONET_PG_L1 0x9000
+#define ECONET_PG_L2 0xa000
+#define ECONET_PG_L3 0xb000
+#define ECONET_PG_L4 0xc000
+
+/* 4 ports (8,9,10,11) or 3 ports (9,10,11) */
+#define ECONET_LEGACY_PORT0_ADDR 8
+#define ECONET_EN7526C_PORT0_ADDR 9
+
+/*
+ * Calibration polling does not complete until ECONET_CAL_MIN_CYCLES
+ * consecutive "done" results are received. See: poll_cal_complete
+ */
+#define ECONET_CAL_MIN_CYCLES 10
+
+/*
+ * Microseconds per calibration poll cycle, calibration takes approximately
+ * ECONET_CAL_CYCLE_US * ECONET_CAL_MIN_CYCLES. Total setup time is usually
+ * less than 150x this number.
+ */
+#define ECONET_CAL_CYCLE_US 10
+
+/* Total time allowed for calibration polling before error is returned. */
+#define ECONET_CAL_TIMEOUT_US 100000
+
+/*
+ * Note: These register definitions were written without the benefit of the
+ * hardware specification, so names are given only where the meaning is
+ * fairly obvious.
+ */
+
+/* G7R24 */
+#define ECONET_CAL_TYPE_MASK GENMASK(14, 12)
+#define ECONET_CAL_TYPE_R50 FIELD_PREP(ECONET_CAL_TYPE_MASK, 0x6)
+#define ECONET_CAL_TYPE_TXOS FIELD_PREP(ECONET_CAL_TYPE_MASK, 0x3)
+#define ECONET_CAL_TYPE_TXAMP FIELD_PREP(ECONET_CAL_TYPE_MASK, 0x7)
+#define ECONET_R50_ZCAL_MASK GENMASK(11, 6)
+#define ECONET_CALIN_EN7526C BIT(4)
+#define ECONET_CAL_DONE_EN7526C BIT(1)
+#define ECONET_CAL_OUT_EN7526C BIT(0)
+
+/* L4R23 */
+#define ECONET_CALIN_LEGACY BIT(2)
+#define ECONET_CAL_DONE_LEGACY BIT(6)
+#define ECONET_CAL_OUT_LEGACY BIT(4)
+
+#define ECONET_DAC_IN_2V 0x0f0
+
+/* L4R17 */
+#define ECONET_TXOS_SIGN BIT(13)
+#define ECONET_TXOS_MAG GENMASK(12, 8)
+
+#define ECONET_TXOS_DEFAULT 0
+#define ECONET_TXOS_MAX 31
+#define ECONET_TXOS_MIN -31
+
+#define ECONET_R50_ZCAL_DEFAULT 0x20
+#define ECONET_R50_ZCAL_MAX 0x3f
+
+/* L4R22 */
+#define ECONET_R50_TXCAL_MASK GENMASK(15, 8)
+#define ECONET_R50_RXCAL_MASK GENMASK(7, 0)
+
+#define ECONET_TXAMP_DEFAULT 0x12
+#define ECONET_TXAMP_MAX 0x3f
+
+/* ZCAL compensation table per chip, per phy, RX + TX */
+struct compensation {
+ s8 zcal_tx;
+ s8 zcal_rx;
+ s8 txos;
+ s8 amp;
+};
+
+static const struct compensation en751221_comp[2][4] = {
+ {
+ /* Legacy */
+ { .zcal_tx = 2, .zcal_rx = -4, .txos = 0, .amp = 1 }, /* 8 */
+ { .zcal_tx = 2, .zcal_rx = -4, .txos = 1, .amp = 2 }, /* 9 */
+ { .zcal_tx = 2, .zcal_rx = -4, .txos = 1, .amp = 1 }, /* 10 */
+ { .zcal_tx = -2, .zcal_rx = -8, .txos = 1, .amp = 1 }, /* 11 */
+ },
+ {
+ /* EN7526C */
+ { .zcal_tx = 3, .zcal_rx = 6, .txos = 1, .amp = 0 }, /* 9 */
+ { .zcal_tx = 2, .zcal_rx = 5, .txos = 1, .amp = -1 }, /* 10 */
+ { .zcal_tx = 6, .zcal_rx = 6, .txos = 1, .amp = -1 }, /* 11 */
+ { }, /* Unused */
+ }
+};
+
+static const u8 zcal_to_r50ohm[64] = {
+ 127, 127, 127, 127, 127, 127, 126, 123, 120, 117, 114, 112, 110, 107, 105, 103,
+ 101, 99, 97, 79, 77, 75, 74, 72, 70, 69, 67, 66, 65, 47, 46, 45,
+ 43, 42, 41, 40, 39, 38, 37, 36, 34, 34, 33, 32, 15, 14, 13, 12,
+ 11, 10, 10, 9, 8, 7, 7, 6, 5, 4, 4, 3, 2, 2, 1, 1
+};
+
+struct econet_socphy_shared {
+ struct phy_device *phydev_p0;
+};
+
+static struct compensation get_ctab(struct phy_device *phydev)
+{
+ struct econet_socphy_shared *shared = phy_package_get_priv(phydev);
+ u8 phy_offset;
+
+ phy_offset = phydev->mdio.addr - shared->phydev_p0->mdio.addr;
+
+ if (WARN_ON_ONCE(phy_offset >= ARRAY_SIZE(en751221_comp[0])))
+ return (struct compensation) {};
+
+ return en751221_comp[phydev->phy_id == ECONET_FEPHY_ID_EN7526C][phy_offset];
+}
+
+static void set_calin_flag(struct phy_device *phydev, bool enabled)
+{
+ struct econet_socphy_shared *shared = phy_package_get_priv(phydev);
+ u16 set;
+
+ if (phydev->phy_id == ECONET_FEPHY_ID_EN7526C) {
+ /* G7R24 is shared so phy_modify_paged is required */
+ set = enabled ? ECONET_CALIN_EN7526C : 0;
+ phy_modify_paged(phydev, ECONET_PG_G7, 24,
+ ECONET_CALIN_EN7526C, set);
+ } else {
+ set = enabled ? ECONET_CALIN_LEGACY : 0;
+ phy_write_paged(shared->phydev_p0, ECONET_PG_L4, 23, set);
+ }
+}
+
+struct cal_complete_ctx {
+ u16 consecutive_trues : 15;
+ bool started : 1;
+ bool observed_false : 1;
+ u16 aborted_tries : 7;
+};
+
+/**
+ * poll_cal_complete - poll for calibration completion.
+ *
+ * Because some legacy chips are known to be unreliable (false positives),
+ * reference code adds 10,000us of fixed delay per calibration cycle.
+ * Practically, there are about 10-50 calibration cycles for each of the
+ * three tunables, so adding a significant fixed delay is costly.
+ *
+ * This implementation instead starts with CALIN disabled, polls for
+ * done status false, then enables CALIN and polls until a true done
+ * status is recorded ECONET_CAL_MIN_CYCLES times consecutively.
+ *
+ * @returns:
+ * < 0 on error
+ * 0 not yet complete
+ * 1 complete false
+ * 2 complete true
+ */
+static int poll_cal_complete(struct phy_device *phydev,
+ struct cal_complete_ctx *ctx)
+{
+ struct econet_socphy_shared *shared = phy_package_get_priv(phydev);
+ int done_mask;
+ int out_mask;
+ int ret;
+
+ if (phydev->phy_id == ECONET_FEPHY_ID_EN7526C) {
+ ret = phy_read_paged(phydev, ECONET_PG_G7, 24);
+ done_mask = ECONET_CAL_DONE_EN7526C;
+ out_mask = ECONET_CAL_OUT_EN7526C;
+ } else {
+ ret = phy_read_paged(shared->phydev_p0, ECONET_PG_L4, 23);
+ done_mask = ECONET_CAL_DONE_LEGACY;
+ out_mask = ECONET_CAL_OUT_LEGACY;
+ }
+ if (ret < 0)
+ return ret;
+
+ if ((ret & done_mask) == 0) {
+ ctx->observed_false = true;
+ ctx->aborted_tries += (ctx->consecutive_trues > 0);
+ ctx->consecutive_trues = 0;
+
+ if (!ctx->started) {
+ set_calin_flag(phydev, true);
+ ctx->started = true;
+ return poll_cal_complete(phydev, ctx);
+ }
+
+ return 0;
+ }
+
+ ctx->consecutive_trues++;
+
+ if (ctx->consecutive_trues < ECONET_CAL_MIN_CYCLES || !ctx->observed_false)
+ return 0;
+
+ if (ctx->aborted_tries)
+ phydev_warn(phydev, "Calibration cycle %d false positives\n",
+ ctx->aborted_tries);
+
+ return ((ret & out_mask) != 0) ? 2 : 1;
+}
+
+/**
+ * poll_cal_complete - check for calibration register
+ * @returns: -error or 1 if calibration result is true.
+ */
+static int en751221_fephy_cal_cycle(struct phy_device *phydev)
+{
+ struct cal_complete_ctx cctx = {};
+ int out;
+ int ret;
+
+ set_calin_flag(phydev, false);
+
+ ret = read_poll_timeout(poll_cal_complete, out, out != 0,
+ ECONET_CAL_CYCLE_US, ECONET_CAL_TIMEOUT_US,
+ false, phydev, &cctx);
+
+ if (ret < 0) {
+ phydev_err(phydev, "Calibration timeout %d (%d / %d / %d / %d)\n",
+ ret, cctx.consecutive_trues, cctx.observed_false,
+ cctx.aborted_tries, cctx.started);
+ } else if (out < 0) {
+ phydev_err(phydev, "Calibration error %d\n", out);
+ ret = out;
+ } else {
+ ret = out > 1;
+ }
+
+ set_calin_flag(phydev, false);
+ return ret;
+}
+
+static int en751221_fephy_r50(struct phy_device *phydev)
+{
+ struct econet_socphy_shared *shared = phy_package_get_priv(phydev);
+ struct compensation ctab = get_ctab(phydev);
+ int zcal_sz = ARRAY_SIZE(zcal_to_r50ohm);
+ u8 rg_zcal_ctrl = ECONET_R50_ZCAL_DEFAULT;
+ int initial_comp_out;
+ int polarity = 0;
+ int comp_out;
+ int ret = 0;
+ u16 rxcal;
+ u16 txcal;
+
+ if (phydev->phy_id == ECONET_FEPHY_ID_EN7526C)
+ phy_write_paged(phydev, ECONET_PG_G7, 24, ECONET_CAL_TYPE_R50);
+ else
+ phy_write_paged(shared->phydev_p0, ECONET_PG_L3, 25, 0xc000);
+
+ phy_modify_paged(phydev, ECONET_PG_L3, 25, 0x1000, 0x1000);
+
+ for (;;) {
+ if (phydev->phy_id == ECONET_FEPHY_ID_EN7526C)
+ phy_modify_paged(phydev, ECONET_PG_G7, 24,
+ ECONET_R50_ZCAL_MASK,
+ FIELD_PREP(ECONET_R50_ZCAL_MASK,
+ rg_zcal_ctrl));
+ else
+ phy_write_paged(shared->phydev_p0, ECONET_PG_L3, 26,
+ rg_zcal_ctrl);
+
+ comp_out = en751221_fephy_cal_cycle(phydev);
+ if (comp_out < 0) {
+ ret = comp_out;
+ goto out;
+ }
+
+ if (polarity == 0) {
+ /* First cycle */
+ initial_comp_out = comp_out;
+ polarity = comp_out ? -1 : 1;
+ } else if (initial_comp_out != comp_out) {
+ /* Found */
+ break;
+ }
+
+ rg_zcal_ctrl += polarity;
+
+ if (rg_zcal_ctrl > ECONET_R50_ZCAL_MAX) {
+ ret = 1;
+ goto out;
+ }
+ }
+
+ rxcal = max(0, min(zcal_sz - 1, ctab.zcal_rx + rg_zcal_ctrl));
+ txcal = max(0, min(zcal_sz - 1, ctab.zcal_tx + rg_zcal_ctrl));
+
+ phy_write_paged(phydev, ECONET_PG_L4, 22,
+ FIELD_PREP(ECONET_R50_TXCAL_MASK, txcal) |
+ FIELD_PREP(ECONET_R50_RXCAL_MASK, rxcal));
+
+out:
+ /* Zero out registers */
+ phy_write_paged(phydev, ECONET_PG_L3, 25, 0x0000);
+
+ if (phydev->phy_id == ECONET_FEPHY_ID_EN7526C)
+ phy_write_paged(phydev, ECONET_PG_G7, 24, 0x0000);
+ else
+ phy_write_paged(shared->phydev_p0, ECONET_PG_L3, 25, 0xc000);
+
+ phy_write_paged(phydev, ECONET_PG_L3, 25, 0x0000);
+
+ return ret;
+}
+
+static int en751221_fephy_tx_offset(struct phy_device *phydev)
+{
+ struct econet_socphy_shared *shared = phy_package_get_priv(phydev);
+ struct compensation ctab = get_ctab(phydev);
+ int initial_comp_out;
+ int polarity = 0;
+ int offset = ECONET_TXOS_DEFAULT;
+ u16 offset_bin;
+ int comp_out;
+ int ret = 0;
+
+ phy_write_paged(phydev, ECONET_PG_L0, 0, 0x2100);
+ phy_write_paged(phydev, ECONET_PG_L0, 26, 0x5200); /* fix MDI */
+
+ if (phydev->phy_id == ECONET_FEPHY_ID_EN7526C) {
+ phy_write_paged(phydev, ECONET_PG_G7, 24, ECONET_CAL_TYPE_TXOS);
+ phy_write_paged(phydev, ECONET_PG_L3, 25, 0x0400);
+ } else {
+ phy_write_paged(shared->phydev_p0, ECONET_PG_L3, 25, 0x4800);
+ phy_write_paged(phydev, ECONET_PG_L3, 25, 0x4c00);
+ }
+
+ phy_modify_paged(phydev, ECONET_PG_G4, 21, 0x0800, 0x0800);
+
+ phy_modify_paged(phydev, ECONET_PG_L0, 30, 0x02c0, 0x02c0);
+
+ phy_write_paged(phydev, ECONET_PG_G1, 26, 0x8000);
+
+ for (;;) {
+ offset_bin = FIELD_PREP(ECONET_TXOS_MAG, abs(offset));
+ if (offset < 0)
+ offset_bin |= ECONET_TXOS_SIGN;
+
+ phy_write_paged(phydev, ECONET_PG_L4, 17, offset_bin);
+
+ comp_out = en751221_fephy_cal_cycle(phydev);
+ if (comp_out < 0) {
+ ret = comp_out;
+ goto out;
+ }
+
+ if (polarity == 0) {
+ /* First cycle */
+ initial_comp_out = comp_out;
+ polarity = comp_out ? -1 : 1;
+ } else if (initial_comp_out != comp_out) {
+ /* Found */
+ break;
+ }
+
+ offset += polarity;
+
+ if (offset > ECONET_TXOS_MAX || offset < ECONET_TXOS_MIN) {
+ ret = 1;
+ offset = 0;
+ goto set_val;
+ }
+ }
+
+ offset += ctab.txos * polarity;
+
+ if (offset > ECONET_TXOS_MAX || offset < ECONET_TXOS_MIN)
+ offset -= ctab.txos * polarity;
+
+set_val:
+ offset_bin = FIELD_PREP(ECONET_TXOS_MAG, abs(offset));
+ if (offset < 0)
+ offset_bin |= ECONET_TXOS_SIGN;
+ phy_write_paged(phydev, ECONET_PG_L4, 17, offset_bin);
+
+out:
+ if (phydev->phy_id == ECONET_FEPHY_ID_EN7526C)
+ phy_write_paged(phydev, ECONET_PG_G7, 24, 0x0000);
+ else
+ phy_write_paged(shared->phydev_p0, ECONET_PG_L3, 25, 0x0000);
+
+ phy_write_paged(phydev, ECONET_PG_L3, 25, 0x0000);
+
+ return ret;
+}
+
+static int en751221_fephy_tx_amp(struct phy_device *phydev)
+{
+ struct econet_socphy_shared *shared = phy_package_get_priv(phydev);
+ struct compensation ctab = get_ctab(phydev);
+ int initial_comp_out;
+ u8 tx_amp = ECONET_TXAMP_DEFAULT;
+ int polarity = 0;
+ int comp_out;
+ int ret = 0;
+
+ phy_write_paged(phydev, ECONET_PG_L0, 0, 0x2100);
+ phy_write_paged(phydev, ECONET_PG_L0, 26, 0x5203);
+ phy_write_paged(phydev, ECONET_PG_G2, 25, 0x10c0);
+
+ phy_write_paged(phydev, ECONET_PG_G1, 26, 0x8000 | ECONET_DAC_IN_2V);
+ phy_write_paged(phydev, ECONET_PG_G4, 21, 0x0800);
+ phy_write_paged(phydev, ECONET_PG_L0, 30, 0x02c0);
+ phy_write_paged(phydev, ECONET_PG_L4, 21, 0x0000);
+
+ if (phydev->phy_id == ECONET_FEPHY_ID_EN7526C) {
+ phy_write_paged(phydev, ECONET_PG_G7, 24, ECONET_CAL_TYPE_TXAMP);
+ phy_write_paged(phydev, ECONET_PG_L3, 25, 0x0600);
+ } else {
+ phy_write_paged(shared->phydev_p0, ECONET_PG_L3, 25, 0xca00);
+ phy_write_paged(phydev, ECONET_PG_L3, 25, 0xca00 | 0x0400);
+ }
+
+ for (;;) {
+ phy_write_paged(phydev, ECONET_PG_L2, 23, tx_amp);
+
+ comp_out = en751221_fephy_cal_cycle(phydev);
+ if (comp_out < 0) {
+ ret = comp_out;
+ goto out;
+ }
+
+ if (polarity == 0) {
+ /* First cycle */
+ initial_comp_out = comp_out;
+ polarity = comp_out ? -1 : 1;
+ } else if (initial_comp_out != comp_out) {
+ /* Found */
+ break;
+ }
+
+ tx_amp += polarity;
+
+ if (tx_amp > ECONET_TXAMP_MAX) {
+ ret = 1;
+ tx_amp = ECONET_TXAMP_DEFAULT;
+ goto set_val;
+ }
+ }
+
+ if (tx_amp + ctab.amp < ECONET_TXAMP_MAX)
+ tx_amp += ctab.amp;
+
+set_val:
+ phy_write_paged(phydev, ECONET_PG_L2, 23, tx_amp);
+
+out:
+ if (phydev->phy_id == ECONET_FEPHY_ID_EN7526C) {
+ phy_write_paged(phydev, ECONET_PG_G7, 24, 0x0000);
+ phy_write_paged(phydev, ECONET_PG_L0, 30, 0x0000);
+ } else {
+ phy_write_paged(shared->phydev_p0, ECONET_PG_L3, 25, 0x0000);
+ }
+
+ phy_write_paged(phydev, ECONET_PG_L3, 25, 0x0000);
+
+ return ret;
+}
+
+static int en751221_fephy_config_init(struct phy_device *phydev)
+{
+ struct econet_socphy_shared *shared = phy_package_get_priv(phydev);
+ u16 l0r26_temp;
+ int ret;
+ int i;
+
+ if (!shared->phydev_p0) {
+ phydev_err(phydev, "Port zero must be configured\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (phydev->mdio.addr < shared->phydev_p0->mdio.addr)
+ return -EOPNOTSUPP;
+
+ /* Global registers */
+ phy_write_paged(phydev, ECONET_PG_G4, 26, 0x8044);
+ phy_write_paged(phydev, ECONET_PG_G5, 21, 0x00ea);
+ phy_write_paged(phydev, ECONET_PG_G5, 27, 0x02f0);
+
+ /* Local registers */
+ phy_write_paged(phydev, ECONET_PG_L0, 30, 0xa000);
+ phy_write_paged(phydev, ECONET_PG_L1, 22, 0xf000);
+ phy_write_paged(phydev, ECONET_PG_L2, 22, 0x4444);
+ phy_write_paged(phydev, ECONET_PG_L2, 24, 0x0c0c);
+ phy_write_paged(phydev, ECONET_PG_L2, 28, 0x7c44);
+ phy_write_paged(phydev, ECONET_PG_L2, 30, 0x0005);
+ phy_write_paged(phydev, ECONET_PG_L3, 17, 0x0000);
+
+ /* For E1/E2, E3 requires 0x2220, but no known EN751221 E3 chips exist */
+ phy_write_paged(phydev, ECONET_PG_L0, 30, 0x2200);
+
+ if (phydev->phy_id == ECONET_FEPHY_ID_EN7526C) {
+ /* 100Mb tx p2z_mid, z2n_mid, z2n_ovs_post, n2z_mid */
+ phy_write_paged(phydev, ECONET_PG_G5, 22, 0x0030);
+ phy_write_paged(phydev, ECONET_PG_G5, 25, 0x0248);
+ phy_write_paged(phydev, ECONET_PG_G5, 27, 0x02f0);
+ phy_write_paged(phydev, ECONET_PG_G5, 28, 0x0230);
+ }
+
+ ret = phy_read_paged(phydev, ECONET_PG_L0, 26);
+ if (ret < 0)
+ return ret;
+
+ l0r26_temp = ret;
+
+ /* BG voltage */
+ phy_write_paged(phydev, ECONET_PG_G2, 25, 0x10c0);
+
+ /* MDI */
+ phy_write_paged(phydev, ECONET_PG_L0, 26, 0x5603);
+
+ /* disable tx slew control */
+ phy_write_paged(phydev, ECONET_PG_L4, 21, 0x0000);
+
+ phy_write_paged(phydev, ECONET_PG_L0, 0, 0x2100);
+
+ for (i = 0; i < 5; i++) {
+ ret = en751221_fephy_r50(phydev);
+ if (!ret)
+ break;
+ }
+ if (ret)
+ return ret;
+
+ for (i = 0; i < 5; i++) {
+ ret = en751221_fephy_tx_offset(phydev);
+ if (!ret)
+ break;
+ }
+ if (ret)
+ return ret;
+
+ for (i = 0; i < 5; i++) {
+ ret = en751221_fephy_tx_amp(phydev);
+ if (!ret)
+ break;
+ }
+ if (ret)
+ return ret;
+
+ phy_write_paged(phydev, ECONET_PG_G1, 26, 0x0000);
+ phy_write_paged(phydev, ECONET_PG_L0, 26, l0r26_temp);
+ phy_write_paged(phydev, ECONET_PG_G1, 26, 0x0000);
+
+ phy_write_paged(phydev, ECONET_PG_L2, 22, 0x4444);
+ phy_write_paged(phydev, ECONET_PG_L0, 0, 0x3100);
+
+ return 0;
+}
+
+static int en751221_fephy_probe(struct phy_device *phydev)
+{
+ int port0 = ECONET_EN7526C_PORT0_ADDR;
+ struct econet_socphy_shared *shared;
+ struct mtk_socphy_priv *priv;
+ int ret;
+
+ if (phydev->phy_id != ECONET_FEPHY_ID_EN7526C)
+ port0 = ECONET_LEGACY_PORT0_ADDR;
+
+ ret = devm_phy_package_join(&phydev->mdio.dev, phydev, port0,
+ sizeof(struct econet_socphy_shared));
+ if (ret)
+ return ret;
+
+ shared = phy_package_get_priv(phydev);
+
+ priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ if (phydev->mdio.addr == port0)
+ shared->phydev_p0 = phydev;
+
+ phydev->priv = priv;
+
+ return 0;
+}
+
+static struct phy_driver en751221_fephy_driver[] = {
+ {
+ PHY_ID_MATCH_EXACT(ECONET_FEPHY_ID_EN7526C),
+ .name = "EcoNet EN751221 FEPHY",
+ .config_init = en751221_fephy_config_init,
+ .config_intr = genphy_no_config_intr,
+ .handle_interrupt = genphy_handle_interrupt_no_ack,
+ .probe = en751221_fephy_probe,
+ .read_page = mtk_phy_read_page,
+ .write_page = mtk_phy_write_page,
+ }
+};
+
+module_phy_driver(en751221_fephy_driver);
+
+static const struct mdio_device_id __maybe_unused en751221_fephy_tbl[] = {
+ { PHY_ID_MATCH_EXACT(ECONET_FEPHY_ID_EN7526C) },
+ { }
+};
+
+MODULE_DESCRIPTION("EcoNet SoC 10/100 Ethernet PHY driver");
+MODULE_AUTHOR("Caleb James DeLisle <cjd@cjdns.fr>");
+MODULE_LICENSE("GPL");
+
+MODULE_DEVICE_TABLE(mdio, en751221_fephy_tbl);
|
| 29/3 | 2026/08/25 19:20 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 11498 output: 219 thoughts: 438 Thoughts: **KMSAN Concerns Addressed: EcoNet PHY Driver**
Okay, so I've just reviewed the code for this new EcoNet 10/100 SoC Ethernet PHY driver, `econet-fe-soc.c`, and I'm feeling pretty good about it from a KMSAN perspective. It's a fairly straightforward PHY driver – implementing standard callbacks like `config_init`, `probe`, and the register access functions. The key is in how it handles memory and register access.
I see it utilizes standard PHYlib functions like `phy_write_paged`, `phy_read_paged`, and `phy_modify_paged` to configure the PHY. It also uses `devm_phy_package_join` and allocates a private `mtk_socphy_priv` structure. This is all standard.
Importantly, there's no interaction with user-space data that could lead to uninitialized memory being exposed. We're talking direct MDIO register reads and writes. I'm not seeing any complex data structures or potential leaks.
The allocation of the `priv` structure using `devm_kzalloc` is especially reassuring. That's a zero-initialized allocation, which is precisely what we want to avoid those tricky KMSAN issues. And `devm_phy_package_join` itself handles the shared structure in a similar safe manner, internally calling `kzalloc` or equivalent, so that's good.
To be extra sure, I just double-checked how `devm_phy_package_join` does its thing, too. It's all zeroed out there, including the shared data and `priv`. I see `struct cal_complete_ctx cctx = {};` is zero-initialized and `get_ctab(phydev)` returns a fully initialized struct by value. Everything appears to be properly initialized.
Therefore, given the nature of the driver, the use of standard and safe memory allocation methods, and the lack of complex data structures interacting with user space, I'm confident we won't be introducing any KMSAN-specific bugs here. Any issues are going to be run-of-the-mill things that KASAN will catch: logic errors, out-of-bounds accesses, or potential NULL pointer dereferences. I'm comfortable setting `NeedsKMSAN=false`.
|
| 30/3 | 2026/08/25 19:21 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch introduces a new Ethernet PHY driver for EcoNet 10/100 SoCs. The driver primarily interacts with hardware registers via MDIO (using phylib functions like phy_write_paged and phy_read_paged) and performs calibration routines. All allocated structures (e.g., mtk_socphy_priv and the shared package data) are zero-initialized using devm_kzalloc or standard phylib functions. Stack variables like cal_complete_ctx are also explicitly zero-initialized. The patch does not introduce new user-space APIs, netlink attributes, ioctls, or complex data structures that could leak uninitialized memory to user space or cause uninitialized memory-dependent control flow. Any potential memory safety issues (such as out-of-bounds accesses or use-after-free) would be effectively caught by KASAN. Therefore, a dedicated KMSAN fuzzing session is not justified.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch introduces a new Ethernet PHY driver for EcoNet 10/100 SoCs. The driver primarily interacts with hardware registers via MDIO (using phylib functions like phy_write_paged and phy_read_paged) and performs calibration routines. All allocated structures (e.g., mtk_socphy_priv and the shared package data) are zero-initialized using devm_kzalloc or standard phylib functions. Stack variables like cal_complete_ctx are also explicitly zero-initialized. The patch does not introduce new user-space APIs, netlink attributes, ioctls, or complex data structures that could leak uninitialized memory to user space or cause uninitialized memory-dependent control flow. Any potential memory safety issues (such as out-of-bounds accesses or use-after-free) would be effectively caught by KASAN. Therefore, a dedicated KMSAN fuzzing session is not justified.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|