DXE ( Driver Execution Environment)阶段执行大部分系统初始化工作,进人此阶段时, 内存已经可以被完全使用,因而此阶段可以进行大量的复杂工作。从程序设计的角度讲, DXE阶段与PEI阶段相似,由于最终的OS 是64位的,所以要引导和启动OS就需要相应的64位环境同时从该阶段开始memory可用空间变多(不局限4G以内),可以使用更好的访问模式、启动性能。
DXE阶段及后面的程序在内存中执行
主要含有以下概念:
DxeMain (),是DXE阶段执行的主函数,同时以参数的形式接收PEI阶段生成的HOB表。
typedef
EFI_STATUS
(EFIAPI *EFI_IMAGE_ENTRY_POINT)(
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
);
System Table包含了以下子结构体:
Non-Driver Model特征:
Driver Model特征:
Protocols由一组数据结构和一个GUID组成,安装在handle上。可以参考:https://blog.csdn.net/weixin_45279063/article/details/116268076
Handle, 也叫对象(Object), 其本质是一个VOID型指针, 是protocol的载体
几乎所有的硬件的初始化都在DXE这做完,同时产生efi system table, 来提供各种service供所面阶段使用。最后把控制权交给bds来boot os.
这些Protocol在UEFI BIOS运行的过程中会安装,且一定需要被安装,如果没有被安装的话,系统就会报错。
//
// DXE Core Global Variables for all of the Architectural Protocols.
// If a protocol is installed mArchProtocols[].Present will be TRUE.
//
// CoreNotifyOnArchProtocolInstallation () fills in mArchProtocols[].Event
// and mArchProtocols[].Registration as it creates events for every array
// entry.
//
EFI_CORE_PROTOCOL_NOTIFY_ENTRY mArchProtocols[] = {
{ &gEfiSecurityArchProtocolGuid, (VOID **)&gSecurity, NULL, NULL, FALSE },
{ &gEfiCpuArchProtocolGuid, (VOID **)&gCpu, NULL, NULL, FALSE },
{ &gEfiMetronomeArchProtocolGuid, (VOID **)&gMetronome, NULL, NULL, FALSE },
{ &gEfiTimerArchProtocolGuid, (VOID **)&gTimer, NULL, NULL, FALSE },
{ &gEfiBdsArchProtocolGuid, (VOID **)&gBds, NULL, NULL, FALSE },
{ &gEfiWatchdogTimerArchProtocolGuid, (VOID **)&gWatchdogTimer, NULL, NULL, FALSE },
{ &gEfiRuntimeArchProtocolGuid, (VOID **)&gRuntime, NULL, NULL, FALSE },
{ &gEfiVariableArchProtocolGuid, (VOID **)NULL, NULL, NULL, FALSE },
{ &gEfiVariableWriteArchProtocolGuid, (VOID **)NULL, NULL, NULL, FALSE },
{ &gEfiCapsuleArchProtocolGuid, (VOID **)NULL, NULL, NULL, FALSE },
{ &gEfiMonotonicCounterArchProtocolGuid, (VOID **)NULL, NULL, NULL, FALSE },
{ &gEfiResetArchProtocolGuid, (VOID **)NULL, NULL, NULL, FALSE },
{ &gEfiRealTimeClockArchProtocolGuid, (VOID **)NULL, NULL, NULL, FALSE },
{ NULL, (VOID **)NULL, NULL, NULL, FALSE }
};
typedef struct _EFI_METRONOME_ARCH_PROTOCOL {
EFI_METRONOME_WAIT_FOR_TICK WaitForTIck;
UINT32 TickPeriod;
} EFI_METRONOME_ARCH_PROTOCOL;
提供一个微小的延时,百万分之一秒为单位。
CoreInstallConfigurationTable(
IN EFI_GUID *Guid,
IN VOID *Table
);
VOID
EFIAPI
DxeMain (
IN VOID *HobStart
)
{
EFI_STATUS Status;
EFI_PHYSICAL_ADDRESS MemoryBaseAddress;
UINT64 MemoryLength;
PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;
UINTN Index;
EFI_HOB_GUID_TYPE *GuidHob;
EFI_VECTOR_HANDOFF_INFO *VectorInfoList;
EFI_VECTOR_HANDOFF_INFO *VectorInfo;
VOID *EntryPoint;
//
// Setup the default exception handlers
// 设置默认的异常处理程序
VectorInfoList = NULL;
GuidHob = GetNextGuidHob (&gEfiVectorHandoffInfoPpiGuid, HobStart);
if (GuidHob != NULL) {
VectorInfoList = (EFI_VECTOR_HANDOFF_INFO *) (GET_GUID_HOB_DATA(GuidHob));
}
// 初始化所有CPU异常项,并提供默认的异常处理程序。调用者应该尝试获取一个正在使用的中断和/或异常向量数组,这些向量需要在PI 1.3规范中定义的
// EFI_VECTOR_HANDOFF_INFO中保存。如果调用者不能得到保留的向量列表或者它不存在,将VectorInfo设置为NULL。
// 如果VectorInfo不为空,则每个vector属性都会相应地初始化异常向量。
Status = InitializeCpuExceptionHandlers (VectorInfoList);
ASSERT_EFI_ERROR (Status);
//
// Initialize Debug Agent to support source level debug in DXE phase
// 此函数用于设置调试环境,以支持源代码级的调试。如果某些调试代理库实例有一些私人数据保存在堆栈中,这个函数必须工作模式不返回给调用者,
// 然后调用者需要结束后所有其他逻辑InitializeDebugAgent()成一个函数并将其传递到InitializeDebugAgent()。
// InitializeDebugAgent()负责在InitializeDebugAgent()的末尾调用传入函数。
// 如果参数函数不为空,调试代理库实例将通过在上下文中传递参数来调用它。如果Function()为空,调试代理库实例将在安装调试环境后返回。
InitializeDebugAgent (DEBUG_AGENT_INIT_DXE_CORE, HobStart, NULL);
//
// Initialize Memory Services
// 初始化内存服务
// 外部函数。基于内存描述符HOBs初始化内存服务。这个函数负责启动内存映射,因此可以进行内存分配和资源分配。
// 这个函数的第一部分不能依赖任何内存服务,直到至少有一个内存描述符提供给内存服务。
CoreInitializeMemoryServices (&HobStart, &MemoryBaseAddress, &MemoryLength);
// 初始化内存概要文件。
MemoryProfileInit (HobStart);
//
// Allocate the EFI System Table and EFI Runtime Service Table from EfiRuntimeServicesData
// Use the templates to initialize the contents of the EFI System Table and EFI Runtime Services Table
// 调用内存服务,为系统表和运行时服务申请内存空间
gDxeCoreST = AllocateRuntimeCopyPool (sizeof (EFI_SYSTEM_TABLE), &mEfiSystemTableTemplate);
ASSERT (gDxeCoreST != NULL);
gDxeCoreRT = AllocateRuntimeCopyPool (sizeof (EFI_RUNTIME_SERVICES), &mEfiRuntimeServicesTableTemplate);
ASSERT (gDxeCoreRT != NULL);
gDxeCoreST->RuntimeServices = gDxeCoreRT;
//
// Start the Image Services.
// 初始化镜像服务,将映像服务添加到EFI引导服务表中,并为该映像安装协议接口。
Status = CoreInitializeImageServices (HobStart);
ASSERT_EFI_ERROR (Status);
//
// Initialize the Global Coherency Domain Services
// 初始化全局配置数据库服务
// 外部函数。基于内存描述符HOBs初始化GCD和内存服务。这个函数负责启动GCD映射和内存映射,
// 因此可以进行内存分配和资源分配。 HobStart将被重新定位到池缓冲区中。
Status = CoreInitializeGcdServices (&HobStart, MemoryBaseAddress, MemoryLength);
ASSERT_EFI_ERROR (Status);
//
// Call constructor for all libraries
// 调用所有库模块的构造函数,该函数由预处理的python脚本生成,位于
// edk2/Build/${CompiledPkgName}/DEBUG_GCC44/${ARCH}/MdeModulePkg/Core/Dxe/DxeMain/DEBUG 目录下的AutoGen.c 文件中。
// 根据由 inf 文件可知DXE模块所需的库,同时自动生成构造函数 ProcessLibraryConstructorList ()
ProcessLibraryConstructorList (gDxeCoreImageHandle, gDxeCoreST);
PERF_END (NULL,"PEI", NULL, 0) ;
PERF_START (NULL,"DXE", NULL, 0) ;
//
// Report DXE Core image information to the PE/COFF Extra Action Library
//
ZeroMem (&ImageContext, sizeof (ImageContext));
ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)gDxeCoreLoadedImage->ImageBase;
// 返回一个指向PE/COFF映像的PDB文件名的指针,该映像已经通过PE/COFF加载器库函数加载到系统内存中。返回Pe32Data指定的PE/COFF图像的PDB文件名。
// 如果Pe32Data指定的PE/COFF图像不是有效的,则返回NULL。如果Pe32Data指定的PE/COFF映像不包含调试目录项,则返回NULL。如果Pe32Data指定的
// PE/COFF图像中的调试目录项不包含PDB文件名,则返回NULL。如果Pe32Data为空,则ASSERT()。
ImageContext.PdbPointer = PeCoffLoaderGetPdbPointer ((VOID*)(UINTN)ImageContext.ImageAddress);
// 返回由Pe32Data指定的PE/COFF头的大小。如果Pe32Data为空,则ASSERT()。
ImageContext.SizeOfHeaders = PeCoffGetSizeOfHeaders ((VOID*)(UINTN)ImageContext.ImageAddress);
// 检索并返回一个指向PE/COFF图像入口点的指针,该图像已经通过PE/COFF加载器库函数加载到系统内存中。检索Pe32Data指定的PE/COFF图像的入口点,
// 并在EntryPoint中返回这个入口点。如果无法从PE/COFF图像中检索入口点,则返回RETURN_INVALID_PARAMETER。否则返回RETURN_SUCCESS。
// 如果Pe32Data为空,则ASSERT()。如果EntryPoint为NULL,则ASSERT()。
Status = PeCoffLoaderGetEntryPoint ((VOID*)(UINTN)ImageContext.ImageAddress, &EntryPoint);
if (Status == EFI_SUCCESS) {
ImageContext.EntryPoint = (EFI_PHYSICAL_ADDRESS)(UINTN)EntryPoint;
}
ImageContext.Handle = (VOID *)(UINTN)gDxeCoreLoadedImage->ImageBase;
ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory;
// 加载和重新定位PE/COFF映像后执行其他操作。如果ImageContext为NULL,则ASSERT()。
PeCoffLoaderRelocateImageExtraAction (&ImageContext);
//
// Install the DXE Services Table into the EFI System Tables's Configuration Table
// 将DXE Services表安装到EFI系统表的配置表中,函数启动服务,用于从EFI系统表中添加、修改或删除系统配置表。
Status = CoreInstallConfigurationTable (&gEfiDxeServicesTableGuid, gDxeCoreDS);
ASSERT_EFI_ERROR (Status);
//
// Install the HOB List into the EFI System Tables's Configuration Table
// 将HOB列表安装到EFI系统表的配置表中,
Status = CoreInstallConfigurationTable (&gEfiHobListGuid, HobStart);
ASSERT_EFI_ERROR (Status);
//
// Install Memory Type Information Table into the EFI System Tables's Configuration Table
// 将内存类型信息表安装到EFI系统表的配置表中
Status = CoreInstallConfigurationTable (&gEfiMemoryTypeInformationGuid, &gMemoryTypeInformation);
ASSERT_EFI_ERROR (Status);
//
// If Loading modules At fixed address feature is enabled, install Load moduels at fixed address
// Configuration Table so that user could easily to retrieve the top address to load Dxe and PEI
// Code and Tseg base to load SMM driver.
// 如果在固定地址功能加载模块,安装加载模块在固定地址配置表,这样用户可以很容易地检索顶部地址加载Dxe和PEI代码和Tseg base加载SMM驱动程序。
if (PcdGet64(PcdLoadModuleAtFixAddressEnable) != 0) {
Status = CoreInstallConfigurationTable (&gLoadFixedAddressConfigurationTableGuid, &gLoadModuleAtFixAddressConfigurationTable);
ASSERT_EFI_ERROR (Status);
}
//
// Report Status Code here for DXE_ENTRY_POINT once it is available
// 在这里报告DXE_ENTRY_POINT可用的状态代码。 如果状态代码类型已启用,则报告带有最小参数的状态代码。
// 如果由type指定的状态码类型在PcdReportStatusCodeProperyMask中启用,然后调用ReportStatusCode()传入类型和值。
REPORT_STATUS_CODE (
EFI_PROGRESS_CODE,
(EFI_SOFTWARE_DXE_CORE | EFI_SW_DXE_CORE_PC_ENTRY_POINT)
);
//
// Create the aligned system table pointer structure that is used by external
// debuggers to locate the system table... Also, install debug image info configuration table.
// 创建对齐的系统表指针结构,外部调试器使用它来定位系统表…另外,安装调试图像信息配置表。
// 创建并初始化DebugImageInfo表。还创建配置表并将其注册到系统表中。
CoreInitializeDebugImageInfoTable ();
// 在DebugImageInfo表中添加一个新的DebugImageInfo结构。如果表的大小不足以容纳另一个entry,则重新分配该表。
CoreNewDebugImageInfoEntry (
EFI_DEBUG_IMAGE_INFO_TYPE_NORMAL,
gDxeCoreLoadedImage,
gDxeCoreImageHandle
);
DEBUG ((DEBUG_INFO | DEBUG_LOAD, "HOBLIST address in DXE = 0x%p\n", HobStart));
DEBUG_CODE_BEGIN ();
EFI_PEI_HOB_POINTERS Hob;
for (Hob.Raw = HobStart; !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));
}
}
for (Hob.Raw = HobStart; !END_OF_HOB_LIST(Hob); Hob.Raw = GET_NEXT_HOB(Hob)) {
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));
} else 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));
}
}
DEBUG_CODE_END ();
//
// Initialize the Event Services
//
Status = CoreInitializeEventServices ();
ASSERT_EFI_ERROR (Status);
// 安装内存配置文件协议。
MemoryProfileInstallProtocol ();
// 初始化PropertiesTable支持。
CoreInitializePropertiesTable ();
// 初始化MemoryAttrubutesTable支持。
CoreInitializeMemoryAttributesTable ();
// 初始化内存保护支持。
CoreInitializeMemoryProtection ();
//
// Get persisted vector hand-off info from GUIDeed HOB again due to HobStart may be updated,
// and install configuration table
// 由于HobStart可能会被更新,所以再次从guided HOB获取持久化的矢量切换信息,并安装配置表
GuidHob = GetNextGuidHob (&gEfiVectorHandoffInfoPpiGuid, HobStart);
if (GuidHob != NULL) {
VectorInfoList = (EFI_VECTOR_HANDOFF_INFO *) (GET_GUID_HOB_DATA(GuidHob));
VectorInfo = VectorInfoList;
Index = 1;
while (VectorInfo->Attribute != EFI_VECTOR_HANDOFF_LAST_ENTRY) {
VectorInfo ++;
Index ++;
}
VectorInfo = AllocateCopyPool (sizeof (EFI_VECTOR_HANDOFF_INFO) * Index, (VOID *) VectorInfoList);
ASSERT (VectorInfo != NULL);
// 启动服务,用于从EFI系统表中添加、修改或删除系统配置表。
Status = CoreInstallConfigurationTable (&gEfiVectorHandoffTableGuid, (VOID *) VectorInfo);
ASSERT_EFI_ERROR (Status);
}
//
// Get the Protocols that were passed in from PEI to DXE through GUIDed HOBs
//
// These Protocols are not architectural. This implementation is sharing code between
// PEI and DXE in order to save FLASH space. These Protocols could also be implemented
// as part of the DXE Core. However, that would also require the DXE Core to be ported
// each time a different CPU is used, a different Decompression algorithm is used, or a
// different Image type is used. By placing these Protocols in PEI, the DXE Core remains
// generic, and only PEI and the Arch Protocols need to be ported from Platform to Platform,
// and from CPU to CPU.
// 获得从PEI传递到DXE通过GUIDed HOBs的协议。这些协议不是架构性的。这个实现是在PEI和DXE之间共享代码,以节省闪存空间。
// 这些协议也可以作为DXE核心的一部分来实现。但是,在每次使用不同的CPU、不同的解压缩算法或不同的映像类型时,这也需要移植DXE内核。
// 通过将这些协议放在PEI中,DXE核心仍然是通用的,只有PEI和Arch协议需要从一个平台移植到另一个平台,从一个CPU移植到另一个CPU。
//
// Publish the EFI, Tiano, and Custom Decompress protocols for use by other DXE components
// 发布EFI、Tiano和自定义解压缩协议,供其他DXE组件使用
// 在引导服务环境中安装协议接口列表。这个函数在循环中调用InstallProtocolInterface()。如果出现任何错误,
// 此函数添加的所有协议都将被删除。这基本上是一个节省空间的lib函数。
Status = CoreInstallMultipleProtocolInterfaces (
&mDecompressHandle,
&gEfiDecompressProtocolGuid, &gEfiDecompress,
NULL
);
ASSERT_EFI_ERROR (Status);
//
// Register for the GUIDs of the Architectural Protocols, so the rest of the
// EFI Boot Services and EFI Runtime Services tables can be filled in.
// Also register for the GUIDs of optional protocols.
// 注册体系结构协议的guid,这样就可以填充EFI引导服务和EFI运行时服务表的其余部分。还要注册可选协议的guid。
// 该函数为架构协议和可选协议创建一个事件,每次安装特定类型的协议时触发该事件。
CoreNotifyOnProtocolInstallation ();
//
// Produce Firmware Volume Protocols, one for each FV in the HOB list.
// 生产固件卷协议,为HOB列表中的每个FV提供一个。
// 这个例程使用FV hobs,并根据需要生成FW_VOL_BLOCK_PROTOCOL实例。
Status = FwVolBlockDriverInit (gDxeCoreImageHandle, gDxeCoreST);
ASSERT_EFI_ERROR (Status);
// 这个例程是驱动程序初始化入口点。它注册一个通知功能。这个通知函数负责动态地构建FV堆栈。
Status = FwVolDriverInit (gDxeCoreImageHandle, gDxeCoreST);
ASSERT_EFI_ERROR (Status);
//
// Produce the Section Extraction Protocol
// 该函数 节提取代码的入口点。初始化节提取接口的实例,并将其安装到一个新的句柄上。
Status = InitializeSectionExtraction (gDxeCoreImageHandle, gDxeCoreST);
ASSERT_EFI_ERROR (Status);
//
// Initialize the DXE Dispatcher
// 初始化DXE调度程序
PERF_START (NULL,"CoreInitializeDispatcher", "DxeMain", 0) ;
// 初始化分配器。初始化FV2协议加入系统时运行的通知函数。
CoreInitializeDispatcher ();
PERF_END (NULL,"CoreInitializeDispatcher", "DxeMain", 0) ;
//
// Invoke the DXE Dispatcher
//
PERF_START (NULL, "CoreDispatcher", "DxeMain", 0);
// 这是DXE的主调度程序,当没有更多的驱动程序需要运行时,它就会退出。释放mScheduledQueue,为每个驱动加载并启动一个PE映像。
// 搜索mDiscoveredList,查看是否可以将任何驱动程序放置在mScheduledQueue上。如果没有在mScheduledQueue上放置驱动程序,
// 则退出该函数。退出时,假设Bds()将被调用,当Bds()退出时,调度程序将被再次调用。
CoreDispatcher ();
PERF_END (NULL, "CoreDispatcher", "DxeMain", 0);
//
// Display Architectural protocols that were not loaded if this is DEBUG build
// 如果是调试构建,则显示未加载的架构协议
DEBUG_CODE_BEGIN ();
// 显示未加载的、DXE内核运行所需的架构协议。仅在调试构建中使用。
CoreDisplayMissingArchProtocols ();
DEBUG_CODE_END ();
//
// Display any drivers that were not dispatched because dependency expression
// evaluated to false if this is a debug build
// 如果这是一个调试构建,显示任何没有被分派的驱动程序,因为依赖表达式计算为false
DEBUG_CODE_BEGIN ();
// 遍历已发现的列表,查找任何已发现但未加载的驱动程序,因为依赖表达式被评估为false。
CoreDisplayDiscoveredNotDispatched ();
DEBUG_CODE_END ();
//
// Assert if the Architectural Protocols are not present.
// 如果体系结构协议不存在,则断言。
// 该函数如果所有AP服务可用则返回TRUE。
Status = CoreAllEfiServicesAvailable ();
if (EFI_ERROR(Status)) {
//
// Report Status code that some Architectural Protocols are not present.
// 报告一些架构协议不存在的状态代码。
REPORT_STATUS_CODE (
EFI_ERROR_CODE | EFI_ERROR_MAJOR,
(EFI_SOFTWARE_DXE_CORE | EFI_SW_DXE_CORE_EC_NO_ARCH)
);
}
ASSERT_EFI_ERROR (Status);
//
// Report Status code before transfer control to BDS
// 向BDS移交控制前报告状态码
REPORT_STATUS_CODE (
EFI_PROGRESS_CODE,
(EFI_SOFTWARE_DXE_CORE | EFI_SW_DXE_CORE_PC_HANDOFF_TO_NEXT)
);
//
// Transfer control to the BDS Architectural Protocol
// 传输控制到BDS架构协议
gBds->Entry (gBds);
//
// BDS should never return
//
ASSERT (FALSE);
CpuDeadLoop ();
UNREACHABLE ();
}
可以参考https://blog.csdn.net/robinsongsog/article/details/109003375