当前位置:首页>Linux>Linux 启动第十篇: 90%工程师踩坑的TF-A、SPL、U-Boot关系,一文理清

Linux 启动第十篇: 90%工程师踩坑的TF-A、SPL、U-Boot关系,一文理清

  • 2026-10-11 08:33:41
Linux 启动第十篇: 90%工程师踩坑的TF-A、SPL、U-Boot关系,一文理清

很多人刚开始学嵌入式 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 加载路径,脑子就会清楚很多。

最新文章

随机文章