BIOS追code之PEI phase

PEI

  • 阶段总述
    • PEI阶段的功能任务:
    • PEI划分:
  • PEI阶段执行流程:
    • 流程描述及流程图
    • PEI Phase– Boot Flow
  • 代码分析
    • PEI service instance
    • PeiCore
    • PEI Dispatcher

阶段总述

PEI(Pre-EFI Initialization,预先EFI初始化),虽然SEC阶段对CPU和CPU内的资源进行了初始化,但是PEI阶段可用的资源依旧十分有限,该阶段对内存进行初始化,主要功能是为DXE阶段准备执行环境,将所需要传递给DXE的信息组成HOB(Hand Off Block)列表,最终将控制权转交到DXE。

UEFI具有模块化设计的特点,PEI就是一个模块。PEI Image的入口函数调用PEI模块的入口函数PEICore。

PEI阶段的功能任务:

初始化内存。
为DXE阶段准备执行环境。
简单而言分为以下几点:

  1. 基本的Chipset初始化
  2. Memory Sizing
  3. BIOS Recovery
  4. S3 Resume
  5. 切换Stack到Memory (Disable CAR, Enable Cache)
  6. 启动DXEIPL(DXE Initial Program Loader)

PEI划分:

PEI内核(PEI Foundation):负责PEI基础服务和流程。
PEIM(PEI Module)派遣器:找出系统中的所有PEIM,并根据PEIM之间的依赖关系按顺序执行PEIM。PEI阶段对系统的初始化主要由PEIM完成。
每个PEIM都是一个独立的模块。通过PEIServices,PEIM可以调用PEI阶段(UEFI?)提供的系统服务。通过调用这些服务,PEIM可以访问PEI内核。PEIM之间的的通信通过PPI(PEIM-to-PEIM Interfaces)完成。

PEI阶段执行流程:

  • 进入PEI入口。
  • 根据SEC阶段传入的信息初始化PS(PEI Core Service)。
  • 调度系统中的PEIM(PEI Module),准备HOB列表。
  • 具体调用的系统中的PEIM有:
  1. CPU PEIM(提供CPU相关功能,如进行Cache设置、主频设置等);
  2. 平台相关PEIM(初始化内存控制器、I/O控制器等);
  3. 内存初始化PEIM(对内存进行初始化,此时内存才可用,之前使用的CPU模拟的临时内存)。
  4. 调用PEIServices得到DEX IPL PPI的Entry服务(即DEXLoadCore)。
    注:PPI与DEX阶段的Protocol类似,每个PPI都是一个结构体,包含有函数指针和变量。每个PPI都有一个GUID。通过PEIServices的LocatePPI服务可以找到GUID对应的PPI实例。
  • DXELoadCore服务找出并运行DXEImage的入口函数,将HOB列表传递给DXE。

流程描述及流程图

  1. SEC模块找到PEI Image的入口函数 _ModuleEntryPoint(函数位于MdePkg/Library/PeimEntryPoint/PeimEntryPoint.c)
  2. _ModuleEntryPoint函数最终调用PEI模块的入口函数PEICore
  3. 进入PEICore后,首先根据从SEC阶段出入的信息设置PEI Core Services,
  4. 调用PEIDispatcher执行系统总的PEIM, 在内存初始化完成后,系统切换栈并重新进入PEICore。(重新进入PEICore后使用的不再是 临时RAM 而是真正的内存。)
  5. 在所有PEIM执行完成后,调用PEIServices的LocatePPI服务得到DXE IPL PPI, 并调用DXE IPL
  6. PPI的Entry服务(即DEXLoadCore), 找出DEX Image的入口函数,执行DXE Image函数并将HOB列表传递给DXE。

PEI阶段执行流程如下图:

BIOS追code之PEI phase_第1张图片

BIOS追code之PEI phase_第2张图片

PEI Phase– Boot Flow

  • PEI Core – PEI内核,负责PEI基础服务和流程
  • PEIM(PEI Module)– 可执行的二进制代码模块,封装着一些关于Processor、Chipset、device或者是平台相关的一些功能
  • PPI(PEIM toPEIM Interface)– PEIM与PEIM之间沟通的接口
  • PEI Dispatcher – PEI派遣器,主要功能是找出系统中的所以PEIM,并根据PEIM之间的依赖关系按顺序执行PEIM
    BIOS追code之PEI phase_第3张图片

代码分析

在SEC阶段的最后,根据函数PeiCore()跳转,该函数位于MdeModulePkg\Core\Pei\PeiMain\PeiMain.c,(现在mem没有初始化,还在cache中执行,后续内存初始化之后,这部分代码在PeiCore.efi中,还会再执行一遍。)
PEI

PEI service instance

