入门UEFI

UEFI基础

1. UEFI系统的组成

UEFI提供给操作系统的接口包括启动服务(Boot Service,BS)、运行时服务(Runing service,RT)以及丰富的接口,详细的组成,可以参考下面的接口图例:
入门UEFI_第1张图片

2. UFEI的启动阶段

UEFI系统的启动遵循UEFI平台初始化的标准,UEFI系统从上电到关机分为7个部分
入门UEFI_第2张图片

2.1 SEC阶段:(Security Phase)安全验证

  1. 接受系统的启动、重启、异常信号
  2. Cache AS RAM(CAR),在Cache上开辟一段空间作为内存使用(此时内存还没初始化,相关C语言运行需要内存和栈的空间)
  3. 传递系统参数给PEI阶段

2.2 PEI阶段:(EFI前期的初始化)

此阶段主要是为DXE阶段做的相关准备工作

  1. 做CPU和相关硬件的初始化,最主要的是内存初始化
  2. 将DXE阶段需要的参数以HOB列表形式进行封装,并传递给DXE阶段

2.3 DXE阶段:(驱动执行环境)

此阶段主要是进行大量的驱动加载和初始化工作

  1. 通过对固件中所有Driver的遍历,当Driver
  2. 当Driver都被执行完成了,系统即完成了初始化

2.4 BDS阶段:(启动设备选择)

此阶段主要初始化控制台设备

  1. 加载必要的设备驱动
  2. 根据用户选择执行相应启动项

OptionRom驱动实在DXE阶段进行的加载,并拷贝到内存中,但是OptionRom中的程序,具体会在BDS阶段才会运行起来。

2.5 TSL阶段:(操作系统加载前期)

此阶段是OS Loader执行的第一个阶段

  1. 为OS Loader准备执行环境
  2. OS Loader调用EXITBootService结束启动服务
  3. 进入RT阶段(RunTime)阶段

TSL是操作系统加载器(OS Loader)执行的第一阶段,在这个阶段OS Loader作为一个UEFI的应用程序在与逆行ExitBootService()服务被调用后,系统进入到Run time阶段。

2.6 RT阶段:(OS环境执行时期)

此阶段主要是RT随着操作系统运行提供相应服务

  1. OS已经完全获得控制权,RT会清理和回收一些之前UEFI占用资源
  2. 这一阶段运行出现错误时,将进入RL修复

系统进入到RT(Run Time)阶段后,系统的控制权从UEFI内核转交到OS Loader手中,UEFI占用的各种资源被回收到OS Loader手中。

2.7 AL阶段:(灾难恢复)

在RT阶段,如果系统(硬件或者软件)遇到灾难性错误,系统固件需要提供错误处理和灾难恢复机制,这种机制运行在AL(After Life)阶段,但是这个阶段的服务在spec中是没有被定义的。

3. 应用程序如何被编译为xxx.efi文件的?

xxx.c文件首先被编译为xxx.obj目标文件
链接器将目标文件和其他库文件链接成为xxx.dll文件
GenFw工具将xxx.dll文件转换成为.efi文件

xxx.c ------> xxx.obj ------> xxx.dll ------> xxx.efi

4. xxx.efi文件是如何被加载到内存的?

在shell中执行.efi文件的时候,shell会首先调用gBS->LoadImage()将.efi文件加载到内存中生成Image对象;然后调用gBS->StartImgae(image)驱动这个Image对象。
StartImage的主要作用就是找出可执行程序映像Image的入口函数,并执行找到的入口函数。
gBS->StartImage的核心是Image->EntryPoint(…),它是程序的入口函数,对应程序来说,就是_ModuleEntryPoint函数,进入_ModuleEntryPoint后,控制权才转交给应用程序。

5. UEFI的各种文件

