天天看點

對[我所認識的BIOS]系列 -- CPU的第一條指令 一文擴充(II):從FDF到Bios Rom image

在"​​對[我所認識的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 = 0xd00

0x0|0x30000
FV = NVRAM

0x30000|0x30000
#RAW - NVRAM_BACKUP

0x60000|0x20000
#NCB_LOGO
FILE = BootLogo/DummyNcbLogo.bin

0x80000|0x8d0000
FV = FV_MAIN_WRAPPER

0x950000|0xC0000
FV = FV_DATA

0xa10000|0x100000
FV = FV_BB_AFTER_MEMORY

0xb10000|0xe8000
FV = FV_FSP_S

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

0xc60000|0xa0000
FV = FV_BB

#FV Section
[FV.FV_MAIN]
BlockSize = 0x1000
NumBlocks = 0x0
...
APRIORI DXE {
  INF AmiModulePkg/AmiStatusCode/StatusCodeDxe.inf
  INF 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給出的資料不謀而合:

對[我所認識的BIOS]系列 -- CPU的第一條指令 一文擴充(II):從FDF到Bios Rom image

雖然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:

對[我所認識的BIOS]系列 -- CPU的第一條指令 一文擴充(II):從FDF到Bios Rom image
對[我所認識的BIOS]系列 -- CPU的第一條指令 一文擴充(II):從FDF到Bios Rom image
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:

對[我所認識的BIOS]系列 -- CPU的第一條指令 一文擴充(II):從FDF到Bios Rom image

如果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是一緻的。

對[我所認識的BIOS]系列 -- CPU的第一條指令 一文擴充(II):從FDF到Bios Rom image
對[我所認識的BIOS]系列 -- CPU的第一條指令 一文擴充(II):從FDF到Bios Rom image

除了File Guid是一緻的,用UEFITool解壓後得到的Nvram.bin和原始的Nvram.bin的内容也是一緻的:

對[我所認識的BIOS]系列 -- CPU的第一條指令 一文擴充(II):從FDF到Bios Rom image

以上是簡單的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.inf
    INF 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                    = 0x00010005
  BASE_NAME                      = SecCore
  MODULE_UNI_FILE                = SecCore.uni
  FILE_GUID                      = 1BA0062E-C779-4582-8566-336AE8F78F09
  MODULE_TYPE                    = SEC
  VERSION_STRING                 = 1.0

[Sources]
  SecMain.c
  SecMain.h
  FindPeiCore.c
  SecBist.c

[Sources.IA32]
  Ia32/ResetVec.nasmb ;ResetVector      

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

對[我所認識的BIOS]系列 -- CPU的第一條指令 一文擴充(II):從FDF到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中提到:

對[我所認識的BIOS]系列 -- CPU的第一條指令 一文擴充(II):從FDF到Bios Rom image
對[我所認識的BIOS]系列 -- CPU的第一條指令 一文擴充(II):從FDF到Bios Rom image

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。