UEFI启动七阶段详解

UEFI系统的启动遵循UEFI平台初始化标准。UEFI系统从上电到关机可以分为以下7个阶段:

SEC(安全验证)→PEI(EFI前期初始化)→DXE(驱动执行环境)→BDS(启动设备选择)→TSL(操作系统加载前期)→RT(Run Time)→AL(系统灾难恢复期)

        下图展示了UEFI系统从加电到关机的7个阶段(以图中竖线为界)。前三个阶段是UEFI初始化阶段,DXE阶段结束后UEFI环境已经准备完毕。BDS和TSL是操作系统加载器作为UEFI应用程序运行的阶段。操作系统加载器调用ExitBootServices()服务后进入RT阶段,RT阶段包括操作系统加载器后期和操作系统运行期。当系统硬件或操作系统出现严重错误不能继续正常运行时,固件会尝试修复错误,这时系统进入AL期。但PI规范和UEFI规范都没有规定AL期的行为。“?”表示其行为由系统供应商自行定义。

 一、SEC(Security Phase)安全验证阶段

        SEC作为整个系统的起点,可能会遇到各种异常,就需要设置IDT,有了中断描述符表接收异常,就能让系统遭遇意外的情况时不会崩溃,同时它还为PEI阶段的代码设置临时内存的基地址和长度,并传给PEI,最后找到PEI代码入口点,移交控制权,并且处理临时内存。SEC是平台初始化的第一个阶段,计算机加电后首先进入这个阶段。它主要做以下4件事情:

  1. 接收和处理系统的启动、重启、异常信号。
  2. SEC phase特色功能”Cache As RAM(CAR)”,在Cache上开辟一片空间作为内存使用。(原因:因为此时内存还没有被初始化,C语言运行时需要内存和栈空间)
  3. SEC阶段作为可信系统的根。
  4. 传递系统参数给PEI phase。

(一)SEC阶段的功能

  1. 接收并处理系统启动和重启的信号,系统加电信号、重启信号、运行过程的异常信号。
  2. 在SEC阶段时,仅有CPU和CPU内部资源被初始化,而各种外部设备和内存都没有被初始化。因此系统需要一部分临时的内存用于代码和数据的存储,一般称为临时RAM,此时它只能位于CPU内部。最常用的临时RAM是Cache,将它作为初始化临时存储区域。
  3. 只有SEC phase被系统信任,下面的各个阶段才有被信任的基础。
  4. SEC phase移交控制权给PEI phase,SEC phase给PEI phase传递的参数如下:
  1. 栈的地址和大小
  2. 系统当前的状态
  3. 可启动固件的地址和大小
  4. 临时RAM区域的地址和大小

(二)SEC阶段的执行流程

        根据临时RAM是否初始化为界限,SEC阶段分为两部分:临时RAM初始化前称为Reset Vector阶段;临时RAM初始化后调用SEC入口函数进入SEC功能区

1、Reset Vector的执行流程如下:

A)进入固件入口

B)从实模式转换为32位平坦模式

C)定位固件中的BFV

D)定位BFV中的SEC映像

E)若是64位系统,要从32位模式转换为64位模式

F)调用SEC入口函数

2、进入SEC功能区

       进入功能区后,首先利用CAR技术初始栈,初始化IDT,初始化EFI_SEC_PEI_HAND_OFF,将控制权交给PEI,并将EFI_SEC_PEI_HAND_OFF传递给PEI。

EFI_SEC_PEI_HAND_OFF结构体很重要,具体如下:

///
/// EFI_SEC_PEI_HAND_OFF structure holds information about
/// PEI core's operating environment, such as the size of location of
/// temporary RAM, the stack location and BFV location.
///
typedef struct _EFI_SEC_PEI_HAND_OFF {
  ///
  /// Size of the data structure.
  ///
  UINT16  DataSize;

  ///
  /// Points to the first byte of the boot firmware volume,
  /// which the PEI Dispatcher should search for
  /// PEI modules.
  ///
  VOID    *BootFirmwareVolumeBase;

  ///
  /// Size of the boot firmware volume, in bytes.
  ///
  UINTN   BootFirmwareVolumeSize;

  ///
  /// Points to the first byte of the temporary RAM.
  ///
  VOID    *TemporaryRamBase;

  ///
  /// Size of the temporary RAM, in bytes.
  ///
  UINTN   TemporaryRamSize;

  ///
  /// Points to the first byte of the temporary RAM
  /// available for use by the PEI Foundation. The area
  /// described by PeiTemporaryRamBase and PeiTemporaryRamSize
  /// must not extend outside beyond the area described by
  /// TemporaryRamBase & TemporaryRamSize. This area should not
  /// overlap with the area reported by StackBase and
  /// StackSize.
  ///
  VOID    *PeiTemporaryRamBase;

  ///
  /// The size of the available temporary RAM available for
  /// use by the PEI Foundation, in bytes.
  ///
  UINTN   PeiTemporaryRamSize;

  ///
  /// Points to the first byte of the stack.
  /// This are may be part of the memory described by
  /// TemporaryRamBase and TemporaryRamSize
  /// or may be an entirely separate area.
  ///
  VOID    *StackBase;

  ///
  /// Size of the stack, in bytes.
  ///
  UINTN   StackSize;
} EFI_SEC_PEI_HAND_OFF;

 EFI_SEC_PEI_HAND_OFF结构体中保存了PEI core的运行环境信息,如临时RAM的位置大小、堆栈位置和BFV位置。

1)数据结构的大小。

  ///
  /// Size of the data structure.
  ///
  UINT16  DataSize;

2)指向BFV的第一个字节,PEI Dispatcher应该搜索PEI模块。

  ///
  /// Points to the first byte of the boot firmware volume,
  /// which the PEI Dispatcher should search for
  /// PEI modules.
  ///
  VOID    *BootFirmwareVolumeBase;

3)BFV的大小,单位是字节。

  ///
  /// Size of the boot firmware volume, in bytes.
  ///
  UINTN   BootFirmwareVolumeSize;

4)指向临时RAM的第一个字节。

  ///
  /// Points to the first byte of the temporary RAM.
  ///
  VOID    *TemporaryRamBase;

5)临时RAM的大小,以字节为单位。

  ///
  /// Size of the temporary RAM, in bytes.
  ///
  UINTN   TemporaryRamSize;

6)指向PEI可以使用的临时RAM的第一个字节。PeiTemporaryRamBase和PeiTemporaryRamSize所描述的区域不能超出TemporaryRamBase和TemporaryRamSize所描述的区域。这个区域不应该与StackBase和StackSize返回的区域重叠。

  ///
  /// Points to the first byte of the temporary RAM
  /// available for use by the PEI Foundation. The area
  /// described by PeiTemporaryRamBase and PeiTemporaryRamSize
  /// must not extend outside beyond the area described by
  /// TemporaryRamBase & TemporaryRamSize. This area should not
  /// overlap with the area reported by StackBase and
  /// StackSize.
  ///
  VOID    *PeiTemporaryRamBase;

7)PEI使用的可用临时RAM的大小,以字节为单位。

  ///
  /// The size of the available temporary RAM available for
  /// use by the PEI Foundation, in bytes.
  ///
  UINTN   PeiTemporaryRamSize;

8)指向堆栈的第一个字节。这可能是由TemporaryRamBase和TemporaryRamSize描述的内存的一部分,也可能是一个完全独立的区域。

  ///
  /// Points to the first byte of the stack.
  /// This are may be part of the memory described by
  /// TemporaryRamBase and TemporaryRamSize
  /// or may be an entirely separate area.
  ///
  VOID    *StackBase;

9)堆栈的大小,以字节为单位。

  ///
  /// Size of the stack, in bytes.
  ///
  UINTN   StackSize;

3、IA32下的SEC入口函数,截取了一部分,详细代码在UefiCpuPkg\SecCore\SecMain.c。SEC的C语言阶段的入口点。SEC汇编代码初始化一些临时内存并建立堆栈后,控制被转移到这个函数。

 

