当前位置: 首页 > news >正文

【UEFI基础】BIOS模块执行的优先级

综述

BIOS下主要通过两种方式来确定一般模块的优先级,一种是fdf文件中指定的优先级,另一种是inf文件中指定的优先级。需要注意这里使用了“一般模块”的说法,因为有些模块(尤其是PEI_CORE,DXE_CORE类型的模块)总是会先执行的,事实上就是因为这些优先执行的模块在控制一般模块的优先级。

fdf中的优先级

APRIORI

fdf中的优先级通过特殊的标识来说明,下面是一个例子:

[FV.DXEFV]
# 中间略APRIORI DXE {INF  MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.infINF  MdeModulePkg/Universal/PCD/Dxe/Pcd.inf# AmdSevDxe must be loaded before TdxDxe. Because in SEV guest AmdSevDxe# driver performs a MemEncryptSevClearMmioPageEncMask() call against the# PcdPciExpressBaseAddress range to mark it shared/unencrypted.# Otherwise #VC handler terminates the guest for trying to do MMIO to an# encrypted region (Since the range has not been marked shared/unencrypted).INF  OvmfPkg/AmdSevDxe/AmdSevDxe.infINF  OvmfPkg/TdxDxe/TdxDxe.inf
!if $(SMM_REQUIRE) == FALSEINF  OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
!endif
}

这里的APRIORI就指定了需要优先执行的模块。

在编译时这个部分会被组成一个Firmware File,上例中可以从DXEFV中找到这个Firmware File,下面是该文件中的实际数据:

在这里插入图片描述

这些数据实际上是一个个的GUID,来自包含模块中的inf文件中的FILE_GUID

[Defines]INF_VERSION                    = 0x00010005BASE_NAME                      = DevicePathDxeMODULE_UNI_FILE                = DevicePathDxe.uniFILE_GUID                      = 9B680FCE-AD6B-4F3A-B60B-F59899003443 # Firmware File中包含的GUIDMODULE_TYPE                    = DXE_DRIVERVERSION_STRING                 = 1.0ENTRY_POINT                    = DevicePathEntryPoint

而这个Firmware File本身也有一个GUID:

在这里插入图片描述

这个GUID实际上是固定的,定义在MdePkg\Include\Guid\Apriori.h中:

#define EFI_APRIORI_GUID \{ \0xfc510ee7, 0xffdc, 0x11d4, {0xbd, 0x41, 0x0, 0x80, 0xc7, 0x3c, 0x88, 0x81 } \}
extern EFI_GUID gAprioriGuid;

这个gAprioriGuid将会在代码中进一步使用,来获取上面提到的APRIORI文件中的GUID,以确定哪些模块需要优先执行。

代码处理gAprioriGuid

相关代码可以在edk2\MdeModulePkg\Core\Dxe\DxeMain.inf中的CoreDispatcher()找到:

    //// Read the array of GUIDs from the Apriori file if it is present in the firmware volume//AprioriFile = NULL;Status      = Fv->ReadSection (Fv,&gAprioriGuid,EFI_SECTION_RAW,0,(VOID **)&AprioriFile,&SizeOfBuffer,&AuthenticationStatus);if (!EFI_ERROR (Status)) {AprioriEntryCount = SizeOfBuffer / sizeof (EFI_GUID);} else {AprioriEntryCount = 0;}//// Put drivers on Apriori List on the Scheduled queue. The Discovered List includes// drivers not in the current FV and these must be skipped since the a priori list// is only valid for the FV that it resided in.//for (Index = 0; Index < AprioriEntryCount; Index++) {for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) {DriverEntry = CR (Link, EFI_CORE_DRIVER_ENTRY, Link, EFI_CORE_DRIVER_ENTRY_SIGNATURE);if (CompareGuid (&DriverEntry->FileName, &AprioriFile[Index]) &&(FvHandle == DriverEntry->FvHandle)){CoreAcquireDispatcherLock ();DriverEntry->Dependent = FALSE;DriverEntry->Scheduled = TRUE;InsertTailList (&mScheduledQueue, &DriverEntry->ScheduledLink);CoreReleaseDispatcherLock ();DEBUG ((DEBUG_DISPATCH, "Evaluate DXE DEPEX for FFS(%g)\n", &DriverEntry->FileName));DEBUG ((DEBUG_DISPATCH, "  RESULT = TRUE (Apriori)\n"));break;}}}

