mtk-wifi-fw/docs/findings.md

7 KiB
Raw Blame History

Findings log

One entry per claim. Every claim carries evidence (kernel source reference or blob offset + observed bytes). Claims that cannot be evidenced are registered in the Unknowns section instead. Blobs referenced by filename are the linux-firmware copies listed in manifest.json (SHA-256 TBD in fixtures).

Blobs observed: mt7981/mt7915/mt7916/mt7986 × {wm, wa, rom_patch} (linux-firmware snapshot 2026-04-20, Debian).

F1 — Connac2 RAM container layout

[region data, packed from offset 0 in table order][optional hidden trailer (see F2)][n_region × 40-byte region table][36-byte trailer at EOF]

Evidence: mt76_connac_mcu.c mt76_connac2_load_ram() + mt76_connac2_send_ram() (region data streamed sequentially from offset 0; table and trailer located from EOF); mt76_connac_mcu.h struct mt76_connac2_fw_region / _fw_trailer. Confirmed by exact layout match on 8 RAM blobs (sum(region.len) + table + trailer (+ hidden trailer) == file size).

Region fields: decomp_crc/decomp_len/decomp_blk_sz (MCU-side decompression, unused when decomp_len==0 — all observed blobs), addr, len, feature_set, type.

F2 — Hidden second trailer carries the full build string

Between region data and the kernel-parsed table sits a trailer the upstream loader never reads. WM blobs: 100 bytes; WA blobs: 36 bytes (no string). Observed family-wide (all 4 chips).

WM hidden trailer = 16×'#' + 40 00 00 00 + 1-byte string length + flags + full build string + '#'-padding. Recovered strings:

  • mt7981_wm: t-neptune-main-mt7915-1953-MT7981_MP2111_IMP-20240823161204
  • mt7916_wm: t-neptune-main-mt7915-1953-MT7916_MP2111_IMP-20240823170147
  • mt7986_wm: t-neptune-main-mt7915-1953-MT7986_MP2111_IMP-20240823160608
  • mt7915_wm: t-neptune-mp-mt7915-2045-MT7915_MP_7_4_2045-20220929103802

Significance: the kernel-visible trailer's fw_ver[10] is stripped (____000000 WM / DEV_000000 WA); the hidden trailer restores branch ("main"/"mp"), internal build number (1953/2045), MP tag, and build timestamp. This is the version metadata the changelog tooling keys on.

Evidence: bytes at offset sum(region.len) in each blob (see manifest.json → hidden_trailer.raw_head).

F3 — region type is a content tag used by some loaders

All parsed mt79-family blobs: type == 0 (FW_TYPE_DEFAULT). It is not ignored upstream: mt7921's loader selects feature_set & FW_FEATURE_NON_DL && type == FW_TYPE_CLC regions as country/location-calibration data instead of downloading them to the MCU.

Evidence: mt7921/mcu.c CLC handling; FW_TYPE_CLC definition in mt76_connac_mcu.h.

F4 — Patch container format

92-byte big-endian header (16-byte build date, 4-byte platform tag — "ALPS" on all four — hw_sw_ver, patch_ver, checksum, descriptor), 64-byte BE section table, plaintext section data at explicit offsets. All observed patch sections: enc_type == 0 (plaintext).

Evidence: mt76_connac2_load_patch() + struct mt76_connac2_patch_hdr/_sec in mt76_connac_mcu.h; exact parse of 4 patch blobs.

Section counts: mt7915 patch = 2 sections, all others = 1; section type constant 0x30002 on every observed section (loader downloads all sections regardless; field semantics otherwise unobserved).

F5 — mt7981_rom_patch internal structure (first disassembly, 2026-08-20)

The 9.7KB patch section (downloaded to 0x00900000) is a boot-ROM function replacement table + code + strings, little-endian NDS32:

offset content
0x000 0x000003ff, 0
0x008 10 LE pointers into boot ROM (0x00801xxx–0x0082bxxx)
0x108 10 LE pointers into patch RAM (0x009002xx–0x009012xx) — replacements
~0x1e0 NDS32 code (Ghidra: 49 functions + entry marker)
0x901000+ string table

