2. 遍历固件卷中的文件
任务目标
在UEFI环境中,固件卷(Firmware Volume)是存放PEIM、DXE驱动、协议等固件文件的容器。本案例实现一个DXE驱动,在入口点遍历指定固件卷(FV)中所有文件,识别每个文件的类型(EFI_FV_FILETYPE),并输出文件GUID及其在FV中的偏移量。
核心实现
#include <Uefi.h>
#include <Pi/PiFirmwareVolume.h>
#include <Pi/PiFirmwareFile.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>
EFI_STATUS
EFIAPI
FvFileTraversalEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_FIRMWARE_VOLUME2_PROTOCOL *FvProtocol;
EFI_HANDLE *HandleBuffer;
UINTN HandleCount;
UINTN Index;
EFI_GUID FileGuid;
EFI_FV_FILETYPE FileType;
EFI_FV_FILE_ATTRIBUTES FileAttributes;
UINTN FileSize;
EFI_PHYSICAL_ADDRESS FileOffset;
UINT32 AuthenticationStatus;
// 1. 定位系统中所有固件卷协议实例
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiFirmwareVolume2ProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "LocateHandleBuffer failed: %r\n", Status));
return Status;
}
// 2. 遍历每个固件卷
for (Index = 0; Index < HandleCount; Index++) {
Status = gBS->HandleProtocol (
HandleBuffer[Index],
&gEfiFirmwareVolume2ProtocolGuid,
(VOID **)&FvProtocol
);
if (EFI_ERROR (Status)) {
continue;
}
DEBUG ((DEBUG_INFO, "=== FV[%d] ===\n", Index));
// 3. 遍历当前FV中的所有文件
FileOffset = 0;
while (TRUE) {
FileGuid = gZeroGuid;
FileType = EFI_FV_FILETYPE_ALL;
FileAttributes = 0;
FileSize = 0;
AuthenticationStatus = 0;
Status = FvProtocol->ReadFile (
FvProtocol,
&FileGuid,
&FileType,
&FileAttributes,
&FileSize,
&FileOffset, // 输入当前偏移,输出下一文件偏移
&AuthenticationStatus
);
if (EFI_ERROR (Status)) {
if (Status == EFI_NOT_FOUND) {
// 已遍历完所有文件
break;
}
DEBUG ((DEBUG_ERROR, "ReadFile error: %r\n", Status));
break;
}
// 4. 输出文件信息:GUID、类型、偏移
DEBUG ((
DEBUG_INFO,
"File GUID: %g, Type: 0x%02X, Offset: 0x%08X, Size: 0x%X\n",
&FileGuid,
FileType,
(UINTN)FileOffset,
FileSize
));
}
}
if (HandleBuffer != NULL) {
FreePool (HandleBuffer);
}
return EFI_SUCCESS;
}
代码说明
- 协议获取:通过
LocateHandleBuffer查找系统中所有实现EFI_FIRMWARE_VOLUME2_PROTOCOL的句柄,该协议是访问FV的标准接口。 - 文件遍历机制:
ReadFile函数具有迭代语义——首次调用时FileOffset传入0,函数返回第一个文件的信息并将FileOffset更新为下一个文件的偏移;后续调用沿用该偏移即可依次获取所有文件。当返回EFI_NOT_FOUND时表示遍历结束。 - 文件类型识别:
FileType参数返回文件类型枚举值(如EFI_FV_FILETYPE_RAW=0x01、EFI_FV_FILETYPE_FREEFORM=0x02、EFI_FV_FILETYPE_DRIVER=0x03等),可直接用于判断文件性质。
编译与运行
- 工程文件:在EDK2环境中创建驱动工程,将上述代码放入
FvFileTraversal.c,并在.inf文件中声明依赖:[Sources] FvFileTraversal.c [LibraryClasses] UefiBootServicesTableLib DebugLib BaseMemoryLib [Protocols] gEfiFirmwareVolume2ProtocolGuid - 编译:使用EDK2构建命令
build -p YourPlatform.dsc -m FvFileTraversal.inf生成驱动。 - 加载:在UEFI Shell中执行
load FvFileTraversal.efi,驱动入口点自动运行。
验证方法
加载驱动后,在串口调试终端或调试器中观察DEBUG_INFO级别日志。预期输出格式为:
=== FV[0] ===
File GUID: 8C8CE58C-8ADB-4B5E-8B7E-9E4D3A1B2C3D, Type: 0x03, Offset: 0x00000028, Size: 0x4A0
File GUID: 1A2B3C4D-5E6F-7890-ABCD-EF1234567890, Type: 0x01, Offset: 0x000004C8, Size: 0x200
...
通过条件:日志中能列出至少一个FV中的多个文件,且每个文件的GUID非零、Type值在合法枚举范围内(0x00~0xFF)、Offset随遍历递增且与文件实际布局一致。若系统中有多个FV,应能看到FV[0]、FV[1]等多个区块。
常见问题
- 无输出:确认FV2协议已安装(部分平台需在DXE阶段后才可用),检查
LocateHandleBuffer返回的句柄数是否为0。 - 死循环:若
ReadFile返回错误但非EFI_NOT_FOUND,代码已做break处理;若返回EFI_SUCCESS但偏移不前进,需检查FV实现是否符合PI规范。 - 类型识别:
EFI_FV_FILETYPE_ALL(0xFF)仅用于查询通配,实际返回的类型值不会是该值。