PEI Foundation 提供的PPI服务主要是以下四个函数。
PeiInstallPpi, //安装PPI
PeiReInstallPpi, //重新安装PPI
PeiLocatePpi, //查找PPI
PeiNotifyPpi, //安装一个Notify
PPI和Notify的数据结构组织如图所示。PEI_CORE_INSTANCE 中PpiData维护了PPI和两种类型的Notify数组。PPI都存放在PpiList的PPiPtrs数组中。而Notify既可以放在CallbackNotifyList也可以放在DispatchNotifyList中。
PeiInstallPpi像PEI foundation安装一个PPI,其实现是通过 GUID 在 PEI PPI 数据库中安装一个接口。把PPI interface 从头部向尾部插入到PPI database 的PPI LIST 数组中。然后使用 ProcessNotify 为新安装的 PPI 处理通知。
EFI_STATUS
InternalPeiInstallPpi (
IN CONST EFI_PEI_SERVICES **PeiServices,
IN CONST EFI_PEI_PPI_DESCRIPTOR *PpiList,
IN BOOLEAN Single
)
{
...
PpiListPointer = &PrivateData->PpiData.PpiList;
Index = PpiListPointer->CurrentCount;
...
for ( ; ;) {
//将要安装的PPI插入到PpiPtrs数组中,然后将索引index+1
PpiListPointer->PpiPtrs[Index].Ppi = (EFI_PEI_PPI_DESCRIPTOR *)PpiList;
Index++;
PpiListPointer->CurrentCount++;
...
}
...
//使用 ProcessNotify 为新安装的 PPI 处理通知。
ProcessNotify (
PrivateData,
EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK,
LastCount,
PpiListPointer->CurrentCount,
0,
PrivateData->PpiData.CallbackNotifyList.CurrentCount
);
return EFI_SUCCESS;
}
PeiLocatePpi根据GUID查找PPI的指针并返回。
PeiLocatePpi (
)
{
//遍历PpiData中的所有PPI指针。
for (Index = 0; Index < PrivateData->PpiData.PpiList.CurrentCount; Index++) {
TempPtr = PrivateData->PpiData.PpiList.PpiPtrs[Index].Ppi;
CheckGuid = TempPtr->Guid;
//如果PPI的GUID 匹配,那么就返回
if ((((INT32 *)Guid)[0] == ((INT32 *)CheckGuid)[0]) &&
(((INT32 *)Guid)[1] == ((INT32 *)CheckGuid)[1]) &&
(((INT32 *)Guid)[2] == ((INT32 *)CheckGuid)[2]) &&
(((INT32 *)Guid)[3] == ((INT32 *)CheckGuid)[3]))
{
...
//返回该匹配的PPI
*Ppi = TempPtr->Ppi;
...
return EFI_SUCCESS;
}
...
}
PeiNotifyPpi其流程和安装PPI差不多把PPI notify 插入PPI database 的PPI LIST 数组中,插入顺序是从PPI LIST的尾部向头部插入并使用 ProcessNotify 为所有以前安装的 PPI 处理通知。
InternalPeiNotifyPpi (
IN CONST EFI_PEI_SERVICES **PeiServices,
IN CONST EFI_PEI_NOTIFY_DESCRIPTOR *NotifyList,
IN BOOLEAN Single
)
{
......
for ( ; ;) {
if ((NotifyList->Flags & EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK) != 0) {
//如果是EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK,就将NotifyList节点插入CallbackNotifyListPointer链表中
...
CallbackNotifyListPointer->NotifyPtrs[CallbackNotifyIndex].Notify = (EFI_PEI_NOTIFY_DESCRIPTOR *)NotifyList;
CallbackNotifyIndex++;
CallbackNotifyListPointer->CurrentCount++;
} else {
...
//否则就将NotifyList节点插入DispatchNotifyListPointer链表中
DispatchNotifyListPointer->NotifyPtrs[DispatchNotifyIndex].Notify = (EFI_PEI_NOTIFY_DESCRIPTOR *)NotifyList;
DispatchNotifyIndex++;
DispatchNotifyListPointer->CurrentCount++;
}
if (Single) {
break;
} else if ((NotifyList->Flags & EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST) ==
EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST)
{
break;
}
...
}
...
//调用ProcessNotify 区处理Notify的回调函数
ProcessNotify (
PrivateData,
EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK,
0,
PrivateData->PpiData.PpiList.CurrentCount,
LastCallbackNotifyCount,
CallbackNotifyListPointer->CurrentCount
);
return EFI_SUCCESS;
}
ProcessNotify这个函数在安装PPI和安装Notify的时候都会被调用。其作用就是从PpiData中查到对应GUID的Notify描述符,然后调用它的回调函数Notify
VOID
ProcessNotify (
IN PEI_CORE_INSTANCE *PrivateData,
IN UINTN NotifyType,
IN INTN InstallStartIndex,
IN INTN InstallStopIndex,
IN INTN NotifyStartIndex,
IN INTN NotifyStopIndex
)
{
...
//遍历所有Notify
for (Index1 = NotifyStartIndex; Index1 < NotifyStopIndex; Index1++) {
if (NotifyType == EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK) {
NotifyDescriptor = PrivateData->PpiData.CallbackNotifyList.NotifyPtrs[Index1].Notify;
} else {
NotifyDescriptor = PrivateData->PpiData.DispatchNotifyList.NotifyPtrs[Index1].Notify;
}
//拿出当前Notify的Guid
CheckGuid = NotifyDescriptor->Guid;
//遍历所有PPI
for (Index2 = InstallStartIndex; Index2 < InstallStopIndex; Index2++) {
SearchGuid = PrivateData->PpiData.PpiList.PpiPtrs[Index2].Ppi->Guid;
//如果PPI的GUID和Notify的GUID匹配,那么调用回调函数Notify通知该PPI。
if ((((INT32 *)SearchGuid)[0] == ((INT32 *)CheckGuid)[0]) &&
(((INT32 *)SearchGuid)[1] == ((INT32 *)CheckGuid)[1]) &&
(((INT32 *)SearchGuid)[2] == ((INT32 *)CheckGuid)[2]) &&
(((INT32 *)SearchGuid)[3] == ((INT32 *)CheckGuid)[3]))
{
NotifyDescriptor->Notify (
(EFI_PEI_SERVICES **)GetPeiServicesTablePointer (),
NotifyDescriptor,
(PrivateData->PpiData.PpiList.PpiPtrs[Index2].Ppi)->Ppi
);
}
}
}
}
PeiGetHobList, //获取HobList指针
PeiCreateHob, //创建一个Hob
HOB是PEI向DXE传递信息的一种方式,HOB在PEI中的实现非常简单,由PEI Foundation管理。PeiCore提供的HOB Service主要是创建HOB:PeiCreateHob。
HOB的初始化在InitializeMemoryServices 中,首先把HobList指向SecCoreData->PeiTemporaryRamBase。这个地址是SEC阶段传过来的。然后就是建立一个HandOffInfoTable 类型的HOB,这个HOB是第一个HOB。
VOID
InitializeMemoryServices (
...
)
{
...
PrivateData->HobList.Raw = SecCoreData->PeiTemporaryRamBase;
...
PeiCoreBuildHobHandoffInfoTable (
BOOT_WITH_FULL_CONFIGURATION,
(EFI_PHYSICAL_ADDRESS)(UINTN)SecCoreData->PeiTemporaryRamBase,
(UINTN)SecCoreData->PeiTemporaryRamSize
);
...
}
PeiCreateHob 会将要创建的Hob插入到HobList的尾部, HOB的组织如下图所示。
PeiCreateHob (
IN CONST EFI_PEI_SERVICES **PeiServices,
IN UINT16 Type,
IN UINT16 Length,
IN OUT VOID **Hob
)
{
...
// 获取PeiCore的HobList的指针并赋值给Hob变量
Status = PeiGetHobList (PeiServices, Hob);
HandOffHob = *Hob;
//拿到HobList尾节点并把它的指针赋值给Hob变量,此时就是Hob变量就是要插入的Hob节点指针
*Hob = (VOID *)(UINTN)HandOffHob->EfiEndOfHobList;
//根据函数参数设置Hob的Type和Length
((EFI_HOB_GENERIC_HEADER *)*Hob)->HobType = Type;
((EFI_HOB_GENERIC_HEADER *)*Hob)->HobLength = Length;
((EFI_HOB_GENERIC_HEADER *)*Hob)->Reserved = 0;
//最后更新Hob尾节点指针,Hob尾节点会放在新建Hob的后面
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++;
...
}
定义在PiHob.h中
#define EFI_HOB_TYPE_HANDOFF 0x0001 //记录了PeiCore的信息
#define EFI_HOB_TYPE_MEMORY_ALLOCATION 0x0002 //记录内存分配的信息
#define EFI_HOB_TYPE_RESOURCE_DESCRIPTOR 0x0003 //记录不同resource
#define EFI_HOB_TYPE_GUID_EXTENSION 0x0004
#define EFI_HOB_TYPE_FV 0x0005 //记录FV的信息
#define EFI_HOB_TYPE_CPU 0x0006 //记录关于CPU的信息
#define EFI_HOB_TYPE_MEMORY_POOL 0x0007 //记录MEMORY_POOL的信息,内存分配块
#define EFI_HOB_TYPE_FV2 0x0009 //记录第二个FV的信息
#define EFI_HOB_TYPE_LOAD_PEIM_UNUSED 0x000A
#define EFI_HOB_TYPE_UEFI_CAPSULE 0x000B
#define EFI_HOB_TYPE_FV3 0x000C //记录第三个FV的信息
#define EFI_HOB_TYPE_UNUSED 0xFFFE
#define EFI_HOB_TYPE_END_OF_HOB_LIST 0xFFFF //尾节点标识符
这里以一个简单的EFI_HOB_TYPE_CPU举例,这个类型比较简单,通过学习这个类型其他的也可触类旁通。这个记录CPU的Memory和IO的寻址空间。这里空间的大小是以bit为单位。比如IO值如果是0x10的话,则代表其寻址空间只有16bit,也就是64KB的大小。
typedef struct {
EFI_HOB_GENERIC_HEADER Header;
UINT8 SizeOfMemorySpace;
UINT8 SizeOfIoSpace;
UINT8 Reserved[6];
} EFI_HOB_CPU;
可以看一下在实际中代码中CPU类型的HOB是如何创建的。在CpuPei初始化中又这么一句代码BuildCpuHob。这里ArmGetPhysicalAddressBits 是44, PcdPrePiCpuIoSize得到的值是16, 所以Memory空间有(2 ^ 44)B, IO空间是64KB
edk2/ArmPkg/Drivers/CpuPei/CpuPei.c
InitializeCpuPeim (
IN EFI_PEI_FILE_HANDLE FileHandle,
IN CONST EFI_PEI_SERVICES **PeiServices
)
{
BuildCpuHob (ArmGetPhysicalAddressBits (), PcdGet8 (PcdPrePiCpuIoSize));
}
VOID
EFIAPI
BuildCpuHob (
IN UINT8 SizeOfMemorySpace,
IN UINT8 SizeOfIoSpace
)
{
EFI_HOB_CPU *Hob;
// 创建一个HOB
Hob = CreateHob (EFI_HOB_TYPE_CPU, sizeof (EFI_HOB_CPU));
//根据传入的参数填充HOB的Memory和IO的空间大小
Hob->SizeOfMemorySpace = SizeOfMemorySpace;
Hob->SizeOfIoSpace = SizeOfIoSpace;
}
我自己写了一段代码在jump到DXE之前dump出所有的HOB块。
EFI_PEI_HOB_POINTERS Hob;
DEBUG((DEBUG_INFO, " EFI_HOB_TYPE_HANDOFF\n"));
for (Hob.Raw = PrivateData->HobList.Raw; !END_OF_HOB_LIST(Hob); Hob.Raw = GET_NEXT_HOB(Hob)) {
if (GET_HOB_TYPE(Hob) == EFI_HOB_TYPE_HANDOFF) {
DEBUG((DEBUG_INFO, " BootMode - 0x%x\n", Hob.HandoffInformationTable->BootMode));
DEBUG((DEBUG_INFO, " EfiMemoryTop - 0x%016lx\n", Hob.HandoffInformationTable->EfiMemoryTop));
DEBUG((DEBUG_INFO, " EfiMemoryBottom - 0x%016lx\n", Hob.HandoffInformationTable->EfiMemoryBottom));
DEBUG((DEBUG_INFO, " EfiFreeMemoryTop - 0x%016lx\n", Hob.HandoffInformationTable->EfiFreeMemoryTop));
DEBUG((DEBUG_INFO, " EfiFreeMemoryBottom - 0x%016lx\n", Hob.HandoffInformationTable->EfiFreeMemoryBottom));
DEBUG((DEBUG_INFO, " EfiEndOfHobList - 0x%lx\n", Hob.HandoffInformationTable->EfiEndOfHobList));
}
}
DEBUG((DEBUG_INFO, " EFI_HOB_TYPE_MEMORY_ALLOCATION\n"));
for (Hob.Raw = PrivateData->HobList.Raw; !END_OF_HOB_LIST(Hob); Hob.Raw = GET_NEXT_HOB(Hob)) {
if (GET_HOB_TYPE(Hob) == EFI_HOB_TYPE_MEMORY_ALLOCATION) {
DEBUG ((
DEBUG_INFO | DEBUG_LOAD,
" Memory Allocation 0x%08x 0x%0lx - 0x%0lx\n", \
Hob.MemoryAllocation->AllocDescriptor.MemoryType, \
Hob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress, \
Hob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress + Hob.MemoryAllocation->AllocDescriptor.MemoryLength - 1
));
}
}
DEBUG((DEBUG_INFO, " EFI_HOB_TYPE_RESOURCE_DESCRIPTOR\n"));
for (Hob.Raw = PrivateData->HobList.Raw; !END_OF_HOB_LIST(Hob); Hob.Raw = GET_NEXT_HOB(Hob)) {
if (GET_HOB_TYPE(Hob) == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) {
DEBUG ((
DEBUG_INFO | DEBUG_LOAD,
" Owner:%g, EFI_RESOURCE_TYPE:0x%x, ResourceAttribute:0x%x, PhysicalStart:0x%11p, ResourceLength:0x%11p\n",
&Hob.ResourceDescriptor->Owner,
Hob.ResourceDescriptor->ResourceType,
Hob.ResourceDescriptor->ResourceAttribute,
Hob.ResourceDescriptor->PhysicalStart,
Hob.ResourceDescriptor->ResourceLength
));
}
}
DEBUG((DEBUG_INFO, " EFI_HOB_TYPE_GUID_CPU\n"));
for (Hob.Raw = PrivateData->HobList.Raw; !END_OF_HOB_LIST(Hob); Hob.Raw = GET_NEXT_HOB(Hob)) {
if (GET_HOB_TYPE(Hob) == EFI_HOB_TYPE_CPU) {
DEBUG((DEBUG_INFO, " SizeofMem:0x%11p, SizeofIO:0x%11p\n", Hob.Cpu->SizeOfMemorySpace, Hob.Cpu->SizeOfIoSpace));
}
}
DEBUG((DEBUG_INFO, " EFI_HOB_TYPE_GUID_EXTENSION\n"));
for (Hob.Raw = PrivateData->HobList.Raw; !END_OF_HOB_LIST(Hob); Hob.Raw = GET_NEXT_HOB(Hob)) {
if (GET_HOB_TYPE(Hob) == EFI_HOB_TYPE_GUID_EXTENSION) {
DEBUG((DEBUG_INFO, " Length:0x%x, Guid:%g\n", GET_HOB_LENGTH(Hob), &Hob.Guid->Name));
}
}
DEBUG((DEBUG_INFO, " EFI_HOB_TYPE_LOAD_PEIM_UNUSED\n"));
for (Hob.Raw = PrivateData->HobList.Raw; !END_OF_HOB_LIST(Hob); Hob.Raw = GET_NEXT_HOB(Hob)) {
if (GET_HOB_TYPE(Hob) == EFI_HOB_TYPE_LOAD_PEIM_UNUSED) {
DEBUG((DEBUG_INFO, " Length:0x%x\n", GET_HOB_LENGTH(Hob)));
}
}
DEBUG((DEBUG_INFO, " EFI_HOB_TYPE_UEFI_CAPSULE\n"));
for (Hob.Raw = PrivateData->HobList.Raw; !END_OF_HOB_LIST(Hob); Hob.Raw = GET_NEXT_HOB(Hob)) {
if (GET_HOB_TYPE(Hob) == EFI_HOB_TYPE_UEFI_CAPSULE) {
DEBUG((DEBUG_INFO, " BaseAddr:0x%x, Length:0x%x\n", Hob.Capsule->BaseAddress, Hob.Capsule->Length));
}
}
DEBUG((DEBUG_INFO, " EFI_HOB_TYPE_MEMORY_POOL\n"));
for (Hob.Raw = PrivateData->HobList.Raw; !END_OF_HOB_LIST(Hob); Hob.Raw = GET_NEXT_HOB(Hob)) {
if (GET_HOB_TYPE(Hob) == EFI_HOB_TYPE_MEMORY_POOL) {
DEBUG((DEBUG_INFO, " Length:0x%x\n", GET_HOB_LENGTH(Hob)));
}
}
DEBUG((DEBUG_INFO, " EFI_HOB_TYPE_FV/2/3\n"));
for (Hob.Raw = PrivateData->HobList.Raw; !END_OF_HOB_LIST (Hob); Hob.Raw = GET_NEXT_HOB (Hob)) {
if (GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_FV) {
DEBUG ((
DEBUG_INFO | DEBUG_LOAD,
" FV Hob 0x%0lx - 0x%0lx\n",
Hob.FirmwareVolume->BaseAddress,
Hob.FirmwareVolume->BaseAddress + Hob.FirmwareVolume->Length - 1
));
} else if (GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_FV2) {
DEBUG ((
DEBUG_INFO | DEBUG_LOAD,
" FV2 Hob 0x%0lx - 0x%0lx\n",
Hob.FirmwareVolume2->BaseAddress,
Hob.FirmwareVolume2->BaseAddress + Hob.FirmwareVolume2->Length - 1
));
DEBUG ((
DEBUG_INFO | DEBUG_LOAD,
" %g - %g\n",
&Hob.FirmwareVolume2->FvName,
&Hob.FirmwareVolume2->FileName
));
} else if (GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_FV3) {
DEBUG ((
DEBUG_INFO | DEBUG_LOAD,
" FV3 Hob 0x%0lx - 0x%0lx - 0x%x - 0x%x\n",
Hob.FirmwareVolume3->BaseAddress,
Hob.FirmwareVolume3->BaseAddress + Hob.FirmwareVolume3->Length - 1,
Hob.FirmwareVolume3->AuthenticationStatus,
Hob.FirmwareVolume3->ExtractedFv
));
if (Hob.FirmwareVolume3->ExtractedFv) {
DEBUG ((
DEBUG_INFO | DEBUG_LOAD,
" %g - %g\n",
&Hob.FirmwareVolume3->FvName,
&Hob.FirmwareVolume3->FileName
));
}
}
}
log打印
dump HobList
EFI_HOB_TYPE_HANDOFF
BootMode - 0x0
EfiMemoryTop - 0x0000000048000000
EfiMemoryBottom - 0x0000000044000000
EfiFreeMemoryTop - 0x0000000047832000
EfiFreeMemoryBottom - 0x0000000044021420
EfiEndOfHobList - 0x44021418
EFI_HOB_TYPE_MEMORY_ALLOCATION
Memory Allocation 0x00000004 0x47FFF000 - 0x47FFFFFF
Memory Allocation 0x00000004 0x47FFE000 - 0x47FFEFFF
Memory Allocation 0x00000004 0x47FFD000 - 0x47FFDFFF
Memory Allocation 0x00000004 0x47FFC000 - 0x47FFCFFF
Memory Allocation 0x00000004 0x47FFB000 - 0x47FFBFFF
Memory Allocation 0x00000004 0x44000000 - 0x4401FFFF
Memory Allocation 0x00000003 0x47FEC000 - 0x47FFAFFF
Memory Allocation 0x00000003 0x47FE5000 - 0x47FEBFFF
Memory Allocation 0x00000003 0x47FE2000 - 0x47FE4FFF
Memory Allocation 0x00000003 0x47FDE000 - 0x47FE1FFF
Memory Allocation 0x00000004 0x47EDD000 - 0x47FDDFFF
Memory Allocation 0x00000003 0x47ED5000 - 0x47EDCFFF
Memory Allocation 0x00000004 0x47EC5000 - 0x47ED4FFF
Memory Allocation 0x00000004 0x47832000 - 0x47EC4FFF
EFI_HOB_TYPE_RESOURCE_DESCRIPTOR
Owner:00000000-0000-0000-0000-000000000000, EFI_RESOURCE_TYPE:0x0, ResourceAttribute:0x2007, PhysicalStart:0x00040000000, ResourceLength:0x00008000000
EFI_HOB_TYPE_GUID_CPU
SizeofMem:0x0000000002C, SizeofIO:0x00000000010
EFI_HOB_TYPE_GUID_EXTENSION
Length:0xE0, Guid:EA296D92-0B69-423C-8C28-33B4E0A91268
Length:0x158, Guid:9B3ADA4F-AE56-4C24-8DEA-F03B7558AE50
Length:0x68, Guid:4C19049F-4137-4DD3-9C10-8B97A83FFDFA
Length:0xB8, Guid:A4EE0728-E5D7-4AC5-B21E-658ED857E834
Length:0x20, Guid:16958446-19B7-480B-B047-7485AD3F716D
Length:0x20, Guid:B199DEA9-FD5C-4A84-8082-2F4170780305
Length:0x238, Guid:86D70125-BAA3-4296-A62F-602BEBBB9081
Length:0x28, Guid:EE4E5898-3914-4259-9D6E-DC7BD79403CF
EFI_HOB_TYPE_LOAD_PEIM_UNUSED
EFI_HOB_TYPE_UEFI_CAPSULE
EFI_HOB_TYPE_MEMORY_
Length:0x48
Length:0x208
Length:0x1C8
Length:0x108
Length:0x108
Length:0x208
Length:0x10
Length:0x40
Length:0xA8
Length:0x20
Length:0x40
Length:0x18
Length:0x18
Length:0x20
Length:0x38
Length:0x18
Length:0x18
Length:0x20
EFI_HOB_TYPE_FV/2/3
FV Hob 0x1000 - 0x1FFFFF
FV Hob 0x47832010 - 0x47EC458F
FV2 Hob 0x47832010 - 0x47EC458F
00000000-0000-0000-0000-000000000000 - 9E21FD93-9C72-4C15-8C4B-E77F1DB2D792
FV3 Hob 0x47832010 - 0x47EC458F - 0x0 - 0x1
00000000-0000-0000-0000-000000000000 - 9E21FD93-9C72-4C15-8C4B-E77F1DB2D792
PeiGetBootMode, //获取BootMode
PeiSetBootMode, //设置BootMode
BootMode主要用来获取和设置是什么启动模式,这个PEI Service比较简单,Set就是将HandOffHob中的bootmode成员设置一下,Get就是获取HandOffHob中bootmode成员并返回。
edk2/MdeModulePkg/Core/Pei/BootMode/BootMode.c
PeiGetBootMode (
IN CONST EFI_PEI_SERVICES **PeiServices,
IN OUT EFI_BOOT_MODE *BootMode
)
{
...
PrivateData = PEI_CORE_INSTANCE_FROM_PS_THIS (PeiServices);
HandOffHob = (PrivateData->HobList.HandoffInformationTable);
*BootMode = HandOffHob->BootMode;
return EFI_SUCCESS;
}
PeiSetBootMode (
IN CONST EFI_PEI_SERVICES **PeiServices,
IN EFI_BOOT_MODE BootMode
)
{
...
PrivateData = PEI_CORE_INSTANCE_FROM_PS_THIS (PeiServices);
HandOffHob = (PrivateData->HobList.HandoffInformationTable);
HandOffHob->BootMode = BootMode;
return EFI_SUCCESS;
}
PeiInstallPeiMemory, //向PEI Foundation注册永久内存
PeiAllocatePages,
PeiAllocatePool,
(EFI_PEI_COPY_MEM)CopyMem,
(EFI_PEI_SET_MEM)SetMem,
永久内存的PEIM MemoryInitPeim会在内存初始化完之后调用这个函数告诉Pei Foundtaion内存信息,并且通过设置SwitchStackSignal通知Pei Foundation 切换内存栈指针到永久内存。SwitchStackSignal这个值后面在Pei Dispatcher中会讲到。
EFI_STATUS
EFIAPI
PeiInstallPeiMemory (
IN CONST EFI_PEI_SERVICES **PeiServices,
IN EFI_PHYSICAL_ADDRESS MemoryBegin,
IN UINT64 MemoryLength
)
{
//将内存信息注册到Pei Foundation中
PrivateData->PhysicalMemoryBegin = MemoryBegin;
PrivateData->PhysicalMemoryLength = MemoryLength;
PrivateData->FreePhysicalMemoryTop = MemoryBegin + MemoryLength;
//设置SwitchStackSignal。
PrivateData->SwitchStackSignal = TRUE;
}
PeiAllocatePages用来分配连续的内存页,从FreeMemoryTop开始向下生长。
PeiAllocatePages (
IN CONST EFI_PEI_SERVICES **PeiServices,
IN EFI_MEMORY_TYPE MemoryType,
IN UINTN Pages,
OUT EFI_PHYSICAL_ADDRESS *Memory
)
{
...
PrivateData = PEI_CORE_INSTANCE_FROM_PS_THIS (PeiServices);
Hob.Raw = PrivateData->HobList.Raw;
...
if (!PrivateData->PeiMemoryInstalled && PrivateData->SwitchStackSignal) {
//如果永久内存被初始化了,从永久内存中分配内存
FreeMemoryTop = &(PrivateData->FreePhysicalMemoryTop);
FreeMemoryBottom = &(PrivateData->PhysicalMemoryBegin);
} else {
//否则从临时内存中分配内存
FreeMemoryTop = &(Hob.HandoffInformationTable->EfiFreeMemoryTop);
FreeMemoryBottom = &(Hob.HandoffInformationTable->EfiFreeMemoryBottom);
}
...
//跳过一些检查和error handle。将FreeMemoryTop减去要分配的内存页
*(FreeMemoryTop) -= Pages * EFI_PAGE_SIZE;
//返回Memory变量给caller,分配出来的内存页起始地址就是FreeMemoryTop
*Memory = *(FreeMemoryTop);
//为这段内存建立Hob信息
InternalBuildMemoryAllocationHob (
*(FreeMemoryTop),
Pages * EFI_PAGE_SIZE,
MemoryType
);
return EFI_SUCCESS;
...
}
VOID
InternalBuildMemoryAllocationHob (
IN EFI_PHYSICAL_ADDRESS BaseAddress,
IN UINT64 Length,
IN EFI_MEMORY_TYPE MemoryType
)
{
//先从HobList头查找有没有可重用的的MemoryAllocationHob。
MemoryAllocationHob = NULL;
Hob.Raw = GetFirstHob (EFI_HOB_TYPE_UNUSED);
while (Hob.Raw != NULL) {
if (Hob.Header->HobLength == sizeof (EFI_HOB_MEMORY_ALLOCATION)) {
//查到了有可重用的Hob就跳出循环
MemoryAllocationHob = (EFI_HOB_MEMORY_ALLOCATION *)Hob.Raw;
break;
}
//不然就继续查找
Hob.Raw = GET_NEXT_HOB (Hob);
Hob.Raw = GetNextHob (EFI_HOB_TYPE_UNUSED, Hob.Raw);
}
if (MemoryAllocationHob != NULL) {
//如果查到可重用的MemoryAllocationHob,就把分配这段内存信息填充到这个Hob中
MemoryAllocationHob->Header.HobType = EFI_HOB_TYPE_MEMORY_ALLOCATION;
ZeroMem (&(MemoryAllocationHob->AllocDescriptor.Name), sizeof (EFI_GUID));
MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress = BaseAddress;
MemoryAllocationHob->AllocDescriptor.MemoryLength = Length;
MemoryAllocationHob->AllocDescriptor.MemoryType = MemoryType;
ZeroMem (MemoryAllocationHob->AllocDescriptor.Reserved, sizeof (MemoryAllocationHob->AllocDescriptor.Reserved));
} else {
//如果没有可以重用的MemoryAllocationHob,那么就新建一个MemoryAllocationHob来记录这一段分配的内存
BuildMemoryAllocationHob (
BaseAddress,
Length,
MemoryType
);
}
}
PeiAllocatePool与PeiAllocatePages完全不同,是直接从Hob中分配MemoryPool类型的Hob出来。
typedef struct {
EFI_HOB_GENERIC_HEADER Header;
} EFI_HOB_MEMORY_POOL;
PeiAl
locatePool (
IN CONST EFI_PEI_SERVICES **PeiServices,
IN UINTN Size,
OUT VOID **Buffer
)
{
EFI_STATUS Status;
EFI_HOB_MEMORY_POOL *Hob;
//创建一个EFI_HOB_TYPE_MEMORY_POOL类型的HOB,
Status = PeiServicesCreateHob (
EFI_HOB_TYPE_MEMORY_POOL,
(UINT16)(sizeof (EFI_HOB_MEMORY_POOL) + Size),
(VOID **)&Hob
//因为EFI_HOB_MEMORY_POOL类型只有EFI_HOB_GENERIC_HEADER一个成员,所以Hob+1就是Header之后的实际数据
//所以分配的地址就是指向Hob的数据区域。
*Buffer = Hob + 1;
return Status;
}
如上图所示,PeiDispacther有两个循环,第一个循环是遍历所有的FV, 第二个循环是遍历FV中所有的PEIM,然后根据PEIM的Dependency进行加载。
PEIM必须是位置无关代码或者带有重定位信息。下图是一个PEI的布局。如果PEIM是位置相关代码,那么重定位信息被放在PE32+文件中。
最下面有一个可选段,是DEPEX。记录了该PEIM的Dependcy。PeiDispachter在加载PEIM之前会去检查该PEIM的Dependency,如果Dependency中的PEIM还未被加载,则该PEIM就需要等到所依赖的PEIM加载完成后再加载。
VOID
PeiDispatcher (
IN CONST EFI_SEC_PEI_HAND_OFF *SecCoreData,
IN PEI_CORE_INSTANCE *Private
)
{
...
do {
...
//第一个循环:遍历FFS中所有的FV
for (FvCount = Private->CurrentPeimFvCount; FvCount < Private->FvCount; FvCount++) {
CoreFvHandle = FindNextCoreFvHandle (Private, FvCount);
//判断这个FV的FvPpi是否是存在的,如果不存在,则没法加载,就跳过
if (CoreFvHandle->FvPpi == NULL)
continue;
Private->CurrentPeimFvCount = FvCount;
if (Private->CurrentPeimCount == 0) {
//去查看这个FV里面有没有Apriori文件,如果存在,则根据Apriori文件堆PEIM加载顺序将PEIM加载顺序排序。
DiscoverPeimsAndOrderWithApriori (Private, CoreFvHandle);
}
//第二个循环: 遍历FV中所有的PEIM。
for (PeimCount = Private->CurrentPeimCount;
PeimCount < Private->Fv[FvCount].PeimCount;
PeimCount++)
{
Private->CurrentPeimCount = PeimCount;
PeimFileHandle = Private->CurrentFileHandle = Private->CurrentFvFileHandles[PeimCount];
//如果该PEIM还没有被加载,则加载它
if (Private->Fv[FvCount].PeimState[PeimCount] == PEIM_STATE_NOT_DISPATCHED) {
//检查PEIM的Dependcy是否满足,如果不满足,则把PeimNeedingDispatch设置为TRUE,同时先不加载这个PEIM。
if (!DepexSatisfied (Private, PeimFileHandle, PeimCount)) {
Private->PeimNeedingDispatch = TRUE;
} else {
//这条路是PEIM满足Dependcy的情况,需要加载该PEIM
Status = CoreFvHandle->FvPpi->GetFileInfo (CoreFvHandle->FvPpi, PeimFileHandle, &FvFileInfo);
if (FvFileInfo.FileType == EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE) {
//如果是EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE文件,那么需要对它进行处理,比如解压缩再加载。
Status = ProcessFvFile (Private, &Private->Fv[FvCount], PeimFileHandle);
if (Status == EFI_SUCCESS) {
Private->Fv[FvCount].PeimState[PeimCount]++;
Private->PeimDispatchOnThisPass = TRUE;
} else {
Private->PeimNeedingDispatch = TRUE;
}
} else {
//否则则是PEIM类型的文件,则开始进行PEIM的加载
//第一步:调用PeiLoadImage加载Image并从中获取Image的EntryPoint
Status = PeiLoadImage (
PeiServices,
PeimFileHandle,
PEIM_STATE_NOT_DISPATCHED,
&EntryPoint,
&AuthenticationState
);
...
if (Status == EFI_SUCCESS) {
//第二步,校验PEIM,如果PEIM校验过了,才进行下一步,主要用来secure boot的
Status = VerifyPeim (Private, CoreFvHandle->FvHandle, PeimFileHandle, AuthenticationState);
if (Status != EFI_SECURITY_VIOLATION) {
Private->Fv[FvCount].PeimState[PeimCount]++;
PeimEntryPoint = (EFI_PEIM_ENTRY_POINT2)(UINTN)EntryPoint;
PeimEntryPoint (PeimFileHandle, (const EFI_PEI_SERVICES **)PeiServices);
Private->PeimDispatchOnThisPass = TRUE;
} else {
//校验不通过就直接跳出循环了,进行出错处理
Private->PeimNeedingDispatch = TRUE;
}
...
}
}
//该函数会检查永久内存是否已经安装,如果已经安装则会切换堆栈到永久内存上,并且第二次进入PeiCore
PeiCheckAndSwitchStack (SecCoreData, Private);
ProcessDispatchNotifyList (Private);
PeiCheckAndSwitchStack (SecCoreData, Private);
if ((Private->PeiMemoryInstalled) && (Private->Fv[FvCount].PeimState[PeimCount] == PEIM_STATE_REGISTER_FOR_SHADOW) && \
(PcdGetBool (PcdMigrateTemporaryRamFirmwareVolumes) ||
(Private->HobList.HandoffInformationTable->BootMode != BOOT_ON_S3_RESUME) ||
PcdGetBool (PcdShadowPeimOnS3Boot))
)
{
if ((Private->HobList.HandoffInformationTable->BootMode != BOOT_ON_S3_RESUME) && !PcdGetBool (PcdShadowPeimOnBoot) &&
!PcdGetBool (PcdMigrateTemporaryRamFirmwareVolumes))
{
//如果永久内存安装了,并且该PEIM的状态是PEIM_STATE_REGISTER_FOR_SHADOW的话,
//那么就需要重新加载PEIM到永久内存上并获取它的EntryPoint
Status = PeiLoadImage (
PeiServices,
PeimFileHandle,
PEIM_STATE_REGISTER_FOR_SHADOW,
&EntryPoint,
&AuthenticationState
);
if (Status == EFI_SUCCESS) {
PeimEntryPoint = (EFI_PEIM_ENTRY_POINT2)(UINTN)EntryPoint;
}
}
//调用PEIM的EntryPoint,一般来说会初始化该PEIM并且安装PEIM内的PPI Service。
PeimEntryPoint (PeimFileHandle, (const EFI_PEI_SERVICES **)PeiServices);
Private->Fv[FvCount].PeimState[PeimCount]++;
//处理一下新安装的PPI的Notify
ProcessDispatchNotifyList (Private);
}
}
}
}
//初始化临时变量进入下一次循环
Private->CurrentFileHandle = NULL;
Private->CurrentPeimCount = 0;
Private->CurrentFvFileHandles = NULL;
}
Private->CurrentPeimFvCount = 0;
} while (Private->PeimNeedingDispatch && Private->PeimDispatchOnThisPass);
}
PeiCheckAndSwitchStack检查SwitchStackSignal是否为TRUE,如果是TRUE,则把栈从临时内存上切换到永久内存上。在永久内存上的栈叫做NewStack,在临时内存上的栈叫做OldStack
PeiCheckAndSwitchStack (
IN CONST EFI_SEC_PEI_HAND_OFF *SecCoreData,
IN PEI_CORE_INSTANCE *Private
)
{
...
PeiServices = (CONST EFI_PEI_SERVICES **)&Private->Ps;
if (Private->SwitchStackSignal) {
UINT32 *StackPointer;
EFI_PEI_HOB_POINTERS Hob;
//计算栈顶地址
for ( StackPointer = (UINT32 *)SecCoreData->StackBase;
(StackPointer < (UINT32 *)((UINTN)SecCoreData->StackBase + SecCoreData->StackSize)) \
&& (*StackPointer == PcdGet32 (PcdInitValueInTempStack));
StackPointer++)
{
}
...
//计算NewStack的大小和栈顶地址
NewStackSize = RShiftU64 (Private->PhysicalMemoryLength, 1);
NewStackSize = ALIGN_VALUE (NewStackSize, EFI_PAGE_SIZE);
NewStackSize = MIN (PcdGet32 (PcdPeiCoreMaxPeiStackSize), NewStackSize);
TopOfOldStack = (UINTN)SecCoreData->StackBase + SecCoreData->StackSize;
TopOfNewStack = Private->PhysicalMemoryBegin + NewStackSize;
//计算NewStack相对于OldStack的偏移
if (TopOfNewStack >= TopOfOldStack) {
StackOffsetPositive = TRUE;
StackOffset = (UINTN)(TopOfNewStack - TopOfOldStack);
} else {
StackOffsetPositive = FALSE;
StackOffset = (UINTN)(TopOfOldStack - TopOfNewStack);
}
Private->StackOffsetPositive = StackOffsetPositive;
Private->StackOffset = StackOffset;
//查找gEfiTemporaryRamSupportPpiGuid用于搬运临时内存到永久内存上
Status = PeiServicesLocatePpi (
&gEfiTemporaryRamSupportPpiGuid,
0,
NULL,
(VOID **)&TemporaryRamSupportPpi
);
if (!EFI_ERROR (Status)) {
//计算堆的偏移
BaseOfNewHeap = TopOfNewStack;
if (BaseOfNewHeap >= (UINTN)SecCoreData->PeiTemporaryRamBase) {
Private->HeapOffsetPositive = TRUE;
Private->HeapOffset = (UINTN)(BaseOfNewHeap - (UINTN)SecCoreData->PeiTemporaryRamBase);
} else {
Private->HeapOffsetPositive = FALSE;
Private->HeapOffset = (UINTN)((UINTN)SecCoreData->PeiTemporaryRamBase - BaseOfNewHeap);
}
//使用gEfiTemporaryRamSupportPpiGuid->TemporaryRamMigration 将OldStack数据搬到NewStack中
TemporaryRamSupportPpi->TemporaryRamMigration (
PeiServices,
TemporaryRamBase,
(EFI_PHYSICAL_ADDRESS)(UINTN)(TopOfNewStack - TemporaryStackSize),
TemporaryRamSize
);
//把分配在临时内存上的Pages搬到永久内存上。
MigrateMemoryPages (Private, TRUE);
//第二次调用PeiCore
PeiCore (SecCoreData, NULL, Private);
} else {
...
//错误处理,这里不分析了,只分析正常流程
}
//
// Code should not come here
//
ASSERT (FALSE);
}
}
上一篇SEC分析 UEFI学习01-ARM AARCH64编译、ArmPlatformPriPeiCore(SEC)中分析了第一次进入PeiCore
PeiCheckAndSwitchStack 第二次进入PeiCore
而第三次PeiCore进入实际是在所有存在于临时内存的数据包括PeiCore自身被搬运到永久内存后。最终调用OldCoreData->ShadowedPeiCore (SecCoreData, PpiList, OldCoreData);跳转到永久内存上。
PeiCore (
IN CONST EFI_SEC_PEI_HAND_OFF *SecCoreDataPtr,
IN CONST EFI_PEI_PPI_DESCRIPTOR *PpiList,
IN VOID *Data
)
{
...
if (OldCoreData == NULL) {
...
//这是第一次进入PeiCore的路径
} else {
//这是第二次和第三次进入PeiCore的路径
if (OldCoreData->ShadowedPeiCore == NULL) {
//ShadowedPeiCore = NULL说明是第二次进入PeiCore
OldCoreData->Ps = &OldCoreData->ServiceTableShadow;
OldCoreData->CpuIo = &OldCoreData->ServiceTableShadow.CpuIo;
//修正_PEI_CORE_INSTANCE 中的各个指针的地址,如下面节选的这些即是将HobList,Ppi指针,Fv指针从临时内存中加上偏移,指向永久内存中的HobList
OldCoreData->HobList.Raw = (VOID *)(OldCoreData->HobList.Raw + OldCoreData->HeapOffset);
...
OldCoreData->PpiData.DispatchNotifyList.NotifyPtrs = (PEI_PPI_LIST_POINTERS *)((UINT8 *)OldCoreData->PpiData.DispatchNotifyList.NotifyPtrs + OldCoreData->HeapOffset);
OldCoreData->Fv = (PEI_CORE_FV_HANDLE *)((UINT8 *)OldCoreData->Fv + OldCoreData->HeapOffset);
//更新FV中PEIM的状态
for (Index = 0; Index < OldCoreData->FvCount; Index++) {
if (OldCoreData->Fv[Index].PeimState != NULL) {
OldCoreData->Fv[Index].PeimState = (UINT8 *)OldCoreData->Fv[Index].PeimState + OldCoreData->HeapOffset;
}
if (OldCoreData->Fv[Index].FvFileHandles != NULL) {
OldCoreData->Fv[Index].FvFileHandles = (EFI_PEI_FILE_HANDLE *)((UINT8 *)OldCoreData->Fv[Index].FvFileHandles + OldCoreData->HeapOffset);
}
}
OldCoreData->TempFileGuid = (EFI_GUID *)((UINT8 *)OldCoreData->TempFileGuid + OldCoreData->HeapOffset);
OldCoreData->TempFileHandles = (EFI_PEI_FILE_HANDLE *)((UINT8 *)OldCoreData->TempFileHandles + OldCoreData->HeapOffset);
...
//根据物理内存更新HandOffHob里的参数
HandoffInformationTable = OldCoreData->HobList.HandoffInformationTable;
HandoffInformationTable->EfiEndOfHobList = HandoffInformationTable->EfiEndOfHobList + OldCoreData->HeapOffset;
HandoffInformationTable->EfiMemoryTop = OldCoreData->PhysicalMemoryBegin + OldCoreData->PhysicalMemoryLength;
HandoffInformationTable->EfiMemoryBottom = OldCoreData->PhysicalMemoryBegin;
HandoffInformationTable->EfiFreeMemoryTop = OldCoreData->FreePhysicalMemoryTop;
HandoffInformationTable->EfiFreeMemoryBottom = HandoffInformationTable->EfiEndOfHobList + sizeof (EFI_HOB_GENERIC_HEADER);
//将MemoryAllocationHob中的参数进行修正到永久内存中
ConvertMemoryAllocationHobs (OldCoreData);
//将Ppi指针进行修正到永久内存中
ConvertPpiPointers (SecCoreData, OldCoreData);
OldCoreData->PeiMemoryInstalled = TRUE;
OldCoreData->PeimDispatcherReenter = TRUE;
OldCoreData->ShadowedPeiCore = (PEICORE_FUNCTION_POINTER)(UINTN)PeiCore;
if (PcdGetBool (PcdMigrateTemporaryRamFirmwareVolumes) ||
((HandoffInformationTable->BootMode == BOOT_ON_S3_RESUME) && PcdGetBool (PcdShadowPeimOnS3Boot)) ||
((HandoffInformationTable->BootMode != BOOT_ON_S3_RESUME) && PcdGetBool (PcdShadowPeimOnBoot)))
{
//将PeiCore从临时内存搬到永久内存并返回shadow后的PeiCore地址
OldCoreData->ShadowedPeiCore = ShadowPeiCore (OldCoreData);
}
//第三次跳转到永久内存上的PeiCore
OldCoreData->ShadowedPeiCore (SecCoreData, PpiList, OldCoreData);
ASSERT (FALSE);
CpuDeadLoop ();
UNREACHABLE ();
}
...
}
再来看QEMU_EFI.FD的布局,这里面一共有6个PEIM。其中PcdPeim, MemoryInit和DxeIpl跟PeiCore的流程相关,CpuPei和PlatfromPei和硬件平台相关, PeiVariablePei在这里没用到。这里就挑三个和PeiCore流程相关的PEIM来分析。
edk2/MdeModulePkg/Universal/PCD/Pei
PcdPeim在这个FV中是第一个运行的PEIM,其他的PEIM都依赖于PcdPeim。PcdPeim提供了一个访问动态PCD的接口。从Pcd.inf中可以看到PcdPeim的EntryPoint是PcdPeimInit
PCD_IS_DRIVER = PEI_PCD_DRIVER
ENTRY_POINT = PcdPeimInit
PcdPeimInit实现很简单,第一就是建立了PCD Database,第二就是注册了PCD的PPI提供给其他PEIM使用。PcdPeimInit在PEI阶段会重入,这里不考虑重入的部分,就考虑第一次初始化的时候。
PcdPeimInit (
IN EFI_PEI_FILE_HANDLE FileHandle,
IN CONST EFI_PEI_SERVICES **PeiServices
)
{
//建立Pcd Database
BuildPcdDatabase (FileHandle);
//安装两个PCD PPI gPcdPpiGuid和gGetPcdInfoPpiGuid
Status = PeiServicesInstallPpi (&mPpiList[0]);
Status = PeiServicesInstallPpi (&mPpiList2[0]);
//注册回调函数和通知
Status = PeiServicesNotifyPpi (&mEndOfPeiSignalPpiNotifyList[0]);
Status = PeiRegisterCallBackOnSet (
&gEfiMdeModulePkgTokenSpaceGuid,
PcdToken (PcdSetNvStoreDefaultId),
PcdSetNvStoreDefaultIdCallBack
);
return Status;
}
BuildPcdDatabase 从FV文件中把Pcd的二进制文件读出来,然后开辟一段空间,将读出来的二进制复制到这段内存中。这段内存就叫做PCD Database。
PEI_PCD_DATABASE *
BuildPcdDatabase (
IN EFI_PEI_FILE_HANDLE FileHandle
)
{
//获取PCD Database在FV上的指针
PeiPcdDbBinary = LocateExPcdBinary (FileHandle);
//创建一个HOB用于PCD Database
Database = BuildGuidHob (&gPcdDataBaseHobGuid, PeiPcdDbBinary->Length + PeiPcdDbBinary->UninitDataBaseSize);
ZeroMem (Database, PeiPcdDbBinary->Length + PeiPcdDbBinary->UninitDataBaseSize);
//把Pcd从FV中复制到开辟的PCD Database中
CopyMem (Database, PeiPcdDbBinary, PeiPcdDbBinary->Length);
//同样的操作把PCD中的callback来一遍。
SizeOfCallbackFnTable = Database->LocalTokenCount * sizeof (PCD_PPI_CALLBACK) * PcdGet32 (PcdMaxPeiPcdCallBackNumberPerPcdEntry);
CallbackFnTable = BuildGuidHob (&gEfiCallerIdGuid, SizeOfCallbackFnTable);
ZeroMem (CallbackFnTable, SizeOfCallbackFnTable);
return Database;
}
再来看gPcdPpiGuid对应PPI其实就是一组设置和获取PCD的接口。
EFI_PEI_PPI_DESCRIPTOR mPpiList[] = {
{
EFI_PEI_PPI_DESCRIPTOR_PPI,
&gPcdPpiGuid,
&mPcdPpiInstance
},
....
PCD_PPI mPcdPpiInstance = {
PeiPcdSetSku,
PeiPcdGet8,
...
PeiPcdGet8Ex,
...
PeiPcdSet8,
...
PeiPcdSet8Ex,
...
PeiRegisterCallBackOnSet,
PcdUnRegisterCallBackOnSet,
PeiPcdGetNextToken,
PeiPcdGetNextTokenSpace
};
当PcdPeim被加载完之后,PEI阶段其他的PEIM就可以使用其接口对动态PCD进行读写了。这里不对PcdPeim的读写进行展开,原理不复杂,网上也有很多分析PcdPeim的读写实现。
edk2/ArmPlatformPkg/MemoryInitPei
真实的芯片上在这之前应该要初始化DDR Controller/PHY和DDR内存。但在qemu上不需要,所以进到MemoryInitPei中。MemoryInitPei从PCD上读出内存相关的信息:起始地址和长度然后上报给Pei Foundation,然后建立相关的HOB以及初始化MMU。
InitializeMemory (
IN EFI_PEI_FILE_HANDLE FileHandle,
IN CONST EFI_PEI_SERVICES **PeiServices
)
{
//从PCD读出内存的基地址和长度
SystemMemoryBase = (UINTN)PcdGet64 (PcdSystemMemoryBase);
SystemMemoryTop = SystemMemoryBase + PcdGet64 (PcdSystemMemorySize);
//从PCD读出QEMU_EFI.FD的基地址和长度
FdBase = (UINTN)PcdGet64 (PcdFdBaseAddress);
FdTop = FdBase + (UINTN)PcdGet32 (PcdFdSize);
...
//计算出UefiMemoryBase的地址,UefiMemory位于内存的最上端
UefiMemoryBase = SystemMemoryTop - FixedPcdGet32 (PcdSystemMemoryUefiRegionSize);
...
//调用PeiServicesInstallPeiMemory 向PeiCore注册Uefi所使用的内存地址和长度
//PeiServicesInstallPeiMemory最终会调用到PeiInstallPeiMemory,这个函数之前讲过, 会把内存信息填到HandOffHob中然后
//把SwitchStackSignal设置为TRUE
Status = PeiServicesInstallPeiMemory (UefiMemoryBase, FixedPcdGet32 (PcdSystemMemoryUefiRegionSize));
//MemoryPeim 建立内存相关的Hob并且初始化MMU。
Status = MemoryPeim (UefiMemoryBase, FixedPcdGet32 (PcdSystemMemoryUefiRegionSize));
...
}
edk2/ArmVirtPkg/Library/ArmVirtMemoryInitPeiLib/ArmVirtMemoryInitPeiLib.c
MemoryPeim (
IN EFI_PHYSICAL_ADDRESS UefiMemoryBase,
IN UINT64 UefiMemorySize
)
{
ResourceAttributes = (
EFI_RESOURCE_ATTRIBUTE_PRESENT |
EFI_RESOURCE_ATTRIBUTE_INITIALIZED |
EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE |
EFI_RESOURCE_ATTRIBUTE_TESTED
);
SystemMemoryTop = PcdGet64 (PcdSystemMemoryBase) +
PcdGet64 (PcdSystemMemorySize);
...
//创建EFI_RESOURCE_SYSTEM_MEMORY 类型的RESOURCE HOB块
BuildResourceDescriptorHob (
EFI_RESOURCE_SYSTEM_MEMORY,
ResourceAttributes,
PcdGet64 (PcdSystemMemoryBase),
PcdGet64 (PcdSystemMemorySize)
);
//把Uefi用到的内存 的DCache Invalidu一下
InvalidateDataCacheRange ((VOID *)(UINTN)UefiMemoryBase, UefiMemorySize);
// 初始化MMU
InitMmu ();
...
}
InitMmu (
VOID
)
{
...
// 获取物理地址和虚拟地址,这里edk2采用了平坦映射,因此物理地址等于虚拟地址。
ArmVirtGetMemoryMap (&MemoryTable);
//配置页表并使能MMU。从此就可以使用dcache。
Status = ArmConfigureMmu (MemoryTable, &TranslationTableBase, &TranslationTableSize);
...
}
到这里,MemoryInit PEIM就初始化完了。当MemoryInit初始化完成,SwitchStackSignal被设置成1,就意味着DDR内存可以使用了。这个时候PeiDispacther就会把所有在临时内存上的数据全部搬到永久内存上去。下图是
原始memorymap
初始化完memory后