UEFI Training – UEFI SEC 阶段详细解析

SEC(Security)阶段是 UEFI 启动过程中的首要阶段,肩负着建立系统信任链和初始化核心硬件(如处理器配置及临时内存建立)的重任。本文将深入剖析EDK II 中SEC阶段的实现机制,从其入口点开始,详细阐述其关键任务,包括处理器和临时内存的初始化、PEI核心的定位、验证与加载,以及最终如何将控制权安全地移交给PEI阶段。

1. 引言

UEFI规范定义了一个清晰的启动流程,该流程被划分为多个逻辑阶段,依次为:SEC (Security)阶段,负责处理最初的系统启动事件并建立信任根;PEI (Pre-EFI Initialization)阶段,执行早期的平台和内存初始化;DXE (Driver Execution Environment)阶段,加载并执行驱动,提供核心EFI服务;BDS (Boot Device Select)阶段,实现启动设备的选择逻辑;以及RT (Runtime) 和 AL (After Life)阶段,提供运行时服务和关机处理。

SEC阶段是整个UEFI启动流程的起点,也是构建系统信任链的基石。它的主要职责是在一个最小化的、通常是资源受限的环境中,执行必要的早期硬件初始化,并为下一阶段——PEI阶段的执行准备好环境。由于SEC阶段在系统上电或复位后最先执行,它对系统的安全性和稳定性至关重要。

2. SEC阶段的核心职责

根据UEFI规范,SEC阶段的核心职责可以概括为以下几点:

  • 处理系统启动和重启事件: SEC是系统上电或发生各种重启事件(如冷启动、热启动、S3唤醒等)后,处理器执行的第一段代码。
  • 建立临时内存(Temporary RAM): 在主内存(DRAM)完全可用之前,SEC阶段需要建立一个临时的、可用的内存区域。这通常通过将CPU的高速缓存配置为RAM(Cache-as-RAM, CAR)来实现。
  • 作为信任链的起点: SEC阶段负责验证下一阶段代码(即PEI核心)的完整性和真实性,从而建立硬件信任根(Hardware Root of Trust)到固件信任根(Firmware Root of Trust)的链条。
  • 传递切换信息给PEI阶段: SEC阶段需要收集关键信息,如临时内存的位置和大小、PEI核心的入口点等,并将这些信息传递给PEI核心。

3. EDK II中的SEC

在EDK II中,SEC阶段的实现通常分布在MdeModulePkg/Core/Sec/以及特定平台的包(如EmulatorPkg/Sec/,OvmfPkg/Sec/,ArmVirtPkg/Sec/等)中。

3.1 入口

SEC阶段的执行始于一个平台相关的汇编入口点,通常命名为_ModuleEntryPoint或类似的名称。这个入口点位于固件启动向量(Reset Vector)所指向的位置。

  • x86平台: 对于传统的x86平台,处理器在启动时处于16位实模式。_ModuleEntryPoint的首要任务之一是将处理器从实模式切换到32位保护模式或64位长模式,以便执行后续的C代码。这涉及到设置GDT (Global Descriptor Table)、IDT (Interrupt Descriptor Table) 等关键数据结构。例如,在 IntelFsp2WrapperPkgLibrarySecFspWrapperPlatformSecLibSampleIa32SecEntry.nasm 中可以看到此类操作。
  • ARM平台: 对于ARM平台,处理器启动时的状态(如异常级别 EL3/EL2/EL1,AArch32/AArch64状态)取决于SoC的设计。入口代码需要进行相应的模式和状态设置。例如,在ArmPlatformPkgSecAArch64ModuleEntryPoint.S中。

此汇编代码还会进行非常早期的CPU初始化,例如使能CPU内部的某些特性,或者根据需要切换执行核心。

3.2 临时栈的建立

在C代码能够可靠执行之前,需要一个有效的栈空间。汇编入口代码会在一个处理器内部资源(如CPU寄存器指向的预定义区域)或者刚刚建立的CAR中设置一个临时的栈。栈指针(如x86的ESP/RSP,ARM的SP)会被初始化指向这个区域。

3.3 Cache-as-RAM (CAR) 初始化

由于主内存(DRAM)此时尚未被内存控制器初始化,SEC阶段无法直接使用DRAM。为了能够执行更复杂的C代码并存储数据,SEC阶段需要一块可用的临时内存。CAR技术通过配置CPU的片上高速缓存(Cache)作为RAM来满足这一需求。

  • 配置机制: CAR的配置高度依赖于CPU架构。
  • x86平台: 通常通过设置内存类型范围寄存器(MTRRs - Memory Type Range Registers)来实现。特定的缓存区域会被设置为“写回”(Write-Back)或“写通”(Write-Through)模式,并使其在内存地址空间中可见且可缓存为RAM。相关MSRs(Model Specific Registers)的配置是关键。
  • ARM平台: 可能涉及到配置内存管理单元(MMU)或特定于SoC的缓存控制器寄存器。
  • CAR的生命周期: CAR是临时的。一旦PEI阶段初始化了主内存,并且关键数据从CAR迁移到DRAM后,CAR所占用的缓存会被恢复其正常的缓存功能。

