找回密码
 立即注册
首页 业界区 业界 【UEFI】HOB 从概念到代码

【UEFI】HOB 从概念到代码

申屠梓彤 2025-6-3 00:37:33
总述

使用 HOB 的原因是因为,在 PEI 阶段内存尚未完全初始化,到了 DXE 阶段才完整初始化了内存,所以无法通过简单的内存地址传递数据,并且我们仍然有一些对于内存空间存储的需求,因此发明了 HOB 机制。
HOBs may be relocated in system memory by the HOB consumer phase. HOBs must not contain pointers to other data in the HOB list, including that in other HOBs. The table must be able to be copied without requiring internal pointer adjustment.[1]
也就是需要注意 PEI 的 HOB 内存空间在 DXE 阶段会被DxeCore relocation,所以不要用 HOB 来存放内存地址。[2]
UEFI 提供了 HOB(Hand-Off-Block)机制,即在 PEI 阶段将数据打包成数据块存放在一段连续的内存中,数据块的标识为 GUID,DXE 阶段可以通过该 GUID 在 HOB 中找到对应数据块。内存中一系列的 HOB 组成了 HOB List。
如下图所示:
1.png

HOB的结构列表 HOB List HOB 是非常重要数据结构,在 CAR 时期将会初始化好,UEFI 的早期的堆栈都基于HOB,
PEI 阶段会创建大量的 HOB,包含板级数据(UBA), silicon Hob(Memmap, RC resource), Setup,当进入PEI PostMem phase后,CAR 里的 HOB 会 shadow 到 Mem, 当进入 DXE (也就是PeiMain 调用了TempPtr.DxeIpl->Entry) 后,这些 HOB 会被 DXE driver 大量采用, 用来创建 Protocol 或者其他初始化。
2.png

由 PEI 阶段进入 DXE 阶段,PeiCore 中传递 HOB 的代码 HOB 在 PEI 阶段会被修改(写),在 DXE 和 SMM 中会被使用,原则上不推荐SMM修改 HOB。创建的单个HOB Size最大为 64K
  1. ASSERT (DataLength <= (0xFFF8 - sizeof (EFI_HOB_GUID_TYPE)));
  2. // Make sure that data length is not too long.
复制代码
  1. #include <uefi.h>
  2. #include <Library/UefiLib.h>
  3. #include <Library/BaseLib.h>
  4. #include <Library/DebugLib.h>
  5. #include <Library/BaseMemoryLib.h>
  6. #include <Library/UefiDriverEntryPoint.h>
  7. #include <Library/PeimEntryPoint.h>
  8. #include <Library/PeiServicesLib.h>
  9. #include <Library/PeiServicesTablePointerLib.h>
  10. #include <Pi/PiHob.h>
  11. #include <Pi/PiPeiCis.h>
  12. EFI_GUID gEfiMyPeimHobGuid = {0x34ad055e, 0x1c21, 0x4b06, {0x80, 0x2f, 0xcd, 0x8a, 0x34, 0x2b, 0xa8, 0xc9}};
  13. // define a data struct for HOB Create
  14. typedef struct _MY_PEIM_HOB
  15. {
  16.   EFI_HOB_GUID_TYPE     EfiHobGuidType;
  17.   CHAR8 c0;
  18.   CHAR8 c1;
  19.   CHAR8 c2;
  20.   CHAR8 c3;
  21.   CHAR8 c4;
  22.   CHAR8 c5;
  23.   CHAR8 c6;
  24.   CHAR8 c7;
  25.   CHAR8 c8;
  26.   CHAR8 c9;
  27. } MY_PEIM_HOB;
  28. // PEI Module Entry Function
  29. EFI_STATUS
  30. EFIAPI
  31. MyPeimHobEntry(
  32.   IN       EFI_PEI_FILE_HANDLE  FileHandle,
  33.   IN CONST EFI_PEI_SERVICES     **PeiServices
  34. )
  35. {
  36.   EFI_STATUS Status;
  37.   DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] MyPeimHobEntry Start..\n"));
  38.   MY_PEIM_HOB *Hob = NULL;
  39.   // create HOB by PeiServices
  40.   Status = PeiServicesCreateHob (
  41.                            EFI_HOB_TYPE_GUID_EXTENSION,
  42.                            sizeof(MY_PEIM_HOB),
  43.                            &Hob);
  44.   // fill the HOB data [Guid + 'Hello HOB!']
  45.   if (!EFI_ERROR(Status))
  46.   {
  47.     Hob->EfiHobGuidType.Name = gEfiMyPeimHobGuid;
  48.     Hob->c0 = 'H';
  49.     Hob->c1 = 'e';
  50.     Hob->c2 = 'l';
  51.     Hob->c3 = 'l';
  52.     Hob->c4 = 'o';
  53.     Hob->c5 = ' ';
  54.     Hob->c6 = 'H';
  55.     Hob->c7 = 'O';
  56.     Hob->c8 = 'B';
  57.     Hob->c9 = '!';
  58.   }
  59.   DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] MyPeimHobEntry End..\n"));
  60.   return EFI_SUCCESS;
  61. }
