保姆级教程:手把手带你读懂Rockchip平台Uboot的_start启动流程(附反编译技巧)

张开发
2026/4/10 1:46:09 15 分钟阅读

分享文章

保姆级教程:手把手带你读懂Rockchip平台Uboot的_start启动流程(附反编译技巧)
深度解析Rockchip平台Uboot启动流程从_start到_main的实战指南当你第一次拿到一块Rockchip开发板看着Uboot启动日志快速滚动时是否好奇过这背后究竟发生了什么作为嵌入式Linux系统的第一道关卡Uboot的启动流程直接决定了后续内核能否顺利加载。本文将带你深入Rockchip平台Uboot的汇编级启动世界通过反编译、调试和实战验证彻底理解从_start到_main的每一个关键步骤。1. 准备工作与环境搭建在开始探索_start流程前我们需要搭建一个完整的开发环境。对于Rockchip平台推荐使用RK3568开发板作为实验平台它的文档和社区支持都比较完善。基础工具链安装sudo apt install gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu \ device-tree-compiler u-boot-tools获取Uboot源码git clone https://github.com/rockchip-linux/u-boot -b next-dev关键工具介绍aarch64-linux-gnu-objdump反汇编工具用于分析Uboot二进制readelf查看ELF文件结构gdb-multiarch支持多架构的调试工具提示建议使用Ubuntu 20.04或更新版本作为开发主机避免工具链兼容性问题开发板连接配置sudo apt install picocom picocom -b 1500000 /dev/ttyUSB02. 入口点_start的定位与分析Uboot的启动始于_start符号这是CPU上电后执行的第一条指令所在位置。要找到这个入口点我们有几种验证方法方法一通过反编译定位aarch64-linux-gnu-objdump -D u-boot u-boot.dis在生成的dis文件中搜索_start你会看到类似这样的输出Disassembly of section .text: 0000000000000000 _start: 0: d503245f bti c 4: d53800a0 mrs x0, mpidr_el1 8: 92401c00 and x0, x0, #0xff c: d503201f nop方法二通过链接脚本确认查看u-boot.lds文件通常位于arch/arm/cpu/armv8/u-boot.lds其中会明确指定入口点ENTRY(_start) SECTIONS { . 0x00000000; .text : { *(.__image_copy_start) *(.vectors) CPUDIR/start.o (.text*) ... } }方法三System.map交叉验证编译完成后生成的System.map文件中_start通常位于最低地址0000000000000000 T _start 0000000000000000 T _vectors 0000000000000400 T _end_ofs注意Rockchip平台的实际加载地址可能与链接地址不同需要考虑重定位因素3. 启动流程关键阶段拆解3.1 异常级别初始化ARMv8架构定义了四个异常级别(EL0-EL3)Uboot通常在EL3或EL2启动。在start.S中关键的异常级别初始化包括确定当前异常级别mrs x0, CurrentEL lsr x0, x0, #2 // EL存储在bit[3:2]设置异常向量表adr x0, vectors msr VBAR_EL3, x0配置安全状态mov x0, #(SCR_EL3_RW | SCR_EL3_NS) msr SCR_EL3, x0典型配置对比配置项EL3设置EL2设置EL1设置执行状态AArch64AArch64AArch64安全状态安全/非安全仅非安全仅非安全MMU状态通常关闭可选开启通常开启3.2 内存布局处理Rockchip平台的内存初始化涉及几个关键符号.globl _start _start: ldr x0, __image_copy_start ldr x1, __image_copy_end sub x1, x1, x0 // 计算拷贝大小 ldr x2, __rel_dyn_start ldr x3, __rel_dyn_end这些符号在u-boot.lds中定义反映了内存的关键区域__image_copy_start/endUboot镜像在内存中的位置__bss_start/endBSS段边界__rel_dyn_start/end重定位表范围内存布局可视化0x00000000 --------------- | 中断向量表 | --------------- | .text | | (代码段) | --------------- | .rodata | | (只读数据) | --------------- | .data | | (可写数据) | --------------- | .bss | | (未初始化数据)| --------------- | 堆区 | --------------- | 栈区 | 0xFFFFFFFF ---------------3.3 关键函数执行路径从_start到_main的典型调用链_start入口点设置基础环境reset处理器复位处理save_boot_params_ret启动参数处理lowlevel_init底层硬件初始化_main转入C语言环境使用gdb跟踪执行流aarch64-linux-gnu-gdb u-boot (gdb) target remote :1234 (gdb) b _start (gdb) b save_boot_params_ret (gdb) c4. 关键配置标志深度解析Rockchip平台的Uboot启动流程受多个CONFIG标志影响理解这些标志对调试至关重要。4.1 CONFIG_POSITION_INDEPENDENT位置无关代码(PIC)允许Uboot在内存中任意地址运行这对Rockchip平台特别重要因为不同SoC的加载地址可能不同。启用PIC时的重定位处理#ifdef CONFIG_POSITION_INDEPENDENT adr x0, _start // 获取当前运行地址 ldr x1, _start // 获取链接地址 subs x2, x0, x1 // 计算偏移 beq 1f // 如果相同则跳过 // 重定位.got和.rela.dyn段 ldr x3, __rel_dyn_start ldr x4, __rel_dyn_end ... #endif4.2 多核启动相关配置Rockchip SoC通常采用大小核架构Uboot需要正确处理多核启动配置项功能描述典型值CONFIG_ARMV8_MULTIENTRY启用多核支持yCONFIG_ARMV8_SPIN_TABLE使用自旋表唤醒从核yCONFIG_ARMV8_SET_SMPEN启用多核数据一致性y自旋表工作机制主核初始化环境后将入口地址写入自旋表从核上电后轮询自旋表中的地址当地址变为非零时从核跳转到该地址执行4.3 时钟与复位配置关键时钟配置#define COUNTER_FREQUENCY 24000000 /* 24MHz */ #define CONFIG_SYS_RESET_SCTRL 1 /* 复位系统控制寄存器 */时钟初始化流程读取CPU的MPIDR寄存器确定核心编号主核初始化PLL和时钟树配置定时器频率(COUNTER_FREQUENCY)如果需要复位系统控制寄存器(CONFIG_SYS_RESET_SCTRL)5. 实战调试技巧与问题排查5.1 常见启动问题分析问题现象1Uboot启动后立即复位可能原因CONFIG_SYS_RESET_SCTRL配置不当检查方法在reset_sctrl函数前添加调试输出问题现象2从核无法启动可能原因自旋表地址未正确设置检查方法使用md命令查看自旋表内存区域5.2 高级调试手段通过JTAG调试配置OpenOCD连接开发板在_start处设置断点单步跟踪汇编执行内存内容检查命令# 查看内存区域 md.b 0x00000000 0x100 # 反汇编特定地址 disassemble 0x00000000, 0x100寄存器检查命令# 查看当前异常级别 mrs x0, CurrentEL # 查看MPIDR寄存器 mrs x0, mpidr_el15.3 性能优化建议关键路径分析使用定时器标记各阶段耗时重点关注重定位、BSS清零、设备初始化内存访问优化确保DCache在合适时机启用对齐关键数据结构的访问大小核调度主核完成必要初始化后尽早唤醒从核合理分配初始化任务给不同核心在实际项目中理解这些底层机制能帮助开发者快速定位启动卡死、内存错误等棘手问题。我曾遇到一个案例由于CONFIG_POSITION_INDEPENDENT配置错误Uboot在重定位后跳转到了错误地址通过反编译和寄存器状态对比最终发现是重定位偏移计算有误。这种深度调试经验正是掌握Rockchip平台启动流程的价值所在。

更多文章