Skip to content

tegra234: bare-metal BL33 target with verified EL2->EL1 + device-tree boot - #839

Draft
dgarske wants to merge 1 commit into
wolfSSL:masterfrom
dgarske:tegra234_baremetal
Draft

tegra234: bare-metal BL33 target with verified EL2->EL1 + device-tree boot#839
dgarske wants to merge 1 commit into
wolfSSL:masterfrom
dgarske:tegra234_baremetal

Conversation

@dgarske

@dgarske dgarske commented Jul 30, 2026

Copy link
Copy Markdown
Member

Summary

Adds a tegra234 target that runs wolfBoot as bare-metal firmware on the NVIDIA Jetson Orin (Tegra234), replacing the edk2 UEFI payload (BL33) instead of running as a UEFI application. BL33 is the normal-world bootloader that ARM Trusted Firmware (BL31) hands off to at EL2 non-secure; on Jetson it is the edk2 UEFI / cpu-bootloader (cpubl) slot. Running here places wolfBoot's verification directly on top of the SoC's fused root of trust, with a minimal trusted computing base beneath it.

This is self-contained and independent of the aarch64_efi UEFI-application target (which runs wolfBoot under UEFI). The two are complementary and are separate efforts. This branch needs nothing from the UEFI work.

Problem it solves

Running wolfBoot as a UEFI application leaves the entire vendor UEFI firmware (a large driver, filesystem, and network stack) between the hardware root of trust and wolfBoot's verifier. For a minimal, auditable secure-boot chain, wolfBoot should own the handoff to the OS directly, with nothing but ARM Trusted Firmware beneath it.

What it does (validated on hardware)

  • Runs as BL33 at EL2 non-secure and prints over the Tegra Combined UART (TCU).
  • Brings up clocks/resets through a BPMP IPC driver (CPU-NS IVC channel + HSP doorbell).
  • Verifies a signed payload with wolfCrypt (ECC P-384 / SHA-384).
  • Drops from EL2 to EL1 and hands off with a device tree in x0 -- the arm64 Linux boot contract -- straight from DRAM, with no storage driver.
  • Tested on a Jetson Orin Nano dev kit via non-persistent RCM boot (module QSPI is never written; recovery is a power-cycle).

Included

  • hal/tegra234.{c,h,ld} -- TCU console, ARMv8 generic timer, BPMP IPC, handoff dump, BL33 -> EL1 boot.
  • config/examples/tegra234.config (EL2 boot) and config/examples/tegra234-linux.config (EL2 -> EL1 + device tree).
  • tools/scripts/tegra234-mkbl33.sh and tegra234-mkpoc.sh -- build the BL33 image with the signed payload + DTB bundled for DRAM boot.
  • CI compile jobs for both configs, and a docs/Targets.md section.

Status / limits (documented in-tree)

Boots a verified payload and device tree from DRAM today. Loading a full kernel from storage is in progress, bounded by two constraints: MB2 caps the BL33 image at 4 MB (a full kernel must be loaded, not bundled), and microSD (SDHCI, DISK_SDCARD, off by default) is blocked on the closed SDMMC1 controller bring-up. Both are noted in hal/tegra234.c.

Testing

  • Build: cp config/examples/tegra234.config .config && make wolfboot.bin CROSS_COMPILE=aarch64-linux-gnu- (and the -linux config). Both are compile-tested in CI.
  • Hardware: tools/scripts/tegra234-mkpoc.sh, then RCM boot; the console shows Firmware Valid -> EL2->EL1 via ERET -> a valid device tree (magic = OK).

@dgarske dgarske self-assigned this Jul 30, 2026
Copilot AI review requested due to automatic review settings July 30, 2026 16:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new tegra234 bare-metal target that runs wolfBoot as the BL33 firmware stage on NVIDIA Jetson Orin (Tegra234), enabling verified payload boot and an EL2→EL1 handoff with a device tree passed in x0 per the arm64 Linux boot ABI.

