SEC
作为BIOS ren,大家都知道BIOS Boot flow 的主要四个流程就是SEC,PEI,DXE,BDS
那按顺序就先来讲讲SEC的东西,虽然作为ODM 基本也不会动这些,但折腾折腾总不是什么坏事
在主板上电时序跑完之后,CPU完成自己的初始化后,指针则会指向 FFFF FFF0 这个地址,也就是从这,开始CPU的奇妙BIOS之旅~
注:FFFF FFF0这个地址是基于4G寻址向下拓展实现(远古时期还有1M寻址向下拓展,也就是这个000F FFF0地址 )
在早期认知当中,FFFF FFF0这个地址是一条跳转语句,但后续在研究这方面发现稍微有所不同,我们可以使用FlexHex 打开任一一个BIOS bin 文件,拉到最后面,会发现,实际执行的第一条的指令为0x90 0x90
也就是Nop 指令,Trace Code后,在reset vector,前两条指令确实为Nop,第三条指令才是跳转(以下Code 均来自于EDK2,后续博文Code 也会基于EDK2)
后基于百度的力量得知Nop指令是单纯历史原因,留下两条无意义的指令
跳完之后继续跳,在CPU早期上电处于一个特殊的模式下,类似于real mode而不完全是,这些跳转的指令改变了CPU CS 指针,使其进入正常的real mode/flat mode
继续跳转
Main16:
OneTimeCall EarlyInit16
;
; Transition the processor from 16-bit real mode to 32-bit flat mode
;
OneTimeCall TransitionFromReal16To32BitFlat
BITS 32
;
; Search for the Boot Firmware Volume (BFV)
;
OneTimeCall Flat32SearchForBfvBase
;
; EBP - Start of BFV
;
;
; Search for the SEC entry point
;
OneTimeCall Flat32SearchForSecEntryPoint
;
; ESI - SEC Core entry point
; EBP - Start of BFV
;
Flat32SearchForBfvBase后就可以找到BFV,也就是我们的Boot firmware volume
后续的指令跳转就不在一一枚举~
下面聊一些SEC 中的东西
早期上电阶段,没用Memory资源可以使用,SEC就做了CAR 功能,可以先让一些Code有堆栈跑起来,SEC主要做一些信任根,传递一些参数
1.记录中断号与调用函数链接关系的IDT table
2.EFI_SEC_PEI_HAND_OFF,这个数据结构含有 SEC-PEI Core 握手交接控制权时所需的各种信息,如temp RAM 的地址和大小,栈的地址和 Boot Firmware Volume 的地址等
IDT table如下:
当Car后,IDT table也就有机会实现初始化,也就是SecCoreStartupWithStack函数实现
另外除了IDT,这个函数也完成了Debug功能的初始化,我们的源代码调试自此之后就可以正常进行了
EFI_SEC_PEI_HAND_OFF结构体的数据也在这个函数下完成了基本信息交互
VOID
EFIAPI
SecCoreStartupWithStack (
IN EFI_FIRMWARE_VOLUME_HEADER *BootFv,
IN VOID *TopOfCurrentStack
)
{
EFI_SEC_PEI_HAND_OFF SecCoreData;
SEC_IDT_TABLE IdtTableInStack;
IA32_DESCRIPTOR IdtDescriptor;
UINT32 Index;
volatile UINT8 *Table;
//
// To ensure SMM can't be compromised on S3 resume, we must force re-init of
// the BaseExtractGuidedSectionLib. Since this is before library contructors
// are called, we must use a loop rather than SetMem.
//
Table = (UINT8 *)(UINTN)FixedPcdGet64 (PcdGuidedExtractHandlerTableAddress);
for (Index = 0;
Index < FixedPcdGet32 (PcdGuidedExtractHandlerTableSize);
++Index)
{
Table[Index] = 0;
}
//
// Initialize IDT - Since this is before library constructors are called,
// we use a loop rather than CopyMem.
//
IdtTableInStack.PeiService = NULL;
for (Index = 0; Index < SEC_IDT_ENTRY_COUNT; Index++) {
UINT8 *Src;
UINT8 *Dst;
UINTN Byte;
Src = (UINT8 *)&mIdtEntryTemplate;
Dst = (UINT8 *)&IdtTableInStack.IdtTable[Index];
for (Byte = 0; Byte < sizeof (mIdtEntryTemplate); Byte++) {
Dst[Byte] = Src[Byte];
}
}
IdtDescriptor.Base = (UINTN)&IdtTableInStack.IdtTable;
IdtDescriptor.Limit = (UINT16)(sizeof (IdtTableInStack.IdtTable) - 1);
if (SevEsIsEnabled ()) {
SevEsProtocolCheck ();
//
// For SEV-ES guests, the exception handler is needed before calling
// ProcessLibraryConstructorList() because some of the library constructors
// perform some functions that result in #VC exceptions being generated.
//
// Due to this code executing before library constructors, *all* library
// API calls are theoretically interface contract violations. However,
// because this is SEC (executing in flash), those constructors cannot
// write variables with static storage duration anyway. Furthermore, only
// a small, restricted set of APIs, such as AsmWriteIdtr() and
// InitializeCpuExceptionHandlers(), are called, where we require that the
// underlying library not require constructors to have been invoked and
// that the library instance not trigger any #VC exceptions.
//
AsmWriteIdtr (&IdtDescriptor);
InitializeCpuExceptionHandlers (NULL);
}
ProcessLibraryConstructorList (NULL, NULL);
if (!SevEsIsEnabled ()) {
//
// For non SEV-ES guests, just load the IDTR.
//
AsmWriteIdtr (&IdtDescriptor);
} else {
//
// Under SEV-ES, the hypervisor can't modify CR0 and so can't enable
// caching in order to speed up the boot. Enable caching early for
// an SEV-ES guest.
//
AsmEnableCache ();
}
DEBUG ((
DEBUG_INFO,
"SecCoreStartupWithStack(0x%x, 0x%x)\n",
(UINT32)(UINTN)BootFv,
(UINT32)(UINTN)TopOfCurrentStack
));
//
// Initialize floating point operating environment
// to be compliant with UEFI spec.
//
InitializeFloatingPointUnits ();
#if defined (MDE_CPU_X64)
//
// ASSERT that the Page Tables were set by the reset vector code to
// the address we expect.
//
ASSERT (AsmReadCr3 () == (UINTN)PcdGet32 (PcdOvmfSecPageTablesBase));
#endif
//
// |-------------| <-- TopOfCurrentStack
// | Stack | 32k
// |-------------|
// | Heap | 32k
// |-------------| <-- SecCoreData.TemporaryRamBase
//
ASSERT (
(UINTN)(PcdGet32 (PcdOvmfSecPeiTempRamBase) +
PcdGet32 (PcdOvmfSecPeiTempRamSize)) ==
(UINTN)TopOfCurrentStack
);
//
// Initialize SEC hand-off state
//
SecCoreData.DataSize = sizeof (EFI_SEC_PEI_HAND_OFF);
SecCoreData.TemporaryRamSize = (UINTN)PcdGet32 (PcdOvmfSecPeiTempRamSize);
SecCoreData.TemporaryRamBase = (VOID *)((UINT8 *)TopOfCurrentStack - SecCoreData.TemporaryRamSize);
SecCoreData.PeiTemporaryRamBase = SecCoreData.TemporaryRamBase;
SecCoreData.PeiTemporaryRamSize = SecCoreData.TemporaryRamSize >> 1;
SecCoreData.StackBase = (UINT8 *)SecCoreData.TemporaryRamBase + SecCoreData.PeiTemporaryRamSize;
SecCoreData.StackSize = SecCoreData.TemporaryRamSize >> 1;
SecCoreData.BootFirmwareVolumeBase = BootFv;
SecCoreData.BootFirmwareVolumeSize = (UINTN)BootFv->FvLength;
//
// Make sure the 8259 is masked before initializing the Debug Agent and the debug timer is enabled
//
IoWrite8 (0x21, 0xff);
IoWrite8 (0xA1, 0xff);
//
// Initialize Local APIC Timer hardware and disable Local APIC Timer
// interrupts before initializing the Debug Agent and the debug timer is
// enabled.
//
InitializeApicTimer (0, MAX_UINT32, TRUE, 5);
DisableApicTimerInterrupt ();
//
// Initialize Debug Agent to support source level debug in SEC/PEI phases before memory ready.
//
InitializeDebugAgent (DEBUG_AGENT_INIT_PREMEM_SEC, &SecCoreData, SecStartupPhase2);
}
SEC剩下的就是call PEI,通过SecStartupPhase2函数寻找Pei 入口函数~
VOID
EFIAPI
SecStartupPhase2 (
IN VOID *Context
)
{
EFI_SEC_PEI_HAND_OFF *SecCoreData;
EFI_FIRMWARE_VOLUME_HEADER *BootFv;
EFI_PEI_CORE_ENTRY_POINT PeiCoreEntryPoint;
SecCoreData = (EFI_SEC_PEI_HAND_OFF *)Context;
//
// Find PEI Core entry point. It will report SEC and Pei Core debug information if remote debug
// is enabled.
//
BootFv = (EFI_FIRMWARE_VOLUME_HEADER *)SecCoreData->BootFirmwareVolumeBase;
FindAndReportEntryPoints (&BootFv, &PeiCoreEntryPoint);
SecCoreData->BootFirmwareVolumeBase = BootFv;
SecCoreData->BootFirmwareVolumeSize = (UINTN)BootFv->FvLength;
//
// Transfer the control to the PEI core
//
(*PeiCoreEntryPoint)(SecCoreData, (EFI_PEI_PPI_DESCRIPTOR *)&mPrivateDispatchTable);
//
// If we get here then the PEI Core returned, which is not recoverable.
//
ASSERT (FALSE);
CpuDeadLoop ();
}