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 ();
- }