在PEI和DXE之间通过HOB 传递参数。例如从VOID EFIAPI DxeMain ( IN VOID *HobStart ) 的入口函数也可以看到输入参数只有一个,这个参数是在PEI阶段准备好的。
拿到HOB 后就可以按照下面的code从HOB中拿到PEI传递过来的数据

VectorInfoList = NULL;
 GuidHob = GetNextGuidHob (&gEfiVectorHandoffInfoPpiGuid, HobStart);
 if (GuidHob != NULL) {
 VectorInfoList = (EFI_VECTOR_HANDOFF_INFO *) (GET_GUID_HOB_DATA(GuidHob));
 }
 调用GetNextGuidHob 来找从HobStart 开始第一个匹配gEfiVectorHandoffInfoPpiGuid的GuidHob。
 VOID *
 EFIAPI
 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)) {
 break;
 }
 GuidHob.Raw = GET_NEXT_HOB (GuidHob);
 }
 return GuidHob.Raw;
 }


可见会遍历整个GuidHob.Raw,如果找到guid的name等于gEfiVectorHandoffInfoPpiGuid 就停止,然后返回 GuidHob.Raw。
然后通过
#define GET_GUID_HOB_DATA(HobStart)
(VOID )((UINT8 **)&(HobStart) + sizeof (EFI_HOB_GUID_TYPE))

就可以拿到想要的gEfiVectorHandoffInfoPpiGuid
OVERVIEW
本篇补充相关PEI到DXE阶段的一写知识
在PEI阶段,PEIM 、PPI、 HOB组成了PEI阶段的最重要的部分,PEI阶段的module也可以理解为Driver就是PEIM,PEI阶段就是由一个一个的PEIM组成的;PPI是PEIM之间互相调用的接口,由惟一的GUID引导,内部也包含一些接口,HOB相当于信件在PEI阶段创建,会记载当前系统的信息,可以自定义HOB,然后在DXE阶段读取。框图如下:

简单解读:
PEI Core主要包括Core Services和Core Dispatcher。

Core Services包括PEI及后面phase要用到的各种Services, 比如Status Code, HOBs,Memory Services,Boot Mode Services等。在PEi阶段get当前计算机启动的boot Mode是有直接定义好的PEI Service函数,在DXE以及后面的阶段要通过HOB方式,通过get HOB LIST然后拆解信息进行get启动的boot Mode。
Core Dispatcher负责派发各PEIMs,意思是将PEIM按照既定的顺序Load并执行。这里的既定顺序即Dependency顺序,就是inf文件里面的depx,只有满足条件才会执行。
各PEIM Entry可能使用其它PEIM的PPI, 也可能使用自己的PPI。
PEI Core最后会找到DXE IPL PPI,(IPL是一个很重要的知识点)进入下一阶段DXE。
DXE获得之前phase Data的是从HOB里拿,PEI Core会创建HOB,PEI和DXE都可以使用HOB的Data。
PEIM
PEIM(PEI Module), 会被编译成efi binary,在一套完整的BIOS code编译完之后进入到build目录就可以找到这个PEIM具体的efi,.inf + .c +.h >build> .efi是为了硬件相关的初始化,PEIM 也提供各自提供接口(interface)给别的PEIM使用

PPI
PPIs (PEIM-to-PEIM Interfaces)

PEIMs被调用是通过PPI,Interface。Interface是UEFI重要的概念,会经常出现。简单来说,PPI只是一个接口,接口里面有成员函数,想调用某个函数必须通过该接口。
PPI的名字:GUID (128-bits value)
PPIs被定义成结构体的形式,在code里看PPI就是一个Struct,其中可能包括功能、数据,或者两者的混合。
PEIM会把它的PPI注册到PEI Foundation,PEI Foundation管理着庞大的PPI数据库
USE PPI
几个重要的PPI Services

