EFI引导

最近遇到的很多问题都和UEFI启动有关,调研了下UEFI的启动流程

一般认为,UEFI由以下几个部分组成:

uefi bios设置自动开机 uefi bios 自动开机_初始化

BIOS自检

Pown On 阶段 这一阶段从上电开始到屏幕出现信息结束,也就是所谓的激活电源阶段。主板上的控制芯片组会向CPU发出并保持一个RESET(重置)信号,让CPU内部自动恢复到初始状态,但CPU在此刻不会马上执行指令。当芯片组检测到电源已经开始稳定供电了这一阶段的主要任务是校验CMOS中的内容是否正确、检查主机上某些硬件的状态以确定下一步的自检,因此,用户无法在屏幕上看到BIOS信息(要等硬件确认后才激活VGA) ,若这个阶段出现错误一般都是致命的(通常为黑屏),只能通过喇叭声和故障灯来判断错误类型。这个阶段只是检查系统上都有哪些设备,并不初始化。系统BIOS所在的ROM是被设计成CPU可直接寻址的,而且地址范围也是固定的,从F0000H至FFFFFH共64KB。

uefi bios设置自动开机 uefi bios 自动开机_初始化_02

各厂家的BIOS程序不尽相同,但基本都是完成POST自检及本地设备的枚举和初始化,包括对硬件执行一系列的测试,用来检测现在都有什么设备以及这些设备是否能正常工作,在这个阶段中,会显示一些信息,例如BIOS版本号等;初始化硬件设备,这个阶段在现代基于PCI的体系结构中相当重要,因为它可以保证所有的硬件设备操作不会引起IRQ线与I/O端口的冲突,在本阶段的最后,会显示系统中所安装的所有PCI设备的一个列表等。在这个阶段如果是热重启,那L1、L2内存就不需要重新初始化,来加快启动速度。

BIOS自检和初始化完成后,开始执行BIOS引导程序。由于系统BIOS空间只有64KB大小,把Linux内核放在这个空间里让BIOS引导程序直接引导是不可能的,只能把内核放在硬盘里(或其他设备,如USB或网络上, BIOS根据启动顺序的设置依次查找),然后再从硬盘里引导Linux内核。但是,这时系统还处于实模式中,寻址能力只有1MB,没有硬盘上的文件系统等信息,不会直接的引导整个Linux内核,而是通过先载入一个引导装入程序,然后由这个引导装入程序来引导Linux内核,本文主要介绍EFI引导。

uefi bios设置自动开机 uefi bios 自动开机_c语言_03

安全验证阶段(SEC)

以临时RAM初始化为边界,SEC阶段又分为两个部分:临时RAM生效之前称为Reset Vector阶段,临时RAM生效后调用SEC入口函数从而进入SEC功能区、其中Reset Vector的执行流程如下:

加电–>进入固件入口–>从实模式转换到32位平坦模式(包含模式)–>定位固件中的BFV–>定位BFV中的SEC映像–>若是64位系统,从32位转换到64位模式–>调用SEC入口函数–>PEI入口函数

系统运行在SEC阶段时,仅CPU和CPU内部资源被初始化,各种外部设备和内存都还没被初始化,因而需要一些临时RAM区域,用于代码和数据的存取(SEC最后及PEI阶段为C语言环境,C语言中的局部变量需要堆栈,这一步是为后续C语言环境准备好堆栈。一句话,C语言是基于栈的程序设计),我们称之为临时RAM,以示与内存的区别。这些RAM只能位于CPU内部。最常用的临时RAM是Cache,当Cache被配置为no-eviction模式时,可以作为内存使用,读命中时返回Cache中的数据,读缺失时不会向主存发出缺页事件;写命中时将数据写入Cache中,写缺失时不会向主存发出缺失事件,这种技术被称为CAS。

