1、课程导论与背景:为什么需要UEFI+ATF协同?系统启动流程全景图

1.1 一个老问题:系统启动到底有多复杂?

做固件这么多年,我经常被问到同一个问题:「系统启动不就是把电插上,然后按一下开机键吗?」

嗯,从用户角度看确实是这样。但从我们固件工程师的角度看,从按下电源键到操作系统跑起来,中间要经历的事情,比你想象的多得多。

我举个例子。你有一台ARM服务器,或者一台带安全启动的x86设备。电源键按下之后,CPU要初始化、内存要训练、外设要枚举、安全环境要建立、操作系统镜像要验证……每一步都可能出问题。而且,不同架构的启动方式还不一样。

x86有UEFI,ARM有ATF(ARM Trusted Firmware)。它们各自管各自的一亩三分地,但问题是——现代系统越来越复杂,这两者必须协同工作。我在一个项目中就遇到过,UEFI和ATF各自跑得挺好,但一对接就死机。查了三天,发现是两者对内存布局的理解不一致。

说白了,UEFI+ATF协同不是「锦上添花」,而是「不得不做」。

1.2 为什么需要UEFI+ATF协同?

先看两个架构各自的定位。

维度 UEFI(x86/ARM通用) ATF(ARM专用)
核心职责 平台初始化、Boot Manager、OS加载 安全监控、PSCI电源管理、异常处理
运行特权级 EL2(Hypervisor级)或EL1(OS级) EL3(最高特权级,Secure Monitor)
安全焦点 Secure Boot、Measured Boot TrustZone、安全启动链、运行时安全服务
启动阶段 SEC → PEI → DXE → BDS → TSL → RT BL1 → BL2 → BL31 → BL32 → BL33

你仔细看这张表,会发现一个关键问题:UEFI和ATF在启动流程上有重叠,但又不完全重合。

ATF的BL33阶段,通常就是加载UEFI固件。而UEFI的SEC阶段,又需要ATF提前把EL3环境搭好。这两者之间,存在一个「握手」的过程。

我个人的习惯是,把ATF看作「地基」,UEFI看作「房子」。地基没打好,房子盖得再漂亮也白搭。反过来,地基打好了,房子设计不合理,也住不舒坦。

核心矛盾:UEFI需要ATF提供安全服务(如PSCI、RME),ATF需要UEFI完成平台初始化。两者必须共享内存、中断、电源状态等资源。一旦信息不同步,系统就会在启动过程中「卡死」或「崩溃」。

1.3 系统启动流程全景图

下面这张图,是我自己画的一个全景流程。它把UEFI和ATF的启动阶段放在一起对比,你看完就能明白「协同」到底发生在哪里。

UEFI + ATF 协同启动全景图 ATF BL1 (Boot ROM) 片内ROM,不可修改,验证BL2签名 ATF BL2 (Trusted Boot Firmware) 加载BL31、BL32、BL33,建立安全启动链 ATF BL31 (EL3 Runtime) PSCI、SMC处理、中断路由、RME ATF BL32 (TEE) 可选,提供安全应用执行环境 ATF BL33 (Non-secure World) 跳转到UEFI或uboot,进入非安全世界 UEFI SEC (Security Phase) CPU初始化,临时内存建立,安全验证 UEFI PEI (Pre-EFI Init) 内存初始化,PEIM执行,S3恢复 UEFI DXE (Driver Exec. Env) 驱动加载,协议注册,ACPI/SMBIOS生成 UEFI BDS (Boot Dev Select) 启动策略,设备枚举,Boot Manager OS Loader / OS Kernel ExitBootServices,进入操作系统 协同关键点:BL33→UEFI SEC 跳转 | SMC调用 | 内存布局共享 | 电源状态同步

你看,ATF从BL1到BL33,UEFI从SEC到OS Loader。两者在BL33/SEC这个点交汇。但交汇不是简单的「你干完我干」,而是需要协商。

举个例子。ATF在BL31阶段注册了PSCI服务,UEFI在DXE阶段需要调用PSCI来开关CPU核心。怎么调?通过SMC指令。ATF在EL3捕获SMC异常,然后执行对应的PSCI函数。这个过程,就是协同。

一个小技巧:我在调试UEFI+ATF协同问题时,习惯先在ATF的BL31里加一个SMC日志,打印出所有来自UEFI的SMC调用。这样能快速判断是UEFI没发SMC,还是ATF没正确处理。

1.4 协同中的常见「坑」

说实话,UEFI+ATF协同的坑,我踩过不少。这里列几个典型的,你以后遇到了可以少走弯路。

  • 内存布局冲突:ATF在EL3有自己的内存区域(如Trusted SRAM),UEFI在非安全世界也有自己的内存。两者如果对同一块物理地址的理解不一致,就会出问题。我曾经遇到过UEFI把ATF的代码段给覆盖了,结果系统一调用PSCI就崩溃。
  • 中断路由混乱:ARM GIC(通用中断控制器)支持中断分组。安全中断要路由到EL3,非安全中断路由到EL1/EL2。如果UEFI配置GIC时不小心把安全中断也路由到了非安全世界,ATF就收不到关键中断了。
  • PSCI版本不匹配:UEFI调用PSCI时,会检查版本。ATF实现的PSCI版本如果低于UEFI要求的版本,调用就会失败。我建议你在UEFI代码里加一个PSCI版本检查的日志,方便定位。
  • 启动顺序依赖:有些平台要求ATF必须先完成某些初始化,UEFI才能开始工作。比如,ATF需要先配置好系统计数器,UEFI才能用它来计时。如果顺序搞反了,UEFI读到的计数器值就是错的。

特别注意:在ARMv8.4及以上架构中,RME(Realm Management Extension)引入了新的安全等级。ATF需要管理Realm世界,UEFI需要与之交互。这个协同比传统TrustZone更复杂。如果你在做新平台,一定要先确认ATF版本是否支持RME,以及UEFI是否适配了RME接口。

1.5 为什么选择UEFI+ATF?

你可能会问:「既然这么麻烦,为什么不用uboot+ATF?或者直接用UEFI跑在x86上?」

原因很简单:生态和标准化。

UEFI有成熟的驱动模型、协议栈、安全启动框架。ATF提供了ARM架构上最权威的EL3实现。两者结合,既能利用UEFI的丰富生态,又能发挥ARM的安全特性。

我个人的经验是,如果你在做ARM服务器、边缘计算设备或者高端嵌入式系统,UEFI+ATF几乎是唯一的选择。uboot虽然轻量,但在安全启动、ACPI支持、UEFI兼容性方面,差距还是明显的。

而且,现在很多芯片厂商(如NXP、TI、Rockchip)都在推UEFI+ATF方案。你学会了这个组合,以后换平台也能快速上手。

1.6 本章小结

这一章,我们聊了UEFI和ATF各自的定位,为什么需要协同,以及协同中常见的坑。核心就一句话:UEFI负责「功能」,ATF负责「安全」。两者必须紧密配合,系统才能稳定启动。