EDK II中,CAR的初始化代码通常是平台相关的,并且可能部分在汇编中,部分在早期的C代码中实现。例如,SecPlatformLib实例会提供相关功能。

3.4 SEC核心逻辑

在汇编代码完成必要的底层设置(如模式切换、临时栈、CAR)后,控制权会转移到SEC阶段的C语言主函数。

Sec Core 函数负责协调SEC阶段的后续任务:

1.服务初始化: SEC阶段提供的服务非常有限,主要是通过直接函数调用。
2.平台早期初始化: 调用平台相关的初始化函数(通常通过SecPlatformInformation PPI或直接链接的库函数)。这可能包括:
- 进一步的CPU初始化(如应用微码更新,虽然这更常见于PEI早期)。
- 早期芯片组初始化(如识别芯片组型号,配置一些关键的总线参数)。
- 使能或查询某些硬件安全特性。
- PEI核心的定位与验证: 这是SEC阶段最核心的任务之一。
- 定位: PEI核心通常位于一个固件卷(Firmware Volume, FV)中,例如名为FV_MAIN或FV_PEICORE的FV。SEC阶段需要知道这个FV在固件存储设备(如SPI Flash)中的基地址和大小。这些信息可能是硬编码的,或者通过某种平台机制获取。SEC会遍历FV中的文件,通过预定义的File GUID(如EFI_FFS_FILETYPE_PEI_CORE或特定的PEI核心GUID)来查找PEI核心文件。
- 验证(可选但推荐): 为了确保信任链的完整性,SEC阶段应该对找到的PEI核心进行数字签名验证。如果平台支持硬件信任根技术(如Intel Boot Guard, AMD PSB),验证过程会由硬件辅助完成或强制执行。如果验证失败,启动过程通常会中止。
- 准备切换信息: SEC阶段需要收集并组织传递给PEI核心的信息。这些信息通常封装在一个特定的数据结构中。

3.5 切换到PEI阶段

当PEI核心被成功定位(和验证)后,SEC阶段的最后一步是将控制权和必要信息传递给PEI核心的入口点。

切换数据结构: UEFI规范定义了EFI_SEC_PEI_HAND_OFF结构体(或其变体,如传递给PeiCoreEntry的参数)来承载这些信息。其关键成员包括:
DataSize: 此结构体的大小。
BootFirmwareVolumeBase: 包含PEI核心的FV的基地址。
BootFirmwareVolumeSize: 包含PEI核心的FV的大小。
TemporaryRamBase: CAR的基地址。
TemporaryRamSize: CAR的大小。
PeiTemporaryRamBase: PEI阶段可用的临时RAM基地址(可能与TemporaryRamBase相同或为其一部分)。
PeiTemporaryRamSize: PEI阶段可用的临时RAM大小。
StackBase: PEI阶段初始栈的基地址。
StackSize: PEI阶段初始栈的大小。
调用PEI核心入口点: SEC阶段会调用PEI核心的入口函数,通常名为PeiCoreEntry。这个函数的地址是从PEI核心文件中解析出来的。调用时,会将上述切换数据结构的指针作为参数传递。

例如,在 IntelFsp2PkgFspSecCoreSecMain.c 中的SecStartup函数最终会调用PeiCore。


VOIDEFIAPISecStartup (  
IN UINT32 SizeOfRam,  
IN UINT32 TempRamBase,  
IN VOID *BootFirmwareVolume,  
IN PEI_CORE_ENTRY PeiCore,  
IN UINTN BootLoaderStack,  
IN UINT32 ApiIdx  ){
// ...Initialize the PEI Core Data...
//
// Call PeiCore Entry
//
PeiCore (&SecCoreData, mPeiSecPlatformInformationPpi);
}

3.6 IA32 与 X64 汇编

SEC阶段的底层初始化依赖于特定CPU架构的汇编代码,以完成C语言环境就绪前的准备工作。其核心任务是设置处理器模式、建立临时内存(CAR),并最终将控制权转交PEI核心。

在 IA32 (32位) 架构下,SEC阶段的汇编流程 通常始于16位实模式,它必须显式地通过加载GDT(全局描述符表)和修改控制寄存器(如CR0)来切换到32位保护模式。随后,它调用一个外部模块(如Intel FSP)的TempRamInit API来初始化CAR。在此过程中,IA32架构高度依赖栈来传递参数给TempRamInit以及后续的平台初始化函数和PEI核心入口函数(SecStartup)。所有信息,包括临时内存的地址、BFV基址、PEI核心入口点等,都通过push指令依次压入栈中。

