4. U-Boot启动链总览

在嵌入式SoC上,上电后的第一条指令并不是U-Boot,而是固化在芯片内部ROM中的BootROM代码。BootROM负责最基础的时钟、引脚和存储控制器初始化,然后按预设顺序寻找可启动介质中的第一阶段引导程序。对于使用U-Boot的典型系统,这条链通常表现为:BootROM → TPL → SPL → U-Boot proper → Linux内核。理解每一级的职责、加载地址和传递参数,是板级调试和启动失败定位的基础。

核心传递关系可以概括为:每一级只做“刚好够用”的初始化,然后把下一级镜像从存储介质搬到内存并跳转执行;参数通过寄存器、设备树或全局数据结构逐级传递。
U-Boot 启动链:镜像加载与执行权传递 BootROM 芯片固化代码 初始化时钟/引脚 TPL 极小初始化 DDR训练/加载SPL SPL 初始化DDR/存储 加载U-Boot proper U-Boot proper 完整驱动/命令行 加载内核与DTB Linux内核 解压/启动 挂载根文件系统 跳转 加载 加载 bootm/booti 存储介质 BootROM TPL镜像 SPL镜像 u-boot.img Image + DTB eMMC/SD/NAND 逐级传递的关键信息 • BootROM → TPL:启动介质选择、镜像偏移、校验结果 • TPL → SPL:DDR已初始化、栈指针、加载地址 • SPL → U-Boot proper:gd结构体、设备树、内存布局、启动参数 • U-Boot proper → 内核:bootargs、initrd地址、DTB地址、入口地址 • 异常回退:若某级校验失败,BootROM尝试下一启动介质

一、每一级的职责边界

BootROM是芯片厂商固化的只读代码,它不依赖外部DDR,只使用片内SRAM。它的主要任务是:初始化最基本的时钟和启动引脚,然后根据BOOT_MODE引脚或eFUSE配置,从SD、eMMC、NAND、SPI NOR等介质中读取头部信息,验证签名或校验和,最后把TPL或SPL镜像搬到片内SRAM并跳转。

TPL(Tiny Program Loader)是可选的一级。当片内SRAM太小,无法容纳SPL时,TPL负责更小的初始化任务,典型场景是初始化DDR控制器并完成DDR训练,然后把SPL从存储介质搬到DDR中执行。很多SoC并不需要TPL,BootROM直接加载SPL即可。

SPL(Secondary Program Loader)是U-Boot编译产物中的第二阶段。它运行在片内SRAM或已初始化的DDR中,负责初始化DDR、时钟、串口和启动介质,然后从存储介质中读取完整的U-Boot proper镜像到DDR,并跳转到其入口。SPL通常不包含命令行和完整驱动,体积被严格限制。

U-Boot proper是用户最熟悉的完整U-Boot。它拥有完整的驱动模型、命令行、环境变量、网络和文件系统支持。它的核心任务是为内核准备启动环境:加载内核镜像、设备树和可选的initrd,设置bootargs,最后通过bootm、booti或bootz命令跳转到内核入口。

二、镜像加载与地址传递流程

启动链的每一级都遵循“加载—校验—跳转”的模式。以典型ARM64 SoC为例,BootROM从eMMC的0x40偏移读取SPL头部,校验通过后加载到0x20000000附近的SRAM;SPL运行后初始化DDR,从eMMC的0x100000偏移读取u-boot.img到0x40000000;U-Boot proper运行后从eMMC的0x2000000偏移读取Image和DTB到0x48000000和0x4A000000,最后执行booti。

# 典型启动日志片段
BootROM: loading SPL from eMMC offset 0x40
SPL: DDR init done, loading U-Boot proper from offset 0x100000
U-Boot 2024.01: board_init_r, DRAM: 2 GiB
Loading kernel from mmc 0:1 ...
Loading DTB from mmc 0:1 ...
Starting kernel at 0x48080000 ...

参数传递方面,SPL会把自己的全局数据结构gd和已初始化的设备树地址传给U-Boot proper;U-Boot proper则通过bootargs环境变量和设备树/chosen节点把内存布局、控制台、根文件系统信息传给内核。如果中间任何一级校验失败,BootROM会按启动顺序尝试下一个介质,SPL也可能回退到USB或串口下载模式。

三、典型场景与板级调试切入点

在板级调试中,启动链的每一级都有明确的观察点。BootROM阶段通常没有串口输出,只能通过测量启动引脚、读取eFUSE或使用厂商工具确认。TPL和SPL阶段可以通过串口打印确认DDR训练是否成功、镜像偏移是否正确。U-Boot proper阶段则可以通过命令行检查环境变量、存储设备和内核加载地址。

阶段 运行位置 关键任务 常见故障 调试手段
BootROM 片内ROM 时钟/引脚初始化、介质选择 启动引脚错误、eFUSE配置错误 测量引脚、厂商下载工具
TPL 片内SRAM DDR训练、加载SPL DDR参数错误、镜像偏移错误 串口日志、示波器测DDR
SPL SRAM/DDR DDR初始化、加载U-Boot proper DDR不稳定、存储读取失败 SPL串口打印、mmc命令
U-Boot proper DDR 驱动初始化、加载内核 环境变量错误、内核地址冲突 命令行、printenv、bdinfo
内核 DDR 解压、启动、挂载根文件系统 bootargs错误、DTB不匹配 earlycon、内核日志

四、常见误区与复盘要点

误区一:认为所有SoC都必须有TPL。实际上TPL只在片内SRAM不足以容纳SPL时使用,很多芯片是BootROM直接加载SPL。
误区二:把SPL和U-Boot proper的加载地址混为一谈。SPL通常运行在片内SRAM或DDR低地址,U-Boot proper运行在DDR较高地址,两者不能重叠。
复盘要点:启动失败时,先确认失败发生在哪一级。如果串口完全没有输出,问题通常在BootROM或TPL;如果SPL有输出但U-Boot proper没有,检查SPL加载地址和存储偏移;如果U-Boot proper能启动但内核不启动,检查bootargs、DTB和内核入口地址。

掌握这条完整传递关系后,再看具体的BOOT_MODE配置、SPL编译选项、U-Boot环境变量和bootm流程,就能把每个配置项映射到启动链的某一级,从而快速缩小问题范围。启动链的本质不是“一堆镜像的堆叠”,而是每一级在资源受限条件下完成最小必要初始化,并把执行权和关键参数可靠地交给下一级。