106 lines
3.9 KiB
Python
106 lines
3.9 KiB
Python
|
|
"""Golden-manifest tests for the Connac2 container extractor.
|
||
|
|
|
||
|
|
Runs against whatever MediaTek blobs exist on this host (skip otherwise).
|
||
|
|
Golden values observed from linux-firmware snapshot 2026-04-20; when a value
|
||
|
|
here fails against a newer snapshot, the blob changed — update the golden
|
||
|
|
value *and* note it in the changelog, don't silently absorb it.
|
||
|
|
"""
|
||
|
|
import subprocess
|
||
|
|
import sys
|
||
|
|
import unittest
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
REPO = Path(__file__).resolve().parent.parent
|
||
|
|
FW = Path('/lib/firmware/mediatek')
|
||
|
|
|
||
|
|
# filename -> (chip_id, eco, n_region, hidden_trailer_string_or_None)
|
||
|
|
GOLDEN_WM = {
|
||
|
|
'mt7981_wm.bin': (0x14, 0, 11,
|
||
|
|
't-neptune-main-mt7915-1953-MT7981_MP2111_IMP-20240823161204'),
|
||
|
|
'mt7986_wm.bin': (0x0f, 0, 11,
|
||
|
|
't-neptune-main-mt7915-1953-MT7986_MP2111_IMP-20240823160608'),
|
||
|
|
'mt7916_wm.bin': (0x13, 0, 9,
|
||
|
|
't-neptune-main-mt7915-1953-MT7916_MP2111_IMP-20240823170147'),
|
||
|
|
'mt7915_wm.bin': (0x0b, 1, 7,
|
||
|
|
't-neptune-mp-mt7915-2045-MT7915_MP_7_4_2045-20220929103802'),
|
||
|
|
}
|
||
|
|
GOLDEN_WA = {
|
||
|
|
'mt7981_wa.bin': (0x00, 0, 3, None),
|
||
|
|
'mt7986_wa.bin': (0x00, 0, 3, None),
|
||
|
|
'mt7916_wa.bin': (0x00, 0, 3, None),
|
||
|
|
'mt7915_wa.bin': (0x00, 1, 3, None),
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
def parse(path: Path, out: Path) -> dict:
|
||
|
|
out.mkdir(parents=True, exist_ok=True)
|
||
|
|
sys.path.insert(0, str(REPO / 'tools'))
|
||
|
|
import mtk_fw_extract as x
|
||
|
|
return (x.carve_patch if 'patch' in path.name else x.carve_ram)(path, out)
|
||
|
|
|
||
|
|
|
||
|
|
class LayoutClosure(unittest.TestCase):
|
||
|
|
"""sum(region.len) + hidden trailer + table + trailer == file size."""
|
||
|
|
|
||
|
|
def test_closure(self):
|
||
|
|
blobs = sorted(FW.glob('mt79*_wm.bin')) + sorted(FW.glob('mt79*_wa.bin'))
|
||
|
|
if not blobs:
|
||
|
|
self.skipTest('no mediatek blobs on this host')
|
||
|
|
import struct
|
||
|
|
T = struct.Struct('<5B2s10s15sI')
|
||
|
|
R = struct.Struct('<III4sIIBB14s')
|
||
|
|
for p in blobs:
|
||
|
|
with self.subTest(blob=p.name):
|
||
|
|
d = p.read_bytes()
|
||
|
|
n = d[len(d) - T.size + 2]
|
||
|
|
tbl = len(d) - T.size - n * R.size
|
||
|
|
data_end = sum(R.unpack_from(d, tbl + i * R.size)[5]
|
||
|
|
for i in range(n))
|
||
|
|
self.assertEqual(
|
||
|
|
data_end + (tbl - data_end) + n * R.size + T.size, len(d),
|
||
|
|
'layout does not close')
|
||
|
|
|
||
|
|
|
||
|
|
class GoldenRAM(unittest.TestCase):
|
||
|
|
|
||
|
|
def run_one(self, name, golden, outdir):
|
||
|
|
p = FW / name
|
||
|
|
if not p.exists():
|
||
|
|
self.skipTest(f'{name} not present')
|
||
|
|
info = parse(p, outdir)
|
||
|
|
chip, eco, n, hidden = golden
|
||
|
|
self.assertEqual(info['chip_id'], chip, name)
|
||
|
|
self.assertEqual(info['eco'], eco, name)
|
||
|
|
self.assertEqual(info['n_region'], n, name)
|
||
|
|
ht = info.get('hidden_trailer', {})
|
||
|
|
self.assertEqual(ht.get('string'), hidden, name)
|
||
|
|
|
||
|
|
def test_wm(self):
|
||
|
|
for name, golden in GOLDEN_WM.items():
|
||
|
|
with self.subTest(blob=name):
|
||
|
|
self.run_one(name, golden, REPO / 'extracted' / 'test_wm')
|
||
|
|
|
||
|
|
def test_wa(self):
|
||
|
|
for name, golden in GOLDEN_WA.items():
|
||
|
|
with self.subTest(blob=name):
|
||
|
|
self.run_one(name, golden, REPO / 'extracted' / 'test_wa')
|
||
|
|
|
||
|
|
|
||
|
|
class GoldenPatch(unittest.TestCase):
|
||
|
|
|
||
|
|
def test_patch(self):
|
||
|
|
golden = {'mt7981_rom_patch.bin': 1, 'mt7986_rom_patch.bin': 1,
|
||
|
|
'mt7916_rom_patch.bin': 1, 'mt7915_rom_patch.bin': 2}
|
||
|
|
for p in sorted(FW.glob('mt79*_rom_patch.bin')):
|
||
|
|
with self.subTest(blob=p.name):
|
||
|
|
info = parse(p, REPO / 'extracted' / 'test_patch')
|
||
|
|
self.assertEqual(info['platform'], 'ALPS')
|
||
|
|
self.assertEqual(info['n_region'], golden[p.name])
|
||
|
|
for sec in info['sections']:
|
||
|
|
self.assertEqual(sec['type'], '0x30002')
|
||
|
|
self.assertEqual(sec['enc_type'], 0) # plaintext (F4)
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == '__main__':
|
||
|
|
unittest.main(verbosity=2)
|