Section 23.3: Trusted Firmware-A (TF-A)

Trusted Firmware-A is the reference implementation of secure world software for ARMv8-A platforms. Understanding TF-A is essential for ARM UEFI development as it provides the boot foundation and runtime services.

TF-A Overview

graph TB
    subgraph "TF-A Boot Loader Stages"
        BL1[BL1: AP Trusted ROM<br/>First stage boot loader]
        BL2[BL2: Trusted Boot Firmware<br/>DRAM init, loads other images]
        BL31[BL31: EL3 Runtime Firmware<br/>PSCI, SMC handling]
        BL32[BL32: Secure-EL1 Payload<br/>OP-TEE, other secure OS]
        BL33[BL33: Non-secure Payload<br/>UEFI Firmware]
    end

    BL1 --> BL2
    BL2 --> BL31
    BL2 --> BL32
    BL2 --> BL33

    style BL1 fill:#633,stroke:#333
    style BL31 fill:#633,stroke:#333
    style BL32 fill:#05c,stroke:#333
    style BL33 fill:#369,stroke:#333

Building TF-A

Prerequisites

# Install dependencies
sudo apt install -y \
    git \
    build-essential \
    gcc-aarch64-linux-gnu \
    device-tree-compiler \
    openssl \
    libssl-dev

# Clone TF-A repository
git clone https://github.com/ARM-software/arm-trusted-firmware.git
cd arm-trusted-firmware

Basic Build for QEMU

# Build for QEMU virt platform
make PLAT=qemu \
     CROSS_COMPILE=aarch64-linux-gnu- \
     BL33=/path/to/QEMU_EFI.fd \
     DEBUG=1 \
     all fip

# Output files:
# build/qemu/debug/bl1.bin
# build/qemu/debug/fip.bin (contains BL2, BL31, BL33)

Build Options Reference

# Common build variables
make PLAT=<platform>           \  # Target platform (qemu, rpi4, fvp, etc.)
     CROSS_COMPILE=aarch64-linux-gnu- \
     BL33=/path/to/uefi.fd     \  # UEFI firmware path
     BL32=/path/to/optee.bin   \  # Secure OS (optional)
     SPD=opteed                \  # Secure Payload Dispatcher
     DEBUG=1                   \  # Debug build
     LOG_LEVEL=40              \  # Verbose logging (0-50)
     ENABLE_STACK_PROTECTOR=strong \
     MEASURED_BOOT=1           \  # TPM measured boot
     TRUSTED_BOARD_BOOT=1      \  # Authenticated boot
     ARM_ROTPK_LOCATION=devel_rsa \
     ROT_KEY=plat/arm/board/common/rotpk/arm_rotprivk_rsa.pem \
     all fip

Platform-Specific Builds

Raspberry Pi 4

# Clone RPI firmware for GPU blobs
git clone --depth=1 https://github.com/raspberrypi/firmware rpi-firmware

# Build TF-A for RPi4
make PLAT=rpi4 \
     CROSS_COMPILE=aarch64-linux-gnu- \
     RPI3_PRELOADED_DTB_BASE=0x1F0000 \
     BL33=/path/to/RPI_EFI.fd \
     DEBUG=1 \
     all

# Deploy to SD card
cp build/rpi4/debug/bl31.bin /boot/
# Modify config.txt:
# enable_uart=1
# arm_64bit=1
# enable_gic=1
# armstub=bl31.bin

NXP i.MX8

# Build for i.MX8M Mini
make PLAT=imx8mm \
     CROSS_COMPILE=aarch64-linux-gnu- \
     BL33=/path/to/imx8mm_uefi.fd \
     SPD=opteed \
     BL32=/path/to/tee.bin \
     DEBUG=1 \
     all

# Combine with i.MX boot container
# Uses NXP's mkimage_imx8 tool

NVIDIA Tegra

# Build for Jetson platforms
make PLAT=tegra \
     TARGET_SOC=t194 \
     CROSS_COMPILE=aarch64-linux-gnu- \
     BL33=/path/to/uefi_jetson.bin \
     DEBUG=1 \
     all

