在"对[我所认识的BIOS]系列 -- CPU的第一条指令 一文扩充(I)"一文中,我用EFITool工具加载了BIOS Rom,发现Reset Vector位于BIOS Rom Image的最底部。本文将探索形成这样的Image结构相关的各种文件(.fdf/.fd/.fv等文件)。

先说说我手头这个BIOS Rom,它由GenFv工具根据fdf文件生成:

;引自Build\Platform.fdf
[FD.ROM]
BaseAddress = 0xff300000
Size = 0xD00000
BlockSize = 0x1000
NumBlocks = 0xd000x0|0x30000
FV = NVRAM0x30000|0x30000
#RAW - NVRAM_BACKUP0x60000|0x20000
#NCB_LOGO
FILE = BootLogo/DummyNcbLogo.bin0x80000|0x8d0000
FV = FV_MAIN_WRAPPER0x950000|0xC0000
FV = FV_DATA0xa10000|0x100000
FV = FV_BB_AFTER_MEMORY0xb10000|0xe8000
FV = FV_FSP_S0xbf8000|0x68000
#FV_FSP
FILE = Build/Fsp_Rebased_M_T.fd0xc60000|0xa0000
FV = FV_BB#FV Section
[FV.FV_MAIN]
BlockSize = 0x1000
NumBlocks = 0x0
...
APRIORI DXE {INF AmiModulePkg/AmiStatusCode/StatusCodeDxe.infINF AmiModulePkg/AmiStatusCode/StatusCodeSmm.inf
...
}INF AmiModulePkg/RomLayout/RomLayoutDxe.inf
INF MdeModulePkg/Core/Dxe/DxeMain.inf
INF AmiModulePkg/Bds/Bds.inf
...[FV.FV_MEFW_CAPSULE]
BlockSize = 0x1000
NumBlocks = 0x600
...
!include AmiModulePkg/Ofbd/Meud/AutoMeud/MeRegionFdfFileStatement.txt[FV.NVRAM]
BlockSize = 0x1000
NumBlocks = 0x30
...
!include AmiModulePkg/NVRAM/NvramFdfFileStatement.txt
0xc60000|0xa0000[FV.FV_MAIN_WRAPPER]
BlockSize = 0x1000
NumBlocks = 0x8d0
...
!include AmiPkg/Configuration/NestedFvMainFdfFileStatement.txt[FV.FV_FSP_S]
BlockSize = 0x1000
NumBlocks = 0xe8
...
!include Intel/CometLakeFspBinPkg/FvFspSFdfFileStatement.txt[FV.FV_BB]
BlockSize = 0x1000
NumBlocks = 0xa0
APRIORI PEI {INF AmiModulePkg/AmiStatusCode/StatusCodePei.inf...
}
...
INF MdeModulePkg/Core/Pei/PeiMain.inf
...
INF Toshiba/FwUpdate/PeiRecKeyCheck/PeiRecKeyCheck.inf
INF UefiCpuPkg/SecCore/SecCore.inf
INF AmiModulePkg/AmiStatusCode/StatusCodePei.inf
...

fdf文件的开头部份是关键字[fd],它表示完整的BIOS Rom image。还指定了BIOS的加载地址BaseAddress = 0xff300000
大小Size = 0xD00000,这与UEFITool给出的数据不谋而合:

虽然UEFITool没有给出Bios Rom image加载地址,但是根据BIOS Rom Image位于4G空间的顶部(0xFFFFFFFF),减去Rom Image Size就可以推得Bios Rom加载地址(就是Fdf文件中的BaseAddress=0xff300000)。

接下来形如下列内容:

Offset|Size
[RegionType]

则是在FD中开辟了一段连续空间,用来存放FV/FILE等内容。其中Offset和Size是这段空间的相对于整个FD的偏移和大小;前面UEFITool图中"BIOS Region"中列出的每一个FFS项都可以对应到Fdf文件[FD]节中RegionType为FV的项(毕竟只有FV才会用到文件系统FFS)。如果仔细比对,可能会发现UEFITool中倒数第二和倒数第三个FFS的Size相加正好对应[FD]节中的FV_FSP:

0xbf8000|0x68000
#FV_FSP
FILE = Build/Fsp_Rebased_M_T.fd

我认为造成这种现象的原因有2个:

首先,FV_FSP项的RegionType是FILE,可以包含任意内容,自然也可以包含其他的fd文件,就如NCB_LOGO项中包含了开机Logo文件;其次,fd文件又由fv组成,UEFITool又能解析fd文件,因此造成了这种FDF和BIOS Rom image不一致。其实,我们可以用UEFITool加载Fsp_Rebuild_M_T.fd,发现其中包含两个FFS:

如果FV_FSP包含的fd文件是经过压缩操作的,那么我敢大胆猜测,FDF文件中RegionType为FV的项将和UEFITool实际得到的相一致。题外话,如果哪天BIOS Rom够大,我是不是可以在Fdf中用RegionType FILE来插入一段电影?