二、PEI(Pre-EFI Initialization)前期初始化阶段

        虽然SEC阶段对CPU和CPU内部的资源进行了初始化。但PEI阶段可用的资源依旧十分有限,该阶段是对内存进行初始化,主要功能是为DXE阶段准备执行环境,将所需要传递给DXE的信息组成HOB(Hand Off Block)列表,最终将控制权转交给DXE。UEFI具有模块化设计的特点,PEI就是一个模块。PEI Image的入口函数调用PEI模块的入口函数PEICore。

(一)PEI阶段的执行流程

 1、SEC模块找到PEI Image的入口函数_ModuleEntryPoint,函数位于       MdePkg/Library/PeiCoreEntryPoint/PeiCoreEntryPoint.c中。

VOID
EFIAPI
_ModuleEntryPoint(
  IN CONST  EFI_SEC_PEI_HAND_OFF    *SecCoreData,
  IN CONST  EFI_PEI_PPI_DESCRIPTOR  *PpiList
)
{
  ProcessModuleEntryPointList (SecCoreData, PpiList, NULL);

  //
  // Should never return
  //
  ASSERT(FALSE);
  CpuDeadLoop ();
}

2、_ModuleEntryPoint函数最终调用PEI模块的入口函数PEICore,进入PEI入口。

VOID
EFIAPI
PeiCore (
  IN CONST EFI_SEC_PEI_HAND_OFF        *SecCoreDataPtr,
  IN CONST EFI_PEI_PPI_DESCRIPTOR      *PpiList,
  IN VOID                              *Data
  )

以OVMF为例,从SEC阶段分析得知,PEI入口函数是PeiCore,位置:

edk2\MdeModulePkg\Core\Pei\PeiMain\PeiMain.c,以上代码只是PeiCore函数的一部分。

3、根据SEC阶段传入的信息初始化PS(PEICore Service)。

  //
  // Initialize PEI Core Services
  //
  InitializeMemoryServices   (&PrivateData, SecCoreData, OldCoreData);

4、调度系统中的PEIM(PEI Module),准备HOB列表。

  //
  // Call PEIM dispatcher
  //
  PeiDispatcher (SecCoreData, &PrivateData);

5、调用PEIServices得到DXE IPL PPI的Entry服务(即DXELoadCore)。

  //
  // Lookup DXE IPL PPI
  //
  Status = PeiServicesLocatePpi (
             &gEfiDxeIplPpiGuid,
             0,
             NULL,
             (VOID **)&TempPtr.DxeIpl
             );
  ASSERT_EFI_ERROR (Status);

        注意:PPI与DXE阶段的Protocol类似,每个PPI都是一个结构体,包含有函数指针和变量。每个PPI都有一个GUID。通过PEIServices的LocatePPI服务可以找到GUID对应的PPI实例。

6、 DXELoadCore服务找出并运行DXEImage的入口函数,将HOB列表传递给DXE。

 // DXE IPL
  Status = TempPtr.DxeIpl->Entry (
                             TempPtr.DxeIpl,
                             &PrivateData.Ps,
                             PrivateData.HobList
                             );

PEI阶段执行流程图如下:

        PEI阶段执行流程完整描述:SEC模块找到PEI Image的入口函数 _ModuleEntryPoint, _ModuleEntryPoint函数最终调用PEI模块的入口函数PEICore,进入PEICore后,首先根据从SEC阶段出入的信息设置PEI Core Services,然后调用PEIDispatcher执行系统总的PEIM,在内存初始化完成后,系统切换栈并重新进入PEICore。重新进入PEICore后使用的不再是临时RAM 而是真正的内存。在所有PEIM执行完成后,调用PEIServices的LocatePPI服务得到DXE IPL PPI,并调用DXE IPL PPI的Entry服务(即DEXLoadCore),找出DEX Image的入口函数,执行DXE Image函数并将HOB列表传递给DXE。

(二)具体调用的系统中的PEIM如下

  1. CPU PEIM(提供CPU相关功能,如进行Cache设置、主频设置等等)。
  2. 平台相关的PEIM(初始化内存控制器、I/O控制器等等)。
  3. 内存初始化PEIM(对内存进行初始化,此时内存才可以被使用,之前使用的是CPU模拟的临时内存)。

