Skip to content

Latest commit

 

History

History

Lesson_FDF

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 

When we build OVMF image the final result is a flash image.

Last messages in the build log provide some information about the image generation process:

$ build --platform=OvmfPkg/OvmfPkgX64.dsc --arch=X64 --buildtarget=RELEASE --tagname=GCC5

<...>

Fd File Name:OVMF (/<...>/edk2/Build/OvmfX64/RELEASE_GCC5/FV/OVMF.fd)

Generate Region at Offset 0x0
   Region Size = 0x40000
   Region Name = DATA

Generate Region at Offset 0x40000
   Region Size = 0x1000
   Region Name = None

Generate Region at Offset 0x41000
   Region Size = 0x1000
   Region Name = DATA

Generate Region at Offset 0x42000
   Region Size = 0x42000
   Region Name = None

Generate Region at Offset 0x84000
   Region Size = 0x348000
   Region Name = FV

Generating FVMAIN_COMPACT FV

Generating PEIFV FV
###
Generating DXEFV FV
########
Generate Region at Offset 0x3CC000
   Region Size = 0x34000
   Region Name = FV

Generating SECFV FV
#
Fd File Name:OVMF_VARS (/<...>/edk2/Build/OvmfX64/RELEASE_GCC5/FV/OVMF_VARS.fd)

Generate Region at Offset 0x0
   Region Size = 0x40000
   Region Name = DATA

Generate Region at Offset 0x40000
   Region Size = 0x1000
   Region Name = None

Generate Region at Offset 0x41000
   Region Size = 0x1000
   Region Name = DATA

Generate Region at Offset 0x42000
   Region Size = 0x42000
   Region Name = None

Fd File Name:OVMF_CODE (/<...>/edk2/Build/OvmfX64/RELEASE_GCC5/FV/OVMF_CODE.fd)

Generate Region at Offset 0x0
   Region Size = 0x348000
   Region Name = FV

Generate Region at Offset 0x348000
   Region Size = 0x34000
   Region Name = FV

Fd File Name:MEMFD (/<...>/edk2/Build/OvmfX64/RELEASE_GCC5/FV/MEMFD.fd)

Generate Region at Offset 0x0
   Region Size = 0x6000
   Region Name = None

Generate Region at Offset 0x6000
   Region Size = 0x1000
   Region Name = None

Generate Region at Offset 0x7000
   Region Size = 0x1000
   Region Name = None

Generate Region at Offset 0x8000
   Region Size = 0x1000
   Region Name = None

Generate Region at Offset 0x9000
   Region Size = 0x2000
   Region Name = None

Generate Region at Offset 0xB000
   Region Size = 0x1000
   Region Name = None

Generate Region at Offset 0xC000
   Region Size = 0x1000
   Region Name = None
Padding region starting from offset 0xD000, with size 0x3000

Generate Region at Offset 0xD000
   Region Size = 0x3000
   Region Name = None

Generate Region at Offset 0x10000
   Region Size = 0x10000
   Region Name = None

Generate Region at Offset 0x20000
   Region Size = 0xE0000
   Region Name = FV

Generate Region at Offset 0x100000
   Region Size = 0xC00000
   Region Name = FV

GUID cross reference file can be found at /<...>/edk2/Build/OvmfX64/RELEASE_GCC5/FV/Guid.xref

FV Space Information
SECFV [13%Full] 212992 (0x34000) total, 27904 (0x6d00) used, 185088 (0x2d300) free
PEIFV [11%Full] 917504 (0xe0000) total, 102824 (0x191a8) used, 814680 (0xc6e58) free
DXEFV [22%Full] 12582912 (0xc00000) total, 2879400 (0x2befa8) used, 9703512 (0x941058) free
FVMAIN_COMPACT [27%Full] 3440640 (0x348000) total, 935208 (0xe4528) used, 2505432 (0x263ad8) free

- Done -

This image process is initiated because the package DSC file OvmfPkg/OvmfPkgX64.dsc has FLASH_DEFINITION identifier defined:

[Defines]
  ...
  FLASH_DEFINITION               = OvmfPkg/OvmfPkgX64.fdf

The referenced FDF file should be formatted according to the EDK II Flash Description (FDF) File Specification. This file defines the flash images generated in the end of the build.

