2. ATF基础概念

说实话,我第一次打开ATF源码的时候,也有点懵。目录一堆,文件散落,不知道从哪看起。后来啃了几个项目,才慢慢摸清楚它的设计思路。今天我就带你捋一遍,保证你以后拿到任何版本的ATF,都能快速找到关键文件。

源码根目录:一眼看穿全局

ATF的源码根目录,通常叫trusted-firmware-a。你进去之后,会看到这么几个核心目录:

  • bl1/ —— 安全世界的第一个启动阶段
  • bl2/ —— 安全世界的第二个启动阶段,负责加载后续镜像
  • bl31/ —— 安全运行时固件,处理SMC异常
  • bl32/ —— 可选的TEE(可信执行环境)
  • bl33/ —— 非安全世界的引导程序,通常是U-Boot或UEFI
  • plat/ —— 平台相关代码,你改得最多的目录
  • include/ —— 公共头文件
  • lib/ —— 通用库,比如c库、xlat表库
  • drivers/ —— 驱动代码
  • tools/ —— 辅助工具,比如证书生成

我个人的习惯:拿到一个新平台,先看plat/目录下的platform.mk和plat_def.h。这两个文件基本决定了你的板子怎么跑。

关键文件与模块:每个文件都有它的脾气

咱们挑几个最重要的文件说说。你想想看,ATF启动就像接力赛,每一棒都有固定的交接点。

文件/模块 位置 核心作用
bl1_entrypoint.S bl1/aarch64/ BL1入口,设置异常向量、初始化CPU
bl2_main.c bl2/ BL2主逻辑,加载BL31/BL32/BL33镜像
bl31_main.c bl31/ BL31初始化,注册运行时服务
runtime_svc.c bl31/ SMC异常分发核心
platform_def.h plat/<平台>/include/ 平台宏定义,内存布局、外设地址

一个小技巧:调试BL1的时候,别急着看C代码。先看bl1_entrypoint.S里的汇编,那里才是真正的第一行代码。我曾经在某个项目里,因为没注意汇编里的cache初始化顺序,折腾了两天才找到问题。

BL1/BL2/BL31/BL32/BL33:五级启动链

这五个概念,是ATF的核心。说白了,就是安全世界和非安全世界交替上场的过程。

BL1 —— 安全世界的“第一把火”

BL1是上电后第一个执行的代码。它通常放在ROM里,不可修改。它的任务很简单:

  • 初始化最基本的硬件(CPU、MMU、cache)
  • 验证BL2镜像的签名
  • 跳转到BL2

避坑指南:我曾经在移植BL1时,忽略了ROM空间限制。BL1代码稍微大了一点,结果链接失败。后来我不得不把一些初始化逻辑挪到BL2里。记住,BL1能省则省。

BL2 —— 安全世界的“后勤部长”

BL2运行在SRAM里,负责加载和验证后续所有镜像。它不参与运行时服务,干完活就退休了。

  • 从Flash或存储设备读取BL31、BL32、BL33
  • 验证每个镜像的证书和哈希
  • 把镜像放到指定内存位置
  • 跳转到BL31

注意:BL2的内存布局非常关键。如果BL2的堆栈和BL31的加载地址重叠,启动就会挂掉。我见过不止一个团队踩过这个坑。一定要仔细核对platform_def.h里的BL2_BASE和BL31_BASE。

BL31 —— 安全世界的“大管家”

BL31是安全运行时固件,它一直活着。它处理来自非安全世界的SMC(安全监控调用)请求。

  • 初始化中断控制器(GIC)
  • 注册运行时服务(PSCI、SIP等)
  • 设置安全和非安全世界的上下文切换
  • 跳转到BL33

BL32 —— 可选的“安全小助手”

BL32是TEE(可信执行环境),比如OP-TEE。它不是必须的。如果你的系统不需要TEE,可以把它留空。

  • 提供安全服务(密钥管理、安全存储)
  • 与BL31通过SMC通信
  • 与BL33(非安全世界)隔离

BL33 —— 非安全世界的“主角”

BL33就是非安全世界的引导程序,通常是U-Boot或UEFI。它运行在EL2(虚拟化扩展)或EL1(操作系统)级别。

  • 加载操作系统内核
  • 提供设备树或ACPI表
  • 通过PSCI接口与BL31交互(比如CPU开关)

启动流程全景图

为了让你更直观地理解,我画了一张图。这张图我每次培训都会用,你仔细看看。

ATF五级启动链流程图 ATF 五级启动链流程图 BL1 ROM / 安全世界 BL2 SRAM / 加载器 BL31 运行时固件 BL33 U-Boot / UEFI BL32 TEE (可选) 关键说明: 1. BL1 -> BL2 -> BL31 -> BL33 是主线,必须依次执行。 2. BL32 是可选分支,由 BL31 在运行时加载和管理。 3. 每个阶段都有严格的镜像验证,失败则停止启动。 4. BL31 是唯一常驻安全世界的组件,负责上下文切换。

各BL模块的内存布局

内存布局是ATF移植中最容易出问题的地方。我整理了一个典型的内存分配表,你参考一下。

模块 典型基地址 大小 存放位置
BL1 0x00000000 64KB ROM
BL2 0x04000000 256KB SRAM
BL31 0x04040000 512KB DRAM
BL32 0x04400000 4MB DRAM(安全区域)
BL33 0x04800000 8MB DRAM(非安全区域)

重要提醒:上面的地址只是示例。实际项目中,你必须根据芯片的Memory Map来调整。我遇到过有人直接把参考板的地址抄过来,结果板子根本起不来。每个平台的SRAM大小、DRAM起始地址都不一样,一定要看数据手册。

避坑总结:我踩过的几个坑

最后,分享几个我亲身经历过的坑,希望能帮你省点时间。

  • BL1代码太大:ROM空间通常只有几十KB。我一开始没注意,加了很多调试代码,结果链接报错。后来我把调试逻辑移到BL2里才解决。
  • BL2和BL31地址重叠:这是最常见的坑。BL2在SRAM里运行,BL31在DRAM里。但如果BL2的堆栈设置得太大,可能会覆盖BL31的加载地址。启动时BL31校验失败,直接死机。
  • BL32镜像没签名:ATF默认要求所有镜像都有签名。我刚开始测试时,为了省事跳过了签名验证,结果BL32加载后直接跑飞。后来老老实实把签名流程加上,问题就没了。
  • BL33入口地址不对:BL33的入口地址必须和BL31跳转时设置的一致。我有个同事改了U-Boot的链接地址,忘了同步修改ATF里的配置,结果BL33永远跑不起来。

嗯,源码结构这块,说白了就是搞清楚每个目录、每个文件、每个BL模块的职责。你只要把今天这张图记在脑子里,以后看ATF代码就不会迷路了。