1. 启动流程全景:从SoC上电到操作系统加载的完整链路

各位同学好。我是老周,在嵌入式固件这行摸爬滚打了十几年。今天咱们聊的这第一课,是整个课程的基石——你得先看清楚,从芯片上电到Linux跑起来,这中间到底发生了什么。

很多人做启动优化,上来就盯着UEFI或者ATF的某一段代码猛改。结果呢?改完发现启动时间没降多少,反而把某个环节搞崩了。为什么?因为你没看清全局。

核心观点:启动优化不是单点优化,而是链路优化。不理解全貌,你连瓶颈在哪都找不到。

1.1 上电那一刻:芯片在干什么?

按下电源键,或者开发板刚接上电。这时候SoC内部发生了什么?

我简单梳理一下:

  1. POR(Power-On Reset):电源稳定后,复位信号释放。芯片内部所有寄存器回到默认值。
  2. Boot ROM 执行:CPU从预设的复位向量地址开始取指。这个地址通常映射到芯片内部的Boot ROM,不可更改。
  3. 硬件自检与时钟初始化:Boot ROM里固化的代码会做最基础的硬件检测,比如检查主晶振是否起振、PLL是否锁定。
  4. 启动设备选择:根据外部引脚电平(比如eFuse、GPIO拉高拉低),决定从哪个设备加载下一级启动代码——NAND Flash、eMMC、SD卡、还是UART。
  5. 加载BL1/BL2:从选定的启动设备读取第一段或第二段启动镜像到SRAM中,然后跳转执行。

嗯,这里要注意。不同厂商的SoC,Boot ROM做的事差别很大。比如有些芯片会把DDR初始化也放在Boot ROM里,有些则丢给下一级去做。我在项目中遇到过某款车规级芯片,Boot ROM里居然还做了安全校验,导致上电到第一行用户代码跑了将近500毫秒——这你受得了吗?

1.2 ATF(ARM Trusted Firmware)的定位

ATF是什么?说白了,它是ARM架构下安全世界的固件框架。它负责把系统从最原始的裸机状态,一步步带到能跑正常OS的状态。

ATF把启动过程分成了几个等级,也就是我们常说的BL(Boot Loader)阶段:

阶段 运行位置 主要职责
BL1 SRAM(片上RAM) 最基础的CPU初始化,加载BL2到SRAM
BL2 SRAM 平台初始化(比如时钟、电源管理),加载BL31/BL32/BL33
BL31 DRAM 运行时服务(PSCI、SMC处理),管理安全监控模式
BL32 DRAM 可选的安全OS(比如OP-TEE)
BL33 DRAM 非安全世界的引导程序,通常是UEFI或U-Boot

我个人习惯把BL1和BL2叫做“前启动阶段”,BL31以后叫“运行时阶段”。前启动阶段追求的是快和稳,运行时阶段追求的是服务和接口的完备性。

避坑指南:我曾经在调试一块开发板时,发现BL2加载BL31总是失败。查了两天,最后发现是BL2里DDR初始化时序参数配错了。所以,如果你遇到ATF启动卡在某个阶段,先检查上一阶段的输出是否完整——很多时候问题不在当前阶段,而在它的“上一站”。

1.3 UEFI的登场时机

UEFI(统一可扩展固件接口)是在BL33这个阶段登场的。它运行在非安全世界,拥有完整的DRAM访问权限。

UEFI要干的事,比ATF的BL阶段复杂得多:

  • DXE(驱动执行环境):枚举PCIe、USB、SATA等外设,加载对应的UEFI驱动。
  • BDS(启动设备选择):根据BootOrder变量,决定从哪个设备启动OS。
  • Runtime Services:提供操作系统运行时可以调用的接口,比如GetVariable、SetVariable。
  • ACPI/SMBIOS表生成:向操作系统描述硬件拓扑和配置信息。

你想想看,UEFI和ATF的分工其实很清晰:ATF管安全、管电源、管异常等级切换;UEFI管外设、管启动策略、管OS接口。两者通过SMC(安全监控调用)指令进行通信。

1.4 完整链路:一张图看懂

下面这张图,是我自己画的一个启动流程全景图。它把SoC上电到OS加载的每个关键节点都串起来了。

启动流程全景图:SoC上电 → OS加载 SoC上电 / POR Boot ROM 执行 启动设备选择 加载BL1到SRAM BL1 (ATF) BL2 (ATF) BL31 (ATF运行时) BL32 (可选安全OS) BL33: UEFI SEC阶段 UEFI PEI阶段 UEFI DXE阶段 UEFI BDS → 加载OS (GRUB/内核) 说明:ATF负责安全世界(EL3/EL2),UEFI负责非安全世界(EL1/EL0)。两者通过SMC指令交互。 虚线箭头表示BL32为可选阶段,不是所有平台都需要安全OS。

1.5 ATF与UEFI的分工协作

很多人搞不清ATF和UEFI到底谁管谁。我打个比方:

  • ATF是“保安队长”:它管安全门禁(异常等级切换)、管电源按钮(PSCI)、管紧急通道(SMC)。它待在EL3,权限最高,但平时不露面。
  • UEFI是“项目经理”:它管硬件资源分配(枚举外设)、管启动流程(选哪个盘启动)、管跟OS对接(ACPI表)。它待在EL1/EL2,干的是具体活。

两者怎么协作?举个例子:

当UEFI想让CPU进入深度睡眠时,它不能直接操作电源管理寄存器——那是安全世界的领地。UEFI会发起一个SMC调用,陷入EL3,由ATF的BL31里的PSCI服务去执行实际的电源操作。执行完再返回到UEFI。

关键接口:SMC(Secure Monitor Call)是ATF和UEFI之间的“官方通信协议”。UEFI通过SMC向ATF请求服务,ATF通过SMC返回结果。这个接口的性能,直接影响启动速度和运行时效率。

1.6 启动时间都花在哪了?

我做过一个实际项目的启动时间分析,数据大概是这样:

阶段 典型耗时 优化空间
Boot ROM + BL1 50-200 ms 小(芯片固话代码,改不了)
BL2(DDR初始化) 100-500 ms 中(可优化DDR训练参数)
BL31 + BL32 30-100 ms 小(主要是安全服务初始化)
UEFI PEI阶段 50-200 ms 中(可裁剪不必要的PEIM)
UEFI DXE阶段 200-1000 ms 大(驱动加载是主要瓶颈)
UEFI BDS + OS加载 100-500 ms 大(可优化BootOrder和内核加载)

你看,UEFI的DXE阶段是最大的时间黑洞。这也是为什么后面我们会花大量篇幅讲DXE的优化——包括并发驱动加载、延迟绑定、以及如何跳过不必要的驱动。

注意:上面的数据是典型值,实际项目差异很大。比如有些服务器平台,DDR初始化就要跑1秒多。我建议你在做优化前,先拿示波器或者JTAG把每个阶段的实际耗时测出来——别靠猜。

1.7 本章小结

这一章我们走了一遍从SoC上电到OS加载的完整链路。核心就三句话:

  • ATF管安全、管电源、管异常等级切换,是启动流程的“地基”。
  • UEFI管外设、管启动策略、管OS接口,是启动流程的“上层建筑”。
  • 两者通过SMC协作,任何一方的性能问题都会拖慢整体启动速度。

从下一章开始,我们会深入ATF的BL1/BL2代码,看看具体怎么优化。但在此之前,我建议你先把本章的流程图打印出来贴墙上——每次做优化前看一眼,问自己:“我现在改的是哪个阶段?会不会影响上下游?”

嗯,今天就到这儿。有问题随时交流。