2. UEFI启动流程全景

UEFI的启动流程,说白了就是一台电脑从按下电源键到操作系统接管前的「热身运动」。我做了这么多年固件,见过不少工程师把UEFI当成黑盒子,出了问题就抓瞎。其实它的四个核心阶段——PEI、DXE、BDS、RT——就像接力赛,每一棒都有明确的任务。

今天我就把这四个阶段掰开揉碎了讲。你想想看,搞懂了这些,以后遇到启动问题,你就能像老中医一样「望闻问切」了。

2.1 PEI阶段:从零开始的「侦察兵」

PEI(Pre-EFI Initialization)是UEFI启动的第一棒。这时候内存还没初始化,CPU刚复位,整个系统就像一张白纸。

PEI的核心任务:

  • 检测CPU、芯片组、主板的初始状态
  • 初始化临时内存(Cache as RAM,简称CAR)
  • 发现并启动主内存控制器
  • 传递系统信息给下一阶段

我个人习惯把PEI比作「侦察兵」。它不需要大部队(完整内存),只需要带点干粮(CPU缓存当内存用)就能摸清地形。

避坑指南:我曾经在一个项目中,PEI阶段死活过不去。查了三天,发现是CAR的栈指针设置错了。嗯,这里要注意——CAR的大小通常只有4KB到64KB,千万别放太多全局变量。

PEI阶段的代码以「PEIM」(PEI Module)的形式存在。它们被打包在固件卷(FV)里,由PEI调度器按依赖关系执行。典型的PEIM包括:

  • CPU PEIM:初始化CPU微码、设置MTRR
  • 芯片组PEIM:配置南北桥寄存器
  • 内存PEIM:训练DDR、初始化内存控制器

2.2 DXE阶段:驱动大军的「阅兵式」

DXE(Driver Execution Environment)是UEFI最庞大的阶段。这时候内存已经就绪,各种驱动可以尽情加载了。

DXE的核心任务:

  • 初始化完整的系统服务(Boot Services、Runtime Services)
  • 枚举并加载所有DXE驱动
  • 创建系统表(System Table)、启动服务表(Boot Services Table)
  • 初始化控制台、存储设备、网络等

DXE阶段的驱动执行顺序由依赖关系决定。每个驱动都有一个DEPEX(Dependency Expression),比如「等我需要的协议准备好了再叫我」。这有点像现代操作系统的设备树——先有根节点,再有子节点。

个人经验:我记得有一次调试一个NVMe驱动,它总在DXE阶段崩溃。后来发现是DEPEX写错了,它依赖的PCI IO协议还没准备好。你想想看,驱动就像排队打饭,前面的人没打好,后面的人只能干瞪眼。

DXE阶段结束后,系统已经具备了完整的硬件抽象层(HAL)。这时候UEFI会调用BDS阶段。

2.3 BDS阶段:选择「谁来接班」

BDS(Boot Device Selection)阶段的任务很明确——决定从哪个设备启动操作系统。

BDS的核心流程:

  1. 枚举所有可启动设备(硬盘、光驱、USB、网络等)
  2. 读取启动顺序(BootOrder变量)
  3. 尝试每个启动项,直到成功
  4. 加载启动项对应的引导程序(如bootmgfw.efi)

BDS阶段会调用「Boot Manager」——这是UEFI内置的一个小程序。它负责维护启动菜单、处理用户按键、执行启动策略。

注意:我曾经遇到一个坑——客户说机器偶尔无法进入系统。查了半个月,发现是BootOrder变量被意外修改了。嗯,这里要提醒大家:BDS阶段对NVRAM的写入操作一定要加锁,否则并发访问会搞乱启动顺序。

BDS阶段还有一个重要功能:加载UEFI Shell。如果你在启动时按某个热键(通常是F2或Del),就会进入Shell环境。这相当于一个迷你操作系统,可以用来调试、更新固件。

2.4 RT阶段:退居二线的「后勤部」

RT(Runtime Services)阶段是UEFI的最后一个阶段。当操作系统接管后,UEFI的大部分代码会被释放,只保留一小部分「运行时服务」常驻内存。

RT阶段保留的服务:

服务名称 功能说明
GetVariable / SetVariable 读写NVRAM变量
GetTime / SetTime 获取/设置系统时间
ResetSystem 重启或关机
UpdateCapsule 固件更新接口

RT阶段的代码必须非常精简,因为它要和操作系统共享内存空间。我见过一些固件在RT阶段还保留着调试打印,结果导致系统蓝屏——说白了,RT阶段不是让你玩的地方。

关键点:RT阶段的服务必须遵循UEFI规范中的「Runtime Calling Convention」。这意味着它们不能使用Boot Services(如AllocatePool),也不能依赖中断。我曾经在RT阶段调用了一个DXE阶段的函数,结果系统直接挂死——嗯,这个教训很深刻。

2.5 四个阶段的协作关系

这四个阶段不是孤立的。它们通过「Hand-Off Block」(HOB)传递信息。PEI阶段把内存信息、CPU信息打包成HOB,DXE阶段读取这些HOB来初始化驱动,BDS阶段再根据DXE提供的协议选择启动设备。

我画了一张图,帮你理解这个流程:

UEFI启动流程全景图 PEI阶段 平台初始化 HOB DXE阶段 驱动执行环境 BDS阶段 启动设备选择 RT阶段 运行时服务 PEI → DXE → BDS → RT,逐级传递控制权 HOB(Hand-Off Block)是阶段间的信息传递载体

从这张图你可以看到,PEI是起点,DXE是主力,BDS是决策者,RT是后勤。每个阶段都有明确的职责边界,不能越俎代庖。

实用技巧:调试启动问题时,我建议你在每个阶段入口加一个串口打印。比如PEI阶段打印「PEI Entry」,DXE阶段打印「DXE Entry」。这样一旦系统卡住,你就能快速定位是哪个阶段出了问题。这招我用了十年,屡试不爽。

好了,UEFI启动流程的全景就讲到这里。这四个阶段是固件工程师的必修课,搞懂了它们,你就能理解为什么有些机器启动快、有些慢,为什么某些外设能识别、某些不能。说白了,UEFI启动就是一场精心编排的交响乐,每个乐器(阶段)都在正确的时间发出正确的声音。

最后提醒:RT阶段的服务是「只读」的——操作系统可以调用它们,但不能修改它们。我曾经见过一个团队在RT阶段尝试分配内存,结果导致系统崩溃。记住:RT阶段不是DXE,别想着搞大动作。

可可精品资料库