4、ATF源码编译:ATF源码获取、编译选项解析、编译流程与产物分析
好,咱们进入实战环节。上一章我们把ATF的架构和启动流程捋了一遍,这一章就该动手了——把源码拉下来,编译出能跑的镜像。
说实话,ATF的编译比我想象中要简单。我第一次接触时,以为要配一堆交叉编译链、设置各种环境变量,结果发现它自带的构建系统做得挺完善。你只要把几个关键参数搞明白,基本就能一次通过。
4.1 源码获取
ATF的源码托管在GitHub上,官方仓库是 ARM-software/arm-trusted-firmware。我个人习惯用 git clone 拉取,然后切到稳定的release版本。
git clone https://github.com/ARM-software/arm-trusted-firmware.git
cd arm-trusted-firmware
git checkout v2.9
为什么推荐切到release版本?因为master分支可能包含一些实验性改动,不一定稳定。我在项目里吃过这个亏——用master编译出来的BL31在某个平台上死活跑不起来,后来发现是某个commit引入了bug。从那以后,我坚持用tag版本。
目前主流平台建议用v2.8或v2.9,这两个版本对ARMv8.2和v8.4的支持比较成熟。如果你用的是最新的ARMv9平台,那可能需要v2.10以上的版本。
4.2 编译选项解析
ATF的编译选项,说白了就是通过 make 命令传递的参数。你不需要全部记住,但有几个核心选项必须搞明白。
| 选项 | 说明 | 我的建议 |
|---|---|---|
PLAT |
目标平台,比如 qemu、fvp、rk3399 |
刚开始用 qemu 练手最方便 |
ARCH |
架构,aarch64 或 aarch32 |
现在主流都是64位,选 aarch64 |
DEBUG |
是否开启调试模式,0 或 1 |
开发阶段开 1,发布时关掉 |
LOG_LEVEL |
日志级别,0~50,越高越详细 | 调试时设 40,正常用 20 |
BL33 |
指定非安全侧镜像路径(通常是U-Boot) | 如果只测ATF,可以不设 |
TRUSTED_BOARD_BOOT |
是否启用可信启动 | 量产必须开,开发阶段可以先关 |
嗯,这里要注意 LOG_LEVEL 这个选项。我见过不少新手把日志级别开到50,结果终端刷屏刷到根本看不清关键信息。其实 LOG_LEVEL=40 已经能看到绝大部分调试信息了。
4.3 编译流程
ATF的编译流程,我画了一张图帮你理解:
实际编译命令长这样:
make CROSS_COMPILE=aarch64-linux-gnu- \
PLAT=qemu \
ARCH=aarch64 \
DEBUG=1 \
LOG_LEVEL=40 \
all
这里 CROSS_COMPILE 指定交叉编译工具链的前缀。如果你用的是ARM官方提供的 gcc-arm-10.2-2020.11-x86_64-aarch64-none-linux-gnu,那前缀就是 aarch64-none-linux-gnu-。不同工具链前缀可能不一样,这个要留意。
build_atf.sh,这样每次编译不用重新敲参数。我项目里至少改过几十次编译选项,脚本化之后省了不少时间。
4.4 编译产物分析
编译完成后,产物默认放在 build/<PLAT>/<BUILD_TYPE>/ 目录下。比如 build/qemu/debug/。我们来看看里面都有什么:
$ ls -la build/qemu/debug/
total 1024
-rw-r--r-- bl1.bin # BL1 镜像,约 32KB
-rw-r--r-- bl2.bin # BL2 镜像,约 64KB
-rw-r--r-- bl31.bin # BL31 镜像,约 128KB
-rw-r--r-- fip.bin # FIP 打包镜像,包含 BL2 + BL31
-rw-r--r-- bl1.elf # BL1 的 ELF 文件,带调试符号
-rw-r--r-- bl2.elf
-rw-r--r-- bl31.elf
-rw-r--r-- bl31.map # 内存映射文件,分析布局用
这几个文件各自有什么用?我简单说一下:
- bl1.bin:ROM里加载的第一个镜像,负责初始化最小系统,然后加载BL2。在QEMU平台上,这个文件通常不用我们自己烧录,QEMU内部已经模拟了ROM行为。
- bl2.bin:可信启动的第二阶段,负责加载BL31和BL33。如果你开启了可信启动,它还会做镜像校验。
- bl31.bin:这是最核心的——EL3运行时固件,提供SMC调用、PSCI电源管理等服务。我们后面做TEE集成,主要就是跟它打交道。
- fip.bin:Firmware Image Package,把BL2、BL31、BL33(如果有)打包成一个文件。QEMU启动时直接加载fip.bin就行。
bl31.bin 是你最需要关注的文件。TEE的SMC入口、安全中断处理、上下文切换都在这里面。
另外,bl31.map 这个文件很多人忽略,但我建议你养成看的习惯。它记录了每个函数、每个变量的内存地址。我在调试一次安全中断处理时,发现某个函数被链接到了意想不到的位置,导致跳转出错。打开map文件一看,原来是链接脚本里某个section的地址对齐没设对。
4.5 常见编译问题与避坑
编译过程中可能会遇到一些问题,我把自己踩过的坑列出来:
错误信息类似
aarch64-linux-gnu-gcc: command not found。解决办法:确保工具链已安装,并且
CROSS_COMPILE 路径正确。我习惯把工具链路径加到 PATH 环境变量里。
比如
PLAT=qemu 但没装QEMU相关的依赖。解决办法:ATF对QEMU平台依赖很少,基本只需要标准C库。如果是FVP平台,需要安装FVP模型。
开启调试模式后,bl31.bin 可能从128KB膨胀到500KB以上。
解决办法:调试阶段无所谓,发布时记得关掉
DEBUG=1。
我曾经在一个项目里,因为忘记关 DEBUG=1,导致BL31镜像超过了平台ROM的限制,启动时直接卡死。查了半天才发现是镜像太大,装不下了。嗯,从那以后我每次发布前都会检查一下镜像大小。
4.6 验证编译结果
编译完了怎么验证能不能跑?最简单的方法是用QEMU:
qemu-system-aarch64 -machine virt \
-cpu cortex-a57 \
-bios bl1.bin \
-semihosting \
-nographic
如果一切正常,你会看到ATF的启动日志,类似这样:
NOTICE: BL1: v2.9(release):v2.9
NOTICE: BL1: Built : 14:48:00, Mar 15 2024
NOTICE: BL2: v2.9(release):v2.9
NOTICE: BL2: Built : 14:48:01, Mar 15 2024
NOTICE: BL31: v2.9(release):v2.9
NOTICE: BL31: Built : 14:48:02, Mar 15 2024
看到这三行 NOTICE,说明你的ATF已经成功跑起来了。接下来就可以开始集成TEE了。
好,这一章的内容就到这里。ATF的编译其实不复杂,关键是理解每个选项的作用,以及知道编译产物里哪个文件是你真正需要的。下一章我们会把这些镜像烧录到目标平台上,看看实际运行效果。
make clean 再重新编译。有时候缓存会导致一些莫名其妙的问题,clean一下就好了。