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].

Transfer list example

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.

Table 1 TL header

Field

Size (bytes)

Offset (bytes)

Description

signature

0x4

0x0

The value of signature must be 0x4a0f_b10b.

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 {tl_base_pa, …, tl_base_pa + used_size - 1} address range, is equal to 0. For the purposes of this calculation, the value of this checksum field in the TL header must be assumed as 0. Note that the checksum must include the TL header, all TEs and the inter-TE padding, but must not include the range reserved for future TE additions up to total_size. The values of inter-TE padding bytes are not defined by this specification and may be uninitialized memory. (This means that multiple TLs with exactly the same size and contents may still have different checksum values.). If checksums are not used, this must be 0.

version

0x1

0x5

The version of the TL header. This field must be set to 0x1 for the TL header layout described in this version of the table. Code that encounters a TL with a version higher than it knows to support may still read the TL and all its TEs, and assume that it is backwards-compatible to previous versions (ignoring any extra bytes in a potentially larger TL or TE header). However, code may not append new entries to a TL unless it knows how to append entries for the specified version.

hdr_size

0x1

0x6

The size of this TL header in bytes. This field is set to 0x18 for the TL header layout described in this version of the table.

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 8 so this value should be set to 3. It must be updated whenever a new TE is added with a larger requirement than the current value.

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 8 (i.e. it includes the inter-TE padding after the end of the last TE). This field must be updated when any entry is added to the TL.

total_size

0x4

0xc

The number of bytes occupied by the entire TL, including any spare space at the end, after used_size. Any entry producer must check if there is sufficient space before adding an entry to the list. Firmware can resize and/or relocate the TL and update this field accordingly, provided that the TL requirements are respected. This field must be a multiple of 8.

flags

0x4

0x10

Flags word. See below for contents.

reserved

0x4

0x14

Reserved word. Must be set to 0 or ignored.

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.

Table 2 Flags

Bit

Name

Description

0

has_checksum

A value of 1 (true) indicates that this TL uses checksums. The checksum field must be valid at the point of handoff.

31:1

unused

Reserved for future use. Must be 0.

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.

Table 3 TE header

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 8 for the TE header layout described in this version of the table.

data_size

0x4

0x4

The exact size of the data content in bytes, not including inter-TE padding. May be 0.

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.

Table 4 Tag ID ranges

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.

  1. Compare tl.signature (tl_base_addr + 0x0) to 0x4a0f_b10b. On a mismatch, abort (this is not a valid TL).

  2. Compare tl.version (tl_base_addr + 0x5) to the expected version (currently 0x1). If there is an exact match, the TL is valid for all operations outlined in this section. If tl.version is larger, the TL is valid for reading but must not be modified or relocated. If tl.version is smaller, either abort or switch to code designed to interpret the respective previous version of this specification (note that the version number 0x0 is illegal and processing should always abort if it is found).

  3. (optional) Check that tl.used_size (tl_base_addr + 0x8) is smaller or equal to tl.total_size (tl_base_addr + 0xc), and that tl.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).

  4. (optional) If has_checksum, check that the xor of tl.used_size bytes starting at tl_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.

  1. Calculate te_base_addr as align8(tl_base_addr + tl.hdr_size). (Do not hardcode the value for tl.hdr_size!)

  2. While te_base_addr - tl_base_addr is smaller or equal to tl.used_size:

    1. (optional) Check that te_base_addr + te.hdr_size + te.data_size - tl_base_addr is smaller or equal to tl.used_size, otherwise abort (the TL is corrupted).

    2. If te.tag_id (te_base_addr + 0x0) is a known tag, interpret the data at te_base_addr + te.hdr_size accordingly. (Do not hardcode the value for te.hdr_size, even for known tags!) Otherwise, ignore the tag and proceed with the next step.

    3. Add align8(te.hdr_size + te.data_size) to te_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.

  1. (optional) Follow the steps in Reading a TL to look for a TE where te.tag_id is 0x0 (XFERLIST_VOID) and te.data_size is greater or equal to new_data_size. If found:

    1. Remember te.data_size as old_void_data_size.

    2. Use the te_base_addr of this tag for the rest of the operation.

    3. If has_checksum, xor the align8(new_data_size + 0x8) bytes starting at te_base_addr with tl.checksum.

    4. Skip the next step (step 2) with all its substeps.

  2. Calculate te_base_addr as tl_base_addr + tl.used_size.

    1. If tl.total_size - tl.used_size is smaller than align8(new_data_size + 0x8), abort (not enough room to add TE).

    2. If has_checksum, xor the 4 bytes from tl_base_addr + 0x8 with tl_base_addr + 0xc from tl.checksum.

    3. Add align8(new_data_size + 0x8) to tl.used_size.

    4. If has_checksum, xor the 4 bytes from tl_base_addr + 0x8 to tl_base_addr + 0xc with tl.checksum.

  3. Set te.tag_id (te_base_addr + 0x0) to new_tag_id.

  4. Set te.hdr_size (te_base_addr + 0x3) to 8.

  5. Set te.data_size (te_base_addr + 0x4) to new_data_size.

  6. Copy or generate the TE data into te_base_addr + 0x8.

  7. If has_checksum, xor the align8(new_data_size + 0x8) bytes starting at te_base_addr with tl.checksum.

  8. 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 to 0x8:

    1. Use te_base_addr + align8(new_data_size + 0x8) as the new te_base_addr for a new XFERLIST_VOID tag.

    2. If has_checksum, xor the 8 bytes from te_base_addr to te_base_addr + 0x8 with tl.checksum.

    3. Set te.tag_id (te_base_addr + 0x0) to 0x0 (XFERLIST_VOID).

    4. Set te.hdr_size (te_base_addr + 0x3) to 0x8.

    5. Set te.data_size (te_base_addr + 0x4) to old_void_data_size - align8(new_data_size) - 0x8.

    6. If has_checksum, xor the 8 bytes from te_base_addr to te_base_addr + 0x8 with tl.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 of 2 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.

  1. Calculate alignment_mask as (1 << new_alignment) - 1.

  2. If (tl_base_addr + tl.used_size + 0x8) & alignment_mask is not 0x0, follow the steps in Adding a new TE with the following inputs (bypass the option to overwrite an existing XFERLIST_VOID TE):

    1. tl_base_addr remains the same

    2. new_tag_id is 0x0 (XFERLIST_VOID)

    3. new_data_size is (1 << new_alignment) - ((tl_base_addr + tl.used_size + 0x8) & alignment_mask) - 0x8.

    4. No data (i.e. just don’t touch the bytes that form the data portion for this TE).

  3. Follow the steps in Adding a new TE with the original inputs (again bypass the option to overwrite an existing XFERLIST_VOID TE).

  4. If new_alignment is larger than tl.alignment:

    1. If has_checksum, xor tl.alignment with tl.checksum.

    2. Set tl.alignment to new_alignment.

    3. If has_checksum, xor tl.alignment with tl.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 after tl_base_addr.

  1. Check that available_size is larger than 0x18 (the assumed tl.hdr_size), otherwise abort.

  2. Set tl.signature (tl_base_addr + 0x0) to 0x4a0f_b10b.

  3. Set tl.checksum (tl_base_addr + 0x4) to 0x0 (for now).

  4. Set tl.version (tl_base_addr + 0x5) to 0x1.

  5. Set tl.hdr_size (tl_base_addr + 0x6) to 0x18.

  6. Set tl.alignment (tl_base_addr + 0x7) to 0x3.

  7. Set tl.used_size (tl_base_addr + 0x8) to 0x18 (the assumed tl.hdr_size).

  8. Set tl.total_size (tl_base_addr + 0xc) to available_size.

  9. If checksums are to be used, set tl.flags (tl_base_addr + 0x10) to 1, else 0. This is the value of has_checksum.

  10. If has_checksum, calculate the checksum as the xor of all bytes from tl_base_addr to tl_base_addr + tl.hdr_size, and write the result to tl.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.

  1. Calculate alignment_mask as (1 << tl.alignment) - 1.

  2. Calculate the current alignment_offset as tl_base_addr & alignment_mask.

  3. Calculate new_tl_base as (target_base & ~alignment_mask) + alignment_offset.

  4. If new_tl_base is below target_base, add alignment_mask + 1 to new_tl_base.

  5. If new_tl_base - target_base + tl.used_size is larger than target_size, abort (not enough space to relocate).

  6. Copy tl.used_size bytes from tl_base_addr to new_tl_base.

  7. If has_checksum, xor the the 4 bytes from new_tl_base + 0xc to new_tl_base + 0x10 with tl.checksum (new_tl_base + 0x4).

  8. Set tl.total_size (new_tl_base + 0xc) to target_size - (new_tl_base - target_base).

  9. If has_checksum, xor the 4 bytes from new_tl_base + 0xc to new_tl_base + 0x10 with tl.checksum (new_tl_base + 0x4).

