深入ATF源码:AArch64安全启动与平台移植实战
2026/9/9 5:43:37 网站建设 项目流程

1. 为什么ATF值得你花时间啃源码

做ARM底层开发的工程师,早晚会撞上Arm Trusted Firmware(ATF)。这是ARM官方开源的EL3固件实现,也是当前几乎所有ARMv8/AArch64方案的启动必经之路——从服务器CPU到手机SoC,再到工控板卡,一上电就跑ATF的BL1,后面跟着BL2、BL31。它解决的核心问题可以一句话概括:在Normal World和Secure World之间建立一道可信的隔离边界,并负责和产品形态相关的电源管理、安全启动、异常分发。

这篇内容不是ATF用户指南的中文翻译,也不是官方文档的复述。我会从源码实际结构出发,逐层拆解ATF各个镜像之间的关系,再讲我在实际做安全审计和平台适配时的判断方法,以及在QEMU和真实SoC上移植ATF会踩到哪些坑。适合的人:准备做安全固件开发的,做BSP移植的,以及想搞明白Secure Monitor里到底跑了什么的嵌入式老兵。

先说一个很多人误解的地方:ATF不是一个"固件"文件,它是一整套多级引导与运行时固件框架。它包含BL1、BL2、BL31、BL32四个主要镜像(如果不跑Secure Payload,BL32可以不要),整个链路从BL1加载到BL31常驻内存,完成从Loader到Runtime的跨越。这里面每一级的安全职责完全不同,源码目录划分也恰好对应这些职责边界,搞懂了这个划分,后续读代码效率会高很多。

2. 源码结构全景:bl1/bl2/bl31那些目录各自负责什么

2.1 顶层目录对应的引导阶段划分

ATF源码拉到本地后,顶层目录看起来很多,实际核心只有几个。bl1/对应Boot ROM阶段的代码,bl2/对应Trusted Boot Firmware,bl31/是EL3 Runtime Firmware,bl32/放的是Secure Payload的相关支持。严格讲BL32不归ATF强制管辖,OP-TEE这种Secure OS才是真正跑在BL32位置上的,ATF只是提供一个加载通道和SMC分发入口。

BL1的代码特点是极简、平台相关性强。因为它工作在SoC最早的启动环境里,而SoC厂商的BootROM通常只做了最基本的时钟、DRAM初始化,剩下的都要由BL1在这段有限空间里补上。BL1源码中你几乎看不到复杂抽象,大量直接的内存映射、串口初始化、MMU配置。它的任务单一而明确:验证BL2的镜像可靠,然后把控制权交过去。

BL2和BL1最大的区别是它处于DRAM可用的环境,因此能做更多系统性工作。BL2负责加载BL31、BL32、BL33(即下一级通用镜像,通常是U-Boot或UEFI),同时完成Trusted Board Boot的完整校验流程。从安全边界角度看,BL1和BL2都属于可信启动链的前半段,任何一环出问题,后面全盘皆输。

2.2 关键模块目录中真正值得读的文件

lib/目录是精华,尤其是lib/el3_runtime/lib/psci/el3_runtime里封装的是EL3世界的通用运行时逻辑,包括异常向量表、SMC分发入口、世界切换的上下文保存与恢复。想理解TrustZone到底是怎么软硬件配合的,这两个目录必读。lib/psci/实现了PSCI电源管理协议,CPU hotplug、suspend/resume、system off/on全都由这里的代码承接。

common/目录下的bl_common.c是所有镜像公用的加载校验逻辑,runtime_svc.c则是运行时服务注册与分发的核心。如果你要新增一个自定义SMC功能(比如给Secure OS增加一个消息通道),你需要理解runtime_svc.c里的DECLARE_RT_SVC机制,每个运行时服务都是一张表,通过SMC功能号的高位索引找到自己。

plat/目录是平台相关代码,也是移植工作的主战场。不同SoC厂商在这个目录下创建自己的子目录,内部存放的内存布局头文件、平台初始化函数、GIC配置、串口驱动这些都是一个一个平台差异的具体呈现。想评测ATF是否真正适配某款芯片,直接看plat/比看其他模块都有效——这里写清楚了硬件和固件之间的所有交互细节。

2.3 编译系统的线索:makefile体系与fiptool

ATF采用了层级Makefile结构,入口在根目录的Makefile。顶层参数最重要:PLAT指定目标平台,TARGET_BOARD用于平台内部细分,DEBUG控制是否带调试信息,LOG_LEVEL调节日志详细度。我第一次编译的时候卡在不知道CROSS_COMPILE必须指定这件事上,命令行少了这个参数,make PLAT=qemu会直接报找不到编译器,提示也不算友好。