复制代码

  • 在DXE阶段中,需要写一个DXE Driver,来获得之前创建好的HOB,并打印出来。
    4.png

  1. [Defines]
  2.   INF_VERSION = 0x00010006
  3.   BASE_NAME = MyPeimHob
  4.   FILE_GUID = e1223900-9e9a-4a7d-8835-ff4f54f5cff9
  5.   MODULE_TYPE = PEIM
  6.   VERSION_STRING = 1.0
  7.   ENTRY_POINT = MyPeimHobEntry
  8. [Sources]
  9.   MyPeimHob.c
  10. [Packages]
  11.   MdePkg/MdePkg.dec
  12.   ShellPkg/ShellPkg.dec
  13.   MdeModulePkg/MdeModulePkg.dec
  14. [LibraryClasses]
  15.   BaseLib
  16.   PeimEntryPoint
  17.   BaseMemoryLib
  18.   DebugLib
  19.   PeiServicesLib
  20. [depex]
  21.   TRUE
复制代码
  1. #include <uefi.h>
  2. #include <Library/UefiLib.h>
  3. #include <Library/BaseLib.h>
  4. #include <Library/DebugLib.h>
  5. #include <Library/BaseMemoryLib.h>
  6. #include <Library/UefiDriverEntryPoint.h>
  7. #include <Pi/PiBootMode.h>
  8. #include <Pi/PiHob.h>
  9. #include <Library/HobLib.h>
  10. #include <Library/UefiBootServicesTableLib.h>
  11. EFI_GUID gEfiMyPeimHobGuid = {0x34ad055e, 0x1c21, 0x4b06, {0x80, 0x2f, 0xcd, 0x8a, 0x34, 0x2b, 0xa8, 0xc9}};
  12. typedef struct _MY_PEIM_HOB
  13. {
  14.   EFI_HOB_GUID_TYPE     EfiHobGuidType;
  15.   CHAR8 c0;
  16.   CHAR8 c1;
  17.   CHAR8 c2;
  18.   CHAR8 c3;
  19.   CHAR8 c4;
  20.   CHAR8 c5;
  21.   CHAR8 c6;
  22.   CHAR8 c7;
  23.   CHAR8 c8;
  24.   CHAR8 c9;
  25. } MY_PEIM_HOB;
  26. EFI_STATUS
  27. EFIAPI
  28. MyDxeHobEntry(
  29.   IN EFI_HANDLE        ImageHandle,
  30.   IN EFI_SYSTEM_TABLE  *SystemTable
  31. )
  32. {
  33.   EFI_STATUS Status = EFI_SUCCESS;
  34.   DEBUG ((EFI_D_ERROR , "[MyHelloWorldHob] MyDxeHobEntry Start..\n"));
  35.   MY_PEIM_HOB *Hob;
  36.   // get the first Hob connected with [gEfiMyPeimHobGuid] by Guid.
  37.   Hob = GetFirstGuidHob (&gEfiMyPeimHobGuid);
  38.   // parse the hob and print debug info
  39.   if ( Hob != NULL)
  40.   {
  41.     DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] MyDxeHob Guid:%g\n", Hob->EfiHobGuidType.Name));
  42.     DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] %c \n", Hob->c0));
  43.     DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] %c \n", Hob->c1));
  44.     DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] %c \n", Hob->c2));
  45.     DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] %c \n", Hob->c3));
  46.     DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] %c \n", Hob->c4));
  47.     DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] %c \n", Hob->c5));
  48.     DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] %c \n", Hob->c6));
  49.     DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] %c \n", Hob->c7));
  50.     DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] %c \n", Hob->c8));
  51.     DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] %c \n", Hob->c9));
  52.   }
  53.   DEBUG ((EFI_D_ERROR , "[MyHelloWorldHob] MyDxeHobEntry End..\n"));
  54.   return Status;
  55. }