回归正题,FDF文件[FD]节之后就是大量的[FV]节,FV的主要作用就是包含组件和模块。他们来填充[FD]节中开辟的空间。先来看一个相对简单的FV节:FV.NVRAM,它只包含一个include语句,指向NvramFdfFileStatement.txt文件:

!include AmiModulePkg/NVRAM/NvramFdfFileStatement.txt

NvramFdfFileStatement.txt文件通过FILE指令,包含binary file:

  FILE RAW = CEF5B9A3-476D-497f-9FDC-E98143E0422C {$(OUTPUT_DIRECTORY)/Nvram.bin}

前面我说过FV的主要作用就是填充[FD]节中开辟的空间,用这个FV节验证一下:

1.FDF中,FV.Nvram位于Bios Rom image的开头,Fv guid:FA4974FC-AF1D-4E5D-BDC5-DACD6D27BAEC,NvramFdfFileStatement.txt中指定的File Guid:CEF5B9A3-476D-497f-9FDC-E98143E0422C;用UEFITool加载Bios Rom image,第一个FFS(就是FV.Nvram)的Volume GUID: FA4974FC-AF1D-4E5D-BDC5-DACD6D27BAEC;再展开FFS,其中包含的唯一的文件的File Guid: CEF5B9A3-476D-497F-9FDC-E98143E0422C,这些值和FDF中的Fv Guid以及File Guid是一致的。

除了File Guid是一致的,用UEFITool解压后得到的Nvram.bin和原始的Nvram.bin的内容也是一致的:

以上是简单的FV的情况,有些复杂的FV中可以嵌套其他的FV,如FDF中的FV.FV_MAIN_WRAPPER,我们再来分析一下它:

[FV.FV_MAIN_WRAPPER]
BlockSize = 0x1000
NumBlocks = 0x8d0...
!include AmiPkg/Configuration/NestedFvMainFdfFileStatement.txt

FV.FV_MAIN_WRAPPER节中内容不多,仅仅含有若干条!include语句,但是FV.FV_MAIN_WRAPPER节占据BIOS Rom Image很大一块空间:BlockSize*NumBlocks=0x1000*0x8d0=0x8d0000。它占用如此多空间的原因是NestedFvMainFdfFileStatement.txt通过FILE指令包含了一块压缩的FV文件,而该FV文件是Dxe阶段的代码:

;NestedFvMainFdfFileStatement.txt的内容:
#Includes FVMAIN FV image
FILE FV_IMAGE = 9E21FD93-9C72-4c15-8C4B-E77F1DB2D792 $(FFS_FILE_CHECKSUM_KEYWORD) {SECTION $(PEI_COMPRESSION_SECTION) {SECTION FV_IMAGE = FV_MAIN
;SECTION FV_IMAGE = FV_MAIN类似指针语句,使FV_IMAGE指向FV_MAIN节,FV_MAIN节定义在platform.fdf中}
}
;FV_MAIN节内容:
[FV.FV_MAIN]
BlockSize = 0x1000
NumBlocks = 0x0
...
APRIORI DXE { ;APRIORI DXE,DXE阶段的APRIORI FILE...INF MdeModulePkg/Universal/PCD/Dxe/Pcd.infINF UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.inf...
}
...
INF MdeModulePkg/Core/Dxe/DxeMain.inf ;Dxe入口
INF AmiModulePkg/Bds/Bds.inf ;Bds入口
...

前面找到Dxe阶段的代码,那Sec和Pei阶段的代码在哪?platform.fdf文件[fd]节中设定FV_BB节位于Bios Rom image的尾部,因此,我们可以猜测并验证Sec模块和Pei模块也位于FV_BB中:

;FV_BB内容
[FV.FV_BB]
BlockSize = 0x1000
NumBlocks = 0xa0
...
APRIORI PEI { ;PEI阶段的APRIORI文件INF AmiModulePkg/AmiStatusCode/StatusCodePei.inf...
}
...
INF MdeModulePkg/Core/Pei/PeiMain.inf ;Pei阶段入口
...
INF UefiCpuPkg/SecCore/SecCore.inf ;Sec阶段入口
...
INF UefiCpuPkg/CpuIoPei/CpuIoPei.inf
INF MdeModulePkg/Universal/PcatSingleSegmentPciCfg2Pei/PcatSingleSegmentPciCfg2Pei.inf
INF AmiCompatibilityPkg/CmosManager/CmosManagerPei.inf

虽然,我们已经确定Sec模块和Pei模块位于FV_BB块中,但是有个问题随之出现:SecCore.inf并不是FV_BB中最后一个模块(夹在其他模块之间),所以一眼看去感觉开机时执行的第一条指令并不在SecCore模块中,这明显与EFI Spec相悖。更何况SecCore.inf含有ResetVector:

;SecCore.inf部分内容
[Defines]INF_VERSION                    = 0x00010005BASE_NAME                      = SecCoreMODULE_UNI_FILE                = SecCore.uniFILE_GUID                      = 1BA0062E-C779-4582-8566-336AE8F78F09MODULE_TYPE                    = SECVERSION_STRING                 = 1.0[Sources]SecMain.cSecMain.hFindPeiCore.cSecBist.c[Sources.IA32]Ia32/ResetVec.nasmb ;ResetVector

