Step to UEFI (253)PEIM 调用顺序研究

当EFI 刚出现的时候,模块没有固定的加载顺序是作为优点介绍的,但是运行过程确实需要加载顺序,这一切是通过源代码中每个模块 INF 中[Depex] Section 决定的。前面的文章中提到了PEIM 加载了一些模块,根据 Log 输出的信息对其运行顺序进行排序结果如下:

顺序文件名INF 文件位置Depex
1PcdPeim.efi\MdeModulePkg\Universal\PCD\Pei\Pcd.infTRUE
2ReportStatusCodeRouterPei.efi\MdeModulePkg\Universal\ReportStatusCodeRouter\Pei\ReportStatusCodeRouterPei.infTRUE
3StatusCodeHandlePei.efi\MdeModulePkg\Universal\StatusCodeHandler\Pei\StatusCodeHandlerPei.infTRUE
4PlatformPei.efi\OvmfPkg\PlatformPei\PlatformPei.infTRUE
 (PeiCore.efi) N/A*
5PcdPeim.efi\MdeModulePkg\Universal\PCD\Pei\Pcd.infTRUE
6DxeIpl.efi\MdeModulePkg\Core\DxeIplPeim\DxeIpl.infgEfiPeiLoadFilePpiGuid AND gEfiPeiMasterBootModePpiGuid
7S3Resume2Pei.efi\UefiCpuPkg\Universal\Acpi\S3Resume2Pei\S3Resume2Pei.infTRUE
8CpuMpPei.efi\UefiCpuPkg\CpuMpPei\CpuMpPei.infTRUE
9DxeCore.efi这里已经进入 DXE 了N/A*
*PeiCore和DxeCore 不是 PEIM 所以没有 Depex。

(有兴趣的朋友可以按照上面给出的 INF 文件中的 ENTRY_POINT 处添加 DEBUG)

可以看到,上述的 PEIM 中大部分 DEPEX 都是 TRUE 就是不依赖任何条件。对于这种,猜测是根据压缩打包的前后顺序来决定,前面使用过 UEFITool NE 检查过生成的文件,可以看到大致顺序:

接下来来研究如何改变这个顺序。在 \OvmfPkg\OvmfPkgX64.fdf 文件中有如下代码:

#
#  PEI Phase modules
#
INF  MdeModulePkg/Core/Pei/PeiMain.inf
INF  MdeModulePkg/Universal/PCD/Pei/Pcd.inf
INF  MdeModulePkg/Universal/ReportStatusCodeRouter/Pei/ReportStatusCodeRouterPei.inf
INF  MdeModulePkg/Universal/StatusCodeHandler/Pei/StatusCodeHandlerPei.inf
INF  OvmfPkg/PlatformPei/PlatformPei.inf
INF  MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf
INF  UefiCpuPkg/Universal/Acpi/S3Resume2Pei/S3Resume2Pei.inf
!if $(SMM_REQUIRE) == TRUE
INF  MdeModulePkg/Universal/FaultTolerantWritePei/FaultTolerantWritePei.inf
INF  MdeModulePkg/Universal/Variable/Pei/VariablePei.inf
INF  OvmfPkg/SmmAccess/SmmAccessPei.inf
!endif
INF  UefiCpuPkg/CpuMpPei/CpuMpPei.inf

我们进行一下修改,将 CpuMpPei.inf 放在最前面。

#
#  PEI Phase modules
#
INF  UefiCpuPkg/CpuMpPei/CpuMpPei.inf
INF  MdeModulePkg/Core/Pei/PeiMain.inf
NF  MdeModulePkg/Universal/PCD/Pei/Pcd.inf
INF  MdeModulePkg/Universal/ReportStatusCodeRouter/Pei/ReportStatusCodeRouterPei.inf
INF  MdeModulePkg/Universal/StatusCodeHandler/Pei/StatusCodeHandlerPei.inf
INF  OvmfPkg/PlatformPei/PlatformPei.inf
INF  MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf
INF  UefiCpuPkg/Universal/Acpi/S3Resume2Pei/S3Resume2Pei.inf
!if $(SMM_REQUIRE) == TRUE
INF  MdeModulePkg/Universal/FaultTolerantWritePei/FaultTolerantWritePei.inf
INF  MdeModulePkg/Universal/Variable/Pei/VariablePei.inf
INF  OvmfPkg/SmmAccess/SmmAccessPei.inf
!endif

