mtk-wifi-fw/PLAN.md

157 lines
8.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# mtk-wifi-fw (working name) — Project Plan
Goal: cut teeth on real firmware RE while producing community-useful tooling,
docs, and update tracking for MediaTek Connac2 WiFi firmware. The product is
the tooling/knowledge and the skills built making it — not a modified router.
## Target facts (verified, keep cited)
- Hardware: GL-MT3000 (Beryl AX), MT7981B (Filogic 820), OpenWrt 24.10.5.
Radios on-SoC at `platform/soc/18000000.wifi`, driver `mt7915e` (mt76).
- "The blob" = 4 images, 3 NDS32LE cores inside the SoC:
- `mt7981_wm.bin` 2.05MB — WM, main MAC/PHY MCU ("neptune", build 20240823)
- `mt7981_wa.bin` 494KB — WA, offload/stats MCU
- `mt7981_wo.bin` 2.45MB — WED packet offload core (FreeRTOS v10, loaded by
`mtk_wed`, not mt76 — separate container format, TODO)
- `mt7981_rom_patch.bin` 9.8KB — boot-ROM patch ("ALPS")
- NDS32LE evidence: `GCC: (2018-10-23_nds32le-elf-mculib-v3) 4.9.4` in wa;
`portable/GCC/NDS32/port.c` in wo.
- No signature/encryption wall: loader is the kernel driver
(`mt7915_load_firmware` → `mt76_connac2_load_patch` + `load_ram`); protocol
supports per-region encrypt (`FW_FEATURE_SET_ENCRYPT`) but blobs ship
plaintext. Source-verified; empirical proof = M3 milestone.
- Container formats (from `mt76_connac_mcu.h`):
- RAM images: region data from offset 0, n×40B region table, 36B trailer at
EOF. `fw_ver` stripped (`____000000`); build_date is the only versioning.
- Patch: 92B BE header + 64B BE section table at start.
- Family coverage: mt7915/7916/7981/7986 all parse. mt7915 blobs = 2022
build, others = 2024 — free pilot for version diffing.
- Tooling: Ghidra has an official NDS32 module (`NDS32:LE:32:default`, some
V3 instr gaps); Andes GNU toolchain public (exact 2018 GCC vintage exists).
- Prior art: cyrozap/mediatek-wifi-re (older chip gen; CC-BY-SA notes,
compatible). No public RE of Connac2-generation firmware exists.
## Strategy shape
RE first, fork later. Nobody can price replacement firmware before the boot
and core-boundary picture exists, so the plan front-loads cheap-certain RE and
gates expensive authoring behind a data-informed go/no-go.
- Phase 1 static RE: carve → ELF → Ghidra → boot/ABI map (no decision needed)
- Phase 2 dynamic RE: patch bytes, reload, observe (patching = verification
instrument, no decision needed)
- Fork point (after boot/handshake + WM↔WA boundary mapped): choose between
a) audit & document track, and/or
b) authoring ladder, easiest-first: custom WO (self-contained, no radio)
→ WM handshake-only → beacon TX → association → data path.
Each rung independently valuable/stoppable.
- Version diffing runs parallel throughout (pure static, high community value).
## MVP v0.1 — "toolkit + changelog" (no hardware required, ~8–12 evenings)
Scope: extract (family-wide, correct) + to-elf + diff report over
linux-firmware git history + format/boot docs + CI cron that tracks upstream
blob drops. The repo becomes the public changelog for silently-updated
firmware.
Worklist in order:
1. Repo scaffolding: git init, dirs, dual LICENSE (MIT code / CC-BY-SA
docs), Makefile; commit existing extractor.
2. Parser correctness: resolve or document-as-unknown the 100/36B gap,
region `type` semantics, feature bit 0x80.
3. Test fixtures: golden manifests per blob (metadata only, no blob bytes).
4. ELF emitter at true load addresses (Ghidra/objdump acceptance test).
5. Dataset: walk linux-firmware git history for mediatek WiFi files.
6. Diff engine + pilot report (mt7915 2022 vs 2024; string-table add/remove
is the human-readable signal).
7. Docs: format.md (every field cited), boot.md, README quickstart.
8. CI: fixtures on commit; weekly cron vs linux-firmware HEAD regenerating
reports.
9. Polish, tag v0.1.0, undersold announcement (OpenWrt forum + r/re).
Not in v0.1: Ghidra annotation projects, symbol DBs, patched blobs, anything
router-dependent.
## Quality gates ("not noise")
- Every claim cites evidence (kernel line, blob offset) or is marked unknown.
No invented semantics.
- Family coverage verified in CI, not asserted in README.
- Reproducible in ~3 commands, plain Linux, no blobs committed
(`make fetch-fw` pulls from kernel.org).
- Undersell announcements.
## Status log
- 2026-08-20: Extractor `tools/mtk_fw_extract.py` written and run on
mt7981/7915/7916/7986 wm+wa+patch (7 files, 4 chips) — all parse clean.
Output in `extracted*/`, `extracted/manifest.json`. Solved same day: the
"gap" is a second trailer family-wide (wm 100B, wa 36B) sitting between
region data and the kernel-parsed trailer; WM's carries the full build
string MTK strips from the kernel-visible trailer (branch, MP tag, build
timestamp) — recovered version metadata for the changelog. Region type:
all parsed blobs are FW_TYPE_DEFAULT(0); loader does use `type` elsewhere
(mt7921 FW_TYPE_CLC). Remaining unknowns: feature bit 0x80 (wm 0xf0xxxxxx
regions), hidden-trailer non-string fields. Verified claims + citations:
docs/findings.md.
- Router unreachable (thinkpad jump host down). Not blocking: v0.1 is pure
static. Hardware options when needed: fix thinkpad / wire devuan-hpz to
router LAN / join `zach` WiFi from devuan-hpz (key + likely MAC-whitelisted,
but changes this machine's network path mid-session).
- 2026-08-20 (later): MVP v0.1 complete. All 9 worklist steps: scaffold,
parser correctness (hidden trailer = recovered version metadata),
dataset-driven golden tests, ELF emitter (readelf-validated; Ghidra
open-check deferred to Ghidra bring-up — deviation noted), 54-revision
dataset, diff engine + 3 pilot reports (2026 build: 0xe003b000 region
~95% changed, new RA/DPD/thermal strings), docs (format/boot), CI
(push + weekly track), v0.1.0 tagged. Announcement drafted, not posted —
hosting decision still open.
- 2026-08-20 (P1-B started): Ghidra 12.1.3 + JDK21 + PyGhidra pipeline
working (venv at ~/data/tools). Patch blob reversed to structure level:
10-entry ROM function replacement table + 49 NDS32 functions decompiling
cleanly, assert anchors (2 source files w/ line numbers), N9 core name
confirmed, CCIF/mailbox/WDT host-comm hooks mapped (findings F5).
Full decomp kept out of repo (blob-derived); notes only. WA import
running.
- Repo name + hosting: RESOLVED — mtk-wifi-fw at
https://zachery.lol/code/zacheryasc/mtk-wifi-fw (self-hosted Forgejo 9,
public, pushed 2026-08-20 with v0.1.0). GitHub mirror optional later.
Forgejo Actions may pick up .github/workflows if a runner is registered
(ci-token exists on docean — unverified).
- Router access path (needed from M3/dynamic phase).
- Fork decision (audit vs authoring ladder mix) — after boot/boundary map.
- WO container format (from `mtk_wed_mcu.c`, different loader) — M2-ish.
## Decision function & finish criteria
Project has no natural completion point; finish = satisficing set + per-node
gates. All checks binary.
**Finish conditions (both required):**
- F1 public artifact: v0.1 shipped passing quality gates, then one of
community uptake / cron keeps it alive / documented sunset ("last verified
against linux-firmware X"). Silent rot is noise; sunset is finish.
- F2 teeth cut, each backed by an artifact: container parsed (done); one
*verified cited* claim from disassembly; changed firmware behavior and
observed it on hardware (P2); optionally self-written code on target.
**Universal gate (every checkpoint):** advance iff next gate is reachable
with known techniques AND has a stated cost ceiling AND its failure would not
invalidate shipped artifacts. Slipped >2x twice → stop-and-replan. Outputs
ship as verified or documented-unknown, never "probably"; unknowns are valid
P1 exits but never authoring foundations.
**Node exits:** MVP = worklist 1–9 + quality gates. P1 = boot chain writable
step-by-step with citations; ≥30 functions named via dispatch anchors, 3
traced end-to-end; memory map complete; WM↔WA characterized; fork memo.
Authoring rung N+1 opens iff rung N demonstrated on hardware AND N+1
interface mapped AND unbrick path proven.
**Kill criteria:** 3 consecutive flash cycles with no hypothesis advance →
rung closed, findings published. ROM-verified lockout discovered → authoring
dead, audit track unaffected. Router unreachable >4 weeks → P2 paused only.
**Zombie rule:** no commits and no CI activity for 2 quarters → archive with
last-verified statement.