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开关)
启动流程全景图
为了让你更直观地理解,我画了一张图。这张图我每次培训都会用,你仔细看看。
各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代码就不会迷路了。