HOB全程Hand off Block,粗暴的理解就是握手时候传递的block;Pei阶段和Dxe阶段的握手,Block中的信息主要是内存等一些平台资源;PEI阶段把自己能获取到的平台相关信息通过block的方式传递给Dxe阶段:兄弟,我只能帮到这里了,接下来要看你的了,你要的东西都在Block里面了;
Pei和Dxe阶段的HOB传递在代码中的体现为PeiMain中:HobList
TempPtr.DxeIpl->Entry (
TempPtr.DxeIpl,
&PrivateData.Ps,
PrivateData.HobList
);
DxeMain中:HobStart
DxeMain (IN VOID *HobStart)
注意:Dxe阶段只会读取Hob,不会改写Hob;
HOB结构的实现分为三部分,PHIT头,描述Hob的其实位置和内存使用信息;各种Hob的列表,Dxe从该列表上获取资源,传参为Hob Type和guid;最后时Hob结束部分;
第一次进入PeiCore的时候,还是Car阶段,内存没有初始化,InitializeMemoryServices 中调用PeiCoreBuildHobHandoffInfoTable开启HOB创建;位置在Car中;
CAR:Cache As Ram,CPU内缓存当临时memory使用,此时实际的memory未完成初始化;
//PeiTemporaryMemoryBase 64K偏移处创建HOB 信息,仿真器中的实现
PeiCoreBuildHobHandoffInfoTable (
BOOT_WITH_FULL_CONFIGURATION,
(EFI_PHYSICAL_ADDRESS) (UINTN) SecCoreData->PeiTemporaryRamBase,
(UINTN) SecCoreData->PeiTemporaryRamSize
);
{
if (OldCoreData == NULL) {
PrivateData->PeiMemoryInstalled = FALSE; //内存为完成初始化
PrivateData->HobList.Raw = SecCoreData->PeiTemporaryRamBase; //Hob位置
PeiCoreBuildHobHandoffInfoTable (
BOOT_WITH_FULL_CONFIGURATION,
(EFI_PHYSICAL_ADDRESS) (UINTN) SecCoreData->PeiTemporaryRamBase,
(UINTN) SecCoreData->PeiTemporaryRamSize
);
}
}
进入PeiCoreBuildHobHandoffInfoTable 中的实现:此处几位构建PHIT(Phase Handoff InformationTable)头,描述了最基本的内存使用信息;
HOB基本结构描述:
HOB头结构
typedef struct {
UINT16 HobType; //Hob 类型
UINT16 HobLength; //Hob总长为多少byte,get Hob的时候需要该Length还定位Hob在内存中的位置
UINT32 Reserved;
} EFI_HOB_GENERIC_HEADER;
Hob 类型:
#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类型对应不同的信息存储结构,如EFI_HOB_TYPE_HANDOFF对应PHIT,可通过上述PHIT的实现推测PHIT头结构的实现;
接下介绍几种典型常用的HOB:
EFI_HOB_TYPE_MEMORY_ALLOCATION/
EFI_HOB_TYPE_RESOURCE_DESCRIPTOR/
EFI_HOB_TYPE_GUID_EXTENSION/
EFI_HOB_TYPE_MEMORY_ALLOCATION:描述一次内存的分配,用于描述的内存范围包括:
EFI_HOB_MEMORY_ALLOCATION : 通用的内存使用描述 对应自定义guid
EFI_HOB_MEMORY_ALLOCATION_STACK:栈的使用描述 gEfiHobMemoryAllocStackGuid
EFI_HOB_MEMORY_ALLOCATION_MODULE: Image入口点Entry的描述
typedef struct {
EFI_GUID Name;
EFI_PHYSICAL_ADDRESS MemoryBaseAddress;
UINT64 MemoryLength;
EFI_MEMORY_TYPE MemoryType; //内存分配时候会讲到,不同Type有不同的使用和生命周期
UINT8 Reserved[4];
} EFI_HOB_MEMORY_ALLOCATION_HEADER;
EFI_HOB_TYPE_RESOURCE_DESCRIPTOR:对一个资源[一段内存区]的类型/属性/权限/内存位置等的描述,内存管理中使用;
资源类型定义
typedef struct {
EFI_HOB_GENERIC_HEADER Header;
EFI_GUID Owner;
EFI_RESOURCE_TYPE ResourceType;
EFI_RESOURCE_ATTRIBUTE_TYPE ResourceAttribute;
EFI_PHYSICAL_ADDRESS PhysicalStart; //起始位置
UINT64 ResourceLength; //区段长度
} EFI_HOB_RESOURCE_DESCRIPTOR;
EFI_HOB_TYPE_GUID_EXTENSION:未明确具体类型的自定义描述,与对应的guid绑定,根据guid获取HOB,如BuildGuidHob/GetFirstGuidHob/GetNextGuidHob
Hob在PEI阶段的实现
PEI_CORE_INSTANCE中EFI_PEI_HOB_POINTERS HobList指向Hob位置,也是DxeCore Entry的入参;
新增HOB:
InternalPeiCreateHob (IN UINT16 Type, IN UINT16 Length)
{
Status = PeiServicesCreateHob (Type, Length, &Hob); //Length为Sizeof(HOB结构)
---->PeiServices = GetPeiServicesTablePointer ();
return (*PeiServices)->CreateHob (PeiServices, Type, Length, Hob);
}
---->
create的操作在PeiService中,直至保存在Pei Instance的Ps中,在PeiMain.c的gPs中:
PeiCreateHob (
IN CONST EFI_PEI_SERVICES **PeiServices,
IN UINT16 Type,
IN UINT16 Length,
IN OUT VOID **Hob
)
{
Status = PeiGetHobList (PeiServices, Hob); //读取PEI_CORE_INSTANCE中HobList.Raw
HandOffHob = *Hob;
Length = (UINT16)((Length + 0x7) & (~0x7)); //长度8byte对齐操作
//可用Hob的空间大小
FreeMemory = HandOffHob->EfiFreeMemoryTop - HandOffHob->EfiFreeMemoryBottom;
// 取HOB end当作返回的HOB,填充Type和长度信息
*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;
//内存寻址增加Length(需返回的Hob大小)后新建HOB End,相当与在End前插入了一个有效hob,Hob end往后顺移;
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;
}
新增样例:见各种BuildXXXXHob接口的实现,如:
HOB布局:此图为网上引用,本地重绘
查找/获取HOB
GetNextHob (IN UINT16 Type, IN CONST VOID *HobStart)
{
EFI_PEI_HOB_POINTERS Hob;
Hob.Raw = (UINT8 *) HobStart; //HOB LIST其实位置
while (!END_OF_HOB_LIST (Hob)) {
if (Hob.Header->HobType == Type) { //类型符合则找到
return Hob.Raw;
}
Hob.Raw = GET_NEXT_HOB (Hob); //否则查找下一个
}
return NULL;
}
---->通过HobLength定位下一个HOB
#define GET_HOB_LENGTH(HobStart) \
((*(EFI_HOB_GENERIC_HEADER **)&(HobStart))->HobLength)
#define GET_NEXT_HOB(HobStart) \
(VOID *)(*(UINT8 **)&(HobStart) + GET_HOB_LENGTH (HobStart))
与guid绑定的HOB全在EFI_HOB_TYPE_GUID_EXTENSION类型中;
GetNextGuidHob (
IN CONST EFI_GUID *Guid,
IN CONST VOID *HobStart
)
{
EFI_PEI_HOB_POINTERS GuidHob;
GuidHob.Raw = (UINT8 *) HobStart;
while ((GuidHob.Raw = GetNextHob (EFI_HOB_TYPE_GUID_EXTENSION, GuidHob.Raw)) != NULL) {
if (CompareGuid (Guid, &GuidHob.Guid->Name)) { //判断GUID是否满足
break;
}
GuidHob.Raw = GET_NEXT_HOB (GuidHob); //GUID不满足,寻找下一个
}
return GuidHob.Raw;
}
使用样例:GetFirstHob/GetFirstGuidHob/GetNextHob/GetNextGuidHob
HOB在内存中的实际布局:
以仿真器的实现为例,Sec阶段传递过来的初始Car信息为:
// |-----------| <---- TemporaryRamBase + TemporaryRamSize 128K
// | Heap |
// | |
// |-----------| <---- StackBase / PeiTemporaryMemoryBase 64K
sizeof (EFI_SEC_PEI_HAND_OFF)
// |-----------| <---- TopOfStack 0x000001b1a5ceffb0 <----SecCoreData
// | |
// | Stack |
// |-----------| <---- TemporaryRamBase
// SEC阶段传递给PEI阶段的PEI运行环境,PeiCode的入参之一,主要为Car内存信息,见上图解析;
typedef struct _EFI_SEC_PEI_HAND_OFF {
UINT16 DataSize;
VOID *BootFirmwareVolumeBase; //FD文件在内存中的首地址
UINTN BootFirmwareVolumeSize; //FD 镜像文件大小
VOID *TemporaryRamBase; //temperary memory/car的首地址
UINTN TemporaryRamSize; //Car大小,缓存为内存可用空间
VOID *PeiTemporaryRamBase; //可用的Car位置和大小
UINTN PeiTemporaryRamSize;
VOID *StackBase; // Car阶段栈大小和地址
UINTN StackSize;
} EFI_SEC_PEI_HAND_OFF;
内存初始化完成后整体内存空间会进行搬迁;
内存初始化(在某个特性的PEIM中)完成后,调用PeiInstallPeiMemory建立物理内存的描述;
PeiInstallPeiMemory (
IN CONST EFI_PEI_SERVICES **PeiServices,
IN EFI_PHYSICAL_ADDRESS MemoryBegin,
IN UINT64 MemoryLength
)
{
PrivateData->PhysicalMemoryBegin = MemoryBegin;
PrivateData->PhysicalMemoryLength = MemoryLength;
PrivateData->FreePhysicalMemoryTop = MemoryBegin + MemoryLength;
PrivateData->SwitchStackSignal = TRUE;
}
之后的内存迁移,即Car到物理内存的迁移在PeiCheckAndSwitchStack完成,形成Stack/Heap空间;SwitchStack完成car到实际内存运行空间的切换;
PeiCheckAndSwitchStack嗲用用BuildStackHob将实际内存Stack保存在Hob中:
BuildStackHob (TopOfNewStack - NewStackSize, NewStackSize)----》
内存初始化完成后第二次进入PeiCode调整内存分配的位置;