Each image is called Flash Device Image and is defined by the [FD.<name>] section.

For example OvmfPkg/OvmfPkgX64.fdf has 4 such sections:

[FD.OVMF]
[FD.OVMF_VARS]
[FD.OVMF_CODE]
[FD.MEMFD]

Each of these sections leads to the Flash Device Image generation:

$ find Build/OvmfX64/RELEASE_GCC5/FV/ -name "*.fd"
Build/OvmfX64/RELEASE_GCC5/FV/OVMF.fd
Build/OvmfX64/RELEASE_GCC5/FV/OVMF_CODE.fd
Build/OvmfX64/RELEASE_GCC5/FV/OVMF_VARS.fd
Build/OvmfX64/RELEASE_GCC5/FV/MEMFD.fd

You can see how they are generated in the log above:

$ build --platform=OvmfPkg/OvmfPkgX64.dsc --arch=X64 --buildtarget=RELEASE --tagname=GCC5

<...>

Fd File Name:OVMF (/<...>/edk2/Build/OvmfX64/RELEASE_GCC5/FV/OVMF.fd)

<...>

Fd File Name:OVMF_VARS (/<...>/edk2/Build/OvmfX64/RELEASE_GCC5/FV/OVMF_VARS.fd)

<...>

Fd File Name:OVMF_CODE (/<...>/edk2/Build/OvmfX64/RELEASE_GCC5/FV/OVMF_CODE.fd)

<...>

Fd File Name:MEMFD (/<...>/edk2/Build/OvmfX64/RELEASE_GCC5/FV/MEMFD.fd)

<...>

GUID cross reference file can be found at /<...>/edk2/Build/OvmfX64/RELEASE_GCC5/FV/Guid.xref

FV Space Information
SECFV [13%Full] 212992 (0x34000) total, 27904 (0x6d00) used, 185088 (0x2d300) free
PEIFV [11%Full] 917504 (0xe0000) total, 102824 (0x191a8) used, 814680 (0xc6e58) free
DXEFV [22%Full] 12582912 (0xc00000) total, 2879400 (0x2befa8) used, 9703512 (0x941058) free
FVMAIN_COMPACT [27%Full] 3440640 (0x348000) total, 935208 (0xe4528) used, 2505432 (0x263ad8) free

- Done -

Flash Device Image flash description tokens

The Flash Device Image is intendend for usage on a specific flash device, therefore the following tokens are mandatory for each FD section:

BaseAddress   = <...>
Size          = <...>
ErasePolarity = <...>
  • BaseAddress field defines an address at which flash image would be mapped to the CPU memory
  • Size field defines the size of the flash image
  • ErasePolarity field defines how to fill not used space in flash image (with 0 or 1)

Most often the following two tokens are also defined:

BlockSize = <...>
NumBlocks = <...>

These tokens define block structure of a flash chip. If they are present this rule should be satisfied:

BlockSize * NumBlocks = Size

Let's look at the values for these tokens in the OvmfPkg/OvmfPkgX64.fdf file. Most of the tokens there are defined via defines. For their definition we should look in the [Defines] section:

[Defines]
!include OvmfPkgDefines.fdf.inc

As you can see this section uses !include directive to abstract all defines in the separate file. If you look at this file, you'll see that it contains some if...endif logic depending on the value of FD_SIZE_IN_KB variable for various configurations. In our case OvmfPkg/OvmfPkgX64.dsc defines FD_SIZE_IN_KB as 4096:

[Defines]
  ...
  !ifdef $(FD_SIZE_1MB)
    DEFINE FD_SIZE_IN_KB           = 1024
  !else
  !ifdef $(FD_SIZE_2MB)
    DEFINE FD_SIZE_IN_KB           = 2048
  !else
  !ifdef $(FD_SIZE_4MB)
    DEFINE FD_SIZE_IN_KB           = 4096
  !else
    DEFINE FD_SIZE_IN_KB           = 4096
  !endif

Therefore we use the following defines from the OvmfPkg/OvmfPkgDefines.fdf.inc file:

DEFINE BLOCK_SIZE        = 0x1000

!if $(FD_SIZE_IN_KB) == 4096
DEFINE VARS_SIZE         = 0x84000
DEFINE VARS_BLOCKS       = 0x84
DEFINE VARS_LIVE_SIZE    = 0x40000
DEFINE VARS_SPARE_SIZE   = 0x42000