这样修改之后再次使用工具查看,可以看到 CpuMpPei确实排在第一位:

但是从 Log 上来看CpuMpPei加载位置提前了,但是加载的第一个 PEIM 仍然是 PcdPeim:

The 0th FV start address is 0x00000820000, size is 0x000E0000, handle is 0x820000
Register PPI Notify: 49EDB1C1-BF21-4761-BB12-EB0031AABB39
Register PPI Notify: EA7CA24B-DED5-4DAD-A389-BF827E8F9B38
Install PPI: B9E0ABFE-5979-4914-977F-6DEE78C278A6
Install PPI: DBE23AA9-A345-4B97-85B6-B226F1617389
DiscoverPeimsAndOrderWithApriori(): Found 0x7 PEI FFS files in the 0th FV
Loading PEIM 9B3ADA4F-AE56-4C24-8DEA-F03B7558AE50
Loading PEIM at 0x0000083A7A0 EntryPoint=0x0000083AC68 PcdPeim.efi
Install PPI: 06E81C58-4AD7-44BC-8390-F10265F72480
Install PPI: 01F34D25-4DE2-23AD-3FF3-36353FF323F1
Install PPI: 4D8B155B-C059-4C8F-8926-06FD4331DB8A
Install PPI: A60C6B59-E459-425D-9C69-0BCC9CB27D81
Register PPI Notify: 605EA650-C65C-42E1-BA80-91A52AB618C6
Loading PEIM EDADEB9D-DDBA-48BD-9D22-C1C169C8C5C6
Loading PEIM at 0x00000820120 EntryPoint=0x00000821364 CpuMpPei.efi
Register PPI Notify: F894643D-C449-42D1-8EA8-85BDD8C65BDE
Loading PEIM A3610442-E69F-4DF3-82CA-2360C4031A23
Loading PEIM at 0x00000840220 EntryPoint=0x000008406E8 ReportStatusCodeRouterPei.efi
Install PPI: 0065D394-9951-4144-82A3-0AFC8579C251
Install PPI: 229832D3-7A30-4B36-B827-F40CB7D45436
Loading PEIM 9D225237-FA01-464C-A949-BAABC02D31D0
Loading PEIM at 0x00000842D20 EntryPoint=0x000008431A8 StatusCodeHandlerPei.efi

进一步研究,在【参考1】有提到:

“在PEIM Dispatcher寻找可启动的PEIM时,会先在每一个FV上定位Apriori文件,然后读取文件内容来查找PEIM的GUID,确保Apriori文件中的PEIM被首先调用。在OvmfPkgX64.fdf中我们可以看到FV.PEIFV中有以下内容:

APRIORI PEI {

INF  MdeModulePkg/Universal/PCD/Pei/Pcd.inf

}“

就是说 PeiDispatcher() 函数会先分析 Apriori 文件,然后首先执行其中指定的PEIM,对应的 OvmfPkgX64.fdf 中指定了 PcdPeim.efi,所以这个文件会首先被执行到。

      if (Private->CurrentPeimCount == 0) {
        //
        // When going through each FV, at first, search Apriori file to
        // reorder all PEIMs to ensure the PEIMs in Apriori file to get
        // dispatch at first.
        //
        DiscoverPeimsAndOrderWithApriori (Private, CoreFvHandle);
      }

接下来编写一个最简单的 PEI Driver, 其中只有输出信息的动作:

  DEBUG ((DEBUG_INFO, "LABZ Test PEIM\n"));

修改如下2个文件加入OVMF 的编译:

1.OvmfPkgX64.dsc

!if $(SMM_REQUIRE) == TRUE
      LockBoxLib|MdeModulePkg/Library/SmmLockBoxLib/SmmLockBoxPeiLib.inf
!endif
  }
  
  OvmfPkg/TestPei/TestPei.inf
  
!if $(SMM_REQUIRE) == TRUE
  MdeModulePkg/Universal/FaultTolerantWritePei/FaultTolerantWritePei.inf
  MdeModulePkg/Universal/Variable/Pei/VariablePei.inf
  OvmfPkg/SmmAccess/SmmAccessPei.inf
!endif

2.OvmfPkgX64.fdf

