我們可以在\Build\NT32IA32\DEBUG_VS2013\IA32\MdeModulePkg\Universal\BdsDxe\BdsDxe\OUTPUT\ 目錄下,找到編譯生成的 BdsDxe.efi。 在 \Build\NT32IA32\DEBUG_VS2013\FV\Ffs\6D33944A-EC75-4855-A54D-809C75241F6CBdsDxe可以看到檔案名為6D33944A-EC75-4855-A54D-809C75241F6C.ffs 的檔案。就是說我們前面看到的BdsDxe.efi,最終得到了一個ffs檔案。
對[我所認識的BIOS]系列 -- CPU的第一條指令 一文擴充(II) 一文多次提到platform.fdf,Bios Rom Image在生成過程中使用該檔案來建構各子產品在Rom Image中的布局,可以說platform.fdf是組織委員了。哪麼是誰任命組織委員----由platform.dsc(班長?)指定。來看個platform.dsc檔案:
;Build\platform.dsc
[Defines]
PLATFORM_VERSION = 1
BUILD_TARGETS = RELEASE | DEBUG
OUTPUT_DIRECTORY = Build/CometLake
FLASH_DEFINITION = Build/Platform.fdf
SUPPORTED_ARCHITECTURES = IA32 | X64
PLATFORM_NAME = CometLake
platform.dsc不僅指派了組織委員,還給組織委員規劃了辦公地點Build\CometLake----從源碼到建構成Ffs都在這個目錄下進行。本文以SecCore.ffs為例,介紹一下從 EFI 到 FFS的編譯過程。整個過程依賴于GenFw\GenFfs\GenFv等EFI工具,以及cl\ld\Makefile等傳統編譯工具。從源碼到生成Ffs的整個編譯過程的由各個子產品目錄下根據inf檔案自動生成的Makefile控制完成,該Makefile檔案位于:${DSCFILE_OUTPUT_DIRECTORY}\${TOOL_CHAIN}\${ARCH}\${MODULE_RELATIVE_DIR}\${PACKAGE_RELATIVE_DIR},對于SecCore子產品,以上值依次為:
DSCFILE_OUTPUT_DIRECTORY = Build\CometLake\
TOOL_CHAIN = RELEASE_VS2015\
ARCH = IA32\
MODULE_RELATIVE_DIR = UefiCpuPkg\SecCore\
PACKAGE_RELATIVE_DIR = SecCore
是以自動生成的Makefile位于Build\CometLake\RELEASE_VS2015\IA32\UefiCpuPkg\SecCore\SecCore下
;Build\CometLake\RELEASE_VS2015\IA32\UefiCpuPkg\SecCore\SecCore\Makefile部分變量定義
MODULE_NAME = SecCore
MODULE_GUID = 1BA0062E-C779-4582-8566-336AE8F78F09
MODULE_NAME_GUID = SecCore
MODULE_VERSION = 1.0
MODULE_TYPE = SEC
MODULE_FILE = SecCore.inf
MODULE_FILE_BASE_NAME = SecCore
BASE_NAME = $(MODULE_NAME)
MODULE_RELATIVE_DIR = UefiCpuPkg\SecCore
PACKAGE_RELATIVE_DIR = SecCore
FFS_OUTPUT_DIR = Build\CometLake\RELEASE_VS2015\FV\Ffs\1BA0062E-C779-4582-8566-336AE8F78F09SecCore
OUTPUT_DIR = Build\CometLake\RELEASE_VS2015\IA32\UefiCpuPkg\SecCore\SecCore\OUTPUT
DEBUG_DIR = Build\CometLake\RELEASE_VS2015\IA32\UefiCpuPkg\SecCore\SecCore\DEBUG
Step1.源碼->WinPE階段:
這個階段,使用微軟編譯器cl和連接配接器ld,根據SecCore.inf BASE_NAME的值生成SecCore.dll,這部分内容是編譯連結的基本功:
a.編譯生成obj檔案;
;Makefile中于生成obj相關的内容
$(OUTPUT_DIR)\SecMain.obj : $(MAKE_FILE)
$(OUTPUT_DIR)\SecMain.obj : $(COMMON_DEPS)
$(OUTPUT_DIR)\SecMain.obj : $(WORKSPACE)\UefiCpuPkg\SecCore\SecMain.c
"$(CC)" /FoBuild\CometLake\RELEASE_VS2015\IA32\UefiCpuPkg\SecCore\SecCore\OUTPUT\.\SecMain.obj $(CC_FLAGS) $(INC) UefiCpuPkg\SecCore\SecMain.c
$(OUTPUT_DIR)\FindPeiCore.obj : $(MAKE_FILE)
$(OUTPUT_DIR)\FindPeiCore.obj : $(COMMON_DEPS)
$(OUTPUT_DIR)\FindPeiCore.obj : $(WORKSPACE)\UefiCpuPkg\SecCore\FindPeiCore.c
"$(CC)" /FoBuild\CometLake\RELEASE_VS2015\IA32\UefiCpuPkg\SecCore\SecCore\OUTPUT\.\FindPeiCore.obj $(CC_FLAGS) $(INC) UefiCpuPkg\SecCore\FindPeiCore.c
$(OUTPUT_DIR)\AutoGen.obj : $(MAKE_FILE)
b.連結生成PE32:
#連結用到的obj
OBJECT_FILES = \
$(OUTPUT_DIR)\SecMain.obj \
$(OUTPUT_DIR)\FindPeiCore.obj \
$(OUTPUT_DIR)\SecBist.obj \
$(OUTPUT_DIR)\AutoGen.obj
#連結時依賴的lib
STATIC_LIBRARY_FILES = \
$(BIN_DIR)\AmiChipsetModulePkg\Library\AmiCspPcieLib\AmiCspPcieBaseLib\OUTPUT\AmiCspPcieBaseLib.lib \
$(BIN_DIR)\AmiChipsetPkg\Library\AmiChipsetIoLib\AmiChipsetIoLib\OUTPUT\AmiChipsetIoLib.lib \
...
$(BIN_DIR)\CrbPkg\Library\CrbSecLib\CrbSecLib\OUTPUT\CrbSecLib.lib \
$(OUTPUT_DIR)\SecCore.lib
#生成lib
$(OUTPUT_DIR)\SecCore.lib : $(OBJECT_FILES)
"$(SLINK)" $(SLINK_FLAGS) /OUT:Build\CometLake\RELEASE_VS2015\IA32\UefiCpuPkg\SecCore\SecCore\OUTPUT\SecCore.lib @$(OBJECT_FILES_LIST)
#連結所有的lib,生成dll
$(DEBUG_DIR)\SecCore.dll : $(MAKE_FILE)
$(DEBUG_DIR)\SecCore.dll : $(STATIC_LIBRARY_FILES)
$(DEBUG_DIR)\SecCore.dll : $(STATIC_LIBRARY_FILES_LIST)
"$(DLINK)" /OUT:Build\CometLake\RELEASE_VS2015\IA32\UefiCpuPkg\SecCore\SecCore\DEBUG\SecCore.dll $(DLINK_FLAGS) $(DLINK_SPATH) @$(STATIC_LIBRARY_FILES_LIST)
Step2.WinPE->Efi階段:
在這個階段,Makefile引入了新的工具GenFw:
;從PE32到Efi階段
MODULE_ENTRY_POINT = _ModuleEntryPoint
...
OBJECT_FILES = \
$(OUTPUT_DIR)\SecMain.obj \
$(OUTPUT_DIR)\FindPeiCore.obj \
$(OUTPUT_DIR)\SecBist.obj \
$(OUTPUT_DIR)\AutoGen.obj
$(DEBUG_DIR)\SecCore.efi : $(DEBUG_DIR)\SecCore.dll
"$(GENFW)" -e $(MODULE_TYPE) -o Build\CometLake\RELEASE_VS2015\IA32\UefiCpuPkg\SecCore\SecCore\DEBUG\SecCore.efi Build\CometLake\RELEASE_VS2015\IA32\UefiCpuPkg\SecCore\SecCore\DEBUG\SecCore.dll $(GENFW_FLAGS)
$(CP) Build\CometLake\RELEASE_VS2015\IA32\UefiCpuPkg\SecCore\SecCore\DEBUG\SecCore.efi $(OUTPUT_DIR)
$(CP) Build\CometLake\RELEASE_VS2015\IA32\UefiCpuPkg\SecCore\SecCore\DEBUG\SecCore.efi $(BIN_DIR)\$(MODULE_NAME_GUID).efi
-$(CP) $(DEBUG_DIR)\*.map $(OUTPUT_DIR)
-$(CP) $(DEBUG_DIR)\*.pdb $(OUTPUT_DIR)
衆所周知,Windows PE檔案可以用IDA加載分析,efi檔案也同樣可以被IDA加載:

