Files
rk3588-ddr-analysis/patch_timeouts.py
T
test0r 816848a474 RK3588 DDR init blob reverse engineering
- Ghidra decompilation of v1.02-v1.19 blobs (118 functions)
- 53 functions renamed, 79 MMIO registers mapped to TRM
- 45 timeout-less poll loops identified and patched
- Production patcher (patch_prod.py) and QEMU emulator
- Comprehensive analysis, frequency tables, community research

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 13:06:47 +02:00

49 lines
1.9 KiB
Python

#!/usr/bin/env python3
"""RK3588 DDR Blob Patcher - converts infinite poll loops to single checks."""
import struct, os
def patch_blob(inpath, outpath):
with open(inpath, 'rb') as f:
blob = bytearray(f.read())
patched = 0
patches = []
NOP = 0xD503201F
for i in range(0, len(blob) - 12, 4):
inst = struct.unpack_from('<I', blob, i)[0]
if (inst & 0xFF000010) == 0x54000000:
imm19 = (inst >> 5) & 0x7FFFF
if imm19 & 0x40000:
offset = -((~imm19 & 0x7FFFF) + 1) * 4
if -16 <= offset <= -4:
loop_start = i + offset
has_load = False
for j in range(loop_start, i, 4):
w = struct.unpack_from('<I', blob, j)[0]
if (w & 0xFFC00000) in (0xB9400000, 0xF9400000, 0xB9800000):
has_load = True
break
if has_load:
cond = inst & 0xF
cond_names = ['EQ','NE','CS','CC','MI','PL','VS','VC','HI','LS','GE','LT','GT','LE','AL','NV']
old = struct.unpack_from('<I', blob, i)[0]
struct.pack_into('<I', blob, i, NOP)
patches.append((i, old, cond_names[cond], offset))
patched += 1
with open(outpath, 'wb') as f:
f.write(blob)
print(f'Patched {patched} tight poll loops:')
for addr, old, cond, offset in patches:
print(f' 0x{addr:05x}: B.{cond} {offset} -> NOP')
return patched, len(blob)
infile = '/opt/rkbin/bin/rk35/rk3588_ddr_lp4_2112MHz_lp5_2400MHz_v1.19.bin'
outfile = '/opt/work/rk3588_ddr_v1.19_patched.bin'
n, size = patch_blob(infile, outfile)
orig_size = os.path.getsize(infile)
print(f'\nOriginal: {orig_size}, Patched: {size} ({"MATCH" if orig_size == size else "MISMATCH!"})')