INF  MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf
INF  UefiCpuPkg/Universal/Acpi/S3Resume2Pei/S3Resume2Pei.inf
!if $(SMM_REQUIRE) == TRUE
INF  MdeModulePkg/Universal/FaultTolerantWritePei/FaultTolerantWritePei.inf
INF  MdeModulePkg/Universal/Variable/Pei/VariablePei.inf
INF  OvmfPkg/SmmAccess/SmmAccessPei.inf
!endif
INF  OvmfPkg/TestPei/TestPei.inf

!if $(TPM_ENABLE) == TRUE
INF  OvmfPkg/Tcg/TpmMmioSevDecryptPei/TpmMmioSevDecryptPei.inf
INF  OvmfPkg/Tcg/Tcg2Config/Tcg2ConfigPei.inf
INF  SecurityPkg/Tcg/TcgPei/TcgPei.inf
INF  SecurityPkg/Tcg/Tcg2Pei/Tcg2Pei.inf
!endif

运行结果如下:

Loading PEIM 89E549B0-7CFE-449D-9BA3-10D8B2312D71
Loading PEIM at 0x00007ECD000 EntryPoint=0x00007ECD5C8 S3Resume2Pei.efi
Install PPI: 6D582DBC-DB85-4514-8FCC-5ADF6227B147
Loading PEIM 122C386D-5ABC-4FB4-B124-FBB82488ACF4
Loading PEIM at 0x00007ECA000 EntryPoint=0x00007ECA470 TestPei.efi
DiscoverPeimsAndOrderWithApriori(): Found 0x0 PEI FFS files in the 1th FV
DXE IPL Entry
Loading PEIM D6A2CB7F-6A18-4E2F-B43B-9920A733700A
Loading PEIM at 0x00007EA3000 EntryPoint=0x00007EA3D78 DxeCore.efi
Loading DXE CORE at 0x00007EA3000 EntryPoint=0x00007EA3D78

之后,尝试将这个PEIM放在APRIORI 中最前面:

APRIORI PEI {
  INF  OvmfPkg/TestPei/TestPei.inf
  INF  MdeModulePkg/Universal/PCD/Pei/Pcd.inf
}

运行结果如下:

The 0th FV start address is 0x00000820000, size is 0x000E0000, handle is 0x820000
Register PPI Notify: 49EDB1C1-BF21-4761-BB12-EB0031AABB39
Register PPI Notify: EA7CA24B-DED5-4DAD-A389-BF827E8F9B38
Install PPI: B9E0ABFE-5979-4914-977F-6DEE78C278A6
Install PPI: DBE23AA9-A345-4B97-85B6-B226F1617389
DiscoverPeimsAndOrderWithApriori(): Found 0x8 PEI FFS files in the 0th FV
Loading PEIM 122C386D-5ABC-4FB4-B124-FBB82488ACF4
Loading PEIM at 0x0000085A820 EntryPoint=0x0000085AC90 TestPei.efi
Loading PEIM 9B3ADA4F-AE56-4C24-8DEA-F03B7558AE50
Loading PEIM at 0x0000083A7A0 EntryPoint=0x0000083AC68 PcdPeim.efi
Install PPI: 06E81C58-4AD7-44BC-8390-F10265F72480
Install PPI: 01F34D25-4DE2-23AD-3FF3-36353FF323F1
Install PPI: 4D8B155B-C059-4C8F-8926-06FD4331DB8A
Install PPI: A60C6B59-E459-425D-9C69-0BCC9CB27D81
Register PPI Notify: 605EA650-C65C-42E1-BA80-91A52AB618C6

可以看到 TestPei 先于PcdPeim运行。

总结:

  1. fdf 文件中 APRIORI PEI  指定的优先级最高;
  2. 模块中 INF 文件 Depex Section 可以指定依赖关系;
  3. 同等优先级的由fdf中的先后顺序决定。

特别强调上述只是为了实验学习之用,在实际工作中请勿轻易调整 PEIM 顺序避免导致奇怪的问题。另外,对于PEIM 调用顺序还可以参考 PI Spec 中 “PEI Dispatcher Introduction”章节描述。

本文使用的自己编写的 PEIM 如下:

参考:

1. https://blog.csdn.net/weixin_39945816/article/details/111506524 uefi下的开机顺序_UEFI启动过程与协议加载顺序

发表评论

您的电子邮箱地址不会被公开。