UEFI 系统开机或重启进入 SEC 阶段,它执行以下四种任务:

  1. **接收并处理系统启动和重启信号:**系统加电信号、系统重启信号、系统运行过程中的严重异常信号。
  2. **初始化临时存储区域:**在 SEC 阶段时,仅 CPU 和其内部(SOC)资源被初始化,外围设备和内存均为初始化,因此需要临时 RAM 区域。临时 RAM 初始化之后才能进入 SEC 的入口函数。
  3. **作为可信系统的根:**SEC 能被系统信任,之后的各个阶段才有被信任的基础。
  4. **传递系统参数给下一阶段(PEI):**需将如下信息作为参数传递给 PEI 的入口函数。

在进入EFI前会将传递系统参数给PEI阶段:SEC阶段的一切工作都是为PEI阶段做准备,最终要把控制权交给PEI阶段,同时要将现阶段的成果汇报给PEI。汇报的手段就是将如下信息作为参数传递给PEI的入口函数:

  • 系统当前状态,PEI可以根据这些状态判断系统的健康状况;
  • 可启动固件(Boot Firmware Volume,BFV)的地址和大小;
  • 临时RAM区域的地址和大小;
  • 栈的地址和大小;

Pre-EFI初始化模块(PEI)

Pre-EFI初始化程序在系统开机的时候最先得到执行,它负责最初的CPU芯片组主存的初始化工作,比较重要的是MMU芯片,负责内存虚拟化技术,这个阶段内存未初始化,使用L1、L2作为临时内存,EI阶段对系统的初始化主要是由PEIM完成的(CPU初始化、Chipset初始化、内存控制器初始化、IO控制器初始化、内存初始化等功能)。每个PEIM是一个独立模块,模块的入口函数传入两个参数,FileHandlePeiServices。通过PEIServices,PEIM可以使用PEI阶段提供的系统服务,通过这些服务,PEIM可以访问PEI Core。PEIM之间通过PPI(PEIM-to-PEIM Interface)完成。PEI阶段主要工作为:

  • 加载定位PEIM
  • 提供PEIM之间的通信方式
  • 为后续阶段提供资源和数据

uefi bios设置自动开机 uefi bios 自动开机_加载_04

列如Pcdpeim的作用是构建pcd database并生成PPI,PlatformPei则是用来平台设备驱动匹配,此外PEI阶段还会初始化做够大的RAM线性内存供DXE阶段使用MemoryInit则是用来初始化内存。

在PEI阶段对总线进行初初始化,并设置好ddr频率,并将内存初始化完成。

uefi bios设置自动开机 uefi bios 自动开机_加载_05

随后开启MMU功能内存初始化完成,当内存初始化后,系统会发生栈切换并重新进入PeiCore,重新进入到PeiCore后使用的内存是我们熟悉的内存。初始化芯片组并通过DxeIpl进入DXE,开始枚举UEFI驱动器。

uefi bios设置自动开机 uefi bios 自动开机_linux_06

PEI 阶段执行流程 :

PEI入口–>初始化PEI Core Services–>Dispatch PEIM(CPU,Chipset,Memory,IO等的初始化)–>准备HOB列表,利用DXEIPL PPI进入DXE–>DXE入口函数

uefi bios设置自动开机 uefi bios 自动开机_初始化_07

UEFI驱动程序执行环境(DXE)

从 DXEIPL进入DXE的核心服务DXECore开始加载UEFI的驱动程序执行环境(DXE)DXE阶段执行大部分系统的初始化工作。当DXE被加载运行时,系统便具有了枚举并加载其他UEFI驱动程序的能力。DXE枚举并加载各种总线(包括PCI、SATA、USB、ISA)及硬件的UEFI驱动程序。

DXe分为两个阶段

  • **DXE 内核(Dxecore):**负责 DXE 基础服务和执行流程。
  • **DXE 派遣器(pcddxe):**负责调度执行 DXE 驱动,初始化系统设备。

PEI阶段通过构建HOB数据,把系统相关的信息传递给DXE阶段,包括但不限于:PEI阶段的内存申请记录、闪存卷(FV)信息、可用内存资源信息、DXE模块数据。