Decompilation is viable end-to-end (Ghidra 12 NDS32:LE:32, official module; our ELF imports directly — deferred acceptance check now PASSED). Evidence: ghidra-proj/patch_decomp.txt (unpublished, blob-derived).

Verified observations:

  • Assert anchors: patch/wf/wm/sys_patch_mcu.c (lines 0x426, 0x5de...), common/sys_patch_common_mcu.c (0x20f, 0x248...) via ROM func_0x008004b8.
  • 20+ distinct direct calls into boot ROM (func_0x0080xxxx) + GP-relative indirect calls (unaff_gp - 0x116xx) — ROM provides the runtime library.
  • Named-entry strings: MCU_Patch_init, ENTRY_wsysMboxSendMsg, ENTRY_wsysMboxRcvAllMsg (mailbox IPC), From_CCIF__host_cpu_sw_interrupt (CCIF = host↔MCU channel), WDT_to_Host / WDT_to_N9 (confirms the MCU core is called N9), Patch_dic_handler_extend, WF_Lt_Sec_handler.
  • Diagnostics suite strings: AXI_Bus_monitor_detect, APB_AHB_bus_timeout, IDLM_monitor, CPU_UTLZ_CNT_* (utilization counters), cache_miss_ratio, register-dump prints (0x8800_0430 etc.).
  • FUN_009002a6: bus-register writer — LE dword writes to offsets 0x110/0x114/0x118/0x11c on bus 5, RMW | 0x400000c0, busy-poll bit 0x40000000. FUN_0090044e: bounded delay loop via ROM timer reads (func_0x00801e20/func_0x00801e16).

Interpretation (labelled): the patch extends boot ROM with host-comm hooks (CCIF interrupt, mailboxes, watchdog-to-host) and bus diagnostics, plus download-plumbing register programming — the glue the ROM needs before WM/WA firmware arrives.

F6 — mt7981_wa survey (2026-08-20)

Ghidra project wa imported + analyzed: 120 functions, 450 strings. Strings self-identify WA's role — host command handling and TX bookkeeping (MCU_EXT_CMD protocol), matching the driver's separate WA MCU queue:

  • cmdEventParserCmd with ucCID printing; EXT_CMD_ID_STAREC_UPDATE, DevInfo Update Command, BssInfo Update Command (own-MAC/BSS record management), staRec with invalid wandidx (station records)
  • Per-STA loss accounting: PKTLOSS[%d]times/cnt[tot_tx,drop_tx,seq], lost seq, dup seq, dlycnt/maxdly, plus [proto,port,src_ip,dest_ip] flow dump formatting

Next: match MCU_EXT_CMD_* enum ids (mt76 headers) to cmdEventParserCmd dispatch — the ABI anchor map (P1-C/D).

Unknowns registry

  • U1 — feature_set bit 7 (0x80): observed only on WM regions at 0xf0xxxxxx addresses (mt7981 r7–r10, mt7986 equivalents). Upstream defines bits 0–6 only; vendor-SDK search found no definition (2026-08-20). Correlation with address family noted; semantics undetermined. May relate to a second download destination or content class.
  • U2 — hidden-trailer non-string fields: the 40 00 00 00 and flag bytes before/around the string (F2) are uninterpreted.
  • U3 — WO container: mt7981_wo.bin uses the mtk_wed loader (mtk_wed_mcu.c), a different format; not yet parsed.
  • NDS32LE target: GCC: (2018-10-23_nds32le-elf-mculib-v3) 4.9.4 string in mt7981_wa region data; FreeRTOS v10.0.0 + portable/GCC/NDS32/port.c strings in mt7981_wo.
  • No per-region encryption in any observed blob (FW_FEATURE_SET_ENCRYPT clear; patch enc_type == 0). Optional protocol support exists in the loader (key from EFUSE path) but is not exercised by shipped images.