我希望从使用 UEFI 的引导加载程序中调用单独的 EFI 文件中的函数,这两个文件都位于同一虚拟硬盘中。我研究了规范中定义的
LoadImage
和 StartImage
函数,但我无法弄清楚应该如何使用 EFI_DEVICE_PATH_PROTOCOL
类型来发挥我的优势。
根据我的研究,我发现其他人遇到的几乎所有示例或问题都是通过使用
EDK2和 GNU-EFI 中提供的一些
FileDevicePath
函数来解决的,我在那一刻我发现从头开始的学习经历非常有趣且富有启发性。我的观察得出的结论是,这个 FileDevicePath
函数能够将与文件路径名和文件媒体类型相关的数据粉碎到 EFI_DEVICE_PATH_PROTOCOL
变量中,但我不确定如何自己执行此操作,因为两个源代码示例都使用“ “魔法数字”的助手”函数和宏。也就是说,我正在努力理解设备路径协议结构所需的数据(路径、类型属性、长度等)如何适合规范的结构声明。如果有人能在这个主题上为我指明正确的方向,我非常尊重您使用这个系统的智慧和知识。感谢大家抽出宝贵的时间来做这件事。
设备路径由一个或多个节点组成。每个节点都是一个具有相同 header 的结构,但标头后可以包含不同的数据。 要迭代设备路径,您必须从标头开头前进 Length 字节才能到达下一个。 如果设备路径有效,则以类型为 0x7F 和子类型为 0x01 或 0xFF 的结束节点终止。我见过使用 0xFF 作为终端节点类型的系统,您也可以检查一下。
您需要通过 EFI_LOADED_IMAGE_PROTOCOL 协议获取父设备(加载引导加载程序的分区)的设备路径。 您需要的字段是 DeviceHandle,使用 HandleProtocol 或 OpenProtocol 从该句柄获取设备路径。
创建设备路径的副本,如果您不想使用辅助函数,则必须迭代设备路径节点,直到找到结束节点以获取长度。您需要在副本中添加一些空间,以便可以插入文件路径节点。
在结束节点之前插入包含 efi 文件路径的文件路径节点。
使用新的设备路径调用LoadImage,并使用LoadImage返回的图像句柄调用StartImage。
这是一个基于 EDK2 的示例,它尝试从同一设备/分区加载并启动另一个文件:
EFI_STATUS EFIAPI DevicePathLengthWithoutEnd(
IN CONST EFI_DEVICE_PATH_PROTOCOL* DevicePath,
OUT UINTN* Length)
{
UINTN TotalLength;
UINT16 CurrentLength;
CONST EFI_DEVICE_PATH_PROTOCOL* CurrentNode = NULL;
if (DevicePath == NULL || Length == NULL) {
return EFI_INVALID_PARAMETER;
}
*Length = 0;
TotalLength = 0;
for (
CurrentNode = DevicePath;
// You may want to check for the SubType also
CurrentNode->Type != 0x7F;
CurrentNode = ((CONST EFI_DEVICE_PATH_PROTOCOL*)(((UINT8*)CurrentNode) + (*(UINT16*)CurrentNode->Length)))) {
CurrentLength = *((UINT16*)CurrentNode->Length);
// Check for invalid nodes
if (CurrentLength < sizeof(EFI_DEVICE_PATH_PROTOCOL)) {
return EFI_INVALID_PARAMETER;
}
TotalLength += (UINTN)CurrentLength;
}
*Length = TotalLength;
return EFI_SUCCESS;
}
EFI_STATUS EFIAPI LoadFile(
IN CONST CHAR16* PathOnCurrentDevice)
{
EFI_STATUS Status;
UINTN PathSize;
UINTN OriginalDevicePathLength;
UINTN NewDevicePathLength;
EFI_LOADED_IMAGE_PROTOCOL* LoadedImage = NULL;
CONST EFI_DEVICE_PATH_PROTOCOL* OriginalDevicePath = NULL;
EFI_DEVICE_PATH_PROTOCOL* NewDevicePath = NULL;
EFI_DEVICE_PATH_PROTOCOL* NodeHelper = NULL;
UINT8* ByteHelper = NULL;
EFI_HANDLE ImageHandle;
if (PathOnCurrentDevice == NULL) {
return EFI_INVALID_PARAMETER;
}
// Get the size of the string (with \0) in bytes
PathSize = StrSize(PathOnCurrentDevice);
// Get the loaded image protocol from the current images handle
Status = gBS->HandleProtocol(
gImageHandle,
&gEfiLoadedImageProtocolGuid,
(VOID**)&LoadedImage);
if (EFI_ERROR(Status)) {
Print(u"%a:%d -> %r\r\n", __FILE__, __LINE__, Status);
goto finish;
}
// Get the device path from the current images source device handle
Status = gBS->HandleProtocol(
LoadedImage->DeviceHandle,
&gEfiDevicePathProtocolGuid,
(VOID**)&OriginalDevicePath);
if (EFI_ERROR(Status)) {
Print(u"%a:%d -> %r\r\n", __FILE__, __LINE__, Status);
goto finish;
}
// Get the length of the device path without the end node
Status = DevicePathLengthWithoutEnd(OriginalDevicePath, &OriginalDevicePathLength);
if (EFI_ERROR(Status)) {
Print(u"%a:%d -> %r\r\n", __FILE__, __LINE__, Status);
goto finish;
}
// You need the old path + the file node + an end node
NewDevicePathLength =
OriginalDevicePathLength +
sizeof(EFI_DEVICE_PATH_PROTOCOL) + PathSize +
sizeof(EFI_DEVICE_PATH_PROTOCOL);
Status = gBS->AllocatePool(
EfiLoaderData,
NewDevicePathLength,
(VOID**)&NewDevicePath);
if (EFI_ERROR(Status)) {
Print(u"%a:%d -> %r\r\n", __FILE__, __LINE__, Status);
goto finish;
}
ByteHelper = (UINT8*)NewDevicePath;
// Clone the old path
gBS->CopyMem(ByteHelper, (VOID*)OriginalDevicePath, OriginalDevicePathLength);
ByteHelper += OriginalDevicePathLength;
// Insert the file node after the old path
NodeHelper = (EFI_DEVICE_PATH_PROTOCOL*)ByteHelper;
NodeHelper->Type = 0x04; // 0x04: Media
NodeHelper->SubType = 0x04; // 0x04: File
*((UINT16*)NodeHelper->Length) = (UINT16)(sizeof(EFI_DEVICE_PATH_PROTOCOL) + PathSize);
ByteHelper += sizeof(EFI_DEVICE_PATH_PROTOCOL);
gBS->CopyMem(ByteHelper, (VOID*)PathOnCurrentDevice, PathSize);
ByteHelper += PathSize;
// Add the end node
NodeHelper = (EFI_DEVICE_PATH_PROTOCOL*)ByteHelper;
NodeHelper->Type = 0x7F; // 0x7f: End
NodeHelper->SubType = 0xFF; // 0xFF: End entire device path
*((UINT16*)NodeHelper->Length) = sizeof(EFI_DEVICE_PATH_PROTOCOL);
// Load and start the image
Status = gBS->LoadImage(
FALSE,
gImageHandle,
NewDevicePath,
NULL,
0,
&ImageHandle);
if (EFI_ERROR(Status)) {
Print(u"%a:%d -> %r\r\n", __FILE__, __LINE__, Status);
goto finish;
}
Status = gBS->StartImage(
ImageHandle,
NULL,
NULL);
if (EFI_ERROR(Status)) {
Print(u"%a:%d -> %r\r\n", __FILE__, __LINE__, Status);
goto finish;
}
Status = EFI_SUCCESS;
goto finish;
finish:
if (NewDevicePath) gBS->FreePool(NewDevicePath);
return Status;
}