/* * uboot_profiler.c – AArch64 U-Boot Static Binary Profiler * * Analyses a raw u-boot.bin without objdump / binutils. * * Reports: * [1] Image type – legacy mkimage, FIT/FDT, or raw AArch64 reset vector * [2] Boot origin – board/SoC strings, entry point, embedded DTB * [3] Entry EL – inferred from SCTLR_EL{1,2,3} MRS/MSR access counts * [4] CPU cores – MIDR_EL1 + MPIDR_EL1 instruction scan * [5] SMP – MPIDR branch count + PSCI CPU_ON SMC detection * [6] Stack – SUB SP / STP [SP,-n]! histogram → peak estimate * [7] Heap/DRAM – size-derived pool + LPDDR4 throughput/latency model * [8] Peripherals – MOVZ+MOVK address chain vs 50-entry known MMIO table * * Build: gcc -O2 -Wall -Wextra -o uboot_profiler uboot_profiler.c -lm * Usage: ./uboot_profiler u-boot.bin */ #include #include #include #include #include #include #include #include #include #include #include /* ==================================================================== * COMPILE-TIME LIMITS * ==================================================================== */ #define MAX_STACK_FRAMES 512 #define MAX_PERIPHERALS 128 #define MAX_STR 256 /* ==================================================================== * AArch64 INSTRUCTION PATTERNS (little-endian 32-bit words) * * Every entry is: (insn & MASK_xxx) == PAT_xxx * ==================================================================== */ /* ── Stack frame ─────────────────────────────────────────────────── */ /* SUB SP, SP, #imm12 (sf=1, op=1, S=0, Rn=SP=31, Rd=SP=31) * Mask covers bits[31:23] and bits[9:0]; bit22 (shift) is left open so * both the shift=0 (0xD100_03FF) and shift=1 (0xD140_03FF) variants * collapse to the same pattern under this mask. */ #define MASK_SUB_SP 0xFF0003FFU #define PAT_SUB_SP 0xD10003FFU /* matches shift=0 and shift=1 */ /* ADD SP, SP, #imm12 (same idea, op=0 → 0x91 / 0x95 base) */ #define MASK_ADD_SP 0xFF0003FFU #define PAT_ADD_SP 0x910003FFU /* matches shift=0 and shift=1 */ /* STP Xn,Xm,[SP,#-imm]! (pre-index, 64-bit) */ #define MASK_STP_PRE 0xFFC07C00U #define PAT_STP_PRE 0xA9800000U /* opc=10,V=0,L=0,Rn=SP(31) */ /* ── System registers ───────────────────────────────────────────── */ /* MRS Xt, MPIDR_EL1 op0=3,op1=0,CRn=0,CRm=0,op2=5 */ #define MASK_MPIDR 0xFFFFFFE0U #define PAT_MPIDR 0xD53800A0U /* MRS Xt, MIDR_EL1 op0=3,op1=0,CRn=0,CRm=0,op2=0 */ #define MASK_MIDR 0xFFFFFFE0U #define PAT_MIDR 0xD5380000U /* MRS Xt, CurrentEL op0=3,op1=0,CRn=4,CRm=2,op2=2 */ #define MASK_CURREL 0xFFFFFFE0U #define PAT_CURREL 0xD5384240U /* MRS/MSR SCTLR_EL{3,2,1} */ #define MASK_SCTLR 0xFFFFFFE0U #define PAT_SCTLR_EL3_R 0xD53E1000U /* MRS */ #define PAT_SCTLR_EL2_R 0xD53C1000U #define PAT_SCTLR_EL1_R 0xD5381000U #define PAT_SCTLR_EL3_W 0xD51E1000U /* MSR */ #define PAT_SCTLR_EL2_W 0xD51C1000U #define PAT_SCTLR_EL1_W 0xD5181000U /* MSR SPSel, #0 / #1 */ #define PAT_SPSEL_0 0xD5384100U #define PAT_SPSEL_1 0xD5384101U /* ── Privilege / hypercalls ─────────────────────────────────────── */ #define MASK_SMC 0xFFE0001FU #define PAT_SMC 0xD4000003U #define MASK_HVC 0xFFE0001FU #define PAT_HVC 0xD4000002U #define PAT_ERET 0xD69F03E0U #define PAT_NOP 0xD503201FU /* ── Address construction ───────────────────────────────────────── */ #define MASK_MOVZ64 0xFF800000U #define PAT_MOVZ64 0xD2800000U #define MASK_MOVK64 0xFF800000U #define PAT_MOVK64 0xF2800000U /* ── Memory access ──────────────────────────────────────────────── */ #define MASK_LDR64 0xFFC00000U #define PAT_LDR64 0xF9400000U #define MASK_STR64 0xFFC00000U #define PAT_STR64 0xF9000000U #define MASK_LDR32 0xFFC00000U #define PAT_LDR32 0xB9400000U #define MASK_STR32 0xFFC00000U #define PAT_STR32 0xB9000000U /* ── Control flow ───────────────────────────────────────────────── */ #define MASK_BL 0xFC000000U #define PAT_BL 0x94000000U #define MASK_B 0xFC000000U #define PAT_B 0x14000000U /* ── Image magic (big-endian as stored in the file) ─────────────── */ #define UBOOT_MAGIC_BE 0x27051956UL #define FDT_MAGIC_BE 0xD00DFEEDUL /* PSCI CPU_ON function IDs (32-bit and 64-bit calling conventions) */ #define PSCI_CPU_ON_32 0x84000003UL #define PSCI_CPU_ON_64 0xC4000003UL /* ==================================================================== * KNOWN PERIPHERAL TABLE * ==================================================================== */ typedef struct { uint64_t base; const char *type; const char *name; } PeriEntry; static const PeriEntry PERI_TABLE[] = { /* QEMU virt platform ─────────────────────────────────────────── */ { 0x08000000ULL, "GIC", "GICv2 Distributor (QEMU virt)" }, { 0x08010000ULL, "GIC", "GICv2 CPU IF (QEMU virt)" }, { 0x08020000ULL, "GIC", "GICv4 Hyp IF (QEMU virt)" }, { 0x09000000ULL, "UART", "PL011 UART0 (QEMU virt)" }, { 0x09010000ULL, "UART", "PL011 UART1 (QEMU virt)" }, { 0x0A000000ULL, "VirtIO", "VirtIO MMIO (QEMU virt)" }, { 0x0C000000ULL, "PCIe", "PCIe ECAM (QEMU virt)" }, /* Raspberry Pi 4 / BCM2711 ─────────────────────────────────── */ { 0xFE200000ULL, "GPIO", "BCM2711 GPIO" }, { 0xFE201000ULL, "UART", "BCM2711 PL011 UART0" }, { 0xFE215000ULL, "UART", "BCM2711 AUX / Mini-UART" }, { 0xFE300000ULL, "PCM", "BCM2711 PCM / I2S" }, { 0xFE804000ULL, "USB", "BCM2711 USB-OTG (DWC-OTG)" }, { 0xFD500000ULL, "PCIe", "BCM2711 PCIe2 host" }, { 0xFF800000ULL, "GIC", "BCM2711 GIC-400" }, /* Rockchip RK3399 ──────────────────────────────────────────── */ { 0xFF180000ULL, "UART", "RK3399 UART0" }, { 0xFF190000ULL, "UART", "RK3399 UART1" }, { 0xFF1A0000ULL, "UART", "RK3399 UART2" }, { 0xFF110000ULL, "I2C", "RK3399 I2C0" }, { 0xFF120000ULL, "I2C", "RK3399 I2C1" }, { 0xFF3C0000ULL, "MMC", "RK3399 SD/MMC" }, { 0xFEE00000ULL, "GIC", "RK3399 GIC" }, { 0xFE330000ULL, "USB", "RK3399 USB3 OTG" }, /* Rockchip RK3568 ──────────────────────────────────────────── */ { 0xFE650000ULL, "UART", "RK3568 UART2" }, { 0xFD400000ULL, "GIC", "RK3568 GIC" }, /* Allwinner A64 / H5 / H6 ─────────────────────────────────── */ { 0x01C28000ULL, "UART", "A64 UART0" }, { 0x01C29000ULL, "UART", "A64 UART1" }, { 0x01C0F000ULL, "MMC", "A64 SD/MMC0" }, { 0x01C11000ULL, "MMC", "A64 SD/MMC2" }, { 0x01C19000ULL, "USB", "A64 USB OTG" }, { 0x01C40000ULL, "GIC", "A64 / H5 GIC" }, { 0x01C30000ULL, "DRAM", "A64 DRAM controller" }, /* i.MX8MQ / i.MX8MM / i.MX8MP ────────────────────────────── */ { 0x30860000ULL, "UART", "i.MX8M UART1" }, { 0x30880000ULL, "UART", "i.MX8M UART2" }, { 0x30B40000ULL, "MMC", "i.MX8M uSDHC1" }, { 0x30B50000ULL, "MMC", "i.MX8M uSDHC2" }, { 0x38100000ULL, "USB", "i.MX8M USB1" }, { 0x38200000ULL, "USB", "i.MX8M USB2" }, { 0x31000000ULL, "GIC", "i.MX8M GIC" }, /* Samsung Exynos ───────────────────────────────────────────── */ { 0x10020000ULL, "GIC", "Exynos GIC Distributor" }, { 0x10040000ULL, "GIC", "Exynos GIC CPU IF" }, { 0x12C00000ULL, "UART", "Exynos UART0" }, { 0x12C10000ULL, "UART", "Exynos UART1" }, /* MediaTek MT81xx ──────────────────────────────────────────── */ { 0x11002000ULL, "UART", "MT81xx UART0" }, { 0x11003000ULL, "UART", "MT81xx UART1" }, /* Amlogic S905 / S922 ──────────────────────────────────────── */ { 0xC1100000ULL, "UART", "Amlogic UART-A" }, { 0xC9000000ULL, "GIC", "Amlogic GIC" }, /* Xilinx ZynqMP / Versal ───────────────────────────────────── */ { 0xFF000000ULL, "UART", "ZynqMP UART0" }, { 0xFF010000ULL, "UART", "ZynqMP UART1" }, { 0xF9010000ULL, "GIC", "ZynqMP GIC" }, { 0xFF0C0000ULL, "MMC", "ZynqMP SD/MMC0" }, { 0xFF160000ULL, "USB", "ZynqMP USB0" }, /* Sentinel ─────────────────────────────────────────────────── */ { 0, NULL, NULL } }; /* ==================================================================== * PROFILE STRUCTURE * ==================================================================== */ typedef struct { /* ── Image / boot origin ─────────────────────────────────────── */ uint32_t magic; bool is_legacy; bool is_fit; bool is_raw_aarch64; uint32_t legacy_entry; bool has_uboot_str; bool has_atf_str; bool has_dtb; char board_str [MAX_STR]; char soc_str [MAX_STR]; char version_str[MAX_STR]; /* ── CPU / Exception Level ───────────────────────────────────── */ bool has_midr; bool has_mpidr; bool has_currel; int mpidr_count; uint32_t mpidr_first_off; int sctlr_el3_r, sctlr_el3_w; int sctlr_el2_r, sctlr_el2_w; int sctlr_el1_r, sctlr_el1_w; int entry_el; int eret_count; bool has_spsel; /* ── SMP ─────────────────────────────────────────────────────── */ bool smp_likely; bool has_smc; bool has_hvc; bool has_psci_cpu_on; int smc_count; /* ── Stack ───────────────────────────────────────────────────── */ uint32_t sub_sp_count; uint32_t add_sp_count; uint32_t stp_count; uint32_t max_frame; uint32_t frames[MAX_STACK_FRAMES]; int frame_n; uint32_t est_peak_stack_kb; /* ── Heap / memory ───────────────────────────────────────────── */ uint64_t largest_addr; uint64_t mmio_lo, mmio_hi; int bl_count; int ldr_count; int str_count; double est_heap_mb; double est_throughput_gbs; double est_latency_ns; /* ── Peripherals ─────────────────────────────────────────────── */ struct { uint64_t base; char type[24]; char name[64]; int hits; } peri[MAX_PERIPHERALS]; int peri_n; /* ── Meta ────────────────────────────────────────────────────── */ size_t file_size; size_t insn_count; double scan_ms; } Profile; /* ==================================================================== * INSTRUCTION HELPERS * ==================================================================== */ static inline uint32_t insn_le(const uint8_t *p) { return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24); } /* Extract immediate from SUB/ADD SP, SP, #imm */ static inline uint32_t sp_imm(uint32_t insn) { uint32_t imm12 = (insn >> 10) & 0xFFF; return ((insn >> 22) & 1) ? (imm12 << 12) : imm12; } /* Extract 64-bit value from MOVZ */ static inline uint64_t movz_val(uint32_t insn) { return (uint64_t)((insn >> 5) & 0xFFFF) << (((insn >> 21) & 3) * 16); } /* Apply MOVK onto an existing register value */ static inline uint64_t movk_apply(uint64_t base, uint32_t insn) { uint32_t hw = (insn >> 21) & 3; uint64_t imm = (uint64_t)((insn >> 5) & 0xFFFF); base &= ~((uint64_t)0xFFFF << (hw * 16)); return base | (imm << (hw * 16)); } /* Heuristic: is this address in a typical ARM SoC MMIO range? */ static inline bool is_mmio_addr(uint64_t v) { return (v >= 0x00010000ULL && v <= 0x3FFFFFFFULL) || /* low SoC space */ (v >= 0xC0000000ULL && v <= 0xFFFFFFFFULL) || /* high 32-bit */ (v >= 0xF00000000ULL); /* hi 36-bit+ */ } /* ==================================================================== * PERIPHERAL TRACKING * ==================================================================== */ static void peri_hit(Profile *r, uint64_t addr) { uint64_t base = addr & ~0xFFFFULL; /* Check existing tracked entries first */ for (int i = 0; i < r->peri_n; i++) { if (r->peri[i].base == base) { r->peri[i].hits++; return; } } if (r->peri_n >= MAX_PERIPHERALS) return; /* Look up in known table */ for (int i = 0; PERI_TABLE[i].name; i++) { if ((PERI_TABLE[i].base & ~0xFFFFULL) == base) { int k = r->peri_n++; r->peri[k].base = base; r->peri[k].hits = 1; strncpy(r->peri[k].type, PERI_TABLE[i].type, sizeof(r->peri[k].type) - 1); strncpy(r->peri[k].name, PERI_TABLE[i].name, sizeof(r->peri[k].name) - 1); return; } } /* Unknown MMIO – still record it */ int k = r->peri_n++; r->peri[k].base = base; r->peri[k].hits = 1; snprintf(r->peri[k].type, sizeof(r->peri[k].type), "MMIO?"); snprintf(r->peri[k].name, sizeof(r->peri[k].name), "Unknown MMIO @ 0x%08llX", (unsigned long long)base); } /* ==================================================================== * STRING SCANNER * ==================================================================== */ static void scan_strings(const uint8_t *data, size_t sz, Profile *r) { static const char *soc_kw[] = { "BCM2711","BCM2835","BCM2836","BCM2837", "RK3399","RK3568","RK3588","RK3326","RK3288", "sun50i","sun8i","A64","H5","H6","H616", "IMX8","i.MX8","i.MX6","imx8", "Exynos","EXYNOS", "MT8183","MT8192","MT8195", "S905","S922","S905X","S905D", "ZynqMP","Zynq","Versal","zynqmp", "Tegra","tegra","T186","T194", "Raspberry Pi","Rock Pi","Orange Pi","Banana Pi", "NXP","rockchip","allwinner","amlogic","xilinx", NULL }; char buf[MAX_STR]; int n = 0; for (size_t i = 0; i < sz; i++) { uint8_t c = data[i]; if (c >= 0x20 && c < 0x7F) { if (n < (int)sizeof(buf) - 1) buf[n++] = (char)c; } else { if (n >= 6) { buf[n] = '\0'; if (!r->has_uboot_str && (strstr(buf,"U-Boot") || strstr(buf,"u-boot"))) { r->has_uboot_str = true; if (!r->board_str[0]) snprintf(r->board_str, sizeof(r->board_str), "%s", buf); } /* Capture version string (contains year like "2023") */ if (!r->version_str[0] && strstr(buf,"U-Boot") && strstr(buf,"20")) snprintf(r->version_str, sizeof(r->version_str), "%s", buf); if (!r->has_atf_str && (strstr(buf,"NOTICE") || strstr(buf,"BL31") || strstr(buf,"TF-A") || strstr(buf,"ATF"))) r->has_atf_str = true; for (int k = 0; soc_kw[k]; k++) { if (strstr(buf, soc_kw[k]) && !r->soc_str[0]) { snprintf(r->soc_str, sizeof(r->soc_str), "%s", buf); break; } } } n = 0; } } } /* ==================================================================== * FDT / DTB EMBEDDED SCAN * ==================================================================== */ static void scan_fdt(const uint8_t *data, size_t sz, Profile *r) { /* Scan on 4-byte alignment for big-endian magic 0xD00DFEED */ for (size_t i = 0; i + 4 <= sz; i += 4) { uint32_t w = ((uint32_t)data[i] << 24) | ((uint32_t)data[i+1] << 16) | ((uint32_t)data[i+2] << 8) | (uint32_t)data[i+3]; if (w == (uint32_t)FDT_MAGIC_BE) { r->has_dtb = true; return; } } } /* ==================================================================== * IMAGE HEADER PARSER * ==================================================================== */ static void parse_header(const uint8_t *data, size_t sz, Profile *r) { if (sz < 4) return; /* U-Boot legacy magic is big-endian in the file */ uint32_t magic = ((uint32_t)data[0] << 24) | ((uint32_t)data[1] << 16) | ((uint32_t)data[2] << 8) | (uint32_t)data[3]; r->magic = magic; if (magic == (uint32_t)UBOOT_MAGIC_BE) { r->is_legacy = true; /* mkimage legacy header layout (all big-endian): * 0x00 magic u32 * 0x04 header CRC u32 * 0x08 timestamp u32 * 0x0C data size u32 * 0x10 load addr u32 * 0x14 entry point u32 ← we want this */ if (sz >= 0x18) r->legacy_entry = ((uint32_t)data[0x14] << 24) | ((uint32_t)data[0x15] << 16) | ((uint32_t)data[0x16] << 8) | (uint32_t)data[0x17]; return; } if (magic == (uint32_t)FDT_MAGIC_BE) { r->is_fit = true; return; } /* Raw AArch64: first instruction should be a branch, NOP, or MOV */ uint32_t i0 = insn_le(data); if ((i0 & MASK_B) == PAT_B || (i0 & MASK_BL) == PAT_BL || (i0 & MASK_MOVZ64) == PAT_MOVZ64 || i0 == PAT_NOP) r->is_raw_aarch64 = true; } /* ==================================================================== * MAIN INSTRUCTION SCAN ENGINE * ==================================================================== */ static void scan_insns(const uint8_t *data, size_t sz, Profile *r) { /* Per-register MOVZ/MOVK state tracker (X0–X30) */ typedef struct { uint64_t val; bool valid; } RegState; RegState regs[32]; memset(regs, 0, sizeof(regs)); r->mmio_lo = UINT64_MAX; r->mmio_hi = 0; /* Skip the 64-byte mkimage legacy header if present */ size_t start = r->is_legacy ? 64 : 0; for (size_t off = start; off + 4 <= sz; off += 4) { uint32_t insn = insn_le(data + off); r->insn_count++; /* ── Stack frame allocation ──────────────────────────────── */ if ((insn & MASK_SUB_SP) == PAT_SUB_SP) { uint32_t imm = sp_imm(insn); if (imm > 0 && imm <= 131072) { /* sanity: ≤128 KB */ r->sub_sp_count++; if (imm > r->max_frame) r->max_frame = imm; if (r->frame_n < MAX_STACK_FRAMES) r->frames[r->frame_n++] = imm; } } if ((insn & MASK_ADD_SP) == PAT_ADD_SP) r->add_sp_count++; if ((insn & MASK_STP_PRE) == PAT_STP_PRE) r->stp_count++; /* ── System register reads/writes ───────────────────────── */ if ((insn & MASK_MPIDR) == PAT_MPIDR) { r->has_mpidr = true; if (!r->mpidr_count) r->mpidr_first_off = (uint32_t)off; r->mpidr_count++; } if ((insn & MASK_MIDR) == PAT_MIDR) r->has_midr = true; if ((insn & MASK_CURREL) == PAT_CURREL) r->has_currel = true; if ((insn & MASK_SCTLR) == PAT_SCTLR_EL3_R) r->sctlr_el3_r++; if ((insn & MASK_SCTLR) == PAT_SCTLR_EL2_R) r->sctlr_el2_r++; if ((insn & MASK_SCTLR) == PAT_SCTLR_EL1_R) r->sctlr_el1_r++; if ((insn & MASK_SCTLR) == PAT_SCTLR_EL3_W) r->sctlr_el3_w++; if ((insn & MASK_SCTLR) == PAT_SCTLR_EL2_W) r->sctlr_el2_w++; if ((insn & MASK_SCTLR) == PAT_SCTLR_EL1_W) r->sctlr_el1_w++; if (insn == PAT_SPSEL_0 || insn == PAT_SPSEL_1) r->has_spsel = true; if (insn == PAT_ERET) r->eret_count++; /* ── Privilege calls ─────────────────────────────────────── */ if ((insn & MASK_SMC) == PAT_SMC) { r->has_smc = true; r->smc_count++; /* * Scan backwards up to 5 instructions for MOVZ X0, #psci_fn * to detect PSCI CPU_ON calls (marks SMP capable firmware). */ for (int bk = 4; bk <= 20 && (int)off - bk >= 0; bk += 4) { uint32_t prev = insn_le(data + off - bk); if ((prev & MASK_MOVZ64) == PAT_MOVZ64 && (prev & 0x1F) == 0) { /* Rd = X0 */ uint64_t fn = movz_val(prev); if (fn == PSCI_CPU_ON_32 || fn == PSCI_CPU_ON_64) { r->has_psci_cpu_on = true; r->smp_likely = true; } break; } } } if ((insn & MASK_HVC) == PAT_HVC) r->has_hvc = true; /* ── MOVZ / MOVK: reconstruct 64-bit addresses ───────────── */ if ((insn & MASK_MOVZ64) == PAT_MOVZ64) { uint32_t rd = insn & 0x1F; regs[rd].val = movz_val(insn); regs[rd].valid = true; continue; /* MOVK typically follows immediately */ } if ((insn & MASK_MOVK64) == PAT_MOVK64) { uint32_t rd = insn & 0x1F; if (regs[rd].valid) { regs[rd].val = movk_apply(regs[rd].val, insn); uint64_t v = regs[rd].val; if (v > r->largest_addr) r->largest_addr = v; if (is_mmio_addr(v)) { peri_hit(r, v); if (v < r->mmio_lo) r->mmio_lo = v; if (v > r->mmio_hi) r->mmio_hi = v; } } continue; } /* * Any instruction writing Rd (not MOVZ/MOVK) invalidates * that register's tracked value to prevent false positives. */ { uint32_t rd = insn & 0x1F; if (rd < 30) regs[rd].valid = false; } /* ── Load / Store / Call counters ────────────────────────── */ if ((insn & MASK_BL) == PAT_BL) r->bl_count++; if ((insn & MASK_LDR64) == PAT_LDR64 || (insn & MASK_LDR32) == PAT_LDR32) r->ldr_count++; if ((insn & MASK_STR64) == PAT_STR64 || (insn & MASK_STR32) == PAT_STR32) r->str_count++; } /* ── SMP secondary heuristics ────────────────────────────────── */ if (r->mpidr_count >= 2) r->smp_likely = true; if (r->has_mpidr && r->eret_count>=2) r->smp_likely = true; /* ── Infer entry exception level from SCTLR access pattern ───── */ int el3 = r->sctlr_el3_r + r->sctlr_el3_w; int el2 = r->sctlr_el2_r + r->sctlr_el2_w; int el1 = r->sctlr_el1_r + r->sctlr_el1_w; if (el3 >= el2 && el3 >= el1) r->entry_el = 3; else if (el2 >= el1) r->entry_el = 2; else r->entry_el = 1; if (r->entry_el == 0) r->entry_el = 2; /* safe default */ /* ── Derived estimates ────────────────────────────────────────── */ /* * Stack peak: largest single frame × 20 nesting levels (conservative * for U-Boot which typically reaches ~8–12 levels deep). */ r->est_peak_stack_kb = (r->max_frame * 20 + 1023) / 1024; /* * Heap pool: binary size × 4 (typical U-Boot relocation overhead + * BSS + malloc pool at end of DRAM region). */ r->est_heap_mb = (double)r->file_size / (1024.0 * 1024.0) * 4.0; /* * Memory throughput / latency model: * Cortex-A53 / A55 @ 1.4 GHz + LPDDR4-3200, 1-channel: * theoretical BW = 3200 MT/s × 64 bit / 8 = 25.6 GB/s total; * U-Boot typically achieves ~3–5 GB/s (sequential copy, no cache). * LPDDR4 CAS latency ≈ 22 ns + routing ≈ 40 ns → ~60–70 ns */ r->est_throughput_gbs = 3.8; r->est_latency_ns = 65.0; } /* ==================================================================== * REPORT PRINTER * ==================================================================== */ #define HR "════════════════════════════════════════════════════════════════════\n" #define HR2 "────────────────────────────────────────────────────────────────────\n" /* Left-aligned key + value pair */ static void field(const char *key, const char *fmt, ...) { char val[512]; va_list ap; va_start(ap, fmt); vsnprintf(val, sizeof(val), fmt, ap); va_end(ap); printf(" │ %-36s%s\n", key, val); } static void sect(const char *title) { printf("\n ┌─ %s\n", title); } static void print_report(const Profile *r, const char *fname) { printf("\n" HR); printf(" U-BOOT BIN PROFILER ─ AArch64 Static Analysis Report\n"); printf(HR); /* ── FILE ──────────────────────────────────────────────────────── */ sect("FILE"); field("Path:", "%s", fname); field("File size:", "%zu bytes (%.1f KB / %.3f MB)", r->file_size, r->file_size / 1024.0, r->file_size / (1024.0 * 1024.0)); field("Instructions scanned:", "%zu", r->insn_count); field("Scan time:", "%.2f ms", r->scan_ms); /* ── BOOT ORIGIN ───────────────────────────────────────────────── */ sect("BOOT ORIGIN"); if (r->is_legacy) field("Image type:", "U-Boot Legacy Image (magic 0x27051956)"); else if (r->is_fit) field("Image type:", "FIT / FDT image (magic 0xD00DFEED)"); else if (r->is_raw_aarch64) field("Image type:", "Raw AArch64 binary (reset vector direct)"); else field("Image type:", "Unknown / wrapped (magic 0x%08X)", r->magic); if (r->is_legacy) field("Entry point (mkimage header):","0x%08X", r->legacy_entry); field("U-Boot string found:", r->has_uboot_str ? "YES" : "NO"); if (r->has_uboot_str && r->board_str[0]) field("Board / version string:", "%.64s", r->board_str); if (r->version_str[0] && strcmp(r->version_str, r->board_str)) field("Version hint:", "%.64s", r->version_str); field("ATF / TF-A marker found:", r->has_atf_str ? "YES" : "NO"); field("Embedded FDT / DTB:", r->has_dtb ? "YES (0xD00DFEED found)" : "NO"); if (r->soc_str[0]) field("SoC hint string:", "%.64s", r->soc_str); /* ── CPU / EXCEPTION LEVEL ─────────────────────────────────────── */ sect("CPU CORE & EXCEPTION LEVEL"); field("MIDR_EL1 read:", r->has_midr ? "YES" : "NO"); field("MPIDR_EL1 reads:", "%d (first at file offset 0x%05X)", r->mpidr_count, r->mpidr_first_off); field("CurrentEL read:", r->has_currel ? "YES" : "NO"); field("MSR SPSel:", r->has_spsel ? "YES" : "NO"); field("ERET instructions:", "%d", r->eret_count); printf(" │\n"); printf(" │ %-36s %s\n", ""," EL3 EL2 EL1"); printf(" │ %-36s reads:%-4d writes:%-2d reads:%-4d writes:%-2d reads:%-4d writes:%d\n", "SCTLR access counts:", r->sctlr_el3_r, r->sctlr_el3_w, r->sctlr_el2_r, r->sctlr_el2_w, r->sctlr_el1_r, r->sctlr_el1_w); printf(" │\n"); field("Inferred entry EL:", "EL%d (dominant SCTLR access level)", r->entry_el); field("SMC instructions:", "%d (%s)", r->smc_count, r->has_smc ? "ATF / PSCI interface detected" : "none – no EL3 firmware calls"); field("HVC instructions:", r->has_hvc ? "YES (hypervisor calls present)" : "NO"); /* ── SMP ───────────────────────────────────────────────────────── */ sect("SMP / MULTI-CORE CAPABILITY"); field("SMP indicated:", "%s", r->smp_likely ? "YES ✓ (MPIDR branch or PSCI CPU_ON detected)" : "NO (single-core boot path)"); field("PSCI CPU_ON (0x8400_0003) call:", r->has_psci_cpu_on ? "YES" : "NO"); field("MPIDR_EL1 affinity reads:", "%d", r->mpidr_count); if (r->smp_likely) { printf(" │\n"); printf(" │ HOW SMP WORKS IN U-BOOT:\n"); printf(" │ Primary core (CPU0) executes normally.\n"); printf(" │ Secondary cores spin on WFE at a magic release address.\n"); printf(" │ Primary calls PSCI CPU_ON via SMC to wake each secondary.\n"); printf(" │ Each secondary jumps to its cpu_on_handler entry point.\n"); } /* ── STACK ─────────────────────────────────────────────────────── */ sect("STACK USAGE ANALYSIS"); field("SUB SP (allocations):", "%u", r->sub_sp_count); field("ADD SP (deallocations):", "%u", r->add_sp_count); field("STP [SP,#-n]! pairs:", "%u (saved register pairs)", r->stp_count); field("Largest single frame:", "%u bytes (%u KB)", r->max_frame, (r->max_frame + 1023) / 1024); field("Unique frame sizes logged:", "%d", r->frame_n); if (r->frame_n > 0) { /* Insertion-sort descending to show top-10 */ uint32_t tmp[MAX_STACK_FRAMES]; int n = r->frame_n; memcpy(tmp, r->frames, (size_t)n * sizeof(uint32_t)); for (int a = 1; a < n; a++) { uint32_t key = tmp[a]; int b = a - 1; while (b >= 0 && tmp[b] < key) { tmp[b+1] = tmp[b]; b--; } tmp[b+1] = key; } int show = n < 10 ? n : 10; printf(" │ %-36s", "Top-10 frame sizes (bytes):"); for (int k = 0; k < show; k++) printf("%u ", tmp[k]); printf("\n"); } field("Estimated peak stack:", "~%u KB (largest frame × 20 nesting levels)", r->est_peak_stack_kb); printf(" │\n"); printf(" │ NOTE: SP base is set at runtime (DRAM base + offset).\n"); printf(" │ Actual peak measurable only with hardware tracing.\n"); /* ── HEAP / MEMORY ─────────────────────────────────────────────── */ sect("HEAP & MEMORY THROUGHPUT ESTIMATES"); field("Estimated heap pool:", "~%.1f MB (binary × 4 reloc/BSS model)", r->est_heap_mb); field("DRAM throughput model:", "~%.1f GB/s (LPDDR4-3200, 1-ch, A53/A72)", r->est_throughput_gbs); field("DRAM access latency model:", "~%.0f ns (CAS ~22 ns + routing)", r->est_latency_ns); printf(" │\n"); field("Largest MOVZ reconstructed address:", "0x%016llX", (unsigned long long)r->largest_addr); if (r->mmio_lo != UINT64_MAX) field("MMIO range (MOVZ+MOVK):", "0x%08llX – 0x%08llX", (unsigned long long)r->mmio_lo, (unsigned long long)r->mmio_hi); field("BL (function calls):", "%d", r->bl_count); field("LDR instructions (64+32):","%-6d STR: %d", r->ldr_count, r->str_count); if (r->str_count > 0) field("LD/ST ratio:", "%.2f : 1", (double)r->ldr_count / (double)r->str_count); /* ── PERIPHERALS ───────────────────────────────────────────────── */ sect("PERIPHERAL MAP (MOVZ + MOVK address reconstruction)"); if (r->peri_n == 0) { printf(" │ No MMIO addresses reconstructed.\n"); printf(" │ Possible reasons:\n"); printf(" │ • Binary uses PC-relative addressing (ADRP/ADD)\n"); printf(" │ • Addresses loaded from in-memory tables\n"); printf(" │ • Binary is position-independent; pass load address\n"); } else { printf(" │ %-14s %-10s %5s %s\n", "Base Addr", "Type", "Hits", "Device"); printf(" │ " HR2); for (int i = 0; i < r->peri_n; i++) { printf(" │ 0x%012llX %-10s %5d %s\n", (unsigned long long)r->peri[i].base, r->peri[i].type, r->peri[i].hits, r->peri[i].name); } } /* ── SUMMARY ───────────────────────────────────────────────────── */ sect("EXECUTIVE SUMMARY"); printf(" │\n"); printf(" │ %-22s %s\n", "Binary format:", r->is_legacy ? "U-Boot legacy mkimage" : r->is_fit ? "FIT / FDT image" : r->is_raw_aarch64 ? "Raw AArch64 binary" : "Unknown / wrapped"); if (r->version_str[0]) printf(" │ %-22s %.60s\n", "Version:", r->version_str); if (r->soc_str[0]) printf(" │ %-22s %.60s\n", "SoC / board hint:", r->soc_str); printf(" │ %-22s EL%d (%s)\n", "Entry exception level:", r->entry_el, r->entry_el == 3 ? "EL3 – full Trusted Firmware present" : r->entry_el == 2 ? "EL2 – hypervisor / Xen entry" : "EL1 – OS / Linux-ready entry"); printf(" │ %-22s %s\n", "SMP capability:", r->smp_likely ? "Yes – PSCI / MPIDR multi-core boot detected" : "No – single-core boot path"); printf(" │ %-22s max frame %u B → estimated peak ~%u KB\n", "Stack:", r->max_frame, r->est_peak_stack_kb); printf(" │ %-22s ~%.1f MB pool │ ~%.1f GB/s │ ~%.0f ns latency\n", "DRAM / heap:", r->est_heap_mb, r->est_throughput_gbs, r->est_latency_ns); printf(" │ %-22s %d device(s) reconstructed from MOVZ+MOVK chains\n", "Peripherals:", r->peri_n); printf(" │ %-22s %s\n", "Embedded DTB:", r->has_dtb ? "Yes – FDT magic 0xD00DFEED found" : "No – device tree likely loaded separately"); printf(" │\n"); printf(HR); printf("\n"); printf(" ACCURACY NOTE: This is a STATIC analysis only.\n"); printf(" All values are heuristic estimates derived from instruction\n"); printf(" patterns. For exact figures:\n"); printf(" • Stack: instrument with JTAG / OpenOCD + GDB\n"); printf(" • Heap/speed: use U-Boot 'meminfo' and 'mmc speed' commands\n"); printf(" • SMP: check device-tree /cpus node and PSCI node\n"); printf(" • Peripherals: cross-reference with SoC TRM / datasheet\n"); printf("\n" HR "\n"); } /* ==================================================================== * MAIN * ==================================================================== */ int main(int argc, char *argv[]) { if (argc < 2) { fprintf(stderr, "AArch64 U-Boot Static Profiler\n" "Usage: %s \n" "Build: gcc -O2 -Wall -o uboot_profiler uboot_profiler.c -lm\n", argv[0]); return 1; } FILE *fp = fopen(argv[1], "rb"); if (!fp) { fprintf(stderr, "Cannot open '%s': %s\n", argv[1], strerror(errno)); return 1; } struct stat st; if (fstat(fileno(fp), &st) != 0) { fclose(fp); fprintf(stderr, "stat() failed: %s\n", strerror(errno)); return 1; } size_t fsz = (size_t)st.st_size; uint8_t *data = (uint8_t *)malloc(fsz); if (!data) { fclose(fp); fprintf(stderr, "malloc(%zu) failed: out of memory\n", fsz); return 1; } if (fread(data, 1, fsz, fp) != fsz) { fclose(fp); free(data); fprintf(stderr, "fread() error\n"); return 1; } fclose(fp); /* ── Profile ──────────────────────────────────────────────────── */ Profile r; memset(&r, 0, sizeof(r)); r.file_size = fsz; struct timespec t0, t1; clock_gettime(CLOCK_MONOTONIC, &t0); parse_header(data, fsz, &r); /* detect image type + entry point */ scan_strings(data, fsz, &r); /* extract board/SoC/version strings */ scan_fdt (data, fsz, &r); /* detect embedded DTB */ scan_insns (data, fsz, &r); /* main AArch64 instruction pass */ clock_gettime(CLOCK_MONOTONIC, &t1); r.scan_ms = (double)(t1.tv_sec - t0.tv_sec) * 1000.0 + (double)(t1.tv_nsec - t0.tv_nsec) / 1.0e6; print_report(&r, argv[1]); free(data); return 0; }