另外,UEFITool也明显显示SecCore位于Bios Rom image尾部:

fdf的设定和实际现象有差异,那一定是GenFv在生成Bios Rom image时有特殊处理。和GenFv Build Bios相关的只能查看Build.log,它记录了从源码到制成Rom Image的全过程,在Build.log的结尾,记录了各个模块在Bios Rom Image的排列位置,我发现了特殊的一处Firmware Volumon:08 No.049 类型是SECC----SecCore,属性被标记为VTF,Bios Rom Image中其他FV中任何模块都不具有该属性!:

+-----------------------------------------------------------------------------+
| Firmware Volume : 08               Location :  00C60000   Length :  0A0000  |
+---+----------------------------------------------+--------+------+-----+----+
|No |         FileName/GUID                        |Location| Size |Attr |Type|
+---+----------------------------------------------+--------+------+-----+----+
|   |FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF          |00C60048|00002C|     |FREE|
|000|1B45CC0A-156A-428A-AF62-49864DA0E6E6          |00C60078|0000FC|     |FRFM|
|001|5B85965C-455D-4CC6-9C4C-7F086967D2B0          |00C60178|00003C|     |FRFM|
|002|RomLayoutPei                                  |00C601B8|007C12|     |PEIM|
|003|PeiCore                                       |00C67DD0|00619E|     |PEIC|
...|048|8E295870-D377-4B75-BFDC-9AE2F6DBDE22          |00CB7780|00003C|     |FRFM|
|   |FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF          |00CB77C0|044668|     |FREE|
|049|1BA0062E-C779-4582-8566-336AE8F78F09          |00CFBE28|0041D8|VTF  |SECC|
+---+----------------------------------------------+--------+------+-----+----+

在PI spec Vol3中提到:

VTF是Volume Top File的缩写,PI spec规定VTF的File Guid为EFI_FFS_VOLUME_TOP_FILE_GUID(1BA0062E-C779-4582-8566-336AE8F78F09),必须位于firmware volume的最后一个字节。而SecCore.inf的FILE_GUID = 1BA0062E-C779-4582-8566-336AE8F78F09。看来只要某个inf指定自己FILE_GUID为EFI_FFS_VOLUME_TOP_FILE_GUID就有机会被安排在最开始执行。

回顾对[我所认识的BIOS]系列 -- CPU的第一条指令 一文扩充(I) 和 [我所认识的BIOS]系列 -- CPU的第一条指令 一文扩充(II)两篇文章,我们从开机第一条指令讲到了整个Bios Rom Image,后面我将再写一篇文章,简单说说如何从源码生成Bios Rom Image。

Reference:

BIOS/UEFI基础——FDF文件

深入UEFI内核

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

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

    我们可以在\Build\NT32IA32\DEBUG_VS2013\IA32\MdeModulePkg\Universal\BdsDxe\BdsDxe\OUTPUT\ 目录下,找到编译生成的 BdsD ...

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

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

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

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

  4. CPU加电后第一条指令

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

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

    自己动手写CPU(1)五级流水线及CPU第一条指令ori 动机 不知为何研一的自由时间突然多起来,可能人一闲下来就容易焦虑吧,hhhhhh.正好之前看到一本<自己动手写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. 自动驾驶故障诊断与容错控制技术研究
  2. HTML的标签描述6
  3. uniapp实现图片预览功能
  4. IP地址与网络上的其他系统有冲突
  5. Ubuntu18.04 root 登录
  6. 如何在ASP.NET Core程序启动时运行异步任务(2)
  7. 机器人军团【动态规划】
  8. java ArrayList的实现
  9. oracle迁移 rman,ORACLE RMAN迁移
  10. 20%3cx 30 的c语言表达式是,判断题(指令正误)
  11. windows10+ubuntu16.04双系统搭建
  12. python pip安装+easy_install
  13. uniapp安卓证书在线制作工具
  14. IDEA 解决一直加载Refreshing VCS history
  15. 2019-4给学员试讲内容分享
  16. 计算机一级win7win10,Win7/Win8.1升级Win10出现黑屏/蓝屏怎么办
  17. App测试-怎么测试启动时间?
  18. BaseWindowedBolt.java
  19. ContentProvider介绍
  20. 解决安卓手机点击有效,苹果手机点击事件无效的问题

热门文章

  1. 【Python】Python时间序列预测 | 经典季节性分解
  2. git仓库详细了解 一
  3. 云计算之路-试用Azure:拐弯抹角的负载均衡
  4. 说说SEO论坛百度快速排名优化,网站被K了怎么办,最好的SEO视频教程
  5. 创业小老板的感慨:经济危机下度日如年!
  6. 开源双模蓝牙协议栈 代码结构介绍
  7. windows网页设置为桌面背景
  8. 数值计算方法 | C语言实现几个数值计算方法(实验报告版)
  9. 无人机航测倾斜摄影测量批量建模软件-Mirauge3D 软件实用技巧
  10. Zillow“炒房”失败,算法神话破灭了吗?