HOB
HOB (Hand-Off Blocks )传输信息的载体。相比于其他 Phase 之间的联系,Pei到DXE之间联系比较薄弱,PEI一 些初始化硬件、内存的数据等,DXE需要知道,HOB便作为桥梁应运而生。分为两个阶段:
1、HOB producer phase (HOB 生产者阶段) 是创建 HOB 和 HOB 列表的预引导阶段。(于 PEI phase)
2、HOB consumer phase (HOB 消费者阶段)是预引导阶段,HOB列表传递到该阶段然后被消费。(于 PEI & DXE phase) HOB在PEI到DXE传送信息的过程遵循one Producer to one Consumer 的模式,即在PEI阶段,一个PEIM创建一个 HOB,在DXE阶段,一个DXE Driver使用那个HOB并且把HOB相关的信息传送给其他的需要这些信息的DXE组件。
HOB LIST
HOB 列表最初由 HOB 生产者阶段构建。 HOB 列表是在存在、初始化和测试的内存中创建的。
HOB list 是在 PEI Phase 被建立的,它存在于已经present, initialized,tested 的 Memory 中。一旦最初的 HOB List 被创 建,物理内存就不能被remapped, interleaved, 或者被后来的程序moved。
1、在PEI Phase 中,使用的数据存储区就叫做HOBs,PEI 可以使用HOB向DXE传递信息。 2、在PEI Phase 中,HOBs是可读可写的,在DXE Phase 中,只能读。 3、HOB是系列的连续的内存结构体 (所有的Hob放在一段连续的内存里面),可以认为其由三部分构成: 第一部分,是 Phase Handoff Information Table (PHIT) 头, 它描述了HOB的起始地址、总的内存使用以及期间发现的 启动模式;
第二部分是各个Hob列表,DXE阶段会根据这一部分获取相关资源;
第三部分是结束部分。
这些在内存中的HOBs的连续的表被称为HOB List。
HOB 生产者阶段内存映射和使用:
HOB TYPE
Hoblist 存储在PrivateData ,在 PEI 末阶段传入DXE。所有HOB类型如下:
// Union of all the possible HOB Types.
typedef union {
EFI_HOB_GENERIC_HEADER *Header;
EFI_HOB_HANDOFF_INFO_TABLE *HandoffInformationTable;
EFI_HOB_MEMORY_ALLOCATION *MemoryAllocation;
EFI_HOB_MEMORY_ALLOCATION_BSP_STORE *MemoryAllocationBspStore;
EFI_HOB_MEMORY_ALLOCATION_STACK *MemoryAllocationStack;
EFI_HOB_MEMORY_ALLOCATION_MODULE *MemoryAllocationModule;
EFI_HOB_RESOURCE_DESCRIPTOR *ResourceDescriptor;
EFI_HOB_GUID_TYPE *Guid;
EFI_HOB_FIRMWARE_VOLUME *FirmwareVolume;
EFI_HOB_FIRMWARE_VOLUME2 *FirmwareVolume2;
EFI_HOB_FIRMWARE_VOLUME3 *FirmwareVolume3;
EFI_HOB_CPU *Cpu;
EFI_HOB_MEMORY_POOL *Pool;
EFI_HOB_UEFI_CAPSULE *Capsule;
UINT8 *Raw;
} EFI_PEI_HOB_POINTERS;
其中 EFI_HOB_GUID_TYPE 允许PEIM将私有数据传递 给DXE驱动程序 (自定义HOB时候会用到的类型,通常用于自己写一些HOB信息。
HOB 实现
HOB相关的主要数据结构
// HobType of EFI_HOB_GENERIC_HEADER.
#define EFI_HOB_TYPE_HANDOFF 0x0001
#define EFI_HOB_TYPE_MEMORY_ALLOCATION 0x0002
#define EFI_HOB_TYPE_RESOURCE_DESCRIPTOR 0x0003
#define EFI_HOB_TYPE_GUID_EXTENSION 0x0004
#define EFI_HOB_TYPE_FV 0x0005
#define EFI_HOB_TYPE_CPU 0x0006
#define EFI_HOB_TYPE_MEMORY_POOL 0x0007
#define EFI_HOB_TYPE_FV2 0x0009
#define EFI_HOB_TYPE_LOAD_PEIM_UNUSED 0x000A
#define EFI_HOB_TYPE_UEFI_CAPSULE 0x000B
#define EFI_HOB_TYPE_FV3 0x000C
#define EFI_HOB_TYPE_UNUSED 0xFFFE
#define EFI_HOB_TYPE_END_OF_HOB_LIST 0xFFFF
// 描述 HOB 内数据的格式和大小。 所有 HOB 都必须包含此通用 HOB 标头。
typedef struct {
// 标识 HOB 数据结构类型。
UINT16 HobType;
// HOB 的字节长度。
UINT16 HobLength; // 该字段必须始终设置为零
UINT32 Reserved;
} EFI_HOB_GENERIC_HEADER;
// EFI_HOB_HANDOFF_INFO_TABLE 中的版本值。
#define EFI_HOB_HANDOFF_TABLE_VERSION 0x0009
// 包含 HOB 生产者阶段使用的一般状态信息。
// 此 HOB 必须是 HOB 列表中的第一个。
typedef struct {
/// HOB 通用标头。 Header.HobType = EFI_HOB_TYPE_HANDOFF. EFI_HOB_GENERIC_HEADER Header;
// 与 PHIT HOB 定义有关的版本号。
// 这个值是四个字节的长度,当它与 4 字节的 BootMode 组合 时提供一个 8 字节对齐的条目。
UINT32 Version;
// 在 HOB 生产者阶段确定的系统引导模式。
EFI_BOOT_MODE BootMode;
// 分配给 HOB 生产者阶段使用的内存的最高地址位置。
//此地址必须对齐 4 KB 以满足 UEFI 的页面限制。
EFI_PHYSICAL_ADDRESS EfiMemoryTop;
// 分配给 HOB 生产者阶段使用的内存的最低地址位置。
EFI_PHYSICAL_ADDRESS EfiMemoryBottom;
// 当前可供 HOB 生产者阶段使用的空闲内存的最高地址位置。
EFI_PHYSICAL_ADDRESS EfiFreeMemoryTop;
// 可供 HOB 生产者阶段使用的空闲内存的最低地址位置。
EFI_PHYSICAL_ADDRESS EfiFreeMemoryBottom;
// HOB 列表的结尾。
EFI_PHYSICAL_ADDRESS EfiEndOfHobList;
} EFI_HOB_HANDOFF_INFO_TABLE;
PHIT 初始化
PeiCore -> InitializeMemoryServices -> PeiCoreBuildHobHandoffInfoTable
构造HOB 必须遵循以下的规则:
1、每个HOB必须以一个HOB generic header开头 (EFI_HOB_GENERIC_HEADER)。此要求允许用户在跳 过其余部分时找到他们感兴趣的 HOB。
2、HOBs可以包含boot services data,在 DXE Phase 结束之前,PEI和DXE都可以调用。
3、HOBs可以被DXE重新安置在系统内存上,每个HOB 都不能包含指向HOB List中其他数据的指针,也不能指向 其他的HOB,这个Table必须可以被Copied而不需要任何 内部指针的调整。
4、所有的HOB在长度上必须是8 bytes的倍数。此要求符 合 Itanium® 处理器系列的对齐限制。
5、PHIT HOB 必须总是在8 byte处开始。由于此要求和此列表中的要求 #4,所有 HOB 都将从 8 字节边界开始。
6、增加的HOB总是被加到HOB List的最后,而且只能在 PEI Phase(HOB Producer Phase)增加,DXE Phase(HOB Consumer Phase)不能。
7、HOBs不能被删除。每个HOB的generic header中都会 描述这个HOB的长度,这样下一个HOB就很容易被找到。
如下:
edk2\MdeModulePkg\Core\Pei\Hob\Hob.c
/**
构建切换信息表 HOB
@param BootMode - 当前引导模式
@param MemoryBegin - 起始内存地址。
@param MemoryLength - 内存长度。
@return EFI_SUCCESS 总是成功初始化 HOB。
**/
EFI_STATUS
PeiCoreBuildHobHandoffInfoTable (
IN EFI_BOOT_MODE BootMode BootMode,
IN EFI_PHYSICAL_ADDRESS MemoryBegin,
IN UINT64 MemoryLength
)
{
EFI_HOB_HANDOFF_INFO_TABLE *Hob;
EFI_HOB_GENERIC_HEADER *HobEnd;
Hob = (VOID *)(UINTN)MemoryBegin;
HobEnd = (EFI_HOB_GENERIC_HEADER*) (Hob+1);
Hob->Header.HobType = EFI_HOB_TYPE_HANDOFF;
Hob->Header.HobLength = (UINT16) sizeof (EFI_HOB_HANDOFF_INFO_TABLE);
Hob->Header.Reserved = 0;
HobEnd->HobType = EFI_HOB_TYPE_END_OF_HOB_LIST;
HobEnd->HobLength = (UINT16) sizeof (EFI_HOB_GENERIC_HEADER);
HobEnd->Reserved = 0;
Hob->Version = EFI_HOB_HANDOFF_TABLE_VERSION;
Hob->BootMode = BootMode;
Hob->EfiMemoryTop = MemoryBegin + MemoryLength;
Hob->EfiMemoryBottom = MemoryBegin;
Hob->EfiFreeMemoryTop = MemoryBegin + MemoryLength;
Hob->EfiFreeMemoryBottom = (EFI_PHYSICAL_ADDRESS) (UINTN) (HobEnd + 1);
Hob->EfiEndOfHobList = (EFI_PHYSICAL_ADDRESS) (UINTN) HobEnd;
return EFI_SUCCESS;
}
增加一个新的HOB到HOB List中
PEI Phase(HOB Producer Phase)肯定包含一个指向 PHIT HOB(这是HOB List的开始)的指针,然后遵循以下的步骤:
1、确定NewHobSize,即确定要创建的HOB的大小(以Byte为单位)。
2、确定是否有足够的空闲内存分配给新的HOB (NewHobSize <= (PHIT->EfiFreeMemoryTop - PHIT->EfiFreeMemoryBottom))。
3、在(PHIT->EfiFreeMemoryBottom)处构建 HOB。
4、设置PHIT->EfiFreeMemoryBottom = PHIT->EfiFreeMemoryBottom + NewHobSize 。
/**
将新的 HOB 添加到 HOB 列表。
@param PeiServices 指向 PEI 基金会发布的 EFI_PEI_SERVICES 表的间接指针。
@param Type 新 HOB 的类型。
@param Length 要分配的新 HOB 的长度。
@param Hob 指向新 HOB 的指针。
@return EFI_SUCCESS 成功创建 HOB。
@retval EFI_INVALID_PARAMETER 如果 Hob 为 NULL
@retval EFI_NOT_AVAILABLE_YET 如果 HobList 仍然不可用。
@retval EFI_OUT_OF_RESOURCES 如果没有更多内存来增长 Hoblist。
**/
EFI_STATUS
EFIAPI
PeiCreateHob (
IN CONST EFI_PEI_SERVICES **PeiServices,
IN UINT16 Type,
IN UINT16 Length,
IN OUT VOID **Hob
)
{
EFI_STATUS Status;
EFI_HOB_HANDOFF_INFO_TABLE *HandOffHob;
EFI_HOB_GENERIC_HEADER *HobEnd;
EFI_PHYSICAL_ADDRESS FreeMemory;
Status = PeiGetHobList (PeiServices, Hob);
if (EFI_ERROR(Status)) {
return Status;
}
HandOffHob = *Hob;
//
// Check Length to avoid data overflow.
//
if (0x10000 - Length <= 0x7) {
return EFI_INVALID_PARAMETER;
}
Length = (UINT16)((Length + 0x7) & (~0x7));
FreeMemory = HandOffHob->EfiFreeMemoryTop - HandOffHob->EfiFreeMemoryBottom;
if (FreeMemory < Length) {
DEBUG ((EFI_D_ERROR, "PeiCreateHob fail: Length - 0x%08x\n", (UINTN)Length));
DEBUG ((EFI_D_ERROR, " FreeMemoryTop (UINTN)HandOffHob->EfiFreeMemoryTop)); - 0x%08x\n",
DEBUG ((EFI_D_ERROR, " FreeMemoryBottom - 0x%08x\n", (UINTN)HandOffHob->EfiFreeMemoryBottom));
return EFI_OUT_OF_RESOURCES;
}
*Hob = (VOID*) (UINTN) HandOffHob->EfiEndOfHobList;
((EFI_HOB_GENERIC_HEADER*) *Hob)->HobType = Type;
((EFI_HOB_GENERIC_HEADER*) *Hob)->HobLength = Length; ((EFI_HOB_GENERIC_HEADER*) *Hob)->Reserved = 0;
HobEnd = (EFI_HOB_GENERIC_HEADER*) ((UINTN) *Hob + Length);
HandOffHob->EfiEndOfHobList = (EFI_PHYSICAL_ADDRESS) (UINTN) HobEnd;
HobEnd->HobType = EFI_HOB_TYPE_END_OF_HOB_LIST;
HobEnd->HobLength = (UINT16) sizeof (EFI_HOB_GENERIC_HEADER);
HobEnd->Reserved = 0;
HobEnd++;
HandOffHob->EfiFreeMemoryBottom = (EFI_PHYSICAL_ADDRESS) (UINTN) HobEnd;
return EFI_SUCCESS;
}