编译完成后生成的关键产物包括bl1.binbl2.binbl31.bin,以及一个名为fip.bin的打包镜像。FIP是Firmware Image Package的缩写,里面封装了BL2之后各级镜像的头部元信息,加载器按照头部信息把对应镜像放到指定物理地址。动手移植时,你必然要和fiptool打交道——这是ATF提供的一个命令行工具,专门用于打包、更新、解包FIP镜像,--dump参数可以查看当前FIP里装了哪些镜像以及各自加载地址。

3. 安全固件工程审计:信任链、SMC调用与EL3隔离是这么实现的

3.1 可信启动链条的层层签名校验

安全审计ATF,最先看的就是安全启动链路。ATF实现了Trusted Board Boot(TBB),整体思路是建立一个逐级校验的信任链。BL1默认被SoC的BootROM加载,通常BootROM会用SoC内部的OTP熔丝区保存的根密钥来验签BL1(这部分代码各厂商自研,ATF不涉及),BL1验签BL2,BL2验签BL31、BL32和BL33。每一级只信任上一级验证过的镜像,形成了一个单向信任链。

ATF里具体做验签的模块在drivers/auth/目录,核心逻辑是基于mbedTLS证书库实现的。每级镜像头部都携带对应的证书链和签名值,ATF利用mbedTLS去做X.509证书解析、RSA/ECDSA验签、哈希比对。审计时重点看两部分:一是根密钥的存储与部署方式,二是校验失败时走什么流程。前者决定攻击者能否替换公钥,后者决定出问题时的降级路径是否安全。

一个容易在审计中注意到的风险点是BL1和BL2之间如果没有强制开启TRUSTED_BOARD_BOOT,那么攻击者在能物理干预启动介质的情况下可以直接替换BL2镜像。所以做产品安全审计的时候,我不只看ATF默认配置,还要检查平台编译参数里是否明确开了TRUSTED_BOARD_BOOT=1,同时确认证书生成工具链有没有保存在不可信环境中。

3.2 SMC调用机制:世界切换的唯一合法入口

CPU在AArch64下通过smc指令触发SMC异常,EL3的异常向量表捕获后按功能号分发到对应运行时服务。ATF的SMC分发逻辑在bl31/aarch64/runtime_exceptions.Slib/el3_runtime/aarch64/context_mgmt.c中。分发过程遵循SMCCC(Secure Monitor Call Calling Convention)约定,功能号的高字节用来索引运行时服务。

我在读这块代码时一个很深的体会是:上下文切换的开销和正确性都在细节里。ATF在每次SMC进入和返回时要保存/恢复EL3通用寄存器、系统寄存器、以及Secure/Normal两个世界的上下文。context_mgmt.c里那些看起来繁琐的结构体字段赋值,全部对应硬件系统寄存器的现场保全。如果移植时GIC配置和中断路由没弄对,SMC handler里收到的中断上下文可能是错的,这种问题排查起来极其痛苦。

3.3 EL3运行时隔离的内存与中断护栏

ATF在EL3建立了一套隔离机制,核心在于内存访问权限和中断归属。内存层面,它通过MMU页表把Secure世界和Normal世界的内存区域严格区分,防止Normal World的非安全访问触达Secure内存。中断层面,GIC配置里把Secure中断(SGI、PPI、SPI)路由到EL3处理,普通外设中断走Normal World。这背后的硬件底座就是TrustZone地址空间控制器以及GIC的安全路由能力。

审计时我会特别看重plat_setup阶段的页表构建逻辑和GIC配置。一个常见的问题是:平台头文件中的TZRAM_BASETZRAM_SIZE定义是不是真的把TZRAM区域保护起来了,还是只是形式上有这个宏。另一个常见问题是GIC v2/v3版本与驱动代码匹配错误,导致SPI中断无法正常分组。这类问题卡住的话从日志上看不出明显征兆,通常要依靠断点或者增加LOG_LEVEL来定位。

4. 平台移植落地:从QEMU启动到真实SoC的完整路线

4.1 起步选择:为什么QEMU是移植实验的第一站

ATF官方提供多个虚拟平台支持,包括QEMU的qemu平台、ARM的FVP平台fvp,以及各种开发板的模拟环境。我个人强烈建议新接触移植的工程师先从QEMU平台入手。原因是QEMU的virt机器是一个高度简化的ARM系统模型,没有真实板上那些电源轨、复杂时钟树和各种外设中断路由问题,你能把精力完全聚焦在ATF自身的逻辑上。

编译QEMU平台的方法很简单:

make CROSS_COMPILE=aarch64-linux-gnu- PLAT=qemu DEBUG=1

编译完成后在build/qemu/debug/可以看到bl1.binbl2.binbl31.binfip.bin等产物。接着启动:

qemu-system-aarch64 -machine virt -cpu cortex-a53 -nographic \ -bios bl1.bin -m 1G

