1. 课程导入
在ARM64服务器、嵌入式与可信计算平台中,固件启动链路通常由两段关键软件共同完成:一段是负责平台初始化、设备枚举与操作系统加载的UEFI固件,另一段是负责底层安全监控、电源管理、异常处理与安全世界切换的ATF(Arm Trusted Firmware)。很多工程师在单独理解UEFI或单独理解ATF时并不困难,但一旦进入真实项目,就会遇到“UEFI如何把控制权交给ATF”“BL31如何返回EL2/EL1”“PSCI调用如何穿过SMC进入ATF”“启动参数如何跨阶段传递”等具体问题。本课程的目标,正是把这两套体系从概念到代码、从启动时序到调试手段完整打通。
一、学习目标:从“知道”到“能对接”
本课程不是单纯讲UEFI规范,也不是单纯讲ATF源码,而是围绕“对接”这一工程动作展开。学完本课程后,你应当能够独立完成以下任务:
- 说清ARM64启动过程中BL1、BL2、BL31、BL33各阶段的职责与交接关系;
- 理解UEFI在BL33阶段作为非安全世界引导加载器的角色定位;
- 掌握PSCI、SMC/HVC、SMC Calling Convention在UEFI与ATF之间的调用路径;
- 能够阅读并修改ATF的plat层、BL31入口与UEFI的DXE/PEI关键代码;
- 能够使用FVP、QEMU或真实开发板复现启动流程并定位对接故障;
- 能够设计启动参数传递、共享内存、日志与异常处理的联调方案。
二、核心概念:先建立一张“启动地图”
ARM64可信固件启动流程通常被划分为多个Boot Loader阶段。BL1位于芯片内部ROM或可信SRAM,负责最基础的平台初始化与BL2加载;BL2负责加载BL31、BL32和BL33;BL31是ATF的运行时固件,常驻EL3,负责安全监控、PSCI服务与世界切换;BL33通常就是UEFI固件,运行在非安全世界,最终加载操作系统。理解这张地图,是后续所有代码分析和调试工作的前提。
需要特别注意的是,UEFI与ATF并不是简单的“前后关系”。在启动早期,BL31先被加载并初始化,随后BL33才被跳转执行;但在运行期,UEFI又可以通过SMC指令主动调用BL31提供的服务。因此二者既是启动阶段的上下游,又是运行阶段的服务调用关系。这种双重关系,正是对接工程中最容易混淆的地方。
三、整体框架:三条主线贯穿全课程
为了避免知识碎片化,本课程用三条主线组织内容。第一条是启动时序线,从BL1到BL33,重点分析每一阶段的入口、参数、内存布局与跳转条件。第二条是服务调用线,围绕PSCI、SMC Calling Convention、共享内存和中断路由,分析UEFI如何请求ATF服务。第三条是工程调试线,包括日志分级、串口输出、FVP模型、JTAG调试、异常回溯与常见启动挂死定位。
| 主线 | 核心问题 | 关键对象 | 典型产出 |
|---|---|---|---|
| 启动时序线 | 控制权如何从BL1交到UEFI? | BL1/BL2/BL31/BL33、入口地址、启动参数 | 启动流程图、内存布局图、跳转日志 |
| 服务调用线 | UEFI如何调用ATF的PSCI服务? | SMC/HVC、PSCI、共享内存、EL3异常向量 | 调用时序图、参数表、返回码分析 |
| 工程调试线 | 启动失败时如何快速定位? | 串口日志、FVP、JTAG、异常等级、栈回溯 | 调试记录、故障树、修复补丁 |
四、案例场景:一次典型的“卡在BL31之后”问题
假设你在FVP平台上构建了ATF和UEFI,串口只输出到“BL31: Initializing runtime services”后就没有后续日志。此时不要急于修改UEFI代码,而应按启动链路逐段排查。首先确认BL31是否成功完成plat初始化并跳转到BL33入口;其次检查BL33的加载地址与入口地址是否与ATF的plat参数一致;然后确认UEFI早期串口是否已经初始化,因为很多“无日志”其实是UEFI还没走到串口驱动。最后再检查PSCI版本、CPU数、共享内存基址等参数是否匹配。
# 典型排查命令示例(FVP环境)
./FVP_Base_RevC-2xAEMvA \
-C pctl.startup=0.0.0.0 \
-C bp.secure_memory=1 \
-C cluster0.NUM_CORES=4 \
-a BL1.bin@0x0 \
-a BL2.bin@0x10000 \
-a BL31.bin@0x20000 \
-a BL33.bin@0x40000
# 观察串口输出中的关键阶段
NOTICE: BL1: Booting BL2
NOTICE: BL2: Booting BL31
NOTICE: BL31: v2.10
NOTICE: BL31: Initializing runtime services
NOTICE: BL31: Preparing for EL3 exit to normal world
五、学习路径与常见误区
建议按“概念→时序→代码→调试→实战”的顺序推进。先建立BL1到BL33的整体地图,再深入BL31入口与PSCI实现,随后阅读UEFI的PSCI客户端与DXE阶段代码,最后在FVP或开发板上完成一次完整启动与调用验证。每学完一个模块,都应回到启动日志中找对应证据,避免只停留在源码阅读。
常见误区有三个:第一,把UEFI当成ATF的“下一阶段”而忽略运行期SMC调用;第二,只关注代码不关注内存布局和入口参数,导致跳转失败却找不到原因;第三,遇到问题直接改UEFI,而不先确认BL31是否已经正确初始化。复盘时建议记录每次启动的完整日志、镜像加载地址、PSCI返回码和异常等级变化,这些信息往往比单步调试更有效。
当你能够把启动时序、服务调用和调试手段三条线合在一起看,UEFI与ATF的对接就不再是零散的知识点,而是一条可以验证、可以复现、可以修复的工程链路。接下来的章节将沿着这条链路,从BL1的第一条指令开始,逐步走到UEFI加载操作系统的最后一跳。