第4章:编译系统入门:ATF的Makefile体系、编译选项详解、如何配置目标平台

说实话,我第一次接触ATF的编译系统时,头都是大的。

几百行的Makefile,各种变量嵌套,还有一堆莫名其妙的编译选项。我当时就在想:这帮写ARM固件的人,是不是故意把编译系统搞这么复杂?

后来做多了才发现,其实ATF的Makefile体系设计得相当精巧。它要支持几十种不同的ARM平台,还要兼顾各种安全启动场景,复杂度是必然的。今天我就带你把它拆开揉碎了讲清楚。

4.1 ATF的Makefile体系:三层结构

ATF的编译系统,说白了就是三层结构:

  • 顶层Makefile:在源码根目录,负责整体编译流程
  • 平台Makefile:在plat/目录下,每个平台有自己的makefile
  • 构建辅助脚本:在tools/和scripts/目录下,处理具体细节

我习惯把这三层比作「总指挥-地方官-办事员」的关系。顶层Makefile定大方向,平台Makefile管具体配置,辅助脚本干脏活累活。

核心要点:ATF的编译入口是顶层Makefile,但它会通过include机制把平台相关的配置拉进来。你不需要修改顶层Makefile,只需要配置好平台相关的变量就行。

4.2 编译选项详解:你必须要知道的几个关键变量

ATF的编译选项多到让人眼花缭乱。但别怕,真正常用的就那么几个。我列个表给你看:

变量名 作用 常用值 我的建议
PLAT 指定目标平台 qemu, rpi3, hikey960等 必填,不填编译不了
ARCH 指定架构 aarch32, aarch64 现在主流是aarch64
DEBUG 调试模式开关 0或1 开发阶段开1,发布时关掉
LOG_LEVEL 日志输出级别 10~50 调试用40,正式用20
BL33 指定非安全侧镜像路径 u-boot.bin路径 移植时经常要配
ARM_ARCH_MAJOR ARM架构主版本 7或8 根据你的核心来

举个例子,我当年移植ATF到某款国产芯片时,就踩过一个坑:LOG_LEVEL设成了50(最高级别),结果串口疯狂输出调试信息,启动时间从2秒变成了15秒。后来改成20,一切正常。

注意:DEBUG=1和LOG_LEVEL=50会显著增加镜像体积。我见过有人把这两个选项同时打开,结果BL31镜像从80KB膨胀到300多KB,直接撑爆了OCM空间。

4.3 如何配置目标平台:从零开始

配置目标平台,说白了就是告诉ATF:「我要给哪个板子编译,这个板子有什么特性」。具体分三步走:

4.3.1 第一步:选择平台目录

ATF的plat/目录下,每个子目录代表一个平台家族。比如:

plat/
├── arm/           # ARM官方开发板
│   ├── common/
│   ├── fvp/
│   └── juno/
├── hisilicon/     # 海思芯片
├── mediatek/      # 联发科
├── qemu/          # QEMU模拟器
└── rockchip/      # 瑞芯微

如果你要移植新平台,就在plat/下新建一个目录。我建议你复制一个已有的平台目录作为模板,比如复制qemu的,然后改改。这样省事很多。

4.3.2 第二步:编写平台描述文件

每个平台目录下,必须有一个platform.mk文件。这个文件告诉编译系统:

  • 你的平台用哪个CPU核心(Cortex-A53还是A72?)
  • 内存布局是怎样的(BL31放在哪?)
  • 有哪些外设(串口、定时器、GIC版本)
  • 安全特性(是否支持TBBR?是否用RoT?)

给你看个简化版的例子:

# 来自 plat/myboard/platform.mk
# 我自己的板子配置

# CPU核心配置
ARM_CORTEX_A53 := 1
ARM_ARCH_MAJOR := 8

# 内存布局
BL31_BASE := 0x10000000
BL31_SIZE := 0x00020000

# 外设配置
CONSOLE_UART_BASE := 0x1C090000
GICV3_SUPPORT := 1

# 安全特性
TRUSTED_BOARD_BOOT := 0
GENERATE_COT := 0

小技巧:我在做移植时,习惯先把TRUSTED_BOARD_BOOT关掉。等BL31能正常跑起来了,再逐步开启安全启动功能。否则问题混在一起,根本查不过来。

4.3.3 第三步:执行编译

配置好平台后,编译命令很简单:

# 基本编译命令
make PLAT=myboard DEBUG=1 LOG_LEVEL=40 all

# 如果你要生成FIP镜像(包含BL31+BL33)
make PLAT=myboard BL33=path/to/u-boot.bin fip

# 清理
make PLAT=myboard clean

嗯,这里要注意:make all会编译所有BL层(BL1、BL2、BL31、BL32)。但实际移植时,我建议你先只编BL31,因为它是ATF的核心,也是我们最常调试的部分。

4.4 编译系统的核心逻辑:一张图说清楚

说了这么多,你可能有点晕。我画了张图,把ATF编译系统的核心逻辑串起来:

ATF编译系统核心逻辑 顶层Makefile PLAT=myboard 平台Makefile (内存/外设/CPU配置) 编译选项 (DEBUG/LOG/BL33等) 编译过程 BL31.bin / fip.bin

这张图你看懂了吗?其实流程很简单:顶层Makefile根据PLAT变量找到对应的平台目录,然后加载平台Makefile和编译选项,最后执行编译生成镜像文件。

4.5 避坑指南:我踩过的三个坑

做ATF移植这几年,我在编译系统上栽过不少跟头。挑三个典型的跟你说说:

  1. 路径问题:我曾经把BL33指向了一个不存在的路径,结果编译报错说找不到文件。我查了半天,最后发现是路径里有个拼写错误。建议你用绝对路径,或者用$(shell pwd)动态获取当前目录。
  2. 缓存问题:ATF的编译系统会缓存一些中间文件。如果你改了平台配置但没make clean,可能会用旧的缓存。我习惯每次改配置都先make clean,虽然慢点但保险。
  3. 交叉编译链:ATF默认用aarch64-linux-gnu-前缀的工具链。如果你用的工具链名字不一样,记得设置CROSS_COMPILE变量。我见过有人用aarch64-none-elf-,结果链接阶段报一堆错。

总结一下:ATF的编译系统其实不复杂,核心就是PLAT变量+平台Makefile+编译选项。你只要搞懂这三者的关系,剩下的就是填参数的事。我个人建议,刚开始移植时先用QEMU平台练手,把编译流程跑通了再上真硬件。

好了,这一章的内容就到这。编译系统是ATF移植的「入口」,搞懂了它,后面的事情就顺了。

可可精品资料库