很多人刚开始学嵌入式 Linux 启动链,最容易混的不是 kernel,也不是 rootfs,而是前面这几个名字:
• TF-A
• SPL
• U-Boot
看日志时它们前后都出现,
看资料时又经常一起提,
于是很多人脑子里就会变成一团:
• 谁先跑?
• 谁负责 DDR?
• 谁负责把 Linux 拉起来?
• 谁才是那个能敲命令的 U-Boot?
如果只用一句话先讲明白这篇文章,那就是:
TF-A、SPL、U-Boot 不是同一个东西,它们是启动链里不同阶段、不同职责的三个角色。
真正要分清的,不是名字本身,
而是:
谁负责最早期环境,谁负责把下一阶段拉起来,谁负责真正启动 Linux。

一句话先讲明白
如果只记一句话,可以先记这个:
TF-A 更偏底层固件和安全态初始化,SPL 负责在资源很少的早期把完整 bootloader 拉起来,U-Boot 负责最终加载 kernel、dtb 和启动 Linux。
这三者不是替代关系,
更像是接力关系。
第一:为什么它们最容易被混?
因为它们都出现在启动早期。
而且很多人最先接触启动链,是从串口日志开始的。
看到一堆 BL、SPL、U-Boot 字样,很容易把它们都理解成“某种 bootloader”。
这样理解不能说完全错,
但会丢掉最关键的边界:
• 有的阶段更靠底层
• 有的阶段运行在 DDR 起来之前
• 有的阶段只是过渡加载
• 有的阶段才是真正负责启动 Linux
所以这三个词一旦不按“职责”去分,就一定会混。
第二:先按启动链顺序理解,就不容易乱
你可以先粗略这么记:
• TF-A:更靠前、更底层
• SPL:早期过渡加载器
• U-Boot:完整主 bootloader
这不是所有平台都严格一模一样,
但作为整体理解已经足够。
也就是说,它们不是并列关系,
而是前一阶段把系统准备到一定程度,再把控制权交给后一阶段。
所以最稳的理解方式不是问“谁更高级”,
而是问:
它在启动链里接的是哪一棒。
第三:U-Boot 是什么?它是你最熟的那一层
U-Boot 是大家最熟的一层,
因为它最“看得见”。
• 你能看到它的 banner,
• 能进命令行,
• 能改环境变量,
• 能执行:
printenv setenv bootcmd
booti bootm bootz
所以平时说“启动问题”,很多人第一反应就是 U-Boot。
这没错。
但要注意:
你平时最常打交道的 U-Boot,通常不是第一棒,而是已经具备较完整能力之后的那一层。
它主要负责的是:
• 初始化更多外设
• 加载 kernel / dtb / initrd
• 设置 bootargs
• 执行启动命令
• 提供交互调试入口
所以你可以把 U-Boot 理解成:
真正负责把 Linux 拉起来的主 bootloader。
第四:SPL 是什么?它为什么经常更关键?
SPL 全称是 Secondary Program Loader。
很多人会把它理解成“缩小版 U-Boot”。
这只说对了一半。
SPL 的确通常比较小,
也常常属于 U-Boot 工程的一部分,
但它真正的意义不是“小”,而是:
它负责在系统资源非常有限的早期,把系统带到能加载完整 U-Boot 的状态。
为什么需要它?
因为系统刚上电时,往往有这些限制:
• DDR 还没初始化
• 可用内存只有片上 SRAM / OCRAM
• BootROM 能拉起的第一阶段镜像很小
• 外设能力还不完整
这时候完整 U-Boot 往往太大,BootROM 直接拉不起来。
所以就需要一个更小的过渡阶段,也就是 SPL。
SPL 常见职责包括:
• 基础时钟初始化
• DDR 初始化
• 初始化最小存储接口
• 从介质里把完整 U-Boot 读出来
• 跳转到 U-Boot proper
所以很多平台上,真正决定你能不能看到 U-Boot 命令行的,不是 U-Boot 本体,而是 SPL。
SPL 的本质,是跨过“早期资源不够”这道坎。
第五:TF-A 又是什么?它和前两个不是一层
TF-A 的全称是 Trusted Firmware-A。
它和 U-Boot、SPL 最大的区别在于:
它通常不是站在普通 bootloader 的视角设计的,而是更偏 ARM64 平台的底层固件和安全态运行时。
很多 ARMv8 平台里,TF-A 会负责这些事:
• 更早期的异常级初始化
• secure monitor / EL3 相关处理
• PSCI、CPU 上电、系统控制等运行时服务
• 把控制权交给后续非安全世界软件
所以你经常会在日志里看到:
• BL1
• BL2
• BL31
这些通常就是 TF-A 语境里的阶段。
你可以先把它理解成:
TF-A 更像是 ARM64 平台里负责更底层启动和安全态的固件层。
第六:TF-A 和 SPL 到底是什么关系?
这也是最容易混的地方。
答案不是简单的“谁替代谁”。
因为不同平台组合方式不一样。
常见情况有三种:
• 只有 SPL + U-Boot
早期 bring-up 主要由 SPL 完成
• TF-A + U-Boot
TF-A 负责更底层阶段,后面交 U-Boot
• TF-A + SPL + U-Boot
三者都在,各自负责不同层次的接力
所以不能简单说:
• TF-A 就是 SPL
• SPL 就是 TF-A
• 两者只能二选一
更准确的说法是:
TF-A 偏底层固件和安全态,SPL 偏资源受限阶段的加载器,U-Boot 偏完整 Linux 启动控制。
第七:最核心的区别,到底怎么记?
如果你想快速记住三者区别,可以直接记这三句:
TF-A:管更底层。
它更关注安全态、异常级和底层运行时。
SPL:管过渡。
它负责在早期资源有限时完成最小初始化,并把完整 bootloader 拉起来。
U-Boot:管启动 Linux。
它负责加载 kernel、dtb、initrd,并提供启动控制和交互能力。
所以一句话压缩就是:
TF-A 管底层,SPL 管过渡,U-Boot 管启动 Linux。
很多人第一次看启动链时,会把 TF-A、SPL、U-Boot 都统称成“前面的几段 bootloader”。
这样理解不算错,
但工程上不够用。
真正更有价值的理解方式是把边界分开:
• TF-A 看底层固件和安全态
• SPL 看早期资源受限阶段
• U-Boot 看完整 Linux 启动控制
只要这三层不混,
你再去看 DDR 初始化、secure world、U-Boot 环境变量、kernel 加载路径,脑子就会清楚很多。