Firmware层级结构
UEFI 常常以二进制文件的形式展现出来,这个二进制文件通过某些手段烧录/写入flash上,就成为了计算机启动的关键代码–固件。我们能够从各个计算机厂商的官网上找到相应型号对应的BIOS文件
面对这个二进制文件,很难不去思考的两个问题:
- 这个文件内部的结构是什么样的?
- UEFI代码中是以什么样的方式组织这个文件的?
PS:
1.后文的所有内容都是进行简单的初级介绍,目前还没有详细深入的研究过整个的image的生成过程,因此未能详尽之处有待后续补充了
2.后续文章中的翻译均为个人翻译,不保证准确性,请参考原文进行理解
3.后续文件中使用的PI Spec包含了两个版本,因为最新的PI Spec中存在一些链接不能跳转以及图片模糊的问题,因此部分使用了之前版本的PI Spec。如果需要当前文章中的Spec版本可去EDKII网站或者我公众号自己找
Firmware的层次结构
想要说清楚FV中的代码结构,首先先要厘清几个常用的概念,我们以PI Spec中(Volume3)的描述来对这些概念加以区分和说明
Firmware Device (FD)
Firmware Device 是一个包含了固件代码或者数据的永久的物理存储设备。典型的存储设备flash,也有些其他的永久存储设备。一个物理上的FD可以进行更小的划分形成逻辑上的FD,相似的,许多物理上的小FD也可以聚合形成逻辑上的一个大FD
从上述描述中我们可以知道与FD有关的几个重要信息:
- FD 原意想要表示的是物理上存储固件代码的设备,比如常见的flash等,其中保存的就是编译完成之后机器的完整UEFI固件。
如图,ROM中包含了某一机器的Firmware 文件,那么这个ROM就可被称之为Firmware Device。物理上,一个存储设备可以被称为一个FD,但是我们当前讨论的是Firmware文件的情况,此时一个完整的固件文件也可以被称之为FD。
2. 我们还可以将FD中的文件从逻辑上划分为更小的块,将这些更更小的块称为逻辑上的FD,这样FD就不仅仅用来指代一个完整的烧录到机器上的文件了,还可以代表文件的某一个层级 (经常用到)
3. 物理上的FD也可以被看成一个逻辑上的大FD(目前没见过这种情况)
通过上面的描述可以看出,无论是物理上还是逻辑上,FV一定是Firmware中最大的一个层级概念
Firmware Volume (FV)
Firmware Volume 是一种逻辑上的FD。在PI规范中,数据代码的基本存储单元就是FV。每一个FV以某一种file system的格式构成,因此file就是firmware 的基本单元
上述描述我个人觉得是非常抽象的,以我的理解用通俗的话来解释一下
- 每个FV是特定代码数据的聚合,每个FV都会实现自己特定的功能
- FV是FD的组成模块,若干个FV最终构成FD (若干个逻辑上的FD还可以组成一个大的FD 请理解好FV FD的区分)
- FV是由file组成的
可见,FV是仅次于FD的下一个文件层次,而FV之下的文件层级就是file了
Firmware File (FF)
这一段太长,翻译部分
Firmware file 是在FV中存储代码或数据的形式,每一个FF都包含了Name Type等属性。
某些特性的FV的存储还要求FF支持额外的属性。特定的FF中的文件数据还以标准的方式细分为了Firmware File Section
简言之,FF是FV的存储形式,FF中的数据还可以更加细分为Firmware File Section
Firmware File Section
Firmware File Section 是特定File中的独立的部分。每一个Section都包含了Type Size两个 属性。
section可以被大致的归为两个类别: Encapsulation sections & Leaf sections
关于上述的两个section的类别。PI Spec中有更加详细的区别的描述,想要详细了解的请自己阅读PI Spec。总的来说,就是Encapsulation section可能包含了其他section的section,而Leaf section 其中的内容直接就是data 不包含其他的section了,由此也说明了section是可以套用的。
综上我们已经介绍了四个层级的详细情况,这四个层级从大到小依次为 FD->FV->FF->File section
但是经常在很多的说明文章中还会出现一个FFS的概念,为什么我没有介绍这个概念呢? 因为我个人觉得首先这个概念的本意表示的并不是一个文件层级,而是一种组织形式;其次当前这个概念的使用基本上就是替代了FF,表示组成FV的文件,但是这个现象的具体原因并不清楚,也有可能是我理解的有问题。PI Spec中关于FFS的详细描述也介绍一下,具体是什么情况大家自己判断
Firmware File System (FFS)
FFS描述了FV中file和空闲空间的组织形式,每一种FFS都有一个独特的GUID,firmware利用这个GUID将新发现的FVB与driver相关联。
通过这段说明应该可以说明我之前的观点:FFS本质上是FV内文件的组织形式并非层级结构的一种。但是很多时候在很多地方往往会混用FFS 和 FF,也就是将FFS这个用来描述文件组织形式的词语代指 FV中的文件,这个现象在PI Spec中就能发现: 在Firmware File Format的格式中,结构体名称就是EFI_FFS_FILE_HEADER
明显此时是FFS与FF混用了。
个人建议可以理解成符合FFS的file文件 ,这样就能比较容易的理解这个说法,其实只要看到FFS能够理解到指代的是FV中包含的文件就可以了。
Firmware 层级的格式
接下来就来看这几种文件层级的格式是什么样的,同样这里以PI Spec中的描述为主要的内容,同时会结合对BIOS image的解析对照理解更加方便。同样这个位置也是大致的介绍
Firmware Volume Format
PI Architecture Firmware Volume Format描述了FV中的二进制布局。FV由header和紧跟在后面的FV data两部分组成。FV header 对应的结构体为EFI_FIRMWARE_VOLUME_HEADER。
FV data的组织形式由GUID来描述,有效的GUID值有两个EFI_FIRMWARE_FILE_SYSTEM2_GUID & EFI_FIRMWARE_FILE_SYSTEM3_GUID
上述一段话主要的内容可以总结为:
- 标准的FV format 由两个部分组成: FV header 和 FV data
- FV header中的GUID 用来形容FV中的data的组织形式 (Fv的下一层级即为FF,所以此处的组织形式描述的是FF之间的组织形式,即前述FFS)
- 有效的GUID有两个
EFI_FIRMWARE_FILE_SYSTEM2_GUID
&EFI_FIRMWARE_FILE_SYSTEM3_GUID
FileSystemGuid
EFI_FIRMWARE_VOLUME_HEADER
的结构如下
前述述第二点,也就是FFS Format ,描述的是同一个FV中FF的组织形式,也就是说FFS的组织形式由header中的GUID来表示。 关于这个部分详细可以参考PI Spec 2.2.2的描述
PI Firmware File System 是FV中文件的二进制布局 。他是一个平坦的文件系统,没有任何层次结构。所有的文件都直接存储在根路径下。文件是端到端存储的,没有任何目录描述存在哪些文件。想要解析FV获取当前存在的文件就需要将FV从头至尾进行遍历
所有以FFS形式存储的文件必须遵守 “PI Architecture Firmware File System Format” ,这个标准的文件头提供了几个层级的完整性检查,以防发生某种损坏。
FV header中有一个数据描述了(firmware)file system GUID。有两种类型的FFS:
EFI_FIRMWARE_FILE_SYSTEM2_GUID
&EFI_FIRMWARE_FILE_SYSTEM3_GUID
。小于16M的file文件使用EFI_FIRMWARE_FILE_SYSTEM2_GUID
表示其 组织形式,若存在大于16M的file文件则需要选择EFI_FIRMWARE_FILE_SYSTEM3_GUID
的文件组织形式。
两个GUID的具体值可以在PI Spec Vol 3 3.2.2 中看到
现在我们已经知道FV Header中是利用 FileSystemGuid
来表示不同的FFS的,
现在就可以找一个实际的UEFI 文件来对应看一下了。
上面是我从电脑厂商的官网随便dump的一个UEFI 文件然后用UEFITool进行解析的结果,可以看到这个文件的Volume就是存在了两种文件组织形式:FILE_SYSTEM2
和 FILE_SYSTEM3
。两种组织形式对应的GUID在右侧可以看到,可以和前述我们说的GUID完全对应起来,也就验证了我们前述内容的正确性
Signature
除了GUID之外,我们还可以关注一下结构体中的 Signature
可以看到 Spec中要求这个位置是强制设置为 _FVH
可以直接从二进制文件证实这一点如下
可以看到二进制文件中的每一个FV的header中都有一个_FVH
作为FV的标志
Firmware File Format
所有的FFS文件都以一个header开头,这个header 与 FV 的起始8 byte对齐。FFS包含了以下两个部分:header 和 Data
可以创建一个只包含header没有data的file ,使用24byte的空间,这种文件类型被称为 zero-length file
如果file包含data,data要紧跟着header。file内的data格式由header内的Type定义,file的data 格式有两种 EFI_FFS_FILE_HEADER or EFI_FFS_FILE_HEADER2。两种不同类型的header可以在PI Spec中查看
此处还要提醒一个点:前面我们说过很多情况下会用FFS来替代FF,这个位置就是一个很好的例子,PI Spec中直接说了FFS file, 但是明显此时想要形容的是FIrmware File的格式情况,是FFS与FF混用了。此处只是想进一步说明前面我说的混用情况。
两种FileHeader代码中的结构体表述如下
Name
需要注意的是,这个header中 Name
成员是一个GUID, 详细说明一个这个GUID的情况
GUID是file的名称,这个GUID作为唯一的标志用以区分file。 任何一个FV中名为某个GUID的文件只能有一个,除非这个file的type为
EFI_FV_FILETYPE_FFS_PAD
VTF
file Name中有一个特殊的名称,那就是 EFI_FFS_VOLUME_TOP_FILE_GUID
VTF文件必须位于FV的最后,这个文件的最后一个byte也是FV的最后一个byte。无论file是什么类型,VTF的Name GUID必须被定义为
EFI_FFS_VOLUME_TOP_FILE_GUID
查看UEFI 文件中的 VTF文件
可以看到当前这个VTF对应的Name GUID就是Spec中要求的GUID的值
Type
比较简单不翻译了。
PI Spec中规定的所有的File type类型如下
此处说一下 我们说的文件层次结构中的file文件相关的定义可以在 inf
文件中查看。以EDKII代码中定义VFT file为例,查看 SecCore.inf 文件可以看到该file的Name GUID 和 Type
由于只有EDKII的源代码,所以解析OVMF.fd为,同时对比代码中相应的file GUID 和 Type
Type为06,对应上面表格中06对应的正好就是PEIM类型
接下来的层级结构就是section了,这层没有详细的看就不详细介绍了,因为EDKII文件都是自动生成的,我平时能用到的最小的层级结构就是file – 用来生成Application
总结
PI Spec中有一副图能够很好的展示 Fireware File FV以下层级结构
而FD前面已经说过了就是FV按照一定的顺序组合起来的,组合顺序详细可以参考FDF文件。
另外还需要说明一点,就是section中是可以在包含FV的,也就是说这些结构是可以相互嵌套的,(但是只能section中包含FV,目前没有看到别的嵌套形式)
具体的例子可以参考这个结构