BIOS/UEFI基础——EDK编译生成的二进制的结构

综述

EDK代码经过编译后,会得到如下的二进制:

  • xxx.Fd
  • xxx.Fv
  • xxx.efi

比如在编译OVMF的时候,会生成如下的文件:

BIOS/UEFI基础——EDK编译生成的二进制的结构_第1张图片

BIOS/UEFI基础——EDK编译生成的二进制的结构_第2张图片

它们的组成关系式:

  • FD包含FV
  • FV包含EFI

如下图所示:

BIOS/UEFI基础——EDK编译生成的二进制的结构_第3张图片

这个图来自:

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:

  1. Firmware Volume Header
  2. Firmware Volume Extended Header
  3. Firmware File Header
  4. Raw Section Header

它们分别在以下的两个文件中定义:

  • MdePkg\Include\Pi\PiFirmwareVolume.h
  • MdePkg\Include\Pi\PiFirmwareFile.h

具体的结构体会在下文介绍。

 

Firmware Volume 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为例:

BIOS/UEFI基础——EDK编译生成的二进制的结构_第4张图片

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的注释中也可以看出来。

 

Firmware Volume Extended Header

///
/// 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:

BIOS/UEFI基础——EDK编译生成的二进制的结构_第5张图片

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了。

 

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了:

BIOS/UEFI基础——EDK编译生成的二进制的结构_第6张图片

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也分为两代,一个是普通的,一个是扩展后的。

下面是普通的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。

 

efi格式

前面介绍的Firmware File只是一个Apriori,还没有涉及到efi模块,我们继续从FV中找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的实际大小:

BIOS/UEFI基础——EDK编译生成的二进制的结构_第7张图片

转化到十六进制是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文件二进制的头部:

BIOS/UEFI基础——EDK编译生成的二进制的结构_第8张图片

efi文件符合的是

《Microsoft Portable Executable and Common Object File Format Specification》(后称Spec),文档可以在https://msdn.microsoft.com/zh-cn/windows/hardware/gg463080.aspx下载到。

在这个文档中描述了二进制头部的格式如下:

BIOS/UEFI基础——EDK编译生成的二进制的结构_第9张图片

它的这种格式被称为Microsoft PE format

 

MS-DOS 2.0 Section

前面一大部分,即MS-DOS 2.0 Section并没有什么用,但是指明了两点:

1. Signature是“MZ”。

2. 0x3c位置是PE头的偏移。这里就是0x80:

 

COFF File Header(PE Header)

从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

前面已经提到了,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:

BIOS/UEFI基础——EDK编译生成的二进制的结构_第10张图片

位置是从0x98开始到0x187为止。

关于Optional Header,涉及到的成员太多了,就不一一介绍了,还是参考Spec。

 

Section Header

再往后就是Section Header了。

在PE Header中已经提到过,这个efi文件有三个Section,所以也就有三个Section Header:

BIOS/UEFI基础——EDK编译生成的二进制的结构_第11张图片

三个Section Header的名称分别是.text,.data和.reloc。

下面说明Section Header的成员:

BIOS/UEFI基础——EDK编译生成的二进制的结构_第12张图片

从0x240开始就是真正的代码了,这个值也在.text这个Section Header中指定了。

以上就是efi文件格式的介绍。

efi文件可以直接通过工具查看。

因为这里使用了VS2015编译的UEFI代码,因此可以通过VS2015自带的工具dumpbin来查看efi文件。

首先使用下面目录里的一个命令提示符(可以随意选):

BIOS/UEFI基础——EDK编译生成的二进制的结构_第13张图片

然后就进入了如下的界面:

BIOS/UEFI基础——EDK编译生成的二进制的结构_第14张图片

在这里可以查看dumpbin工具:

BIOS/UEFI基础——EDK编译生成的二进制的结构_第15张图片

然后进入到EDK编译目录下,就可以查看efi文件的信息了,比如:

BIOS/UEFI基础——EDK编译生成的二进制的结构_第16张图片

需要注意:

1. 不能使用普通的CMD,不然没有dumpbin命令;

2. 这里的信息跟前面的截图不能完成匹配,因为不是一个时间段写的,内容可能不一样了。

 

你可能感兴趣的:(UEFI开发基础)