EDK代码经过编译后,会得到如下的二进制:
比如在编译OVMF的时候,会生成如下的文件:
它们的组成关系式:
如下图所示:
这个图来自:
http://101.96.10.65/www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/fsp-architecture-spec-v2.pdf
FSP INFO HEADER可以不关注。
这里需要关注的就是几个Header:
它们分别在以下的两个文件中定义:
具体的结构体会在下文介绍。
///
/// Describes the features and layout of the firmware volume.
///
typedef struct {
///
/// The first 16 bytes are reserved to allow for the reset vector of
/// processors whose reset vector is at address 0.
///
UINT8 ZeroVector[16];
///
/// Declares the file system with which the firmware volume is formatted.
///
EFI_GUID FileSystemGuid;
///
/// Length in bytes of the complete firmware volume, including the header.
///
UINT64 FvLength;
///
/// Set to EFI_FVH_SIGNATURE
///
UINT32 Signature;
///
/// Declares capabilities and power-on defaults for the firmware volume.
///
EFI_FVB_ATTRIBUTES_2 Attributes;
///
/// Length in bytes of the complete firmware volume header.
///
UINT16 HeaderLength;
///
/// A 16-bit checksum of the firmware volume header. A valid header sums to zero.
///
UINT16 Checksum;
///
/// Offset, relative to the start of the header, of the extended header
/// (EFI_FIRMWARE_VOLUME_EXT_HEADER) or zero if there is no extended header.
///
UINT16 ExtHeaderOffset;
///
/// This field must always be set to zero.
///
UINT8 Reserved[1];
///
/// Set to 2. Future versions of this specification may define new header fields and will
/// increment the Revision field accordingly.
///
UINT8 Revision;
///
/// An array of run-length encoded FvBlockMapEntry structures. The array is
/// terminated with an entry of {0,0}.
///
EFI_FV_BLOCK_MAP_ENTRY BlockMap[1];
} EFI_FIRMWARE_VOLUME_HEADER;
以前面图中的DXEFV.Fv为例:
1. 首先是ZeroVector,它就是16个0x00,跟上图符合。
2. FileSystemGuid也是16个字节,在FDF文件中,每个FV都有一个FvNameGuid:
[FV.DXEFV]
FvNameGuid = 7CB8BDC9-F8EB-4F34-AAEA-3EE4AF6516A1
BlockSize = 0x10000
不过这里并不是使用这个,而是使用了如下的GUID:
## Include/Guid/FirmwareFileSystem2.h
gEfiFirmwareFileSystem2Guid = { 0x8c8ce578, 0x8a3d, 0x4f1c, { 0x99, 0x35, 0x89, 0x61, 0x85, 0xc3, 0x2d, 0xd3 }}
这说明了它是Firmware File System 2类型的,这也是当前的最新版本(EFI_FVH_REVISION的值是0x02)。
3. FvLength表示了FV的大小,这里是A00000h,就是10M,符合DXEFV.Fv的大小,不过在并入最后的FD的时候它是会被压缩的。注意它的长度是64位的。
4. Signature:对于Firmware Volume Header,使用的标识是“_FVH”。
5. Attributes:它有如下的取值:
//
// Attributes bit definitions
//
#define EFI_FVB2_READ_DISABLED_CAP 0x00000001
#define EFI_FVB2_READ_ENABLED_CAP 0x00000002
#define EFI_FVB2_READ_STATUS 0x00000004
#define EFI_FVB2_WRITE_DISABLED_CAP 0x00000008
#define EFI_FVB2_WRITE_ENABLED_CAP 0x00000010
#define EFI_FVB2_WRITE_STATUS 0x00000020
#define EFI_FVB2_LOCK_CAP 0x00000040
#define EFI_FVB2_LOCK_STATUS 0x00000080
#define EFI_FVB2_STICKY_WRITE 0x00000200
#define EFI_FVB2_MEMORY_MAPPED 0x00000400
#define EFI_FVB2_ERASE_POLARITY 0x00000800
#define EFI_FVB2_READ_LOCK_CAP 0x00001000
#define EFI_FVB2_READ_LOCK_STATUS 0x00002000
#define EFI_FVB2_WRITE_LOCK_CAP 0x00004000
#define EFI_FVB2_WRITE_LOCK_STATUS 0x00008000
#define EFI_FVB2_ALIGNMENT 0x001F0000
#define EFI_FVB2_ALIGNMENT_1 0x00000000
#define EFI_FVB2_ALIGNMENT_2 0x00010000
#define EFI_FVB2_ALIGNMENT_4 0x00020000
#define EFI_FVB2_ALIGNMENT_8 0x00030000
#define EFI_FVB2_ALIGNMENT_16 0x00040000
#define EFI_FVB2_ALIGNMENT_32 0x00050000
#define EFI_FVB2_ALIGNMENT_64 0x00060000
#define EFI_FVB2_ALIGNMENT_128 0x00070000
#define EFI_FVB2_ALIGNMENT_256 0x00080000
#define EFI_FVB2_ALIGNMENT_512 0x00090000
#define EFI_FVB2_ALIGNMENT_1K 0x000A0000
#define EFI_FVB2_ALIGNMENT_2K 0x000B0000
#define EFI_FVB2_ALIGNMENT_4K 0x000C0000
#define EFI_FVB2_ALIGNMENT_8K 0x000D0000
#define EFI_FVB2_ALIGNMENT_16K 0x000E0000
#define EFI_FVB2_ALIGNMENT_32K 0x000F0000
#define EFI_FVB2_ALIGNMENT_64K 0x00100000
#define EFI_FVB2_ALIGNMENT_128K 0x00110000
#define EFI_FVB2_ALIGNMENT_256K 0x00120000
#define EFI_FVB2_ALIGNMENT_512K 0x00130000
#define EFI_FVB2_ALIGNMENT_1M 0x00140000
#define EFI_FVB2_ALIGNMENT_2M 0x00150000
#define EFI_FVB2_ALIGNMENT_4M 0x00160000
#define EFI_FVB2_ALIGNMENT_8M 0x00170000
#define EFI_FVB2_ALIGNMENT_16M 0x00180000
#define EFI_FVB2_ALIGNMENT_32M 0x00190000
#define EFI_FVB2_ALIGNMENT_64M 0x001A0000
#define EFI_FVB2_ALIGNMENT_128M 0x001B0000
#define EFI_FVB2_ALIGNMENT_256M 0x001C0000
#define EFI_FVB2_ALIGNMENT_512M 0x001D0000
#define EFI_FVB2_ALIGNMENT_1G 0x001E0000
#define EFI_FVB2_ALIGNMENT_2G 0x001F0000
#define EFI_FVB2_WEAK_ALIGNMENT 0x80000000
这里的值是0x4FEFF,它是一个32位的值,最终就是如下的值:
#define EFI_FVB2_READ_DISABLED_CAP 0x00000001
#define EFI_FVB2_READ_ENABLED_CAP 0x00000002
#define EFI_FVB2_READ_STATUS 0x00000004
#define EFI_FVB2_WRITE_DISABLED_CAP 0x00000008
#define EFI_FVB2_WRITE_ENABLED_CAP 0x00000010
#define EFI_FVB2_WRITE_STATUS 0x00000020
#define EFI_FVB2_LOCK_CAP 0x00000040
#define EFI_FVB2_LOCK_STATUS 0x00000080
#define EFI_FVB2_STICKY_WRITE 0x00000200
#define EFI_FVB2_MEMORY_MAPPED 0x00000400
#define EFI_FVB2_ERASE_POLARITY 0x00000800
#define EFI_FVB2_READ_LOCK_CAP 0x00001000
#define EFI_FVB2_READ_LOCK_STATUS 0x00002000
#define EFI_FVB2_WRITE_LOCK_CAP 0x00004000
#define EFI_FVB2_WRITE_LOCK_STATUS 0x00008000
#define EFI_FVB2_ALIGNMENT_16 0x00040000
对应到fdf文件中FV这个Seciton的属性:
[FV.DXEFV]
FvNameGuid = 7CB8BDC9-F8EB-4F34-AAEA-3EE4AF6516A1
BlockSize = 0x10000
FvAlignment = 16
ERASE_POLARITY = 1
MEMORY_MAPPED = TRUE
STICKY_WRITE = TRUE
LOCK_CAP = TRUE
LOCK_STATUS = TRUE
WRITE_DISABLED_CAP = TRUE
WRITE_ENABLED_CAP = TRUE
WRITE_STATUS = TRUE
WRITE_LOCK_CAP = TRUE
WRITE_LOCK_STATUS = TRUE
READ_DISABLED_CAP = TRUE
READ_ENABLED_CAP = TRUE
READ_STATUS = TRUE
READ_LOCK_CAP = TRUE
READ_LOCK_STATUS = TRUE
6. HeaderLength大小为16位,这里的值是0x48,即72个字节:
7. Checksum就不多说了,也是16位大小。
8. ExtHeaderOffset表示的是Firmware Volume Extended Header的偏移。这里的值是0x60。
9. Reserved占一个字节。
10. Revision占一个字节,这里的值是0x02。
11. BlockMap是一个数组,不过只有一个成员,类型是EFI_FV_BLOCK_MAP_ENTRY:
typedef struct {
///
/// The number of sequential blocks which are of the same size.
///
UINT32 NumBlocks;
///
/// The size of the blocks.
///
UINT32 Length;
} EFI_FV_BLOCK_MAP_ENTRY;
这里的值分别是0xA0和0x10000,它们的乘积就是整个FV的大小了。
不过因为HeaderLength的值是0x48,所以后面还有8个0x00,也应该是这里的一部分,这从BlockMap的注释中也可以看出来。
///
/// Extension header pointed by ExtHeaderOffset of volume header.
///
typedef struct {
///
/// Firmware volume name.
///
EFI_GUID FvName;
///
/// Size of the rest of the extension header, including this structure.
///
UINT32 ExtHeaderSize;
} EFI_FIRMWARE_VOLUME_EXT_HEADER;
当然这不是全部,后面还可以跟一个个的ExtEntry,其结构如下:
///
/// Entry struture for describing FV extension header
///
typedef struct {
///
/// Size of this header extension.
///
UINT16 ExtEntrySize;
///
/// Type of the header.
///
UINT16 ExtEntryType;
} EFI_FIRMWARE_VOLUME_EXT_ENTRY;
这也还不是完全体,根据不同的ExtEntryType可以有不同的值,主要有以下两种类型:
#define EFI_FV_EXT_TYPE_OEM_TYPE 0x01
///
/// This extension header provides a mapping between a GUID and an OEM file type.
///
typedef struct {
///
/// Standard extension entry, with the type EFI_FV_EXT_TYPE_OEM_TYPE.
///
EFI_FIRMWARE_VOLUME_EXT_ENTRY Hdr;
///
/// A bit mask, one bit for each file type between 0xC0 (bit 0) and 0xDF (bit 31). If a bit
/// is '1', then the GUID entry exists in Types. If a bit is '0' then no GUID entry exists in Types.
///
UINT32 TypeMask;
///
/// An array of GUIDs, each GUID representing an OEM file type.
///
/// EFI_GUID Types[1];
///
} EFI_FIRMWARE_VOLUME_EXT_ENTRY_OEM_TYPE;
和
#define EFI_FV_EXT_TYPE_GUID_TYPE 0x0002
///
/// This extension header EFI_FIRMWARE_VOLUME_EXT_ENTRY_GUID_TYPE provides a vendor specific
/// GUID FormatType type which includes a length and a successive series of data bytes.
///
typedef struct {
///
/// Standard extension entry, with the type EFI_FV_EXT_TYPE_OEM_TYPE.
///
EFI_FIRMWARE_VOLUME_EXT_ENTRY Hdr;
///
/// Vendor-specific GUID.
///
EFI_GUID FormatType;
///
/// An arry of bytes of length Length.
///
/// UINT8 Data[1];
///
} EFI_FIRMWARE_VOLUME_EXT_ENTRY_GUID_TYPE;
这两个也是不定结构体,就是说下面还可以接东西。
还是以DXEFV.Fv为例,前面已经看到Firmware Volume Extended Header的起始位置是0x60:
1. 起始的FvName也是一个GUID,这个才是写在FDF中的GUID:
[FV.DXEFV]
FvNameGuid = 7CB8BDC9-F8EB-4F34-AAEA-3EE4AF6516A1
BlockSize = 0x10000
2. ExtHeaderSize的值是0x14,即20个字节,也就是说,包含一个GUID加这个参数刚好20个字节,后面没有跟ExtEntry。
之后的4个字节的FF是为了8字节对齐,再后面的部分应该是Firmware File的Header了。
这种Header有两种类型:
下面是一代Firmware File Header:
///
/// Each file begins with the header that describe the
/// contents and state of the files.
///
typedef struct {
///
/// This GUID is the file name. It is used to uniquely identify the file.
///
EFI_GUID Name;
///
/// Used to verify the integrity of the file.
///
EFI_FFS_INTEGRITY_CHECK IntegrityCheck;
///
/// Identifies the type of file.
///
EFI_FV_FILETYPE Type;
///
/// Declares various file attribute bits.
///
EFI_FFS_FILE_ATTRIBUTES Attributes;
///
/// The length of the file in bytes, including the FFS header.
///
UINT8 Size[3];
///
/// Used to track the state of the file throughout the life of the file from creation to deletion.
///
EFI_FFS_FILE_STATE State;
} EFI_FFS_FILE_HEADER;
下面是二代Firmware File Header:
typedef struct {
///
/// This GUID is the file name. It is used to uniquely identify the file. There may be only
/// one instance of a file with the file name GUID of Name in any given firmware
/// volume, except if the file type is EFI_FV_FILETYPE_FFS_PAD.
///
EFI_GUID Name;
///
/// Used to verify the integrity of the file.
///
EFI_FFS_INTEGRITY_CHECK IntegrityCheck;
///
/// Identifies the type of file.
///
EFI_FV_FILETYPE Type;
///
/// Declares various file attribute bits.
///
EFI_FFS_FILE_ATTRIBUTES Attributes;
///
/// The length of the file in bytes, including the FFS header.
/// The length of the file data is either (Size - sizeof(EFI_FFS_FILE_HEADER)). This calculation means a
/// zero-length file has a Size of 24 bytes, which is sizeof(EFI_FFS_FILE_HEADER).
/// Size is not required to be a multiple of 8 bytes. Given a file F, the next file header is
/// located at the next 8-byte aligned firmware volume offset following the last byte of the file F.
///
UINT8 Size[3];
///
/// Used to track the state of the file throughout the life of the file from creation to deletion.
///
EFI_FFS_FILE_STATE State;
///
/// If FFS_ATTRIB_LARGE_FILE is set in Attributes, then ExtendedSize exists and Size must be set to zero.
/// If FFS_ATTRIB_LARGE_FILE is not set then EFI_FFS_FILE_HEADER is used.
///
UINT64 ExtendedSize;
} EFI_FFS_FILE_HEADER2;
二代其实就是在一代的基础上做了扩充,所以二代也是一个不定结构体。
为了判断是哪种类型,提供了如下的宏来判断:
#define IS_FFS_FILE2(FfsFileHeaderPtr) \
(((((EFI_FFS_FILE_HEADER *) (UINTN) FfsFileHeaderPtr)->Attributes) & FFS_ATTRIB_LARGE_FILE) == FFS_ATTRIB_LARGE_FILE)
还是从DXEFV.Fv开始看,前面已经说到从0x78开始就是一个Firmware File了:
1. 最开始Name也是一个GUID:
## Include/Guid/Apriori.h
gAprioriGuid = { 0xFC510EE7, 0xFFDC, 0x11D4, { 0xBD, 0x41, 0x00, 0x80, 0xC7, 0x3C, 0x88, 0x81 }}
2. IntegrityCheck可以看成是一个Checksum,具体类型是:
///
/// Used to verify the integrity of the file.
///
typedef union {
struct {
///
/// The IntegrityCheck.Checksum.Header field is an 8-bit checksum of the file
/// header. The State and IntegrityCheck.Checksum.File fields are assumed
/// to be zero and the checksum is calculated such that the entire header sums to zero.
///
UINT8 Header;
///
/// If the FFS_ATTRIB_CHECKSUM (see definition below) bit of the Attributes
/// field is set to one, the IntegrityCheck.Checksum.File field is an 8-bit
/// checksum of the file data.
/// If the FFS_ATTRIB_CHECKSUM bit of the Attributes field is cleared to zero,
/// the IntegrityCheck.Checksum.File field must be initialized with a value of
/// 0xAA. The IntegrityCheck.Checksum.File field is valid any time the
/// EFI_FILE_DATA_VALID bit is set in the State field.
///
UINT8 File;
} Checksum;
///
/// This is the full 16 bits of the IntegrityCheck field.
///
UINT16 Checksum16;
} EFI_FFS_INTEGRITY_CHECK;
它占据两个字节。
3. Type占据一个字节,有以下的取值:
///
/// File Types Definitions
///
#define EFI_FV_FILETYPE_ALL 0x00
#define EFI_FV_FILETYPE_RAW 0x01
#define EFI_FV_FILETYPE_FREEFORM 0x02
#define EFI_FV_FILETYPE_SECURITY_CORE 0x03
#define EFI_FV_FILETYPE_PEI_CORE 0x04
#define EFI_FV_FILETYPE_DXE_CORE 0x05
#define EFI_FV_FILETYPE_PEIM 0x06
#define EFI_FV_FILETYPE_DRIVER 0x07
#define EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER 0x08
#define EFI_FV_FILETYPE_APPLICATION 0x09
#define EFI_FV_FILETYPE_SMM 0x0A
#define EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE 0x0B
#define EFI_FV_FILETYPE_COMBINED_SMM_DXE 0x0C
#define EFI_FV_FILETYPE_SMM_CORE 0x0D
#define EFI_FV_FILETYPE_OEM_MIN 0xc0
#define EFI_FV_FILETYPE_OEM_MAX 0xdf
#define EFI_FV_FILETYPE_DEBUG_MIN 0xe0
#define EFI_FV_FILETYPE_DEBUG_MAX 0xef
#define EFI_FV_FILETYPE_FFS_MIN 0xf0
#define EFI_FV_FILETYPE_FFS_MAX 0xff
#define EFI_FV_FILETYPE_FFS_PAD 0xf0
这里的取值是0x02,即FreeForm。
4. Attributes也占据一个字节,取值如下:
///
/// FFS File Attributes.
///
#define FFS_ATTRIB_LARGE_FILE 0x01
#define FFS_ATTRIB_FIXED 0x04
#define FFS_ATTRIB_DATA_ALIGNMENT 0x38
#define FFS_ATTRIB_CHECKSUM 0x40
这里的值是0x00,跟上面的都不一样,表示不带属性?
因为FFS_ATTRIB_LARGE_FILE是来表示这个Header是否是2代的(参考2代结构体的注释说明),目前来看这个Header不是2代。
5. Size占3个字节,这里的取值是0x4C,即76个字节。这就是整个Firmware File的大小,包括头部。
6. State占据1个字节,它的取值如下:
///
/// FFS File State Bits.
///
#define EFI_FILE_HEADER_CONSTRUCTION 0x01
#define EFI_FILE_HEADER_VALID 0x02
#define EFI_FILE_DATA_VALID 0x04
#define EFI_FILE_MARKED_FOR_UPDATE 0x08
#define EFI_FILE_DELETED 0x10
#define EFI_FILE_HEADER_INVALID 0x20
这里的值是0xF8,看上去不像是一个正常的Header。
查看它的数据,从0x78开始,总共有0x4C个数据,前面的是Header,所以这里从0x90开始到0xC4的数据是该Firmware File的数据:
那么这些数据是什么呢?将在"Raw Section Header"中介绍。
Raw Section Header也分为两代,一个是普通的,一个是扩展后的。
下面是普通的Raw Section Header:
///
/// Common section header.
///
typedef struct {
///
/// A 24-bit unsigned integer that contains the total size of the section in bytes,
/// including the EFI_COMMON_SECTION_HEADER.
///
UINT8 Size[3];
EFI_SECTION_TYPE Type;
///
/// Declares the section type.
///
} EFI_COMMON_SECTION_HEADER;
下面是扩展的Raw Section Header:
typedef struct {
///
/// A 24-bit unsigned integer that contains the total size of the section in bytes,
/// including the EFI_COMMON_SECTION_HEADER.
///
UINT8 Size[3];
EFI_SECTION_TYPE Type;
///
/// If Size is 0xFFFFFF, then ExtendedSize contains the size of the section. If
/// Size is not equal to 0xFFFFFF, then this field does not exist.
///
UINT32 ExtendedSize;
} EFI_COMMON_SECTION_HEADER2;
无论是普通还是扩展,都也可能不是全部,可能只是一个更大的Raw Section的一部分。
下面是几类Raw Section:
Compression Section:
///
/// An encapsulation section type in which the
/// section data is compressed.
///
typedef struct {
///
/// Usual common section header. CommonHeader.Type = EFI_SECTION_COMPRESSION.
///
EFI_COMMON_SECTION_HEADER CommonHeader;
///
/// The UINT32 that indicates the size of the section data after decompression.
///
UINT32 UncompressedLength;
///
/// Indicates which compression algorithm is used.
///
UINT8 CompressionType;
} EFI_COMPRESSION_SECTION;
typedef struct {
///
/// Usual common section header. CommonHeader.Type = EFI_SECTION_COMPRESSION.
///
EFI_COMMON_SECTION_HEADER2 CommonHeader;
///
/// UINT32 that indicates the size of the section data after decompression.
///
UINT32 UncompressedLength;
///
/// Indicates which compression algorithm is used.
///
UINT8 CompressionType;
} EFI_COMPRESSION_SECTION2;
Freeform Subtype GUID Section:
///
/// The leaf section which contains a single GUID.
///
typedef struct {
///
/// Common section header. CommonHeader.Type = EFI_SECTION_FREEFORM_SUBTYPE_GUID.
///
EFI_COMMON_SECTION_HEADER CommonHeader;
///
/// This GUID is defined by the creator of the file. It is a vendor-defined file type.
///
EFI_GUID SubTypeGuid;
} EFI_FREEFORM_SUBTYPE_GUID_SECTION;
typedef struct {
///
/// The common section header. CommonHeader.Type = EFI_SECTION_FREEFORM_SUBTYPE_GUID.
///
EFI_COMMON_SECTION_HEADER2 CommonHeader;
///
/// This GUID is defined by the creator of the file. It is a vendor-defined file type.
///
EFI_GUID SubTypeGuid;
} EFI_FREEFORM_SUBTYPE_GUID_SECTION2;
GUID Defined Section:
///
/// The leaf section which is encapsulation defined by specific GUID.
///
typedef struct {
///
/// The common section header. CommonHeader.Type = EFI_SECTION_GUID_DEFINED.
///
EFI_COMMON_SECTION_HEADER CommonHeader;
///
/// The GUID that defines the format of the data that follows. It is a vendor-defined section type.
///
EFI_GUID SectionDefinitionGuid;
///
/// Contains the offset in bytes from the beginning of the common header to the first byte of the data.
///
UINT16 DataOffset;
///
/// The bit field that declares some specific characteristics of the section contents.
///
UINT16 Attributes;
} EFI_GUID_DEFINED_SECTION;
typedef struct {
///
/// The common section header. CommonHeader.Type = EFI_SECTION_GUID_DEFINED.
///
EFI_COMMON_SECTION_HEADER2 CommonHeader;
///
/// The GUID that defines the format of the data that follows. It is a vendor-defined section type.
///
EFI_GUID SectionDefinitionGuid;
///
/// Contains the offset in bytes from the beginning of the common header to the first byte of the data.
///
UINT16 DataOffset;
///
/// The bit field that declares some specific characteristics of the section contents.
///
UINT16 Attributes;
} EFI_GUID_DEFINED_SECTION2;
User Interface Section:
///
/// The leaf section which contains a unicode string that
/// is human readable file name.
///
typedef struct {
EFI_COMMON_SECTION_HEADER CommonHeader;
///
/// Array of unicode string.
///
CHAR16 FileNameString[1];
} EFI_USER_INTERFACE_SECTION;
typedef struct {
EFI_COMMON_SECTION_HEADER2 CommonHeader;
CHAR16 FileNameString[1];
} EFI_USER_INTERFACE_SECTION2;
Version Section:
///
/// The leaf section which contains a numeric build number and
/// an optional unicode string that represents the file revision.
///
typedef struct {
EFI_COMMON_SECTION_HEADER CommonHeader;
UINT16 BuildNumber;
///
/// Array of unicode string.
///
CHAR16 VersionString[1];
} EFI_VERSION_SECTION;
typedef struct {
EFI_COMMON_SECTION_HEADER2 CommonHeader;
///
/// A UINT16 that represents a particular build. Subsequent builds have monotonically
/// increasing build numbers relative to earlier builds.
///
UINT16 BuildNumber;
CHAR16 VersionString[1];
} EFI_VERSION_SECTION2;
另外还有很多种的Raw Section,不过多是EFI_COMMON_SECTION_HEADER或EFI_COMMON_SECTION_HEADER2的别名,具体有哪些可以看下面:
typedef union {
EFI_COMMON_SECTION_HEADER *CommonHeader;
EFI_COMPRESSION_SECTION *CompressionSection;
EFI_GUID_DEFINED_SECTION *GuidDefinedSection;
EFI_PE32_SECTION *Pe32Section;
EFI_PIC_SECTION *PicSection;
EFI_TE_SECTION *TeSection;
EFI_PEI_DEPEX_SECTION *PeimHeaderSection;
EFI_DXE_DEPEX_SECTION *DependencySection;
EFI_VERSION_SECTION *VersionSection;
EFI_USER_INTERFACE_SECTION *UISection;
EFI_COMPATIBILITY16_SECTION *Code16Section;
EFI_FIRMWARE_VOLUME_IMAGE_SECTION *FVImageSection;
EFI_FREEFORM_SUBTYPE_GUID_SECTION *FreeformSubtypeSection;
EFI_RAW_SECTION *RawSection;
//
// For section whose size is equal or greater than 0x1000000
//
EFI_COMMON_SECTION_HEADER2 *CommonHeader2;
EFI_COMPRESSION_SECTION2 *CompressionSection2;
EFI_GUID_DEFINED_SECTION2 *GuidDefinedSection2;
EFI_PE32_SECTION2 *Pe32Section2;
EFI_PIC_SECTION2 *PicSection2;
EFI_TE_SECTION2 *TeSection2;
EFI_PEI_DEPEX_SECTION2 *PeimHeaderSection2;
EFI_DXE_DEPEX_SECTION2 *DependencySection2;
EFI_VERSION_SECTION2 *VersionSection2;
EFI_USER_INTERFACE_SECTION2 *UISection2;
EFI_COMPATIBILITY16_SECTION2 *Code16Section2;
EFI_FIRMWARE_VOLUME_IMAGE_SECTION2 *FVImageSection2;
EFI_FREEFORM_SUBTYPE_GUID_SECTION2 *FreeformSubtypeSection2;
EFI_RAW_SECTION2 *RawSection2;
} EFI_FILE_SECTION_POINTER;
接着上面提到的二进制:
1. Size占据3个字节,这里就是0x34,刚好到0xC4的位置,后面跟的4个FF是用来8字节对齐的。
2. Type占据1个字节,有以下的取值:
///
/// Leaf section Type values.
///
#define EFI_SECTION_PE32 0x10
#define EFI_SECTION_PIC 0x11
#define EFI_SECTION_TE 0x12
#define EFI_SECTION_DXE_DEPEX 0x13
#define EFI_SECTION_VERSION 0x14
#define EFI_SECTION_USER_INTERFACE 0x15
#define EFI_SECTION_COMPATIBILITY16 0x16
#define EFI_SECTION_FIRMWARE_VOLUME_IMAGE 0x17
#define EFI_SECTION_FREEFORM_SUBTYPE_GUID 0x18
#define EFI_SECTION_RAW 0x19
#define EFI_SECTION_PEI_DEPEX 0x1B
#define EFI_SECTION_SMM_DEPEX 0x1C
这里的值是0x19,表示的是RAW数据。
如下所示:
///
/// The leaf section which contains an array of zero or more bytes.
///
typedef EFI_COMMON_SECTION_HEADER EFI_RAW_SECTION;
typedef EFI_COMMON_SECTION_HEADER2 EFI_RAW_SECTION2;
RAW数据的Raw Seciton大小就是一个EFI_COMMON_SECTION_HEADER,而后者的大小就是4个字节,所以从0x94的就是真正的数据了。但是这些数据是什么呢?
从前面的Firmware File Header结构体成员FvName中我们可以知道它是一个Apriori,对应到fdf文件中就是如下的内容:
APRIORI DXE {
INF MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf
INF MdeModulePkg/Universal/PCD/Dxe/Pcd.inf
!if $(SMM_REQUIRE) == FALSE
INF OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
!endif
}
由于SMM_REQUIRE的值是FALSE,所以这里包含所有3个模块。
这3个模块的GUID分别是:
9B680FCE-AD6B-4F3A-B60B-F59899003443
80CF7257-87AB-47f9-A3FE-D50B76D89541
733cbac2-b23f-4b92-bc8e-fb01ce5907b7
正好和前面的内容一一对应,所以DXEFV.Fv中包含的第一个Firmware File就是Apriori,里面包含若干了GUID。
前面介绍的Firmware File只是一个Apriori,还没有涉及到efi模块,我们继续从FV中找efi。
对于DXEFV.Fv,我们继续往下找,理论上能够找到下面的efi模块:
INF MdeModulePkg/Core/Dxe/DxeMain.inf
这是定义在fdf文件中Apriori后续的第一个模块。
查看DXEFV.Fv的后续内容:
从0xC4到0xC8之间是填充0xFF来做8字节对齐,后面就是Firmware File Header。
它的第一个元素是Name,它是一个GUID,对应的值是:
D6A2CB7F-6A18-4e2f-B43B-9920A733700A
刚好和DXEFV.Fv的后续内容一致。
接着是IntegrityCheck,是一个16位的值,这里的值是0xAAB1,属于Checksum,不用特别关注。
接着是Type,它的值是0x05,表示的是EFI_FV_FILETYPE_DXE_CORE,也能够对应起来。
接着是Attributes,它的值是0x00,这个也没有特别的说明,不过至少说明该Firmware File Header不是扩展型的。
接着是Size,它的值是0x22D5E,包括头部。我们查看DxeCore.efi的实际大小:
转化到十六进制是0x22D20,加上Firmware File Header(大小是0x18)和Raw Section Header(大小是0x4,后面会说明),这跟计算的结果不匹配,这是为什么?后续在说明。
之后是Raw Section Header:
它的大小只有4个字节,表示的分别是大小(0x22D24,包含头部)和类型(EFI_SECTION_PE32)。
Raw Section Header中的大小加上Header本身的大小刚好0x22D24,所以真正的DxeCore.efi在FV中的位置是0xE4-0x22E03。
查考FV中的该位置:
前面也已经讲到,Firmware File Header里面的大小是0x22D5E,似乎还多了一些字节。
而从上图,我们看到了DxeCore等的字样,它应该也是Firmware File的一部分。
不过它们是在哪里定义的呢?具体是什么东西呢?可以认为Firmware File最后还有一个结束结构体,但是这个结构体是什么内容,目前没有找到具体的说明。
下面开始说明efi文件。以下是一个efi文件二进制的头部:
efi文件符合的是
《Microsoft Portable Executable and Common Object File Format Specification》(后称Spec),文档可以在https://msdn.microsoft.com/zh-cn/windows/hardware/gg463080.aspx下载到。
在这个文档中描述了二进制头部的格式如下:
它的这种格式被称为Microsoft PE format。
前面一大部分,即MS-DOS 2.0 Section并没有什么用,但是指明了两点:
1. Signature是“MZ”。
2. 0x3c位置是PE头的偏移。这里就是0x80:
从0x80开始首先是Signature “PE”,它其实是占了4个字节,所以应该是“PE\0\0”。
再之后是称为COFF File Header的部分。
这个头部的介绍如下(以0x80为基址计算):
0x04. Machine:这个程序对应的目标机器类型,占两个字节,比如这里的值是0x8664,表示的就是Intel X64的机器。具体的值可以参考Spec;
0x06. NumberOfSections:段的数量,占两个字节,这里的值是3,表示有三个段,分别是text、data和reloc,后面可以看到;
0x08. TimeDateStamp:占4个字节,表示的是从1970年1月1日0时开始的秒数,然而这里并没有使用;
0x0C. PointerToSymbolTable:占4个字节,指向System Table,这里的值是0,一般都不会用这个,因为已经被弃用。
0x10. NumberOfSysmbols:占4个字节,也不用了,所以值是0;
0x14. SizeOfOptionalHeader:占2个字节,Optional Header的大小,这里的值是0xF0;
0x16. Characteristics:占2个字节,属性值,具体可以参考Spec;
前面已经提到了,Optional Header的大小是0xF0,虽然没有某个成员特别说明存在Optional Header,但是既然大小都有了,存在也是肯定的了。
关于Optional Header,Spec中有如下的说明:
Every image file has an optional headerthat provides information to the loader. This header is optional in the sensethat some files (specifically, object files) do not have it.For image files,this header is required. An object file can have an optional header, butgenerally this header has no function in an object file except to increase its size.
也就是说是,对于我们的efi文件,肯定是有的。下面是具体的Header:
位置是从0x98开始到0x187为止。
关于Optional Header,涉及到的成员太多了,就不一一介绍了,还是参考Spec。
再往后就是Section Header了。
在PE Header中已经提到过,这个efi文件有三个Section,所以也就有三个Section Header:
三个Section Header的名称分别是.text,.data和.reloc。
下面说明Section Header的成员:
从0x240开始就是真正的代码了,这个值也在.text这个Section Header中指定了。
以上就是efi文件格式的介绍。
efi文件可以直接通过工具查看。
因为这里使用了VS2015编译的UEFI代码,因此可以通过VS2015自带的工具dumpbin来查看efi文件。
首先使用下面目录里的一个命令提示符(可以随意选):
然后就进入了如下的界面:
在这里可以查看dumpbin工具:
然后进入到EDK编译目录下,就可以查看efi文件的信息了,比如:
需要注意:
1. 不能使用普通的CMD,不然没有dumpbin命令;
2. 这里的信息跟前面的截图不能完成匹配,因为不是一个时间段写的,内容可能不一样了。