主要内容:UEFI 在编译最后会生成一个可以烧进ROM里面的.fd文件。我们下面要了解的是FD是怎么存储的。一个FD(Flash Device binary image),它就是一个二进制镜像文件,其中包含多个FV文件。FV的存储格式是什么样子的?驱动、代码、数据是如何组织在一起,在代码中如何对FV进行发现和修改等操作?
- 固件FV存储的格式
- 对FV的访问
- PEI访问FV
- DXE访问FV
- code\data 存储模式
- EFI IMAGE的格式
- EFI OPTION ROM格式
- Variable格式
1. 固件存储的格式
FV是PI规范规定的标准的固件存储格式。
-
FD (Flash Device)
- 由FV组成
-
FV(Firmware Volume)
- 代码和数据的基本存储仓库
- FV是由固件文件系统(FFS(Firmware File System))组织的,并且每个FFS都有自己的属性。
- 每个Firmware Volume Image包括 Header 和 FFS Image, 还有Free Space。
-
FFS(Firmware File System)
- 一个FFS(firmware file system)描述在FV 上 Firmware Files 和 Free Space 是如何组织的
- file GUID 每个固件文件系统都有一个唯一的GUID,该GUID被固件用来将驱动程序与新发现的FV关联起来
- type 标识FFS的类型,在PEI\PEIM core进行分发时根据type进行查找和分发。
- EFI_FV_FILETYPE_RAW
- EFI_FV_FILETYPE_FREEFORM 按照section的方式存储数据比如ACPI table
- EFI_FV_FILETYPE_PEIM
- EFI_FV_FILETYPE_DRIVER
- EFI_FV_FILETYPE_APPLICATION
- EFI_FV_FILETYPE_SMM
- EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE
-
FS(Firmware Files)
- 固件卷中具体存储的code和data
- 属性:Name、Type、Alignment、Size
- 每个file也有各自的GUID且各不相同

-
section

- 存放具体的数据
- section是可以嵌套的
- type 标识section类型
- EFI_SECTION_PE32
- EFI_SECTION_DXE_DEPEX/EFI_SECTION_PEI_DEPEX/EFI_SECTION_SMM_DEPEX
- EFI_SECTION_USER_INTERFACE
- EFI_SECTION_RAW
- EFI_SECTION_FIRMWARE_VOLUME_IMAGE
每种类型的Section都存储特定的数据。比如PE32类型的Section存储的是代码。而RAW类型的Section则存储包括ACPI Tables这样的纯数据。



2. PEI访问FV
- FV被认为是线性地址区域
- 分层的实现FvPpi
- FvInfoPpi 详细说明了FV镜像的基地址和长度,传递给PEI。
- PEI中存在一个FvPpi服务,FvPpi有一套标准的服务来访问FV中的段数据。
- FvHob详细说明了FV镜像的基地址和长度,是PEI和DXE传递数据的方式。
3. DXE访问FV

DXE阶段拿到FV信息后会生成一个FVB 协议,在FVB之上生成一个FV 协议。
通过FVB基于块对flash进行读写,可以对variable进行操作。
- FV是基础的固件存储的抽象
- 它是只读的,直到读/写DXE驱动程序把它加载起来
- 作为分层协议实现
- Firmware Volume protocol 固件卷协议(固件设备格式摘要)
- Firmware Volume Block protocol 固件卷块协议(抽象固件设备硬件和分区)
- FFS (Firmware File System)
- Space-optimized binary flat format(空间优化的可执行文件格式)
为什么在DXE阶段引入FVB层的概念?
使得数据存储的介质可变,FV提供给上层类似于文件系统的协议,FVB则负责原始数据块写入。这样在更换了下层介质后,FV层不用进行修改,只需要做FVB的适配即可。
3. code/data 存储模式
目的:改数据和代码互不影响。
- EFI支持的文件系统分区
- FAT32。文件系统头包含一个GUID,它描述用于组织固件卷数据的固件文件系统的格式。
- 可插拔的文件系统抽象
- FV
- 存储位置和媒介是独立的。(存储的数据可以在其他的存储设备上)
- 代码和数据的位置不需要在构建时确定
- Dispatcher和BDS可以使用FV、File或network来查找所需的组件
- 可以根据平台需求改变封装
- 举例:对PCI设备驱动程序的加载可以通过OPTION ROM 网络等方式加载。对开机logo的加载存放在fv中,驱动进行访问加载和显示。
4. EFI IMAGE(PE/COFF)
UEFI Images是UEFI定义的、包含可执行代码的一类文件,最显著的特征是包含一个用来定义这段可执行代码格式的PE/COFF header,这个header定义了Processor Type和Image Type。UEFI OS Loader是一种特殊类型的Application。
一个UEFI Image是通过EFI Boot Service gBS->LoadImage()被加载(Load)并重定位(Relocation)到系统内存中的;通过gBS->StartImage()被调用。