相比之下,在 X64 (64位) 架构下,SEC汇编 通常假设系统已处于64位长模式。其流程的关键区别在于遵循了X64的调用约定(ABI)。这意味着函数参数,无论是传递给TempRamInit还是SecStartup,都优先通过寄存器(如RCX, RDX, R8, R9等)传递,而不是栈。这使得代码在某些方面更为高效。此外,X64汇编必须严格遵守16字节的栈对齐规则,并在调用函数前为被调用者预留“影子空间”(Shadow Space),这是IA32所没有的强制要求。

总结来说,两种架构在SEC阶段的目标一致,但实现路径因其固有的体系结构差异而有所不同。最关键的对比点包括:

  • 处理器模式转换: IA32需要从16位手动转换到32位保护模式,而X64通常直接在64位模式下操作。
  • 参数传递: IA32主要依赖栈,而X64优先使用寄存器。
  • 栈的使用: X64有严格的16字节对齐和影子空间要求,IA32则没有。
  • 寄存器和操作数: IA32使用32位寄存器和操作数,X64则使用64位寄存器和操作数,并可通过REX前缀访问更多通用寄存器。

这些底层差异最终导致了两种架构在SEC阶段汇编代码的具体实现上的不同。

4. SEC阶段的关键数据结构与服务

  • Firmware Volumes (FVs): SEC阶段依赖FV来定位PEI核心。FV的格式由UEFI PI规范定义。
  • EFI_SEC_PEI_HAND_OFF / EFI_PEI_CORE_ENTRY_POINT的参数: 这是SEC与PEI之间信息传递的主要载体。
  • SEC Platform Information PPI (gEfiSecPlatformInformationPpiGuid): 虽然PPI(PEIM-to-PEIM Interface)是PEI阶段的概念,但有时SEC阶段会通过一个特殊的机制(如SecPlatformInformation函数指针数组)获取平台特定的信息和功能,这些信息在概念上类似于一个极简的PPI。它允许SEC核心代码与平台特定的SEC代码解耦。这个PPI(或等效机制)可以提供如临时RAM区域、FV位置等信息。

EDK II的MdePkgIncludePpiSecPlatformInformation.h (或类似的 SecPlatformInformation2.h for FSP) 定义了相关结构。

5. SEC阶段的安全性考量

SEC阶段是系统信任的根基,其安全性至关重要:

  • 代码的健壮性: SEC阶段的代码必须非常健壮,因为它在非常早期的、可能不稳定的硬件环境中运行。任何错误都可能导致系统无法启动。
  • 代码的最小化: 为了减少攻击面和出错的可能性,SEC阶段的代码应该尽可能小而精炼。
  • 完整性校验: 如前所述,对PEI核心的完整性校验是构建信任链的关键。如果SEC本身被篡改,整个信任链都将失效。因此,保护SEC代码本身(例如通过Boot ROM或硬件验证机制如Intel Boot Guard的ACM - Authenticated Code Module)是最高安全级别的要求。
  • 早期硬件安全配置: 如果平台支持,SEC阶段可能会进行一些非常早期的硬件安全配置,例如锁定某些关键的系统配置寄存器(SCRTM - Static Core Root of Trust for Measurement)。

6. 平台差异性与EDK II的抽象

EDK II通过库和PPI(或等效机制)的设计,努力将SEC核心逻辑与平台特定代码分离。

  • SecPlatformLib: 平台开发者需要提供这个库的实例,实现与特定硬件相关的操作,如CAR初始化、获取FV信息等。
  • 架构差异:
  • x86 SEC: 涉及复杂的模式切换、GDT/IDT设置、MTRR配置等。
  • ARM SEC: 涉及ARM架构的异常级别处理、MMU/MPU的早期配置(如果需要)、TrustZone的早期设置等。ArmPkg 和 ArmVirtPkg 提供了ARM平台的SEC实现基础。
  • SoC特定启动: 许多SoC(System on Chip)有其自己固化的Boot ROM,它可能执行了部分传统SEC阶段的任务,然后再跳转到UEFI SEC代码。在这种情况下,UEFI SEC的入口条件和职责可能会有所调整。Intel FSP (Firmware Support Package) 中的FspSecCore也是一个例子,它封装了部分SEC逻辑。
 

 7. 结语 

SEC阶段作为UEFI启动流程的先锋,承担着初始化系统最底层硬件、建立临时运行环境并安全地将控制权移交给PEI阶段的关键使命。它不仅是后续所有固件模块执行的基础,更是整个系统信任链的源头。EDK II通过模块化和库化的设计,提供了一个灵活且可移植的SEC阶段实现框架,允许平台开发者在其基础上进行适配和扩展。

版权声明:
作者:bin
链接:https://ay123.net/mystudy/uefi/1894/
来源:爱影博客
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
海报
UEFI Training – UEFI SEC 阶段详细解析
SEC(Security)阶段是 UEFI 启动过程中的首要阶段,肩负着建立系统信任链和初始化核心硬件(如处理器配置及临时内存建立)的重任。本文将深入剖析EDK II 中S……
<<上一篇
下一篇>>
文章目录
关闭
目 录