# Flash using NVIDIA tools

BL1 Details

BL1 is the root of trust, running from ROM:

// bl1/bl1_main.c - Simplified flow
void bl1_main(void)
{
    // 1. Architecture setup
    bl1_arch_setup();

    // 2. Platform early setup
    bl1_early_platform_setup();

    // 3. Enable MMU
    bl1_plat_arch_setup();

    // 4. Print version
    NOTICE("BL1: %s\n", version_string);

    // 5. Perform platform setup
    bl1_platform_setup();

    // 6. Load and authenticate BL2
    bl1_load_bl2();

    // 7. Run BL2
    bl1_run_bl2(&bl2_ep);
}

Platform Porting for BL1

// plat/<platform>/bl1_plat_setup.c
void bl1_early_platform_setup(void)
{
    // Watchdog disable/configuration
    watchdog_disable();

    // Console initialization
    console_pl011_register(
        PLAT_UART_BASE,
        PLAT_UART_CLK_IN_HZ,
        PLAT_CONSOLE_BAUDRATE,
        &console
    );

    // Platform-specific security setup
    platform_security_setup();
}

void bl1_plat_arch_setup(void)
{
    // Configure MMU
    const mmap_region_t bl1_regions[] = {
        MAP_REGION_FLAT(
            PLAT_ROM_BASE, PLAT_ROM_SIZE,
            MT_CODE | MT_SECURE),
        MAP_REGION_FLAT(
            PLAT_SRAM_BASE, PLAT_SRAM_SIZE,
            MT_MEMORY | MT_RW | MT_SECURE),
        {0}
    };

    setup_page_tables(bl1_regions);
    enable_mmu_el3(0);
}

BL2 Details

BL2 handles memory initialization and image loading:

// bl2/bl2_main.c - Simplified flow
void bl2_main(void)
{
    // 1. Architecture setup
    bl2_arch_setup();

    // 2. Platform setup (DRAM init happens here)
    bl2_platform_setup();

    // 3. Load images from FIP
    bl2_load_images();

    // 4. Transfer to BL31 via SMC
    smc(BL1_SMC_RUN_IMAGE, (u_register_t)&bl31_ep_info,
        0, 0, 0, 0, 0, 0);
}

void bl2_platform_setup(void)
{
    // Memory controller initialization
    dram_init();

    // I/O setup for loading images
    plat_io_setup();

    // Security region configuration
    configure_tzc400();
}

DDR Training in BL2

// Example: DDR4 initialization sequence
void dram_init(void)
{
    // 1. Configure DDR PLL
    ddr_pll_init(DDR_FREQ_MHZ);

    // 2. Configure DDR controller
    ddr_ctrl_init();

    // 3. Configure PHY
    ddr_phy_init();

    // 4. Run training algorithms
    ddr_write_leveling();
    ddr_read_gate_training();
    ddr_read_eye_training();
    ddr_write_eye_training();

    // 5. Verify memory
    ddr_self_test();

    INFO("DRAM: %llu MB initialized\n",
         dram_size / (1024 * 1024));
}

BL31 Runtime Services

BL31 provides runtime services at EL3:

// bl31/bl31_main.c
void bl31_main(void)
{
    // 1. EL3 architecture setup
    bl31_arch_setup();

    // 2. Platform setup
    bl31_platform_setup();

    // 3. Initialize runtime services
    runtime_svc_init();

    // 4. Initialize BL32 (Secure Payload)
    if (bl32_init)
        bl32_init();

    // 5. Prepare BL33 entry
    bl31_prepare_next_image_entry();

    // 6. Exit to BL33
    bl31_plat_runtime_setup();
    el3_exit();
}

SMC Handler Registration

// Registering SMC handlers
DECLARE_RT_SVC(
    psci_svc,                   // Service name
    OEN_STD_START,              // Owning entity range start
    OEN_STD_END,                // Owning entity range end
    SMC_TYPE_FAST,              // SMC type
    psci_smc_setup,             // Init function
    psci_smc_handler            // SMC handler
);