Step3:GenSec及GenFfs階段:
當上一個階段完成時,制作Ffs所需的二進制檔案基本都有了,可以合成Ffs。Ffs是Firmware File system的縮寫。一般File System用于管理多個檔案,EFI既然引入File system的概念,暗示我們在Ffs内包含若幹binary檔案的事實。這些binary檔案由GenSec生成,作為Section包含到Ffs中。
a.GenSec:
$(FFS_OUTPUT_DIR)\$(MODULE_GUID)SEC1.1.te : $(FFS_OUTPUT_DIR)\$(MODULE_GUID)Te.raw
GenSec -s EFI_SECTION_TE -o $(FFS_OUTPUT_DIR)\$(MODULE_GUID)SEC1.1.te $(FFS_OUTPUT_DIR)\$(MODULE_GUID)Te.raw
$(FFS_OUTPUT_DIR)\$(MODULE_GUID)Te.raw : $(FFS_OUTPUT_DIR)\$(MODULE_GUID).stripped
GenFw -t -o $(FFS_OUTPUT_DIR)\$(MODULE_GUID)Te.raw $(FFS_OUTPUT_DIR)\$(MODULE_GUID).stripped
$(FFS_OUTPUT_DIR)\$(MODULE_GUID).stripped : Build\CometLake\RELEASE_VS2015\IA32\UefiCpuPkg\SecCore\SecCore\DEBUG\SecCore.efi
GenFw -l -o $(FFS_OUTPUT_DIR)\$(MODULE_GUID).stripped Build\CometLake\RELEASE_VS2015\IA32\UefiCpuPkg\SecCore\SecCore\DEBUG\SecCore.efi
$(FFS_OUTPUT_DIR)\$(MODULE_GUID)SEC3.1.raw : $(OUTPUT_DIR)\ResetVec.bin
GenSec -s EFI_SECTION_RAW -o $(FFS_OUTPUT_DIR)\$(MODULE_GUID)SEC3.1.raw $(OUTPUT_DIR)\ResetVec.bin
Makefile顯示兩條依賴鍊,通過這兩條依賴鍊制作Section:
a.1:GenSec生成Sec1.1.te。SEC1.1.te依賴Te.raw; Te.raw又依賴secore.stripped:
GenFw首先剝離Seccore.efi的重定位資訊,然後生成TE頭,而IDA也能加載此時生成的TE檔案:
$(FFS_OUTPUT_DIR)\$(MODULE_GUID)Te.raw : $(FFS_OUTPUT_DIR)\$(MODULE_GUID).stripped
GenFw -t -o $(FFS_OUTPUT_DIR)\$(MODULE_GUID)Te.raw $(FFS_OUTPUT_DIR)\$(MODULE_GUID).stripped
$(FFS_OUTPUT_DIR)\$(MODULE_GUID).stripped : Build\CometLake\RELEASE_VS2015\IA32\UefiCpuPkg\SecCore\SecCore\DEBUG\SecCore.efi
GenFw -l -o $(FFS_OUTPUT_DIR)\$(MODULE_GUID).stripped Build\CometLake\RELEASE_VS2015\IA32\UefiCpuPkg\SecCore\SecCore\DEBUG\SecCore.efi
a.2:Sec3.1.raw依賴ResetVec.bin
$(FFS_OUTPUT_DIR)\$(MODULE_GUID)SEC3.1.raw : $(OUTPUT_DIR)\ResetVec.bin
GenSec -s EFI_SECTION_RAW -o $(FFS_OUTPUT_DIR)\$(MODULE_GUID)SEC3.1.raw $(OUTPUT_DIR)\ResetVec.bin
b.當這兩個Section生成後,GenFfs開始制作Ffs:
$(FFS_OUTPUT_DIR)\$(MODULE_GUID).ffs : $(FFS_OUTPUT_DIR)\$(MODULE_GUID)SEC1.1.te $(FFS_OUTPUT_DIR)\$(MODULE_GUID)SEC3.1.raw
GenFfs -t EFI_FV_FILETYPE_SECURITY_CORE -g $(MODULE_GUID) -o $(FFS_OUTPUT_DIR)\$(MODULE_GUID).ffs -i $(FFS_OUTPUT_DIR)\$(MODULE_GUID)SEC1.1.te -n 8 -i $(FFS_OUTPUT_DIR)\$(MODULE_GUID)SEC3.1.raw -n 16
生成的Ffs檔案位于${DSCFILE_OUTPUT_DIRECTORY}\${TOOL_CHAIN}\${ARCH}\FV\Ffs\${FILE_GUID}\目錄下,其中FILE_GUID就是子產品inf檔案中指定的FILE_GUID,對于SecCore的Ffs位于Build\CometLake\RELEASE_VS2015\FV\Ffs\1BA0062E-C779-4582-8566-336AE8F78F09SecCore下:
打開UEFITool并展開File GUID: 1BA0062E-C779-4582-8566-336AE8F78F09項,會發現該項包含的Section其實就是以上GenSec和GenFfs的共同結果:
最後GenFv會使用FV\Ffs目錄下的Ffs制作生成FV和FD。
============================================================================================
後記GenSec附帶一個作用:
有時廠商會以binary File的形式更新的網卡/顯示卡驅動,bios如果需要更新該驅動,最終會以GenSec的方式包含到platform.fdf中以最終生成bios rom image:
下面是包含GopDriver.efi驅動的例子:
;NB\GOP\IntelSaGopDriver.sdl
TOKEN
Name = "OEM_INTEL_GOP_EFI_DRIVER_FILE"
Value = "NB/GOP/CometLake/x64/IntelGopDriver.efi"
TokenType = Expression
TargetMAK = Yes
TargetFDF = Yes
Token = "CPU_CFL_SUPPORT" "=" "1"
End
;NB\GOP\VbtFdfFileStatements.txt
!if $(MULTIPLE_VBIOS_AND_VBT_SUPPORT) == 0
#
# File GUID is as same as gIntelSiliconPkgTokenSpaceGuid.PcdIntelGraphicsVbtFileGuid
#
FILE FREEFORM = 56752da9-de6b-4895-8819-1945b6b76c22 {
SECTION RAW = $(OEM_INTEL_GOP_VBT_BIN_FILE)
}
!else
FILE FREEFORM = DCB132E7-27D2-40FF-9C3F-9F280B3D10F5 {
SECTION RAW = $(OEM_PLATFROM_1_INTEL_GOP_VBT_BIN_FILE)
}
!endif
#FV Section
[FV.FV_BB_AFTER_MEMORY]
BlockSize = 0x1000
NumBlocks = 0x100
...
!include NB/GOP/VbtFdfFileStatements.txt
!include Intel/CometLakeSiliconPkg/SystemAgent/IntelGraphicsPeim/PeiLogoFdfFileStatements.txt
MicroCode更新也類似:
;某Microcode sdl
;Microcode\Desktop\KabyLakeDesktopUc.sdl
ELINK
Name = "$(MICROCODE_DESKTOP_DIR)/M22506E8_00000034.mcb"
Parent = "MICROCODE_FILES"
Help = "Intel(R) KabyLake -S -X Processor A-0 Stepping"
Token = "DESKTOP_M22506E8" "=" "1"
InvokeOrder = AfterParent
End
;Microcode\Microcode.sdl MicroCode Elink頭結點
ELINK
Name = "MICROCODE_FILES"
InvokeOrder = ReplaceParent
End
;Microcode\Microcode.mak, MicroCode.bin生成Mak
#List of microcode files separated by spaces.
MICROCODE_FILES_LIST = $(strip $(MICROCODE_FILES))
#List of merge64 formated microcode files separted by spaces.
MERGE_64_MCODE_STRING = $(foreach Microcode, $(MICROCODE_FILES_LIST), \
file $(Microcode) binfile=$(Microcode) align=$(MICROCODE_ALIGNMENT) end)
;FIT\FitTable\FitMicrocodeFdfFileStatement.txt
FILE RAW = 17088572-377F-44ef-8F4E-B09FFF46A070 Align=16 LOCATION=$(FV_MICROCODE_BINARY_BASE) {
Build/MICROCODE.BIN
}
;platform.fdf
[FV.FV_DATA]
BlockSize = 0x1000
NumBlocks = 0xc0
...
!include AmiChipsetModulePkg/FIT/FitTable/FitMicrocodeFdfFileStatement.txt
參考: