對[我所認識的BIOS]系列 -- CPU的第一條指令 一文擴充(III):從源代碼到 FFS 文件

我們可以在\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

參考:

UEFI Relocation 原理

從源代碼到 FFS 文件

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章