#!/usr/bin/env python3 """RK3588 DDR Blob Patcher v3 — counted-loop trampolines with SITE SUBSET. v2 BUG identified 2026-04-15: the original patcher copied at most ONE body instruction beyond the LDR into the trampoline. For poll patterns of form `LDR; AND; CMP; B.cond` (5 of the 16 sites), the CMP was dropped. The trampoline's `B.inv_cond` then tested stale flags → random branch → brick. v3 fix: copy the **full** original loop body (LDR + every intermediate instruction, in original order) into the trampoline, so the last flag-setting instruction matches the original loop. New trampoline layout (N = len(body) — usually 2 or 3): +0: MOV W16, #TIMEOUT +4 .. +4N:
; LDR + all test instructions +4N+4: B.inv_cond .done +4N+8: SUBS W16, W16, #1 +4N+12: B.NE .retry (back to +4) +4N+16: B return_addr ; timeout path .done: +4N+20: B return_addr ; success path Size per trampoline: 4 * (N + 5) bytes. With max body of 3 instructions that's 4*8 = 32 bytes (same as v2); 2-inst bodies take 28 bytes. Site subset flags: --sites all | early | mid | late | none --sites 0,3,5-7 # explicit index list Site index is stable (determined by find_poll_loops' ascending-offset ordering). As of rk3588_ddr_v1.19: 0..7 EARLY cluster (0x07b78..0x07f08) — SGRF + PHY firmware fsm 8..10 MID cluster (0x09124..0x0aaf8) — DfiStatus / training start 11..15 LATE cluster (0x0d154..0x0d378) — UctWriteProt / CalBusy / late """ import struct import sys import argparse TIMEOUT_ITERATIONS = 0x4000 COUNTER_REG = 16 COND_NAMES = ['EQ','NE','CS','CC','MI','PL','VS','VC', 'HI','LS','GE','LT','GT','LE','AL','NV'] CLUSTERS = { 'early': list(range(0, 8)), 'mid': list(range(8, 11)), 'late': list(range(11, 16)), 'all': list(range(0, 16)), 'none': [], } def encode_movz(rd, imm16, shift=0): hw = shift // 16 return 0x52800000 | (hw << 21) | (imm16 << 5) | rd def encode_subs_imm(rd, rn, imm12): return 0x71000000 | (imm12 << 10) | (rn << 5) | rd def encode_b(from_offset, to_offset): delta = (to_offset - from_offset) // 4 if delta < 0: delta = delta & 0x3FFFFFF return 0x14000000 | (delta & 0x3FFFFFF) def encode_bcond(cond, from_offset, to_offset): delta = (to_offset - from_offset) // 4 imm19 = delta & 0x7FFFF return 0x54000000 | (imm19 << 5) | cond def encode_bne(from_offset, to_offset): return encode_bcond(1, from_offset, to_offset) def invert_cond(cond): return cond ^ 1 def find_poll_loops(blob): """Find tight backward-branch poll loops of form