PEI 基本服务接口记录在一个数据结构,其补称为PEI Service Table. PeiMain 提供这些基本的服务,PEI 模块可以使用这些服务来做硬件的初始化。但是由于PEI阶段内存还没有初始化,多数服务都是运行在临时RAM上,所以PEI 服务都是一些最基本的服务,并不像之后的DXE 服务那么丰富。而且由于没有内存可以使用,PEI 服务表也需要通过指针传递给每个PEI模块的入口函数,让每个PEI模块可以使用。PEI 模块服务可以包括以下七类,每个服务的介绍将在后续章节中和代码一起进行展开。

  1. PPI 服务:PPI 是指PEI 模块之间的接口。PPI 管理的相关操作,安装,加载及通知,从而方便不同的PEI模块之间可以相互使用彼此的服务。PPI 列表由PeiMain 进行维护。
  2. 启动模式服务: 管理系统的启动模式,比如应急启动,待机启动。
  3. HOB 服务: 创建数据结构来传递需要的数据到下一个阶段(DXE阶段)。HOB 就是载体,被称为传递块,其目的是用来记录需要传递的数据。
  4. FV(固件标签)服务: 管理多个FV空间,并发现存储的PEI模块。FV是存储代码和数据的逻辑空间,并有固件组组结构的文件系统。FV基本的存储单元可以放置PEI模块的二进制代码或其他的数据。
  5. 内存服务:提供内存管理服务,比如申请内存空间等,需要在内存初始化之前和之后都有可以使用的内存管理服务。
  6. 状态编码报告服务:提供普通的进展及错误状态编码报告服务,这些服务可以被输出到端口0x80或者串口为用户提供调用信息。
  7. 重启服务:提供通用的系统重启服务,包括冷启动,热启动等。
    参考文章链接:https://blog.csdn.net/robinsongsog/article/details/102653826
EFI_PEI_SERVICES  gPs = {
  {
    PEI_SERVICES_SIGNATURE,
    PEI_SERVICES_REVISION,
    sizeof (EFI_PEI_SERVICES),
    0,
    0
  },
  // 这个函数通过GUID在PEI PPI数据库中安装一个接口。服务的目的是发布一个接口,其他各方可以使用该接口调用额外的PEIMs。
  PeiInstallPpi,
  // 这个函数通过GUID在PEI PPI数据库中重新安装一个接口。服务的目的是发布一个接口,其他各方可使用该接口用不同的接口替换协议数据库中同名的接口。
  PeiReInstallPpi,
  // locate一个给定的指定PPI。
  PeiLocatePpi,
  // 此函数安装通知服务,以便在安装或重新安装给定接口时回调该服务。
  // 服务的目的是发布一个接口,其他各方可以使用该接口来调用以后可能实现的其他PPIs。
  PeiNotifyPpi,

  // 该服务使PEIMs能够确定引导模式的当前值。
  PeiGetBootMode,
  // 该服务使PEIMs能够更新启动模式变量。
  PeiSetBootMode,

  // 获取指向HOB列表的指针。
  PeiGetHobList,
  // 向HOB列表中添加一个新的HOB。
  PeiCreateHob,
  
  // 通过索引搜索固件卷
  PeiFfsFindNextVolume,
  // 在固件卷中搜索下一个匹配文件。
  PeiFfsFindNextFile,
  // 在指定的文件中搜索下一个匹配部分。
  PeiFfsFindSectionData,

  // 该功能向PEI Foundation注册发现的内存配置。
  // 使用模型是发现永久内存的PEIM将调用此服务。
  // 这个例程将把发现的内存信息保存到PeiCore的私有数据中,并设置SwitchStackSignal标志。
  // 在分配了发现内存的PEIM后,PeiDispatcher将临时内存迁移到永久内存。
  PeiInstallPeiMemory, 
  // 该服务的目的是发布一个接口,允许PEIMs分配由PEI Foundation管理的内存范围。
  PeiAllocatePages,
  // 内存池分配服务。在发现永久内存之前,将在临时内存中为池分配堆。通常,临时内存中的堆大小不会超过64K,因此可以分配的最大池大小是64K。
  PeiAllocatePool,
  (EFI_PEI_COPY_MEM)CopyMem,
  (EFI_PEI_SET_MEM)SetMem,

  // 状态代码报告程序的核心版本
  PeiReportStatusCode,
  // 核心版本的复位系统
  PeiResetSystem,
  
  // 这个默认的EFI_PEI_CPU_IO_PPI安装实例分配给EFI_PEI_SERVICE。PeiCore初始化时的CpuIo。
  &gPeiDefaultCpuIoPpi,
  // 将EFI_PEI_PCI_CFG2_PPI安装的默认实例分配给EFI_PEI_SERVICE。PeiCore初始化时的PciCfg。
  &gPeiDefaultPciCfg2Ppi,
  
  // 根据卷的名称查找卷中的文件
  PeiFfsFindFileByName,
  // 返回关于特定文件的信息。
  PeiFfsGetFileInfo,
  // 返回指定卷的信息。这个函数返回关于特定固件卷的信息,包括它的名称、类型、属性、起始地址和大小。
  PeiFfsGetVolumeInfo,
  // 当PEI Foundation发现永久内存时,这个程序使一个PEIM注册自己到映射。
  PeiRegisterForShadow,
  // 在指定的文件中搜索下一个匹配部分。
  PeiFfsFindSectionData3,
  // 返回关于特定文件的信息。
  PeiFfsGetFileInfo2,
  // 重置整个平台。
  PeiResetSystem2
};