// Handler implementation
uintptr_t psci_smc_handler(
    uint32_t smc_fid,
    u_register_t x1,
    u_register_t x2,
    u_register_t x3,
    u_register_t x4,
    void *cookie,
    void *handle,
    u_register_t flags)
{
    switch (smc_fid) {
    case PSCI_VERSION:
        return psci_version();

    case PSCI_CPU_ON_AARCH64:
        return psci_cpu_on(x1, x2, x3);

    case PSCI_CPU_OFF:
        return psci_cpu_off();

    case PSCI_AFFINITY_INFO_AARCH64:
        return psci_affinity_info(x1, x2);

    case PSCI_SYSTEM_RESET:
        psci_system_reset();
        /* Should not return */
        break;

    case PSCI_SYSTEM_OFF:
        psci_system_off();
        /* Should not return */
        break;

    default:
        WARN("Unknown PSCI function: 0x%x\n", smc_fid);
        return SMC_UNK;
    }
}

Platform-Specific SMC Handlers

// Silicon vendor SiP (SoC Implementation Specific) services
DECLARE_RT_SVC(
    plat_sip_svc,
    OEN_SIP_START,
    OEN_SIP_END,
    SMC_TYPE_FAST,
    plat_sip_setup,
    plat_sip_handler
);

uintptr_t plat_sip_handler(
    uint32_t smc_fid,
    u_register_t x1,
    u_register_t x2,
    u_register_t x3,
    u_register_t x4,
    void *cookie,
    void *handle,
    u_register_t flags)
{
    switch (smc_fid) {
    case SIP_SVC_UID:
        // Return SIP service UUID
        SMC_RET4(handle, SIP_UID_0, SIP_UID_1, SIP_UID_2, SIP_UID_3);

    case SIP_SVC_VERSION:
        SMC_RET2(handle, SIP_VERSION_MAJOR, SIP_VERSION_MINOR);

    case SIP_CONFIGURE_CLOCKS:
        return plat_configure_clocks(x1, x2);

    case SIP_GET_SOC_VERSION:
        return plat_get_soc_version();

    default:
        return SMC_UNK;
    }
}

PSCI Implementation

Power Domain Topology

// Platform power domain tree
// plat/<platform>/platform.h

#define PLAT_MAX_PWR_LVL        2
#define PLAT_NUM_PWR_DOMAINS    (PLATFORM_CLUSTER_COUNT + \
                                 PLATFORM_CORE_COUNT + 1)

// Example: 2 clusters, 4 cores each
static const unsigned char plat_power_domain_tree_desc[] = {
    /* System level */
    1,
    /* Cluster level */
    PLATFORM_CLUSTER_COUNT,
    /* CPU cores per cluster */
    PLATFORM_CLUSTER0_CORE_COUNT,
    PLATFORM_CLUSTER1_CORE_COUNT,
};

const unsigned char *plat_get_power_domain_tree_desc(void)
{
    return plat_power_domain_tree_desc;
}

PSCI Platform Operations

// plat/<platform>/plat_psci.c
static const plat_psci_ops_t plat_psci_ops = {
    .cpu_standby          = plat_cpu_standby,
    .pwr_domain_on        = plat_pwr_domain_on,
    .pwr_domain_off       = plat_pwr_domain_off,
    .pwr_domain_suspend   = plat_pwr_domain_suspend,
    .pwr_domain_on_finish = plat_pwr_domain_on_finish,
    .pwr_domain_suspend_finish = plat_pwr_domain_suspend_finish,
    .system_off           = plat_system_off,
    .system_reset         = plat_system_reset,
    .validate_power_state = plat_validate_power_state,
    .get_sys_suspend_power_state = plat_get_sys_suspend_power_state,
};

