1. ATF启动流程概览:BL1、BL2、BL31、BL32、BL33各阶段职责与交互

做固件调试这么多年,我见过太多人一上来就对着启动日志从头看到尾,结果越看越懵。为什么?因为你不清楚每个阶段在干什么,自然不知道哪些日志是正常的,哪些是异常信号。

ATF(ARM Trusted Firmware)的启动流程,说白了就是一条从最底层硬件到操作系统内核的接力赛。每一棒都有自己的任务,交接不好就会翻车。今天我们就来拆解这五个关键阶段:BL1、BL2、BL31、BL32、BL33。

核心认知:ATF启动不是单线程的线性执行,而是一个「信任链」的逐级验证过程。每一级都要验证下一级的签名和完整性,确保系统在可信的状态下启动。

1.1 整体架构:五级接力赛

先看一张我手绘的启动流程图,你把它存脑子里,后面分析日志时随时对照。

ATF 五级启动流程与信任链 BL1 ROM Bootloader BL2 Trusted Boot Firmware BL31 EL3 Runtime BL32 Secure OS BL33 Non-Secure OS 验证+加载 配置+跳转 加载 加载 SMC 调用 关键说明: 1. 实线箭头表示加载/验证/跳转流程,虚线箭头表示运行时服务调用 2. BL31 是常驻 EL3 的运行时服务,BL32/BL33 通过 SMC 指令请求服务 3. 信任链从 BL1 开始逐级验证,任何一级验证失败都会导致启动终止

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 启动日志,我建议你按这个顺序扫读:

  1. 先看 BL1 输出 — 确认串口初始化成功,BL2 加载正常
  2. 再看 BL2 输出 — 检查所有镜像是否加载成功,签名验证是否通过
  3. 接着看 BL31 输出 — 确认运行时服务初始化完成,没有报错
  4. 然后看 BL32 输出 — 如果存在,检查 TEE 初始化是否正常
  5. 最后看 BL33 输出 — 确认引导程序成功接管

你想想看,如果 BL1 阶段就卡住了,后面根本不用看。这就是分层排查的思路。

1.5 本章小结

ATF 的五级启动流程,本质上是一个信任链的逐级传递。BL1 信任 BL2,BL2 信任 BL31/BL32/BL33,每一级都通过签名验证来确保下一级没有被篡改。理解了这个链条,你再看启动日志时,就能快速定位问题出在哪个环节。