/* RK3588 DDR blob emulator v2 - with proper entry stub */ #include #include #include #include #include #define BLOB_BASE 0x00000000 #define BLOB_SIZE 0x20000 #define STACK_BASE 0x00100000 #define STACK_SIZE 0x10000 #define SRAM_BASE 0x001F0000 #define SRAM_SIZE 0x10000 typedef struct { uint64_t base; uint64_t size; const char *name; } mmio_t; static mmio_t mmio[] = { {0xFD580000, 0x20000, "GRF"}, {0xFD5F0000, 0x10000, "BUS_GRF"}, {0xFD8C0000, 0x10000, "SCRU"}, {0xFE010000, 0x20000, "DDRC"}, {0xFE030000, 0x10000, "FW_DDR"}, {0xFE050000, 0x10000, "SGRF"}, {0xFE0C0000, 0x40000, "DDRPHY"}, {0xFECC0000, 0x10000, "SCRAMBLE"}, {0xFF000000, 0x100000, "SRAM_BOOT"}, {0, 0, NULL} }; static int instr_count = 0, max_instr = 50000, mmio_count = 0; static int verbose = 0; static void mmio_read(uc_engine *uc, uc_mem_type type, uint64_t addr, int size, int64_t val, void *ud) { uint32_t ret = 0; uint32_t off = addr & 0xFFFF; /* SGRF: return ready */ if (addr >= 0xFE050000 && addr < 0xFE060000) { if (off == 0x00E0) ret = 0; /* status = ready */ if (off == 0x0054) ret = 1; /* CON21 = done */ if (off == 0x00E4) ret = 0; /* enable */ } /* DDRPHY: return ready/not-busy */ else if (addr >= 0xFE0C0000 && addr < 0xFE100000) { if ((off & 0xFFF) == 0xA24) ret = 0x02; /* DfiStatus = ready */ if ((off & 0xFFF) == 0x684) ret = 0; /* CalBusy = idle */ if ((off & 0xFFF) == 0x090) ret = 0; /* MicroContMux */ if ((off & 0xFFF) == 0x080) ret = 0; /* MicroReset done */ if ((off & 0xFFF) == 0x514) ret = 0; /* training done */ } /* SCRU: PLL locked */ else if (addr >= 0xFD8C0000 && addr < 0xFD8D0000) { ret = 0x01; /* PLL locked */ } uc_mem_write(uc, addr, &ret, 4); mmio_count++; if (verbose || mmio_count <= 100) printf(" MMIO RD 0x%lx = 0x%x\n", addr, ret); } static void hook_code(uc_engine *uc, uint64_t addr, uint32_t size, void *ud) { instr_count++; if (instr_count >= max_instr) { printf("LIMIT at PC=0x%lx (%d instrs, %d MMIO)\n", addr, instr_count, mmio_count); uc_emu_stop(uc); } if (instr_count <= 20 || instr_count % 5000 == 0) printf("[%d] PC=0x%lx\n", instr_count, addr); } int main(int argc, char **argv) { if (argc < 2) { printf("Usage: %s [max_instr] [verbose]\n", argv[0]); return 1; } if (argc > 2) max_instr = atoi(argv[2]); if (argc > 3) verbose = 1; FILE *f = fopen(argv[1], "rb"); fseek(f, 0, SEEK_END); long sz = ftell(f); fseek(f, 0, SEEK_SET); uint8_t *blob = malloc(sz); fread(blob, 1, sz, f); fclose(f); printf("Loaded %ld bytes\n", sz); uc_engine *uc; uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc); uc_mem_map(uc, BLOB_BASE, BLOB_SIZE, UC_PROT_ALL); uc_mem_write(uc, BLOB_BASE, blob, sz); uc_mem_map(uc, STACK_BASE, STACK_SIZE, UC_PROT_ALL); uint64_t sp = STACK_BASE + STACK_SIZE - 16; uc_reg_write(uc, UC_ARM64_REG_SP, &sp); uc_mem_map(uc, SRAM_BASE, SRAM_SIZE, UC_PROT_ALL); for (int i = 0; mmio[i].name; i++) { uc_mem_map(uc, mmio[i].base, mmio[i].size, UC_PROT_ALL); uc_hook hh; uc_hook_add(uc, &hh, UC_HOOK_MEM_READ, mmio_read, NULL, mmio[i].base, mmio[i].base + mmio[i].size); } uc_hook hh; uc_hook_add(uc, &hh, UC_HOOK_CODE, hook_code, NULL, BLOB_BASE, BLOB_BASE + BLOB_SIZE); /* Skip the entry version check loop - start at the real init (0x40) The entry at 0x0 is a version check gate that requires a specific return value from the PMU status check. On real hardware this is set by BL2. We skip it and go directly to the DDR init code. */ uint64_t start_pc = 0x00000040; /* FUN_00000040 = first real init function */ /* But FUN_40 takes parameters. The main orchestrator is at the thunk target. Let's find where Reset would jump after the version check. */ /* Reset flow: 0x0 -> check version -> thunk_FUN_00010978 FUN_00010978 is the main orchestrator at offset 0x10978 */ start_pc = 0x00010978; printf("Starting at PC=0x%lx (skipping entry version check)\n\n", start_pc); uc_err err = uc_emu_start(uc, start_pc, BLOB_BASE + sz, 0, max_instr); uint64_t pc, x0; uc_reg_read(uc, UC_ARM64_REG_PC, &pc); uc_reg_read(uc, UC_ARM64_REG_X0, &x0); printf("\nStopped: %s PC=0x%lx X0=0x%lx (%d instrs, %d MMIO)\n", uc_strerror(err), pc, x0, instr_count, mmio_count); uc_close(uc); free(blob); return 0; }