QEMU的-bios参数直接加载BL1镜像。启动后你会看到ATF的日志打印,显示BL1、BL2、BL31依次初始化。这个过程跑通后,等于完成了ATF移植的"最小闭环",后续在真实SoC上做的事情,本质是把这个闭环里的平台相关部分替换成自己板子的实际情况。

4.2 真实SoC移植要动哪些文件

QEMU跑通只是热身。真实SoC移植时,第一步是在plat/下新建一个自己的平台目录,比如plat/myboard/,然后拷贝一个参考平台的结构。多数人会以官方已有的arm平台为模板,因为它的抽象更通用些。最关键的文件有:

  • platform.mk:定义平台编译参数,包括BL2的源文件、平台外设驱动、GIC版本、是否开启安全启动。
  • plat_def.h(或者common_def.h):定义内存地址空间,包括TZRAM起始地址与大小、非安全内存区域范围、以及各级镜像的加载地址分布。
  • plat_setup.c:实现平台初始化入口,包括串口、定时器、GIC、内存映射关系的建立。
  • plat_helpers.S:一些平台相关的启动汇编辅助代码,比如初始化异常向量表或者配置系统寄存器。

这些文件之间有个隐含的依赖关系容易忽略:platform.mk中指定的源文件路径会直接决定编译进入镜像的代码量,如果漏加某个驱动文件,链接期不会报错,但对应的函数可能因为弱符号原因变成空操作。这是真遇到过的现象,串口初始化函数没被编进去,控制台一片黑,查了很久最终用nm看符号表才发现函数根本没在镜像里。

4.3 内存布局设计:ATF移植中最容易翻车的点

内存布局在ATF移植中处于核心位置。ATF本身运行在TZRAM(TrustZone RAM)中,设计原则是BL1占用起始端固定大小,BL2在DRAM临时空间运行,BL31(含BL32)则从固定地址常驻。这个地址分配需要在plat_def.h中定义清晰,并和各镜像的链接脚本对应一致。

一个典型的常见错误:BL31运行地址和BL33(比如U-Boot)的加载地址发生重叠。ATF执行完BL31初始化后,会跳到BL33的入口;如果BL31代码把自身放在0x40000000,而U-Boot的链接地址也在这个区间,那么跳转后立即跑飞。这类问题通过查看FIP内各镜像的实际加载地址,和平台头文件的内存区域定义做比对,往往一眼就能发现。

实际工作里我还遇到过可执行段被放在只读页表区域导致启动即崩溃的情况。这种问题的排查方式比较原始——逐段检查dcache/mmu初始化前后的代码执行,利用QEMU的单步调试能力逐步定位崩溃点。

4.4 用fi ptool管理多镜像引导链

在真实产品中,烧录的不会是裸的bl1.bin加一堆零散二进制,而是一个FIP包。使用fiptool打包:

fiptool create --tb-fw build/myboard/debug/bl2.bin \ --soc-fw build/myboard/debug/bl31.bin \ --nt-fw u-boot.bin \ --tos-fw optee.bin \ my-fip.bin

打包出的FIP会按照头部元信息把各级镜像记录进去。BL2运行时通过load_img系列函数从FIP中解析对应镜像加载到指定地址。如果要改用--dump查看现有FIP内容:

fiptool info my-fip.bin

每一条都会列出镜像类型、UUID、加载标志等信息。移植的时候多养成检查FIP内容的习惯,不要靠记忆猜烧进去什么了——这个工具能省很多烧录调式的时间。另外新版ATF还支持--nt-fw-config等扩展参数,涉及BlueSpecIA等配置解析时也能用得上。

4.5 从QEMU迁移到真实板卡时的清单

搞过几次板级移植之后,我整理了一份自己用的检查清单,基本能覆盖大部分翻车点:

  • 确认串口引脚和调试终端设置正确,否则第一板启动根本看不到日志。
  • 确认GIC版本与实际硬件匹配,GIC v2和v3的驱动代码不同,配置错误直接导致中断失效。
  • 确认内存映射:包括TZRAM地址、DRAM地址、以及各级镜像加载地址的三方对齐。
  • 确认时钟初始化:ATF早期阶段的时钟依赖平台代码完成,否则DRAM初始化或者外设访问都会出现随机错误。
  • 确认安全启动开关与证书链:至少先关闭TRUSTED_BOARD_BOOT跑通基本功能,再逐步开启验签。
  • 确认BL33入口地址:U-Boot或UEFI的实际头部位置和ATF跳转目标必须一致。

5. 我实测踩过的坑与对应的排查链路

5.1 串口没有输出的问题排查

第一次在自研板上跑ATF,板子上电后串口完全没有内容。这个现象最容易让人抓狂,因为"没输出"不代表代码没跑。我的排查链路是这样:先用JTAG/调试器确认PC是不是停在BL1入口;如果停在入口,检查是不是串口驱动初始化失败;如果PC已经跑飞了,那首选怀疑内存映射和代码重定位。对于后者,用调试器单步跟在bl31_main之前设置断点,看代码空间跳到哪里去了——我那次发现是链接脚本里BL31基准地址被平台头文件覆盖,实际镜像没有加载到指定位置。