2.7. Standard transfer entries

The following entry types are currently defined:

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).

Table 5 Empty type layout

Field

Size (bytes)

Offset (bytes)

Description

tag_id

0x3

0x0

The tag_id field must be set to 0.

hdr_size

0x1

0x3

The size of this entry header in bytes must be set to 8.

data_size

0x4

0x4

The size of the void space in bytes. May be 0. For XFERLIST_VOID, data_size MUST be a multiple of 8 (i.e. there must be no space left to inter-TE padding after this TE).

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.

Table 6 FDT type layout

Field

Size (bytes)

Offset (bytes)

Description

tag_id

0x3

0x0

The tag_id field must be set to 1.

hdr_size

0x1

0x3

The size of this entry header in bytes must be set to 8.

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].

Table 7 HOB block type layout

Field

Size (bytes)

Offset (bytes)

Description

tag_id

0x3

0x0

The tag_id field must be set to 2.

hdr_size

0x1

0x3

The size of this entry header in bytes must be set to 8.

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].

Table 8 HOB list type layout

Field

Size (bytes)

Offset (bytes)

Description

tag_id

0x3

0x0

The tag_id field must be set to 3.

hdr_size

0x1

0x3

The size of this entry header in bytes must be set to 8.

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.

Table 9 ACPI table aggregate type layout

Field

Size (bytes)

Offset (bytes)

Description

tag_id

0x3

0x0

The tag_id field must be set to 4.

hdr_size

0x1

0x3

The size of this entry header in bytes must be set to 8.

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].

Table 10 TPM event log type layout

Field

Size (bytes)

Offset (bytes)

Description

tag_id

0x3

0x0

The tag_id field must be set to 5.

hdr_size

0x1

0x3

The size of this entry header in bytes must be set to 8.

data_size

0x4

0x4

The size of the event log in bytes + sizeof(flags) i.e. 0x4.

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 0. Other bits should be set to zero.

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.

Table 11 TPM CRB base type layout

Field

Size (bytes)

Offset (bytes)

Description

tag_id

0x3

0x0

The tag_id field must be set to 6.

hdr_size

0x1

0x3

The size of this entry header in bytes must be set to 8.

data_size

0x4

0x4

This value should be set to 0xc` i.e. sizeof(crb_base_address) + sizeof(crb_size).

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.