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 是来自 GetVariableBoot#### 变量的值):

   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");