#!/usr/bin/env python3 """Characterise the AMD "F32" microcode (SDMA, RLC) that has no public disassembler. This exists so the "F32 is a blocker" row in FIRMWARE-COVERAGE-MATRIX.md rests on a measurement rather than an inherited claim. It reports, for each image: * container handling, including the nested $PS1 PSP header (code starts at +0x200) * the most common 32-bit words -- the NOP encoding shows up here * per-bit entropy across the word, to look for a low-entropy opcode field * how many distinct values the top 4..8 bits take, i.e. whether an opcode field of that width is plausible * capstone coverage across every architecture it supports, as a negative control Interpretation notes (2026-09 run on Navi 31 blobs): SDMA `sdma_6_0_0.bin` NOP 0x90000000 (x132), top-4 bits take all 16 values RLC `gc_11_0_0_rlc.bin` NOP 0x90000000 (x191), top-4 bits take all 16 values No capstone architecture fits: the best score is Thumb at 98%, which is meaningless because Thumb decodes almost any byte string. So: fixed-width 32-bit, a NOP of 0x90000000, and no known ISA -- consistent with the tree's long-standing "AMD proprietary F32, no public disassembler" call, now verified rather than assumed. Deriving the encoding would need paired execution traces, which is a project in itself. usage: f32_probe.py [ ...] """ import collections import math import struct import sys try: import capstone as cs except ImportError: cs = None ARCHES = [('riscv64', 'CS_ARCH_RISCV', 'CS_MODE_RISCV64'), ('arm', 'CS_ARCH_ARM', 'CS_MODE_ARM'), ('thumb', 'CS_ARCH_ARM', 'CS_MODE_THUMB'), ('xtensa', 'CS_ARCH_XTENSA', 'CS_MODE_XTENSA_ESP32'), ('mips32el', 'CS_ARCH_MIPS', 'CS_MODE_MIPS32'), ('ppc32', 'CS_ARCH_PPC', 'CS_MODE_32'), ('sparc', 'CS_ARCH_SPARC', None), ('tricore', 'CS_ARCH_TRICORE', None), ('sh', 'CS_ARCH_SH', None), ('m68k', 'CS_ARCH_M68K', None), ('hppa', 'CS_ARCH_HPPA', None), ('alpha', 'CS_ARCH_ALPHA', None)] def payload(path): d = open(path, 'rb').read() ucode_size, ucode_off = struct.unpack_from('> bit & 1) p = ones / n out.append(0.0 if p in (0.0, 1.0) else -(p * math.log2(p) + (1 - p) * math.log2(1 - p))) return out def main(): for path in sys.argv[1:]: b, nested = payload(path) words = [struct.unpack_from('> (32 - width) for w in words) print(' top-%d bits: %d/%d distinct values, most common %s' % ( width, len(top), 1 << width, ' '.join('0x%x(x%d)' % kv for kv in top.most_common(3)))) if cs is None: print(' (capstone unavailable; skipping the negative control)') continue nz = next((i for i in range(0, max(1, len(b) - 63), 4) if b[i:i + 64] != bytes(64)), 0) rows = [] for name, arch_n, mode_n in ARCHES: arch = getattr(cs, arch_n, None) if arch is None: continue mode = getattr(cs, mode_n, 0) if mode_n else 0 if name == 'mips32el': mode |= cs.CS_MODE_LITTLE_ENDIAN try: md = cs.Cs(arch, mode) except Exception: # noqa: BLE001 continue md.skipdata = True cov = tot = 0 for x in md.disasm(b[nz:nz + 0x2000], 0): tot += x.size if x.mnemonic not in ('.byte', '.db', 'unimp', 'c.unimp', 'ill', 'illegal', 'undefined'): cov += x.size rows.append((cov / max(1, tot), name)) print(' capstone coverage: %s' % ', '.join( '%s=%.0f%%' % (n, 100 * c) for c, n in sorted(rows, reverse=True)[:6])) print(' NOTE: a high Thumb score is not evidence -- Thumb decodes almost ' 'anything. Only a coherent, structured decode counts.') if __name__ == '__main__': main()