DEFINE FW_BASE_ADDRESS   = 0xFFC00000
DEFINE FW_SIZE           = 0x00400000
DEFINE FW_BLOCKS         = 0x400
DEFINE CODE_BASE_ADDRESS = 0xFFC84000
DEFINE CODE_SIZE         = 0x0037C000
DEFINE CODE_BLOCKS       = 0x37C
DEFINE FVMAIN_SIZE       = 0x00348000
DEFINE SECFV_OFFSET      = 0x003CC000
DEFINE SECFV_SIZE        = 0x34000
!endif

DEFINE MEMFD_BASE_ADDRESS = 0x800000

Now we can calculate the token values for each Flash Device Image in the OvmfPkg/OvmfPkgX64.fdf:

[FD.OVMF]
BaseAddress   = $(FW_BASE_ADDRESS)   # 0xFFC00000
Size          = $(FW_SIZE)           # 0x00400000
ErasePolarity = 1
BlockSize     = $(BLOCK_SIZE)        # 0x1000
NumBlocks     = $(FW_BLOCKS)         # 0x400

...

[FD.OVMF_VARS]
BaseAddress   = $(FW_BASE_ADDRESS)   # 0xFFC00000
Size          = $(VARS_SIZE)         # 0x84000
ErasePolarity = 1
BlockSize     = $(BLOCK_SIZE)        # 0x1000
NumBlocks     = $(VARS_BLOCKS)       # 0x84

...

[FD.OVMF_CODE]
BaseAddress   = $(CODE_BASE_ADDRESS) # 0xFFC84000
Size          = $(CODE_SIZE)         # 0x0037C000
ErasePolarity = 1
BlockSize     = $(BLOCK_SIZE)        # 0x1000
NumBlocks     = $(CODE_BLOCKS)       # 0x37C

...

[FD.MEMFD]
BaseAddress   = $(MEMFD_BASE_ADDRESS) # 0x800000
Size          = 0xD00000
ErasePolarity = 1
BlockSize     = 0x10000
NumBlocks     = 0xD0

Here you can see that BlockSize * NumBlocks = Size formula is correct for every FD.

And you can verify that size of the files matches the Size field values (although it is not mandatory as we will see next):

$ printf "%x\n" `stat -c "%s"  Build/OvmfX64/RELEASE_GCC5/FV/OVMF.fd`
400000
$ printf "%x\n" `stat -c "%s"  Build/OvmfX64/RELEASE_GCC5/FV/OVMF_CODE.fd`
37c000
$ printf "%x\n" `stat -c "%s"  Build/OvmfX64/RELEASE_GCC5/FV/OVMF_VARS.fd`
84000
$ printf "%x\n" `stat -c "%s"  Build/OvmfX64/RELEASE_GCC5/FV/MEMFD.fd`
d00000

Couple of words about the OVMF*.fd images itself. Remember what commands we've used to launch QEMU:

qemu-system-x86_64 \
  -drive if=pflash,format=raw,file=Build/OvmfX64/RELEASE_GCC5/FV/OVMF.fd \
  ...

qemu-system-x86_64 \
  -drive if=pflash,format=raw,readonly,file=Build/OvmfX64/RELEASE_GCC5/FV/OVMF_CODE.fd \
  -drive if=pflash,format=raw,file=Build/OvmfX64/RELEASE_GCC5/FV/OVMF_VARS.fd \
  ...

If you look closely to the OVMF_CODE.fd and OVMF_VARS.fd you could notice that

[FD.OVMF_VARS].BaseAddress + [FD.OVMF_VARS].Size = [FD.OVMF_CODE].BaseAddress
[FD.OVMF_CODE].BaseAddress + [FD.OVMF_CODE].Size = 0x100000000

[FD.OVMF].BaseAddress + [FD.OVMF].Size = 0x100000000

[FD.OVMF].BaseAddress == [FD.OVMF_VARS].BaseAddress

Simply speaking the [FD.OVMF] image is just the [FD.OVMF_VARS] image followed by the [FD.OVMF_CODE] image.

Regions

Flash Device Image consists of regions. It is necessary to have at least one region in a Flash Device Image.

The most simple definition of region is:

Offset|Size

For example:

