1. 解析UEFI启动流程与平台初始化阶段划分
任务目标
编写一个UEFI应用程序,在启动时打印当前所处的阶段,并识别SEC、PEI、DXE、BDS、RT各阶段的入口点特征。该程序作为DXE阶段的UEFI Application运行,通过UEFI系统表与运行时服务定位各阶段入口点的特征地址,并在串口/控制台输出阶段标识。
实现原理
UEFI平台初始化按SEC→PEI→DXE→BDS→RT顺序推进。本案例程序在DXE阶段被加载执行,通过以下方式识别各阶段:
- SEC:无标准UEFI接口,入口点为处理器复位向量(0xFFFFFFF0),通过读取固件基址附近特征判断。
- PEI:通过EFI_PEI_SERVICES结构定位,其签名
PEIM位于服务表头部。 - DXE:当前程序运行阶段,通过
gST->Hdr.Signature(EFI_SYSTEM_TABLE_SIGNATURE)确认。 - BDS:通过
gST->BootServices中的ExitBootServices入口地址间接识别(BDS阶段负责调用它)。 - RT:通过
gST->RuntimeServices表头签名EFI_RUNTIME_SERVICES_SIGNATURE确认。
完整代码
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
// PEI服务签名
#define PEI_SERVICES_SIGNATURE 0x565245535f494550ULL // "PEI_SERV"
EFI_STATUS
EFIAPI
UefiMain (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
UINTN Index;
EFI_PEI_SERVICES **PeiServices;
EFI_RUNTIME_SERVICES *RT;
UINT64 SecEntry;
UINT64 DxeEntry;
UINT64 BdsEntry;
// 1. 识别当前阶段:DXE(通过系统表签名)
if (SystemTable->Hdr.Signature == EFI_SYSTEM_TABLE_SIGNATURE) {
Print(L"[阶段识别] 当前运行阶段: DXE\r\n");
Print(L" - 系统表签名: 0x%016lx (EFI_SYSTEM_TABLE_SIGNATURE)\r\n",
SystemTable->Hdr.Signature);
}
// 2. 识别RT阶段:运行时服务表签名
RT = SystemTable->RuntimeServices;
if (RT != NULL && RT->Hdr.Signature == EFI_RUNTIME_SERVICES_SIGNATURE) {
Print(L"[阶段识别] RT阶段入口特征: 运行时服务表已就绪\r\n");
Print(L" - RT服务表签名: 0x%016lx\r\n", RT->Hdr.Signature);
Print(L" - GetTime入口: 0x%p\r\n", RT->GetTime);
Print(L" - SetVariable入口: 0x%p\r\n", RT->SetVariable);
}
// 3. 识别BDS阶段:通过ExitBootServices入口(BDS阶段调用此函数切换到RT)
if (SystemTable->BootServices != NULL) {
BdsEntry = (UINT64)(UINTN)SystemTable->BootServices->ExitBootServices;
Print(L"[阶段识别] BDS阶段入口特征: ExitBootServices @ 0x%p\r\n",
(VOID *)(UINTN)BdsEntry);
Print(L" - BDS阶段负责调用ExitBootServices以退出启动服务\r\n");
}
// 4. 识别DXE阶段入口:DXE核心通过系统表BootServices分发
DxeEntry = (UINT64)(UINTN)SystemTable->BootServices;
Print(L"[阶段识别] DXE阶段入口特征: BootServices表 @ 0x%p\r\n",
(VOID *)(UINTN)DxeEntry);
Print(L" - BootServices表签名: 0x%016lx\r\n",
SystemTable->BootServices->Hdr.Signature);
// 5. 识别PEI阶段:通过定位PEI服务表(在HOB列表中查找)
// 实际PEI服务在DXE阶段已不可直接访问,此处通过HOB列表中的引导服务
// 数据结构间接确认PEI阶段的存在与入口特征。
PeiServices = NULL;
// 遍历HOB列表查找PEI服务表指针(简化:通过引导服务分配内存搜索)
// 此处演示通过DXE阶段可访问的HOB列表定位PEI阶段特征
{
EFI_PHYSICAL_ADDRESS HobStart;
EFI_HOB_HANDOFF_INFO_TABLE *HandoffTable;
HobStart = SystemTable->BootServices->GetHobList();
HandoffTable = (EFI_HOB_HANDOFF_INFO_TABLE *)HobStart;
if (HandoffTable != NULL && HandoffTable->Header.HobType == EFI_HOB_TYPE_HANDOFF) {
Print(L"[阶段识别] PEI阶段入口特征: HOB列表起始 @ 0x%p\r\n",
(VOID *)(UINTN)HobStart);
Print(L" - 引导模式: 0x%x\r\n", HandoffTable->BootMode);
// 在HOB列表中搜索PEI服务表指针(类型EFI_HOB_TYPE_GUID_EXTENSION)
// 实际固件中PEI服务表地址可通过特定GUID HOB获取
// 此处演示通过HOB类型识别PEI阶段遗留信息
for (Index = 0; Index < 16; Index++) {
EFI_HOB_GENERIC_HEADER *Hob;
Hob = (EFI_HOB_GENERIC_HEADER *)(UINTN)(HobStart + Index * sizeof(UINT64));
if (Hob->HobType == EFI_HOB_TYPE_GUID_EXTENSION) {
Print(L" - 发现GUID扩展HOB (PEI阶段遗留数据)\r\n");
break;
}
}
}
}
// 6. 识别SEC阶段:复位向量位于4GB-16字节处(0xFFFFFFF0)
SecEntry = 0xFFFFFFF0ULL;
Print(L"[阶段识别] SEC阶段入口特征: 复位向量 @ 0x%llx\r\n", SecEntry);
Print(L" - SEC阶段从复位向量开始执行,初始化临时RAM\r\n");
// 7. 打印阶段划分总结
Print(L"\r\n[阶段划分总结]\r\n");
Print(L" SEC: 0xFFFFFFF0 (复位向量)\r\n");
Print(L" PEI: HOB列表 @ 0x%p (PEI服务已移交DXE)\r\n",
(VOID *)(UINTN)SystemTable->BootServices->GetHobList());
Print(L" DXE: BootServices @ 0x%p (当前执行阶段)\r\n",
(VOID *)(UINTN)SystemTable->BootServices);
Print(L" BDS: ExitBootServices @ 0x%p\r\n",
(VOID *)(UINTN)SystemTable->BootServices->ExitBootServices);
Print(L" RT: RuntimeServices @ 0x%p\r\n", (VOID *)(UINTN)RT);
return EFI_SUCCESS;
}
编译与运行
- 环境:EDK2开发环境(含OVMF模拟器或真实UEFI平台)
- 编译:将上述代码保存为
PhaseDetect.c,在.inf文件中声明UefiMain入口,使用build命令编译生成PhaseDetect.efi - 运行:在UEFI Shell中执行
PhaseDetect.efi,或在OVMF中通过启动菜单加载
验证方法
程序执行后,串口或控制台输出应包含以下关键信息:
- 首行显示
当前运行阶段: DXE,确认程序在DXE阶段被加载 - 显示
RT阶段入口特征及GetTime/SetVariable函数指针地址(非零值) - 显示
BDS阶段入口特征及ExitBootServices地址(非零值) - 显示
PEI阶段入口特征及HOB列表起始地址(非零值) - 显示
SEC阶段入口特征为固定地址0xFFFFFFF0 - 末尾输出五阶段总结表,各阶段地址均为有效非零值
若在OVMF中运行,所有输出应正常显示且无错误码返回(程序返回EFI_SUCCESS)。
常见问题
- 编译错误:确认
.inf文件中包含UefiLib、UefiBootServicesTableLib、UefiRuntimeServicesTableLib库依赖 - 输出乱码:检查串口终端波特率与固件设置一致(通常115200)
- HOB遍历不完整:实际固件HOB结构可能更复杂,本示例仅演示定位方法,不保证所有固件均能遍历到GUID扩展HOB