1. 启动流程全景:从SoC上电到操作系统加载的完整链路
各位同学好。我是老周,在嵌入式固件这行摸爬滚打了十几年。今天咱们聊的这第一课,是整个课程的基石——你得先看清楚,从芯片上电到Linux跑起来,这中间到底发生了什么。
很多人做启动优化,上来就盯着UEFI或者ATF的某一段代码猛改。结果呢?改完发现启动时间没降多少,反而把某个环节搞崩了。为什么?因为你没看清全局。
核心观点:启动优化不是单点优化,而是链路优化。不理解全貌,你连瓶颈在哪都找不到。
1.1 上电那一刻:芯片在干什么?
按下电源键,或者开发板刚接上电。这时候SoC内部发生了什么?
我简单梳理一下:
- POR(Power-On Reset):电源稳定后,复位信号释放。芯片内部所有寄存器回到默认值。
- Boot ROM 执行:CPU从预设的复位向量地址开始取指。这个地址通常映射到芯片内部的Boot ROM,不可更改。
- 硬件自检与时钟初始化:Boot ROM里固化的代码会做最基础的硬件检测,比如检查主晶振是否起振、PLL是否锁定。
- 启动设备选择:根据外部引脚电平(比如eFuse、GPIO拉高拉低),决定从哪个设备加载下一级启动代码——NAND Flash、eMMC、SD卡、还是UART。
- 加载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加载的每个关键节点都串起来了。
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代码,看看具体怎么优化。但在此之前,我建议你先把本章的流程图打印出来贴墙上——每次做优化前看一眼,问自己:“我现在改的是哪个阶段?会不会影响上下游?”
嗯,今天就到这儿。有问题随时交流。