【UEFI】HOB 從概念到代碼
總述
使用 HOB 的原因是因為,在 UEFI 啟動過程中,PEI 階段向 DXE 階段安全、結構化地傳遞初始化信息(比如內存布局、平臺配置、資源分布等),因此發明了 HOB 機制。
另外,在 PEI 階段內存已經完全初始化,到了 DXE 階段使用的是已完成初始化的內存,DXE 只是“使用”和“管理”內存。一旦內存初始化完成(PEI 中后期),完全可以通過地址傳遞。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。
如下圖所示:

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 或者其他初始化。

HOB 在 PEI 階段會被修改(寫),在 DXE 和 SMM 中會被使用,原則上不推薦SMM修改 HOB。創建的單個HOB Size最大為 64K
ASSERT (DataLength <= (0xFFF8 - sizeof (EFI_HOB_GUID_TYPE)));
// Make sure that data length is not too long.
需要大數據量的HOB需要注意。
HOB 在PEI到 DXE 傳送信息的過程遵循 one Producer to one Consumer 的模式,即在PEI階段,一個 PEIM 創建一個 HOB,在 DXE 階段,一個 DXE Driver 使用那個 HOB 并且把 HOB 相關的信息傳送給其他的需要這些信息的 DXE 組件。
因此,只有 PEI Phase 才允許增加或改動這些 HOBs,當 HOB List 被傳送給 DXE Phase 之后,他們就是只讀的(Read Only)。
一個只讀的 HOB List 的延伸就是 Handoff 信息,比如 Boot Mode,必須以別的方式來處理。比如,DXE Phase 想要產生一個 Recovery 條件,它不能 update Boot Mode,而是通過使用特殊方式的 reset call 來實現。
HOB list 是在 PEI Phase 被建立的,它存在于已經 present,initialized,tested 的Memory 中。一旦最初的 HOB List 被創建,物理內存就不能被 remapped, interleaved, 或者被后來的程序 moved。
第一次進入 PeiCore 的時候,還是 CAR 階段,內存沒有初始化,InitializeMemoryServices 中調用 PeiCoreBuildHobHandoffInfoTable 開啟 HOB 創建;位置在Car中;


網上會找到很多資料說:
如何增加一個新的HOB到HOB List中?
PEI Phase(HOB Producer Phase)肯定包含一個指向PHIT HOB(這是HOB List的開始)的指針,然后遵循以下的步驟:
1. 確定NewHobSize,即確定要創建的HOB的大?。ㄒ訠yte為單位)
2. 確定是否有足夠的空閑內存分配給新的HOB(NewHobSize <= (PHIT->EfiFreeMemoryTop - PHIT->EfiFreeMemoryBottom))
3. 在(PHIT->EfiFreeMemoryBottom)處構建HOB
4. 設置PHIT->EfiFreeMemoryBottom = PHIT->EfiFreeMemoryBottom + NewHobSize~~
但是在開發中,其實我們不必操作 PHIT 中的各個堆棧指針,已經做好的有很多輪子。
代碼
說了那么多,我們肯定是面向 API 編程,不用費勁心思去自己對HOB List中 PHIT的各種操作。
- 在PEI階段中,需要寫一個PEIM,來創建一個HOB。HOB不建議放內存地址,所以這里沒用字符數組。