代码也非常的简单:

  • 获取GUID。
  • 遍历GUID。
  • 遍历找到的所有模块,与指定的GUID匹配,如果匹配到了就放到mScheduledQueue

以上只是第一步,即存放优先模块,在edk2\MdeModulePkg\Core\Dxe\Dispatcher\Dispatcher.c文件的头部对应如下的说明:

Step #1 - When a FV protocol is added to the system every driver in the FV
is added to the mDiscoveredList. The SOR, Before, and After Depex are
pre-processed as drivers are added to the mDiscoveredList. If an Apriori
file exists in the FV those drivers are addeded to the
mScheduledQueue. The mFvHandleList is used to make sure a
FV is only processed once.

主要是这一句:

If an Apriori file exists in the FV those drivers are addeded to the mScheduledQueue.

在执行时:

EFI_STATUS
EFIAPI
CoreDispatcher (VOID)
{// 其它次要代码已经略去do {//// Drain the Scheduled Queue//while (!IsListEmpty (&mScheduledQueue)) {// 获取模块DriverEntry = CR (mScheduledQueue.ForwardLink,EFI_CORE_DRIVER_ENTRY,ScheduledLink,EFI_CORE_DRIVER_ENTRY_SIGNATURE);// 加载模块Status = CoreLoadImage (FALSE,gDxeCoreImageHandle,DriverEntry->FvFileDevicePath,NULL,0,&DriverEntry->ImageHandle);// 执行之后移除魔魁啊DriverEntry->Scheduled   = FALSE;DriverEntry->Initialized = TRUE;RemoveEntryList (&DriverEntry->ScheduledLink);if (DriverEntry->IsFvImage) {//// Produce a firmware volume block protocol for FvImage so it gets dispatched from.//Status = CoreProcessFvImageFile (DriverEntry->Fv, DriverEntry->FvHandle, &DriverEntry->FileName);} else {// 执行模块Status = CoreStartImage (DriverEntry->ImageHandle, NULL, NULL);}ReturnStatus = EFI_SUCCESS;}} while (ReadyToRun);

这里有两个循环,第二个while循环中就是先执行mScheduledQueue中的模块。对应edk2\MdeModulePkg\Core\Dxe\Dispatcher\Dispatcher.c文件的头部的说明:

Step #2 - Dispatch. Remove driver from the mScheduledQueue and load and
start it. After mScheduledQueue is drained check the
mDiscoveredList to see if any item has a Depex that is ready to
be placed on the mScheduledQueue.

主要对应第一句:

Dispatch. Remove driver from the mScheduledQueue and load and start it.

inf中的优先级

并不是所有的模块都可以包含依赖关系,有些模块的依赖关系即使写了也会被忽略,在《edk-ii-inf-specification.pdf》中有如下的说明:

  • If the Module is a Library, then a [Depex] section is optional.
    If the Module is a Library with a MODULE_TYPE of BASE, the generic (i.e., [Depex]) and generic with only architectural modifier entries (i.e., [Depex.IA32]) are not permitted. It is permitted to have a Depex section if one ModuleType modifier is specified (i.e., [Depex.common.PEIM).
  • If the ModuleType is USER_DEFINED , then a [Depex] section is optional. If a PEI, SMM or DXE DEPEX section is required, the user must specify a ModuleType of PEIM to generate a PEI_DEPEX section, a ModuleType of DXE_DRIVER to generate a DXE_DEPEX section, or a ModuleType of DXE_SMM_DRIVER to generate an SMM_DEPEX section.
  • If the ModuleType is SEC, UEFI_APPLICATION, UEFI_DRIVER, PEI_CORE, SMM_CORE or DXE_CORE, no [Depex] sections are permitted and all library class [Depex] sections are ignored.
  • Module types PEIM, DXE_DRIVER, DXE_RUNTIME_DRIVER, DXE_SMM_DRIVER require a DXE_SAL_DRIVER and [Depex] section unless the dependencies are specified by a PEI_DEPEX , DXE_DEPEX or SMM_DEPEX in the [Binaries] section.

生成depex文件

inf中有一个Section包含了依赖关系,下面是一个例子(来自beni\BeniPkg\Dxe\DxeDriverInBds\DxeDriverInBds.inf):

[Depex]gEfiPciIoProtocolGuid

也就是说要执行本模块,前提是gEfiPciIoProtocolGuid这个GUID已经安装。

在编译模块时,会生成一个特定的文件(通过edk2\BaseTools\Source\Python\AutoGen\GenDepex.py执行相关操作,它也是AutoGen的一部分),名称格式是“模块名.depex”,本例就是DxeDriverInBds.depex:

在这里插入图片描述

上图高亮的就是gEfiPciIoProtocolGuid这个GUID。不过这里还是有几点需要说明:

  • 首先GUID前面有一个02,它表示的是OPCODE,在edk2\BaseTools\Source\Python\AutoGen\GenDepex.py可以看到更多的OPCODE:
    Opcode = {"PEI"   : {DEPEX_OPCODE_PUSH  :   0x02,DEPEX_OPCODE_AND   :   0x03,DEPEX_OPCODE_OR    :   0x04,DEPEX_OPCODE_NOT   :   0x05,DEPEX_OPCODE_TRUE  :   0x06,DEPEX_OPCODE_FALSE :   0x07,DEPEX_OPCODE_END   :   0x08},"DXE"   : {DEPEX_OPCODE_BEFORE:   0x00,DEPEX_OPCODE_AFTER :   0x01,DEPEX_OPCODE_PUSH  :   0x02,DEPEX_OPCODE_AND   :   0x03,DEPEX_OPCODE_OR    :   0x04,DEPEX_OPCODE_NOT   :   0x05,DEPEX_OPCODE_TRUE  :   0x06,DEPEX_OPCODE_FALSE :   0x07,DEPEX_OPCODE_END   :   0x08,DEPEX_OPCODE_SOR   :   0x09},

02表示的是DEPEX_OPCODE_PUSH,03表示的是DEPEX_OPCODE_AND,08表示的是DEPEX_OPCODE_END

  • 这里还有第二个GUID,对应gEfiPcdProtocolGuid
  ## Include/Protocol/PiPcd.hgEfiPcdProtocolGuid = { 0x13a3f0f6, 0x264a, 0x3ef0, { 0xf2, 0xe0, 0xde, 0xc5, 0x12, 0x34, 0x2f, 0x34 } }

不确定为什么要包含这个GUID,并且当[Depex]下只有TRUE的时候,也有这个GUID:

在这里插入图片描述

另外值得注意的一点是,在fdf文件中,也会将这个PCD模块包含在APRIORI中,因为PCD是一种基本的模式,为了所有的模块都能够支持PCD,有这样的依赖也是可以理解的。

将depex文件包含到BIOS二进制中

通过查看fdf可以知道depex是如何被包含的,主要是通过Rules这种[Section],比如一个DXE_DRIVER,它生成的ffs文件结构遵循的Rule如下:

[Rule.Common.DXE_DRIVER]FILE DRIVER = $(NAMED_GUID) {DXE_DEPEX    DXE_DEPEX Optional      $(INF_OUTPUT)/$(MODULE_NAME).depexPE32     PE32                    $(INF_OUTPUT)/$(MODULE_NAME).efiUI       STRING="$(MODULE_NAME)" OptionalVERSION  STRING="$(INF_VERSION)" Optional BUILD_NUM=$(BUILD_NUMBER)RAW ACPI  Optional               |.acpiRAW ASL   Optional               |.aml}

即首先是一个depex文件,然后是一个efi文件,如下所示:

在这里插入图片描述

代码处理

模块中没有一个特定的GUID(像gAprioriGuid)来指定依赖,不过depex本来就是ffs中的一部分,所以是可以读出来的,在一个描述模块的结构体中有成员来表示这个depex(以DXE为例):

typedef struct {// 其它略VOID                             *Depex;		// 描述依赖关系UINTN                            DepexSize;	// depex的大小
} EFI_CORE_DRIVER_ENTRY;

所以在得到模块的时候我们就已经能够知道它依赖的GUID了,而这些模块通过一个全局变量mDiscoveredList构成了一个链表,后续会通过遍历这个链表来进行各种操作。

真正处理依赖关系的代码同样在CoreDispatcher()

EFI_STATUS
EFIAPI
CoreDispatcher (VOID)
{// 其它次要代码已经略去do {//// Drain the Scheduled Queue//while (!IsListEmpty (&mScheduledQueue)) {// 首次执行的时候,执行fdf中的优先模块// 后面的操作又会往mScheduledQueue里面放更多的模块,又会继续执行}//// Search DriverList for items to place on Scheduled Queue//ReadyToRun = FALSE;for (Link = mDiscoveredList.ForwardLink; Link != &mDiscoveredList; Link = Link->ForwardLink) {DriverEntry = CR (Link, EFI_CORE_DRIVER_ENTRY, Link, EFI_CORE_DRIVER_ENTRY_SIGNATURE);if (DriverEntry->DepexProtocolError) {//// If Section Extraction Protocol did not let the Depex be read before retry the read//// 会将满足依赖的模块继续放入mScheduledQueueStatus = CoreGetDepexSectionAndPreProccess (DriverEntry);}if (DriverEntry->Dependent) {if (CoreIsSchedulable (DriverEntry)) {CoreInsertOnScheduledQueueWhileProcessingBeforeAndAfter (DriverEntry);ReadyToRun = TRUE;}}}} while (ReadyToRun);

总体来说,对于依赖关系的处理如下:

在这里插入图片描述

其它

开始的时候说过依赖关系主要有两种,实际上还是有一些衍生的方式。比如在inf中的优先级中有提到depex文件,它可以有不同的用法,在inf中包含[Depex]可以生成depex文件,也可以手动生成(通过edk2\BaseTools\Source\Python\AutoGen\GenDepex.py),然后直接放到fdf中来为某个文件指定依赖,这种情况对于在fdf中直接包含efi文件时时很有用的,下面是一个例子:

FILE DRIVER = 5BBA83E5-F027-4ca7-BFD0-16358CC9E123 {SECTION PE32 = $(PLATFORM_FEATURES_PATH)/Icc/IccOverClocking/IccOverClocking.efiSECTION DXE_DEPEX = $(PLATFORM_FEATURES_PATH)/Icc/IccOverClocking/IccOverClocking.depexSECTION UI = "IccOverClocking"}

http://www.mrgr.cn/news/27474.html

相关文章:

  • 【Python模拟websocket登陆-拆包封包】
  • 大模型基础BERT——Transformers的双向编码器表示
  • Vite初始化Vue3+Typescrpt项目
  • ElasticSearch-全文检索(一)基本介绍
  • Python爬虫----python爬虫基础
  • 欧拉开机显示机器信息官方脚本
  • matlab delsat = setdiff(1:69,unique(Eph(30,:))); 语句含义
  • 二十天刷leetcode【hot100】算法- day2[后端golang]
  • 文件的应用实例
  • Python 解析 JSON 数据
  • C/C++内存管理——内存泄漏/内存碎片
  • Ubuntu 22.04.5 LTS 发布下载 - 现代化的企业与开源 Linux
  • 接入 API 接口之前,你必须清楚的那些事儿
  • 第十二周:机器学习笔记
  • 资料分析(2021-2024国考)
  • C语言:链表
  • C#命令行参数解析库System.CommandLine介绍
  • 9.15学习记录
  • [记录一个bug]流媒体服务瓶颈排查
  • 腾讯云技术深度探索:构建高效云原生微服务架构
  • 华为项目管理培训产品总监兼首席架构师刘钊受邀为第四届中国项目经理大会演讲嘉宾
  • 13 Midjourney从零到商用·进阶篇:灯光、角度与风格等精细控制方法
  • EDC与 ClearingHouse 相关的库和模块
  • 工作流activiti笔记(三)坑!!!手把手!!
  • 安全第一:API 接口接入前的防护性注意要点
  • Python:抓取 Bilibili(B站)评论、弹幕、字幕等