0x500|0x600

This is a region that starts at offset 0x500 with a size of 0x600. It is important to note that the final Flash Device Image size is defined by its regions and not by the Size token value. The none of the regions can have addresses above the Size.

If we want to, we can assign PCDs to the region offset/size values:

Offset|Size
TokenSpaceGuidCName.PcdOffsetCName | TokenSpaceGuidCName.PcdSizeCName

For example:

0x500|0x600
gEfiMyTokenSpaceGuid.PcdFlashRegionBaseAddress | gEfiMyTokenSpaceGuid.PcdFlashRegionSize

This way the build system will automatically override the respective PCDs. Off course these PCDs gEfiMyTokenSpaceGuid.PcdFlashRegionBaseAddress and gEfiMyTokenSpaceGuid.PcdFlashRegionSize must be defined in the DEC file. The first <Offset> PCD would be overriden with a respect to FD.<...>].BaseAddress value, i.e. it would be assigned to the ([FD.<...>].BaseAddress + <Offset>) value.

They can be of types PcdsFixedAtBuild or PcdsPatchableInModule, but not dynamic!

Another thing that we would want to add to our region definition is a region type:

Offset|Size
TokenSpaceGuidCName.PcdOffsetCName | TokenSpaceGuidCName.PcdSizeCName
  RegionType

If RegionType is not present, it is considered None and edk2 doesn't touch this region data. It can be usefull, for example, if we define a space in flash reserved for logs. If RegionType is present, it can be one of the following types: FV, DATA, FILE, INF or CAPSULE.

Here are some explanation for the most common ones: FV, DATA, FILE:

FV region

The FV region type is a pointer to some Firmware Volume. Firmware Volume (FV) is a region with a filesystem inside. Formatting of this region is defined by the UEFI Platform Initialization (PI) specification (Volume 3: Shared Architectural Elements). We will look at the Firmware Volumes in the next lesson.

Example:

0x000000|0x0C0000
FV = FVMAIN

In this case FDF must define the section [FV.FVMAIN].

DATA region

In the case of a DATA region we immediately define the data inside the region via initialized array:

0x0|0x50
DATA = {
  0xDE, 0xAD, 0xBE, 0xEF
}

This would give us a region with such data:

00000000  de ad be ef ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
00000010  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
*
00000050

If you don't want to pollute FDF file, you can create some file and put data text into it:

0x0|0x50
DATA = {
  !include mydata.txt
}
$ cat mydata.txt
0xDE, 0xAD, 0xBE, 0xEF

FILE region

FILE region is a pointer to a binary file that will be loaded into the flash device.

For example create a simple file with text:

$ cat "hello!" > hello.txt

And define this region:

0x0|0x50
FILE = hello.txt

In the final flash image it would look like this:

00000000  68 65 6c 6c 6f 21 0a ff  ff ff ff ff ff ff ff ff  |hello!..........|
00000010  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
*
00000050

OVMF Flash Device Image regions

Let's look closely to the OVMF Flash Device Image regions.

Start with [FD.OVMF_VARS] Flash Device Image:

[FD.OVMF_VARS]
...
!include VarStore.fdf.inc

As you can see all of its regions are defined in the OvmfPkg/VarStore.fdf.inc file.

If we remember that FD_SIZE_IN_KB is equal to 4096 in our case we can simplify OvmfPkg/VarStore.fdf.inc content to this:

0x00000000|0x00040000 # NV_VARIABLE_STORE
DATA = {
  ...
}
0x00040000|0x00001000 # NV_EVENT_LOG
0x00041000|0x00001000 # NV_FTW_WORKING
DATA = {
  ...
}
0x00042000|0x00042000 # NV_FTW_SPARE

Here we have two empty regions, and two regions initialized with DATA arrays.

Now we can see how FDF content corresponds to build log output:

Fd File Name:OVMF_VARS (/<...>/edk2/Build/OvmfX64/RELEASE_GCC5/FV/OVMF_VARS.fd)

Generate Region at Offset 0x0
   Region Size = 0x40000
   Region Name = DATA

Generate Region at Offset 0x40000
   Region Size = 0x1000
   Region Name = None

Generate Region at Offset 0x41000
   Region Size = 0x1000
   Region Name = DATA

Generate Region at Offset 0x42000
   Region Size = 0x42000
   Region Name = None

