对[我所认识的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加载:

对[我所认识的BIOS]系列 -- CPU的第一条指令 一文扩充(III):从源代码到 FFS 文件_第1张图片对[我所认识的BIOS]系列 -- CPU的第一条指令 一文扩充(III):从源代码到 FFS 文件_第2张图片

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下:

对[我所认识的BIOS]系列 -- CPU的第一条指令 一文扩充(III):从源代码到 FFS 文件_第3张图片

打开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 文件

你可能感兴趣的:(bios)