UEFI解析全路径
UEFI Resolve full path
我正在使用 GNU-EFI 开发某种引导加载程序。到目前为止,我已经能够读取 Boot####
NVRAM 变量,所以我有一个半填充的 FilePathList[]
,它看起来像这样(打印有 DevicePathToStr
):
HD(Part2, SigCD0400E6-54F3-49F4-81F2-65B21E8278A8)/\EFI\Microsoft\Boot\bootmgfw.efi
当传递给 LoadImage
时,它失败并返回 EFI_NOT_FOUND
。据我了解(UEFI Doc Section 3.1.2),我需要在已有的路径之前添加完整路径。我发现正确的路径是 PciRoot(0x0)/Pci(0x1, 0x1)/Ata(0x0)
,但我不确定如何根据我所拥有的以编程方式找到此路径,以便可以将其添加到前面。
我目前的代码如下,请原谅我的低质量,到目前为止我一直在努力让一些东西工作。
EFI_STATUS status;
EFI_GUID vendor = EFI_GLOBAL_VARIABLE;
UINT32 Attr;
UINTN size = 256;
UINT16 *buf = AllocateZeroPool(size);
if (buf == NULL)
Print(L"Failed to allocate buffer\n");
status = uefi_call_wrapper(RT->GetVariable, 5,
L"BootOrder", /*VariableName*/
&vendor, /*VendorGuid*/
&Attr, /*Attributes*/
&size, /*DataSize*/
buf /*Data*/
);
if (status != EFI_SUCCESS)
Print(L"Failed to read BootOrder (%d)\n", status);
// should contain an int for the correct boot option
UINT16 bootopt = buf[0];
FreePool(buf);
CHAR16 *name = AllocateZeroPool(18); // Bootxxxx[=10=] unicode
SPrint(name, 18, L"Boot%04x", bootopt);
Print(L"Next boot: %s\n", name);
size = 0;
do {
buf = AllocateZeroPool(size);
if (buf == NULL)
Print(L"Failed to allocate buffer\n");
status = uefi_call_wrapper(RT->GetVariable, 5,
name,
&vendor,
&Attr,
&size,
buf
);
if (status == EFI_SUCCESS) break;
FreePool(buf);
// if it fails, size is set to what it needs to be
// handy that
} while(status == EFI_BUFFER_TOO_SMALL);
if (!(buf[0]&LOAD_OPTION_ACTIVE)) Print(L"BootOption not active\n");
Print(L"%s: 0x%r\n\n", name, buf);
UINT8 *OrigFilePathList = ((UINT8*)buf) + (sizeof(UINT32) + sizeof(UINT16) + StrSize(buf+3));
UINT16 *FilePathListLength = ((UINT16*)OrigFilePathList)+2;
Print(L"&OrigFilePathList = 0x%r\n", OrigFilePathList);
Print(L"sizeof(_EFI_LOAD_OPTION) = %d\n", size);
Print(L"struct _EFI_LOAD_OPTION {\n");
Print(L" Attributes = %d\n", *(UINT32 *)(buf));
Print(L" FilePathListLength = %d,\n", *FilePathListLength);
Print(L" Description = %s,\n", buf+3);
Print(L" FilePathList[] = {\n");
UINT16 totallength = 0;
UINT8 *FilePathList = OrigFilePathList;
for (UINT8 i = 0; i < *FilePathListLength+1; i++) {
Print(L" &FilePathList[%d] = 0x%r\n", i, OrigFilePathList);
Print(L" FilePathList[%d].Type = %d ", i, *OrigFilePathList);
switch (*OrigFilePathList) {
case 0x01:
Print(L"(Hardware Device Path)\n");
break;
case 0x02:
Print(L"(ACPI Device Path)\n");
break;
case 0x03:
Print(L"(Messaging Device Path)\n");
break;
case 0x04:
Print(L"(Media Device Path)\n");
break;
case 0x05:
Print(L"(BIOS Boot Specification Device Path)\n");
break;
case 0x7f:
Print(L"(End Of Hardware Device Path)\n");
break;
default:
Print(L"(Unknown Device Path)\n");
break;
}
Print(L" FilePathList[%d].SubType = %d\n", i, *(OrigFilePathList+1));
Print(L" FilePathList[%d].Length = %d\n", i, *(UINT16*)(OrigFilePathList+2));
totallength += *(UINT16*)(OrigFilePathList+2);
OrigFilePathList += *(UINT16*)(OrigFilePathList+2);
}
Print(L" }\n");
Print(L" &OptionalData = 0x%r\n", OrigFilePathList);
Print(L" OptionalDataLength = %d\n", size-totallength);
Print(L"}\n");
// The hard drive device path can be appended to the matching hardware
// device path and normal boot behavior can then be used.
// We need to locate the Type 1 FilePathList and prepend it to what we've already got
// Need to prefix PciRoot(0x0)/Pci(0x1, 0x1)/Ata(0x0)
// but automatically find it
// in theory we should be able to use the list of handles to devices that support SIMPLE_FILE_SYSTEM_PROTOCOL
// to find the right device
Print(L"%s\n", DevicePathToStr((EFI_DEVICE_PATH *)FilePathList));
/* EFI_STATUS (EFIAPI *EFI_IMAGE_LOAD) (
IN BOOLEAN BootPolicy,
IN EFI_HANDLE ParentImageHandle,
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
IN VOID *SourceBuffer OPTIONAL,
IN UINTN SourceSize,
OUT EFI_HANDLE *ImageHandle
); */
EFI_HANDLE *NextHandle = AllocateZeroPool(sizeof(EFI_HANDLE));
status = uefi_call_wrapper(BS->LoadImage, 6,
/* status = BS->LoadImage( */
TRUE, /* BootPolicy */
ImageHandle, /* ParentImageHandle */
(EFI_DEVICE_PATH *)FilePathList, /* DevicePath */
NULL, /* SourceBuffer */
0, /* SourceSize */
NextHandle /* ImageHandle */
);
if (status != EFI_SUCCESS)
Print(L"Failed to LoadImage (%d)\n", status);
else
Print(L"LoadImage OK\n");
我需要哪些功能和流程才能完全限定 FilePathList
以便它可以与 LoadImage
一起使用?
请相信,当我寻求帮助时,我会解决问题。
大体的思路是用LocateHandleBuffer
找到SIMPLE_FILE_SYSTEM_PROTOCOL
的所有句柄。使用这些句柄,将路径(使用 DevicePathFromHandle
)与我们已有的路径进行比较以找到合适的设备。 LoadImage
现在适合我了。
下面的示例代码(buf
是来自 GetVariable
的 Boot####
变量的值):
Print(L"Description = %s\n", (CHAR16*)buf + 3);
EFI_DEVICE_PATH *BootX = (EFI_DEVICE_PATH*) (((UINT8*)buf) + (sizeof(UINT32) + sizeof(UINT16) + StrSize(buf+3)));
UINTN NoHandles = 0;
EFI_HANDLE *handles = NULL;
EFI_GUID SimpleFileSystemGUID = SIMPLE_FILE_SYSTEM_PROTOCOL;
status = uefi_call_wrapper(BS->LocateHandleBuffer,
5,
ByProtocol,
&SimpleFileSystemGUID,
NULL,
&NoHandles,
&handles
);
if (status != EFI_SUCCESS)
Print(L"Failed to LocateHandleBuffer (%d)\n", status);
else
Print(L"LocateHandleBuffer OK (%d handles)\n", NoHandles);
EFI_DEVICE_PATH *prefix;
UINTN index;
for (index = 0; index < NoHandles; index++) {
prefix = DevicePathFromHandle(handles[index]);
while(!IsDevicePathEnd(NextDevicePathNode(prefix))) prefix = NextDevicePathNode(prefix);
if(LibMatchDevicePaths(prefix, BootX)) {
break;
} else {
FreePool(prefix);
}
}
prefix = DevicePathFromHandle(handles[index]);
// prefix ends with the same node that BootX starts with
// so skip forward BootX so we can prepend prefix
BootX = NextDevicePathNode(BootX);
EFI_DEVICE_PATH *fullpath = AppendDevicePath(prefix, BootX);
Print(L"Booting: %s\n", DevicePathToStr(fullpath));
/* EFI_STATUS (EFIAPI *EFI_IMAGE_LOAD) (
IN BOOLEAN BootPolicy,
IN EFI_HANDLE ParentImageHandle,
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
IN VOID *SourceBuffer OPTIONAL,
IN UINTN SourceSize,
OUT EFI_HANDLE *ImageHandle
); */
EFI_HANDLE *NextHandle = AllocateZeroPool(sizeof(EFI_HANDLE));
status = uefi_call_wrapper(BS->LoadImage, 6,
/* status = BS->LoadImage( */
TRUE, /* BootPolicy */
ImageHandle, /* ParentImageHandle */
fullpath, /* DevicePath */
NULL, /* SourceBuffer */
0, /* SourceSize */
NextHandle /* ImageHandle */
);
if (status != EFI_SUCCESS)
Print(L"Failed to LoadImage (%d)\n", status);
else
Print(L"LoadImage OK\n");
我正在使用 GNU-EFI 开发某种引导加载程序。到目前为止,我已经能够读取 Boot####
NVRAM 变量,所以我有一个半填充的 FilePathList[]
,它看起来像这样(打印有 DevicePathToStr
):
HD(Part2, SigCD0400E6-54F3-49F4-81F2-65B21E8278A8)/\EFI\Microsoft\Boot\bootmgfw.efi
当传递给 LoadImage
时,它失败并返回 EFI_NOT_FOUND
。据我了解(UEFI Doc Section 3.1.2),我需要在已有的路径之前添加完整路径。我发现正确的路径是 PciRoot(0x0)/Pci(0x1, 0x1)/Ata(0x0)
,但我不确定如何根据我所拥有的以编程方式找到此路径,以便可以将其添加到前面。
我目前的代码如下,请原谅我的低质量,到目前为止我一直在努力让一些东西工作。
EFI_STATUS status;
EFI_GUID vendor = EFI_GLOBAL_VARIABLE;
UINT32 Attr;
UINTN size = 256;
UINT16 *buf = AllocateZeroPool(size);
if (buf == NULL)
Print(L"Failed to allocate buffer\n");
status = uefi_call_wrapper(RT->GetVariable, 5,
L"BootOrder", /*VariableName*/
&vendor, /*VendorGuid*/
&Attr, /*Attributes*/
&size, /*DataSize*/
buf /*Data*/
);
if (status != EFI_SUCCESS)
Print(L"Failed to read BootOrder (%d)\n", status);
// should contain an int for the correct boot option
UINT16 bootopt = buf[0];
FreePool(buf);
CHAR16 *name = AllocateZeroPool(18); // Bootxxxx[=10=] unicode
SPrint(name, 18, L"Boot%04x", bootopt);
Print(L"Next boot: %s\n", name);
size = 0;
do {
buf = AllocateZeroPool(size);
if (buf == NULL)
Print(L"Failed to allocate buffer\n");
status = uefi_call_wrapper(RT->GetVariable, 5,
name,
&vendor,
&Attr,
&size,
buf
);
if (status == EFI_SUCCESS) break;
FreePool(buf);
// if it fails, size is set to what it needs to be
// handy that
} while(status == EFI_BUFFER_TOO_SMALL);
if (!(buf[0]&LOAD_OPTION_ACTIVE)) Print(L"BootOption not active\n");
Print(L"%s: 0x%r\n\n", name, buf);
UINT8 *OrigFilePathList = ((UINT8*)buf) + (sizeof(UINT32) + sizeof(UINT16) + StrSize(buf+3));
UINT16 *FilePathListLength = ((UINT16*)OrigFilePathList)+2;
Print(L"&OrigFilePathList = 0x%r\n", OrigFilePathList);
Print(L"sizeof(_EFI_LOAD_OPTION) = %d\n", size);
Print(L"struct _EFI_LOAD_OPTION {\n");
Print(L" Attributes = %d\n", *(UINT32 *)(buf));
Print(L" FilePathListLength = %d,\n", *FilePathListLength);
Print(L" Description = %s,\n", buf+3);
Print(L" FilePathList[] = {\n");
UINT16 totallength = 0;
UINT8 *FilePathList = OrigFilePathList;
for (UINT8 i = 0; i < *FilePathListLength+1; i++) {
Print(L" &FilePathList[%d] = 0x%r\n", i, OrigFilePathList);
Print(L" FilePathList[%d].Type = %d ", i, *OrigFilePathList);
switch (*OrigFilePathList) {
case 0x01:
Print(L"(Hardware Device Path)\n");
break;
case 0x02:
Print(L"(ACPI Device Path)\n");
break;
case 0x03:
Print(L"(Messaging Device Path)\n");
break;
case 0x04:
Print(L"(Media Device Path)\n");
break;
case 0x05:
Print(L"(BIOS Boot Specification Device Path)\n");
break;
case 0x7f:
Print(L"(End Of Hardware Device Path)\n");
break;
default:
Print(L"(Unknown Device Path)\n");
break;
}
Print(L" FilePathList[%d].SubType = %d\n", i, *(OrigFilePathList+1));
Print(L" FilePathList[%d].Length = %d\n", i, *(UINT16*)(OrigFilePathList+2));
totallength += *(UINT16*)(OrigFilePathList+2);
OrigFilePathList += *(UINT16*)(OrigFilePathList+2);
}
Print(L" }\n");
Print(L" &OptionalData = 0x%r\n", OrigFilePathList);
Print(L" OptionalDataLength = %d\n", size-totallength);
Print(L"}\n");
// The hard drive device path can be appended to the matching hardware
// device path and normal boot behavior can then be used.
// We need to locate the Type 1 FilePathList and prepend it to what we've already got
// Need to prefix PciRoot(0x0)/Pci(0x1, 0x1)/Ata(0x0)
// but automatically find it
// in theory we should be able to use the list of handles to devices that support SIMPLE_FILE_SYSTEM_PROTOCOL
// to find the right device
Print(L"%s\n", DevicePathToStr((EFI_DEVICE_PATH *)FilePathList));
/* EFI_STATUS (EFIAPI *EFI_IMAGE_LOAD) (
IN BOOLEAN BootPolicy,
IN EFI_HANDLE ParentImageHandle,
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
IN VOID *SourceBuffer OPTIONAL,
IN UINTN SourceSize,
OUT EFI_HANDLE *ImageHandle
); */
EFI_HANDLE *NextHandle = AllocateZeroPool(sizeof(EFI_HANDLE));
status = uefi_call_wrapper(BS->LoadImage, 6,
/* status = BS->LoadImage( */
TRUE, /* BootPolicy */
ImageHandle, /* ParentImageHandle */
(EFI_DEVICE_PATH *)FilePathList, /* DevicePath */
NULL, /* SourceBuffer */
0, /* SourceSize */
NextHandle /* ImageHandle */
);
if (status != EFI_SUCCESS)
Print(L"Failed to LoadImage (%d)\n", status);
else
Print(L"LoadImage OK\n");
我需要哪些功能和流程才能完全限定 FilePathList
以便它可以与 LoadImage
一起使用?
请相信,当我寻求帮助时,我会解决问题。
大体的思路是用LocateHandleBuffer
找到SIMPLE_FILE_SYSTEM_PROTOCOL
的所有句柄。使用这些句柄,将路径(使用 DevicePathFromHandle
)与我们已有的路径进行比较以找到合适的设备。 LoadImage
现在适合我了。
下面的示例代码(buf
是来自 GetVariable
的 Boot####
变量的值):
Print(L"Description = %s\n", (CHAR16*)buf + 3);
EFI_DEVICE_PATH *BootX = (EFI_DEVICE_PATH*) (((UINT8*)buf) + (sizeof(UINT32) + sizeof(UINT16) + StrSize(buf+3)));
UINTN NoHandles = 0;
EFI_HANDLE *handles = NULL;
EFI_GUID SimpleFileSystemGUID = SIMPLE_FILE_SYSTEM_PROTOCOL;
status = uefi_call_wrapper(BS->LocateHandleBuffer,
5,
ByProtocol,
&SimpleFileSystemGUID,
NULL,
&NoHandles,
&handles
);
if (status != EFI_SUCCESS)
Print(L"Failed to LocateHandleBuffer (%d)\n", status);
else
Print(L"LocateHandleBuffer OK (%d handles)\n", NoHandles);
EFI_DEVICE_PATH *prefix;
UINTN index;
for (index = 0; index < NoHandles; index++) {
prefix = DevicePathFromHandle(handles[index]);
while(!IsDevicePathEnd(NextDevicePathNode(prefix))) prefix = NextDevicePathNode(prefix);
if(LibMatchDevicePaths(prefix, BootX)) {
break;
} else {
FreePool(prefix);
}
}
prefix = DevicePathFromHandle(handles[index]);
// prefix ends with the same node that BootX starts with
// so skip forward BootX so we can prepend prefix
BootX = NextDevicePathNode(BootX);
EFI_DEVICE_PATH *fullpath = AppendDevicePath(prefix, BootX);
Print(L"Booting: %s\n", DevicePathToStr(fullpath));
/* EFI_STATUS (EFIAPI *EFI_IMAGE_LOAD) (
IN BOOLEAN BootPolicy,
IN EFI_HANDLE ParentImageHandle,
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
IN VOID *SourceBuffer OPTIONAL,
IN UINTN SourceSize,
OUT EFI_HANDLE *ImageHandle
); */
EFI_HANDLE *NextHandle = AllocateZeroPool(sizeof(EFI_HANDLE));
status = uefi_call_wrapper(BS->LoadImage, 6,
/* status = BS->LoadImage( */
TRUE, /* BootPolicy */
ImageHandle, /* ParentImageHandle */
fullpath, /* DevicePath */
NULL, /* SourceBuffer */
0, /* SourceSize */
NextHandle /* ImageHandle */
);
if (status != EFI_SUCCESS)
Print(L"Failed to LoadImage (%d)\n", status);
else
Print(L"LoadImage OK\n");