【UEFI】HOB 从概念到代码
总述使用 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.
也就是需要注意 PEI 的 HOB 内存空间在 DXE 阶段会被DxeCore relocation,所以不要用 HOB 来存放内存地址。
UEFI 提供了 HOB(Hand-Off-Block)机制,即在 PEI 阶段将数据打包成数据块存放在一段连续的内存中,数据块的标识为 GUID,DXE 阶段可以通过该 GUID 在 HOB 中找到对应数据块。内存中一系列的 HOB 组成了 HOB List。
如下图所示:
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 或者其他初始化。
由 PEI 阶段进入 DXE 阶段,PeiCore 中传递 HOB 的代码 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.#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_HANDLEFileHandle,
IN CONST EFI_PEI_SERVICES **PeiServices
)
{
EFI_STATUS Status;
DEBUG ((EFI_D_ERROR, " 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
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, " MyPeimHobEntry End..\n"));
return EFI_SUCCESS;
}
[*]在DXE阶段中,需要写一个DXE Driver,来获得之前创建好的HOB,并打印出来。
INF_VERSION = 0x00010006
BASE_NAME = MyPeimHob
FILE_GUID = e1223900-9e9a-4a7d-8835-ff4f54f5cff9
MODULE_TYPE = PEIM
VERSION_STRING = 1.0
ENTRY_POINT = MyPeimHobEntry
MyPeimHob.c
MdePkg/MdePkg.dec
ShellPkg/ShellPkg.dec
MdeModulePkg/MdeModulePkg.dec
BaseLib
PeimEntryPoint
BaseMemoryLib
DebugLib
PeiServicesLib
TRUE#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 , " MyDxeHobEntry Start..\n"));
MY_PEIM_HOB *Hob;
// get the first Hob connected with by Guid.
Hob = GetFirstGuidHob (&gEfiMyPeimHobGuid);
// parse the hob and print debug info
if ( Hob != NULL)
{
DEBUG ((EFI_D_ERROR, " MyDxeHob Guid:%g\n", Hob->EfiHobGuidType.Name));
DEBUG ((EFI_D_ERROR, " %c \n", Hob->c0));
DEBUG ((EFI_D_ERROR, " %c \n", Hob->c1));
DEBUG ((EFI_D_ERROR, " %c \n", Hob->c2));
DEBUG ((EFI_D_ERROR, " %c \n", Hob->c3));
DEBUG ((EFI_D_ERROR, " %c \n", Hob->c4));
DEBUG ((EFI_D_ERROR, " %c \n", Hob->c5));
DEBUG ((EFI_D_ERROR, " %c \n", Hob->c6));
DEBUG ((EFI_D_ERROR, " %c \n", Hob->c7));
DEBUG ((EFI_D_ERROR, " %c \n", Hob->c8));
DEBUG ((EFI_D_ERROR, " %c \n", Hob->c9));
}
DEBUG ((EFI_D_ERROR , " MyDxeHobEntry End..\n"));
return Status;
}
[*]根据PEIM和DXE Driver的类型,填写OvmfPkgX64.fdf和OvmfPkgX64.dsc,来编译写好的两个模块,具体
在 OvmfPkgX64.fdf
在OvmfPkgX64.dsc中
最后build一下,然后再在qemu目录下创建setup-qemu-x64.bat文件,里面内容
INF_VERSION = 0x00010006
BASE_NAME = MyDxeHob
FILE_GUID = 0760c3a1-a98e-4d86-8102-160d49e29e51
MODULE_TYPE = UEFI_DRIVER
VERSION_STRING = 1.0
ENTRY_POINT = MyDxeHobEntry
MyDxeHob.c
MdePkg/MdePkg.dec
ShellPkg/ShellPkg.dec
MdeModulePkg/MdeModulePkg.dec
UefiDriverEntryPoint
BaseLib
BaseMemoryLib
DebugLib
PrintLib
DevicePathLib
UefiBootServicesTableLib
MemoryAllocationLib
UefiLib
HobLib
TRUE其中...\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 是保存在这个临时的栈空间里的。
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 简单使用 ↩︎
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页:
[1]