我们可以在\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   =  1BUILD_TARGETS  =  RELEASE | DEBUGOUTPUT_DIRECTORY =  Build/CometLakeFLASH_DEFINITION =  Build/Platform.fdfSUPPORTED_ARCHITECTURES   =  IA32 | X64PLATFORM_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-336AE8F78F09SecCoreOUTPUT_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.rawGenSec -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).strippedGenFw -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.efiGenFw -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.binGenSec -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).strippedGenFw -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.efiGenFw -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.binGenSec -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.rawGenFfs -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
TOKENName  = "OEM_INTEL_GOP_EFI_DRIVER_FILE"Value  = "NB/GOP/CometLake/x64/IntelGopDriver.efi"TokenType = ExpressionTargetMAK = YesTargetFDF = YesToken = "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)}
!elseFILE 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
ELINKName  = "$(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头结点
ELINKName  = "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]系列 -- CPU的第一条指令 一文扩充(III):从源代码到 FFS 文件相关推荐

  1. 对[我所认识的BIOS]系列 -- CPU的第一条指令 一文扩充(II):从FDF到Bios Rom image

    在"对[我所认识的BIOS]系列 -- CPU的第一条指令 一文扩充(I)"一文中,我用EFITool工具加载了BIOS Rom,发现Reset Vector位于BIOS Rom ...

  2. 【自己动手写CPU】第一条指令ori的实现

    验证过程 实现ori指令->建立最小SOPC->验证ori指令是否正确 ori指令说明 ori是进行逻辑"或"的运算指令 ori指令的指令码是6'b001101.处理器 ...

  3. 【我所認知的BIOS】--第一条指令

    [我所認知的BIOS]-->第一条指令 By LightSeed 2009-10-26 其实早就想写这样一篇文章了,今天才着手写了下.说来也惭愧关于CPU的第一条指令的问题,在一开始study的 ...

  4. 自己动手写CPU(1)五级流水线及CPU第一条指令ori

    自己动手写CPU(1)五级流水线及CPU第一条指令ori 动机 不知为何研一的自由时间突然多起来,可能人一闲下来就容易焦虑吧,hhhhhh.正好之前看到一本<自己动手写CPU>,就按照此书 ...

  5. CPU加电后第一条指令

    当我们按下电源开关时,电源就开始向主板和其它设备供电,此时电压还不太稳定,主板上的控制芯片组会向CPU发出并保持一个RESET(重置)信号,让 CPU内部自动恢复到初始状态,但CPU在此刻不会马上执行 ...

  6. 计算机开机执行的第一条指令是什么?

    第一条指令的位置在FFFF:0000,也就是物理地址FFFF0.第一条指令是跳转到F000:EO5B. 接下来准备由实模式进入保护模式.加载GDT,置PE位为1,清指令预取队列并真正进入保护模式. 那 ...

  7. verilog实现多周期处理器之——(二)第一条指令ori的实现

    本博文希望对于OpenMIPS第一条指令ori加以实现并总结.会加入一些基本的理论以及博主的学习记录. 流水与五级流水 什么是流水:拆分,并行.将多条指令的执行相互重叠起来.就构成了流水,这样充分利用 ...

  8. 【基础】ARM芯片上电取第一条指令流程

    转载:ARM上电启动及Uboot代码分析 网上关于ARM的bootloader(以Uboot为例)的启动顺序的资料有好多,但是对于Uboot的地址映射.体系结构级操作介绍很少,都是直接开始Start. ...

  9. ARM上电后第一条指令

    网上关于ARM的bootloader(以Uboot为例)的启动顺序的资料有好多,但是对于Uboot的地址映射.体系结构级操作介绍很少,都是直接开始Start.s代码的阅读.本文拟详细分析Uboot从上 ...

最新文章

  1. 他智商167,超过爱因斯坦,花17年炸遍美国高校,却因一疏漏被捕
  2. 用例设计方法及其覆盖率
  3. 关于设置GridControl属性在代码中的顺序带来的不同效果
  4. mysql使用数据库预处理_php中对MYSQL操作之预处理技术(2)数据库dql查询语句
  5. 零基础开始学 Web 前端开发,有什么建议吗?--知
  6. Linux中如何使用Htop监控工具?【网络安全】
  7. 使用 C# 下载文件的十八般武艺
  8. Node.js压缩与解压数据
  9. android使用Dialog实现复选功能与数据库结合综合实例
  10. Android sdcard读写权限问题之中的一个
  11. 设计篇-网页设计规范
  12. 高速扩张的云市场,需要怎样的安全能力?
  13. 服务器硬件配置应如何选择?
  14. Origin科研绘图20211129:双y轴能量曲线及loess光滑处理
  15. nginx反向代理加gitlab认证
  16. WinForm的控件
  17. C++-------什么是this指针?this指针详解
  18. 陈阳,她离开微软,投身开源10年!只管热爱,无问西东
  19. wince调节屏幕亮度
  20. [转载]ExtJs4 笔记(8) Ext.slider 滚轴控件、 Ext.ProgressBar 进度条控件、 Ext.Editor 编辑控件...

热门文章

  1. 华三imc服务器型号,华三imcportal服务器常见错误分析报告.doc
  2. 杨云华师大计算机,2017-2018学年第二学期教师辅导-华东师范大学计算中心网站.DOC...
  3. Unity3D---通过Shader实现美颜
  4. Android开发-AMD平台如何使用Android studio自带模拟器
  5. 【pytorch】时间序列预测 —— 同时预测多个分位点
  6. SQL代码建表时引用外键,有红线提示引用了无效的表
  7. golang sync.Map和map+mutex性能比较
  8. pacman / yaourt 命令
  9. [转帖]彩色硬盘 关于西数硬盘 企业盘、黑盘、蓝盘、绿盘、红盘的区别
  10. matlab笔记 与excel表格的数据交互—xlsread和xlswrite函数