Now it is time to look at Firmware Volumes (FV
) and how they are described in the FDF file.
Firmware Volume
is one of the region types in the Flash Device Image (FD)
. If you declare some region as FV, you must provide its name <FVname>
and define a separate section [FV.<FVname>]
:
[FD.<FDname>]
...
0xXXXX|0xYYYY
FV = <FVname>
[FV.<FVname>]
...
Firmware volume is a region with a special formatting which is defined by the UEFI Platform Initialization (PI) specification (Volume 3: Shared Architectural Elements)
.
At the start of each FV is a special header EFI_FIRMWARE_VOLUME_HEADER
(https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Pi/PiFirmwareVolume.h):
EFI_FIRMWARE_VOLUME_HEADER
Summary:
Describes the features and layout of the firmware volume.
Prototype:
typedef struct {
UINT8 ZeroVector[16];
EFI_GUID FileSystemGuid;
UINT64 FvLength;
UINT32 Signature;
EFI_FVB_ATTRIBUTES_2 Attributes;
UINT16 HeaderLength;
UINT16 Checksum;
UINT16 ExtHeaderOffset;
UINT8 Reserved[1];
UINT8 Revision;
EFI_FV_BLOCK_MAP BlockMap[];
} EFI_FIRMWARE_VOLUME_HEADER
Parameters:
ZeroVector The first 16 bytes are reserved to allow for the reset vector of processors whose reset vector is at address 0
FileSystemGuid Declares the file system with which the firmware volume is formatted
FvLength Length in bytes of the complete firmware volume, including the header
Signature Set to {'_','F','V','H'}
Attributes Declares capabilities and power-on defaults for the firmware volume
HeaderLength Length in bytes of the complete firmware volume header
Checksum A 16-bit checksum of the firmware volume header. A valid header sums to zero
ExtHeaderOffset 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
Reserved In this version of the specification, this field must always be set to zero
Revision Set to 2. Future versions of this specification may define new header fields and will increment the Revision field accordingly
FvBlockMap[] An array of run-length encoded FvBlockMapEntry structures. The array is terminated with an entry of {0,0}
FvBlockMapEntry.NumBlocks The number of blocks in the run.
FvBlockMapEntry.BlockLength The length of each block in the run
Description:
A firmware volume based on a block device begins with a header that describes the features and layout of the firmware volume. This header includes a description of the capabilities, state, and block map of the device.
The rest of the data in the FV region is organized via files in a filesystem. The filesystem in this case is called a firmware file system (FFS)
. The FFS defines how files are stored in flash. The type of the FFS that is used is defined by the the header GUID field EFI_FIRMWARE_VOLUME_HEADER.FileSystemGuid
.
Currently the UEFI Platform Initialization (PI) specification defines two filesystems:
#define EFI_FIRMWARE_FILE_SYSTEM2_GUID \
{ 0x8c8ce578, 0x8a3d, 0x4f1c, { 0x99, 0x35, 0x89, 0x61, 0x85, 0xc3, 0x2d, 0xd3 } }
#define EFI_FIRMWARE_FILE_SYSTEM3_GUID \
{ 0x5473c07a, 0x3dcb, 0x4dca, { 0xbd, 0x6f, 0x1e, 0x96, 0x89, 0xe7, 0x34, 0x9a } }
The main difference between them is that EFI_FIRMWARE_FILE_SYSTEM3
supports files with a size >16MB
. For the rest of the article we would describe EFI_FIRMWARE_FILE_SYSTEM2
.
Each file in the filesystem would have a header EFI_FFS_FILE_HEADER
(https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Pi/PiFirmwareFile.h):
EFI_FFS_FILE_HEADER
Summary:
Each file begins with a header that describes the state and contents of the file. The header is 8-byte aligned with respect to the beginning of the firmware volume
Prototype:
typedef struct {
EFI_GUID Name;
EFI_FFS_INTEGRITY_CHECK IntegrityCheck;
EFI_FV_FILETYPE Type;
EFI_FFS_FILE_ATTRIBUTES Attributes;
UINT8 Size[3];
EFI_FFS_FILE_STATE State;
} EFI_FFS_FILE_HEADER;
Parameters:
Name 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
IntegrityCheck Used to verify the integrity of the file
Type Identifies the type of file
Attributes Declares various file attribute bits
Size The length of the file in bytes, including the FFS header
State Used to track the state of the file throughout the life of the file from creation to deletion
The data inside the file is formatted with respect to the EFI_FFS_FILE_HEADER.Type
field. Specification defines these file types:
Name | Value | Description |
---|---|---|
EFI_FV_FILETYPE_RAW | 0x01 | Binary data |
EFI_FV_FILETYPE_FREEFORM | 0x02 | Sectioned data |
EFI_FV_FILETYPE_SECURITY_CORE | 0x03 | Platform core code used during the SEC phase |
EFI_FV_FILETYPE_PEI_CORE | 0x04 | PEI Foundation |
EFI_FV_FILETYPE_DXE_CORE | 0x05 | DXE Foundation |
EFI_FV_FILETYPE_PEIM | 0x06 | PEI module (PEIM) |
EFI_FV_FILETYPE_DRIVER | 0x07 | DXE driver |
EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER | 0x08 | Combined PEIM/DXE driver |
EFI_FV_FILETYPE_APPLICATION | 0x09 | Application |
EFI_FV_FILETYPE_MM | 0x0A | Contains a PE32+ image that will be loaded into MMRAM in MM Traditional Mode |
EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE | 0x0B | Firmware volume image |
EFI_FV_FILETYPE_COMBINED_MM_DXE | 0x0C | Contains PE32+ image that will be dispatched by the DXE Dispatcher and will also be loaded into MMRAM in MM Tradition Mode |
EFI_FV_FILETYPE_MM_CORE | 0x0D | MM Foundation that support MM Traditional Mode |
EFI_FV_FILETYPE_MM_STANDALONE | 0x0E | Contains a PE32+ image that will be loaded into MMRAM in MM Standalone Mode |
EFI_FV_FILETYPE_MM_CORE_STANDALONE | 0x0F | MM Foundation that support MM Tradition Mode and MM Standalone Mode |
EFI_FV_FILETYPE_OEM_MIN…EFI_FV_FILETYPE_OEM_MAX | 0xC0-0xDF | OEM File Types |
EFI_FV_FILETYPE_DEBUG_MIN…EFI_FV_FILETYPE_DEBUG_MAX | 0xE0-0xEF | Debug/Test File Types |
EFI_FV_FILETYPE_FFS_MIN…EFI_FV_FILETYPE_FFS_MAX | 0xF0-0xFF | Firmware File System Specific File Types |
EFI_FV_FILETYPE_FFS_PAD | 0xF0 | Pad File For FFS |
Now let's try to create the most simple firmware volume which would contain one binary file. Here is code for this structure (UefiLessonsPkg/UefiLessonsPkg.fdf
):
[FD.SimpleImage]
BaseAddress = 0x0
Size = 0x1000
ErasePolarity = 1
0x100|0x500
FV = SimpleVolume
[FV.SimpleVolume]
FvAlignment = 16
FILE RAW = 15c658f6-eb5c-4b8f-b232-d6bd7368a73e {
$(WORKDIR)/hello.txt
}
Like in the FD case, we can set some characteristics of FV via predefined tokens.
In this example we have only one token setting FvAlignment = 16
, which is placed rigth after the [FV.SimpleVolume]
. It is the only mandatory token for the Firmware Volume
. We will talk about FV tokens later.
Next we define what goes into the FFS of the FV. Here we have one FILE of type RAW
, which means that the file type EFI_FFS_FILE_HEADER.Type
is equal to EFI_FV_FILETYPE_RAW
.
And specification defines this type like this:
EFI_FV_FILETYPE_RAW
The file type EFI_FV_FILETYPE_RAW denotes a file that does not contain sections and is treated as a raw data file
The GUID value 15c658f6-eb5c-4b8f-b232-d6bd7368a73e
I've generated via uuidgen
utility. It defines a unique name for our file in the FFS and will be written to the EFI_FFS_FILE_HEADER.Name
field.
Inside the brackets we define content for the file. In our case it is our hello.txt
generated via echo "hello!" > hello.txt
command.
Let's build and check our FD image:
$ hexdump Build/UefiLessonsPkg/RELEASE_GCC5/FV/SIMPLEIMAGE.fd -C
00000000 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
*
00000100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000110 78 e5 8c 8c 3d 8a 1c 4f 99 35 89 61 85 c3 2d d3 |x...=..O.5.a..-.|
00000120 00 05 00 00 00 00 00 00 5f 46 56 48 00 08 04 00 |........_FVH....|
00000130 48 00 cd e3 00 00 00 02 00 05 00 00 01 00 00 00 |H...............|
00000140 00 00 00 00 00 00 00 00 f6 58 c6 15 5c eb 8f 4b |.........X..\..K|
00000150 b2 32 d6 bd 73 68 a7 3e 5f aa 01 00 1f 00 00 f8 |.2..sh.>_.......|
00000160 68 65 6c 6c 6f 21 0a ff ff ff ff ff ff ff ff ff |hello!..........|
00000170 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
*
00000600
Besides FD, EDKII build system also generates images for Firmware Volumes. We've generated our FV with Offset|Size = 0x100|0x500
, therefore you can see how SIMPLEIMAGE.fd
has 0x100 bytes of 0xff's at the start of an image, and how SIMPLEVOLUME.Fv
starts right from its data.
$ hexdump Build/UefiLessonsPkg/RELEASE_GCC5/FV/SIMPLEVOLUME.Fv -C
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000010 78 e5 8c 8c 3d 8a 1c 4f 99 35 89 61 85 c3 2d d3 |x...=..O.5.a..-.|
00000020 00 05 00 00 00 00 00 00 5f 46 56 48 00 08 04 00 |........_FVH....|
00000030 48 00 cd e3 00 00 00 02 00 05 00 00 01 00 00 00 |H...............|
00000040 00 00 00 00 00 00 00 00 f6 58 c6 15 5c eb 8f 4b |.........X..\..K|
00000050 b2 32 d6 bd 73 68 a7 3e 5f aa 01 00 1f 00 00 f8 |.2..sh.>_.......|
00000060 68 65 6c 6c 6f 21 0a ff ff ff ff ff ff ff ff ff |hello!..........|
00000070 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
*
00000500
Firmware Volume data starts with a header. In our case:
typedef struct {
UINT8 ZeroVector[16]; = { 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 }
EFI_GUID FileSystemGuid; = { 0x8c8ce578, 0x8a3d, 0x4f1c, { 0x99, 0x35, 0x89, 0x61, 0x85, 0xc3, 0x2d, 0xd3 } } = EFI_FIRMWARE_FILE_SYSTEM2_GUID
UINT64 FvLength; = 0x0000000000000500
UINT32 Signature; = "_FVH"
EFI_FVB_ATTRIBUTES_2 Attributes; = 0x00040800 = (EFI_FVB2_ERASE_POLARITY | EFI_FVB2_ALIGNMENT_16)
UINT16 HeaderLength; = 0x0048
UINT16 Checksum; = 0xe3cd
UINT16 ExtHeaderOffset; = 0x0000
UINT8 Reserved[1]; = 0x00
UINT8 Revision; = 0x02
EFI_FV_BLOCK_MAP_ENTRY BlockMap[1]; = [{0x00000500, 0x00000001}, {0x00000000, 0x00000000}]
} EFI_FIRMWARE_VOLUME_HEADER;
Right after the EFI_FIRMWARE_VOLUME_HEADER
we have a header for our only file:
typedef struct {
EFI_GUID Name; = 15c658f6-eb5c-4b8f-b232-d6bd7368a73e
EFI_FFS_INTEGRITY_CHECK IntegrityCheck; = 0x5faa
EFI_FV_FILETYPE Type; = 0x01 (=EFI_FV_FILETYPE_RAW)
EFI_FFS_FILE_ATTRIBUTES Attributes; = 0x00
UINT8 Size[3]; = 0x00001f
EFI_FFS_FILE_STATE State; = 0xf8
} EFI_FFS_FILE_HEADER;
Right after that we have a content of our file hello.txt
:
$ hexdump hello.txt -C
00000000 68 65 6c 6c 6f 21 0a |hello!.|
0000000
The rest of the FV is filled with 0xff
.
There is an utility BaseTools/BinWrappers/PosixLike/VolInfo
that you can use to dump information about Firmware Volumes. Here is an example how we can use it to dump information about our Formware Volume:
$ VolInfo Build/UefiLessonsPkg/RELEASE_GCC5/FV/SIMPLEVOLUME.Fv -x Build/UefiLessonsPkg/RELEASE_GCC5/FV/Guid.xref
VolInfo Version 1.0 Build Developer Build based on Revision: Unknown
ParseGuidBaseNameFile: Build/UefiLessonsPkg/RELEASE_GCC5/FV/Guid.xref
Signature: _FVH (4856465F)
Attributes: 40800
EFI_FVB2_ERASE_POLARITY
EFI_FVB2_ALIGNMENT_16
Header Length: 0x00000048
File System ID: 8c8ce578-8a3d-4f1c-9935-896185c32dd3
Revision: 0x0002
Number of Blocks: 0x00000500
Block Length: 0x00000001
Total Volume Size: 0x00000500
============================================================
File Name: 15C658F6-EB5C-4B8F-B232-D6BD7368A73E /<...>/edk2/$(WORKDIR)/hello.txt
File Offset: 0x00000048
File Length: 0x0000001F
File Attributes: 0x00
File State: 0xF8
EFI_FILE_DATA_VALID
File Type: 0x01 EFI_FV_FILETYPE_RAW
There are a total of 1 files in this FV
As you can see this utility gives us the same information that we've parsed ourselves.
Let's add another file to our FFS. For a change let's initialize our next file with binary content:
$ echo -n -e \\xDE\\xAD\\xBE\\xEF > DEADBEEF.txt
$ hexdump DEADBEEF.txt -C
00000000 de ad be ef |....|
00000004
Now let's add it to our FFS:
[FD.SimpleImage]
BaseAddress = 0x0
Size = 0x1000
ErasePolarity = 1
0x100|0x500
FV = SimpleVolume
[FV.SimpleVolume]
FvAlignment = 16
FILE RAW = 15c658f6-eb5c-4b8f-b232-d6bd7368a73e {
$(WORKDIR)/hello.txt
}
FILE RAW = dd77425e-d338-43e7-8e94-1a755e0c217d {
$(WORKDIR)/DEADBEEF.txt
}
Build image and look at the Firmware Volume content:
$ hexdump /home/aladyshev/tiano/2021/edk2/Build/UefiLessonsPkg/RELEASE_GCC5/FV/SIMPLEVOLUME.Fv -C
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000010 78 e5 8c 8c 3d 8a 1c 4f 99 35 89 61 85 c3 2d d3 |x...=..O.5.a..-.|
00000020 00 05 00 00 00 00 00 00 5f 46 56 48 00 08 04 00 |........_FVH....|
00000030 48 00 cd e3 00 00 00 02 00 05 00 00 01 00 00 00 |H...............|
00000040 00 00 00 00 00 00 00 00 f6 58 c6 15 5c eb 8f 4b |.........X..\..K|
00000050 b2 32 d6 bd 73 68 a7 3e 5f aa 01 00 1f 00 00 f8 |.2..sh.>_.......|
00000060 68 65 6c 6c 6f 21 0a ff 5e 42 77 dd 38 d3 e7 43 |hello!..^Bw.8..C|
00000070 8e 94 1a 75 5e 0c 21 7d 01 aa 01 00 1c 00 00 f8 |...u^.!}........|
00000080 de ad be ef ff ff ff ff ff ff ff ff ff ff ff ff |................|
00000090 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
*
00000500
In this picture I've tried to provide visual parsing of data:
Here you can see how our files follow each other in the FFS. Each of the files has its own EFI_FFS_FILE_HEADER
with its unique Name (=GUID). The important thing to note that the filesystem is flat, files just follow one another. Therefore to find some file by GUID, we need to traverse FFS from the start.
Also here you can see that the padding byte 0xff was inserted between the files. It was inserted because according to the specification each file must start at 8 byte boundary.
We can use VolInfo
to see how it interprets our Firmware Volume:
$ VolInfo Build/UefiLessonsPkg/RELEASE_GCC5/FV/SIMPLEVOLUME.Fv -x Build/UefiLessonsPkg/RELEASE_GCC5/FV/Guid.xref
VolInfo Version 1.0 Build Developer Build based on Revision: Unknown
ParseGuidBaseNameFile: Build/UefiLessonsPkg/RELEASE_GCC5/FV/Guid.xref
Signature: _FVH (4856465F)
Attributes: 40800
EFI_FVB2_ERASE_POLARITY
EFI_FVB2_ALIGNMENT_16
Header Length: 0x00000048
File System ID: 8c8ce578-8a3d-4f1c-9935-896185c32dd3
Revision: 0x0002
Number of Blocks: 0x00000500
Block Length: 0x00000001
Total Volume Size: 0x00000500
============================================================
File Name: 15C658F6-EB5C-4B8F-B232-D6BD7368A73E /home/aladyshev/tiano/2021/edk2/$(WORKDIR)/hello.txt
File Offset: 0x00000048
File Length: 0x0000001F
File Attributes: 0x00
File State: 0xF8
EFI_FILE_DATA_VALID
File Type: 0x01 EFI_FV_FILETYPE_RAW
============================================================
File Name: DD77425E-D338-43E7-8E94-1A755E0C217D /home/aladyshev/tiano/2021/edk2/$(WORKDIR)/DEADBEEF.txt
File Offset: 0x00000068
File Length: 0x0000001C
File Attributes: 0x00
File State: 0xF8
EFI_FILE_DATA_VALID
File Type: 0x01 EFI_FV_FILETYPE_RAW
There are a total of 2 files in this FV
Indeed the FV contains 2 files of type EFI_FV_FILETYPE_RAW
.
Currently in our Firmware Volume
we've defined only one attribute FvAlignment
. Along with these attributes it help to set flags in the EFI_FIRMWARE_VOLUME_HEADER.Attributes
field.
FvAlignment = <...>
ERASE_POLARITY = 1|0
MEMORY_MAPPED = TRUE|FALSE
STICKY_WRITE = TRUE|FALSE
LOCK_CAP = TRUE|FALSE
LOCK_STATUS = TRUE|FALSE
WRITE_DISABLED_CAP = TRUE|FALSE
WRITE_ENABLED_CAP = TRUE|FALSE
WRITE_STATUS = TRUE|FALSE
WRITE_LOCK_CAP = TRUE|FALSE
WRITE_LOCK_STATUS = TRUE|FALSE
READ_DISABLED_CAP = TRUE|FALSE
READ_ENABLED_CAP = TRUE|FALSE
READ_STATUS = TRUE|FALSE
READ_LOCK_CAP = TRUE|FALSE
READ_LOCK_STATUS = TRUE|FALSE
The setting of these attributes will set respective EFI_FVB2_*
flags which are defined in the https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Pi/PiFirmwareVolume.h.
You can read the meaning of these flags in the UEFI Platform Initialization (PI) specification (Volume 3: Shared Architectural Elements)
.
If you look to VolInfo
output, you'll see that out FV has two attributes set:
EFI_FVB2_ERASE_POLARITY
(set by default, means that uninitialized data bits in volume are set to 1)EFI_FVB2_ALIGNMENT_16
(set by ourFvAlignment=16
setting)
Similar to FD, the FV has an attribute that defines an address at which flash volume would be mapped to the CPU memory:
FvBaseAddress = <...>
And the tokens that define flash block structure:
BlockSize = <...>
NumBlocks = <...>
Other possible attribute for the FV that you can come across is FvNameGuid
:
FvNameGuid = <GUID>
# Example:
# FvNameGuid = 763BED0D-DE9F-48F5-81F1-3E90E1B1A015
This attribute would lead to the creation of a file of type EFI_FV_FILETYPE_FFS_PAD
(padding file) with a GUID value in its data. This file would be placed first in the FV. We would investigate this file when we would talk about different file types.