#include <uefi.h>
#include <Library/UefiLib.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/UefiDriverEntryPoint.h>
#include <Library/PeimEntryPoint.h>
#include <Library/PeiServicesLib.h>
#include <Library/PeiServicesTablePointerLib.h>
#include <Pi/PiHob.h>
#include <Pi/PiPeiCis.h>
EFI_GUID gEfiMyPeimHobGuid = {0x34ad055e, 0x1c21, 0x4b06, {0x80, 0x2f, 0xcd, 0x8a, 0x34, 0x2b, 0xa8, 0xc9}};
// define a data struct for HOB Create
typedef struct _MY_PEIM_HOB
{
EFI_HOB_GUID_TYPE EfiHobGuidType;
CHAR8 c0;
CHAR8 c1;
CHAR8 c2;
CHAR8 c3;
CHAR8 c4;
CHAR8 c5;
CHAR8 c6;
CHAR8 c7;
CHAR8 c8;
CHAR8 c9;
} MY_PEIM_HOB;
// PEI Module Entry Function
EFI_STATUS
EFIAPI
MyPeimHobEntry(
IN EFI_PEI_FILE_HANDLE FileHandle,
IN CONST EFI_PEI_SERVICES **PeiServices
)
{
EFI_STATUS Status;
DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] MyPeimHobEntry Start..\n"));
MY_PEIM_HOB *Hob = NULL;
// create HOB by PeiServices
Status = PeiServicesCreateHob (
EFI_HOB_TYPE_GUID_EXTENSION,
sizeof(MY_PEIM_HOB),
&Hob);
// fill the HOB data [Guid + 'Hello HOB!']
if (!EFI_ERROR(Status))
{
Hob->EfiHobGuidType.Name = gEfiMyPeimHobGuid;
Hob->c0 = 'H';
Hob->c1 = 'e';
Hob->c2 = 'l';
Hob->c3 = 'l';
Hob->c4 = 'o';
Hob->c5 = ' ';
Hob->c6 = 'H';
Hob->c7 = 'O';
Hob->c8 = 'B';
Hob->c9 = '!';
}
DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] MyPeimHobEntry End..\n"));
return EFI_SUCCESS;
}
[Defines]
INF_VERSION = 0x00010006
BASE_NAME = MyPeimHob
FILE_GUID = e1223900-9e9a-4a7d-8835-ff4f54f5cff9
MODULE_TYPE = PEIM
VERSION_STRING = 1.0
ENTRY_POINT = MyPeimHobEntry
[Sources]
MyPeimHob.c
[Packages]
MdePkg/MdePkg.dec
ShellPkg/ShellPkg.dec
MdeModulePkg/MdeModulePkg.dec
[LibraryClasses]
BaseLib
PeimEntryPoint
BaseMemoryLib
DebugLib
PeiServicesLib
[depex]
TRUE
- 在DXE階段中,需要寫一個DXE Driver,來獲得之前創建好的HOB,并打印出來。
![image]()
#include <uefi.h>
#include <Library/UefiLib.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/UefiDriverEntryPoint.h>
#include <Pi/PiBootMode.h>
#include <Pi/PiHob.h>
#include <Library/HobLib.h>
#include <Library/UefiBootServicesTableLib.h>
EFI_GUID gEfiMyPeimHobGuid = {0x34ad055e, 0x1c21, 0x4b06, {0x80, 0x2f, 0xcd, 0x8a, 0x34, 0x2b, 0xa8, 0xc9}};
typedef struct _MY_PEIM_HOB
{
EFI_HOB_GUID_TYPE EfiHobGuidType;
CHAR8 c0;
CHAR8 c1;
CHAR8 c2;
CHAR8 c3;
CHAR8 c4;
CHAR8 c5;
CHAR8 c6;
CHAR8 c7;
CHAR8 c8;
CHAR8 c9;
} MY_PEIM_HOB;
EFI_STATUS
EFIAPI
MyDxeHobEntry(
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status = EFI_SUCCESS;
DEBUG ((EFI_D_ERROR , "[MyHelloWorldHob] MyDxeHobEntry Start..\n"));
MY_PEIM_HOB *Hob;
// get the first Hob connected with [gEfiMyPeimHobGuid] by Guid.
Hob = GetFirstGuidHob (&gEfiMyPeimHobGuid);
// parse the hob and print debug info
if ( Hob != NULL)
{
DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] MyDxeHob Guid:%g\n", Hob->EfiHobGuidType.Name));
DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] %c \n", Hob->c0));
DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] %c \n", Hob->c1));
DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] %c \n", Hob->c2));
DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] %c \n", Hob->c3));
DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] %c \n", Hob->c4));
DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] %c \n", Hob->c5));
DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] %c \n", Hob->c6));
DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] %c \n", Hob->c7));
DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] %c \n", Hob->c8));
DEBUG ((EFI_D_ERROR, "[MyHelloWorldHob] %c \n", Hob->c9));
}
DEBUG ((EFI_D_ERROR , "[MyHelloWorldHob] MyDxeHobEntry End..\n"));
return Status;
}
[Defines]
INF_VERSION = 0x00010006
BASE_NAME = MyDxeHob
FILE_GUID = 0760c3a1-a98e-4d86-8102-160d49e29e51
MODULE_TYPE = UEFI_DRIVER
VERSION_STRING = 1.0
ENTRY_POINT = MyDxeHobEntry
[Sources]
MyDxeHob.c
[Packages]
MdePkg/MdePkg.dec
ShellPkg/ShellPkg.dec
MdeModulePkg/MdeModulePkg.dec
[LibraryClasses]
UefiDriverEntryPoint
BaseLib
BaseMemoryLib
DebugLib
PrintLib
DevicePathLib
UefiBootServicesTableLib
MemoryAllocationLib
UefiLib
HobLib
[depex]
TRUE
- 根據PEIM和DXE Driver的類型,填寫OvmfPkgX64.fdf和OvmfPkgX64.dsc,來編譯寫好的兩個模塊,具體
在OvmfPkgX64.fdf


在OvmfPkgX64.dsc中


最后build一下,然后再在qemu目錄下創建setup-qemu-x64.bat文件,里面內容
"D:\Program Files\qemu\qemu-system-x86_64.exe" -bios "D:\edk2\edk2\Build\OvmfX64\DEBUG_VS2019\FV\OVMF.fd" -M "pc" -m 256 -cpu "qemu64" -boot order=dc -serial stdio -hda fat:rw:D:\edk2\ovmf\esp -net none
其中...\ovmf\esp文件夾需要自己創建,或者改成自己想掛載的路徑下。
在qemu目錄下執行qemu>setup-qemu-x64.bat | findstr MyHelloWorldHob 查看結果

在這個例子里用GUID直接找HOB,實際開發中,會使用GetHobList()找到HOB頭,然后循環調用GetNextHob()來找對應類型的HOB,可以參考EDK2中的原生代碼,全局搜索GetNextHob()這個函數就能找到。篇幅原因,這里先Todo.......
后記:HOB 是如何跨階段傳遞的?
1. 在 Cache-As-RAM 中創建
PEI 階段在 DRAM 初始化之前運行,因此它使用 Cache-As-RAM(CAR) 作為臨時內存。
HOB 就是在這個 CAR 中被創建的。
在早期 PEI 階段,HOB 是保存在這個臨時的??臻g里的。
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 列表,從而繼續處理。


浙公網安備 33010602011771號