工程上应对"没输出"最有效的办法是最小化环境依赖,比如先在QEMU上验证相同编译参数下日志正常,再回来隔离板级问题。这样既验证了工具链和编译流程,又能把怀疑范围缩小到平台代码。

5.2 GIC版本不匹配导致中断完全失效

某次在一块含GIC-400的板子(GICv2架构)上用了GICV3配置,结果就是跑完BL31后,SMC请求没问题,但所有物理中断都不触发。查了半天发现platform.mk中GIC相关宏定义写错,导致GIC驱动以GICv3模式去访问没有实现相关寄存器的硬件,当然整个中断机制瘫痪。这类问题在源码审计中也极具迷惑性——代码看着没问题,实际寄存器映射完全不对。

经验值就是:移植新平台前先确认GIC精确型号,并对照docs/里支持矩阵选择驱动版本。对于ARM通用GIC,ATF驱动接口本身很稳定,难点完全在版本匹配。

5.3 烧录FIP却跑不起来:检查校验和与头部对齐

还有一次产品化测试阶段,固件交到产线烧录后出现部分板卡启动失败,且失败率不低。重新烧录同一份镜像到故障板,有些能恢复,说明不是硬件逻辑失效,大概率是烧录时FIP镜像头部数据被截断或者校验区域写不完整。把FIP拆开对比正常板卡与异常板卡的二进制差异后,发现TTBR等无关地址被优化工具打包时错位了2字节,导致BL2验签时RSA签名长度校验和失败。

自那之后我在产线流程中会额外加一道FIP完整性校验步骤:用fiptool info在离线端打印FIP内所有镜像的布局和哈希,再比对产线烧录器读回的数据,确保烧录前和烧录后镜像完全一致。

5.4 工具链选择与编译期警告的陷阱

编译ATF推荐使用较新的aarch64-none-elf-gccaarch64-linux-gnu-gcc,版本太老的编译链会在浮点调用约定上出问题——不认识-mgeneral-regs-only选项编译出来的代码可能在EL3上下文切换时丢掉浮点状态。这里尤其不要用老旧的ARM Compiler 5.06(那个是给Cortex-M/A32传统工作流用的),ATF源码从设计上就没有为ARMCC v5做适配,硬编也能编过一部分,但链接期各种符号缺失。遇到编译告警不能当成无事发生,ATF里很多告警是平台代码宏没有正确展开的征兆——宁可停下来查清楚,也别带着告警往下走。

5.5 安全启动开启后的一次现场返工

我印象最深的问题是在某客户GPU服务器板卡上,为了满足安全需求,我强行开启了TRUSTED_BOARD_BOOT,结果BL2验签BL31时老报"Certificate invalid"。当时先怀疑是证书生成工具问题,后来用ATF的cert_create工具配合--rot-key重新生成根密钥和对应证书后解决了。回看原因,是原始的根证书没和当前镜像的Image ID对应关系对上——证书链和镜像链的映射必须一致,这在做批量产品时尤其容易出错。

经验就是:开启安全启动前,一定要在本地保留一套完整的证书生成链路脚本和对应的密钥备份机制,否则后期产品升级时连自己都没法签新固件。

6. 源码评测总结与后续扩展方向

从架构全景到平台落地,ATF的整体设计质量在同类固件项目中属于上乘。它与具体SoC耦合度控制得比较好,平台代码和通用逻辑的边界清晰,源码组织也适合后来者维护。举一个细节:plat目录中的平台操作接口都设计成函数指针表或弱符号形式,这让替换某块硬件驱动时不需要改通用逻辑代码。当然,这种灵活性的代价是学习曲线陡——新手第一次接触往往不知道自己该改哪一层,极易一头扎进平台代码里。

后续如果你要深入,有几条值得走的路:一是研究OP-TEE与ATF的配合方式,特别是SMC分发如何把请求转发给Trusted OS;二是看原生的spm/ff-a实现,新版ATF在支持ARM FF-A规范方面有较大投入,对搞虚拟化和多安全分区的人来说是趋势;三是仔细阅读lib/el3_runtime下一百多行的启动汇编,把异常向量、世界切换、中断代理解析一遍,做完这一轮你会对TrustZone机制有真正质的理解。

最后分享一个小技巧:读ATF源码时,先不要贪多。从bl31_main进入,顺着runtime_svc_initsmc_handler64走一遍主流程,比闷头细读整个平台目录要高效得多。ATF本身不是那种每行都值得读的代码,它有强烈的路径依赖,抓住主线后再按需回看就是最好的节奏。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询