InstallPpi() 安装PPI到PEI foundation,protocol install完后是放到Handle Datebase里面
 LocatePpi() 根据PPI名字GUID从PEI foundation找Interface
 NotifyPpi() PPI里的function不会在派发时就执行,会有一个判定条件,通知系统这个PPI会在某个PPI被安装时才执行。
 Install PPI
 /**Install PPI services. It is implementation of EFI_PEI_SERVICE.InstallPpi.
 这是个service,PEI foundation提供的。 通过GUID安装。目的是让别人调用。
 @param PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation.
 标准格式,入口第一人参数是铁定的EFI_PEI_SERVICES指针
 @param PpiList Pointer to PPI array that want to be installed.
 第二个参数是PPI List, LIST里包括Flag、GUID和函数 参考.h里的EFI_PEI_PPI_DESCRIPTOR定义@retval EFI_SUCCESS if all PPIs in PpiList are successfully installed.
 @retval EFI_INVALID_PARAMETER if PpiList is NULL pointer
 if any PPI in PpiList is not valid
 @retval EFI_OUT_OF_RESOURCES if there is no more memory resource to install PPI**/
 EFI_STATUS
 EFIAPI
 PeiInstallPpi (
 IN CONST EFI_PEI_SERVICES **PeiServices,
 IN CONST EFI_PEI_PPI_DESCRIPTOR *PpiList
 );

实例:
以下面的PPI为例,一个给Capsule服务的PPI,里面有三个成员 CapsuleCoalesce, CheckCapsuleUpdate, CreateState。

CONST EFI_PEI_CAPSULE_PPI mCapsulePpi = {
 CapsuleCoalesce,
 CheckCapsuleUpdate,
 CreateState
 };

给InstallPPI传递的第二个参数:*PpiList。Descriptor有三个值,第一个为属性,第二个为绑定的GUID用于后面locate调用,第三个参数放入struct接口,这样就定义好了ppiList。

安装PpiList, 通常代码会用Library封装(*PeiServices)->InstallPpi 成PeiServicesInstallPpi

PeiServicesInstallPpi原型函数,还是那个标准的InstallPpi,传了两个参数This指针和PpiList

EFI_PEI_PPI_DESCRIPTOR
//
 // PEI Ppi Services List Descriptors
 //
 #define EFI_PEI_PPI_DESCRIPTOR_PIC 0x00000001
 #define EFI_PEI_PPI_DESCRIPTOR_PPI 0x00000010
 #define EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK 0x00000020
 #define EFI_PEI_PPI_DESCRIPTOR_NOTIFY_DISPATCH 0x00000040
 #define EFI_PEI_PPI_DESCRIPTOR_NOTIFY_TYPES 0x00000060
 #define EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST 0x80000000
 ///
 /// The data structure through which a PEIM describes available services to the PEI Foundation.
 ///
 typedef struct {
 ///
 /// This field is a set of flags describing the characteristics of this imported table entry.
 /// All flags are defined as EFI_PEI_PPI_DESCRIPTOR_***, which can also be combined into one.
 /// 上面描述的属性。
 UINTN Flags;
 ///
 /// The address of the EFI_GUID that names the interface.
 /// 所绑定的GUID
 EFI_GUID *Guid;
 ///
 /// A pointer to the PPI. It contains the information necessary to install a service.
 /// 所需要install的PPI
 VOID *Ppi;
 } EFI_PEI_PPI_DESCRIPTOR;LocatePPI()
 /**
 Locate a given named PPI.
 用GUID从Database中找想要的PPI@param PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation.
 @param Guid Pointer to GUID of the PPI.
 @param Instance Instance Number to discover.
 @param PpiDescriptor Pointer to reference the found descriptor. If not NULL,
 returns a pointer to the descriptor (includes flags, etc)
 @param Ppi Pointer to reference the found PPI@retval EFI_SUCCESS if the PPI is in the database
 @retval EFI_NOT_FOUND if the PPI is not in the database**/
 EFI_STATUS
 EFIAPI
 PeiLocatePpi (
 IN CONST EFI_PEI_SERVICES **PeiServices,
 IN CONST EFI_GUID *Guid,
 IN UINTN Instance,
 IN OUT EFI_PEI_PPI_DESCRIPTOR **PpiDescriptor,
 IN OUT VOID **Ppi
 );
 //第一个参数是固定的EFI_PEI_SERVICES第二个参数为GUID,第三个参数通常为0,第四个参数为NULL最重要是拿到第五个参数,通过这个指针可以得到Interface。

