BIOS 背景
BIOS 全称为“基本输入/输出系统”,它是存储在主板 ROM 里的一组程序代码,这些代码包括:
- 加电自检程序,用于开机时对硬件的检测;
- 系统初始化代码,包括硬件设备的初始化,创建 BIOS 中断;
- 基本的外围 I/O 处理的子程序代码;
- CMOS 设置程序。
BIOS 程序运行在 16 位实模式下,实模式下最大的寻址范围是1MB,0x0C0000~0x0FFFFF
保留给 BIOS 使用。开机后,CPU跳到 0x0FFFF0
处执行,一般这里是一条跳转指令,跳到真正的 BIOS 入口处执行:
(1)BIOS代码首先做的是加电自检(Power On Self Test,POST)主要是检测关机设备是否正常工作,设备设置是否与CMOS中的设置一致,如果发现硬件错误,则通过喇叭报警;
(2)POST检测通过后初始化显示设备并显示显卡信息,接着初始化其他设备;
(3)设备初始化完毕后开始检查 CPU 和内存并显示检测结果;
(4)内存检测通过以后开始检测标准设备,例如硬盘、光驱、串口设备、并口设备等。
(5)然后检测即插即用设备,并为这些设备分配中断号、I/O 端口和 DMA通道等资源。如果硬件配置发生变化,那么这些变化的配置将更新到CMOS中。
(6)随后,根据配置的启动顺序从设备启动,将启动设备主引导记录的启动代码通过 BIOS中断读入内存,然后控制权交到引导程序手中,最终引导进入操作系统。
BIOS 存在的问题:
(1)开发效率低:大部分 BIOS 代码使用汇编开发,开发效率不言而喻。汇编开发的另一个缺点是使得代码与设备的耦合程度太高,代码受硬件变化的影响大;
(2)性能差:BIOS基本输入/输出服务需要通过中断来完成,开销大,并且BIOS没有提供异步工作模式,大量的时间消耗在等待上。
(3)功能扩展性差,升级缓慢:BIOS代码采用静态链接,增加硬件功能时,必须将16位代码放置在 0x0C0000~0x0DFFFF
区间,初始化时将其设置为约定的中断处理程序。而且 BIOS 没有提供动态加载设备驱动的方案。
(4)安全性:BIOS 运行过程中对可执行代码没有安全方面的考虑;
(5)不支持从硬盘 2TB 以上的地址引导:受限于BIOS硬盘的寻址方式,BIOS 硬盘采用 32 位地址,因而引导扇区的最大逻辑地址位 2 的 32 次方(换算成字节地址,就是 2 TB = 2 ^ 32 * 512,512 是扇区大小)
UEFI
UEFI(Unified Extensible Firmware Interface,统一可扩展固件接口)定义了操作系统和平台固件之间的接口,它是 UEFI Forum 发布的一种标准。它只是一种标准,没有提供实现。
UEFI实现一般可分为两部分:
(1)平台初始化(遵循 Platform Initialization 标准,同样由 UEFI Forum 发布)。
(2)固件-操作系统接口。
UEFI 系统组成
UEFI 提供给操作系统的接口包括:**启动服务(BootServices,BS)和运行时服务(RuntimeService,RT)**以及隐藏在 BS 之后的丰富的 Protocol。
BS 和 RT 以表的形式(C语言中的结构体)存在。UEFI 驱动和服务以 Protocol 的形式通过 BS 提供给操作系统。
从操作系统加载器(OS Loader)被加载,到 OS Loader 执行 ExitBootServices() 的这段时间,是从 UEFI 环境向操作系统过渡的过程。在这个过程中,OS Loader 可以通过 BS 和 RT 使用 UEFI 提供的服务,将计算机系统资源逐渐转移到自己手中,这个过程称为 TSL(Transient System Load)。
当 OS Loader 完全掌握了计算机系统资源时,BS 也就完成了它的使命。OS Loader 调 ExitBootServices()结束 BS 并回收 BS 占用的资源,之后计算机系统进入 UEF Runtime 阶段。
在 Runtime 阶段只有运行时服务继续为 OS 提供服务,BS 已经从计算机系统中销毁。
在 TSL阶段,系统资源通过 BS 管理,BS 提供的服务如下:
(1)事件服务:异步操作,在 UEFI 系统内执行并发操作;
(2)内存管理:主要提供内存的分配与释放服务,管理系统内存映射;
(3)Protocol 管理:提供了 Protocol 的安装与卸载服务,以及注册 Protocol 通知函数的服务;
(4)Protocol 使用类服务:Protocol 打开、关闭,以及查找支持 Protocol 的控制器;
(5)驱动管理:包括用于将驱动安装到控制器的 connect 服务,以及将驱动从控制器上卸载的 disconnect 服务;
(6)Image 管理:包括加载、卸载、启动和推出 UFEI 应用程序和驱动;
(7)ExitBootServices:用于结束启动服务;
RT 提供的服务:
(1)时间服务:读取、设定系统时间;
(2)读写 UEFI 系统变量:读取、设置系统变量,通过这些系统变量可以保存系统配置;
(3)虚拟内存服务:将物理地址转换为虚拟地址;
(4)其他服务:包括重启系统的 ResetSystem,获取系统提供的下一个单调单增值等。
UEFI 相对于 BIOS 的优势:
(1)UEFI 绝大部分代码采用 C 语言编写,UEFI 应用程序和驱动也可以使用 C++ 编写;UEFI 通过固件-操作系统接口(BS 和 RT 服务)为 OS 和 OS 加载器屏蔽了底层硬件细节,使得 UEFI 上层应用可以方便重用;
(2)可扩展性:一是驱动的模块化设计,每个驱动是一个独立的模块,可以包含在固件中,也可以放在设备上,运行时根据需要动态加载;二是软硬件升级的兼容性,每个表、Protocol 都有版本号;
(3)性能提升:一是提供了基于事件的异步操作,提高了CPU利用率;二是舍弃了中断这种比较耗时的操作外部设备的方式,仅仅保留了时钟中断,外部设备采用"时间+异步操作"完成;
(4)安全性:当系统的安全启动功能被打开后,UEFI 在执行应用程序和驱动前会先检测程序和驱动的证书,仅当证书被信任时才会执行这个应用程序或驱动。UEFI应用程序和驱动采用 PE/COFF 格式,其签名放在签名块中。
PE的全称是Portable Executable,用中文就是“可移植、可执行的”。
COFF的全称是Common Object File Format,用中文描述是“通用对象文件结构”。
PE 文件 和 COFF 文件格式可以看一下这个专栏:PE文件和COFF文件格式分析,写的很好
UEFI 系统的启动过程
UEFI 系统的启动遵循 UEFI Platform Initialization 标准,从加电到关机分为七个阶段:
前三个阶段是 UEFI 初始化阶段,DXE 阶段结束后 UEFI 环境已经准备完毕。
BDS 和 TSL 是操作系统加载器作为 UEFI 应用程序运行的阶段。操作系统加载器调用 ExitBootServices() 服务后进入 RT 阶段,RT阶段包括操作系统加载器后期和操作系统运行期。
当系统硬件或操作系统出现严重错误不能继续正常运行时,固件会尝试修复错误,这时系统进人 AL 期。但 PI 规范和 UEFI 规范都没有规定 AL 期的行为。“?” 号表示其行为由系统供应商自行定义。
1.SEC 阶段
安全阶段:SEC(Security) 阶段是平台初始化的第一个阶段,计算机系统加电后进入这个阶段。
UEFI 系统开机或重启进入 SEC 阶段,SEC 功能:
(1)接收并处理系统启动和重启信号:系统加电信号、系统重启信号、系统运行过程中的严重异常信号;
(2)初始化临时存储区域:系统运行在SEC阶段时,仅CPU和CPU内部资源被初始化,各种外部设备和内存都没有被初始化,因而系统需要一些临时 RAM 区域,用于代码和数据的存取,将之称为临时 RAM,以示与内存的区别。这些临时 RAM只能位于CPU内部。最常用的临时RAM是Cache,当Cache被配置为 no-eviction 模式时,可以作为内存使用,读命中时返回 Cache 中的数据,读缺失时不会向主存发出缺失事件;写命中时将数据写入 Cahce,写缺失时不会向主存发出缺失事件,这种技术称为 CAR(Cache As Ram)。
(3)作为可信系统的根:作为取得对系统控制权的第一部分,SEC 阶段是整个可信系统的根。SEC能被系统信任,以后的各个阶段才有被信任的基础。通常,SEC在将控制权转移给 PEI(Pre EFl Initialization Environment,EFI 前期初始化)之前,可以验证 PEI。
(4)传递系统参数给下一阶段(即 PEI):SEC 阶段的一切工作都是为 PEI 阶段做准备最终 SEC要把控制权转交给PEI,同时要将现阶段的成果汇报给 PEI,如系统当前状态,PEI 可以根据这些状态判断系统的健康状况,可启动固件(Boot Firmware Volume),临时 RAM 区域以及栈的地址和大小等。
SEC 执行流程:
以临时 RAM 初始化为界,SEC的 执行又分为两大部分:临时 RAM 生效之前称为 Reset Vector 阶段,临时 RAM 生效后调用 SEC 入口函数从而进入 SEC 功能区。
其中 Reset Vector 的执行流程如下:
1)进入固件入口;
2)从实模式转换到 32 位平坦模式(包含模式);
3)定位固件中的 BFV(Boot Firmware Volume);
4)定位 BFV 中的 SEC 映像;
5)若是 64 位系统,从 32 位模式转换到 64 位模式。
6)调用 SEC 入口函数。
2.PEI 阶段
EFI 前期初始化(Pre EFI Initialization Environment)阶段资源仍然十分有限,内存到了 PEI 后期才被初始化,主要功能是为 DXE(Driver Execution Environment,驱动执行环境) 准备执行环境,将需要传递到 DXE 的信息组成 HOB(Handoff Block) 列表,最终将控制权转交到 DXE 手中。
PEI阶段将数据打包成数据块存放在一段连续的内存中,数据块的标识为GUID,DXE阶段可以通过该GUID在HOB中找到对应数据块。
参考博客:UEFI——HOB简单使用
PEI 从功能上可以分为两个部分:
1.PEI 内核(PEI Foundation):负责 PEI 基础服务和流程;
2.PEIM(PEI Module)派遣器:主要功能是找出系统中的所有 PEIM,并根据 PEIM 之间的依赖关系顺序执行 PEIM。PEI 阶段对系统的初始化主要由 PEIM 完成的。
每个 PEIM 是一个独立的模块,通过 PeiServices,PEIM 可以使用 PEI 阶段提供的系统服务,通过这些系统服务,PEIM 可以访问 PEI 内核。PEIM 之间的通信通过 PPI(PEIM-to-PEIM Interfaces) 完成。
PPI 与 DXE 阶段的 Protocol 类似,每个 PPI 是一个结构体,包含了函数指针和变量;
每个 PPI 都有一个 GUID。根据 GUID,通过 PeiServices 的 LocatePpi 服务可以得到 GUID 对应的 PPI 实例。
UEFI的一个重要特点是其模块化的设计,模块载入内存后生成 Image。
Image 的入口函数为 _ModuleEntryPoint。PEI 也是一个模块,PEI Image的入口函数 _ModuleEntryPoint
,位于 MdePkg/Library/PeimEntryPoint/PeimEntryPoint.c
。
_ModuleEntryPoint 最终调用 PEI 模块的入口函数 PeiCore
,位于MdeModulePkg/Core/Pei/PeiMain/PeiMain.c
。
进人 PeiCore
后,首先根据从 SEC 阶段传入的信息设置 PeiCore Services,然后调用 PeiDispatcher 执行系统中的 PEIM,当内存初始化后,系统会发生栈切换并重新进入PeiCore。重新进入 PeiCore 后使用的内存为我们所熟悉的内存。所有 PEIM 都执行完毕后,调用 PeiServices 的 LocatePpi 服务得到 DXE IPL PPI,并调用DXE IPL PPI的 Entry 服务,这个Entry 服务实际上是 DxeLoadCore,它找出 DXE Image 的入口函数,执行 DXE Image 的入口函数并将 HOB 列表传递给 DXE。
DXE IPL 是DXE Initial Program Loader 的简称。PI 规范规定 PEI 阶段必须有一个Architecture PPI,称为 DXE IPL PPI, PEI Foundation 通过DXE IPL PPI 向DXE 阶段交权。当PEI Foundation dispatch 完所有的PEIM 后,调用DXE IPL PPI. DXE IPL PPI 主要任务是负责定位并装载DXE Foundation, 然后将控制权交给 DXE Foundation。
3.DEX 阶段
驱动执行环境:DXE(Driver Execution Environment) 阶段执行大部分系统初始化工作,进入此阶段时内存已经可以被完全使用,因而此阶段可以进行大量的复杂工作。
DEX 从功能上可以分为两部分:
DEX 内核:负责 DXE 基础服务和执行流程;
DXE 派遣器:负责调度执行 DXE 驱动,初始化系统设备。
DXE 提供的基础服务包括系统表、启动服务、Run Time Services。每个 DXE 驱动是一个独立的模块,DXE 驱动之间通过 Protocol 通信。Protocol 是一种特殊的结构体,每个 Protocol 对应一个 GUID,利用系统 BootServices 的 OpenProtocol,并根据 GUID 来打开对应的 Protocol,进而使用这个 Protocol 提供的服务。
当所有的 Driver 都执行完毕后,系统完成初始化,DXE 通过 EFI_BDS_ARCH_PROTOCOL 找到 BDS 并调用 BDS 的入口函数,从而进入 BDS 阶段。从本质上讲,BDS 是一种特殊的 DXE 阶段的应用程序。
4.BDS 阶段
BDS(Boot Device Selection)的主要功能是执行启动策略,其主要功能包括:
1.初始化控制台设备;
2.加载必要的设备驱动;
3.根据系统设置加载和执行启动项。
如果加载启动项失败,系统将重新执行 DXE dispatcher
以加载更多的驱动,然后重新尝试加载启动项。BDS 策略通过全局 NVRAM 变量配置。这些变量可以通过运行时服务的 GetVariable()
读取,通过 SetVariable()
设置。例如,变量 BootOrder 定义了启动顺序,变量 Boot####
定义了各个启动项(####
为4个十六进制大写符号)。
用户选中某个启动项(或系统进入默认的启动项)后,OS Loader 启动,系统进入 TSL 阶段。
5.TSL 阶段
TSL(Transient System Load) 是操作系统加载器(OS Loader)执行的第一阶段,在这一阶段 OS Loader 作为一个 UEFI 应用程序运行,系统资源仍然由 UEFI 内核控制。当启动服务的 ExitBootServices() 服务被调用后,系统进人 RunTime 阶段。
TSL 阶段之所以称为临时系统,在于它存在的目的就是为操作系统加载器准备执行环境。虽然是临时系统,但其功能已经很强大,已经具备了操作系统的雏形,UEFI Shell 是这个临时系统的人机交互界面。正常情况下,系统不会进入 UEFI Shell,而是直接执行操作系统加载器,只有在用户干预下或操作系统加载器遇到严重错误时才会进入 UEFIShell。
6.RT 阶段
系统进入 RT(Run Time) 阶段后,系统的控制权从 UEFI 内核转交到 OSLoader 手中,UEFI 占用的各种资源被回收到 OS Loader,仅有 UEFI 运行时服务保留给 OS Loader 和 OS 使用。随着 OS Loader 的执行,OS 最终取得对系统的控制权。
7.AL 阶段
在 RT 阶段,如果系统(硬件或软件)遇到灾难性错误,系统固件需要提供错误处理和灾难恢复机制,这种机制运行在 AL(Afer Life) 阶段。UEFI 和 UEFI PI 标准都没有定义此阶段的行为和规范。
以上内容均来自于:《UEFI 原理与编程》