SEC阶段(安全校验)
SEC阶段是平台初始化的第一个阶段,计算机系统加载或重启后进入这个阶段
SEC阶段功能
主要是这下面几个功能:
这是一段红色的文本。
*接收并除了系统启动和重启信号:系统加电、重启、运行异常信号
*初始化临时存储区域:CAR(cache as ram),Cache被配置为no-eviction模式.
*作为可信系统的根
*传递参数给下一阶段:系统当前状态、可启动固件(Boot Firmware Volume)的临时RAM区域的地址和大小、栈的地址和大小
提示:详细功能解释如下:
1、接收并处理系统启动信号:系统加电信号、系统重启信号、系统运行过程中的严重异常信号。
2、初始化临时存储区域:当系统运行在SEC阶段时,仅CPU和CPU内部资源被初始化,各种外部设备和内存都没有被初始化,因而系统需要一些临时的RAM区域,用于代码和数据的存取,我们将之称为临时RAM,以示与内存的区别。这些临时的RAM只能位于CPU内部。最常用的临时RAM就是Cache,当Cache被配置为no-eviction模式时,可以作为内存使用,读命中时返回Cache中的数据,读缺失时不会向主存发出缺失事件;写命中时将数据写入Cache,写缺失时不会向主存发出缺失事件,这种计数称为CAR(Cache As Ram)
3、作为可信系统的根:作为取得对系统控制权的第一部分,SEC阶段是整个可信系统的根。SEC能被系统信任,以后各个阶段才有被信任的基础。通常,SEC在将控制权转移给PEI之前,可以验证PEI
4、传递系统参数给下一个阶段(即PEI):SEC阶段的一切工作都是为了PEI阶段做准备,最终SEC要把控制权转交给PEI,同时要将现阶段的成果汇报给PEI。汇报的手段就是将如下信息作为参数传递给PEI的入口函数。
• 系统当前的状态,PEI可以根据这些状态判断系统的健康状况。
• 可启动固件的地址和大小
• 临时RAM区域的地址和大小
• 栈的地址和大小
SEC阶段执行流程
以临时RAM初始化为界,SEC的执行又分为两大部分:临时的RAM生效之前称为Reset Vector阶段,临时RAM生效后调用SEC入口函数从而进入SEC功能区。
大致如下图:
Reset Vector
UEFI第一条指令所在的位置被称为ResetVector,之后便会进入ResetVector执行阶段。Reset Vector的执行流程如下:
• 进入UEFI固件入口
• 从实模式转换到32位保护模式
• 定位固件中的Boot Firmware Volume
• 定位BFV中的SEC映像
• 若是64位系统,从32位模式转换成64位模式
• 调用SEC入口函数
在Reset Vector部分,因为系统还没有栈的概念,所以不能使用基于栈的程序设计,需要使用 汇编语言
来完成一些操作。
注:
第一条指令:所在的位置是0xFFFFFFF0(这个地址有个专有的名称Reset Vector)
实模式:程序中用到的地址都是真实的物理地址,即程序员可见的地址完全是真实的内存地址,在实模式下所有的段都是可以读、写、可执行的。
保护模式:保护进程地址空间,程序A的地址空间,不能随意被程序B访问。
Reset Vector的执行流程代码分析:
1、Reset Vector进入UEFI固件的入口
; The VTF signature
;
; VTF-0 means that the VTF (Volume Top File) code does not require
; any fixups.
;
vtfSignature:
DB 'V', 'T', 'F', 0
ALIGN 16
resetVector:
;
; Reset Vector
;
; This is where the processor will begin execution
;
nop
nop
jmp EarlyBspInitReal16
ALIGN 16
fourGigabytes:
2、通过jmp跳转到Init16.asm的EarlyBspInitReal16位置
注:
DI(destination index)目的变址寄存器:用做隐含的目的串地址,默认在ES中。根据DI来确定是BSP(boot strap processor)启动还是AP(application processor)启动。
ES(Extra segment register)额外的段寄存器:它通常跟DI一起用来做指针使用。
BP(Base Pointer)基指针:通常BP用来保存使用局部变量的地址。
EarlyBspInitReal16:
mov di, 'BP'
jmp short Main16
;
; @param[out] DI 'AP' to indicate application processor
;
EarlyApInitReal16:
mov di, 'AP'
jmp short Main16
;
3、Main16(从实模式转换到32位保护模式;找到UEFI固件中的BFV;从BFV中找到Sec的镜像;如果是64位系统,则从32位模式转换至64位模式)
•EarlyInit16:获取BIST信息
•TransitionFromReal16To32BitFlat:通过CR0和CR4寄存器初始化CAR,及GDT信息获取完成16位实模式转换到32位保护模式
•Flat32SearchForBfvBase:从Top 16M中根据GUID,每4k去匹配查找BFV
•Flat32SearchForSecEntryPoint:从BFV中获取FFS
•其余部分:如果是64位系统,则从32位模式转换至64位模式;否则启动SecCore EntryPoint
BITS 16
;
; Modified: EBX, ECX, EDX, EBP
;
; @param[in,out] RAX/EAX Initial value of the EAX register
; (BIST: Built-in Self Test)
; @param[in,out] DI 'BP': boot-strap processor, or
; 'AP': application processor
; @param[out] RBP/EBP Address of Boot Firmware Volume (BFV)
; @param[out] DS Selector allowing flat access to all addresses
; @param[out] ES Selector allowing flat access to all addresses
; @param[out] FS Selector allowing flat access to all addresses
; @param[out] GS Selector allowing flat access to all addresses
; @param[out] SS Selector allowing flat access to all addresses
;
; @return None This routine jumps to SEC and does not return
;
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
;
%ifdef ARCH_IA32
;
; Restore initial EAX value into the EAX register
;
mov eax, esp
;
; Jump to the 32-bit SEC entry point
;
jmp esi
%else
;
; Transition the processor from 32-bit flat mode to 64-bit flat mode
;
OneTimeCall Transition32FlatTo64Flat
BITS 64
;
; Some values were calculated in 32-bit mode. Make sure the upper
; 32-bits of 64-bit registers are zero for these values.
;
mov rax, 0x00000000ffffffff
and rsi, rax
and rbp, rax
and rsp, rax
;
; RSI - SEC Core entry point
; RBP - Start of BFV
;
;
; Restore initial EAX value into the RAX register
;
mov rax, rsp
;
; Jump to the 64-bit SEC entry point
;
jmp rsi
%endif
SEC Core
进入SEC功能区后,首先利用 CAR 技术初始化栈,初始化 IDT,初始化EFI_SEC_PEI_HAND_OFF,将控制权转交给PEI,并将 EFI_SEC_PEI_HAND_OFF 传递给PEI。
注:
CAR (Cache As RAM):在Cache上开辟一段空间作为内存使用(此时内存尚未初始化,相关C语言运行需要内存和栈的空间);
IDT ( Interrupt Descriptor Table ) 中断描述表:记录了0~255的中断号和调用函数之间的关系。
提示:
_SEC_IDT_TABLE在IDT之前保留8个字节来存储 EFI_PEI_SERVICES**,因为 IDT 基础地址应该是 8 字节对齐。注意:对于IA32,只有IDT前面的4个字节用于存储EFI_PEI_SERVICES**。如下:
typedef struct _SEC_IDT_TABLE {
//
// Reserved 8 bytes preceding IDT to store EFI_PEI_SERVICES**, since IDT base
// address should be 8-byte alignment.
// Note: For IA32, only the 4 bytes immediately preceding IDT is used to store
// EFI_PEI_SERVICES**
//
UINT64 PeiService;
IA32_IDT_GATE_DESCRIPTOR IdtTable[SEC_IDT_ENTRY_COUNT];
} SEC_IDT_TABLE;
EFI_SEC_PEI_HAND_OFF实际上就是一个结构体,是UEFI当中在SEC阶段最重要的一个数据结构,将环境从以汇编语言执行转向C语言执行。如下:
跳转到SEC入口地址
根据最后的call指令找到跳转到SecCoreStartupWithStack函数,在IA32平台下的入口函数在EDKII\Edk2\UefiPayloadPkg\SecCore\SecMain.c中SecStartup(SecCore EntryPoint)做的动作如下:
•初始化FPU
•初始化IDT
•获取SecCore参数,往后传递给PEI用
•启动SecStartupPhase2
PEI入口函数
PeiCoreEntryPoint这个值就是一个64位的虚拟地址,这个地址是PEI阶段的Entry point函数的入口地址,然后通过(*PeiCoreEntryPoint) (SecCoreData, PpiList); 跳转到PEI阶段。如下:
UEFI最重要的特点就是模块化设计,模块载入内存生成Image。Image的入口函数是_ModuleEntryPoint。而PEI也是一个模块, PEI入口函数如下图:
ProcessModuleEntryPointList的细节部分如下:
最终调用PEI入口函数 PeiCore(edk2\MdeModulePkg\Core\Pei\PeiMain\PeiMain.c)
![](https://i-blog.csdnimg.cn/blog_migrate/5a371214bfdea7781dc91804eb7b6ae7.gif)
总结:站在巨人的肩膀上学习总结