- DOS Header
- DOS 签名
- COFF Header
- 机器类型: IA32 or X64 or ARM
- Optional Header
- Section alignment and file alignment(段对齐和文件对齐)
- Entry point relative address in PE image(入口的相对地址)
- Subsystem: EFI driver/EFI runtime driver/EFI application(子系统)
- Data Directory(数据目录)
- Debug directory to find the debug PDB file path in .rdata section(调试目录,在.rdata部分找到调试PDB文件路径)
- Relocation directory to find the relocation data in .reloc section(重定向目录,在.reloc节中找到重定向数据)在将efi加载到内存中,对绝对地址进行重定向。
- Section data
- .text: the binary instruction code
- .rdata: the read only data
- .data: the global data
- .rsrc: the resource section to include the additional data
- .reloc: info to reload PE image to the different address
5. EFI OPTIONROM
设备厂商发布设备的UEFI驱动有3种途径:
第一种:与平台BIOS厂商合作,将设备驱动以源码或者二进制的形式包含在UEFI固件中,与UEFI固件一同发布。
第二种:通过UEFI系统分区表(EFI System Partition,ESP)来发布。
第三种:通过PCI Option ROM,将UEFI驱动编译为PCI Option ROM,写入PCI/PCIE板卡设备的存储ROM中,UEFI BIOS将自动加载设备驱动。
Option ROM的特点和格式:
- option ROM是内置在device中的
- 在UEFI boot的时候被自动加载
- EFI Option ROM包含EFI 镜像,作为设备驱动程序
- ROM 头有0xEF1作为签名
- PCI数据结构包含了设备ID和供应商ID(哪些设备和哪些供应商是支持的)
- option rom镜像可能包含不止一个镜像,用来支持不同的平台架构
- 一般来说,独立的板卡,如网卡、还原卡、物理隔离卡等PCI/PCIE设备,都有自己的ROM芯片,Option Rom代码都存储在ROM芯片中。而板载设备会将Option Rom包在BIOS内以节省成本。
- UEFI Option ROM实在Legacy Option ROM上发展而来的,在UEFI规范中,它归类在PCI设备驱动中。UEFI Option ROM需要满足UEFI的规范。
- 在实际运行中,PCI总线驱动必须扫描所有的PCI设备的Option ROM,以判定是否加载。这项工作发生在爱PCI的枚举阶段,PCI总线驱动会寻找设备分类代码(CodeType)为3且标志位是0xEF1的Option ROM。然后检查EFI镜像头结构的子系统值,如果该值设置为11或12(表示启动时服务驱动或运行时服务驱动),那么PCI总线会把Option ROM加载到内存中,作为PCI设备驱动。

6. variable 存储格式
在UEFI架构下,Variable取代了Legacy下的CMOS的功能,用来存放UUID、Setup等的数据。一种数据存储的方式,储存在NVRAM(flash part)或是Memory里,可在Variable的Attribute里设定。
例如设置开机的启动项,设置开机密码等功能.这部分功能在标准的UEFI中都是使用Variable。

VARIABLE:
-
Variable Guid
-
Attribute
c/// /// Attributes of variable. /// #define EFI_VARIABLE_NON_VOLATILE 0x00000001 #define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x00000002 #define EFI_VARIABLE_RUNTIME_ACCESS 0x00000004 -
UnicodeNameString
-
VariableData
SERVICE & ATTRIBUTE:
Variable Service 是由Runtime Service Table所提供。


- Read Variable(PEI阶段是只读的)
- PEI: ReadOnlyVariable2Ppi
- DXE:VariableArchProtocol
- Set Variable(只能在DXE(比较后面的时间)和RT阶段修改)
- DXE VariableWriteArchProtocol is ready(在这个协议ready之后才可写,之前是只读的)
EFI_VARIABLE_NON_VOLATILE: Volatile variable doesn't require this attribute.EFI_VARIABLE_BOOTSERVICE_ACCESS: boot time only variable.EFI_VARIABLE_RUNTIME_ACCESS: OS runtime访问变量
- Variable Reclaim(变量回收)
- variable写在flash中时。由于flash介质的特性(修改数据需要先擦除再重写,而擦除需要擦除整个块),所以flash上修改数据会重新写数据然后把之前的标注为delete。relaim就是把有效的数据保存然后擦除flash,再把有效数据写回flash。在variable写的比较多的时候就可能会触发此操作。