(三)PEI阶段的功能

  1. 初始化内存
  2. 为DXE阶段准备执行环境

具体为:

        基本的Chipset初始化

        Memory Sizing

        BIOS Recovery

        S3 Resume

        切换Stack到Memory(Disable CAR, Enable Cache)

        启动DXEIPL(DXE Initial Program Loader)

 (四)PEI划分

        PEI内核(PEI Foundation):负责PEI基础服务和流程。

        PEIM(PEI Module)派遣器:找出系统中所有的PEIM,并根据PEIM之间的依赖关系按顺序执行PEIM。PEI阶段对系统的初始化主要由PEIM完成。

        每个PEIM都是一个独立的模块。通过PEIMServices,PEIM可以调用PEI阶段提供的系统服务。通过调用这些服务,PEIM可以访问PEI内核。PEIM之间的通信通过PPI(PEIM-to-PEIM Interfaces)完成。

(五)为什么要有PEI Phase?

1. ROM空间的问题,所有的Code都没有压缩。

2. Memory还没有初始化。

3. Chipset没有初始化。

三、DXE(Driver Execution Environment)驱动执行环境阶段

        DXE阶段执行大部分系统初始化工作,进入此阶段时,已经有足够的内存可以使用,因此可以完成大量的驱动加载和初始化工作。遍历固件中所有的Driver,当Driver所依赖的资源都满足要求时,调度Driver到执行队列执行,直到所有的Driver都被加载和执行完毕,系统完成初始化。

(一)DXE阶段的执行流程

1、DXE Core入口。

VOID
EFIAPI
DxeMain (
  IN  VOID *HobStart
  )

2、根据HOB列表初始化系统服务。

  //
  VectorInfoList = NULL;
  GuidHob = GetNextGuidHob (&gEfiVectorHandoffInfoPpiGuid, HobStart);
  if (GuidHob != NULL) {
    VectorInfoList = (EFI_VECTOR_HANDOFF_INFO *) (GET_GUID_HOB_DATA(GuidHob));
  }
  Status = InitializeCpuExceptionHandlersEx (VectorInfoList, NULL);
  ASSERT_EFI_ERROR (Status);

3、初始化DXE调度程序。

  //
  // Initialize the DXE Dispatcher
  //
  CoreInitializeDispatcher ();

4、负责调用Dispatcher,所有的DXE Driver在这个函数中被检测并执行

  CoreDispatcher ();

5、 传输控制到BDS架构协议。

 DXE阶段的执行流程如下:

(二)DXE阶段的功能

  1. 几乎所有的硬件初始化都在这里完成。
  2. 产生EFI System Table,来提供各种Service供所有阶段使用。
  3. 把控制权交给BDS来BOOT OS。

(三)涉及到的元件及功能

  1. DXE Core:可视为DXE的核心,用来Dispatch DXE Driver和产生EFI System Table,以提供BOOT Service,RunTime Service,DXE Service,负责DXE基础服务和执行流程。
  2. DXE Driver:被DXE Core所读取,用来做各种硬件初始化,产生protocol和其它Service。
  3. DXE Dispatcher:DXE Core的一部分,以正确的顺序来搜寻和执行DXE Driver,负责调度执行DXE驱动,初始化系统设备。DXE提供的基础服务包括系统表、启动服务、Run Time Services。
  4. DXE architecture protocol:由DXE Driver所产生,是DXE Core和HardWare沟通的唯一媒介,所以没有install完全不能开机。
  5. EFI System Table: 包含了许多pointer,如所有EFI System Table,Configuration Table,Handledatabase,Console Device。

(四)DXE Architecture Protocol种类及其功能

   1、Security: 提供 DXE core 验证 firmware volume中的程序是否可用。

   2、Cpu: 提供cpu的service,如管理cache,管理中断,取得处理器频率,查询处理器的timer。

   3、下面是一个结构体,它用来提供一个微小的延时,单位为百万分之一秒。

