1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
|
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](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](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 |
# Create simple Firmware Volume
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:
```cpp
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:
```cpp
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`.
# VolInfo
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.
# Another file
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:
![FV](FV.png?raw=true)
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`.
# Firmware Volume attributes
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](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 our `FvAlignment=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.
# Links
- [\[FV\] Sections](https://edk2-docs.gitbook.io/edk-ii-fdf-specification/2_fdf_design_discussion/25_-fv-_sections)
- [Create the FV Image File(s)](https://edk2-docs.gitbook.io/edk-ii-build-specification/10_post-build_imagegen_stage_-_flash/104_create_the_fv_image_files)
|