3、BIOS/UEFI固件架构:BIOS与UEFI的区别,UEFI的六个阶段概述
好,我们进入第三章。这一章聊的是固件架构,说白了就是服务器上电后,第一个跑起来的软件长什么样。
我刚开始做芯片验证那会儿,总觉得固件就是个“黑盒子”——按一下电源键,屏幕亮了,系统起来了。至于中间发生了什么,完全没概念。直到有一次调试一个内存训练失败的问题,我才被迫把UEFI的启动流程从头到尾啃了一遍。嗯,从那以后,我再也不敢小看固件了。
3.1 BIOS vs UEFI:到底差在哪?
先说说传统BIOS。BIOS的全称是Basic Input/Output System,它诞生于1980年代。说实话,它的设计思路非常“古老”——16位实模式,1MB地址空间限制,用中断向量表做I/O。你想想看,现在的服务器动辄几百GB内存、几十个核心,BIOS这套东西根本跑不动。
UEFI(统一可扩展固件接口)就是来取代BIOS的。它不是BIOS的升级版,而是完全重写的。我个人习惯把UEFI理解成一个“微型操作系统”——它有驱动模型、有协议栈、有内存管理,甚至还能跑网络。
| 对比项 | 传统BIOS | UEFI |
|---|---|---|
| 处理器模式 | 16位实模式 | 32/64位保护模式+长模式 |
| 地址空间 | 1MB限制 | 无限制(取决于CPU) |
| 磁盘分区 | MBR(最大2TB) | GPT(最大9.4ZB) |
| 驱动模型 | 无,依赖Option ROM | UEFI Driver Model |
| 启动速度 | 慢(逐项检测) | 快(并行初始化) |
| 安全性 | 无 | Secure Boot |
核心区别一句话:BIOS是固件,UEFI是固件+操作系统接口规范。UEFI定义了固件和操作系统之间的标准接口,而BIOS没有。
3.2 UEFI的六个阶段:从按下电源键到操作系统
UEFI把启动过程分成了六个阶段。每个阶段都有明确的任务和边界。我在项目中遇到过不少问题,最后发现都是某个阶段的初始化顺序搞错了。所以,理解这六个阶段非常关键。
3.2.1 SEC阶段(安全验证)
SEC是Security的缩写。这是CPU复位后执行的第一段代码。它的任务很简单:验证下一个阶段(PEI)的代码是否可信。
为什么会需要这个?因为如果PEI阶段的代码被篡改了,整个系统就不可信了。SEC阶段通常放在CPU内部的缓存(Cache as RAM)里运行,因为这时候内存还没初始化。
避坑指南:我曾经遇到过一个案例,SEC阶段的签名验证算法实现有bug,导致某些合法的固件更新被拒绝。排查了整整两天才发现是哈希计算的长度参数传错了。嗯,这种底层代码,一个bit都不能错。
3.2.2 PEI阶段(EFI前初始化)
PEI的全称是Pre-EFI Initialization。这个阶段的主要工作是:初始化CPU、芯片组、内存控制器,然后检测并初始化内存。
PEI阶段的代码非常精简,因为它运行在“临时内存”里——通常是CPU缓存模拟的内存(Cache as RAM,简称CAR)。等真正的DRAM初始化完成后,PEI会把控制权交给DXE阶段。
PEI的核心数据结构叫HOB(Hand-Off Block),它记录了PEI阶段发现的所有硬件信息。DXE阶段会读取HOB来继续初始化。
// PEI阶段的一个典型HOB结构
typedef struct {
UINT16 HobType; // HOB类型
UINT16 HobLength; // HOB长度
UINT32 Reserved; // 保留
} EFI_HOB_GENERIC_HEADER;
3.2.3 DXE阶段(驱动执行环境)
DXE是Driver Execution Environment的缩写。这是UEFI最核心的阶段。说白了,DXE就是一个驱动框架——它加载各种硬件驱动,初始化PCIe设备、存储控制器、网络接口等。
DXE阶段运行在真正的内存里,可以访问全部地址空间。它使用一种叫“DXE Dispatcher”的调度器,按依赖关系依次加载驱动。比如,PCIe根桥驱动必须先加载,然后才能加载PCIe设备驱动。
关键点:DXE阶段会构建UEFI的系统表(System Table)和启动服务表(Boot Services Table)。这些表是操作系统启动时获取硬件信息的标准接口。
3.2.4 BDS阶段(启动设备选择)
BDS是Boot Device Selection的缩写。这个阶段的任务是:决定从哪个设备启动操作系统。
BDS会遍历所有可启动设备——硬盘、SSD、USB、网络(PXE)等。它按照用户配置的启动顺序,依次尝试加载启动项。如果第一个设备启动失败,就尝试下一个。
我记得有一次调试一个服务器,它总是从网络启动而不是本地硬盘。查了半天,发现是BDS阶段的启动顺序配置被重置了。嗯,这种问题通常和NVRAM(非易失性内存)的校验和错误有关。
3.2.5 TSL阶段(临时系统加载)
TSL是Transient System Load的缩写。这个阶段很短,它的任务就是:加载操作系统的启动加载器(Boot Loader),比如GRUB、Windows Boot Manager等。
TSL阶段会调用UEFI的启动服务(Boot Services),比如ExitBootServices()。这个调用很关键——一旦调用了ExitBootServices,UEFI就把硬件的控制权完全交给了操作系统,UEFI自己退居二线,只提供运行时服务(Runtime Services)。
注意:ExitBootServices()调用之后,UEFI的启动服务就不可用了。如果你在操作系统里还想调用UEFI的某些功能,只能通过运行时服务接口。我见过有人写内核驱动时忘了这个限制,结果系统直接挂掉。
3.2.6 RT阶段(运行时)
RT是Runtime的缩写。这是UEFI的最后一个阶段。操作系统已经接管了硬件,UEFI只提供一些基本的运行时服务,比如:
- 设置系统时间(GetTime/SetTime)
- 读写UEFI变量(GetVariable/SetVariable)
- 重启/关机(ResetSystem)
RT阶段的代码会常驻在内存中,操作系统可以通过UEFI的运行时服务表来调用它们。这些服务通常用SMI(系统管理中断)或虚拟化技术来实现隔离。
3.3 六个阶段的时间分布
在实际项目中,这六个阶段的时间占比大概是这样:
| 阶段 | 典型耗时 | 说明 |
|---|---|---|
| SEC | < 10ms | 纯软件验证,很快 |
| PEI | 100ms - 500ms | 内存训练是瓶颈 |
| DXE | 500ms - 2s | 驱动加载和初始化 |
| BDS | < 100ms | 遍历启动设备 |
| TSL | < 50ms | 加载启动加载器 |
| RT | 持续运行 | 操作系统运行期间 |
优化建议:如果你发现服务器启动慢,90%的瓶颈在PEI阶段的内存训练和DXE阶段的驱动加载。我曾经通过优化内存训练参数,把一台服务器的启动时间从45秒降到了12秒。嗯,这就是为什么理解这六个阶段很重要——你知道该从哪里下手。
3.4 小结
这一章我们聊了BIOS和UEFI的区别,以及UEFI的六个阶段。说白了,UEFI就是一套标准化的启动框架,把“从按下电源键到操作系统启动”这个过程拆成了六个清晰的阶段。每个阶段各司其职,环环相扣。
下一章,我们会深入PEI阶段,看看内存训练到底是怎么做的。到时候我会分享一个我在调试DDR5内存时遇到的“玄学”问题——嗯,保证让你大开眼界。