int plat_pwr_domain_on(u_register_t mpidr)
{
    unsigned int cpu_id = plat_core_pos_by_mpidr(mpidr);

    // Set CPU entry point
    mmio_write_64(PLAT_RVBAR_BASE + cpu_id * 8,
                  (uint64_t)bl31_warm_entrypoint);

    // Power on CPU
    plat_power_on_cpu(cpu_id);

    // De-assert reset
    plat_release_cpu(cpu_id);

    return PSCI_E_SUCCESS;
}

void plat_system_reset(void)
{
    // Trigger system reset via watchdog or reset controller
    mmio_write_32(PLAT_RST_CTRL, PLAT_RST_TRIGGER);

    wfi();
}

Firmware Image Package (FIP)

FIP Structure

graph LR
    subgraph "FIP Layout"
        TOC[Table of Contents<br/>UUID, offset, size]
        BL2[BL2 Image]
        BL31[BL31 Image]
        BL32[BL32 Image<br/>Optional]
        BL33[BL33 Image<br/>UEFI]
        CERT[Certificates<br/>Optional]
    end

    TOC --> BL2
    BL2 --> BL31
    BL31 --> BL32
    BL32 --> BL33
    BL33 --> CERT

Creating FIP

# Using fiptool
./tools/fiptool/fiptool create \
    --tb-fw build/qemu/debug/bl2.bin \
    --soc-fw build/qemu/debug/bl31.bin \
    --tos-fw /path/to/optee/tee.bin \
    --nt-fw /path/to/uefi/QEMU_EFI.fd \
    fip.bin

# Inspect FIP contents
./tools/fiptool/fiptool info fip.bin

# Update single image in FIP
./tools/fiptool/fiptool update \
    --nt-fw /path/to/new_uefi.fd \
    fip.bin

FIP UUID Definitions

// include/tools_share/firmware_image_package.h
#define UUID_TRUSTED_BOOT_FIRMWARE_BL2 \
    {{0x5f, 0xf9, 0xec, 0x0b}, {0x4d, 0x22}, {0x3e, 0x4d}, \
     0xa5, 0x44, {0xc3, 0x9d, 0x81, 0xc7, 0x3f, 0x0a}}

#define UUID_EL3_RUNTIME_FIRMWARE_BL31 \
    {{0x47, 0xd4, 0x08, 0x6d}, {0x4c, 0xfe}, {0x98, 0x46}, \
     0x9b, 0x95, {0x29, 0x50, 0xcb, 0xbd, 0x5a, 0x00}}

#define UUID_SECURE_PAYLOAD_BL32 \
    {{0x05, 0xd0, 0xe1, 0x89}, {0x53, 0xdc}, {0x13, 0x47}, \
     0x8d, 0x2b, {0x50, 0x0a, 0x4b, 0x7a, 0x3e, 0x38}}

#define UUID_NON_TRUSTED_FIRMWARE_BL33 \
    {{0xd6, 0xd0, 0xee, 0xa7}, {0xfc, 0xea}, {0xd5, 0x4b}, \
     0x97, 0x82, {0x99, 0x34, 0xf2, 0x34, 0xb6, 0xe4}}

Authenticated Boot

Certificate Chain

graph TB
    ROTPK[Root of Trust Public Key<br/>Burned in OTP/eFuse]
    ROTSK[Root of Trust Secret Key<br/>Offline, secured]

    ROTPK --> |Verifies| TB_FW_CERT
    TB_FW_CERT[Trusted Boot FW Certificate<br/>Signs BL2 hash]
    TB_FW_CERT --> |Verifies| BL2

    TB_FW_CERT --> |Contains| TB_FW_KEY
    TB_FW_KEY[Trusted Boot FW Key]
    TB_FW_KEY --> |Signs| SOC_FW_CERT
    TB_FW_KEY --> |Signs| TOS_FW_CERT
    TB_FW_KEY --> |Signs| NT_FW_CERT

    SOC_FW_CERT[SoC FW Certificate] --> |Verifies| BL31
    TOS_FW_CERT[Trusted OS Certificate] --> |Verifies| BL32
    NT_FW_CERT[Non-trusted FW Certificate] --> |Verifies| BL33

    style ROTPK fill:#f99,stroke:#333

