UEFI启动的七个阶段介绍
系统固件开发学习系列:
目录
[二、SEC 安全初始化阶段](#二、SEC 安全初始化阶段)
[三、PEI 预 EFI 初始化阶段](#三、PEI 预 EFI 初始化阶段)
[七、RT 和 AL 阶段](#七、RT 和 AL 阶段)
一、整体概念
UEFI启动的七个阶段分别为:
- SEC (Security),安全初始化阶段。
- PEI (Pre-EFI Initialization),预EFI初始化阶段。
- DXE (Driver Execution Envirenment),驱动执行环境阶段。
- BDS (Boot Device Selection),启动设备选择阶段。
- TSL (Transient System Load),操作系统加载前期阶段。
- RT (Runtime) ,运行时阶段。
- AL(After Life):关闭阶段(系统关机或重启)
首先对以上启动步骤进行一个大概的理解,以便对整个系统有全局性的思考。
SEC 阶段 ,负责处理系统复位、CPU 初始化和临时内存的搭建,为后续阶段奠定安全可信的执行基础;
紧接着是 PEI 阶段 ,它在资源极度受限的环境中初始化内存控制器 等关键硬件,并将系统信息通过 HOB 列表传递给下一棒;
随后进入核心的 DXE 阶段 ,大量驱动程序在此被执行,全面初始化平台硬件并构建出丰富的Boot Services与Runtime Services服务框架;
硬件就绪后,BDS阶段 接管,它初始化控制台设备、管理启动项,并依据策略加载操作系统的引导程序(.efi文件);
引导程序开始执行便进入了TSL阶段,它作为临时系统加载环境,依然利用Boot Services进行内核加载等准备工作;
准备就绪后,引导程序调用ExitBootServices(),系统进入RT阶段,此时Runtime Services依然可用,但Boot Services已被卸载,操作系统内核开始掌控全局;
最终,当操作系统完全启动后,若系统进入睡眠或关机状态,则会在需要时进入AL阶段,由固件提供特定的运行时错误处理或睡眠恢复支持。
二、SEC---安全初始化阶段
2.1 简介
系统上电后,CPU 处于最原始的硬件状态(如 x86 的实模式),此时 DRAM(主内存)及外设均未初始化。SEC(Security)阶段 作为UEFI固件执行的第一个环节,其核心任务就是将CPU从这种不可控的上电状态,转变为一个可执行代码的稳定环境,并为后续的 PEI(Pre-EFI Initialization)阶段准备好执行环境,其实也类似于 ARM 嵌入式设备的启动,start.S 汇编入口文件。
SEC阶段的代码位于主板 SPI Flash 的 Boot Block 区域,体积小(通常几十 KB)且拥有最高执行权限。其执行流程如下:
绝大多数主板使用 SPI Flash 存储固件。容量多为8~64MB,速度几十 MB/s,接口线4根,可擦写。
- 建立临时执行环境 :由于 DRAM 尚未可用,SEC 阶段利用 **Cache-as-RAM(CAR)**技术,将CPU的内部高速缓存配置为临时的读写内存(即栈空间),从而为 C 语言等高级语言的运行建立最基本的执行环境。
- 执行核心初始化 :使用汇编语言完成 CPU 关键寄存器的初始化,确保处理器处于稳定、可预测的状态。
- 安全验证与交接 :在将控制权移交前,SEC 阶段会校验PEI核心镜像的哈希值,以确保固件代码的完整性和安全性。
- 传递控制权:最后,SEC 系统当前状态、BFV(Boot Firmware Volume)地址以及临时 RAM 区域等关键参数传递给 PEI 阶段,并跳转至 PEI 入口点,完成其历史使命。
实模式:x86 CPU 启动后的初始状态。实模式下,CPU能直接访问的内存空间只有 1MB,使用"段:偏移"的方式寻址,没有内存保护,并且只能进行单任务处理。实模式目前的主要作用是系统启动。当你打开电脑时,现代 CPU(即使是 64 位的)会自动进入实模式。这时,BIOS 或 UEFI 固件会进行硬件自检(POST),然后加载操作系统的引导程序。这个引导程序最初也是在实模式下运行的。一旦操作系统接管,它就会功成身退,让位于更高级的保护模式或长模式。
2.2 具体步骤
-
首先进行 CPU 初始化,禁止 CPU 的段寄存器,禁止中断,清除状态寄存器,开启 Cache,关闭不安全的功能(如调试端口)。然后建立临时栈(Cache-As-RAM, CAR),因为此时 DRAM 还未初始化,而代码执行又需要栈空间,SEC 阶段需要用 CPU Cache 模拟RAM。
CAR 技术的实现方式是,通过 MSR(Model Specific Register)配置Cache进入"Write-Back"模式。将 Cache 中的一部分地址空间映射成伪地址,在该区域内建立堆栈,此时 CPU 就可以正常执行函数调用,存放临时变量。
-
在建立执行环境之后,需要验证固件的完整性。安全是 SEC 阶段名称的由来,在现代系统中,主板固件是攻击者的目标之一。固件制造商会在 Flash 中存放公钥(PK),UEFI 镜像(PEI Core 及 DXE Core)使用私钥签名,SEC 阶段计算哈希(SHA256)并验证签名,若验证失败,立即终止启动。
SHA256:一种著名的加密哈希函数。
-
部分主板允许在 SEC 阶段输出调试信息,例如使用串口输出 "SEC Start",提供"Early Debug"功能。在商用系统中通常被关闭。
-
SEC 会判断当前的启动属于哪种情况:正常启动,冷启动(断电重启),热启动(软重启(CTRL+ALT+DEL)),S3 恢复(从休眠唤醒),恢复模式(由于错误进入 Recovery 路径)。这些信息会被打包成结构体传给 PEI Core。
-
最后,SEC 阶段通过一个标准的入口将控制权交给 PEI 核心。
c
// SecCoreData: SEC 阶段生成的系统信息(栈、内存范围、HOB 起始地址)
// PpiList: 提供给 PEI 的功能接口(如临时存储、调试等)
PeiCoreEntryPoint (SecCoreData, PpiList)
SEC 阶段重要的数据结构:
C
/**
* brief: 描述 Cache-As-RAM 区域,栈区,提供固件卷(Firmware Volume, FV)的地址。
*/
typedef struct {
EFI_PHYSICAL_ADDRESS DataSize;
EFI_PHYSICAL_ADDRESS PeiTemporaryRamBase;
EFI_PHYSICAL_ADDRESS PeiTemporaryRamSize;
EFI_PHYSICAL_ADDRESS StackBase;
EFI_PHYSICAL_ADDRESS StackSize;
EFI_PHYSICAL_ADDRESS BootFirmwareVolumeBase;
EFI_PHYSICAL_ADDRESS BootFirmwareVolumeSize;
} EFI_SEC_PEI_HAND_OFF;
在 OVMF 中,SEC 阶段的入口文件为OvmfPkg/Sec/Secmain.c。其主要逻辑如下:
c
VOID
SecMain (
IN UINT32 SizeOfRam,
IN UINT32 StackBase
)
{
InitializeCpu (); // CPU 初始化
InitializeCacheAsRam (); // 建立Cache-As-RAM
VerifyFirmwareIntegrity (); // 固件验证
DetectBootMode (); // 检测启动类型
HandOffToPeiCore (); // 跳转至PEI阶段
}
三、PEI---预 EFI 初始化阶段
3.1 简介
PEI 阶段是 UEFI 启动流程中最关键的过渡阶段之一,它的任务是让系统从没有内存,几乎什么都不能做的状态,过渡到可以执行复杂 C 语言代码,和加载驱动的 DXE 阶段 。当系统从 SEC 阶段过来后,PEI 模块的入口函数首先会执行,初始化一个称为 PEI 核心的数据结构,作为 PEI 阶段的中枢管理器,负责调度后续所有 PEI 模块的执行。由于此时 DRAM 还不能使用,CPU 只能访问 CPU Cache,所有 PEI 模块都被压缩存放在固件设备中,由 PEI 核心按需解压到 Cache 中去运行。PEI 阶段的首要任务就是就行内存控制器的初始化 。具体来说,PEI 模块通过硬编码或从非易失性存储器中读取的内存时序参数,逐步配置内存控制器,最终完成物理内存的检测与初始化。在内存就绪前后,PEI 阶段会执行一系列关键的硬件初始化操作,包括配置芯片组的基本寄存器 ,检测并初始化 BFV 中的其他 PEIM,处理固件中的安全策略,以及构建一份名为 HOB 的 Hand-Off Block 数据结构链表 。HOB 列表至关重要,其以只读的方式将 PEI 阶段得到的系统硬件信息---内存布局,CPU 特性,设备状态等,完整地传递给 DXE 阶段。一旦内存初始化完成,HOB 列表构建完毕,PEI 阶段的最终任务就是定位并调用 DXE 初始程序加载器的入口函数,将系统控制权彻底移交至 DXE 阶段。
3.2 关键点 & 执行流程
-
PEI 阶段入口。
_ModuleEntryPoint()或_pei_main(),在 SEC 阶段完成之后,会将一些信息传入 PEI 阶段,如临时内存的基地址和大小,Boot Mode,固件卷基地址,栈顶指针,是否为冷启动等。 -
PEI 阶段由两类模块组成,分别为
PEI Core(PEI Foundation),和PEIMs(PEI Modules)。前者为 PEI 的内核,负责调度 PEIM,创建HOB,加载DXE。后者为各种硬件初始化模块,如内存控制器,芯片组,I/O 控制器等。PEI Core 会调用一系列PEIM。这些模块由EFI_PEI_PPI_DESCRIPTOR描述。 -
一些关键数据结构。
c/** * HOB 用于项 DXE 传递硬件资源信息 * 下面是一些常见的 HOB 类型,这些 HOB 最后会被 DXE Core 读取,用来构建 UEFI 系统表。 */ EFI_HOM_MEMORY_ALLOCATION // 内存分配信息 EFI_HOB_RESOURCE_DESCRIPTOR // 物理内存资源描述 EFI_HOB_GUID_TYPE // 自定义结构(比如SMBIOS、ACPI表等) EFI_HOB_FIRMWARE_VOLUME // 固件卷位置 EFI_HOB_CPU // CPU 信息(寄存器宽度、特性) /** * PPI(PEI-to-PEI Interface) * PEI 阶段存在诸多模块,PPI 用于这些模块之间的通信 * PEI Core 会根据 GUID 调用这些接口。 */ EFI_PEI_PPI_DESCRIPTOR gMemoryInitPpi = { EFI_PEI_PPI_DESCRIPTOR_PPI, &gEfiPeiMemoryInitPpiGuid, &PeiMemoryInitInterface };
GUID(Globally Unique Identifier ,全局唯一标识符)是一个 128 位的唯一编号,UEFI 使用它来唯一标识:各种模块(PEIM、PPI、Protocol),各种数据结构(比如 HOB、FV 文件、变量等)
例如:#define EFI_PEI_READ_ONLY_VARIABLE2_PPI_GUID
{ 0x2ab86ef5, 0xecb5, 0x4134, {0xb5, 0x89, 0x39, 0x1e, 0x29, 0x8b, 0xfa, 0x88} }
PEIM之间靠 GUID 来标识和查找接口,而不是靠函数名或文件路径。
执行流程:
-
SEC 阶段完成 CPU 栈初始化,将临时内存参数传给 PEI,跳转到
PeiCore()。 -
建立临时内存(栈,全局变量区),CAR。
-
PEI Core 解析 Firmware Volume,查找 .PEIM 模块,调用其中的入口函数,执行例如
-
MemoryInitPeim→ 初始化 DRAM; -
CpuInitPeim→ 配置 CPU; -
PchInitPeim→ 初始化芯片组; -
BoardInitPeim→ 板级设备配置。
-
-
内存初始化完成之后 PEI Core 会通过
BuildHob()系列函数创建各种 HOB。记录系统物理内存布局、固件卷地址、CPU信息等。cBuildResourceDescriptorHob(...); BuildMemoryAllocationHob(...); BuildCpuHob(...); -
PEI Core 搜索包含 DXE Core 的固件卷,创建一个
EFI_HOB_FIRMWARE_VOLUME,将该区域标记为可执行代码段。 -
将堆栈和数据从 Cache 搬到 DRAM,更新指针,释放临时 RAM。
-
进入 DXE 阶段。
典型文件:
txt
SEC Core MdeModulePkg/Core/Sec/SecMain.c 最早入口
PEI Core MdeModulePkg/Core/Pei/PeiMain.c PEI主循环与调度
MemoryInitPeim MdeModulePkg/Universal/MemoryInitPei/ 内存初始化模块
HobLib MdePkg/Library/HobLib/ 创建 HOB 的库函数
PeiServicesLib MdePkg/Library/PeiServicesLib/ 提供 PEIM 注册、PPI 管理接口
四、DXE---驱动执行环境阶段
4.1 简介
DEX 阶段是 UEFI 固件初始化的核心与主体,其核心使命是通过加载大量的驱动程序,来全面初始化处理器,芯片组及各类平台硬件,并构建出丰富的 Boot Services 与 Runtime Services 服务框架,最终为操作系统的加载做好一切准备。
System Table 是总表;
Boot Services 是启动期间的 API 集合,可供启动期间的程序调用;
Runtime Services 是 OS 运行期间的 API 集合,在操作系统启动之后,会为其创建虚拟地址空间映射,仍然可以调用相关函数。
具体介绍见下文。
DEX 在接受 PEI 阶段提交的 HOB 列表后,其便在一个功能完备的内存环境中展开。DEX 首先会初始化一系列基础服务 ,如驱动程序调度器,句柄数据库,Protocol 接口管理体系等,为后续驱动程序的有序加载和交互提供基础设施。随后,系统会进入一个密集的驱动程序发现与执行期 。固件会从多个 Firmware Volume 中 扫描加载大量的 DXE 驱动程序 ,这些程序遵循依赖关系被有序调度,逐一执行,从而完整对所有 CPU 高级功能,芯片组复杂单元,总线控制器,磁盘设备,网络接口以及输入输出设备 等平台硬件的深度检测,初始化和驱动。在此过程中,每个驱动程序通常会将其提供的功能以 Protocol 的形式安装到系统数据库中 ,其他驱动或应用程序可以通过定位这些 Protocol 来使用相应的服务,这种模块化的设计 使得硬件初始化和服务构建高效灵活可扩展。在硬件资源就绪后,DXE 会逐步构建并完善 Boot Services 与 Runtime Services,前者为操作系统加载器提供内存管理,协议接口,事件定时等关键服务,后者包含在操作系统运行后仍需固件管理的功能。在所有的驱动程序成功加载并执行完毕之后,DXE 阶段的核心任务编宣告完成,系统将控制权交给 BDS阶段。
4.2 关键点介绍
DEX 阶段是 UEFI 的"核心操作系统内核",负责建立完整的驱动执行环境,并初始化所有系统资源,为加载 Bootloader 做准备。
DEX 阶段的执行环境:
- 内存已完全初始化;
- MMU、Cache 已启用;
- 可以使用堆、栈;
- 支持动态加载
.efi驱动; - 支持中断(部分架构)。
DEX 阶段的核心:
- 初始化 CPU,内存,总线控制器(从内向外,包括MMU、Cache、PCI、USB、SATA 等);
- 构建 EFI System Table(建立 UEFI 的核心接口表,供后续驱动,应用调用);
- 加载 DXE 驱动(从 FV 中解析并执行);
- 管理协议(Protocol),不同驱动之间通过 GUID 协议通信(类似面向对象接口);
- 启动 BDS 阶段。
三大核心模块:
-
DEX Core:管理驱动加载,协议注册,服务调度。其就像一个迷你的操作系统内核,它建立全局数据结构(EFI_SYSTEM_TABLE, Boot Services, Runtime Services),管理内存分配与页表,维护"Protocol Database"------所有 GUID 协议的注册表,提供动态驱动加载,卸载和通信机制。它在 PEI 阶段结束时被加载,加载入口由 PEI 阶段的 HOB 提供。
-
DEX Dispatcher:调度器,扫描 FV 中的 DXE 驱动,并按依赖关系执行。DXE Dispatcher 像一个动态驱动管理器,扫描固件卷中所有的 DXE Driver文件(.efi),根据文件中的 Dependency Section 判断依赖关系,按照依赖顺序逐个加载,执行驱动,类似于 Linux 中的 modprobe。
-
DEX Drivers:各种功能驱动模块PCI、USB、图形、文件系统、网络、变量服务等。几乎所有硬件的驱动,服务都在这里被初始化,如下表所示。每个驱动都是一个独立的 efi 文件,包含驱动入口函数,一组协议实现(用 GUID 标识),安装、注册、回调机制。
分类 示例 CPU/Chipset MTRR、Cache、中断控制器 内存 内存映射服务 PCI/USB/SATA 总线控制器驱动 图形 GOP (Graphics Output Protocol) 文件系统 Simple File System Protocol 网络 PXE Base Code Protocol 变量存储 Variable Services Protocol 安全 Secure Boot Protocol
Protocol
前面多次提到 Protocol,这是 DXE 阶段的通信机制。先前我们介绍过 PEI 阶段的通信机制 PPI,接下来我们对 Protocol 进行简要说明,以对 DXE 阶段的通信有一个初步的理解。
c
/* Protocol 的结构: */
typedef struct {
EFI_STATUS (EFIAPI *Start)(...);
EFI_STATUS (EFIAPI *Stop)(...);
...
} EFI_DRIVER_PROTOCOL;
/* 每个协议由一个 GUID 唯一标识 */
#define EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID \
{ 0x9042a9de, 0x23dc, 0x4a38, {0x96,0xfb,0x7a,0xde,0xd0,0x80,0x51,0x6a} }
/* 安装协议 */
gBS->InstallProtocolInterface(Handle, &gEfiGraphicsOutputProtocolGuid, EFI_NATIVE_INTERFACE, &Gop);
/* 查找协议 */
EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop;
gBS->LocateProtocol(&gEfiGraphicsOutputProtocolGuid, NULL, (VOID **)&Gop);
这里对 PPI 以及 Protocol 的介绍较为简略,本文主要学习对 UEFI 的启动过程,细节内容后续学习过程中会陆续补充。
重要的全局结构:
EFI_SYSTEM_TABLE,Boot Services,Runtime Services。
系统表:
当 UEFI 应用(如 Bootloader)运行时,会把这个表作为参数传入efi_main(),让应用可以访问 UEFI 服务。
c
/* 1. EFI系统表 EFI_SYSTEM_TABLE
* 操作系统启动后,主要通过系统表与固件交互
*/
typedef struct {
EFI_TABLE_HEADER Hdr; // 表头信息(签名、版本、大小)
CHAR16 *FirmwareVendor; // 固件厂商
UINT32 FirmwareRevision;// 固件版本号
EFI_HANDLE ConsoleInHandle; // 控制台输入设备句柄
EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ConIn; // 输入协议接口(键盘输入)
EFI_HANDLE ConsoleOutHandle;// 控制台输出设备句柄
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut; // 输出协议接口(屏幕输出)
EFI_HANDLE StandardErrorHandle;
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *StdErr; // 标准错误输出
EFI_RUNTIME_SERVICES *RuntimeServices;// 运行时服务表指针
EFI_BOOT_SERVICES *BootServices; // 引导服务表指针
UINTN NumberOfTableEntries;
EFI_CONFIGURATION_TABLE *ConfigurationTable;
} EFI_SYSTEM_TABLE;
Boot Services:
它是一个结构体EFI_BOOT_SERVICES,它在系统还没有"交出控制权"给操作系统之前,提供大量的底层服务接口,也就相当于一系列 API,提供上层调用,调用方式如下。
| 功能类别 | 示例函数 | 作用 |
|---|---|---|
| 内存管理 | AllocatePages() / FreePages() |
分配或释放物理页 |
| 事件与定时 | CreateEvent() / WaitForEvent() |
创建、等待事件 |
| 协议操作 | InstallProtocolInterface() / LocateProtocol() |
安装、查找驱动协议 |
| 句柄操作 | HandleProtocol() / LocateHandle() |
查找设备句柄 |
| 加载执行 | LoadImage() / StartImage() |
加载并执行 EFI 应用或驱动 |
| 设备访问 | OpenProtocol() / CloseProtocol() |
访问驱动协议接口 |
| 退出引导服务 | ExitBootServices() |
结束 Boot Services,进入操作系统 |
c
/* 2. Boot Services
- 提供内存分配,事件、协议管理、句柄操作等功能。
- 在 OS Loader 阶段使用;
- 在 ExitBootServices() 调用后失效。
*/
// Boot Services提供接口调用方式举例
EFI_STATUS Status;
EFI_PHYSICAL_ADDRESS Addr = 0x100000;
Status = SystemTable->BootServices->AllocatePages(AllocateAnyPages, EfiLoaderData, 1, &Addr);
Runtime Services:
Runtime Services 同样是一个结构体 EFI_RUNTIME_SERVICES。与 Boot Services 不同的是,它提供的服务在操作系统启动之后仍然存在,这些函数的实现被 OS 的 UEFI 驱动或内核映射进虚拟地址空间。部分服务如下。
| 功能类别 | 示例函数 | 说明 |
|---|---|---|
| 时间日期 | GetTime() / SetTime() |
获取或设置系统时间 |
| 变量服务 | GetVariable() / SetVariable() |
访问 NVRAM 变量 |
| 系统复位 | ResetSystem() |
重启、关机等操作 |
| 虚拟地址转换 | SetVirtualAddressMap() |
OS 建立虚拟内存时使用 |
| 固件更新 | UpdateCapsule() |
支持 BIOS/UEFI 更新机制 |
c
/* 3. Runtime Services
- 提供变量访问、时间服务、系统重启等功能;
- 在 OS 运行时仍可使用;
- 驻留在固件中,操作系统通过 SMM 调用。
五、BDS---启动设备选择阶段
5.1 简介
BDS 阶段是连接平台硬件初始化完成与操作系统加载器执行 的桥梁,核心使命是初始化控制台设备,管理启动选项,并依据预设的启动策略来定位并执行目标操作系统的引导程序 。当 DXE 阶段将所有必要的硬件驱动和 Boot Services 都准备好之后,系统控制权移交至 BDS,BDS 首先会执行一系列全局性系统初始化,包括图形控制台需要的显卡,键盘,鼠标 等。随后,BDS 会加载和执行特定的驱动和应用程序,进一步枚举和初始化更多的设备,如 USB,网卡 等,这些设备在 DXE 阶段未被强制初始化,但对于发现启动项至关重要。在设备就绪后,BDS 核心逻辑会根据 NVRAM 中存储的 UEFI 启动变量来搜集和整理所有可用的启动选项 ,这些选项可能来自硬盘上的 EFI 系统分区,网络服务器,光驱或者其他可引导介质 。随后,BDS 会依据启动策略(例如默认启动,用户手动选择或单一启动)来尝试执行这些启动项 。它会按照优先级顺序**,逐个加载每个启动项对应的 OS 引导程序至内存** 中(如Windows的bootmgfw.efi或Linux的grubx64.efi)。一旦某个引导程序被成功加载并执行,BDS阶段便将其辛苦构建的整个Boot Services环境移交给该引导程序。至此,BDS阶段的历史任务宣告完成,系统的控制权正式过渡给操作系统的引导装载程序。
5.2 关键点介绍
BDS 的核心指责是根据系统配置和启动策略,选择合适的启动设备,并执行对应的启动加载程序。
在 EDK2 中,BDS 阶段主要通过以下文件实现:MdeModulePkg/Core/DxeBds/
核心函数入口:
c
VOID EFIAPI BdsEntry (
IN EFI_BDS_ARCH_PROTOCOL *This
)
// 流程包括:
BdsInitialize() // 初始化控制台设备;
BdsBootDeviceSelect() // 启动设备选择;
BdsBootViaBootOption() // 尝试加载 Boot####;
BdsBootSuccessHandler() // BdsBootFailHandler():处理成功或失败结果;
BdsEnterFrontPage() // 用户交互界面。
具体任务
-
创建控制台设备(Console)。确定系统的输入输出设备,键盘,显示器,串口等。建立标准的输入输出句柄(ConIn、ConOut、StdErr),使得用户可以看到 BIOS/UEFI 的启动画面、输入命令。
-
加载并执行 Boot Manager(引导管理器)。这是 UEFI 规范规定的,系统固件应该包括一个引导管理器,负责引导顺序管理。它读取 Boot Order 、Boot Option 等 NVRAM 变量,判断从哪个设备启动。
-
枚举所有 Boot Option(启动选项)。这些选项保存在 UEFI 变量中,如:
cBootOrder // 启动顺序,例如 [0001, 0003, 0000] Boot0000 // 启动项0,例如 EFI Shell Boot0001 // 启动项1,例如 Windows Boot Manager Boot0003 // 启动项3,例如 UEFI USB Device每个 Boot Option 包含启动设备路径(EFI_DEVICE_PATH)和加载映像文件路径。
-
尝试加载启动项中的 EFI 可执行文件。BDS 通过 Boot Services 的
LoadImage()和StartImage()来加载 EFI 应用(例如 bootx64.efi)。通常这些文件存放在\EFI\BOOT\BOOTX64.EFI,\EFI\Microsoft\Boot\bootmgfw.efi。如果加载失败,就尝试下一个 Boot Option。 -
处理 Boot Maintenance Manager (BMM)。若所有 Boot Option 均失败,BDS 会进入 BMM(类似 BIOS Setup 界面)。允许用户修改启动顺序、设置 NVRAM 启动变量、更新 Boot Option。
-
最终移交控制权。成功加载 Bootloader(例如 GRUB、Windows Boot Manager)后:调用
ExitBootServices(),销毁 Boot Services,把控制权交给 Bootloader,进入操作系统加载阶段。
关键数据结构
-
Boot Option(启动项结构)
ctypedef struct { UINT32 Attributes; UINT16 FilePathListLength; CHAR16 Description[]; // 启动项名称(如 "Windows Boot Manager") EFI_DEVICE_PATH_PROTOCOL FilePathList[]; // 启动文件路径 UINT8 OptionalData[]; // 启动时需要传入的参数 } EFI_LOAD_OPTION; -
BootOrder:存储在 NVRAM 中的启动顺序表。
Boot Order 启动项编号 含义 0001 Windows Boot Manager 启动 Windows 0003 UEFI USB Device 从 USB 启动 0000 EFI Shell 启动到 UEFI Shell
六、TSL---瞬态系统加载阶段
6.1 简介
TSL 是 BDS 阶段成功找到并开始执行操作系统引导程序(如.efi文件)后,直到该引导程序将系统控制权正式移交给操作系统内核之前的一个临时性的过渡执行环境 。在此阶段,操作系统的引导程序(例如 GRUB 或 Windows Boot Manager)作为 UEFI 环境下的一个特殊应用程序,依然完整地运行在 UEFI Boot Services 所构建的富运行时环境之中 ,它可以自由地调用 Boot Services 提供的内存管理、协议接口和磁盘I/O等服务,来完成诸如加载操作系统内核、初始化文件系统、解析配置文件等 关键准备工作;然而,TSL阶段的**"临时"性** 也正体现在此,其核心目标是为最终告别 UEFI 环境并启动真正的操作系统做准备,因此,一旦引导程序完成了所有必要的加载任务,它就必须主动调用 ExitBootServices() 函数 ,这个关键调用标志着 TSL 阶段的终结。系统控制权由此正式移交,从而进入不再依赖UEFI固件的RT阶段。
6.2 关键点介绍
TSL 是瞬态的,它只在交接控制权的瞬间存在。
主要任务
- 执行 BootLoader。BDS 阶段通过
LoadImage()+StartImage()启动 BootLoader,BootLoader 在 UEFI 环境中执行,它是一个标准的 EFI 应用程序。这意味着 BootLoader 也能使用 UEFI 提供的 Boot Services 和 Runtime Services。 - 准备加载操作系统内核,BootLoader(例如 GRUB 或 Windows Boot Manager)会解析配置文件,选择内核映像(kernel image),加载内核到内存,设置命令行参数、RAMDisk、ACPI 表、内存映射等信息,准备 CPU 状态(模式、分页、栈等)。
- 退出 UEFI 环境。调用
ExitBootServices()。此时,所有 Boot Services 被销毁,只剩下 Runtime Services。 - 跳转到操作系统入口点。
一些关键函数
GetMemoryMap()。在调用ExitBootServices()之前,BootLoader 必须 先调用这个函数,获取当前系统的内存布局,返回一个MapKey,这个MapKey是 ExitBootServices 的凭证(必须匹配当前内存状态)。ExitBootServices(),退出 UEFI 环境。通知 UEFI 固件释放所有 Boot Services,停止所有驱动、定时器、中断等,将内存管理完全交给 BootLoader/OS,仅保留 Runtime Services(例如时间、变量等功能)。
ExitBootServices()调用前后内存映射的变化。- 在
ExitBootServices()之前:所有固件驱动、服务都在运行,Boot Services 负责内存分配、设备管理,内存中存在大量 EFI 类型的区域,如EfiBootServicesData。 - 在
ExitBootServices()之后:固件停止介入,所有 Boot Services 相关区域被标记为 "可重用",BootLoader 或 OS 内核接管内存管理,仅剩EfiRuntimeServicesCode/Data区域仍被保留,供 OS 使用。
七、RT 和 AL 阶段
RT 阶段是在操作系统已经接过控制权之后,不做过多介绍,简单说一下此时固件还能作用的 Runtime Services 的相关函数。
| 功能类别 | 典型函数 | 作用 |
|---|---|---|
| 时间服务 | GetTime(), SetTime() |
获取/设置系统时间(CMOS时钟) |
| 虚拟内存服务 | SetVirtualAddressMap() |
固件映射从物理地址切换到虚拟地址(OS启用分页后使用) |
| 变量服务 | GetVariable(), SetVariable() |
读取/写入NVRAM变量(如启动项、SecureBoot状态) |
| 重置服务 | ResetSystem() |
控制系统重启、关机、S3睡眠等 |
| 字符串服务 | (已废弃) | 早期版本中用于输出信息 |
AL 阶段是操作系统关闭电源或重启前的最后一个阶段。其通常在以下情况发生:
- 用户执行关机(Shutdown);
- 系统执行重启(Reboot);
- 系统进入睡眠/休眠(S3/S4);
- 系统掉电(Power Loss)或崩溃。
AL 阶段控制权变化:
| 时间点 | 控制权归属 | 状态 |
|---|---|---|
| OS 正常运行 | 操作系统 | 使用 Runtime Services |
| 调用 ResetSystem() | 操作系统 → 固件 | 固件开始复位 |
| 重启/关机完成 | 固件接管 | 重新进入 SEC 阶段或断电 |
结语
本文较为详细介绍了 UFEI 规范中启动的主要流程,由于本人是初学者,很多地方理解的不是很深刻,未来学习中会持续修改补充。本文是学习 UEFI 启动过程的笔记记录,主要内容源自 AI 搜索整理。
Seriousness is the cornerstone of everything!