blob_emu: position-correct load + integrity-check pass + lazy mmap
The blob is position-dependent: entry code at 0x14-0x1c does (return_addr & 0xFFFFFF00) == 0xFF001000 to validate the caller. Previously we loaded at 0x0, so the check could never pass and emu hung at 0x1c forever. Fixed: load blob at 0xFF001000 (bootrom TPL slot), map SRAM window 0xFF000000..0xFF100000, let x30 point at RET_STUB outside the window. Emu now runs through the integrity check, the first ~120 instructions of entry dispatch, and stops at blob+0x10A80 on an MSR/MRS sysreg access Unicorn doesn|t model -- the same depth ddr_emu2.c (C version) historically reached. Stock and patched (--sites all) behave identically under both --stub 0x00 and 0xFF. That|s the regression gate: any future patch that crashes in the pre-sysreg segment will diverge. Also added catch-all UC_HOOK_MEM_UNMAPPED with lazy 64KB page-map + stub fallback so unknown MMIO targets don|t crash the emu before we know about them.
This commit is contained in:
+79
-9
@@ -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__":
|
||||
|
||||
Reference in New Issue
Block a user