Building with Trusted Boot

# Generate keys (one-time setup)
openssl genrsa -out rot_key.pem 2048
openssl genrsa -out trusted_world_pk.pem 2048
openssl genrsa -out non_trusted_world_pk.pem 2048

# Build with authenticated boot
make PLAT=qemu \
     CROSS_COMPILE=aarch64-linux-gnu- \
     TRUSTED_BOARD_BOOT=1 \
     GENERATE_COT=1 \
     CREATE_KEYS=1 \
     ROT_KEY=rot_key.pem \
     TRUSTED_WORLD_KEY=trusted_world_pk.pem \
     NON_TRUSTED_WORLD_KEY=non_trusted_world_pk.pem \
     BL33=/path/to/QEMU_EFI.fd \
     all fip

Running TF-A with QEMU

#!/bin/bash
# run-tfa-qemu.sh

TFA_DIR=~/arm-trusted-firmware
EDK2_DIR=~/edk2

# Build TF-A
cd $TFA_DIR
make PLAT=qemu \
     CROSS_COMPILE=aarch64-linux-gnu- \
     BL33=$EDK2_DIR/Build/ArmVirtQemu-AARCH64/DEBUG_GCC5/FV/QEMU_EFI.fd \
     DEBUG=1 \
     all fip

# Create flash images
dd if=build/qemu/debug/bl1.bin of=flash.bin bs=4096 conv=notrunc
dd if=build/qemu/debug/fip.bin of=flash.bin bs=4096 seek=64 conv=notrunc

# Run QEMU
qemu-system-aarch64 \
    -M virt,secure=on,virtualization=on \
    -cpu cortex-a57 \
    -smp 2 \
    -m 1024 \
    -bios flash.bin \
    -serial stdio \
    -d unimp \
    -semihosting-config enable=on,target=native

Debugging TF-A

Debug Output Levels

// Log levels in TF-A
#define LOG_LEVEL_NONE    0   // No output
#define LOG_LEVEL_ERROR   10  // Errors only
#define LOG_LEVEL_NOTICE  20  // Normal operation
#define LOG_LEVEL_WARNING 30  // Warnings
#define LOG_LEVEL_INFO    40  // Informational
#define LOG_LEVEL_VERBOSE 50  // Detailed debug

// Build with verbose logging
make PLAT=qemu LOG_LEVEL=50 ...

GDB Debugging

# Run QEMU with debug
qemu-system-aarch64 -M virt,secure=on -cpu cortex-a57 \
    -bios flash.bin -nographic -s -S

# Connect GDB
aarch64-linux-gnu-gdb
(gdb) target remote :1234
(gdb) add-symbol-file build/qemu/debug/bl1/bl1.elf 0x0
(gdb) add-symbol-file build/qemu/debug/bl2/bl2.elf 0xe01b000
(gdb) add-symbol-file build/qemu/debug/bl31/bl31.elf 0xe040000
(gdb) break bl31_main
(gdb) continue

Platform Porting Checklist

- [ ] Platform directory structure
      plat/<vendor>/<platform>/
      ├── include/
      │   └── platform_def.h
      ├── aarch64/
      │   └── plat_helpers.S
      ├── bl1_plat_setup.c
      ├── bl2_plat_setup.c
      ├── bl31_plat_setup.c
      ├── plat_psci.c
      ├── plat_topology.c
      ├── plat_gic.c
      ├── plat_io_storage.c
      └── platform.mk

- [ ] Platform definition (platform_def.h)
- [ ] Memory map configuration
- [ ] GIC initialization
- [ ] UART/Console driver
- [ ] Clock and reset control
- [ ] PSCI power operations
- [ ] Secure world configuration (TZC)
- [ ] IO layer for image loading
- [ ] Build system integration

References


Next: Section 23.4: SystemReady Compliance - Certification requirements and testing.


Back to top

UEFI Development Guide is not affiliated with the UEFI Forum. Content is provided for educational purposes.

This site uses Just the Docs, a documentation theme for Jekyll.