从UEFI引导加载程序调用独立EFI文件的入口点
自研UEFI引导加载器调用同盘EFI文件的设备路径实现指引
核心逻辑
要调用LoadImage和StartImage,核心是手动构造EFI设备路径链——这是UEFI用来定位存储设备和文件的结构化数据,由多个节点链式拼接而成,最后以结束节点收尾。你需要实现两个关键节点:定位虚拟硬盘分区的媒体节点,以及定位目标EFI文件的文件路径节点。
分步实现
1. 设备路径节点基础结构
所有设备路径节点都包含统一头部,后续紧跟节点特定数据:
typedef struct { UINT8 Type; // 设备类型:媒体设备=0x01,结束节点=0x7F UINT8 SubType; // 子类型:硬盘分区=0x01,文件路径=0x04,结束节点=0xFF UINT16 Length; // 整个节点的字节长度(含头部) } EFI_DEVICE_PATH_PROTOCOL;
2. 构造硬盘分区媒体节点
这个节点用来定位你的虚拟硬盘上的EFI系统分区:
typedef struct { EFI_DEVICE_PATH_PROTOCOL Header; UINT32 PartitionNumber; // 分区号(从1开始,通常EFI分区是1) UINT64 PartitionStart; // 可选,填0让UEFI自动识别 UINT64 PartitionSize; // 可选,填0让UEFI自动识别 UINT8 Signature[16]; // GPT分区GUID/MBR分区签名(前4字节) UINT8 SignatureType; // 0x01=MBR,0x02=GPT } MEDIA_HARDDRIVE_DEVICE_PATH;
- 头部填充:
Type=0x01,SubType=0x01,Length=sizeof(MEDIA_HARDDRIVE_DEVICE_PATH) - 分区信息:根据你的虚拟硬盘分区类型(MBR/GPT)填充
Signature和SignatureType,PartitionNumber填对应分区号
3. 构造文件路径节点
这个节点用来指定分区内的目标EFI文件路径(UTF-16编码):
typedef struct { EFI_DEVICE_PATH_PROTOCOL Header; CHAR16 PathName[]; // 宽字符路径,比如L"\\EFI\\BOOT\\test.efi" } MEDIA_FILEPATH_DEVICE_PATH;
- 头部填充:
Type=0x01,SubType=0x04,Length=sizeof(EFI_DEVICE_PATH_PROTOCOL) + (wcslen(PathName)+1)*2(要包含末尾的空字符) - 路径要求:必须是UEFI支持的UTF-16宽字符串,路径分隔符用
\
4. 拼接设备路径链
将三个部分按顺序拼接在连续内存中:
- 先通过
gBS->AllocatePool分配足够内存,总长度=媒体节点长度 + 文件节点长度 + 4(结束节点长度) - 依次复制媒体节点、文件节点,最后添加结束节点(
Type=0x7F,SubType=0xFF,Length=4)
5. 调用UEFI服务加载启动EFI文件
构造好设备路径后,直接调用标准UEFI服务:
EFI_STATUS status; EFI_HANDLE image_handle; EFI_DEVICE_PATH_PROTOCOL *device_path = /* 你构造的设备路径 */; // 加载镜像 status = gBS->LoadImage( FALSE, gImageHandle, device_path, NULL, 0, &image_handle ); // 启动镜像 if (!EFI_ERROR(status)) { status = gBS->StartImage(image_handle, NULL, NULL); }
关键注意事项
- 内存管理:用
gBS->AllocatePool分配设备路径内存,使用完后用gBS->FreePool释放 - 编码正确性:必须使用UTF-16宽字符串,C语言中
L""前缀的字符串直接满足要求 - 节点长度准确:每个节点的
Length字段必须精确计算,否则UEFI会无法解析设备路径 - 分区信息获取:如果不知道分区号或GUID,可以遍历系统中的
EFI_BLOCK_IO_PROTOCOL设备,自行读取解析分区表
内容的提问来源于stack exchange,提问作者DSMOS
相关产品推荐
相关产品推荐

