前文提到了在 PEI 阶段的末尾,通过下面的代码调用 DxeIpl 进入 DXE 中。
//
// Enter DxeIpl to load Dxe core.
//
DEBUG ((EFI_D_INFO, "DXE IPL Entry\n"));
Status = TempPtr.DxeIpl->Entry (
TempPtr.DxeIpl,
&PrivateData.Ps,
PrivateData.HobList
);
定义在 PeiMain.c 中
PEI_CORE_TEMP_POINTERS TempPtr;
PEI_CORE_TEMP_POINTERS 定义如下:
///
/// Union of temporarily used function pointers (to save stack space)
///
typedef union {
PEICORE_FUNCTION_POINTER PeiCore;
EFI_PEIM_ENTRY_POINT2 PeimEntry;
EFI_PEIM_NOTIFY_ENTRY_POINT PeimNotifyEntry;
EFI_DXE_IPL_PPI *DxeIpl;
EFI_PEI_PPI_DESCRIPTOR *PpiDescriptor;
EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor;
VOID *Raw;
} PEI_CORE_TEMP_POINTERS;
EFI_DXE_IPL_PPI 定义如下:
///
/// Final service to be invoked by the PEI Foundation.
/// The DXE IPL PPI is responsible for locating and loading the DXE Foundation.
/// The DXE IPL PPI may use PEI services to locate and load the DXE Foundation.
///
struct _EFI_DXE_IPL_PPI {
EFI_DXE_IPL_ENTRY Entry;
};
/**
The architectural PPI that the PEI Foundation invokes when
there are no additional PEIMs to invoke.
This function is invoked by the PEI Foundation.
The PEI Foundation will invoke this service when there are
no additional PEIMs to invoke in the system.
If this PPI does not exist, it is an error condition and
an ill-formed firmware set. The DXE IPL PPI should never
return after having been invoked by the PEI Foundation.
The DXE IPL PPI can do many things internally, including the following:
- Invoke the DXE entry point from a firmware volume
- Invoke the recovery processing modules
- Invoke the S3 resume modules
@param This Pointer to the DXE IPL PPI instance
@param PeiServices Pointer to the PEI Services Table.
@param HobList Pointer to the list of Hand-Off Block (HOB) entries.
@retval EFI_SUCCESS Upon this return code, the PEI Foundation should enter
some exception handling.Under normal circumstances,
the DXE IPL PPI should not return.
**/
typedef
EFI_STATUS
(EFIAPI *EFI_DXE_IPL_ENTRY)(
IN CONST EFI_DXE_IPL_PPI *This,
IN EFI_PEI_SERVICES **PeiServices,
IN EFI_PEI_HOB_POINTERS HobList
);
对着应\MdeModulePkg\Core\DxeIplPeim\DxeLoad.c
//
// Module Globals used in the DXE to PEI hand off
// These must be module globals, so the stack can be switched
//
CONST EFI_DXE_IPL_PPI mDxeIplPpi = {
DxeLoadCore
};
可以用加 DEBUG Message 的方法确定,前面的TempPtr.DxeIpl->Entry() 调用,执行的是\mdemodulepkg\core\dxeiplpeim\DxeLoad.c中的
/**
Main entry point to last PEIM.
This function finds DXE Core in the firmware volume and transfer the control to
DXE core.
@param This Entry point for DXE IPL PPI.
@param PeiServices General purpose services available to every PEIM.
@param HobList Address to the Pei HOB list.
@return EFI_SUCCESS DXE core was successfully loaded.
@return EFI_OUT_OF_RESOURCES There are not enough resources to load DXE core.
**/
EFI_STATUS
EFIAPI
DxeLoadCore (
IN CONST EFI_DXE_IPL_PPI *This,
IN EFI_PEI_SERVICES **PeiServices,
IN EFI_PEI_HOB_POINTERS HobList
)
其中通过 gEfiPeiLoadFilePpiGuid 加载 DXE Core
Loading PEIM D6A2CB7F-6A18-4E2F-B43B-9920A733700A
Loading PEIM at 0x00007EA3000 EntryPoint=0x00007EA3D78 DxeCore.efi
DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Loading DXE CORE at 0x%11p EntryPoint=0x%11p\n", (VOID *)(UINTN)DxeCoreAddress, FUNCTION_ENTRY_POINT (DxeCoreEntryPoint)));
Loading DXE CORE at 0x00007EA3000 EntryPoint=0x00007EA3D78
最后,使用下面的代码跳入 DxeCore:
//
// Transfer control to the DXE Core
// The hand off state is simply a pointer to the HOB list
//
HandOffToDxeCore (DxeCoreEntryPoint, HobList);
跳转之后首先进入一个DxeCoreEntryPoint 模块,代码在 \MdePkg\Library\DxeCoreEntryPoint
/**
The entry point of PE/COFF Image for the DXE Core.
This function is the entry point for the DXE Core. This function is required to call
ProcessModuleEntryPointList() and ProcessModuleEntryPointList() is never expected to return.
The DXE Core is responsible for calling ProcessLibraryConstructorList() as soon as the EFI
System Table and the image handle for the DXE Core itself have been established.
If ProcessModuleEntryPointList() returns, then ASSERT() and halt the system.
@param HobStart The pointer to the beginning of the HOB List passed in from the PEI Phase.
**/
VOID
EFIAPI
_ModuleEntryPoint (
IN VOID *HobStart
)
在这个函数中通过下面的函数转入 DXE Core:
//
// Call the DXE Core entry point
//
ProcessModuleEntryPointList (HobStart);
特别之处在于对应的ProcessModuleEntryPointList 位于\Build\OvmfX64\DEBUG_VS2015x86\X64\MdeModulePkg\Core\Dxe\DxeMain\DEBUG\AutoGen.c
VOID
EFIAPI
ProcessModuleEntryPointList (
IN VOID *HobStart
)
{
DxeMain (HobStart);
}
这个文件是编译期自动生成的,无法直接插入 DEBUG 来输出(编译时会被覆盖)。起初我以为是BIOS代码指定了上述的内容,但是在代码中无法搜索到类似的字样,后来经过研究这是 Basetools 里面代码生成的。在\BaseTools\Source\Python\AutoGen\GenC.py有如下定义:
gDxeCoreEntryPointString = TemplateString("""
${BEGIN}
VOID
EFIAPI
ProcessModuleEntryPointList (
IN VOID *HobStart
)
{
${Function} (HobStart);
}
${END}
""")
如果我们在 ${Function} (HobStart); 前面加入一行注释,那么对应的Auto Gen.c 会变成如下:
EFIAPI
ProcessModuleEntryPointList (
IN VOID *HobStart
)
{
// www.lab-z.com
DxeMain (HobStart);
}
调用的代码在 \mdemodulepkg\core\dxe\dxemain\DxeMain.c
// Main entry point to the DXE Core
//
/**
Main entry point to DXE Core.
@param HobStart Pointer to the beginning of the HOB List from PEI.
@return This function should never return.
**/
VOID
EFIAPI
DxeMain (
IN VOID *HobStart
)
从这里也可以看出:EDK2 编译过程中有BaseTools中的工具参与了编译过程,如果你在纯代码部分无法找到对应的内容,不妨考虑一下编译工具中。
最后,讲个好玩的事情:
肯·汤普森还有一个备受争议的行为,就是在UNIX里留后门。是的,这哥们竟然在代码里下毒。
最开始的时候,UNIX系统在贝尔实验室是供大家免费使用的。有人发现,肯·汤普森总能进入每个人的账户,于是一位同事就分析UNIX代码,重新编译了系统。
令人意想不到的是,肯·汤普森还是能进入他们的账户,贝尔实验室的科学家们却对此束手无策。
直到1983年,肯·汤普森在他的图灵奖获奖感言里揭示了这一秘密,原来,让他轻松“侵入”各位同事账户的秘诀不在UNIX代码,而在编译UNIX代码的C编译器里,而肯·汤普森正是编译器的开发者。