mtk-wifi-fw/docs/format.md

4.1 KiB
Raw Blame History

MediaTek Connac2 WiFi firmware container format

Scope: mt7915/7916/7981/7986_{wm,wa,rom_patch}.bin as shipped in linux-firmware. All field layouts are transcribed from drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h (structs mt76_connac2_fw_trailer, mt76_connac2_fw_region, mt76_connac2_patch_hdr, mt76_connac2_patch_sec); loader behavior from mt76_connac_mcu.c (mt76_connac2_load_patch, mt76_connac2_load_ram, mt76_connac2_send_ram). Verified against 54 historical blob revisions (dataset/history.jsonl).

RAM images (*_wm.bin, *_wa.bin) — little-endian

offset 0:  region data, packed back-to-back in region-table order
           (driver streams from offset 0, advancing by each region's len)
+sum(len): [optional] hidden second trailer — 100B on WM, 36B on WA
           (present on every observed revision; loader never reads it)
+gap:      region table: n_region × 40-byte records
end-36:    36-byte trailer

Region record (40 bytes)

off size field notes
0 4 decomp_crc CRC of decompressed data (unused when decomp_len==0)
4 4 decomp_len 0 = stored uncompressed (all observed revisions)
8 4 decomp_blk_sz MCU-side decompression block size
12 4 rsv
16 4 addr download destination (bus address)
20 4 len stored length == download length
24 1 feature_set bit0 ENCRYPT, bits1-2 KEY_IDX, bit4 ENCRY_MODE, bit5 OVERRIDE_ADDR, bit6 NON_DL, bit7: undefined upstream — see U1
25 1 type content tag: 0 DEFAULT everywhere in this family; mt7921 uses 2 = CLC (country/location data, paired with NON_DL)
26 14 rsv1

Observed feature_set bits in the wild: 0x20 (OVERRIDE_ADDR, WM region 0), 0x80 (unknown, all 0xf0xxxxxx regions), 0xc0 (NON_DL|0x80, zero-length entries). No observed blob sets ENCRYPT.

Trailer (36 bytes, at EOF)

off size field notes
0 1 chip_id 0x0b mt7915, 0x13 mt7916, 0x14 mt7981, 0x0f mt7986 (WM); 0x00 on WA
1 1 eco_code silicon eco
2 1 n_region
3 1 format_ver
4 1 format_flag
5 2 rsv
6 10 fw_ver[10] stripped by MTK: ____000000 (WM) / DEV_000000 (WA)
16 15 build_date[15] %Y%m%d%H%M%S ASCII
31 4 crc LE32

Hidden second trailer (not read by the upstream loader)

WM (100B): 16×'#', 40 00 00 00, 1-byte string length, flags, then the full build string t-neptune-<branch>-mt7915-<n>-<CHIP>_<MPTAG>_<MPVER>-<build-ts>, '#'-padded. WA (36B): no string. This restores the version metadata MTK strips from fw_ver — the key input to version diffing (tools/fw_history.py).

Observed build strings per revision: dataset/history.jsonl.

Patch images (*_rom_patch.bin) — big-endian

offset 0:    92-byte header
offset 92:   n_section × 64-byte section records
explicit:    section data at each section's `offs`

Header: build_date[16], platform[4] ("ALPS" on all observed), hw_sw_ver, patch_ver, checksum(u16), rsv(u16), descriptor (patch_ver, subsys, feature, n_section, crc, rsv[11]).

Section record: type, offs, size, info union (addr, len, key_idx/enc-mode, align_len). Observed: type == 0x30002 on every section; enc_type == 0 (plaintext); download addresses 0x00900000-range; section counts 1 (7916/7981/7986) and 2 (7915, one small + one data section).

WO images (mt7981_wo.bin, mt7986_wo_{0,1}.bin)

Different container, loaded by the ethernet-side WED driver (drivers/net/ethernet/mediatek/mtk_wed_mcu.c), not mt76. Not yet parsed (unknown U3). Firmware content: FreeRTOS v10.0 + NDS32 port, "coe/" source paths (string evidence).

Firmware ISA

NDS32 (Andes) 32-bit little-endian. Evidence: GCC: (2018-10-23_nds32le-elf-mculib-v3) 4.9.4 in mt7981_wa data; portable/GCC/NDS32/port.c in mt7981_wo. tools/mtk_fw_elf.py emits EM_NDS32(167) ET_EXEC ELF for Ghidra import.

Unknowns

U1 feature_set bit7 (0x80): correlated with 0xf0xxxxxx destinations; undefined upstream. U2 hidden-trailer non-string fields. U3 WO container format. See docs/findings.md.