1. ATF启动流程概览:BL1、BL2、BL31、BL32、BL33各阶段职责与交互
做固件调试这么多年,我见过太多人一上来就对着启动日志从头看到尾,结果越看越懵。为什么?因为你不清楚每个阶段在干什么,自然不知道哪些日志是正常的,哪些是异常信号。
ATF(ARM Trusted Firmware)的启动流程,说白了就是一条从最底层硬件到操作系统内核的接力赛。每一棒都有自己的任务,交接不好就会翻车。今天我们就来拆解这五个关键阶段:BL1、BL2、BL31、BL32、BL33。
核心认知:ATF启动不是单线程的线性执行,而是一个「信任链」的逐级验证过程。每一级都要验证下一级的签名和完整性,确保系统在可信的状态下启动。
1.1 整体架构:五级接力赛
先看一张我手绘的启动流程图,你把它存脑子里,后面分析日志时随时对照。
1.2 各阶段职责详解
BL1 — 信任链的起点
BL1 是芯片上电后执行的第一段代码,固化在 ROM 里,不可修改。它的任务很简单:初始化最基本的硬件(比如串口、DRAM 控制器),然后从 Flash 中找到 BL2 的镜像,验证签名,加载到 SRAM 中,跳转过去。
我个人习惯把 BL1 叫做「看门大爷」——它只做两件事:开门(初始化硬件)、查证件(验证 BL2)。
实战经验:我在调试某款 SoC 时遇到过 BL1 阶段串口无输出的问题。查了半天发现是 BL1 的串口初始化代码依赖一个未生效的 PLL 时钟。说白了,硬件还没准备好,代码就急着用——典型的时序问题。
BL2 — 真正的加载器
BL2 运行在 Trusted SRAM 中,是信任链的第二环。它负责从非易失存储(eMMC、NAND、SPI NOR 等)中加载 BL31、BL32、BL33 的镜像,并验证它们的签名。
BL2 的日志里你会看到类似这样的输出:
INFO: Loading image id=3 (BL31) at address 0x04001000
INFO: Image hash verified successfully
INFO: Loading image id=4 (BL32) at address 0x06000000
INFO: Loading image id=5 (BL33) at address 0x08000000
嗯,这里要注意:如果 BL2 阶段报 Image hash verification failed,那基本就是镜像被篡改或者烧录错了。我曾经因为 eMMC 分区表写错,导致 BL2 加载了错误的 BL31 镜像,卡了整整两天。
BL31 — EL3 的管家
BL31 是 ATF 的核心,运行在 EL3 异常级别。它初始化中断控制器(GIC)、系统计数器、电源管理控制器等,然后进入一个等待状态,随时响应来自 BL32 或 BL33 的 SMC 请求。
说白了,BL31 就是安全世界和非安全世界之间的「海关」。所有跨世界的调用(比如请求安全服务、切换 CPU 电源状态)都必须经过 BL31 的审核和转发。
| 服务类型 | SMC 功能 ID | 说明 |
|---|---|---|
| PSCI | 0x84000000 - 0x8400FFFF | CPU 电源管理、系统挂起/重启 |
| SiP | 0x82000000 - 0x8200FFFF | 厂商自定义服务 |
| OEM | 0x83000000 - 0x8300FFFF | OEM 特定功能 |
| TOS | 0x81000000 - 0x8100FFFF | 可信操作系统服务 |
BL32 — 安全世界的守护者
BL32 是一个可选组件,运行在 S-EL1。它通常是一个可信操作系统(如 OP-TEE、Trusty),提供安全存储、加密运算、密钥管理等功能。
如果产品不需要安全服务,BL32 可以完全跳过。但一旦用了,它的初始化日志就必须仔细看。我记得有一次客户反馈手机指纹支付失败,查日志发现 BL32 初始化时 TEE 上下文创建失败,原因是安全存储分区损坏。
BL33 — 最终的目标
BL33 就是非安全世界的引导程序,通常是 U-Boot 或 EDK2。它运行在 EL2,负责加载操作系统内核(Linux、Android 等)。
从 ATF 的角度看,BL33 启动完成后,ATF 的使命就结束了。剩下的工作交给操作系统,ATF 只作为后台服务响应 SMC 请求。
1.3 阶段间的交互机制
这五个阶段不是各干各的,它们之间有明确的交互协议。我总结成一张表,方便你对照日志分析:
| 交互 | 发起方 | 接收方 | 通信方式 | 典型日志关键词 |
|---|---|---|---|---|
| 加载 BL2 | BL1 | BL2 | 函数调用 + 跳转 | Loading BL2 |
| 加载 BL31/BL32/BL33 | BL2 | BL31/BL32/BL33 | FIP 解析 + 镜像加载 | Loading image id=3 |
| 运行时服务 | BL32/BL33 | BL31 | SMC 指令 | SPD: svc 0x84000002 |
| 电源管理 | BL33 | BL31 | PSCI SMC | PSCI: CPU_SUSPEND |
避坑指南:我曾经遇到一个 case,BL2 加载 BL31 成功后,BL31 初始化到一半就挂了。日志里只看到 BL31: Initializing runtime services,然后就没有然后了。后来发现是 BL31 的 GIC 初始化代码依赖一个 BL2 没有配置的时钟域。说白了,BL2 和 BL31 之间的「接口契约」没对齐——BL2 以为它配好了,BL31 以为它没配。
1.4 启动日志的阅读顺序
拿到一份完整的 ATF 启动日志,我建议你按这个顺序扫读:
- 先看 BL1 输出 — 确认串口初始化成功,BL2 加载正常
- 再看 BL2 输出 — 检查所有镜像是否加载成功,签名验证是否通过
- 接着看 BL31 输出 — 确认运行时服务初始化完成,没有报错
- 然后看 BL32 输出 — 如果存在,检查 TEE 初始化是否正常
- 最后看 BL33 输出 — 确认引导程序成功接管
你想想看,如果 BL1 阶段就卡住了,后面根本不用看。这就是分层排查的思路。
1.5 本章小结
ATF 的五级启动流程,本质上是一个信任链的逐级传递。BL1 信任 BL2,BL2 信任 BL31/BL32/BL33,每一级都通过签名验证来确保下一级没有被篡改。理解了这个链条,你再看启动日志时,就能快速定位问题出在哪个环节。