2012年5月22日 星期二

UEFI/PI (4) Pre-EFI Phase

  • PEI Overview
      Pre-EFI Initialization (PEI) 是Platform Initialization的第二個Phase定義在Platform Initialization Specification, Ver 1.2.1, Volume 1。 
      所有的machine restart event都會invoke PEI Phase。PEI執行的階段只有on-processor的資源可以使用, 例如以processor cache當作call stack然後來執行Pre-EFI Initialization(PEIM)。PEI主要可以分成6個任務如下:
      1. Reset
          PEI consumes Reset, INIT 和 Machine Check Architecture( MCA)。以Itaium來說,reset會call Processor abstraction layer(PAL) 然後call進 firmware。
      2. Start-up
          PEI是在rom裡面執行的,因此有非常的小的start-up code。Modular的driver都是absolutly address的。
      3. Support
          為了要提供DXE phase以C的環境執行,PEI必須建立暫時的memory以供C使用stack。
      4. Modules
          PEI core locate、validates和dispatch 執行PEI modules(PEIM),這些module是用來初始化platform的feature或chipset。
      5. Publish
          PEI藉由PEIM to PEIM Interface (PPI)來 溝通。
      6. Goals
          PEI主要的目的是Discover boot mode,Launch modules that initialize main memory Discovery & launch DXE core。

      而上面提到的PEIM負責了主要四個項目:
      1. 初始化永久記憶體。
      2. 在Hand-Off Blocks中寫入memory的資訊。
      3. 在Hand-Off Blocks中 描述firmware volume的location。
      4. 將control 轉移給DXE Phase。

      下圖說明了PEI Phase的操作流程,當進入PEI之後,首先會 初始化PEIM的dispatcher,接著執行各個PEIM直到最後一個PEIM存在DXE IPL將control及HOBs 轉移給DXE Phase。
       
      HOBs是唯一pass給DXE Phase的資料結構。由於 PEI負責了recovery及S3 resume,PEI Phase必須要越小越好,複雜的運算或演算法應該放到DXE階段執行(這樣開機才會快)。 
      為什麼我們需要PEI呢?原因是:
      1. 寫不使用memory的程式是非常困難的。 
      2. Porting的困難。以assembly來說,某部分的code vendorA使用EBP另外一個vendor 使用EBX,修改起來會比C來的費力。
      3. 在開發一個feature的時候,我們基本上都會希望可以使用memory以及有基本的chipset被初始化。並且,PEI也負責了S3 (resume )的部分。 
      下面我們將深入的介紹PEI Phase。
  • PEI Foundation
    • PEI Foundation Introduction
        PEI Foundation 是PEI的entity負責了主要四個項目:
        1. Dispatch PEIMs
        2. 維護boot mode
        3. 初始化永久記憶體
        4. Invoke DXE loader
        PEI Foundation 包含了SEC 和 Pre-EFI( PEI)被設計成一個很小的一個片段,在SEC結束後,PEI最開始的code是一段platform-specific的machine code, 這也是porting會遭遇的困難。因此,這也是為什麼要讓PEI foundation的code越小越好(事情盡量以C來做,比較好移植)。
    • PEI Prerequisites
        在進入PEI Phase之前要滿足兩個條件:
        1. Processor execution mode。
        2. 可以access 存在PEI Foundation的Boot Firmware Volume(BFV)。
        SEC將control交出的對象就是PEI Foundation。而放置PEI Foundation的Firmware Volume(FV)就稱為Boot Firmware Volume。PEIM可能會放在一般FV或是BFV,但是有些特殊的PEIM必須要放在BFV來提供其他PEIM的location。
    • PEI Foundation Entry Point
        SEC call進PEI Foundation時包含了下列資訊:
        1. A set of PPIs
        2. Size and location of the the Boot Firmware Volume (BFV)
        3. Size and location of the temporary RAM
        4. Size and location of the temporary RAM available for use by the PEI Foundation
        5. Size and location of the stack
        在SPEC裡面是強制規定SEC一定要將這些資訊放到stack中來invoke PEI Foundation。 
    • PEI Calling Convention Processor Binding
        下面將簡述 processor-specific mechanisms 去取得指向PEI Service Table的指標。(EFI_PEI_SERVICES**)
      • X86
          以x86而言, EFI_PEI_SERVICES** 放在 4 bytes immediately preceding the Interrupt Descriptor Table,取得的方法如下。
          IDTR32          STRUCT
          Limit           DW 1 DUP (?)
          BaseAddress     DD 1 DUP (?)
          IDTR32          ENDS
          sub       esp, SIZEOF IDTR32
          sidt      FWORD PTR ss:[esp]
          mov       eax, [esp].IDTR32.BaseAddress
          mov       eax, DWORD PTR [eax – 4]
          add       esp, SIZEOF IDTR32
          
      • X64
          x64的系統則是放在8 bytes immediately preceding the Interrupt Descriptor Table,取得的方法如下。
          IDTR64          STRUCT
          Limit           DW 1 DUP (?)
          BaseAddress     DQ 1 DUP (?)
          IDTR64          ENDS
          sub       rsp, SIZEOF IDTR64
          sidt      [rsp]
          mov       rax, [rsp].IDTR64.BaseAddress
          mov       rax, QWORD PTR [rax – 8]
          add       rsp, SIZEOF IDTR64
      • ARM
          如果是ARM Processor Family processors的話,EFI_PEI_SERVICES** 是放在 TPIDRURW read/write Software Thread ID register defined in the ARMv7-A Architectural Reference Manual.
          CpuReadTPIDRURW:
              MRC p15, 0, r0, c13, c0, 2
              bx lr
          EFI_PEI_SERVICES ** 
          GetPeiServicesTablePointer (
            VOID
             )
          {
            return (EFI_PEI_SERVICES **)(UINTN)CpuReadTPIDRURW ();
          }
          
  • Memory
      PEI core需要一段暫時的memory,大小會depends on CPU和chipset,以IA32為例就是8K了。IA32使用 Cache As Ram(CAR)的技術來提供暫用的memory。方法是將cache設定成no eviction mode。 下圖是PEI initial的memory map。主要分成幾個部分:
      1. BFV
          BFV全名是Boot Firmware Volume,BFV是存放PEI core的地方,同時也是recovery block。
      2. FV
          FV全名是Firmware Volume,定義在 Firmware Storage, vol.3。FV有可能是0到多個。
      3. T-RAM
          T-RAM就是temporary memory。在初始化memory 之前存放stack, data的地方。
  • PEI Core
      PEI core 負責dispatching PEIMs 及提供基本的services 如下圖 PEI core 的主要function如下:
      1. *SecCoreData 存放著例如T-RAM的大小及位置、stack的地方和BFV的地方。
      2. *PpiList 記錄著要由PEI core initial的PPI description的list。如果這個list是空的則會存放一個結束的tag:EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST
      3. *Data 則是指向舊的PEI core,如果是NULL則代表第一次進入。

      下圖說明了在系統reset或power-on之後到準備進入DXE的流程
  • PEI Service
      PEI Foundation會建立一個所有PEIM都可以看到的system table叫PEI Service Table 。 PEI Service有可能被定義成 function、command 或capability manifested。 也因為PEI階段直到最後面才會有memory可以使用,因此相對於後面phase的service,PEI service可以做的事情就相對比較少了。 
      在build time的時候,PEI Foundation的位置和建立的temporary ram是不可知的,因此指向PEI 的pointer會傳進每一個PEIM的entry point 及PEIM-to-PEIM Interface (PPI)。
       PEI Foundation提供了下列七種service。
      1. PPI Service
          PEIM to PEIM Interface是用來讓PEIM之間做溝通,這個interface存放在T-RAM中。PPI service提供了四種function: InstallPpi()、ReinstallPpi()、LocatePpi() 、NotifyPpi()
      2. Boot Mode Service
          管理系統的boot mode( S3, S5, normal boot, diagnostics, etc.)。
      3. HOB Service
          Hand-Off Blocks的簡稱,用來將資料傳到下一個phase(DXE)
      4. Firmware Volume Service
          在Firmware File System (FFS)中找尋PEIMs及其他的firmware files。 FfsFindNextVolume() 、FfsFindNextFile()、FfsFindSectionData()
      5. PEI Memory Service
          管理permanent memory在被discovered之前/後的memory操作。InstallPeiMemory()、AllocatePages()、AllocatePool()、CopyMem()、SetMem()
      6. Status Code Service
          提供common progress/error code 的reporting 服務。一般來說是for debug用的。
      7. Reset Service
          Initiate warm or cold restart of the system
  • PEI Modules(PEIMs)
    • PEI Modules 
        PEIM是一個unit 的code and/or data用來初始化平台、processor。他抽象化了domain-specific logic相似於DXE Driver。 一個platform所擁有的PEIMs可能包含了以下幾種:
        1. Platform-specific PEIMs
        2. Processor-specific PEIMs
        3. Chipset-specific PEIMs
        4. PEI CIS–prescribed architectural PEIMs
        5. Miscellaneous PEIMs

        下表列出了PEIM的幾個特性

    • PEIM Structure
        每一個PEIM包存在一個檔案中,包含了:
        1. Standard header
        2. Execute-in-place code/data section
        3. Optional relocation information
        4. Authentication information, if present
        PEIM binary image 可以從存放的FV執行或是在永久記憶體被初始化後shadow過去的compressed component中執行。 PEIM可執行的section可能是position-dependent 或 position-independent code. 如果是position-dependent code, relocation information must be provided in the PEIM image to allow FV store software to relocate the image to a different location than it is compiled.  下圖是PEIM的layout,有些是必要的有些則是optional的。 PEIM binary可以是XIP(execute in place)(沒有壓縮的)或是在memary初始化後copy到ram執行(壓縮的binary)。這類的position dependent code 因為base address及entry point已經改變了,因此必須作相對應的修改。
  • PPI Interface
      PEIM使用 PEIM to PEIM Interface(PPI)來與彼此溝通,而PEIM執行的過程中也有可能會去invoke其他的PEIM靠的就是PPI。PPI包含在EFI_PEI_PPI_DESCRIPTOR 中。
      要使用PPI必須要透過LocatePpi()來查詢。 要提供PPI則使用InstallPpi()來讓別人使用。我們藉由PPI database來access PPI,這個database 包含了所有的PPI。如同剛剛提到的,我們可以藉由LocatePpi(), NotifyPpi(), 及ReinstallPpi()來access這個 database。
       PPI有兩種,分別是Architecture PPI Additional PPI
      1. Architecture PPI的GUID是global的。這類的PPI提供給PEI foundation作為common interface來使用。 而這類的 PPI也會根據不同的platform來實作,例如ReportStatusCode()。
      2. Additional PPI相較於 Architecture PPI 只會被PEI Foundation使用而不是給其他的PEIMs使用, Additional PPI 定義了兩種PPI可以給其他PEIMs使用。
        1. Required PPIs
            CPU I/O PPI 、PCI Configuration PPI 、 Stall PPI、 PEI Variable Services
        2. Optional PPIs
            Security (SEC) Platform Information PPI
  • PEI Dispatcher
      PEIM Dispatcher是一個state machine實作在PEI Foundation裡。他的工作是將control依序地交給PEIM。PEI Dispatcher會檢查Firmware Volume中檔案型態
      EFI_FV_FILETYPE_PEIMEFI_FV_FILETYPE_COMBINED_PEIM_DRIVER依照他的相依性或參考priori file(optional的file,事先定義好PEIM的順序)來決定何時dispatch 這個PEIM。
  • PEI HOBs and Memory
    • HOBs
        HOB是一連串的data structure在PEI階段所建立的。HOB描述platform的feature、設定或資料。在DXE phase,HOB是read only的。以下分開介紹幾個HOB的角色。
        1. Bridge HOB是PEI跟DXE的溝通橋樑。
        2. Self-Discribing HOB定義在PI的SPEC.也定義了他的attribute及length.
        3. Allocated HOB是alloct在block的heap, 是一塊連續的記憶體。
        4. Snapshot HOB裡面指向的PEIM定義的physical的值例如FVs, Physical memory properties在DXE是不可以更改的。
        5. Memory PEI建立HOB並存在system memory供DXE使用。
    • Hand-Off Block (HOB) List
        在PEI中,DXE IPL PPI會將HOB list pass給DXE Foundation。而這個list必須要至少包含以下7種HOB。
        1. PEI Hand-off Information Table(PHIT):指向HOB List
        2. 一個或多個描述physical system memory的Resource Descriptor HOB(s)
        3. Boot-Strap Processor (BSP)stack HOB:告訴DXE現在的stack location
        4. BSPStore(Backing Store Pointer Store) HOB: Itanium only
        5. 一個或多個描述firmware devices的Resource Descriptor HOB(s)
        6. 一個或多個Firmware Volume HOB(s)
        7. Memory Allocation Module HOB:DXE會將他放到GCD
    • Pre-permanent Memory Environment
        下面分幾個部分介紹PEI Pre-permanent Memory Environment:
        1. Purpose: 初始化 system memory
        2. Discovery: Discovery boot type。例如resuming from state, recovery, or normal boot
        3. Environment: 到這個階段還沒有system memory可以用。因此使用CPU的cache來放置data及stack,模擬出C可以執行的環境。
        4. Model: Run在32 bit的protected mode
        5. Input: aware of the location of the firmware volumes and the PEI service table
    • Post-permanent Memory Environment
        下面一樣分幾個部分介紹PEI Post-permanent Memory Environment:
        1. Purpose: Transition 到DXE, 準備HOBs
        2. Discovery: 擁有stack及HOB list
        3. Environment: 擁有system memory, XIP from firmware volume
        4. Model: Run在32 bit的protected mode
        5. Input: 建立指向第一個HOB的pointer, 也就是PHIT(PEI Hand-Off Information Table)
    • PEI Memory Map Sample
        下圖是PEI Memory Map的範例,也就是"可能"的長相。

        首先,在PEI過程中,我們可能會populate HOBs, 同時,PHIT會指向第一個HOB。PHIT也會記錄著PeiMemoryBottom, PeiFreeMemoryBottom, PeiFreeMemoryTop, PeiMemoryTop。PeiMemoryTop跟PeiFreeMemoryTop之間是fixed memory,也就是DXE不可修改的位置。PeiFreeMemory是供DXE去使用的memory space。
  • Transition To DXE
    • PEI Transition To DXE
        總結一下PEI從啟動到移轉給DXE的過程
        1. 一開始的PEI phase是沒有system memory可以使用的。
        2. PEI core dispatch PEIMs,在這個階段初始化CPU, Chipset等等裝置並將結果記錄在HOB List中
        3. 在DXE Initial Program Load(IPL) PEIM被執行的時候,HOBs List就存在了
        4. DXE IPL將 control傳給DXE core。當傳遞完成後,所有的PEI phase的services及PPI將無法再access。唯一傳遞給DXE的只有HOB list
    • DXE Initial Program Load(IPL)
        IPL有很多目的如下:
        1. 禁止所有的hard coded的address
        2. 尋找最大的physical memory HOB,一般來說置於memory的top
        3. 將stack allocate到memory的top
        4. 尋找HOB LIST的firmware volume給DXE core
        5. 將DXE core load到memory
        6. switch stack(從T-RAM轉到system memory)然後轉移到DXE
    • Sample Code
      • Call to DXE IPL
      • PEI Transition
    • Alternative Boot Path
      • Alternative Boot Path
          事實上,PEI並非每次開機都會跳到DXE。PEI有可能根據boot mode的不同改變boot path。PEIM可以使用SetBootMode()來改變boot mode。