test0r cd4d01fd69 Add trampoline patcher v5 - counted loop timeouts for all 45 polls
Each poll loop branches to an appended trampoline that:
- Initializes w18 = 0x20000 (128K iterations)
- Copies the original loop body (LDR + condition check)
- Decrements w18, retries until timeout
- Falls through on timeout (no hang)

QEMU verified: original stuck at 0x10350, trampoline progresses through all polls.
Blob grows from 76704 to 78068 bytes (+1364 bytes trampoline section).

NOT YET TESTED ON REAL HARDWARE - the NOP approach bricked the GenBook.
This counted approach preserves the poll loops with a safety timeout.

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

RK3588 DDR Init Blob — Reverse Engineering Project

n## Prerequisites

Patching (any platform)

Emulation (x86_64 only)

Decompilation (x86_64 only)

Cross-compilation tools (optional)

Decompilation, analysis, and patching of the closed-source Rockchip RK3588 DDR initialization binary blobs.

Quick Start

# Apply production patch to current blob
python3 patch_prod.py /path/to/rk3588_ddr_lp4_2112MHz_lp5_2400MHz_v1.19.bin output.bin

# Run QEMU emulation test (on x86 with unicorn)
python3 /opt/work/emu_test.py

# Build the C emulator (on x86 oppenheimer container)
gcc -O2 -o ddr_emu ddr_emu2.c -lunicorn -lm
./ddr_emu blob.bin 50000

Files

Decompiled Sources

File Description
ddr_decompiled.c Raw Ghidra decompilation (fast blob, 118 functions)
ddr_conservative_decompiled.c Raw decompilation (conservative blob)
ddr_annotated.c Human-readable annotated source (53 named functions, 79 named registers)
ddr_diff.txt Diff between fast and conservative (only 12 lines!)
ddr_fast_asm.s Full AArch64 disassembly (17,308 lines)
ddr_conservative_asm.s Conservative disassembly

Headers & Register Maps

File Description
rk3588_ddr.h Complete RK3588 DDR memory map (TRM Part 2 verified)
rk3588_regs_annotated.h All 79 MMIO registers with hardware block annotations

Patchers

File Description
patch_prod.py Production patcher — NOPs 40 non-critical polls, keeps 5 training loops
patch_timeouts.py Aggressive patcher — NOPs all 16 B.cond polls (analysis only)

Patched Blobs

File Patches Use
rk3588_ddr_v1.19_prod.bin 40 NOPs, 5 kept Production-ready
rk3588_ddr_v1.19_patched_v2.bin 45 NOPs (all) Analysis/QEMU testing

Analysis & Research

File Description
ANALYSIS.md Full technical analysis with register maps and version comparison
BUG_ANALYSIS.md Bug report, optimization opportunities, training explainer
DDR_FREQUENCY_TABLE.md All LPDDR5 frequencies from 2112-3200 MHz
COMMUNITY_RESEARCH.md 40+ sources on DDR training, Rockchip issues, community OC

Emulation

File Description
ddr_emu2.c Unicorn-based C emulator with MMIO stubs
Ghidra project On oppenheimer (CT131): /opt/work/ghidra_project/

Ghidra Export Scripts

File Description
ExportDecompiled.java Exports all functions as decompiled C
ExportAsm.java Exports full disassembly listing

QEMU Emulation Approach

Why QEMU Alone Doesn't Work

The DDR blob runs at EL3 (secure world) and accesses hardware-specific MMIO registers. Standard QEMU virt machine doesn't model RK3588 hardware, so:

  • All MMIO reads return 0 (unmapped memory)
  • System register writes (MSR VBAR_EL3, etc.) cause exceptions
  • The blob gets stuck on the very first register check

Solution: Unicorn Engine

We use the Unicorn CPU emulator (libcorn) which provides:

  • AArch64 instruction emulation without OS/machine model
  • Memory mapping API to create MMIO stub regions
  • Exception hooks to skip privileged instructions (MSR/MRS)
  • Code hooks for instruction counting and timeouts

Emulation Setup

Memory Map:
  0x00000000 - 0x0001FFFF  Blob code + data (128KB)
  0x00100000 - 0x0010FFFF  Stack (64KB)
  0x001F0000 - 0x001FFFFF  SRAM mailbox
  0xFD580000 - 0xFD59FFFF  GRF (pre-seeded with 0)
  0xFD5F0000 - 0xFD5FFFFF  BUS_GRF
  0xFD8C0000 - 0xFD8CFFFF  SCRU
  0xFE010000 - 0xFE02FFFF  DDRC
  0xFE030000 - 0xFE03FFFF  FW_DDR
  0xFE050000 - 0xFE05FFFF  SGRF (pre-seeded: STATUS=0, CON21=1)
  0xFE0C0000 - 0xFE0FFFFF  DDRPHY (pre-seeded: DfiStatus=2, CalBusy=0)
  0xFECC0000 - 0xFECCFFFF  DDR_SCRAMBLE
  0xFF000000 - 0xFF0FFFFF  SRAM_BOOT

Pre-seeded MMIO Values

Training-critical registers are pre-seeded with "ready" values:

  • SGRF_DDR_STATUS (0xFE0500E0) = 0 (ready)
  • SGRF_DDR_CON21 (0xFE050054) = 1 (done)
  • DfiStatus (PHY+0xA24) = 0x02 (DFI ready)
  • CalBusy (PHY+0x684) = 0x00 (not busy)
  • MicroContMuxSel (PHY+0x10090) = 0 (available)
  • MicroReset (PHY+0x10080) = 0 (reset complete)
  • UctWriteProtShadow (PHY+0x10514) = 0 (training done)

Exception Handling

The hook_intr callback skips MSR/MRS/cache instructions by advancing PC+4. This allows the blob to execute through privileged setup code without implementing full EL3 register emulation.

Results

Blob Instructions Final PC Behavior
Original 500K (limit) 0x10350 Stuck in TBZ poll loop
Patched (all NOP) 500K (limit) 0x09124 Progressed into PHY training
Production patch Similar to original for training loops varies Training polls preserved

The original blob hangs at 0x10350 (a TBZ bit 1, -4 loop waiting for a PHY register). The patched blob passes through all 45 poll points and reaches deep PHY training code at 0x09124, where it waits for actual training completion (which requires real hardware feedback).

Production Patch Policy

Poll Type Action Reason
SGRF status NOP Hardware ready at check time
Firewall NOP Synchronous write
PLL lock NOP Already locked by calling code
BUS_GRF NOP Configuration, not status
PHY DfiStatus KEEP Active training wait
PHY CalBusy KEEP ZQ calibration in progress
PHY MicroReset KEEP Firmware startup
PHY UctWriteProt KEEP Training completion
PHY MicroContMux KEEP Firmware mailbox
Unknown NOP Prevent hangs (conservative)

How to Use on Real Hardware

WARNING: Flashing a patched DDR blob can brick your board. Recovery requires maskrom mode. Only proceed if you understand the risks.

# 1. Backup current blob
dd if=/dev/mmcblk0 of=backup_idb.bin bs=512 count=8192

# 2. Patch
python3 patch_prod.py original_blob.bin patched_blob.bin

# 3. Flash (use rkdeveloptool in maskrom, or rkddr tool)
# See https://github.com/hbiyik/rkddr for safe in-place patching
S
Description
RK3588 DDR init blob reverse engineering, decompilation, and patching
Readme 1 MiB
Languages
C 54.3%
Assembly 40.3%
Python 5.1%
Makefile 0.2%
Java 0.1%