PeiCore

/**
  这个例程在从SEC过渡到PEI期间由PeiMain模块的主入口调用。
  在PEI核中切换堆栈后,它将用旧的核数据重新启动。

  @param SecCoreDataPtr  指向一个包含PEI核心操作环境信息的数据结构,例如临时RAM的大小和位置,堆栈位置和BFV位置。
                         
  @param PpiList        指向PEI核心最初要安装的一个或多个PPI描述符的列表。
                        空的PPI列表由单个描述符组成,其结束标记为EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST。
                        作为初始化阶段的一部分,PEI Foundation将把这些sec托管的PPIs添加到其PPI数据库中,
                        这样PEI Foundation和任何模块都可以利用这些早期PPIs中的相关服务调用和/或代码。
                        
  @param Data           指向旧核心数据的指针,用于初始化核心数据区域。如果为空,它是PeiCore第一次进入。
**/
VOID
EFIAPI
PeiCore (
  IN CONST EFI_SEC_PEI_HAND_OFF        *SecCoreDataPtr,
  IN CONST EFI_PEI_PPI_DESCRIPTOR      *PpiList,
  IN VOID                              *Data
  )
{
  //该结构为PeiMain的内部数据结构,维护运行PEI阶段所有需要的数据信息,包括PEI服务,FV数据空间,PEI模块的dispatch 状态,可使用的内存空间等
  PEI_CORE_INSTANCE           PrivateData;
  //保存着PEI核心运行环境的信息,如临时RAM的位置大小、堆栈位置和BFV位置。
  EFI_SEC_PEI_HAND_OFF        *SecCoreData;
  EFI_SEC_PEI_HAND_OFF        NewSecCoreData;
  EFI_STATUS                  Status;
  //临时使用的函数指针的联合(以节省堆栈空间)
  PEI_CORE_TEMP_POINTERS      TempPtr;
  PEI_CORE_INSTANCE           *OldCoreData;
  //EFI_PEI_CPU_IO_PPI提供一组内存和基于I/ o的服务。服务的视角是处理器的视角,而不是总线或系统的视角。
  EFI_PEI_CPU_IO_PPI          *CpuIo;
  //EFI_PEI_PCI_CFG_PPI接口用于在PCI根桥控制器后面抽象对PCI控制器的访问。
  EFI_PEI_PCI_CFG2_PPI        *PciCfg;
  //包含HOB生产者阶段使用的一般状态信息。该HOB必须是HOB列表中的第一个。
  EFI_HOB_HANDOFF_INFO_TABLE  *HandoffInformationTable;
  //这是一个可选的PPI,可由SEC或PEIM生产。如果存在,它提供一个服务来禁用临时RAM的使用。这种服务只能被PEIFoundation调用后,
  //从临时RAM过渡到永久RAM完成。这个PPI为系统架构提供了临时RAM迁移PPI的替代方案,允许临时RAM和永久RAM同时被启用和访问,而没有任何副作用。
  EFI_PEI_TEMPORARY_RAM_DONE_PPI *TemporaryRamDonePpi;
  UINTN                       Index;
  
  //
  // 检索传递到PEI核心的内容
  //
  OldCoreData = (PEI_CORE_INSTANCE *) Data;
  SecCoreData = (EFI_SEC_PEI_HAND_OFF *) SecCoreDataPtr;

  //
  // 执行PEI核心阶段的具体行动。
  //
  if (OldCoreData == NULL) {
    //
    // 如果OldCoreData为NULL,表示current是在内存可用之前进入PEI核的第一个entry。
    //
    ZeroMem (&PrivateData, sizeof (PEI_CORE_INSTANCE));
    PrivateData.Signature = PEI_CORE_HANDLE_SIGNATURE; //#define PEI_CORE_HANDLE_SIGNATURE  SIGNATURE_32('P','e','i','C')
    CopyMem (&PrivateData.ServiceTableShadow, &gPs, sizeof (gPs));//gPs记录着Pei的服务实例,复制该表到ServiceTableShadow
  } else {
    //
    // PEI Core可以使用内存。看看Pei Core是否已经把gPs表拷贝到ServiceTableShadow里。
    // 当OldCoreData不为NULL时,但ShadowedPeiCore 为NULL时,这是第二次进入PeiCore, 切换HOB
    // 列表从堆中到物理内存中,更新传递类型的HOB 数据块记录物理内存的地址,某些PPI 地址空间记录在堆上,
    // 也需要切换到物理内存中。最后加载PeiMain 模块到物理内存中,并调用内存中的PeiCore 函数地址。
    //
    if (OldCoreData->ShadowedPeiCore == NULL) {
      //
      // 修复PeiCore的private数据
      // EFI_PEI_CPU_IO_PPI提供了一组基于内存和I/的服务。服务的视角是处理器的视角,而不是总线或系统的视角。
      //
      OldCoreData->Ps    = &OldCoreData->ServiceTableShadow;//将ps指向服务列表
      OldCoreData->CpuIo = &OldCoreData->ServiceTableShadow.CpuIo;//CpuIo 包括了IoRead8登CPU操作函数
      if (OldCoreData->HeapOffsetPositive) {
        OldCoreData->HobList.Raw = (VOID *)(OldCoreData->HobList.Raw + OldCoreData->HeapOffset);
        OldCoreData->UnknownFvInfo        = (PEI_CORE_UNKNOW_FORMAT_FV_INFO *) ((UINT8 *) OldCoreData->UnknownFvInfo + OldCoreData->HeapOffset);
        OldCoreData->CurrentFvFileHandles = (EFI_PEI_FILE_HANDLE *) ((UINT8 *) OldCoreData->CurrentFvFileHandles + OldCoreData->HeapOffset);
        OldCoreData->PpiData.PpiListPtrs  = (PEI_PPI_LIST_POINTERS *) ((UINT8 *) OldCoreData->PpiData.PpiListPtrs + OldCoreData->HeapOffset);
        OldCoreData->Fv                   = (PEI_CORE_FV_HANDLE *) ((UINT8 *) OldCoreData->Fv + OldCoreData->HeapOffset);
        for (Index = 0; Index < PcdGet32 (PcdPeiCoreMaxFvSupported); Index ++) {
          OldCoreData->Fv[Index].PeimState     = (UINT8 *) OldCoreData->Fv[Index].PeimState + OldCoreData->HeapOffset;
          OldCoreData->Fv[Index].FvFileHandles = (EFI_PEI_FILE_HANDLE *) ((UINT8 *) OldCoreData->Fv[Index].FvFileHandles + OldCoreData->HeapOffset);
        }
        OldCoreData->FileGuid             = (EFI_GUID *) ((UINT8 *) OldCoreData->FileGuid + OldCoreData->HeapOffset);
        OldCoreData->FileHandles          = (EFI_PEI_FILE_HANDLE *) ((UINT8 *) OldCoreData->FileHandles + OldCoreData->HeapOffset);
      } else {
        OldCoreData->HobList.Raw = (VOID *)(OldCoreData->HobList.Raw - OldCoreData->HeapOffset);
        OldCoreData->UnknownFvInfo        = (PEI_CORE_UNKNOW_FORMAT_FV_INFO *) ((UINT8 *) OldCoreData->UnknownFvInfo - OldCoreData->HeapOffset);
        OldCoreData->CurrentFvFileHandles = (EFI_PEI_FILE_HANDLE *) ((UINT8 *) OldCoreData->CurrentFvFileHandles - OldCoreData->HeapOffset);
        OldCoreData->PpiData.PpiListPtrs  = (PEI_PPI_LIST_POINTERS *) ((UINT8 *) OldCoreData->PpiData.PpiListPtrs - OldCoreData->HeapOffset);
        OldCoreData->Fv                   = (PEI_CORE_FV_HANDLE *) ((UINT8 *) OldCoreData->Fv - OldCoreData->HeapOffset);
        for (Index = 0; Index < PcdGet32 (PcdPeiCoreMaxFvSupported); Index ++) {
          OldCoreData->Fv[Index].PeimState     = (UINT8 *) OldCoreData->Fv[Index].PeimState - OldCoreData->HeapOffset;
          OldCoreData->Fv[Index].FvFileHandles = (EFI_PEI_FILE_HANDLE *) ((UINT8 *) OldCoreData->Fv[Index].FvFileHandles - OldCoreData->HeapOffset);
        }
        OldCoreData->FileGuid             = (EFI_GUID *) ((UINT8 *) OldCoreData->FileGuid - OldCoreData->HeapOffset);
        OldCoreData->FileHandles          = (EFI_PEI_FILE_HANDLE *) ((UINT8 *) OldCoreData->FileHandles - OldCoreData->HeapOffset);
      }

      //
      // 修复PeiService的地址
      // 缓存指向PEI服务表的指针,该表由peiservicestlepointer以特定于CPU的方式指定,
      // 在平台初始化前efi初始化核心接口规范的CPU绑定部分中指定。该功能根据PI规范在KR7寄存器中设置PEI服务指针。
      //
      SetPeiServicesTablePointer ((CONST EFI_PEI_SERVICES **)&OldCoreData->Ps);

      //
      // 初始化PEI核心所链接的库
      //
      ProcessLibraryConstructorList (NULL, (CONST EFI_PEI_SERVICES **)&OldCoreData->Ps);

      //
      // Update HandOffHob for new installed permanent memory
      // 为新安装的永久内存更新handffhob
      // EFI_HOB_HANDOFF_INFO_TABLE包含HOB生产者阶段所使用的一般状态信息。这个HOB必须是HOB列表中的第一个。
      //
      HandoffInformationTable = OldCoreData->HobList.HandoffInformationTable;
      if (OldCoreData->HeapOffsetPositive) {
        HandoffInformationTable->EfiEndOfHobList   = HandoffInformationTable->EfiEndOfHobList + OldCoreData->HeapOffset;
      } else {
        HandoffInformationTable->EfiEndOfHobList   = HandoffInformationTable->EfiEndOfHobList - OldCoreData->HeapOffset;
      }
      HandoffInformationTable->EfiMemoryTop        = OldCoreData->PhysicalMemoryBegin + OldCoreData->PhysicalMemoryLength;
      HandoffInformationTable->EfiMemoryBottom     = OldCoreData->PhysicalMemoryBegin;
      HandoffInformationTable->EfiFreeMemoryTop    = OldCoreData->FreePhysicalMemoryTop;
      HandoffInformationTable->EfiFreeMemoryBottom = HandoffInformationTable->EfiEndOfHobList + sizeof (EFI_HOB_GENERIC_HEADER);

      //
      // We need convert the PPI descriptor's pointer
      // 我们需要转换PPI描述符的指针
      // 该函数负责将PPI指针从临时内存堆栈迁移到PEI安装的内存中。
      //
      ConvertPpiPointers (SecCoreData, OldCoreData);

      //
      // 在迁移了整个临时内存之后,我们可以在永久内存中分配页。
      // 更新状态标志位,表示PEI Memory 已实装
      //
      OldCoreData->PeiMemoryInstalled = TRUE;

      //
      // Indicate that PeiCore reenter
      // 指示PeiCore重新进入,更新状态标志位
      //
      OldCoreData->PeimDispatcherReenter = TRUE;
      //PcdLoadModuleAtFixAddressEnable是启用/禁用固定地址加载模块特性的标志。
      //BootMode是在HOB产生阶段确定的系统启动模式。 #define BOOT_ON_S3_RESUME 0x11
      if (PcdGet64(PcdLoadModuleAtFixAddressEnable) != 0 && (OldCoreData->HobList.HandoffInformationTable->BootMode != BOOT_ON_S3_RESUME)) {
        //
        // if Loading Module at Fixed Address is enabled, allocate the PEI code memory range usage bit map array.
        // Every bit in the array indicate the status of the corresponding memory page available or not
        // 如果启用了固定地址加载模块,则分配PEI代码内存范围使用 bit map数组。
        // 数组中的每个位都表示对应的内存页是否可用
        //
        OldCoreData->PeiCodeMemoryRangeUsageBitMap = AllocateZeroPool (((PcdGet32(PcdLoadFixAddressPeiCodePageNumber)>>6) + 1)*sizeof(UINT64));
      }

      //
      // Shadow PEI Core. When permanent memory is avaiable, shadow PEI Core and PEIMs to get high performance.
      // Shadow PEI核心。当永久存储器可用时,对PEI核和PEIMs进行Shadow处理,以获得高性能。
      // 所谓BIOS shadow实质上就是将BIOS程序从设备拷贝到内存中,只是不管在设备还是在内存中,两者用到的地址是相同的,
      // 所以对正在运行的程序来说,并没法看出什么区别来,所以叫shadow,只是通过控制PAM寄存器让其定向到不同的设备上。
      // 对BIOS程序进行shadow的主要目的就是为了实现性能的提升,因为BIOS刚开始执行的时候,内存并没有初始化,程序没法直接放到内存中。
      //
      OldCoreData->ShadowedPeiCore = (PEICORE_FUNCTION_POINTER) (UINTN) PeiCore;
      // 表示是否在内存准备好后,在S3启动路径上shadow PEIM。 PcdShadowPeimOnBoot|TRUE|BOOLEAN|0x30001029
      if ((HandoffInformationTable->BootMode == BOOT_ON_S3_RESUME && PcdGetBool (PcdShadowPeimOnS3Boot))
          || (HandoffInformationTable->BootMode != BOOT_ON_S3_RESUME && PcdGetBool (PcdShadowPeimOnBoot))) {
          //ShadowPeiCore():Shadow PeiCore模块从flash安装到内存。
        OldCoreData->ShadowedPeiCore = ShadowPeiCore (OldCoreData);
      }
      
      //
      // PEI Core has now been shadowed to memory.  Restart PEI Core in memory.
      // PE1I核心现在被shadow到内存。重新启动内存中的PEI核。
      // SecCoreData:指向一个包含SEC到PEI转换数据的数据结构,如临时RAM的大小和位置,堆栈位置和BFV位置。
      // PpiList:指向PEI核心最初要安装的一个或多个PPI描述符的列表。空的PPI列表由单个描述符组成,
      //         其结束标记为EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST。
      //         作为初始化阶段的一部分,PEI Foundation将把这些sec托管的PPIs添加到其PPI数据库中,
      //         这样PEI Foundation和任何模块都可以利用这些早期PPIs中的相关服务调用和/或代码。
      // OldCoreData:指向旧核心数据的指针,用于初始化核心数据区域。
      //
      OldCoreData->ShadowedPeiCore (SecCoreData, PpiList, OldCoreData);
      
      //
      // Should never reach here.
      //
      ASSERT (FALSE);
      CpuDeadLoop();

      UNREACHABLE ();
    }

    //
    // 内存对PEI核是可用的,而PEI核已经被shadow在内存中。
    //
    CopyMem (&NewSecCoreData, SecCoreDataPtr, sizeof (NewSecCoreData));
    SecCoreData = &NewSecCoreData;

    CopyMem (&PrivateData, OldCoreData, sizeof (PrivateData));

    CpuIo = (VOID*)PrivateData.ServiceTableShadow.CpuIo;
    PciCfg = (VOID*)PrivateData.ServiceTableShadow.PciCfg;
    
    CopyMem (&PrivateData.ServiceTableShadow, &gPs, sizeof (gPs));
    
    PrivateData.ServiceTableShadow.CpuIo  = CpuIo;
    PrivateData.ServiceTableShadow.PciCfg = PciCfg;
  }
  
  //
  // Cache a pointer to the PEI Services Table that is either in temporary memory or permanent memory
  // 缓存一个指向PEI Services表的指针,该表在临时内存或永久内存中
  //
  PrivateData.Ps = &PrivateData.ServiceTableShadow;

  //
  // Save PeiServicePointer so that it can be retrieved anywhere.
  // 保存PeiServicePointer,以便可以在任何地方检索它。
  // 缓存指向PEI服务表的指针,该表由peiservicestlepointer以特定于CPU的方式指定,
  // 在平台初始化前efi初始化核心接口规范的CPU绑定部分中指定。该函数根据PI规范将PEI服务的指针设置在IDT表的前面。
  //
  SetPeiServicesTablePointer ((CONST EFI_PEI_SERVICES **)&PrivateData.Ps);

  //
  // Initialize libraries that the PEI Core is linked against
  // 初始化PEI核心所链接的库
  //
  ProcessLibraryConstructorList (NULL, (CONST EFI_PEI_SERVICES **)&PrivateData.Ps);

  //
  // Initialize PEI Core Services
  // 
  InitializeMemoryServices   (&PrivateData,    SecCoreData, OldCoreData);
  if (OldCoreData == NULL) {
    //
    // Initialize PEI Core Private Data Buffer
    // 初始化PEI核心服务
    // PcdPeiCoreMaxPpiSupported:PeiCore的PPI数据库支持最大PPI计数。
    // PcdPeiCoreMaxFvSupported:PeiCore的调度支持FV的最大值。
    // PcdPeiCoreMaxPeimPerFv:PeiCore的调度支持每个FV中的最大文件计数。支持的文件类型包括PEIM,组合PEIM和FV。
    //
    PrivateData.PpiData.PpiListPtrs  = AllocateZeroPool (sizeof (PEI_PPI_LIST_POINTERS) * PcdGet32 (PcdPeiCoreMaxPpiSupported));
    ASSERT (PrivateData.PpiData.PpiListPtrs != NULL);
    PrivateData.Fv                   = AllocateZeroPool (sizeof (PEI_CORE_FV_HANDLE) * PcdGet32 (PcdPeiCoreMaxFvSupported));
    ASSERT (PrivateData.Fv != NULL);
    PrivateData.Fv[0].PeimState      = AllocateZeroPool (sizeof (UINT8) * PcdGet32 (PcdPeiCoreMaxPeimPerFv) * PcdGet32 (PcdPeiCoreMaxFvSupported));
    ASSERT (PrivateData.Fv[0].PeimState != NULL);
    PrivateData.Fv[0].FvFileHandles  = AllocateZeroPool (sizeof (EFI_PEI_FILE_HANDLE) * PcdGet32 (PcdPeiCoreMaxPeimPerFv) * PcdGet32 (PcdPeiCoreMaxFvSupported));
    ASSERT (PrivateData.Fv[0].FvFileHandles != NULL);
    for (Index = 1; Index < PcdGet32 (PcdPeiCoreMaxFvSupported); Index ++) {
      PrivateData.Fv[Index].PeimState     = PrivateData.Fv[Index - 1].PeimState + PcdGet32 (PcdPeiCoreMaxPeimPerFv);
      PrivateData.Fv[Index].FvFileHandles = PrivateData.Fv[Index - 1].FvFileHandles + PcdGet32 (PcdPeiCoreMaxPeimPerFv);
    }
    PrivateData.UnknownFvInfo        = AllocateZeroPool (sizeof (PEI_CORE_UNKNOW_FORMAT_FV_INFO) * PcdGet32 (PcdPeiCoreMaxFvSupported));
    ASSERT (PrivateData.UnknownFvInfo != NULL);
    PrivateData.CurrentFvFileHandles = AllocateZeroPool (sizeof (EFI_PEI_FILE_HANDLE) * PcdGet32 (PcdPeiCoreMaxPeimPerFv));
    ASSERT (PrivateData.CurrentFvFileHandles != NULL);
    PrivateData.FileGuid             = AllocatePool (sizeof (EFI_GUID) * PcdGet32 (PcdPeiCoreMaxPeimPerFv));
    ASSERT (PrivateData.FileGuid != NULL);
    PrivateData.FileHandles          = AllocatePool (sizeof (EFI_PEI_FILE_HANDLE) * (PcdGet32 (PcdPeiCoreMaxPeimPerFv) + 1));
    ASSERT (PrivateData.FileHandles != NULL);
  }
  InitializePpiServices      (&PrivateData,    OldCoreData);
  
  //
  // Update performance measurements 
  // 更新性能测量
  // PERF_START 和END之间用于测量时间,SEC阶段运行多长时间可以通过该功能实现统计打印。
  if (OldCoreData == NULL) {
    PERF_START (NULL, "SEC", NULL, 1);
    PERF_END   (NULL, "SEC", NULL, 0);

    //
    // If first pass, start performance measurement.
    // 如果第一次通过,就开始进行性能评估。
    //
    PERF_START (NULL,"PEI",    NULL, 0);
    PERF_START (NULL,"PreMem", NULL, 0);

  } else {
    PERF_END   (NULL,"PreMem",  NULL, 0);
    PERF_START (NULL,"PostMem", NULL, 0);
  }

  //
  // Complete PEI Core Service initialization
  // 完成PEI核心服务初始化
  //
  InitializeSecurityServices (&PrivateData.Ps, OldCoreData);
  InitializeDispatcherData   (&PrivateData,    OldCoreData, SecCoreData);
  InitializeImageServices    (&PrivateData,    OldCoreData);

  //
  // 执行PEI核心阶段的具体行动
  //  
  if (OldCoreData == NULL) {
    //
    // Report Status Code EFI_SW_PC_INIT
    //
    REPORT_STATUS_CODE (
      EFI_PROGRESS_CODE,
      (EFI_SOFTWARE_PEI_CORE | EFI_SW_PC_INIT)
      );
      
    //
    // If SEC provided any PPI services to PEI, install them.
    // 如果SEC向PEI提供了任何PPI服务,安装它们。
    //
    if (PpiList != NULL) {
      Status = PeiServicesInstallPpi (PpiList);
      ASSERT_EFI_ERROR (Status);
    }
  } else {
    //
    // Try to locate Temporary RAM Done Ppi.
    // 尝试locate临时RAM已完成Ppi。
    //
    Status = PeiServicesLocatePpi (
               &gEfiTemporaryRamDonePpiGuid,
               0,
               NULL,
               (VOID**)&TemporaryRamDonePpi
               );
    if (!EFI_ERROR (Status)) {
      //
      // 从临时RAM过渡到永久RAM完成后,禁用临时RAM的使用。
      //
      TemporaryRamDonePpi->TemporaryRamDone ();
    }

    //
    // Alert any listeners that there is permanent memory available
    // 提醒任何侦听器有可用的永久内存
    // 如果设置了PcdPerformanceLibraryPropertyMask的PERFORMANCE_LIBRARY_PROPERTY_MEASUREMENT_ENABLED位,
    // 则调用StartPerformanceMeasurement()。
    //
    PERF_START (NULL,"DisMem", NULL, 0);
    Status = PeiServicesInstallPpi (&mMemoryDiscoveredPpi);

    //
    // Process the Notify list and dispatch any notifies for the Memory Discovered PPI
    // 处理Notify列表并为发现的PPI分配任何通知
    // 检查刚刚被分派的PEIM是否导致任何通知被安装。如果是,则go处理与以前安装的PPIs匹配的任何分派级别通知。
    // 使用“while”而不是“if”,因为DispatchNotify可以修改DispatchListEnd(使用NotifyPpi),所以我们必须迭代直到相同。
    //
    ProcessNotifyList (&PrivateData);

    PERF_END (NULL,"DisMem", NULL, 0);
  }

  //
  // Call PEIM dispatcher
  // pei dispatcher 为pei core 的一部分,用来搜寻和执行peim.
  //
  PeiDispatcher (SecCoreData, &PrivateData);

  if (PrivateData.HobList.HandoffInformationTable->BootMode != BOOT_ON_S3_RESUME) {
    //
    // Check if InstallPeiMemory service was called on non-S3 resume boot path.
    // 检查InstallPeiMemory服务是否在非s3恢复引导路径上被调用。
    //
    ASSERT(PrivateData.PeiMemoryInstalled == TRUE);
  }

  //
  // Measure PEI Core execution time.
  //
  PERF_END (NULL, "PostMem", NULL, 0);

  //
  // Lookup DXE IPL PPI
  //
  Status = PeiServicesLocatePpi (
             &gEfiDxeIplPpiGuid,
             0,
             NULL,
             (VOID **)&TempPtr.DxeIpl
             );
  ASSERT_EFI_ERROR (Status);

  if (EFI_ERROR (Status)) {
    //
    // Report status code to indicate DXE IPL PPI could not be found.
    // 报告状态代码以指示无法找到DXE IPL PPI。
    //
    REPORT_STATUS_CODE (
      EFI_ERROR_CODE | EFI_ERROR_MAJOR,
      (EFI_SOFTWARE_PEI_CORE | EFI_SW_PEI_CORE_EC_DXEIPL_NOT_FOUND)
      );
    CpuDeadLoop ();
  }

  //
  // 输入DxeIpl加载Dxe core。
  //
  DEBUG ((EFI_D_INFO, "DXE IPL Entry\n"));
  Status = TempPtr.DxeIpl->Entry (
                             TempPtr.DxeIpl,
                             &PrivateData.Ps,
                             PrivateData.HobList
                             );
  //
  // Should never reach here.
  //
  ASSERT_EFI_ERROR (Status);
  CpuDeadLoop();
  //
  // 信号编译器和分析程序,该调用是不可达的。由编译器删除超过这一点的代码。
  //
  UNREACHABLE ();
}