typedef struct _EFI_METRONOME_ARCH_PROTOCOL {
        EFI_METRONOME_WAIT_FOR_TICK    WaitForTIck;
         UINT32                        TickPeriod;
}EFI_METRONOME_ARCH_PROTOCOL;

  4、Timer: 提供固定时间的中断,使Dxe Core Dispatch 完成所有driver后,会将控制权交给BDS。

四、BDS (Boot Device Select)启动设备选择阶段

该阶段所做的任务:

        在BDS阶段,主要是初始化控制台设备,加载执行必要的设备驱动,根据用户的选择,执行相应的启动项。

概述

        DXE阶段最终会调用BDS ARCH Protocol的接口EFI_BDS_ARCH_PROTOCOL.Entry()转入BDS阶段。BDS阶段负责加载额外的驱动,与用户交互,必要的硬件初始化,并转入操作系统。

        这个阶段前面是DXE后面是OS Loader或shell,同时所有定制化的东西都在这个阶段完成,最主要的Setup page(UI界面)是不同的,不同的BIOS Vendor的UI是不一样的。造成直接影响的三个决策是:启动策略的管理、启动设备的选择、Setup界面。

        从DXE拿到控制权后由用户去选择启动设备,开始转交控制权。在过程中可能失败,为了防止此类现象出现定义了Watch Dog来实时监控状态,如果失败则会返回启动界面,BDS再从下一个启动选项开始启动,直到启动成功,如上图所示。所以在BDS阶段要通过Boot Option这个Variable准备好所有可能的Boot选项,可以提供给用户选择。

        在整个BDS phase还需要提供用户一个可配置的UI界面,该界面包含若干配置选项信息,这些信息完全是客户定制化的,客户想给用户暴露什么信息,就提供什么信息,正常时候报给客户的信息有:启动列表(哪些是设备是可以启动的)、基本的一些配置(Seurite boot如果这个选项是强制选项,那么根本不会暴露给用户)、显示的页面(宽屏或是窄屏)、安全相关的passwd、硬件相关的CPU芯片组的工作,硬盘工作模式等等。

(一)BSD阶段执行流程

BDS阶段是可以用于定制化的module,可以根据客户要求进行相应的设计。

1、Entry()的服务例程。连接好设备,初始化控制台,并尝试启动选项。BDS 的入口函数,负责安装EFI_BDS_ARCH_PROTOCOL 协议,以让DXE Foundation 调用。

VOID
EFIAPI
BdsEntry (
  IN EFI_BDS_ARCH_PROTOCOL  *This
  )

2、Timeout用于在例如设备启动过程中时间的累计计算,如果在时间内一直未成功,则会进行下一个设备启动。

 BootTimeOut = PcdGet16 (PcdPlatformBootTimeOut);

3、函数用于初始化一个加载选项。

 Status = EfiBootManagerInitializeLoadOption (
             &LoadOption,
             LoadOptionNumberUnassigned,
             LoadOptionTypePlatformRecovery,
             LOAD_OPTION_ACTIVE,
             L"Default PlatformRecovery",
             FilePath,
             NULL,
             0
             );

4、在调用平台代码之前初始化ConnectConIn事件。通过PcdGetbool来判定条件进而触发事件函数 (用于实现定制化要求)。

 if (PcdGetBool (PcdConInConnectOnDemand)) {
    Status = gBS->CreateEventEx (
                    EVT_NOTIFY_SIGNAL,
                    TPL_CALLBACK,
                    BdsDxeOnConnectConInCallBack,
                    NULL,
                    &gConnectConInEventGuid,
                    &gConnectConInEvent
                    );
    if (EFI_ERROR (Status)) {
      gConnectConInEvent = NULL;
    }
  }

5、执行平台初始化,可以通过OEM/IBV定制,可以在PlatformBootManagerBeforeConsole中完成的事情。

  PERF_START (NULL, "PlatformBootManagerBeforeConsole", "BDS", 0);

6、基于控制台设备变量ConIn、ConOut和ErrOut连接所有控制台设备。

 EfiBootManagerConnectAllDefaultConsoles ();

