如何使用GNU-EFI访问其他分区中的EFI可执行文件?
问题
我需要在磁盘中创建两个FAT16分区,并将一个EFI二进制文件放置在另一个FAT16分区中。最初我编写了一个基础的HelloWorld.efi应用(第二阶段),在第一阶段使用以下两个API来执行同一分区内的HelloWorld.efi:
efi_status = BS->HandleProtocol(image_handle, &EFI_LOADED_IMAGE_GUID, (void **)&shim_li); efi_status = BS->HandleProtocol(device, &EFI_SIMPLE_FILE_SYSTEM_GUID, (void **) &drive);
目前我正尝试寻找访问另一分区中HelloWorld.efi的方法,使用上述API仅能访问同一分区内的文件,请问应该使用哪些API?
解决方案
要访问磁盘上的其他分区,需要先枚举磁盘的所有分区,再逐个验证并挂载目标分区的文件系统,核心步骤和API如下:
1. 获取磁盘的分区协议
从已加载的镜像中拿到磁盘设备句柄,再通过该句柄获取EFI_PARTITION_PROTOCOL——这个协议用于枚举磁盘上的所有分区:
// 从EFI_LOADED_IMAGE获取磁盘设备句柄 EFI_HANDLE disk_handle = shim_li->DeviceHandle; // 获取分区协议 EFI_PARTITION_PROTOCOL *partition_proto; efi_status = BS->HandleProtocol(disk_handle, &EFI_PARTITION_PROTOCOL_GUID, (void**)&partition_proto); if (EFI_ERROR(efi_status)) { // 处理错误 return efi_status; }
2. 枚举所有分区
调用EFI_PARTITION_PROTOCOL的GetPartitionList方法,获取磁盘上所有分区的句柄列表:
EFI_HANDLE *partition_handles; UINTN handle_count; efi_status = partition_proto->GetPartitionList( partition_proto, FALSE, // FALSE表示返回所有类型的分区 &partition_handles, &handle_count ); if (EFI_ERROR(efi_status)) { return efi_status; }
3. 遍历分区并识别目标FAT16分区
对每个分区句柄尝试获取EFI_SIMPLE_FILE_SYSTEM_PROTOCOL,验证是否为可挂载的FAT分区。同时可以通过分区类型GUID(EFI_FAT16_FILE_SYSTEM_GUID)精准定位目标分区:
for (UINTN i = 0; i < handle_count; i++) { EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *fs_proto; efi_status = BS->HandleProtocol( partition_handles[i], &EFI_SIMPLE_FILE_SYSTEM_GUID, (void**)&fs_proto ); if (EFI_ERROR(efi_status)) { continue; // 跳过非文件系统分区 } // 可选:验证分区是否为FAT16类型 EFI_DEVICE_PATH_PROTOCOL *dev_path; efi_status = BS->HandleProtocol( partition_handles[i], &EFI_DEVICE_PATH_PROTOCOL_GUID, (void**)&dev_path ); if (!EFI_ERROR(efi_status)) { // 遍历设备路径,找到分区类型节点 while (!IsDevicePathEnd(dev_path)) { if (dev_path->Type == MEDIA_DEVICE_PATH && dev_path->SubType == MEDIA_HARDDRIVE_DP) { HARDDRIVE_DEVICE_PATH *hd_dev_path = (HARDDRIVE_DEVICE_PATH*)dev_path; if (CompareGuid(&hd_dev_path->PartitionTypeGuid, &EFI_FAT16_FILE_SYSTEM_GUID)) { // 找到FAT16分区,开始挂载 break; } } dev_path = NextDevicePathNode(dev_path); } } // 挂载文件系统根目录 EFI_FILE_PROTOCOL *root_dir; efi_status = fs_proto->OpenVolume(fs_proto, &root_dir); if (!EFI_ERROR(efi_status)) { // 访问目标分区内的HelloWorld.efi EFI_FILE_PROTOCOL *target_file; efi_status = root_dir->Open( root_dir, &target_file, L"HelloWorld.efi", EFI_FILE_MODE_READ, 0 ); if (!EFI_ERROR(efi_status)) { // 成功打开文件,后续可加载执行 target_file->Close(target_file); } root_dir->Close(root_dir); } }
4. 释放资源
枚举完成后,释放分区句柄列表的内存:
BS->FreePool(partition_handles);
关键API总结
- EFI_PARTITION_PROTOCOL:用于枚举磁盘上的所有分区
- EFI_DEVICE_PATH_PROTOCOL:用于识别分区的类型(如FAT16)
- 配合原有的
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL和EFI_FILE_PROTOCOL完成文件访问
内容的提问来源于stack exchange,提问作者Santi
相关产品推荐
相关产品推荐