Changes:

  • Add a Tegra234 bare-metal HAL (console via TCU mailbox, generic timer, BPMP IPC, EL2→EL1 handoff plumbing) plus a DRAM-resident linker script.
  • Add build/config/docs/CI support for the new tegra234 and tegra234-linux configurations and bundle-building scripts for DRAM-staged boot.
  • Extend the generic SDHCI driver with an optional platform clock hook to support BPMP/PMC-managed clock trees (e.g., Tegra).

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tools/scripts/tegra234-mkpoc.sh New helper to build a PoC BL33 bundle containing wolfBoot + signed payload + DTB for DRAM-staged boot.
tools/scripts/tegra234-mkbl33.sh New helper to build an RCM-bootable BL33 bundle for EL2 payload boot.
test-app/boot_arm64_start.S Preserve/restores handoff x0 across runtime init so the PoC payload can consume DTB pointer.
test-app/app_tegra234.c New Tegra234 PoC payload that prints EL and validates DTB magic at the handed-off pointer.
src/sdhci.c Add weak platform clock hook integration into SDHCI clock programming.
src/boot_aarch64_start.S Tegra234-specific simple-startup tweaks: use dedicated DRAM stack and capture handoff x0.
Makefile Add tegra234 main target selection consistent with RAM-boot style targets.
include/sdhci.h Declare the optional sdhci_platform_set_clock() hook.
hal/tegra234.ld New DRAM-resident linker script and staging address symbols for the Tegra234 BL33 target.
hal/tegra234.h New target header enabling simple builtin startup for Tegra234.
hal/tegra234.c New Tegra234 HAL implementation (console/timer/BPMP IPC/boot handoff + SDHCI WIP hooks).
docs/Targets.md Document the new Tegra234 BL33-firmware target and basic build steps.
config/examples/tegra234.config New base config for Tegra234 BL33 (EL2 payload boot).
config/examples/tegra234-linux.config New config enabling EL2→EL1 + DTB handoff path for DRAM-staged Linux boot.
arch.mk Add Tegra234-specific AArch64 flags and optional handoff dump build flag.
.github/workflows/test-configs.yml Add CI build jobs for the two new tegra234 example configs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/sdhci.c
Comment thread tools/scripts/tegra234-mkpoc.sh Outdated
Comment thread docs/Targets.md Outdated
Comment thread tools/scripts/tegra234-mkbl33.sh Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated no new comments.

Suppressed comments (6)

tools/scripts/tegra234-mkbl33.sh:49

  • The bundle assembly overwrites wolfboot.bin unconditionally and does not validate the final size against MB2’s CPUBL cap. If the bundle is too large, users end up with a broken wolfboot.bin and no clear failure mode until boot-time. Validate bundle.bin size first, then replace wolfboot.bin only if it is within the cap.
cp wolfboot.bin bundle.bin
truncate -s "$PAYLOAD_OFFSET" bundle.bin
cat test-app/image_v1_signed.bin >> bundle.bin
truncate -s "$DTB_OFFSET" bundle.bin
cat "$DTB" >> bundle.bin
mv bundle.bin wolfboot.bin

echo "BL33 bundle -> wolfboot.bin: $(stat -c%s wolfboot.bin) bytes"

test-app/app_tegra234.c:74

  • main is declared as main(void) but relies on reading the DTB pointer out of the ABI-defined x0 register via inline asm. Because the C signature does not declare an argument, the compiler is free to treat x0 as a scratch register in the prologue/early scheduling, so the value may be clobbered before it is captured. Declare main with an explicit first argument and use it directly to make the handoff contract robust.
void __attribute__((section(".boot"))) main(void) {
    uint64_t dtb, el;

    /* wolfBoot's el2_to_el1_boot() passes the DTB pointer in x0; capture it as
     * the very first thing, before the compiler can reuse the register. */
    __asm__ volatile("mov %0, x0" : "=r"(dtb));
    __asm__ volatile("mrs %0, CurrentEL" : "=r"(el));

test-app/boot_arm64_start.S:67

  • The comment says main(void) ignores the restored x0 argument, but tegra234’s payload now depends on the DTB pointer being passed through x0. If main is updated to take an explicit argument (recommended), this comment should reflect that contract to avoid confusion.
    /* Restore the handoff argument (x0) and jump to main - never returns.
     * main(void) ignores it, but tegra234's PoC payload reads x0 to recover
     * the DTB pointer. */

tools/scripts/tegra234-mkbl33.sh:21

  • This script describes an MB2 4 MiB BL33 cap but never defines or checks it, so it can silently produce an image that MB2 will reject at boot. Define the cap alongside the offsets so it can be enforced when assembling the bundle.

This issue also appears on line 42 of the same file.

PAYLOAD_OFFSET=$((0x200000))   # TEGRA234_BUNDLE_OFFSET (hal)
DTB_OFFSET=$((0x300000))       # TEGRA234_DTB_OFFSET (hal)

hal/tegra234.ld:76

  • The bundle layout relies on the wolfBoot runtime footprint (including the dedicated stack) fitting below the 0x200000 payload offset. Today this is only enforced by external scripts. Adding a linker ASSERT makes this constraint visible and fails fast at link time if the image+stack grows into the payload region.
    .stack (NOLOAD) :
    {
        . = ALIGN(16);
        _stack_bottom = .;
        . += 0x40000;      /* 256 KB */
        . = ALIGN(16);
        END_STACK = .;
    } > DDR_MEM
    . = ALIGN(4);

tools/scripts/tegra234-mkpoc.sh:55

  • The script overwrites wolfboot.bin before checking the MB2 CPUBL size cap. If the bundle exceeds 4 MiB, the script exits but leaves wolfboot.bin replaced with the invalid bundle. Check bundle.bin against the cap first, and only then replace wolfboot.bin.
cp wolfboot.bin bundle.bin
truncate -s "$PAYLOAD_OFFSET" bundle.bin
cat test-app/image_v1_signed.bin >> bundle.bin
truncate -s "$DTB_OFFSET" bundle.bin
cat "$DTB" >> bundle.bin
mv bundle.bin wolfboot.bin

total=$(stat -c%s wolfboot.bin)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants