4、ATF构建与集成:ARM Trusted Firmware的编译、配置与集成到Buildroot
好,咱们进入第四章。这一章要啃的,是整个启动链里最核心的一块——ARM Trusted Firmware,简称ATF。说白了,它就是负责把CPU从最原始的裸机状态,一步步带到能跑操作系统的那段“引导代码”。在ARMv8架构里,它运行在EL3异常级别,是整个系统的信任根。
我记得刚接触ATF那会儿,看着那一堆编译选项和平台宏,头都大了。后来在项目里踩过几次坑,才慢慢摸清楚它的脾气。今天我就把ATF的编译、配置,以及怎么把它优雅地塞进Buildroot这套流程,给你掰扯清楚。
4.1 ATF在启动链中的位置与作用
先看一张图,你就明白ATF在整个启动流程里到底站在哪。
你看,ATF主要产出三个镜像:BL1、BL2、BL31。BL1是第一个被CPU执行的代码,它初始化最基本的硬件,然后加载BL2。BL2负责加载BL31和后面的BL32、BL33。BL31运行在EL3,提供运行时服务,比如PSCI(电源管理)和SMC(安全监控调用)。
核心要点:ATF不只是一个引导加载器,它更是一个安全固件框架。它定义了从EL3到EL0的完整异常级别切换规范。你想想看,没有它,操作系统根本没法安全地管理电源和休眠。
4.2 ATF源码结构与编译配置
ATF的源码托管在 https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git。它的目录结构很清晰,我挑几个关键目录说说:
- plat/:平台相关代码。每个SoC厂商在这里放自己的板级支持包。比如
plat/rockchip、plat/allwinner。 - bl1/、bl2/、bl31/:对应各个启动阶段的源码。
- drivers/:驱动代码,比如GIC、UART、定时器。
- include/:头文件,定义了各种宏和接口。
- tools/:辅助工具,比如
fiptool用来打包镜像。
编译ATF,本质上就是调用 make,传入一堆参数。我个人习惯写一个shell脚本,把参数都列清楚,免得每次敲错。
# 以 Rockchip RK3588 为例
make CROSS_COMPILE=aarch64-linux-gnu- \
PLAT=rk3588 \
DEBUG=0 \
LOG_LEVEL=40 \
BL33=../u-boot/u-boot.bin \
all fip
这里每个参数都有讲究:
| 参数 | 说明 | 我的建议 |
|---|---|---|
CROSS_COMPILE |
交叉编译工具链前缀 | 必须与Buildroot工具链一致 |
PLAT |
目标平台名称 | 在 plat/ 目录下找对应名字 |
DEBUG |
0=release, 1=debug | 调试阶段用1,量产用0 |
LOG_LEVEL |
日志等级,0~50 | 40够用,50太啰嗦 |
BL33 |
非安全侧镜像路径 | 通常指向U-Boot的二进制 |
all fip |
编译目标,生成FIP包 | FIP包含BL2、BL31、BL32、BL33 |
小技巧:如果你只想编译BL31,可以只执行 make bl31。这在调试阶段能省不少时间。我曾经在调PSCI时,每次只改BL31的代码,编译加烧写只要10秒,效率高很多。
4.3 将ATF集成到Buildroot
Buildroot里对ATF的支持已经相当成熟了。它提供了一个专门的包叫 arm-trusted-firmware。你只需要在 make menuconfig 里配置一下就行。
具体步骤:
- 进入配置菜单:
make menuconfig - 找到Bootloaders:
Bootloaders ---> - 选中ATF:
[*] arm-trusted-firmware - 配置平台和选项:进入子菜单,填写平台名、BL33路径等。
配置界面大概长这样:
Bootloaders
─────────────────────────────────────────
[*] arm-trusted-firmware
Platform name (rk3588) --->
Custom repository version (v2.9) --->
[*] Build FIP image
(../output/images/u-boot.bin) BL33 image path
(--enable-pie) Additional ATF build options
这里有个坑,我必须要提醒你:BL33路径是相对于Buildroot输出目录的。如果你把U-Boot编译在外部,路径写错了,ATF编译会报错,说找不到BL33。我曾经因为这个折腾了一下午,最后发现是路径少了个 ../。
注意:ATF的版本要和你的SoC匹配。比如RK3588需要ATF v2.9及以上。如果版本不对,编译可能通过,但运行时BL2加载BL31会卡死。我建议你直接去SoC厂商的官方仓库拉ATF源码,别用上游主线,除非你很确定兼容性。
4.4 自定义ATF配置与调试
很多时候,默认配置不够用。比如你要调整BL2的加载地址,或者开启某个调试功能。这时候就需要修改ATF的配置文件了。
ATF的配置主要分布在两个地方:
- 平台头文件:比如
plat/rockchip/rk3588/include/platform_def.h,定义了内存布局、外设基地址等。 - Makefile:在
plat/rockchip/rk3588/platform.mk里,可以添加编译选项。
举个例子,我想在BL31里开启串口调试输出:
# 在 platform.mk 中添加
BL31_SOURCES += drivers/ti/uart/uart_16550.c
$(eval $(call add_define,PLAT_LOG_LEVEL_ASSERT))
然后重新编译:
make PLAT=rk3588 DEBUG=1 LOG_LEVEL=50 bl31
这样BL31启动时就会打印详细的调试信息。嗯,这里要注意,LOG_LEVEL=50会输出大量日志,可能会影响启动速度,但调试时很有用。
4.5 验证ATF集成是否成功
编译完成后,你会得到几个关键文件:
| 文件 | 路径(在Buildroot输出目录) | 说明 |
|---|---|---|
bl1.bin |
build/arm-trusted-firmware-xxx/build/rk3588/release/ |
BL1镜像,通常烧写到Boot ROM区域 |
fip.bin |
images/ |
FIP包,包含BL2、BL31、BL33 |
bl31.bin |
同上 | 单独的BL31镜像,调试用 |
怎么验证它跑起来了?最简单的方法:接上串口,看启动日志。如果看到类似下面的输出,说明ATF工作正常:
NOTICE: BL1: v2.9(release):v2.9-113-g1234567
NOTICE: BL1: Built : 14:30:00, Jan 15 2025
INFO: BL1: Booting BL2
INFO: BL2: v2.9(release):v2.9-113-g1234567
INFO: BL2: Built : 14:30:02, Jan 15 2025
INFO: BL2: Loading BL31
INFO: BL2: Loading BL33
NOTICE: BL31: v2.9(release):v2.9-113-g1234567
NOTICE: BL31: Built : 14:30:04, Jan 15 2025
如果卡在某个阶段,比如没有打印 Booting BL2,那多半是BL1加载BL2时出了问题。检查一下FIP包里的BL2地址是否正确,或者串口波特率是否匹配。
总结一下:ATF的集成,说白了就是三步:选对平台、配好参数、编译打包。但每一步都有细节,尤其是BL33路径和版本匹配。我建议你在Buildroot里第一次集成时,先用官方默认配置跑通,再慢慢加自己的修改。这样出了问题,也知道是哪里改坏了。