实例:
以Capsule为例:Guid为gEfiPeiCapsulePpiGuid,通过这个GUID从Database中找想要的PPI, 想要的PPI都在这个Capsule指针里,Locate之后就可以调用PPI里的函数实现CheckCapsuleUpdate功能

这也是封装过的LocatePpi,原型函数是下面这样,标准的LocatePpi,当然直接用PeiService->也是可以的

Notifyppi()
 /**Install a notification for a given PPI.
 注册一个notification服务,当给定的那个PPI被安装或者再安装时,执行notify里的函数。@param PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation.
 @param NotifyList Pointer to list of Descriptors to notify upon.@retval EFI_SUCCESS if successful
 @retval EFI_OUT_OF_RESOURCES if no space in the database
 @retval EFI_INVALID_PARAMETER if not a good descriptor
 NotifyList里包括Flag,GUID和Notify Routine**/
 EFI_STATUS
 EFIAPI
 PeiNotifyPpi (
 IN CONST EFI_PEI_SERVICES **PeiServices,
 IN CONST EFI_PEI_NOTIFY_DESCRIPTOR *NotifyList
 );

实例:当这个gEfiEndOfPeiSignalPpiGuid指向的PPI被安装时才会触发S3EndOfPeiNotify()

1->首先将要做的事情写到这个函数中,
2->定义好Notify Descriptor :FLAG 、所需加载的PPI GUID、需要执行的函数
3->调用封装后的PeiServiceNotifyPpi => (*PeiServices)->NotifyPpi(PeiServices,NotifyList);
注:在自定义的函数中在确定的点运行相关ppi最好自己写一个Ppi作为触发条件,否则在使用code中定义好的Ppi时需确保不会被其他函数影响或影响其它函数

HOB
HOB (Hand-Off Blocks )传输信息的载体,相比于其他Phase之间的联系,Pei到DXE之间联系比较薄弱,PEI一些初始化硬件、内存的数据等,DXE需要知道,HOB便作为桥梁应运而生。

HOB producer phase (PEI phase)
HOB consumer phase (PEI & DXE phase) (如getbootmodehob就是在PEI阶段调用的,但DXE阶段是不能产生HOB的)
HOB实际上就是一个链表,当我们找到一个hoblist的头,那么整个链表的数据都能get到,比如说GetHobList(),会直接获取hoblist的指针,而且第一个HOB总是为PHIT == Phase Handoff Information Table,里面是boot mode
其它HOB可能出现在List任意位置, 最重要的是System Memory HOB & Firmware Volumes, HOB列表总是会以END_OF_HOB_LIST结束

所以判定是否是HOBlist的最后一个一般都是while (!END_OF_HOB_LIST (Hob))。

下图中没有显示的另一个HOB类型是GUID HOB,它允许PEIM将私有数据传递给DXE驱动程序。

HOB TYPE

GUID类型的HOB是自定义HOB时候会用到的类型,通常用于自己写一些HOB信息。

HOB的使用

/**
 Add a new HOB to the HOB List.@param PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation.
 @param Type Type of the new HOB.
 @param Length Length of the new HOB to allocate.
 @param Hob Pointer to the new HOB.@return EFI_SUCCESS Success to create HOB.
 @retval EFI_INVALID_PARAMETER if Hob is NULL
 @retval EFI_NOT_AVAILABLE_YET if HobList is still not available.
 @retval EFI_OUT_OF_RESOURCES if there is no more memory to grow the Hoblist.**/
 EFI_STATUS
 EFIAPI
 PeiCreateHob (
 IN CONST EFI_PEI_SERVICES **PeiServices,
 IN UINT16 Type, //对于自定义的HOB 一般使用EFI_HOB_GUID_TYPE
 IN UINT16 Length,
 IN OUT VOID **Hob
 );


GetHobList()返回整个HOB列表中匹配GUID HOB的第一个实例
由于阶段不一样,PEI直接拿到HOB List Pointer, DXE通过System Configuration Table (gST)拿。

传入的参数第一个为Hob的GUID,所有的UEFI元素都有自己的GUID,传入得到的返回结果就是HOBlist ,通过的是configration table去抓的(属于systemtable的成员),每个DXE Driver的entrypoint都有两个参数imaginehandle 和systemtable,故而可以通过systemtable可以访问整个系统的资源。