复制代码

  • 根据PEIM和DXE Driver的类型,填写OvmfPkgX64.fdf和OvmfPkgX64.dsc,来编译写好的两个模块,具体
    在 OvmfPkgX64.fdf
5.png

6.png

在OvmfPkgX64.dsc中
7.png

8.png

最后build一下,然后再在qemu目录下创建setup-qemu-x64.bat文件,里面内容
  1. [Defines]
  2.   INF_VERSION = 0x00010006
  3.   BASE_NAME = MyDxeHob
  4.   FILE_GUID = 0760c3a1-a98e-4d86-8102-160d49e29e51
  5.   MODULE_TYPE = UEFI_DRIVER
  6.   VERSION_STRING = 1.0
  7.   ENTRY_POINT = MyDxeHobEntry
  8. [Sources]
  9.   MyDxeHob.c
  10. [Packages]
  11.   MdePkg/MdePkg.dec
  12.   ShellPkg/ShellPkg.dec
  13.   MdeModulePkg/MdeModulePkg.dec
  14. [LibraryClasses]
  15.   UefiDriverEntryPoint
  16.   BaseLib
  17.   BaseMemoryLib
  18.   DebugLib
  19.   PrintLib
  20.   DevicePathLib
  21.   UefiBootServicesTableLib
  22.   MemoryAllocationLib
  23.   UefiLib
  24.   HobLib
  25. [depex]
  26.   TRUE
复制代码
其中...\ovmf\esp文件夹需要自己创建,或者改成自己想挂载的路径下。
在qemu目录下执行qemu>setup-qemu-x64.bat | findstr MyHelloWorldHob 查看结果
9.png

在这个例子里用GUID直接找HOB,实际开发中,会使用GetHobList()找到HOB头,然后循环调用GetNextHob()来找对应类型的HOB,可以参考EDK2中的原生代码,全局搜索GetNextHob()这个函数就能找到。篇幅原因,这里先Todo.......
后记:HOB 是如何跨阶段传递的?

1. 在 Cache-As-RAM 中创建

PEI 阶段在 DRAM 初始化之前运行,因此它使用 Cache-As-RAM(CAR) 作为临时内存。
HOB 就是在这个 CAR 中被创建的。
在早期 PEI 阶段,HOB 是保存在这个临时的栈空间里的。
2. 内存初始化之后,迁移 HOB

当 DRAM 初始化完成之后,PEI Core 会分配一块真实的内存区域作为 永久内存池(Permanent Memory)。
此时,PEI Core 会把已经存在于 CAR 中的 HOB 数据复制到新分配的 DRAM 区域。
在 PeiMain 中,PeiDispatcher() 会调用一个专门用于初始化永久内存的PEIM,这个PEIM根据你所编译的Pkg不同而不同。这个PEIM会进行内存初始化。
这一步叫做 HOB 的重定位(HOB Relocation)。
内核会更新 HOB list 的起始地址指针。
3. DXE 阶段读取 HOB

当转交控制权给 DXE Loader 时,PEI 会通过 EFI_PEI_HOB_POINTER 将 HOB list 的新地址传递过去。
DXE Core 通过这个地址获取 HOB 列表,从而继续处理。


  • UEFI Platform Initialization Specification ↩︎
  • UEFI 基础教程 (七) - HOB 简单使用 ↩︎

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
3.png
您需要登录后才可以回帖 登录 | 立即注册