The [FD.OVMF_CODE] flash device image consists of two Firmware Volumes (for the defines definition look at the OvmfPkg/OvmfPkgDefines.fdf.inc content):

[FD.OVMF_CODE]
...
0x00000000|$(FVMAIN_SIZE)        # 0x00000000|0x00348000
FV = FVMAIN_COMPACT

$(FVMAIN_SIZE)|$(SECFV_SIZE)     # 0x00348000|0x34000
FV = SECFV

And here is example of a build log output for this FD from the start of the lesson:

Fd File Name:OVMF_CODE (/<...>/edk2/Build/OvmfX64/RELEASE_GCC5/FV/OVMF_CODE.fd)

Generate Region at Offset 0x0
   Region Size = 0x348000
   Region Name = FV

Generate Region at Offset 0x348000
   Region Size = 0x34000
   Region Name = FV

Finally the [FD.OVMF] content:

[FD.OVMF]
...
!include VarStore.fdf.inc

$(VARS_SIZE)|$(FVMAIN_SIZE)
FV = FVMAIN_COMPACT

$(SECFV_OFFSET)|$(SECFV_SIZE)
FV = SECFV

Which means:

0x00000000|0x00040000 # NV_VARIABLE_STORE
DATA = {
  ...
}
0x00040000|0x00001000 # NV_EVENT_LOG
0x00041000|0x00001000 # NV_FTW_WORKING
DATA = {
  ...
}
0x00042000|0x00042000 # NV_FTW_SPARE

$(VARS_SIZE)|$(FVMAIN_SIZE)      # 0x84000 | 0x00348000
FV = FVMAIN_COMPACT

$(SECFV_OFFSET)|$(SECFV_SIZE)    # 0x003CC000 | 0x34000
FV = SECFV

Compare it with the build log for this FD:

Fd File Name:OVMF (/<...>/edk2/Build/OvmfX64/RELEASE_GCC5/FV/OVMF.fd)

Generate Region at Offset 0x0
   Region Size = 0x40000
   Region Name = DATA

Generate Region at Offset 0x40000
   Region Size = 0x1000
   Region Name = None

Generate Region at Offset 0x41000
   Region Size = 0x1000
   Region Name = DATA

Generate Region at Offset 0x42000
   Region Size = 0x42000
   Region Name = None

Generate Region at Offset 0x84000
   Region Size = 0x348000
   Region Name = FV

...

Generate Region at Offset 0x3CC000
   Region Size = 0x34000
   Region Name = FV

Creating UefiLessonsPkg.fdf

If you want to, you can experiment with FDF in our UefiLessonsPkg package.

For this add FLASH_DEFINITION define to the UefiLessonsPkg/UefiLessonsPkg.dsc file:

[Defines]
  ...
  FLASH_DEFINITION               = UefiLessonsPkg/UefiLessonsPkg.fdf

...

And create file UefiLessonsPkg/UefiLessonsPkg.fdf. For example fill it with this content:

[FD.SimpleImage]
BaseAddress   = 0x0
Size          = 0x1000
ErasePolarity = 1

0x00|0x50

Important notice: keep in mind that the FDF file must end with an empty string. In other case EDKII build would fail!

The FDF file above would produce the following FD as a build result:

Fd File Name:SIMPLEIMAGE (/<...>/edk2/Build/UefiLessonsPkg/RELEASE_GCC5/FV/SIMPLEIMAGE.fd)

Generate Region at Offset 0x0
   Region Size = 0x50
   Region Name = None

You can look at its content with a help of a hexdump utility:

$ hexdump /<...>/edk2/Build/UefiLessonsPkg/RELEASE_GCC5/FV/SIMPLEIMAGE.fd
0000000 ffff ffff ffff ffff ffff ffff ffff ffff
*
0000050

Padding regions

If regions would start not from the 0x0, the build system will automatically create a padding region. For example this config:

[FD.SimpleImage]
BaseAddress   = 0x0
Size          = 0x1000
ErasePolarity = 1

0x60|0x300

Would produce the following log:

Fd File Name:SIMPLEIMAGE (/<...>/edk2/Build/UefiLessonsPkg/RELEASE_GCC5/FV/SIMPLEIMAGE.fd)
Padding region starting from offset 0x0, with size 0x60

