4. BL2阶段详解:BL2的职责、平台配置、加载并验证后续镜像
好,我们进入BL2阶段。这是整个ATF启动流程里我个人觉得最“承上启下”的一环。BL1把接力棒交给你,你得把后面所有镜像都安排好——说白了,BL2就是那个“总调度+安检员”。
4.1 BL2的核心职责
BL2运行在EL3,但它不像BL1那样只做极简初始化。它的任务更复杂:
- 加载并验证后续镜像:BL31(EL3运行时固件)、BL32(可选的安全OS)、BL33(非安全侧Bootloader,比如U-Boot)。
- 管理平台配置:从FIP(Firmware Image Package)里解析镜像,并处理平台特定的硬件信息。
- 建立安全上下文:为BL31准备好运行环境,包括内存布局和参数传递。
嗯,这里要注意:BL2本身也是被BL1验证过的。所以整个信任链是连续的——BL1验BL2,BL2验后面的所有镜像。
信任链关键点:BL2是第一个“主动验证者”。它不再依赖BL1的验证逻辑,而是用自己的代码去验后面的镜像。一旦BL2被攻破,整个安全启动就完了。
4.2 平台配置:BL2怎么知道该做什么?
BL2不是写死的。它通过平台描述文件(比如plat/<platform>/下的代码)来适配不同硬件。我刚开始接触ATF时,最头疼的就是理清这些配置关系。
核心配置项包括:
| 配置项 | 作用 | 常见值 |
|---|---|---|
BL2_BASE |
BL2在SRAM中的基地址 | 0x04020000 |
BL2_LIMIT |
BL2可用的最大地址 | 0x04030000 |
BL31_BASE |
BL31加载地址 | 0x04003000 |
BL32_BASE |
BL32加载地址(可选) | 0x06000000 |
BL33_BASE |
BL33加载地址 | 0x80000000 |
这些值通常在include/plat/<platform>/platform_def.h里定义。我曾经在一个项目里因为BL2_BASE和BL31_BASE重叠,导致BL31加载时直接覆盖了BL2的代码——嗯,死机是必然的。
避坑指南:我曾经在调试一块新板子时,发现BL2加载BL31后系统就挂。查了两天才发现是BL31_BASE设在了BL2的代码段内。记住:BL2和BL31不能有地址重叠,除非你明确知道自己在做什么。
4.3 加载并验证后续镜像
BL2从FIP里读取镜像。FIP是一个打包文件,里面包含了所有需要验证的镜像和它们的元数据(比如哈希值、签名)。
流程大致如下:
- 解析FIP:找到每个镜像的偏移和大小。
- 加载到内存:把镜像从存储介质(比如eMMC、NOR Flash)读到SRAM或DRAM。
- 验证签名:用公钥验签,确保镜像没被篡改。
- 传递控制权:跳转到BL31入口。
代码层面,核心函数是load_auth_image()。它封装了加载和验证两个步骤。我习惯把它理解为“先读进来,再检查一下,不行就停”。
// 伪代码示意
int load_auth_image(unsigned int image_id,
image_info_t *image_data,
entry_point_info_t *entry_point) {
// 1. 从FIP加载镜像到内存
int err = load_image(image_id, image_data);
if (err) return err;
// 2. 验证镜像签名和哈希
err = auth_image(image_id, image_data);
if (err) {
ERROR("Image %u authentication failed!\n", image_id);
panic();
}
// 3. 设置入口点
entry_point->pc = image_data->image_base;
return 0;
}
你想想看,如果验证失败,BL2会直接调用panic()——系统就停在这里了。这是故意的:安全启动宁可死机,也不运行未经验证的代码。
4.4 BL2与后续镜像的关系
BL2加载完所有镜像后,会跳转到BL31。但跳转前,它会把一些关键信息通过bl31_params结构体传过去。这个结构体包含了:
- BL32和BL33的入口地址
- 平台特定的配置数据
- 内存布局信息
我见过不少新手在这里犯错:BL2传的参数不对,导致BL31启动后找不到BL33。说白了,BL2就像一个交接员,交接清单必须准确。
调试技巧:如果你在BL31启动后卡住,先检查BL2传给BL31的参数。可以在BL2的bl2_main()里加打印,把bl31_params的内容dump出来。我曾经靠这个办法半小时定位了一个参数对齐问题。
4.5 BL2阶段的SVG流程图
下面这张图展示了BL2从启动到跳转BL31的完整流程。我建议你对照代码看,会更清楚。
4.6 平台适配的常见陷阱
最后聊几个我踩过的坑:
- FIP打包顺序:BL2解析FIP时,镜像顺序必须和代码里定义的
image_id一致。我遇到过FIP里BL31和BL32顺序反了,结果BL2把BL32当BL31加载——直接死机。 - 内存对齐:BL2加载镜像时,目标地址必须按16字节对齐。有些平台要求更严格,比如64字节对齐。不满足对齐条件,验证会失败。
- 公钥存储:BL2用的公钥通常烧在OTP或eFuse里。如果公钥没烧对,所有镜像都验不过。我曾经在生产线上遇到过一批芯片OTP写入失败,排查了很久才发现是硬件问题。
总结一下:BL2是安全启动的“总闸门”。它做对了一切,后面的镜像才能安全运行。做平台适配时,多花时间检查地址、对齐和FIP结构,能省去大量调试时间。