PEI Dispatcher

PEI 调度器负责调用一个个PEI的驱动 (PEIM),直到所有满足条件的PEI驱动都被调用。被调用的条件:
1.该PEI驱动没有被调用过
2.该PEI驱动的文件格式是正确的,可读的
3.该驱动是可信的
4.该驱动的依赖条件(dependency)被满 足, 驱动可以设置自己的依赖条件,如内存被找到之后才可以调用该驱动)
注意:PEI驱动大部分都是没有压缩的,只有少部分只有极少数的PEIM为了提高性能而存在于RAM上运行
BIOS追code之PEI phase_第4张图片

在PeiMain.c这个文件中,派遣器先找,然后根据依赖关系进行调度。

 //
 // Call PEIM dispatcher
 //
  PeiDispatcher (SecCoreData, &PrivateData);
      //
      // Start to dispatch all modules within the current Fv.
      //
      for (PeimCount = Private->CurrentPeimCount;
           (PeimCount < PcdGet32 (PcdPeiCoreMaxPeimPerFv)) && (Private->CurrentFvFileHandles[PeimCount] != NULL);
           PeimCount++) {
        Private->CurrentPeimCount  = PeimCount;
        PeimFileHandle = Private->CurrentFileHandle = Private->CurrentFvFileHandles[PeimCount];

        if (Private->Fv[FvCount].PeimState[PeimCount] == PEIM_STATE_NOT_DISPATCHED) {
          if (!DepexSatisfied (Private, PeimFileHandle, PeimCount)) {
            Private->PeimNeedingDispatch = TRUE;
          } else {
            Status = CoreFvHandle->FvPpi->GetFileInfo (CoreFvHandle->FvPpi, PeimFileHandle, &FvFileInfo);
            ASSERT_EFI_ERROR (Status);
            if (FvFileInfo.FileType == EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE) {
              //
              // For Fv type file, Produce new FvInfo PPI and FV hob
              //
              Status = ProcessFvFile (Private, &Private->Fv[FvCount], PeimFileHandle);
              if (Status == EFI_SUCCESS) {
                //
                // PEIM_STATE_NOT_DISPATCHED move to PEIM_STATE_DISPATCHED
                //
                Private->Fv[FvCount].PeimState[PeimCount]++;
                Private->PeimDispatchOnThisPass = TRUE;
              } else {
              //
                // The related GuidedSectionExtraction/Decompress PPI for the
                // encapsulated FV image section may be installed in the rest
                // of this do-while loop, so need to make another pass.
                //
                Private->PeimNeedingDispatch = TRUE;
              }
            } else {
              //
              // For PEIM driver, Load its entry point
              // 找到PEI驱动并执行它
              Status = PeiLoadImage (
                         PeiServices,
                         PeimFileHandle,
                         PEIM_STATE_NOT_DISPATCHED,
                         &EntryPoint,
                         &AuthenticationState
                         );
                      ......
                   }
              }

你可能感兴趣的:(bios)