3. 启动流程概述

说实话,ATF 开发环境搭建这件事,看着简单,但坑是真不少。我见过太多人卡在第一步——工具链装好了,编译却报一堆莫名其妙的错。嗯,今天咱们就把这事彻底捋清楚。

3.1 交叉编译工具链的选择与安装

ATF 是跑在 ARM 平台上的固件,你肯定不能用 PC 的 gcc 去编译它。你需要一套交叉编译工具链。说白了,就是在 x86 电脑上生成 ARM 机器码的工具集。

核心原则:工具链的版本必须与你的目标 ARM 架构匹配。AArch64 和 AArch32 的工具链不通用。

3.1.1 我常用的工具链

我个人习惯用 ARM 官方提供的 GNU-A 系列工具链。你可以从 ARM 官网的「Developer」->「Downloads」->「GNU Toolchain」找到它。下载时注意区分:

  • aarch64-none-elf-:用于编译 64 位 ARMv8-A 裸机程序,ATF 的 BL1/BL2/BL31 都用它。
  • arm-none-eabi-:用于编译 32 位 ARM 裸机程序,如果你在移植老平台可能会用到。

下载后解压到 /opt/toolchains/ 目录,然后把 bin/ 路径加到 PATH 环境变量里。我在项目中遇到过有人把工具链装到带空格的路径下,结果 Makefile 解析路径时直接崩了。所以,路径里千万别有空格或中文。

小技巧:装好后跑一下 aarch64-none-elf-gcc -v,确认版本号。我建议用 10.3-2021.07 或更新的版本,太老的工具链对 ATF 新特性的支持有问题。

3.1.2 验证工具链是否可用

写个最简单的 C 文件,交叉编译一下:

// test.c
void main(void) {
    while(1);
}
$ aarch64-none-elf-gcc -c test.c -o test.o
$ aarch64-none-elf-objdump -d test.o

如果能正常反汇编出 AArch64 指令,说明工具链没问题。如果报错说找不到 crt0.o 或 _start,别慌——裸机程序本来就没有这些,你只需要加 -nostdlib 选项就行。

3.2 依赖库准备

ATF 编译时依赖几个关键库。你想想看,它要处理加解密、镜像解析、平台初始化,这些功能总不能全自己手写吧?

依赖库 用途 获取方式
mbedtls 加密算法(SHA256、RSA、ECDSA) ATF 源码自带 mbedtls/ 子模块
fiptool 打包 FIP 镜像的工具 ATF 编译时自动生成
dtc 设备树编译器(某些平台需要) 系统包管理器安装或源码编译

注意:mbedtls 的版本要和 ATF 匹配。我曾经因为用了系统自带的旧版 mbedtls,结果编译 ATF 2.6 时签名验证一直失败。后来发现 ATF 源码里自带了 mbedtls 子模块,直接用 git submodule update --init 拉下来最省心。

拉取子模块的命令:

$ cd atf
$ git submodule update --init --recursive

如果你在编译时遇到 mbedtls_config.h 找不到的错误,八成是子模块没拉全。嗯,这个坑我踩过两次。

3.3 Makefile 基础与编译选项

ATF 的构建系统基于 Makefile。你不需要精通 Makefile 语法,但得知道几个关键变量和编译流程。

3.3.1 核心 Makefile 变量

编译 ATF 时,你通过 make 命令传入参数。我个人习惯把这些参数写到一个 build.sh 脚本里,避免每次敲一长串。

$ make CROSS_COMPILE=aarch64-none-elf- \
         PLAT=qemu \
         DEBUG=1 \
         BL33=../u-boot/u-boot.bin \
         all fip
变量名 说明 常用值
CROSS_COMPILE 交叉编译工具链前缀 aarch64-none-elf-
PLAT 目标平台名称 qemu, fvp, mt8186
DEBUG 是否开启调试信息 1 或 0
BL33 非安全世界镜像路径(通常是 U-Boot) ../u-boot/u-boot.bin
LOG_LEVEL 日志输出等级 40(ERROR), 50(NOTICE)

调试建议:开发阶段把 DEBUG=1 和 LOG_LEVEL=40 打开,这样能看到详细的启动日志。量产时记得关掉,否则固件体积会变大,启动速度也会变慢。

3.3.2 编译目标(target)

ATF 的 Makefile 提供了几个常用目标:

  • all:编译所有 BL 镜像(BL1、BL2、BL31)。
  • fip:生成 FIP 镜像包,把 BL2、BL31、BL33 打包到一起。
  • clean:清理编译产物。
  • distclean:彻底清理,包括配置文件。

你想想看,如果只跑 make all,你得到的是分散的 bl1.bin、bl2.bin、bl31.bin。但实际烧录时,通常只需要一个 fip.bin 文件。所以 make all fip 是我最常用的组合。

3.3.3 平台配置与编译选项

每个平台都有自己的配置文件,放在 plat/<vendor>/<platform>/ 目录下。比如 QEMU 平台的配置在 plat/qemu/qemu/ 里。

你可以通过 make PLAT_<xxx>=1 的方式开启或关闭某些功能。举个例子:

$ make CROSS_COMPILE=aarch64-none-elf- \
         PLAT=qemu \
         DEBUG=1 \
         PLAT_QEMU_MMAP=1 \
         all fip

这些平台特有选项的定义,你得去对应平台的 platform.mk 文件里查。我在项目中遇到过有人想当然地加了一个 PLAT_MY_FEATURE=1,结果 Makefile 根本不认,编译出来的固件行为完全不对。所以,加选项前一定先确认平台 Makefile 里有没有定义它。

3.4 知识体系与编译流程

下面这张图总结了 ATF 开发环境搭建的核心逻辑。你顺着这个流程走,基本不会出大问题。

ATF 开发环境搭建与编译流程 ATF 开发环境搭建与编译流程 1. 安装交叉工具链 aarch64-none-elf-gcc 2. 准备依赖库 mbedtls + dtc 3. 选择平台配置 PLAT=qemu 4. 执行编译命令 make CROSS_COMPILE=... PLAT=... DEBUG=1 all fip 5. 编译产物 bl1.bin + fip.bin 6. 验证与调试 QEMU 模拟运行 / 串口日志 ⚠️ 常见问题 工具链路径带空格 → 编译报错 | 子模块未拉取 → mbedtls 找不到 PLAT 拼写错误 → 平台配置不生效 | DEBUG=0 → 日志太少,调试困难

3.5 避坑指南

最后,我把这些年踩过的坑总结一下。你照着做,能省不少时间。

我曾经犯过的错:

  • 工具链版本太老:用 4.9 版本的 Linaro 工具链编译 ATF 2.8,结果链接时报错说 __aeabi_uldivmod 找不到。换到 10.3 版本后问题消失。
  • 忘记拉子模块:直接 git clone 完就编译,mbedtls 目录是空的,编译到一半报错。记得跑 git submodule update --init。
  • PLAT 参数拼错:把 PLAT=qemu 写成 PLAT=QEMU,Makefile 找不到平台目录,直接报错退出。大小写敏感,注意检查。
  • BL33 路径给错:给了相对路径但当前工作目录不对,编译成功但 FIP 包里没有 BL33,启动时卡在 Waiting for BL33。建议用绝对路径。

嗯,环境搭建这部分就这些内容。你把工具链装好、子模块拉全、Makefile 参数写对,编译 ATF 就是一条命令的事。别在这上面花太多时间,后面还有更多有意思的东西等着你呢。