diff --git a/blob_emu.py b/blob_emu.py index d870819..6cd9988 100755 --- a/blob_emu.py +++ b/blob_emu.py @@ -9,8 +9,12 @@ import argparse, sys from unicorn import * from unicorn.arm64_const import * -BLOB_BASE = 0x00000000 -BLOB_SIZE = 0x00200000 # 2 MB slot +# The blob is position-dependent: it expects to be loaded at 0xFF001000 +# (the RK3588 bootrom's TPL SRAM slot). An integrity check at 0x14-0x1c +# ANDs the BL return address with 0xFFFFFF00 and compares to 0xFF001000. +SRAM_BASE = 0xFF000000 +SRAM_SIZE = 0x00100000 # 1 MB SRAM window (covers 0xFF000000..0xFF100000) +BLOB_BASE = 0xFF001000 # where the blob actually lives STACK_BASE = 0x00400000 STACK_SIZE = 0x00100000 RET_STUB = 0x00800000 @@ -27,7 +31,32 @@ MMIO = [ (0xFE0C0000, 0x00040000, "DDRPHY"), (0xFE400000, 0x00010000, "PMU"), (0xFECC0000, 0x00010000, "SCRAMBLE"), - (0xFF000000, 0x00100000, "SRAM_BOOT"), + # SRAM_BOOT (0xFF000000..0xFF100000) is regular RWX memory where + # the blob lives, NOT MMIO — not listed here. +] + +# Per-address stub values — ported from ddr_emu2.c. +# Each entry: address → value to return on read. +# Use base + offset; the MMIO read hook checks exact match first, +# then falls back to region-specific patterns, then to the constant stub. +ABS_STUB = { + # SGRF (0xFE050000) + 0xFE0500E0: 0x00000000, # status = ready + 0xFE050054: 0x00000001, # CON21 = done + 0xFE0500E4: 0x00000000, # enable +} +# Region-offset patterns: (region_base, region_end, offset_mask, offset_value, return_value) +REGION_OFF = [ + # DDRPHY (0xFE0C0000..0xFE100000), offset masked to lower 12 bits + (0xFE0C0000, 0xFE100000, 0xFFF, 0xA24, 0x00000002), # DfiStatus ready + (0xFE0C0000, 0xFE100000, 0xFFF, 0x684, 0x00000000), # CalBusy idle + (0xFE0C0000, 0xFE100000, 0xFFF, 0x090, 0x00000000), # MicroContMux + (0xFE0C0000, 0xFE100000, 0xFFF, 0x080, 0x00000000), # MicroReset done + (0xFE0C0000, 0xFE100000, 0xFFF, 0x514, 0x00000000), # training done +] +# Region constants: entire region returns this by default +REGION_CONST = [ + (0xFD8C0000, 0xFD8D0000, 0x00000001), # SCRU — PLL locked everywhere ] def run(blob_path, stub_byte, max_insn, trace, entry=0): @@ -35,8 +64,8 @@ def run(blob_path, stub_byte, max_insn, trace, entry=0): print(f"blob: {blob_path} size=0x{len(blob):x} stub=0x{stub_byte:02x}") uc = Uc(UC_ARCH_ARM64, UC_MODE_ARM) - uc.mem_map(BLOB_BASE, BLOB_SIZE, UC_PROT_ALL) - uc.mem_write(BLOB_BASE, blob) + uc.mem_map(SRAM_BASE, SRAM_SIZE, UC_PROT_ALL) # SRAM window + uc.mem_write(BLOB_BASE, blob) # blob at 0xFF001000 uc.mem_map(STACK_BASE, STACK_SIZE, UC_PROT_ALL) uc.mem_map(RET_STUB, RET_SIZE, UC_PROT_ALL) uc.mem_write(RET_STUB, b"\x00\x00\x20\xd4") # brk #0 @@ -65,14 +94,48 @@ def run(blob_path, stub_byte, max_insn, trace, entry=0): if trace and state["count"] % 10000 == 0: print(f"[{state['count']}] PC=0x{addr:x}") - def hook_mmio_read(uc, typ, addr, size, val, ud): - v = stub_word & ((1 << (size * 8)) - 1) - uc.mem_write(addr, v.to_bytes(size, "little")) + def stub_value(addr): + # Exact-address overrides first + if addr in ABS_STUB: + return ABS_STUB[addr] + # Region + offset patterns + for rbase, rend, mask, off_val, ret_val in REGION_OFF: + if rbase <= addr < rend and (addr & mask) == off_val: + return ret_val + # Whole-region constants + for rbase, rend, ret_val in REGION_CONST: + if rbase <= addr < rend: + return ret_val + # Fall through to constant-byte stub + return stub_word - uc.hook_add(UC_HOOK_CODE, hook_code, begin=BLOB_BASE, end=BLOB_BASE + BLOB_SIZE) + mmio_reads = [] + def hook_mmio_read(uc, typ, addr, size, val, ud): + v = stub_value(addr) & ((1 << (size * 8)) - 1) + uc.mem_write(addr, v.to_bytes(size, "little")) + if len(mmio_reads) < 50: + mmio_reads.append((uc.reg_read(UC_ARM64_REG_PC), addr, v)) + + uc.hook_add(UC_HOOK_CODE, hook_code, begin=SRAM_BASE, end=SRAM_BASE + SRAM_SIZE) for base, sz, _ in MMIO: uc.hook_add(UC_HOOK_MEM_READ, hook_mmio_read, begin=base, end=base + sz) + # Catch any unmapped read/write and log what the blob wanted + unmapped = [] + def hook_unmapped(uc, typ, addr, size, val, ud): + pc = uc.reg_read(UC_ARM64_REG_PC) + unmapped.append((pc, typ, addr, size, val)) + # Lazily map a 64KB page containing the address and stub it + page = addr & ~0xFFFF + try: + uc.mem_map(page, 0x10000, UC_PROT_ALL) + except UcError: + pass + v = stub_value(addr) & ((1 << (size * 8)) - 1) + uc.mem_write(addr, v.to_bytes(size, "little")) + return True # tell Unicorn to continue + uc.hook_add(UC_HOOK_MEM_UNMAPPED, hook_unmapped) + uc.reg_write(UC_ARM64_REG_SP, STACK_BASE + STACK_SIZE - 16) uc.reg_write(UC_ARM64_REG_X30, RET_STUB) @@ -84,6 +147,13 @@ def run(blob_path, stub_byte, max_insn, trace, entry=0): return 1 print(f"HALT insns={state['count']} max_pc=0x{state['max_pc']:x}") + print(f"--- first MMIO reads ({len(mmio_reads)}) ---") + for pc, addr, v in mmio_reads[:20]: + print(f" PC=0x{pc:x} RD 0x{addr:x} -> 0x{v:x}") + if unmapped: + print(f"--- unmapped accesses ({len(unmapped)}) ---") + for pc, typ, addr, size, val in unmapped[:20]: + print(f" PC=0x{pc:x} type={typ} 0x{addr:x} size={size}") return 0 if __name__ == "__main__":