3. ATF 源码结构分析:关键目录的功能与依赖关系
ARM Trusted Firmware (ATF) 的源码采用分层模块化设计,将安全世界的启动流程拆解为多个独立的镜像阶段。理解各目录的职责与依赖关系,是进行启动优化和定制移植的基础。本章将深入分析 bl1、bl2、bl31、bl32、common 和 plat 这六个核心目录。
3.1 整体架构与启动流水线
ATF 的启动流程是一个严格的线性流水线,每个 Boot Loader (BL) 阶段负责特定的初始化任务,并加载下一个阶段。下图展示了从 ROM 到 Rich OS 的完整启动链路。
关键概念: ATF 的启动顺序是 BL1 → BL2 → BL31/32 → 非安全世界 (U-Boot/Linux)。BL33 (如 U-Boot) 由 BL31 在退出安全世界后加载。
3.2 关键目录功能详解
每个 BL 阶段对应源码中的一个顶级目录,它们各自承担不可替代的职责。下表总结了各目录的核心功能与输出产物。
| 目录 | 全称 | 核心功能 | 输出产物 |
|---|---|---|---|
| bl1 | Boot Loader Stage 1 | ROM 加载后的第一个固件,初始化 CPU、平台基础外设、TrustZone 控制器,加载 BL2 到 SRAM。 | bl1.bin |
| bl2 | Boot Loader Stage 2 | 运行在 SRAM,负责加载 BL31、BL32 和 BL33 镜像到指定内存,并传递平台参数。 | bl2.bin |
| bl31 | Boot Loader Stage 3 (EL3 Runtime) | EL3 异常等级运行时固件,处理安全监控调用 (SMC)、电源管理、PSCI 服务,是安全世界的入口。 | bl31.bin |
| bl32 | Boot Loader Stage 2 (Secure OS) | 安全操作系统镜像(如 OP-TEE),提供安全服务,运行在 S-EL1。 | bl32.bin |
| common | Common Library | 存放各 BL 阶段共享的通用代码,如内存管理、链表操作、控制台输出、异常处理框架。 | 静态库 (libcommon.a) |
| plat | Platform Porting Layer | 平台移植层,包含所有与具体 SoC 和板级硬件相关的代码,如 GIC、UART、定时器、电源域。 | 平台定制代码 |
3.3 目录间的依赖关系
ATF 的依赖关系遵循严格的单向原则:低阶段依赖高阶段提供的接口,高阶段依赖低阶段完成初始化。同时,所有 BL 阶段都依赖 common 和 plat 目录。
- bl1 → bl2:BL1 负责加载 BL2,BL2 的入口地址和参数由 BL1 通过平台结构体传递。
- bl2 → bl31/bl32/bl33:BL2 解析镜像头部信息,将 BL31、BL32 和 BL33 加载到各自的内存区域,并设置好跳转上下文。
- bl31 → bl32:BL31 在初始化 EL3 运行时环境后,通过 SMC 调用或直接跳转进入 BL32(安全 OS)。
- bl31 → bl33:BL31 在完成安全世界初始化后,通过
el3_exit退出到非安全世界的 BL33。 - 所有 BL → common:每个 BL 阶段都链接
common库,使用其提供的通用函数,如console_putc、memcpy、assert。 - 所有 BL → plat:每个 BL 阶段都通过平台宏和回调函数访问硬件资源,如
plat_get_my_entrypoint、plat_my_core_pos。
优化提示: 在启动优化中,
bl2 和 bl31 是主要优化对象。减少 BL2 的镜像加载时间(如使用 DMA 或 XIP 执行)以及精简 BL31 的初始化流程,能显著缩短整体启动时间。
3.4 平台目录 (plat) 的内部结构
plat 目录是移植工作的核心,其内部按厂商和 SoC 系列组织。一个典型的平台目录结构如下:
plat/
├── arm/ # ARM 官方参考平台
│ ├── common/ # ARM 平台通用代码
│ ├── fvp/ # Fixed Virtual Platform 模型
│ └── juno/ # Juno 开发板
├── hisilicon/ # 海思平台
│ └── hikey960/ # HiKey960 开发板
├── mediatek/ # 联发科平台
│ └── mt8173/ # MT8173 SoC
├── nvidia/ # 英伟达平台
│ └── tegra/ # Tegra SoC 系列
├── rockchip/ # 瑞芯微平台
│ ├── common/ # Rockchip 通用代码
│ ├── rk3328/ # RK3328 SoC
│ └── rk3399/ # RK3399 SoC
└── st/ # STMicroelectronics 平台
└── stm32mp1/ # STM32MP1 系列
每个平台目录下必须实现 platform.mk 和一系列以 plat_ 开头的 C 文件,用于定义内存布局、电源管理、中断控制器等硬件参数。
3.5 依赖关系总结图
下图以模块依赖的视角,展示了各目录之间的编译与运行时依赖关系。
3.6 编译与链接依赖
在构建系统中,依赖关系通过 Makefile 和链接脚本体现。以下是各 BL 阶段与 common、plat 的编译依赖示例:
# 以 bl31 的 Makefile 片段为例
BL31_SOURCES += $(COMMON_DIR)/bl_common.c
BL31_SOURCES += $(COMMON_DIR)/tlb.c
BL31_SOURCES += $(PLAT_DIR)/plat_psci.c
BL31_SOURCES += $(PLAT_DIR)/plat_topology.c
# 链接时,bl31.elf 需要链接 libcommon.a 和平台对象
$(BUILD_DIR)/bl31/bl31.elf: $(BL31_OBJS) $(COMMON_LIB) $(PLAT_LIB)
$(LD) -o $@ $^ -T $(LINKER_SCRIPT)
移植注意事项: 在添加新平台时,必须确保
plat 目录下实现了所有被 BL1、BL2、BL31 调用的平台接口。遗漏任何一个 plat_ 函数都会导致链接失败。建议从 ARM 官方参考平台(如 FVP)复制一份,然后逐步替换硬件相关代码。
3.7 启动流程中的关键函数调用链
为了更直观地理解目录间的协作,下表列出了从 BL1 到 BL33 的关键函数调用及其所属目录。
| 阶段 | 关键函数 | 所属目录 | 功能描述 |
|---|---|---|---|
| BL1 | bl1_main() |
bl1 | BL1 主入口,初始化控制台、内存、加载 BL2 |
| BL1 | plat_get_my_entrypoint() |
plat | 获取当前 CPU 的入口点(用于多核启动) |
| BL2 | bl2_main() |
bl2 | BL2 主入口,解析镜像列表,加载后续阶段 |
| BL2 | load_image() |
common | 从存储介质加载镜像到内存 |
| BL31 | bl31_main() |
bl31 | BL31 主入口,初始化运行时服务、PSCI、中断 |
| BL31 | psci_setup() |
bl31 | 初始化电源管理状态机 |
| BL31 | el3_exit() |
common | 从 EL3 退出到非安全世界 |
| BL32 | opteed_setup() |
bl32 | 初始化 OP-TEE 安全 OS |
通过以上分析可以看出,ATF 的源码结构清晰地将安全启动的各个阶段解耦,common 提供共享基础设施,plat 封装硬件差异,使得开发者可以专注于特定阶段的优化而不影响其他模块。