BIOS ROM Image分析


Flash芯片

之前Desktop的案子,BIOS 由一块32M的Nor Flash存储,还有一块Flash做PDR Region

主板上的NorFlash

生成的BIOS Bin

BIOS编译过程中会根据不同的xml file生成对应的BIOS bin文件,xml file可以用Intel提供的Fit.exe工具解析

FPT整刷的bin

Intel闪存映像布局

英特尔在相应芯片组的数据表页面上讨论了BIOS的结构。对于所有从第六系列开始的芯片组,此格式通常保持不变,该文件分为3-5个区域,可选的是GbE区域和PDR。

image-20210804112152776

UEFI Tool解析

用开源EFITool工具加载了编译生成的bin文件,AMI有自己的MMtool

可以发现Reset Vector位于BIOS Rom Image的最底部,探索形成这样的Image结构相关的各种文件(.fdf/.fd/.fv等文件)

组成结构:16M BIOS + 16M ME ,PDR Regin存在备用的PDR Flash上

BIOS Rom部分,由GenFv工具根据fdf文件生成,根据代码的定义的Flash Layout相关的Token Veb会自动生成fdf,前面的部分描述了Flahs Layout的布局,详细列出各个FV的起始地址和Size,edk2的话fdf则需要自己写

#FD Section
[FD.AMIROM]
BaseAddress = 0xff000000
Size = 0x1000000
ErasePolarity = 1
BlockSize = 0x1000
NumBlocks = 0x1000

DEFINE UNCOMPRESSED_PEI_EXECUTABLES_ALIGNMENT =  
DEFINE SEC_CORE_EXECUTABLE_ALIGNMENT = Align = 8
......

0x0|0x20000
FV = LNV_OEM_DATA_REGION

0x20000|0x50000
FV = NVRAM

0x70000|0x50000
#RAW - NVRAM_BACKUP

0xc0000|0x20000
FV = LEM_FV_DATA

0xe0000|0x870000
FV = FV_MAIN_WRAPPER

0x950000|0x20000
FV = FV_CMP

0x970000|0x10000
FV = FV_BCP

0x980000|0x110000
FV = FV_DATA_BACKUP

0xa90000|0x110000
FV = FV_DATA

0xba0000|0xa0000
FV = FV_BB_AFTER_MEMORY_BACKUP

0xc40000|0x90000
FV = FV_FSP_S_BACKUP

0xcd0000|0xa0000
FV = FV_BB_AFTER_MEMORY

0xd70000|0x90000
FV = FV_FSP_S

0xe00000|0x70000
#FV_FSP_BACKUP
FILE = Build/Fsp_Rebased_M_T.fd

0xe70000|0x90000
FV = FV_BB_BACKUP

0xf00000|0x70000
#FV_FSP
FILE = Build/Fsp_Rebased_M_T.fd

0xf70000|0x90000
FV = FV_BB

#FV Section
[FV.FV_MAIN]
BlockSize = 0x1000
NumBlocks = 0x0
FvAlignment = 16
MEMORY_MAPPED = TRUE

FvNameGuid = 5C60F367-A505-419A-859E-2A4FF6CA6FE5

