2012年5月30日 星期三

EDK2(Tianocore) with VC2012

EDK2是Intel 的UEFI實作 open source出來的成果。目前的build script裡面還沒把VC2012加進去。 以下簡單說明抓取EDK2到怎麼加入一個toolchain然後compile。

  • 下載EDK2
      我是直接透過svn來fetch code。 svn co https://edk2.sourceforge.net/svnroot/edk2 edk2
  • 修改Build Script
      抓下來後我們修改的地方是trunk\edk2
      1. edksetup.bat
          這邊要把VS110COMNTOOLS這個環境變數加上去,圖中圈起來的部分就是額外加入的部分。
          if defined VS110COMNTOOLS (
              call "%VS110COMNTOOLS%\vsvars32.bat"
            ) else (
          
      2. Conf/tools_def.txt
          加入的地方分成三個部分。
          1. 在 Line49的地方加入
              DEFINE VS2012x86_BIN    = C:\Program Files (x86)\Microsoft Visual Studio 11.0\Vc\bin
              DEFINE VS2012x86_DLL    = C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE;DEF(VS2012x86_BIN)
              DEFINE VS2012x86_BINX64 = DEF(VS2012x86_BIN)\x86_amd64
              DEFINE VS2012x86_BIN64  = DEF(VS2012x86_BIN)\x86_ia64
              
          2. 在Line 479的地方加入
              #  VS2005x86xASL -win64- Requires:
              #                             Microsoft Visual Studio 2005 Team Suite Edition (x86)
              #                             Microsoft Windows Server 2003 Driver Development Kit(Microsoft WINDDK) version 3790.1830 
              #                        Optional:
              #                             Required to build EBC drivers:
              #                               Intel(r) Compiler for Efi Byte Code (Intel(r) EBC Compiler)
              #                             Required to build platforms or ACPI tables:
              #                               Microsoft ASL ACPI Compiler (asl.exe) v4.0.0 from
              #                               http://download.microsoft.com/download/2/c/1/2c16c7e0-96c1-40f5-81fc-3e4bf7b65496/microsoft_asl_compiler-v4-0-0.msi
              
          3. 第三個比較長,我是貼在2299行,也就是上一段IPF Definition結束的地方。
              ####################################################################################
              #   VS2012x86xASL     - Microsoft Visual Studio 2012 with Microsoft ASL
              #   ASL  - Microsoft ACPI Source Language Compiler (asl.exe)
              ####################################################################################
              *_VS2012x86xASL_*_*_FAMILY        = MSFT
              
              *_VS2012x86xASL_*_MAKE_PATH       = DEF(VS2012x86_BIN)\nmake.exe
              *_VS2012x86xASL_*_MAKE_FLAG       = /nologo
              *_VS2012x86xASL_*_RC_PATH         = DEF(WINSDK_BIN)\rc.exe
              
              *_VS2012x86xASL_*_MAKE_FLAGS      = /nologo
              *_VS2012x86xASL_*_SLINK_FLAGS     = /NOLOGO /LTCG
              *_VS2012x86xASL_*_APP_FLAGS       = /nologo /E /TC
              *_VS2012x86xASL_*_PP_FLAGS        = /nologo /E /TC /FIAutoGen.h
              *_VS2012x86xASL_*_VFRPP_FLAGS     = /nologo /E /TC /DVFRCOMPILE /FI$(MODULE_NAME)StrDefs.h
              
              *_VS2012x86xASL_*_ASM16_PATH      = DEF(VS2012x86_BIN)\ml.exe
              
              ##################
              # ASL definitions
              ##################
              *_VS2012x86xASL_*_ASL_PATH        = DEF(WIN_ASL_BIN)
              *_VS2012x86xASL_*_ASL_FLAGS       = DEF(MS_ASL_FLAGS)
              *_VS2012x86xASL_*_ASL_OUTFLAGS    = DEF(MS_ASL_OUTFLAGS)
              *_VS2012x86xASL_*_ASLCC_FLAGS     = DEF(MSFT_ASLCC_FLAGS)
              *_VS2012x86xASL_*_ASLPP_FLAGS     = DEF(MSFT_ASLPP_FLAGS)
              *_VS2012x86xASL_*_ASLDLINK_FLAGS  = DEF(MSFT_ASLDLINK_FLAGS)
              
              ##################
              # IA32 definitions
              ##################
              *_VS2012x86xASL_IA32_*_DLL        = DEF(VS2012x86_DLL)
              
              *_VS2012x86xASL_IA32_MAKE_PATH    = DEF(VS2012x86_BIN)\nmake.exe
              *_VS2012x86xASL_IA32_CC_PATH      = DEF(VS2012x86_BIN)\cl.exe
              *_VS2012x86xASL_IA32_VFRPP_PATH   = DEF(VS2012x86_BIN)\cl.exe
              *_VS2012x86xASL_IA32_ASLCC_PATH   = DEF(VS2012x86_BIN)\cl.exe
              *_VS2012x86xASL_IA32_ASLPP_PATH   = DEF(VS2012x86_BIN)\cl.exe
              *_VS2012x86xASL_IA32_SLINK_PATH   = DEF(VS2012x86_BIN)\lib.exe
              *_VS2012x86xASL_IA32_DLINK_PATH   = DEF(VS2012x86_BIN)\link.exe
              *_VS2012x86xASL_IA32_ASLDLINK_PATH= DEF(VS2012x86_BIN)\link.exe
              *_VS2012x86xASL_IA32_APP_PATH     = DEF(VS2012x86_BIN)\cl.exe
              *_VS2012x86xASL_IA32_PP_PATH      = DEF(VS2012x86_BIN)\cl.exe
              *_VS2012x86xASL_IA32_ASM_PATH     = DEF(VS2012x86_BIN)\ml.exe
              
                    *_VS2012x86xASL_IA32_MAKE_FLAGS  = /nologo
                DEBUG_VS2012x86xASL_IA32_CC_FLAGS    = /nologo /c /WX /GS- /W4 /Gs32768 /D UNICODE /O1ib2 /GL /FIAutoGen.h /EHs-c- /GR- /GF /Gy /Zi /Gm
              RELEASE_VS2012x86xASL_IA32_CC_FLAGS    = /nologo /c /WX /GS- /W4 /Gs32768 /D UNICODE /O1ib2 /GL /FIAutoGen.h /EHs-c- /GR- /GF
              NOOPT_VS2012x86xASL_IA32_CC_FLAGS      = /nologo /c /WX /GS- /W4 /Gs32768 /D UNICODE /FIAutoGen.h /EHs-c- /GR- /GF /Gy /Zi /Gm /Od
              
                DEBUG_VS2012x86xASL_IA32_ASM_FLAGS   = /nologo /c /WX /W3 /Cx /coff /Zd /Zi
              RELEASE_VS2012x86xASL_IA32_ASM_FLAGS   = /nologo /c /WX /W3 /Cx /coff /Zd
              NOOPT_VS2012x86xASL_IA32_ASM_FLAGS     = /nologo /c /WX /W3 /Cx /coff /Zd /Zi
              
                DEBUG_VS2012x86xASL_IA32_DLINK_FLAGS = /NOLOGO /NODEFAULTLIB /IGNORE:4001 /OPT:REF /OPT:ICF=10 /MAP /ALIGN:32 /SECTION:.xdata,D /SECTION:.pdata,D /MACHINE:X86 /LTCG /DLL /ENTRY:$(IMAGE_ENTRY_POINT) /SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER /SAFESEH:NO /BASE:0 /DRIVER /DEBUG
              RELEASE_VS2012x86xASL_IA32_DLINK_FLAGS = /NOLOGO /NODEFAULTLIB /IGNORE:4001 /IGNORE:4254 /OPT:REF /OPT:ICF=10 /MAP /ALIGN:32 /SECTION:.xdata,D /SECTION:.pdata,D /MACHINE:X86 /LTCG /DLL /ENTRY:$(IMAGE_ENTRY_POINT) /SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER /SAFESEH:NO /BASE:0 /DRIVER /MERGE:.data=.text /MERGE:.rdata=.text
              NOOPT_VS2012x86xASL_IA32_DLINK_FLAGS   = /NOLOGO /NODEFAULTLIB /IGNORE:4001 /OPT:REF /OPT:ICF=10 /MAP /ALIGN:32 /SECTION:.xdata,D /SECTION:.pdata,D /MACHINE:X86 /LTCG /DLL /ENTRY:$(IMAGE_ENTRY_POINT) /SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER /SAFESEH:NO /BASE:0 /DRIVER /DEBUG
              
              ##################
              # X64 definitions
              ##################
              *_VS2012x86xASL_X64_*_DLL         = DEF(VS2012x86_DLL)
              
              *_VS2012x86xASL_X64_CC_PATH       = DEF(VS2012x86_BINX64)\cl.exe
              *_VS2012x86xASL_X64_PP_PATH       = DEF(VS2012x86_BINX64)\cl.exe
              *_VS2012x86xASL_X64_APP_PATH      = DEF(VS2012x86_BINX64)\cl.exe
              *_VS2012x86xASL_X64_VFRPP_PATH    = DEF(VS2012x86_BINX64)\cl.exe
              *_VS2012x86xASL_X64_ASLCC_PATH    = DEF(VS2012x86_BINX64)\cl.exe
              *_VS2012x86xASL_X64_ASLPP_PATH    = DEF(VS2012x86_BINX64)\cl.exe
              *_VS2012x86xASL_X64_ASM_PATH      = DEF(VS2012x86_BINX64)\ml64.exe
              *_VS2012x86xASL_X64_SLINK_PATH    = DEF(VS2012x86_BINX64)\lib.exe
              *_VS2012x86xASL_X64_DLINK_PATH    = DEF(VS2012x86_BINX64)\link.exe
              *_VS2012x86xASL_X64_ASLDLINK_PATH = DEF(VS2012x86_BINX64)\link.exe
              
                DEBUG_VS2012x86xASL_X64_CC_FLAGS     = /nologo /c /WX /GS- /X /W4 /Gs32768 /D UNICODE /O1ib2s /GL /Gy /FIAutoGen.h /EHs-c- /GR- /GF /Zi /Gm
              RELEASE_VS2012x86xASL_X64_CC_FLAGS     = /nologo /c /WX /GS- /X /W4 /Gs32768 /D UNICODE /O1ib2s /GL /Gy /FIAutoGen.h /EHs-c- /GR- /GF
              NOOPT_VS2012x86xASL_X64_CC_FLAGS       = /nologo /c /WX /GS- /X /W4 /Gs32768 /D UNICODE /Gy /FIAutoGen.h /EHs-c- /GR- /GF /Zi /Gm /Od
              
                DEBUG_VS2012x86xASL_X64_ASM_FLAGS    = /nologo /c /WX /W3 /Cx /Zd /Zi
              RELEASE_VS2012x86xASL_X64_ASM_FLAGS    = /nologo /c /WX /W3 /Cx /Zd
              NOOPT_VS2012x86xASL_X64_ASM_FLAGS      = /nologo /c /WX /W3 /Cx /Zd /Zi
              
                DEBUG_VS2012x86xASL_X64_DLINK_FLAGS  = /NOLOGO /NODEFAULTLIB /IGNORE:4001 /OPT:REF /OPT:ICF=10 /MAP /ALIGN:32 /SECTION:.xdata,D /SECTION:.pdata,D /Machine:X64 /LTCG /DLL /ENTRY:$(IMAGE_ENTRY_POINT) /SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER /SAFESEH:NO /BASE:0 /DRIVER /DEBUG
              RELEASE_VS2012x86xASL_X64_DLINK_FLAGS  = /NOLOGO /NODEFAULTLIB /IGNORE:4001 /IGNORE:4254 /OPT:REF /OPT:ICF=10 /MAP /ALIGN:32 /SECTION:.xdata,D /SECTION:.pdata,D /Machine:X64 /LTCG /DLL /ENTRY:$(IMAGE_ENTRY_POINT) /SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER /SAFESEH:NO /BASE:0 /DRIVER /MERGE:.data=.text /MERGE:.rdata=.text
              NOOPT_VS2012x86xASL_X64_DLINK_FLAGS    = /NOLOGO /NODEFAULTLIB /IGNORE:4001 /OPT:REF /OPT:ICF=10 /MAP /ALIGN:32 /SECTION:.xdata,D /SECTION:.pdata,D /Machine:X64 /LTCG /DLL /ENTRY:$(IMAGE_ENTRY_POINT) /SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER /SAFESEH:NO /BASE:0 /DRIVER /DEBUG
              
      3. 編譯
          1. edksetup.bat --nt32
          2. build -t VS2012x86xASL
      4. 啟動模擬器
          build -t VS2012x86xASL run
  • 2012年5月25日 星期五

    UEFI/PI (7) UEFI Firmware Hierarchy


  • UEFI Firmware Hierarchy
      1. Firmware Storage
          Firmware Storage 是非揮發性的memory儲存BISO或Firmware的code。UEFI的其中一個優勢是DXE和PEI 可以把code解壓到firmware storage外。 Firmware Storage最小的單位是UEFI Firmware File,再來是Firmware File System(FFS)、Firmware Volumn(FV)及Firmware Device(FD)。
          • UEFI Firmware Files
              UEFI Firmware Files是最小/少的模組化的code,包含了Code及Data存放在FV中。每一個UEFI Firmware File都有Name、Type、Alignment及Size等四個attribute。 UEFI build tool會建立Firmware Files。Firmware Files的組成如下圖:
                其中,entry point包含在Data區塊裡頭。
            • Firmware File System
                Firmware File System(FFS)描述FV的file及free space的資訊。每一個FFS都有一個unique的GUID來連結FV。FFS參考FAT 32的作法,他的header如下圖。
                  同時,我們也利用UEFI Build tool建立FFS。
              • Firmware Volumes
                  一個Firmware Volumes對應著一個logical firmware device。FV是由FFS來組成的,Firmware file則是FV組成的元件。FV可以表示flash的一部分或是多個flash或甚至一個partition或是network。所有PI component都存放在FV,在PI的過程中,必須透過FFS去讀取。
                • Firmware Devices
                    Firmware Device是指非揮發性的Storage。包含了Firmware Code、Firmware Data及Fault Tolerant。
                      Firmware Device包含了FV Main放置了DXE Driver,FV Recovery放置了security及PEI code。我們以Recovery FV為例作解釋。Recovery FV包含了FFS放置著Boot Block Code(包含了PEI security phase code) 及Recovery code(包含了也許USB stack來讀取USB device)。
                  • Creating UEFI Firmware Files
                      我們如何產生UEFI Image呢?
                      1. 撰寫C code
                      2. 用build tool編譯產生Library及object file
                      3. 產生的Library及object file是intermediate file,利用微軟的compiler把他們轉成PE/COFF格式的image。
                      4. 如果是在Linux的環境,我們可以使用GCC來取代。
                      5. Build Tool會將標準的header置換成UEFI header。
                      在PEI階段,因為我們是run在rom裡面,因此我們希望Image越小越好。所以我們是製作成Terse Image。它簡化了UEFI header的項目如下圖。

                  • Physical Memory Map
                      從下圖我們可以看到各個層級之間的關係。Firmware Device被load進system memory之後,它包含了firmware volume。而firmware volume又包含了許多Firmware File System,Firmware File System又放置著許多Firmware File。

              • UEFI/PI (6) UEFI Drivers



              • UEFI Drivers and Overview
                  1. Driver's Location
                      UEFI Driver run在DXE Phase中,在platform 初始化的過程中可能重複的被active。
                       
                      同時,UEFI Driver是green H的其中一部份,如下圖所示。 


                  2. UEFI Driver's Attributes
                      UEFI Driver是system driven的image。在DXE Phase中由UEFI Loader到系統中。UEFI Driver能提供protocols同時也可能使用其他Driver提供的protocol。我們可以使用UEFI Driver去支援特定硬體或override 已經存在的Driver。
                      1. Supportive
                          UEFI Driver支援複雜的bus hierachies。UEFI Driver可以與bus interface溝通來將device連接到bus。
                      2. Independent
                          UEFI Driver可以存放在任意的storage 中 包含flash。
                      3. Flexible
                          其中一個 UEFI Driver會建立UEFI Driver Binding Protocol.這一個protocol是UEFI定義的protocol用來將Driver與device做配對。同時也支援version info、hot-plug及unload的功能。
                        • Extensible
                            UEFI Driver被設計成可以支援未知的bus或device的種類。
                    • UEFI Driver's Functions

                        1. UEFI Driver是firmware的延伸
                            可以支援新的硬體,與HW、OS無關。
                        2. Portability
                            因為與Platform Arch.無關,因此可以跨平台。
                        3. UEFI SPEC.提供許多API
                            因此可以加速開發。
                    • UEFI Driver's Contents
                        UEFI Driver包含了四大部分,
                        1.會有一個Entry Point作為Driver的進入點。
                        2.會提供Published Function 供其他的Driver使用。
                        3.Consumed Function,參考別的Driver提供出來的功能。
                        4.Data Structure,用來滿足Driver所需要的資料存放。 

                    • UEFI Driver's Entry Point
                        如果前面所提到的,DXE Dispatcher會Load Driver,UEFI Driver透過UEFI Loader將Driver Load到記憶體中。Loader 會先進入Driver的Entry Point,接著Driver會建立Published Protocol。以下圖為例,他提供了Binding Protocol( supported, Start, Stop),最後離開。

                    • Drivers v.s. Applications
                        UEFI Application 與 UEFI Driver本質上非常的類似,下表列出他們的比較。 



                • UEFI Protocols
                    1. UEFI Protocols
                        一個UEFI protocol定義在SPEC中,可能是一塊function pointer、data structure或是API。 下面分成三個部份來解釋。
                        1. Confused with Drivers
                            UEFI Driver是一個可執行的UEFI Image。它包含了許多的protocol而每一個protcol也包含了多個handle。因此,Protocl是Driver建立的。
                            我們可以把UEFI protocl想像成interface。它包含了許多function pointer, data structure或APIs。
                        2. Produced by Driver
                            UEFI Driver可以建立許多protocol。
                        3. Consumed by Anyone
                            UEFI protocol可能被任一的Driver使用。例如UEFI platform Driver可能在其他的Driver啟動時使用。
                    2. Example A: EFI_PCI_IO_PROTOCOL
                        下面的例子是PCI I/O Protocol。這一個Driver run在UEFI boot services/environment來存取memory及PCI controler的I/O。另外,與PCI相關的SPEC也定義在此protocol中。這一個Protocol的目的是要將對PCI device的讀寫抽象化。EFI_PCI_IO_PROTOCOL抽象化了對PCI 裝置的memory,I/O, PCI configuration及DMA interface的操作。每一個PCI Bus的PCI control都有對應的一個EFI_PCI_IO_PROTOCOL instance。對操作PCI裝置的Driver都必須透過對應的instance來操作。PCI controler的handle至少會有EFI_PATH_PROTOCOL及EFI_PCI_IO_PROTOCOL的instance。

                    3. Example B
                        EFI_DISK_IO_PROTOCOL是對block device存取抽象化。File System等等的code都需要依賴這個protocol。

                    4. Example C
                        這個protocl的解釋如下:UEFI Image may use the device path to match its own device Drivers to the particular device. Note that the executing UEFI OS loader and UEFI application images must access all physical devices via Boot Services device handles until ExitBootServices() is successfully called.



                • Driver Design
                    1. Design Process Steps

                    2. Driver Types
                        在撰寫Driver之前,必須先知道我們要寫的是哪一種Driver。在UEFI Image中可以分成兩種,Driver及Application。
                        UEFI Applications中有一種特殊的application叫做OS Loader。與一般的Application不同的是OS Loader會呼叫 ExitBootService然後就將control pass到OS kernel了。
                        接著我們來了解Driver中的幾種類型。
                        1. Service Driver
                            a. 不管理hardware。 
                            b.提供service給其他Driver。 
                            c.不支援Driver binding protocol。 
                            d. 在entry point裡install protocol。  
                            e. 建立一個或多個 service handlers。 
                            f. 產生service-specific protocols。 
                            例如: UEFI Decompress Protocol、UEFI Byte Code Virtual Machine、Boot Integrity Service(BIS)。
                        2. Initialization Drivers
                            a. 與hardware會有溝通。 
                            b. one-time initialization。 
                            c. 不會建立handle及protocol。 
                            d. 結束後就會unload。
                        3. Root Bridge Driver
                            a. 管理部分的core chipset。 
                            b. 直接與HW溝通。 
                            c. 建立一個或多個 root bridge handle。 
                            d. 建立 root bridge I/O protocols,安裝在新的root bridge handle。 例如:PCI Host Bridge。
                        4. Bus Driver
                            他是UEFI Driver Model的Driver。用來管理Bus controller。
                            start()會建立一個或多個child handles及Bus Specific I/O protocols。例如:PCI NIC card、UART controllers。
                        5. Device Driver
                            管理controller或peripheral device。
                            start()不會建立child handle但會建立一個或多個protocols。
                            例如:PCI Video Adapter、USB Host Controller、USB keyboard/Mice。
                        6. Hybrid Driver
                            結合Bus Driver及device Driver。
                            例如:PCI SCSI Host Controller、PCI Fiber Channel Controller。
                    3. Consumed Protocols
                        什麼時候會使用那些Protocl呢?例如 PCI Adapter需要PCI I/O Protocol或加上也許Device Path protocol。 而像USB keyboard/mice, DVD rom這類的裝置就需要USB I/O protocol及device path protocol。
                    4. Produces Protocols
                        那我們可能會產生什麼protocol呢?例如,假設是在寫keyboard Driver,我們可能會建立simple input protocol;滑鼠可能會有simple pointer protocol;USB flash disk可能會有block I/O protocol。Depends on 你寫的裝置。



                • Writing Drivers
                    1. Writing UEFI Drivers
                        接下來,我們寫一個Driver需要那些protocol呢?參考下表:

                    2. Initialized
                        當UEFI Driver被dispatch時,首先會進入entry point然後初始化。在初始化的過程中會宣告此Driver會提供那些protocol。UEFI Driver 是relocatable pickup image,所以可以load在記憶體的任意位置。在Driver initialize的過程中是不會touch HW的,在過程中會呼叫Driver binding protocol,在初始化後,service就被註冊了,這也是會什麼可以快速開機的原因。
                    3. Supported
                        在初始化後,Binding Protocol會開始define Supported、Start及Stop function。
                        接下來以PCI Driver為例。 首先Open PCI Protocol( EFI_PCI_IO_PROTOCOL),然後check這個Driver是否支援這個controller( EFI_DEVICE_PATH_PROTOCOL)再來關閉PCI Protcol並return 是否支援。


                    4. Start
                        Start()會產生EFI_Block_IO_Protocol 建立child protocol(functions)。

                    5. Stop
                        Stop不會產生任何的Protocol,反而他會移除Start()所產生的Protocol。

                    6. Recommended Protocols


                • Tips and Techniques

                    1. Driver Guideline
                        主要有四個原則
                        1. 在Driver entry的地方不要touch HW
                        2. 在Support()保持簡單。
                        3. Start()對應Stop();Driver Entry對應Unload
                        4. 把複雜的事情放到Start()和Stop()去做。
                    2. Design Checklist

                    3. Recommendations
                        在實作Test/Debug的時候有幾點注意的事情如下:

                    4. Start() Code
                        這個範例是儲存PCI的Attributes。

                    5. Stop() Code
                        將Attribute reset回原本的值。

                    6. Library Functions
                        我們應該多利用既有的function來將code size降低,如下:

                    7. Other Helpful Suggestions
                        這裡分成兩大塊。
                        Reducing Option ROM Size
                        1. 使用UEFI支援的Compression來降低ROM Size。
                        2. Compile with EFI Byte Code Compiler(EBC)

                        Improving Protability
                        1. 不要假設max number of children。
                        2. 不要寫死memory address或使用assembly。
                        3. 不要使用floating point arithmetic
                        4. Dome minor ebc point considerations
                        5. Bus Driver應該支援一次產生一個child。(可以加速開機)
                • 2012年5月23日 星期三

                  UEFI/PI (5) Driver Execution Environment





                • DXE Overview
                    1. DXE Overview
                        Driver Execution Enviroment (DXE)是 PI SPEC.裡的第二個phase,在這個phase中有許多的東西被啟動或初始化。同時,EFI services、protocols及driver都在這個時候被implement或support。DXE也建立了許多system table。
                      • Importance of DXE
                          Without DXE
                          1. 每個人的coding style 不同,同時,每家code base也可能不一樣。因此,BIOS的feature的實作方式也會因為vendor不同而有所差異,這造成了移植的困難。
                          2. 一個單一的source file控制整個啟動流程,造成開發新的feature或更改上的困難。
                          With DXE
                          1. DXE參考OS的做法,driver及library都定義得很清楚,因此也很容易實作模組化的 driver。
                      • DXE Foundation's Function
                          DXE Foundation是DXE Phase的核心,他有許多的functions可以使用。下面分成四個部分來介紹DXE Foundation。
                            
                          1. Initialize Platform
                              DXE Foundation initial Chipset及platform。 其中一個function會load driver來建構環境,最後boot manager 及OS
                          2. Dispatch DXE
                              DXE foundation會先dispatch DXE driver,他會處理相關的相依問題。
                          3. Dispatch UEFI
                              在dispatch DXE driver後,DXE foundation會去dispatch UEFI driver,而UEFI driver是沒有相依問題的。
                          4. Load Boot Manager
                              每一個driver在entry point的地方都必須註冊與那些protocol相依,因此Load Boot Manager可以依此做initial 的排序。最後會將control交給下一個phase - BDS。
                      • DXE Foundation Properties
                          DXE Foundation有許多的特性,以下一一介紹。
                          1. HOB List Dependent
                              上一個phase PEI會將所有的hardware, reosurce的 state放到HOB傳給DXE,因此,DXE無須知道hard-coded address
                          2. No Hardware Specifics
                              DXE drivers必須透過Architecture Protocols(APs)來與硬體溝通。因此不可以假定現在使用的是哪個硬體然後去使用它特定的東西,例如address。
                          3. No Hard-Code Address
                              因為沒有hard-coded的address,在system memory任何地方都可以load DXE foundation。
                      • DXE Components
                          DXE 同樣有許多的component,以下一一介紹。
                          1. Drivers
                              負責初始化Processor、Chipset及Platform。而DXE Driver 更是負責建構APs。
                          2. Foundation
                              負責Dispatch drivers、建立Boot services、Runtime Services及DXE Services。
                          3. Architectural Protocols(APs)
                              因為沒有hard-coded的address,在system memory任何地方都可以load DXE foundation。
                          4. EFI System Table
                              EFI system table存放了指標指向包含了UEFI Service tables、 Configuration tables、 Handle databases及console devices
                          5. Dispatcher
                              DXE Dispatcher會依照相依以正確的順序啟動 driver。
                      • DXE Phase Flow
                          DXE的執行階段可以分成幾個步驟:
                          1. DXE Foundation Initial Services,包含UEFI Service及DXE Service。
                          2. DXE Foundation Dispatch DXE drivers。
                          3. DXE Dispatcher 搜尋FV裡面是否有priori file,依照順序將driver load到memory。
                          4. 接著尋找FV裡剩下的Driver並Load進memory。




                  • Data Structures and Events
                      1. Data Structures

                      2. Events
                          因為Event是與CPU architecture不相關的,因此UEFI使用event來作為object、protocol之間的訊號。 解釋下圖,主要可以分成Singnal events及wait events。
                           
                          1. Exit Boot Service Events
                              是當呼叫ExitBootService()的時候從wait event轉回signaled state。
                          2. Set Virtual Address Map Events
                              SetVirtualAddressState()
                          3. Periodic Timer Events
                              是週期性觸發的event
                          4. One Shot Timer Events
                              可以指定時間觸發的timer event
                          5. Wait Event
                        • EFI System Table





                    • DXE Foundation
                        1. DXE Foundation Code Flow
                            DXE Foundation有兩個attributes: Single Threaded Environment and One Interrupt
                            1. Single Threaded Environment
                                a. 在DXE Foundation的code只會有Boot Strap Processor(BSP)會執行。 b. 其他的application processor都進入 wait mode。
                            2. One Interrupt
                                a. DXE foundation只使用timer tick作為software的interrupt。 b. 因為只使用timer iterrupt,因此所有的devices都會被pulled c. DXE foundation 會使用event取代multiple interrupts。
                        2. DXE Main
                          • DXE Main Source Code






                      • Architecture Protocols
                          1. APs Defined
                              DXE藉由DXE Drivers 會產生 Architectural Protocols(APs)抽象化硬體的溝通,以下以三個部份來介紹。
                              1. Function
                                  可以把AP's 想像成脫離platform-specific hardware的wrapper function。舉例來說,CPU AP 管理interrupt、取得processor info、取得processor-based的timer。
                              2. Support
                                  AP's 是low-level的protocols用來支援啟動以及runtime services。我們可以利用higher-level的platform function來使用AP's提供的功能。
                              3. Dependency
                                  有些AP可能會與其他的AP相依,舉例來說,whatchdog 需要IO及timer的access。有三種方式可以解決相依的問題:a. 利用Dependency Grammar來load DXE b. 使用RegistryProtocolNotify()來通知AP被load進去了。 c. 使用Priorl List將預先知道的AP 依照相依順序排好讓dispatcher按照這個順序dispatch。
                          2. APs' Role
                              ,藍色的在os runtime會被free掉,灰色則可以繼續使用
                          3. APs' Location






                      • DXE Dispatcher
                          1. DXE Dispatcher
                              DXE Dispatcher 負責load和dispatch FV(Firmware Volume)裡的 DXE及UEFI driver。 DXE Dispatcher主要有幾個目標如下:
                              1. Driver可能是由不同的組織、部門所撰寫。因此Dispatcher必須能處理DXE Driver 之間的dispatch 順序
                              2. DXE Dispatcher支援例如emergency patches。另外,DXE Dispatcher也讓開發人員更容易整合自己開發的driver。
                          2. DXE Dispatcher Source Code
                            • DXE Dispatcher State Machine
                              • DXE State Machine Example




                          3. DXE Drivers
                              1. DXE Driver Type
                                  DXE drivers包含了device或service code執行在UEFI driver之前。如同之前所提到的,DXE 階段的driver分成兩種,即DXE Driver和UEFI Driver。下表比較了兩個的不同。 
                                • BDS Driver
                                    在DXE階段最後一個driver是BDS( Boot Device Selection) Driver。BDS driver負責建立console( keyboard, video)及執行UEFI Boot Option。 下圖是啟動BDS的示意圖。額外說明的是,因為當Dispatcher load BDS的時候,會先進去她的entry point讀取相依的APs,因此假設仍有未完成的APs則可能回到DXE Dispatcher去load其他的driver。 




                            • SMM
                                System Management Mode(SMM)他是CPU的一個模式,當我們觸發CPU的SMI Pin的時候就會進入SMM Mode。 SMM模式下的執行的程序被稱作SMM處理程序,所有的SMM處理程序只能在稱作系統管理內存(System Management RAM,SMRAM)的空間內運行。 
                                1. SMM Service
                                    SMM service是高優先權的system management interrupt(SMI),在runtime的時候仍然可以接管系統。下面分成三個特性及兩個service特點來介紹。 Characteristics
                                    1. 在SMI的時候,CPU執行pre-defined starting vector。
                                    2. SMM的code放在SMRAM中,在SMM初始化後會lock起來。
                                    3. SMI是由hardware event或 system interrupt所產生的。它可以被偵測、清除或disable。
                                    Services
                                    1. SMM service是一個handlers的list。並且都是DXE foundation service的subset。
                                    2. SMM services和DXE foundation services提供同樣的functionality但不同的是SMM locate在SMRAM中。
                                    3. SMI是由hardware event或 system interrupt所產生的。它可以被偵測、清除或disable。
                            • 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。