7、尝试启动EFI启动选项。这个例程设置L“BootCurent”,并通知EFI准备好启动事件。

EfiBootManagerBoot (&BootManagerMenu);

8、整个启动函数,包括枚举所有boot option ,然后判断UEFI还是Legacy启动,Legacy启动通过读MBI进行;UEFI启动通过读取特定路径下的BOOt信息,根据Boot Option优先级选择设备启动。

BdsBootDeviceSelect ();

(二)BDS Steps

  1. 初始化语言和字符串数据库
  2. 获得当前启动模式
  3. 基于启动模式建立设备清单
  4. 连接设备
  5. 检测input output设备
  6. 执行内存测试
  7. 过程引导选项

(三)执行策略

  1. 初始化控制台console设备,查看系统有多少可以启动的设备。
  2. 启动所有检测到的设备,加载Driver。
  3. 检测启动console设备,即输入输出设备。
  4. 根据收集到的所有信息,提供一个setup的UI界面,终端用户可以在此选择,当用户真正选择启动选项的时候,BDS就会加载启动选项里的OS loader,最后移交真正的控制权给OS Loader ,由OS Loader 转移控制权给OS。

(四)BDS三大任务

console初始化、Driver初始化、BootDeviceSelect

具体包括:

        初始化快捷键服务、初始化SystemTable 中的FirmwareVendor 和FirmwareRevision 域、平台相关BDS 初始化、初始化HwErrRecSupport 系统变量、加载操作系统等。

       当加载项其启动失败时,系统将重新执行DXE Dispatcher以加载更多的驱动,然后重新尝试加载驱动项。BDS策略通过全局NVRAM变量配置,这些变量可以被运行时服务的GetVariable()读取,通过SetVariable()设置(如BootOrder定义了启动顺序,Boot####对应不同的启动项,#为十六进制数)。当用户选中某个启动项(或进入系统默认启动项)后,OS Loader启动,系统进入TSL阶段。

五、TSL(Transient System Load)操作系统加载前期阶段

      TSL阶段是OS Loader执行的第一个阶段,为OS Loader准备执行环境,OS Loader调用ExitBootService结束启动服务,进入RunTime阶段。

        TSL阶段被称为临时系统的原因在于它为操作系统加载器准备执行环境。虽然是临时系统,但是已经具备操作系统的雏形,UEFI Shell是这个临时系统的人机交互界面。正常运行中,系统不会进入UEFI Shell,而是直接执行OS Loader,只有在用户干预或是操作系统加载器出现严重问题时才会进入UEFI Shell。

在TSL阶段,系统资源管理通过BS管理,BS提供的服务如下:

  1. 事件服务:事件是异步操作的基础。有了事件的支持,才可以在UEFI系统内执行并发操作。
  2. 内存管理:主要提供内存的分配与释放,管理系统的内存映射。
  3. Protocol管理:提供了安装Protocol与卸载Protocol的服务,以及注册Protocol通知函数(该函数会在Protocol安装时调用)的服务。
  4. Protocol的实用类服务:包括Protocol的打开与关闭,查找支持protocol的控制器。
  5. 驱动管理:包括用于将驱动安装到控制器的connect服务,及将驱动从控制器上卸载的disconnect服务。
  6. Image管理:此类服务包括加载,卸载,启动和推出UEFI应用程序或驱动。
  7. ExitBootServices:用于结束启动服务,注销BS。

六、 RT(Run Time)运行阶段

        在RT阶段,OS Loader已经完全取得了系统的控制权,因此要清理和回收一些之前被UEFI占用的资源,runtime services随着操作系统的运行提供相应的运行时的服务,这个期间一旦出现错误和异常,将进入AL进行修复。

RT阶段提供的服务如下:

七、AL(After Life)灾难恢复阶段

        在RT阶段如果系统(硬件或是软件)遇到灾难性错误,系统固件需要提供错误处理以及灾难恢复机制,这种机制运行在AL阶段。根据厂家自定义修复方案,UEFI和UEFI PI均未对AL阶段的行为和规范进行定义。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值