Step to UEFI (256)运行两次的 PEIM

在查看 Log 的时候,发现一个有趣的地方:

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
………………….
Notify: PPI Guid: EE16160A-E8BE-47A6-820A-C6900DB0250A, Peim notify entry point: 847814
PlatformPei: ClearCacheOnMpServicesAvailable
Loading PEIM 9B3ADA4F-AE56-4C24-8DEA-F03B7558AE50
Loading PEIM at 0x00007ED8000 EntryPoint=0x00007ED84C8 PcdPeim.efi
Reinstall PPI: 06E81C58-4AD7-44BC-8390-F10265F72480
Reinstall PPI: 4D8B155B-C059-4C8F-8926-06FD4331DB8A
Reinstall PPI: 01F34D25-4DE2-23AD-3FF3-36353FF323F1
Reinstall PPI: A60C6B59-E459-425D-9C69-0BCC9CB27D81

就是说在 PEI 阶段,PcdPeim.efi 运行了两次。本着探究的精神着手研究原因。

首先研究了一下 PeiCore 的部分,唯一可以确定的是:第二次运行是在内存Ready 后的PeiDispatcher() 函数中:

         if (Status == EFI_SUCCESS) {
            //
            // PEIM_STATE_REGISTER_FOR_SHADOW move to PEIM_STATE_DONE
            //
            Private->Fv[Index1].PeimState[Index2]++;
            //
            // Call the PEIM entry point
            //
            PeimEntryPoint = (EFI_PEIM_ENTRY_POINT2)(UINTN)EntryPoint;
			
            PERF_START_IMAGE_BEGIN (PeimFileHandle);
            PeimEntryPoint(PeimFileHandle, (const EFI_PEI_SERVICES **) &Private->Ps);
            PERF_START_IMAGE_END (PeimFileHandle);
          }

于是目光转到 PcdPei 本身,在\MdeModulePkg\Universal\PCD\Pei\Pcd.c 文件中PcdPeimInit() 函数里面可以看到,运行之后调用了 PeiServicesRegisterForShadow() 函数:

EFI_STATUS
EFIAPI
PcdPeimInit (
  IN       EFI_PEI_FILE_HANDLE  FileHandle,
  IN CONST EFI_PEI_SERVICES     **PeiServices
  )
{
  EFI_STATUS Status;
  Status = PeiServicesRegisterForShadow (FileHandle);
  if (Status == EFI_ALREADY_STARTED) {
    //
    // This is now starting in memory, the second time starting.
    //
    EFI_PEI_PPI_DESCRIPTOR *OldPpiList;
    EFI_PEI_PPI_DESCRIPTOR *OldPpiList2;
    VOID *Ppi;
    VOID *Ppi2;

猜测第二次运行就是因为这个函数的缘故,修改之前的TestPei 加入PeiServicesRegisterForShadow() 函数:

EFI_STATUS
EFIAPI
InitializeTestPei (
  IN       EFI_PEI_FILE_HANDLE  FileHandle,
  IN CONST EFI_PEI_SERVICES     **PeiServices
  )
{
  EFI_STATUS Status;
  
  DEBUG ((DEBUG_INFO, "LABZ Test PEIM\n"));

  Status = PeiServicesRegisterForShadow (FileHandle);
  if (Status == EFI_ALREADY_STARTED) {
          DEBUG ((DEBUG_INFO, "LABZ Test RUN AGAIN\n"));
  }
  return EFI_SUCCESS;
}

运行之后查看 Log:

iscoverPeimsAndOrderWithApriori(): Found 0x8 PEI FFS files in the 0th FV
Loading PEIM 122C386D-5ABC-4FB4-B124-FBB82488ACF4
Loading PEIM at 0x0000085A920 EntryPoint=0x0000085ADA8 TestPei.efi
LABZ Test PEIM
Loading PEIM 9B3ADA4F-AE56-4C24-8DEA-F03B7558AE50
Loading PEIM at 0x0000083A8A0 EntryPoint=0x0000083AD68 PcdPeim.efi
Install PPI: 06E81C58-4AD7-44BC-8390-F10265F72480
Install PPI: 01F34D25-4DE2-23AD-3FF3-36353FF323F1
……………………
Notify: PPI Guid: EE16160A-E8BE-47A6-820A-C6900DB0250A, Peim notify entry point: 847914
PlatformPei: ClearCacheOnMpServicesAvailable
Loading PEIM 122C386D-5ABC-4FB4-B124-FBB82488ACF4
Loading PEIM at 0x00007EDB000 EntryPoint=0x00007EDB488 TestPei.efi
LABZ Test PEIM
LABZ Test RUN AGAIN
Loading PEIM 9B3ADA4F-AE56-4C24-8DEA-F03B7558AE50
Loading PEIM at 0x00007ED5000 EntryPoint=0x00007ED54C8 PcdPeim.efi
Reinstall PPI: 06E81C58-4AD7-44BC-8390-F10265F72480
Reinstall PPI: 4D8B155B-C059-4C8F-8926-06FD4331DB8A
Reinstall PPI: 01F34D25-4DE2-23AD-3FF3-36353FF323F1

可以看到 TestPei 运行了2次。

在\mdepkg\library\peiserviceslib\PeiServicesLib.c可以看到 PeiServicesRegisterForShadow() 函数定义:

/**
  This service is a wrapper for the PEI Service RegisterForShadow(), except the
  pointer to the PEI Services Table has been removed.  See the Platform
  Initialization Pre-EFI Initialization Core Interface Specification for details.

  @param FileHandle             PEIM's file handle. Must be the currently
                                executing PEIM.

  @retval EFI_SUCCESS           The PEIM was successfully registered for
                                shadowing.

  @retval EFI_ALREADY_STARTED   The PEIM was previously
                                registered for shadowing.

  @retval EFI_NOT_FOUND         The FileHandle does not refer to a
                                valid file handle.
**/
EFI_STATUS
EFIAPI
PeiServicesRegisterForShadow (
  IN  EFI_PEI_FILE_HANDLE FileHandle
  )
{
  return (*GetPeiServicesTablePointer())->RegisterForShadow (FileHandle);
}

本质上是使用 EFI_PEI_SERVICE 中的RegisterForShadow来实现的,这个函数的作用是注册一个 PEIM, 当内存 Ready时会再次执行。

【参考1】

参考:

1. https://uefi.org/sites/default/files/resources/PI_Spec_1_7_A_final_May1.pdf Platform Initialization Specification, Vol. 1

发表评论

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