APRIORI DXE {
	INF AmiModulePkg/AmiStatusCode/StatusCodeDxe.inf
	INF AmiModulePkg/AmiStatusCode/StatusCodeSmm.inf
	INF MdeModulePkg/Universal/PCD/Dxe/Pcd.inf
	......

!include AmiPkg/Configuration/FontFileStatement.txt
!include AmiModulePkg/NVRAM/FvMainDefaultsFdfFileStatement.txt
!include AmiChipsetModulePkg/LegacyRom/Vbios/VbiosFdfFileStatements.txt
......

#FV Section
[FV.FV_LOGOROMHOLE]
BlockSize = 0x1000
NumBlocks = 0x20
FvAlignment = 1
......
FvNameGuid = E54D9684-2735-43ef-A379-30F2F592BA10


!include AmiTsePkg/Core/em/AMITSE/Logoffs.txt

#FV Section
[FV.FV_MEFW_CAPSULE]
BlockSize = 0x1000
NumBlocks = 0x600
FvAlignment = 16
......
FvNameGuid = 9F8B1DEF-B62B-45F3-8282-BFD7EA19801B

INF RuleOverride = SubtypeGuidUncompressedNoChecksum Build/MeFwFid.inf
......

!include AmiModulePkg/Ofbd/Meud/AutoMeud/MeRegionFdfFileStatement.txt

#FV Section
[FV.LNV_OEM_DATA_REGION]
BlockSize = 0x1000
NumBlocks = 0x20
FvAlignment = 1
......


#FV Section
[FV.NVRAM]
BlockSize = 0x1000
NumBlocks = 0x50
FvAlignment = 16
......
FvNameGuid = FA4974FC-AF1D-4E5D-BDC5-DACD6D27BAEC


!include AmiModulePkg/NVRAM/NvramFdfFileStatement.txt

#FV Section
[FV.LEM_FV_DATA]
BlockSize = 0x1000
NumBlocks = 0x20
FvAlignment = 16
......
FvNameGuid = 4f1c52d3-d824-4d2a-a2f0-ec40c23c5916

INF RuleOverride = SubtypeGuidUncompressedNoChecksum 
......

!include AmiTsePkg/Core/em/AMITSE/Romhole.txt

#FV Section
[FV.FV_CMP]
BlockSize = 0x1000
NumBlocks = 0x20
FvAlignment = 16
......
FvNameGuid = aea406cf-4fc8-4dc3-9796-1ee03b64c715

!include LenovoModulePkg/Universal/ExtendedComputrace/AbsolutePkg/OEMSpecifiedAgentX64ProductionInstaller.txt

#FV Section
[FV.FV_BCP]
BlockSize = 0x1000
NumBlocks = 0x10
FvAlignment = 16
......
FvNameGuid = 8649FC2D-C0E6-4262-AD51-0CEABAB6429E

INF RuleOverride = Uncompressed PldmPkg/BiosConfigPreserve/BiosConfigPreserveBin/BCPFlashData/BCPFlashData.inf


#FV Section
[FV.FV_DATA_BACKUP]
BlockSize = 0x1000
NumBlocks = 0x110
FvAlignment = 16
......
FvNameGuid = E4A068F1-5EF1-4ACE-857C-7935F8A0C708


!include CrbPkg/AmiCrbIntelTopSwap/FvDataBackupFdfFileStatement.txt

#FV Section
[FV.FV_DATA]
BlockSize = 0x1000
NumBlocks = 0x110
FvAlignment = 16
......
FvNameGuid = AFDD39F1-19D7-4501-A730-CE5A27E1154B

INF RuleOverride = UncompressedBinaryNoChecksum AmiChipsetModulePkg/FIT/ReserveBinary/ReserveBootGuardSaveResigndata.inf

!include AmiChipsetModulePkg/FIT/FitTable/FitMicrocodeFdfFileStatement.txt

#FV Section
[FV.FV_BB_AFTER_MEMORY_BACKUP]
BlockSize = 0x1000
NumBlocks = 0xa0
FvAlignment = 16
......
FvNameGuid = 5b08a058-784f-4938-9a49-1588aa05f4b9

......

#FV Section
[FV.FV_FSP_S_BACKUP]
BlockSize = 0x1000
NumBlocks = 0x90
FvAlignment = 16


!include Intel/CometLakeFspBinPkg/FvFspSFdfFileStatement.txt

#FV Section
[FV.FV_BB_AFTER_MEMORY]
BlockSize = 0x1000
NumBlocks = 0xa0
FvAlignment = 16
......

#FV Section
[FV.FV_FSP_S]
BlockSize = 0x1000
NumBlocks = 0x90
FvAlignment = 16


!include Intel/CometLakeFspBinPkg/FvFspSFdfFileStatement.txt
......

#FV Section
[FV.FV_BB_BACKUP]
BlockSize = 0x1000
NumBlocks = 0x90
FvBaseAddress = 0xfff70000
......
FvNameGuid = 61C0F511-A691-4F54-974F-B9A42172CE53

APRIORI PEI {
	INF AmiModulePkg/AmiStatusCode/StatusCodePei.inf
	INF AmiModulePkg/IO/NCT6686D/NCT6686DPeiInit.inf
	......
	
!include Build/BiosGuardPubKeyhash_FdfFileStatement.txt
!include AmiChipsetModulePkg/FIT/FitTable/FitTableFdfFileStatement.txt
!include LenovoModulePkg/Universal/BuildNaming/BuildNaming.txt

#FV Section
[FV.FV_BB]
BlockSize = 0x1000
NumBlocks = 0x90
FvAlignment = 16
......
FvNameGuid = 61C0F511-A691-4F54-974F-B9A42172CE53

APRIORI PEI {
	INF AmiModulePkg/AmiStatusCode/StatusCodePei.inf
	INF AmiModulePkg/IO/NCT6686D/NCT6686DPeiInit.inf
	INF LenovoModulePkg/Universal/AssetID/AssetIDOnRecoveryMode/AssetIDOnRecoveryModePei.inf
	......

!include AmiChipsetModulePkg/BiosGuard/Binary/BiosGuardBinary_FdfFileStatement.txt
!include Intel/CometLakePlatSamplePkg/Binaries/Pct/PctGpioFdfFileStatements.txt
!include 
......

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

根据BIOS Rom Image位于4G空间的顶部(0xFFFFFFFF),减去Rom Image Size推算下可以推出Bios Rom加载地址(就是Fdf文件中的BaseAddress=0xff000000)

接下来形如下列内容,则是在FD中开辟了一段连续空间,用来存放FV/FILE等内容。

Offset|Size
[RegionType]

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

0xf00000|0x70000
#FV_FSP
FILE = Build/Fsp_Rebased_M_T.fd\

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实际得到的相一致。

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节验证一下:

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 Section
[FV.FV_MAIN_WRAPPER]
BlockSize = 0x1000
NumBlocks = 0x870
......
!include AmiPkg/Configuration/NestedFvMainFdfFileStatement.txt

FV.FV_MAIN_WRAPPER节中内容不多,仅仅含有若干条!include语句,但是FV.FV_MAIN_WRAPPER节占据BIOS Rom Image很大一块空间:BlockSizeNumBlocks=0x10000x870=0x870000。它占用如此多空间的原因是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
  }
}

FV_MAIN节内容如下:

#FV Section
[FV.FV_MAIN]
BlockSize = 0x1000
NumBlocks = 0x0
FvAlignment = 16
......
INF AmiModulePkg/RomLayout/RomLayoutDxe.inf
INF MdeModulePkg/Core/Dxe/DxeMain.inf
INF AmiModulePkg/Bds/Bds.inf

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

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

SecCore.inf部分内容

另外,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中任何模块都不具有该属性:

在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就有机会被安排在最开始执行。


文章作者: Holy Chen
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Holy Chen !
  目录