对于Linux系统下面是用Makefile生成elf文件已经bin文件,UEFI文件的编译,主要使用build以及GenFW命令。
build命令用于编译包,它需要一个.dsc文件,一个.dec文件以及多个.inf文件;
GenFw命令用于制作固件或者OptionRom Image,它需要一个.dec文件以及一个.fdf文件,整体文件的编译过程如下所示:
入门UEFI_第3张图片

dsc文件的构成有着它自己的规则,它的存在类似于VS中的package管控,在我的理解下它类似于一个大的Linux工程下面要运行的一个脚本文件,管控了整个工程的编译方式,也可以把它理解为Linux工程下顶层目录下面的Makefile,它的参数可以向下面传递,可以将定义的参数以及全局变量传递给下层的.inf文件。

dec文件主要定义了公开的数据结构和接口,提供给其他的模块来使用。它里面定义了很多的全局性变量,可以提供给整个Package使用,类似于Linux下面链接器脚本定义的全局变量,不同的连接器脚本中定义的变量可以进行赋值,dec基本作用就是定义变量的值,该变量是否被使用还要看程序中是否有声明。

.fdf文件,只有在使用GenFW命令生成optionRom的时候,才会用到。它与linux下面的链接器脚本.lds文件的功能,有类似的地方。

一套UEFI源码,只有一个fdf文件,它规定把那些包编入到flash中,也可以指定编入flash的什么位置,并且会为这个Package分配唯一的一个guid,方便代码的调用。

6. UEFI 模块编译过程

入门UEFI_第4张图片

UEFI工程文件
EDK2两个概念:Module模块、Package包。UEFI源码里有很多以*Pkg命名的文件夹,每个可以称为Package。Package包是一组模块及平台描述符文件(.dsc)和声明文件(.dec)组成。

.dsc描述符
.dec声明文件
.efi生成的可执行文件
.inf元数据文件

App例:

文件1: app.c

#include  

EFI_STATUS
EFIAPI
MyHello(
  IN EFI_HANDLE           ImageHandle,     //参数固定
  IN EFI_SYSTEM_TABLE     *SystemTable
  )
  {
    SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Hello World!\n");
    return EFI_SUCCESS;
}

文件2: app.inf

##
[Defines]
  INF_VERSION                    = 0x00010006 //INF标准的版本号
  BASE_NAME                      = Hello
  FILE_GUID                      = //从https://www.guidgen.com线上获取
  MODULE_TYPE                    = UEFI_APPLICATION
  VERSION_STRING                 = 0.1
  ENTRY_POINT                    = MyHello //入口函数

[Sources]
  app.c

[Packages]
  MdePkg/MdePkg.dec

[LibraryClasses]
  UefiLib
  UefiApplicationEntryPoint

.efi生产过程 app.c —> app.obj —> app.dll —> app.efi

  • .dll生成时使用了_ModuleEntryPoint
  • .dll生成.efi用GenFw工具
  • .efi是遵循PE32格式的二进制文件
  • _ModuleEntryPoint是二进制文件函数入口

执行app.efi调用过程
ShellPkg/Application/Shell/ShellProtocol.c

InternalShellExecuteDevicePath()
{
	gBS->LoadImage() //生成image
	gBS->OpenProtocol() //获取参数
	gBS->StartImage() //加载image
}

gBS->StartImage() 跳转到入口函数MyHello 并执行,gBS->StartImage是函数指针,指向CoreStartImage()函数,位于MdeModulePkg/Core/Dxe/Image/Image.c;里。主要函数是Image->EntryPoint(),它是函数入口也就是_ModuleEntryPoint,进入_ModuleEntryPoint后控制权交给应用程序。_ModuleEntryPoint函数位于MdePkg/Library/UefiDriverEntryPoint/DriverEntryPoint.c

回顾整个过程:
StartImage—>_ModuleEntryPoint—>ProcessModuleEntryPointList—>MyHello

参考:UEFI原理与编程

你可能感兴趣的:(UEFI学习,开发语言,uefi)