2. Transfer list
The TL must have a TL header which must be followed by one or more Transfer
Entries (TE). The whole TL must be contiguous in physical address space. The TL
header and all the TEs are 8-byte aligned (we use align8()
to denote this).
The TL header specifies the number of bytes occupied by the
TL. The TEs are defined in Section 2.3 and
Section 2.7. Each TE carries a header which contains an
identifier, tag_id
, that must be used to determine the content of the associated
TE. The TL header is located at tl_base_pa
. The tl_base_pa
is passed in the
register allocated for that handoff boundary (as specified in
Section 3). A
depiction of the TL is present in Fig. 2 , there the first TE in
the list (TE[0]) is shown to start at the end of the TL header
(tl_base_pa + 8
). The second TE in the list (TE[1]) starts at the next multiple
of 8, after the end of the TE[0].
Fig. 2 Transfer list example
2.1. Transfer list requirements
R1: The tl_base_pa
address must be non-zero and at an 8-byte aligned boundary.
R2: All fields defined in this specification must be stored in memory with little-endian byte order.
R3: The base address of a TE must be the 8-byte aligned address immediately after the end of the previous entry (or TL header, if the TE is the first entry on the TL).
R4: When relocating the TL, the offset from tl_base_pa
to the nearest alignment boundary specified by the alignment
field in the TL header must be preserved.
2.2. Transfer list header
A TL must begin with a TL header. The layout of the TL header is shown in
Table 1. The presence of a TL header may be verified by
inspecting the signature field which must contain the 0x4a0f_b10b
value. The
version field determines the contents of the handoff start header. The version
will only be changed, by an update to this specification, when new TL header or
TE header fields are defined (i.e. not when allocating new tag IDs), and all
changes will be backwards-compatible to older readers.
Field |
Size (bytes) |
Offset (bytes) |
Description |
---|---|---|---|
signature |
0x4 |
0x0 |
The value of signature must be |
checksum |
0x1 |
0x4 |
If enabled by the flags, the checksum is used to provide basic protection against something overwriting the TL in memory. The checksum is set to a value such that the xor over every byte in the { |
version |
0x1 |
0x5 |
The version of the TL header. This field must be set to |
hdr_size |
0x1 |
0x6 |
The size of this TL header in bytes. This field is set to |
alignment |
0x1 |
0x7 |
The maximum alignment required by any TE in the TL, specified as a power of two. For a newly created TL, the alignment requirement is |
used_size |
0x4 |
0x8 |
The number of bytes within the TL that are used by TEs. This field accounts for the size of the TL header plus the size of all the entries contained in the TL. It must be a multiple of |
total_size |
0x4 |
0xc |
The number of bytes occupied by the entire TL, including any spare space at the end, after |
flags |
0x4 |
0x10 |
Flags word. See below for contents. |
reserved |
0x4 |
0x14 |
Reserved word. Must be set to |
2.2.1. TL Flags
The TL flags word is intended to signal properties relating to the TL as a
whole. Future flag values may be added according to the rules of the version
field.
Bit |
Name |
Description |
---|---|---|
0 |
has_checksum |
A value of |
31:1 |
unused |
Reserved for future use. Must be |
2.3. TL entry header
All TEs must start with an entry header followed by a data section.
Note: the size of an entry (hdr_size + data_size) is not mandatorily an 8-byte multiple. When traversing the TL firmware must compute the next TE address following R3.
For example, assume the current TE is te
and its address is te_base_addr
. Using
C language notation, a derivation of the base address of the next TE
(next_base_addr) is the following:
next_base_addr = align8(te_base_addr + te.hdr_size + te.data_size)
The TE header is defined in Table 3.
Field |
Size (bytes) |
Offset (bytes) |
Description |
---|---|---|---|
tag_id |
0x3 |
0x0 |
The entry type identifier. |
hdr_size |
0x1 |
0x3 |
The size of this entry header in bytes. This field must be set to |
data_size |
0x4 |
0x4 |
The exact size of the data content in bytes, not including inter-TE padding. May be |
2.4. TL Contents
Tags are expected to have a simple layout (representable by a C structure) and each tag should only represent data for a single logical concept. Data for multiple distinct concepts should be split across different tags, even if they’re always expected to appear together on the first platform adding the tag (to encourage reusability in different situations). Alternatively, complex data may be represented in a different kind of well-established handoff data structure (e.g. FDT [DT], HOB [PI]) that is inserted into the TL as a single TE. Tag layouts (including the meaning of all fields) are considered stable after being added to this specification and must never be changed in a backwards-incompatible way. If a backwards-incompatible change is desired, a new tag ID should be allocated for the new version of the layout instead.
Tag layouts may be changed in a backwards-compatible manner by allowing new
valid values in existing fields (including reserved fields), as long as the
original layout definition clearly defined how unknown values in those fields
should be handled, and the rest of the TE would still be considered valid and
correct for older readers that consider the new values unknown. TE layouts may
also be expanded by adding new fields at the end, with the same restrictions.
TEs should not contain explicit version numbers and instead just use the
data_size
value to infer how many fields exist. TE layouts which have been
changed like this must clearly document which fields or valid values were added
at a later time, and in what order.
It is strongly recommended that entries of a given type are unique in the TL. If firmware designs require multiple TE instances of a given type, then that TE type definition should provide sufficient information for the TE consumer to disambiguate between all TE instances. That information can be, for example, a sub-type field or contained within a self-describing data blob.
The TL must not hold pointers or addresses within its entries, which refer to anything in the TL. These can make it difficult to relocate the TL. TL relocation typically happens in later phases of the boot when there is more memory available, which is needed for adding larger entries.
The TL may hold pointers or addresses which refer to regions outside the TL, if this is necessary. For example, the MMIO address of a device may be included in a TE. But in general, pointers and addresses should be avoided. Instead, the data structure itself should generally be contained within the TL. This approach provides the greatest flexibility for later boot stages to handle memory as they wish, since relocating the TL is fairly simple and self-contained, without needing to consider relocating other data structures strewn around the memory.
Where pointers or addresses are needed due to some project-specific restriction, a separate TE should generally be created for that purpose, rather than mixing pointers with other data. Of course there may be exceptions where two pointers belong together, or there is a pointer and a size which belong together. In any case, the PR should clearly document the need for these pointers.
2.5. Entry-type allocation
Tag IDs must be allocated in this specification before use. A new tag ID can be allocated by submitting a pull request to this repository that adds a description of the respective TE data layout to this specification. Tag IDs do not have to be allocated in order. Submitters are encouraged to try to group tag IDs together in logical clusters at 16 or 256-aligned boundaries (e.g. all tags related to a particular chipset or to a particular firmware project could use adjacent tag numbers), but there are no predefined ranges and no reservations of tag ranges for specific use.
The {0xff_f000, ..., 0xff_ffff}
range is reserved for non-standardized use.
Anyone is free to use tags from that range for any custom TE layout without
adding their definitions to this specification first. The use of this range is
strongly discouraged for anything other than local experiments or code that
will only ever be used in closed-source components owned by the entity
controlling the entire final firmware image. In particular, a creator of a TE
that just contains platform-specific data or internal structures specific to
a single firmware implementation, should allocate a standardized tag for it
in this specification – using the non-standardized range is strongly
discouraged. Since standards often emerge organically, the goal is to
create unique tag IDs for everything just in case it turns out to be useful in
more applications than initially anticipated. Basically, whenever you’re
submitting code for a new TE layout to any public open-source project, that’s
probably a good indication that you should allocate a tag ID for it in this
specification.
tag ID range |
Description |
---|---|
0x0 – 0x7f_ffff |
Standardized range. Any tag ID in this range must first be allocated in this specification before being used. The allocation of the tag ID requires the entry layout to be defined as well. |
0x80_0000 – 0xff_efff |
Reserved. (Can later be used to extend standardized range if necessary.) |
0xff_f000 – 0xff_ffff |
Non-standardized range. Tag IDs in this range may be used without allocation in this specification. This range should not be used for anything other than local experimentation or closed-source components that are entirely under the control of a single platform firmware integrator. Tags in this range are not tracked in this repository and PRs to add tag defintions for this range will not be accepted. |
2.6. Standard operations
This section describes the valid operations that may be performed on a TL in more detail, in order to clarify how to use the various fields and to serve as a guideline for implementation.
2.6.1. Validating a TL header
Inputs:
tl_base_addr
: Base address of the existing TL.
Compare
tl.signature
(tl_base_addr + 0x0
) to0x4a0f_b10b
. On a mismatch, abort (this is not a valid TL).Compare
tl.version
(tl_base_addr + 0x5
) to the expected version (currently0x1
). If there is an exact match, the TL is valid for all operations outlined in this section. Iftl.version
is larger, the TL is valid for reading but must not be modified or relocated. Iftl.version
is smaller, either abort or switch to code designed to interpret the respective previous version of this specification (note that the version number0x0
is illegal and processing should always abort if it is found).(optional) Check that
tl.used_size
(tl_base_addr + 0x8
) is smaller or equal totl.total_size
(tl_base_addr + 0xc
), and thattl.total_size
is smaller or equal to the size of the total area reserved for the TL (if known). If not, abort (TL is corrupted).(optional) If
has_checksum
, check that the xor oftl.used_size
bytes starting attl_base_addr
is 0x0. If not, abort (TL is corrupted).
2.6.2. Reading a TL
Inputs:
tl_base_addr
: Base address of the existing TL.
Calculate
te_base_addr
asalign8(tl_base_addr + tl.hdr_size)
. (Do not hardcode the value fortl.hdr_size
!)While
te_base_addr - tl_base_addr
is smaller or equal totl.used_size
:(optional) Check that
te_base_addr + te.hdr_size + te.data_size - tl_base_addr
is smaller or equal totl.used_size
, otherwise abort (the TL is corrupted).If
te.tag_id
(te_base_addr + 0x0
) is a known tag, interpret the data atte_base_addr + te.hdr_size
accordingly. (Do not hardcode the value forte.hdr_size
, even for known tags!) Otherwise, ignore the tag and proceed with the next step.Add
align8(te.hdr_size + te.data_size)
tote_base_addr
.
2.6.3. Adding a new TE
Inputs:
tl_base_addr
: Base address of the TL to add a TE to.new_tag_id
: ID number of the tag for the new TE.new_data_size
: Size in bytes of the data to be encapsulated in the TE.[data]: Data to be copied into the TE or generated on the fly.
(optional) Follow the steps in Reading a TL to look for a TE where
te.tag_id
is0x0
(XFERLIST_VOID) andte.data_size
is greater or equal tonew_data_size
. If found:Remember
te.data_size
asold_void_data_size
.Use the
te_base_addr
of this tag for the rest of the operation.If
has_checksum
, xor thealign8(new_data_size + 0x8)
bytes starting atte_base_addr
withtl.checksum
.Skip the next step (step 2) with all its substeps.
Calculate
te_base_addr
astl_base_addr + tl.used_size
.If
tl.total_size - tl.used_size
is smaller thanalign8(new_data_size + 0x8)
, abort (not enough room to add TE).If
has_checksum
, xor the 4 bytes fromtl_base_addr + 0x8
withtl_base_addr + 0xc
fromtl.checksum
.Add
align8(new_data_size + 0x8)
totl.used_size
.If
has_checksum
, xor the 4 bytes fromtl_base_addr + 0x8
totl_base_addr + 0xc
withtl.checksum
.
Set
te.tag_id
(te_base_addr + 0x0
) tonew_tag_id
.Set
te.hdr_size
(te_base_addr + 0x3
) to8
.Set
te.data_size
(te_base_addr + 0x4
) tonew_data_size
.Copy or generate the TE data into
te_base_addr + 0x8
.If
has_checksum
, xor thealign8(new_data_size + 0x8)
bytes starting atte_base_addr
withtl.checksum
.If an existing XFERLIST_VOID TE was chosen to be overwritten in step 1, and
old_void_data_size - new_data_size
is greater or equal to0x8
:Use
te_base_addr + align8(new_data_size + 0x8)
as the newte_base_addr
for a new XFERLIST_VOID tag.If
has_checksum
, xor the 8 bytes fromte_base_addr
tote_base_addr + 0x8
withtl.checksum
.Set
te.tag_id
(te_base_addr + 0x0
) to0x0
(XFERLIST_VOID).Set
te.hdr_size
(te_base_addr + 0x3
) to0x8
.Set
te.data_size
(te_base_addr + 0x4
) toold_void_data_size - align8(new_data_size) - 0x8
.If
has_checksum
, xor the 8 bytes fromte_base_addr
tote_base_addr + 0x8
withtl.checksum
.
2.6.4. Adding a new TE with special data alignment requirement
Inputs:
tl_base_addr
: Base address of the TL to add a TE to.new_tag_id
: ID number of the tag for the new TE.new_alignment
: The alignment boundary as a power of2
that the data must be aligned to.new_data_size
: Size in bytes of the data to be encapsulated in the TE.[data]: Data to be copied into the TE or generated on the fly.
Calculate
alignment_mask
as(1 << new_alignment) - 1
.If
(tl_base_addr + tl.used_size + 0x8) & alignment_mask
is not0x0
, follow the steps in Adding a new TE with the following inputs (bypass the option to overwrite an existing XFERLIST_VOID TE):tl_base_addr
remains the samenew_tag_id
is0x0
(XFERLIST_VOID)new_data_size
is(1 << new_alignment) - ((tl_base_addr + tl.used_size + 0x8) & alignment_mask) - 0x8
.No data (i.e. just don’t touch the bytes that form the data portion for this TE).
Follow the steps in Adding a new TE with the original inputs (again bypass the option to overwrite an existing XFERLIST_VOID TE).
If
new_alignment
is larger thantl.alignment
:If
has_checksum
, xortl.alignment
withtl.checksum
.Set
tl.alignment
tonew_alignment
.If
has_checksum
, xortl.alignment
withtl.checksum
.
2.6.5. Creating a TL
Inputs:
tl_base_addr
: Base address where to place the new TL.available_size
: Available size in bytes to reserve for the TL aftertl_base_addr
.
Check that
available_size
is larger than0x18
(the assumedtl.hdr_size
), otherwise abort.Set
tl.signature
(tl_base_addr + 0x0
) to0x4a0f_b10b
.Set
tl.checksum
(tl_base_addr + 0x4
) to0x0
(for now).Set
tl.version
(tl_base_addr + 0x5
) to0x1
.Set
tl.hdr_size
(tl_base_addr + 0x6
) to0x18
.Set
tl.alignment
(tl_base_addr + 0x7
) to0x3
.Set
tl.used_size
(tl_base_addr + 0x8
) to0x18
(the assumedtl.hdr_size
).Set
tl.total_size
(tl_base_addr + 0xc
) toavailable_size
.If checksums are to be used, set
tl.flags
(tl_base_addr + 0x10
) to1
, else0
. This is the value ofhas_checksum
.If
has_checksum
, calculate the checksum as the xor of all bytes fromtl_base_addr
totl_base_addr + tl.hdr_size
, and write the result totl.checksum
.
2.6.6. Relocating a TL
Inputs:
tl_base_addr
: Base address of the existing TL.target_base
: Base address of the target region to relocate into.target_size
: Size in bytes of the target region to relocate into.
Calculate
alignment_mask
as(1 << tl.alignment) - 1
.Calculate the current
alignment_offset
astl_base_addr & alignment_mask
.Calculate
new_tl_base
as(target_base & ~alignment_mask) + alignment_offset
.If
new_tl_base
is belowtarget_base
, addalignment_mask + 1
tonew_tl_base
.If
new_tl_base - target_base + tl.used_size
is larger thantarget_size
, abort (not enough space to relocate).Copy
tl.used_size
bytes fromtl_base_addr
tonew_tl_base
.If
has_checksum
, xor the the 4 bytes fromnew_tl_base + 0xc
tonew_tl_base + 0x10
withtl.checksum
(new_tl_base + 0x4
).Set
tl.total_size
(new_tl_base + 0xc
) totarget_size - (new_tl_base - target_base)
.If
has_checksum
, xor the 4 bytes fromnew_tl_base + 0xc
tonew_tl_base + 0x10
withtl.checksum
(new_tl_base + 0x4
).
2.7. Standard transfer entries
The following entry types are currently defined:
empty entry: tag_id =
0
(Section 2.7.1).fdt entry: tag_id =
1
(Section 2.7.2).single HOB block entry: tag_id =
2
(Section 2.7.3).HOB list entry: tag_id =
3
(Section 2.7.4).ACPI table aggregate entry: tag_id =
4
(Section 2.7.5).TPM event log entry: tag_id =
5
(Section 2.7.6).TPM CRB base entry: tag_id =
6
(Section 2.7.7).Entries related to Trusted Firmware (Section 2.7.8).
2.7.1. Empty entry layout (XFERLIST_VOID)
The empty or void entry should not contain any information to be consumed by any firmware stage. The intent of the void entry type is to remove information from the list without needing to relocate subsequent entries, or to create padding for entries that require a specific alignment. Void entries may be freely overwritten with new TEs, provided the resulting TL remains valid (i.e. a void entry can only be overwritten by a TE of equal or smaller size; if the size is more than 8 bytes smaller, a new void entry must be created behind the new TE to cover the remaining space up to the next TE).
Field |
Size (bytes) |
Offset (bytes) |
Description |
---|---|---|---|
tag_id |
0x3 |
0x0 |
The tag_id field must be set to |
hdr_size |
0x1 |
0x3 |
The size of this entry header in bytes must be set to |
data_size |
0x4 |
0x4 |
The size of the void space in bytes. May be |
void_data |
data_size |
hdr_size |
Void content |
2.7.2. FDT entry layout (XFERLIST_FDT)
The fdt is defined in [DT]. The FDT TE contains the fdt in the data section. The intent of the FDT entry is to carry the hardware description devicetree in the flattened devicetree (FDT) [DT] representation.
Field |
Size (bytes) |
Offset (bytes) |
Description |
---|---|---|---|
tag_id |
0x3 |
0x0 |
The tag_id field must be set to |
hdr_size |
0x1 |
0x3 |
The size of this entry header in bytes must be set to |
data_size |
0x4 |
0x4 |
The size of the FDT in bytes. |
fdt |
data_size |
hdr_size |
The fdt field contains the hardware description fdt. |
2.7.3. HOB block entry layout (XFERLIST_HOB_B)
The HOB is defined in [PI]. This entry type encapsulates a single HOB block. The intent of the HOB block entry is to hold a single HOB block. A complete HOB list can then be constructed, by a receiver, by obtaining all the HOB blocks in the TL and following the HOB list requirements defined in [PI].
Field |
Size (bytes) |
Offset (bytes) |
Description |
---|---|---|---|
tag_id |
0x3 |
0x0 |
The tag_id field must be set to |
hdr_size |
0x1 |
0x3 |
The size of this entry header in bytes must be set to |
data_size |
0x4 |
0x4 |
The size of the HOB block in bytes. |
hob_block |
data_size |
hdr_size |
Holds a single HOB block. |
2.7.4. HOB list entry layout (XFERLIST_HOB_L)
The HOB list is defined in [PI]. The HOB list starts with a PHIT block and can contain an arbitrary number of HOB blocks. This entry type encapsulates a complete HOB list. An enclosed HOB list must respect the HOB list constraints specified in [PI].
Field |
Size (bytes) |
Offset (bytes) |
Description |
---|---|---|---|
tag_id |
0x3 |
0x0 |
The tag_id field must be set to |
hdr_size |
0x1 |
0x3 |
The size of this entry header in bytes must be set to |
data_size |
0x4 |
0x4 |
The size of the HOB list in bytes. |
hob_list |
data_size |
hdr_size |
Holds a complete HOB list. |
2.7.5. ACPI table aggregate entry layout (XFERLIST_ACPI_AGGR)
This entry type holds one or more ACPI tables. The first table must start at
offset hdr_size
from the start of the entry. Since ACPI tables usually have an
alignment requirement larger than 8, writers may first need to create an
XFERLIST_VOID padding entry so that the subsequent te_base_addr + te.hdr_size
will be correctly aligned. Any subsequent ACPI tables must be located at the
next 16-byte alligned address following the preceding ACPI table. Note that each
ACPI table has a Length
field in the ACPI table header [ACPI], which must be
used to determine the end of the ACPI table. The data_size
value must be set
such that the last ACPI table in this entry ends at offset
hdr_size + data_size
from the start of the entry.
Field |
Size (bytes) |
Offset (bytes) |
Description |
---|---|---|---|
tag_id |
0x3 |
0x0 |
The tag_id field must be set to |
hdr_size |
0x1 |
0x3 |
The size of this entry header in bytes must be set to |
data_size |
0x4 |
0x4 |
The size of all included ACPI tables + padding in bytes. |
acpi_tables |
data_size |
hdr_size |
One or more ACPI tables. |
2.7.6. TPM event log table entry layout (XFERLIST_EVLOG)
This entry type holds TPM-related information for a platform. The TPM event log info is a region containing a TPM event log as defined by TCG EFI Protocol Specification [TCG_EFI].
Field |
Size (bytes) |
Offset (bytes) |
Description |
---|---|---|---|
tag_id |
0x3 |
0x0 |
The tag_id field must be set to |
hdr_size |
0x1 |
0x3 |
The size of this entry header in bytes must be set to |
data_size |
0x4 |
0x4 |
The size of the event log in bytes + sizeof(flags) i.e. |
flags |
0x4 |
hdr_size |
flags are intended to signal properties of this TE. Bit 0 is
need_to_replay flag. Some firmware components may compute measurements
to be extended into a TPM and add them to the TPM event log, but those
components are unable to access the TPM themselves. In this case, the
component should set the “need_to_replay” flag so that the next
component in the boot chain is aware that the PCRs have not been
extended. A component with access to the TPM would replay the event log
by reading each measurement recorded and extending it into the TPM. Once
the measurements are extended into the TPM, then the “need_to_replay”
flag must be cleared if the transfer list is passed to additional
firmware components. Default value is |
event_log |
data_size - 0x4 |
hdr_size + 0x4 |
Holds a complete event log. |
2.7.7. TPM CRB base address table entry layout (XFERLIST_TPM_CRB_BASE)
The CRB info defines the address of a region of memory that has been carved out and reserved for use as a TPM Command Response Buffer interface.
Field |
Size (bytes) |
Offset (bytes) |
Description |
---|---|---|---|
tag_id |
0x3 |
0x0 |
The tag_id field must be set to |
hdr_size |
0x1 |
0x3 |
The size of this entry header in bytes must be set to |
data_size |
0x4 |
0x4 |
This value should be set to |
crb_base_address |
0x8 |
hdr_size |
The physical base address of a region of memory reserved for use as a TPM’s Command Response Buffer region. |
crb_size |
0x4 |
hdr_size + 0x8 |
Size of CRB. |