uefi bios设置自动开机 uefi bios 自动开机_linux_08

DXE阶段主要使用的几类HOB数据:

  • 可用内存资源信息,类型为EFI_HOB_TYPE_RESOURCE_DESCRIPTOR,用于初始化内存申请与回收服务,提供申请和回收内存的方法
  • DXE模块数据,用于初始化镜像服务,提供加载、解析和执行文件的方法
  • 闪存卷信息,FlashVolume,对每个闪存卷建立一个PROTOCOL用于读取数据。所有的驱动数据从这里面读取,然后调度执行。

随后加载smbiosDxesmbios表转换为系统表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CvoS08q8-1648085581151)(img/image-20220323160024795.png)]

dxe过程中加载的模块含义
Dispatcher/ DXE调度器
DxeMain/ DXE阶段入口函数
Event/ 异步事件机制
FwVol/ 闪存文件系统FFS
FwVolBlock/ 闪存的块操作
Gcd/ 全局配置数据库
Hand/ 句柄Handle
Image/ 镜像文件服务,加载、解析和执行
Library/ 库函数,三个与lock相关的操作
Mem/ 内存服务,申请和释放
Misc/ 其他内容,看门狗、
SectionExtrantion/
DxeCore.uni 字符串数据
DxeCoreExtra.uni 字符串数据
DxeMain.h 头文件
DxeMain.inf 模块信息文件

在Dxe过程中会加载异步事件服务,所以事件可以并发执行,DXE驱动之间通过Protocol通信,Protocol是一种特殊的结构体,每个Protocol都有一个对应的GUID,利用BootServices的OpenProtocol(HandleProtocol或LocateProtocol),并根据GUID来打开对应的Protocol,进而使用这个Protocol提供的服务。DXE框架如下:

  • 启动服务Boot Services
  • 内存服务 Memory
  • 异步事件服务 Event Timer TPL
  • 镜像服务 Image
  • 句柄和协议服务 Handle and Protocol
  • 运行时服务 Runtime Services
  • 变量服务 Variable

当所有的DXE Drivers都执行完成后,系统完成初始化,DXE通过EFI_BDS_ARCH_PROTOCOL找到BDS并调用BDS的入口函数,从而进入到BDS阶段Dxe执行流程如下。

uefi bios设置自动开机 uefi bios 自动开机_uefi bios设置自动开机_09

执行启动策略阶段(BDS)

BDS阶段顾名思义,主要功能是执行启动策略,BIOS在这一阶段引导操作系统并将控制权交给操作系统。

  • 初始化控制台设备(ConsoleIn/ConsoleOut,USB/PS2键盘鼠标,VGA等)
  • 加载必要的设备驱动(PCI枚举动作在这一阶段被执行)
  • 根据系统设置加载和执行启动项

如果加载启动项失败,系统将重新执行 DXE dispatcher 来加载更多的驱动,然后重新尝试加载启动项。

uefi bios设置自动开机 uefi bios 自动开机_c语言_10

单所有设备加载完BIOS开始引导操作系统,将控制权转交给操作系统

uefi bios设置自动开机 uefi bios 自动开机_linux_11

TSL阶段

TSL(Transient System Load)是操作系统加载器(OS Loader)执行的第一阶段。该阶段内 OS Loader 作为 UEFI 的应用程序运行,系统资源仍由 UEFI 内核控制,UEFI shell和grub就是在这个阶段。

uefi bios设置自动开机 uefi bios 自动开机_初始化_12

RT 阶段

RT(Run Time)阶段,系统的控制权从 UEFI 内核转到 OS Loader,UEFI 占用的各种资源被回收到 OS Loader。UEFI内核开始将控制权转交给操作系统内核。

TSL 阶段的目的是为 OS Loader 准备执行环境,具备操作系统的雏形,UEFI Shell 是这个檩式系统的人机交互界面,正常情况下不会进入。通过BOOT.efi引导系统