Generate Region at Offset 0x0
   Region Size = 0x60
   Region Name = None

Generate Region at Offset 0x60
   Region Size = 0x300
   Region Name = None

And hexdump output would look like this:

$ hexdump /<...>/edk2/Build/UefiLessonsPkg/RELEASE_GCC5/FV/SIMPLEIMAGE.fd
0000000 ffff ffff ffff ffff ffff ffff ffff ffff
*
0000360

The padding region is also created if there is an unused space between two regions. For example this config:

[FD.SimpleImage]
BaseAddress   = 0x0
Size          = 0x1000
ErasePolarity = 1
BlockSize = 0x100
NumBlocks = 0x10

0x0|0x50

0x80|0x40

would produce the following build log output:

Fd File Name:SIMPLEIMAGE (/<...>/edk2/Build/UefiLessonsPkg/RELEASE_GCC5/FV/SIMPLEIMAGE.fd)

Generate Region at Offset 0x0
   Region Size = 0x50
   Region Name = None
Padding region starting from offset 0x50, with size 0x30

Generate Region at Offset 0x50
   Region Size = 0x30
   Region Name = None

Generate Region at Offset 0x80
   Region Size = 0x40
   Region Name = None

And hexdump output would look like this:

$ hexdump /<...>/edk2/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  |................|
*
000000c0

Define PCDs from region offset and size

You can define PCDs in the DEC file UefiLessonsPkg/UefiLessonsPkg.dec:

[PcdsFixedAtBuild]
  ...
  gUefiLessonsPkgTokenSpaceGuid.Region1Offset|0|UINT32|0x00000005
  gUefiLessonsPkgTokenSpaceGuid.Region1Size|0|UINT32|0x00000006

And initialize them in FDF:

[FD.SimpleImage]
BaseAddress   = 0x0
Size          = 0x1000
ErasePolarity = 1

0x0|0x50
gUefiLessonsPkgTokenSpaceGuid.Region1Offset | gUefiLessonsPkgTokenSpaceGuid.Region1Size

For example if we add these PCDs to the UefiLessonsPkg/PCDLesson/PCDLesson.inf file:

[FixedPcd]
  ...
  gUefiLessonsPkgTokenSpaceGuid.Region1Offset
  gUefiLessonsPkgTokenSpaceGuid.Region1Size

The following defines would be generated in the Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/PCDLesson/PCDLesson/DEBUG/AutoGen.h file ready for the usage in your code:

#define _PCD_TOKEN_Region1Offset  0U
#define _PCD_SIZE_Region1Offset 4
#define _PCD_GET_MODE_SIZE_Region1Offset  _PCD_SIZE_Region1Offset
#define _PCD_VALUE_Region1Offset  0x00000000U                               <------------
extern const  UINT32  _gPcd_FixedAtBuild_Region1Offset;
#define _PCD_GET_MODE_32_Region1Offset  _gPcd_FixedAtBuild_Region1Offset
//#define _PCD_SET_MODE_32_Region1Offset  ASSERT(FALSE)  // It is not allowed to set value for a FIXED_AT_BUILD PCD

#define _PCD_TOKEN_Region1Size  0U
#define _PCD_SIZE_Region1Size 4
#define _PCD_GET_MODE_SIZE_Region1Size  _PCD_SIZE_Region1Size
#define _PCD_VALUE_Region1Size  0x00000050U                                 <------------
extern const  UINT32  _gPcd_FixedAtBuild_Region1Size;
#define _PCD_GET_MODE_32_Region1Size  _gPcd_FixedAtBuild_Region1Size
//#define _PCD_SET_MODE_32_Region1Size  ASSERT(FALSE)  // It is not allowed to set value for a FIXED_AT_BUILD PCD

Define DATA region

As a final example let's declare the region as DATA and initialize it:

[FD.SimpleImage]
BaseAddress   = 0x0
Size          = 0x1000
ErasePolarity = 1

0x0|0x50
DATA = {
  0xDE, 0xAD, 0xBE, 0xEF
}

This would give us the following content in the final image:

$ hexdump /<...>/edk2/Build/UefiLessonsPkg/RELEASE_GCC5/FV/SIMPLEIMAGE.fd -C
00000000  de ad be ef ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
00000010  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
*
00000050