1. 启动流程全景:从按下电源键到操作系统启动的完整链路,UEFI与ATF的角色分工
各位同学,今天我们正式开始这门课。我是你们的讲师,做了十几年固件,从BIOS到UEFI再到ATF,踩过的坑不少。第一节课,咱们先拉个全景图。
你按下电源键,到屏幕上出现操作系统登录界面,这中间发生了什么?很多人觉得就是“开机嘛,BIOS跑一下,然后引导系统”。其实没那么简单。尤其是现代ARM服务器和嵌入式设备,这里面的角色分工,比你想象的要复杂得多。
我习惯把整个启动流程分成三个阶段:硬件初始化、固件交接、操作系统引导。UEFI和ATF分别负责不同的阶段,但它们之间有一条清晰的协作线。
1.1 按下电源键之后的第一秒
电源键按下,主板通电。这时候CPU还处于复位状态,所有寄存器都是默认值。第一个执行的代码,是固化在ROM里的启动向量。
在x86平台上,CPU会直接跳转到UEFI的SEC(安全初始化)阶段。在ARM平台上,情况稍有不同——CPU会先进入ATF的BL1阶段。为什么会有这个差异?说白了,ARM从设计之初就更强调安全隔离,而x86的启动路径是历史演进来的。
核心要点: 无论哪种架构,第一个执行的代码都是不可变的、固化在ROM里的。这部分代码负责最基本的硬件初始化,然后才把控制权交给下一级固件。
1.2 UEFI的启动阶段
UEFI的启动流程,我把它拆成七个阶段,用SEC、PEI、DXE、BDS、TSL、RT、AL这七个缩写来记。不过咱们今天不背口诀,咱们讲逻辑。
- SEC(安全初始化):验证CPU状态,建立临时内存(Cache as RAM)。我记得第一次调这个阶段时,连个打印都没有,全靠看LED灯闪烁次数来debug,真是原始。
- PEI(EFI预初始化):初始化内存控制器,检测内存条。这个阶段很关键,内存没起来,后面什么都干不了。
- DXE(驱动执行环境):加载各种驱动——PCIe、SATA、USB、网络。这时候你才能看到UEFI的图形界面。
- BDS(启动设备选择):扫描启动设备,找到操作系统引导器。嗯,这里要注意,UEFI只负责找到并加载引导器,它不负责操作系统内核的初始化。
个人经验: 我在项目中遇到过DXE阶段驱动加载顺序导致死机的问题。两个驱动都依赖同一个硬件资源,但谁先加载谁后加载没定义好,结果就是系统卡在UEFI界面进不去。后来我们加了一个依赖仲裁机制才解决。
1.3 ATF的启动阶段
ATF(ARM Trusted Firmware)是ARM架构下的安全固件。它的启动流程也分阶段:BL1、BL2、BL31、BL32、BL33。
| 阶段 | 功能 | 运行特权级 |
|---|---|---|
| BL1 | ROM代码,验证BL2 | EL3(最高特权) |
| BL2 | 加载BL31、BL32、BL33 | EL3 |
| BL31 | 运行时服务,处理安全监控调用 | EL3 |
| BL32 | 可信操作系统(如OP-TEE) | EL1(安全世界) |
| BL33 | 非安全世界的引导器(通常是UEFI或U-Boot) | EL2 |
你想想看,ATF的BL1和BL2其实在做一件事:建立信任链。每个阶段都验证下一阶段的签名,确保没有被篡改。这个设计思路,说白了就是“层层把关,步步为营”。
避坑指南: 我曾经在BL2阶段踩过一个坑——签名验证通过后,加载BL31时地址对齐没处理好,导致BL31运行时直接跳到了非法地址。ARM的EL3异常处理机制很严格,地址不对齐就直接panic。从那以后,我每次检查BL31加载地址都会多看两眼。
1.4 UEFI与ATF的协作点
在ARM平台上,UEFI和ATF不是各自为政的。它们有一个明确的交接点——BL33阶段。
ATF的BL33阶段会加载UEFI(或者U-Boot),然后UEFI接管非安全世界的控制权。这时候,UEFI运行在EL2(虚拟化扩展特权级),而ATF的BL31仍然在EL3提供安全服务。
举个例子:UEFI在引导操作系统时,如果需要访问安全存储(比如TPM),它不能直接操作硬件,而是通过SMC(安全监控调用)指令,陷入EL3,由BL31代为执行。这就是所谓的安全监控模式。
// UEFI调用ATF安全服务的伪代码示例
// 通过SMC指令陷入EL3
UINT64 SMC_Call(UINT64 FunctionID, UINT64 Arg1, UINT64 Arg2) {
UINT64 Result;
__asm__ volatile(
"smc #0\n"
: "=r" (Result)
: "r" (FunctionID), "r" (Arg1), "r" (Arg2)
: "memory"
);
return Result;
}
这段代码看着简单,但实际项目中,FunctionID的定义、参数的传递方式、返回值的解析,都有严格的规范。我建议你仔细阅读SMCCC(SMC Calling Convention)规范,不然很容易在参数传递上出问题。
1.5 完整链路图
下面这张图,是我自己画的启动流程全景图。你把它存下来,后面每节课都会用到。
1.6 角色分工总结
说了这么多,咱们用一句话总结UEFI和ATF的分工:
- UEFI:负责硬件初始化、驱动加载、启动设备选择、操作系统引导。它运行在非安全世界,主要关注功能和兼容性。
- ATF:负责安全启动、信任链建立、安全监控服务。它运行在安全世界(EL3),主要关注安全和隔离。
在ARM平台上,两者是前后接力、上下分层的关系。ATF先跑,建立安全基础;然后UEFI接手,完成引导任务。在x86平台上,UEFI自己包揽了所有工作,但现代x86平台也开始引入类似的安全固件(比如Intel的Boot Guard)。
一句话记住: UEFI管“能不能用”,ATF管“安不安全”。两者缺一不可。
好了,第一节课就到这里。后面的课程,我们会深入每个阶段的细节,包括代码实现、调试方法、常见问题。你先把这张全景图刻在脑子里,后面学起来会轻松很多。