From a93ce6c3e9f4fe1beb4eb1dea97f3b39ee867959 Mon Sep 17 00:00:00 2001 From: Markus Fritsche Date: Fri, 3 Apr 2026 23:16:32 +0200 Subject: [PATCH] Add instrumented MMIO tracer and first trace Unicorn-based tracer captures every MMIO read/write with PC and instruction count. First trace of trampoline blob: 19 MMIO accesses in 200K instructions. Boot sequence: PMU_GRF read -> SRAM flag -> SRAM self-register -> BUS_GRF QoS -> DDRC reset -> SCRU PLL config -> BUS_GRF route -> polls Co-Authored-By: Claude Opus 4.6 (1M context) --- mmio_trace.csv | 20 +++++++ trace_mmio.py | 142 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+) create mode 100644 mmio_trace.csv create mode 100644 trace_mmio.py diff --git a/mmio_trace.csv b/mmio_trace.csv new file mode 100644 index 0000000..ad87cc1 --- /dev/null +++ b/mmio_trace.csv @@ -0,0 +1,20 @@ +instr,op,addr,register,value,pc +13,R,0xFD588080,GRF+0x8080,0x00000000,0x109A0 +17,R,0xFF000010,SRAM+0x10,0x00000000,0x109B0 +39,W,0xFF016F58,SRAM+0x16F58,0x-5640EC1FEBFFFFFF,0x00B14 +45,W,0xFF016F60,SRAM+0x16F60,0x58000164A9BF7BFD,0x00B14 +51,W,0xFF016F68,SRAM+0x16F68,0x-6D87A3FF6BFFFFF9,0x00B14 +57,W,0xFF016F70,SRAM+0x16F70,0x54000001EB04001F,0x00B14 +77,R,0xFF000010,SRAM+0x10,0x00000000,0x009A8 +84,W,0xFD5F8098,BUS_GRF+0x8098,0xFF005500,0x009C4 +87,W,0xFE0100F0,DDRC+0xF0,0x00000000,0x009D0 +88,W,0xFE0100F4,DDRC+0xF4,0x00000000,0x009D4 +89,W,0xFE0100F8,DDRC+0xF8,0x00000000,0x009D8 +90,W,0xFE0100FC,DDRC+0xFC,0x00000000,0x009DC +109,W,0xFD8C8004,SCRU+0x8004,0x00000000,0x10A8C +114,W,0xFD8C8014,SCRU+0x8014,0xFFFFFFFF,0x10AA0 +115,W,0xFD8C8018,SCRU+0x8018,0xFFFFFFFF,0x10AA4 +118,W,0xFD8C8008,SCRU+0x8008,0x00000000,0x10AB0 +121,W,0xFD8C8004,SCRU+0x8004,0x00000001,0x10ABC +151,W,0xFD5F4000,BUS_GRF+0x4000,0x0FF00880,0x00660 +154,W,0xFD5F800C,BUS_GRF+0x800C,0x0FF00AA0,0x0066C diff --git a/trace_mmio.py b/trace_mmio.py new file mode 100644 index 0000000..7aee914 --- /dev/null +++ b/trace_mmio.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python3 +""" +RK3588 DDR Blob Instrumented Tracer +Logs every MMIO read/write with full register context. +Builds a map of: which function accesses which registers in what order. +""" +import struct, sys, json +from unicorn import * +from unicorn.arm64_const import * + +MMIO_RANGES = [ + (0xFD580000, 0xFD5A0000, "GRF"), + (0xFD5F0000, 0xFD600000, "BUS_GRF"), + (0xFD8C0000, 0xFD8D0000, "SCRU"), + (0xFE010000, 0xFE030000, "DDRC"), + (0xFE030000, 0xFE040000, "FW_DDR"), + (0xFE050000, 0xFE060000, "SGRF"), + (0xFE0C0000, 0xFE100000, "DDRPHY"), + (0xFECC0000, 0xFECD0000, "SCRAMBLE"), + (0xFF000000, 0xFF100000, "SRAM"), +] + +def region_name(addr): + for lo, hi, name in MMIO_RANGES: + if lo <= addr < hi: + return f"{name}+0x{addr-lo:X}" + return f"0x{addr:X}" + +def run_trace(blob_path, max_instr=200000, outfile=None): + with open(blob_path, 'rb') as f: + blob = f.read() + + trace = [] + count = [0] + last_pc = [0] + + uc = Uc(UC_ARCH_ARM64, UC_MODE_ARM) + for base in range(0, 0x100000000, 0x10000000): + try: uc.mem_map(base, 0x10000000, UC_PROT_ALL) + except: pass + uc.mem_write(0, blob) + uc.reg_write(UC_ARM64_REG_SP, 0x00110000 - 16) + + # Pre-seed ready values + for addr, val in [(0xFE0500E0, 0), (0xFE050054, 1)]: + uc.mem_write(addr, struct.pack('= max_instr: + uc_inst.emu_stop() + + def on_intr(uc_inst, intno, data): + pc = uc_inst.reg_read(UC_ARM64_REG_PC) + uc_inst.reg_write(UC_ARM64_REG_PC, pc + 4) + + uc.hook_add(UC_HOOK_MEM_READ, on_mem_read) + uc.hook_add(UC_HOOK_MEM_WRITE, on_mem_write) + uc.hook_add(UC_HOOK_CODE, on_code) + uc.hook_add(UC_HOOK_INTR, on_intr) + + try: + uc.emu_start(0x10978, len(blob), timeout=60*1000000) + except: + pass + + pc = uc.reg_read(UC_ARM64_REG_PC) + print(f"Executed {count[0]} instructions, final PC=0x{pc:x}") + print(f"Captured {len(trace)} MMIO accesses") + + # Summary + reads = [t for t in trace if t['op'] == 'R'] + writes = [t for t in trace if t['op'] == 'W'] + print(f" Reads: {len(reads)}, Writes: {len(writes)}") + + # Group by register + reg_counts = {} + for t in trace: + r = t['reg'] + if r not in reg_counts: + reg_counts[r] = {'R': 0, 'W': 0} + reg_counts[r][t['op']] += 1 + + print(f"\nMMIO Register Access Summary:") + print(f"{'Register':<30s} {'Reads':>6s} {'Writes':>7s}") + print("-" * 45) + for reg in sorted(reg_counts.keys()): + c = reg_counts[reg] + print(f"{reg:<30s} {c['R']:>6d} {c['W']:>7d}") + + # Write full trace + if outfile: + with open(outfile, 'w') as f: + f.write("instr,op,addr,register,value,pc\n") + for t in trace: + f.write(f"{t['instr']},{t['op']},{t['addr']},{t['reg']},{t['val']},{t['pc']}\n") + print(f"\nFull trace written to {outfile}") + + # Show first 30 accesses + print(f"\nFirst 30 MMIO accesses:") + for t in trace[:30]: + print(f" [{t['instr']:6d}] {t['op']} {t['reg']:<25s} = {t['val']} (PC={t['pc']})") + + return trace + +if __name__ == '__main__': + blob = sys.argv[1] if len(sys.argv) > 1 else '/opt/work/rk3588_ddr_v1.19_trampoline.bin' + out = sys.argv[2] if len(sys.argv) > 2 else '/opt/work/mmio_trace.csv' + run_trace(blob, max_instr=200000, outfile=out)