Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/test-configs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,34 @@ jobs:
config-file: ./config/examples/raspi3-encrypted.config
make-args: wolfboot.bin CROSS_COMPILE=aarch64-linux-gnu-

tegra234_test:
uses: ./.github/workflows/test-build.yml
with:
arch: aarch64
# Tegra234 bare-metal BL33: bootloader + signed payload (RAM boot, no
# contiguous factory.bin), mirroring raspi3. This is the target's full
# MAIN_TARGET, and what the bundling scripts build.
config-file: ./config/examples/tegra234.config
make-args: wolfboot.bin test-app/image_v1_signed.bin CROSS_COMPILE=aarch64-linux-gnu-

tegra234_linux_test:
uses: ./.github/workflows/test-build.yml
with:
arch: aarch64
# Tegra234 DRAM-staged Linux boot (MMU/EL2->EL1/DTB code path).
config-file: ./config/examples/tegra234-linux.config
make-args: wolfboot.bin test-app/image_v1_signed.bin CROSS_COMPILE=aarch64-linux-gnu-

tegra234_sdcard_test:
uses: ./.github/workflows/test-build.yml
with:
arch: aarch64
# Tegra234 microSD/SDHCI path. Hardware bring-up is still blocked, so
# this job exists to keep the Tegra SDHCI shim and the BPMP clock driver
# compiling.
config-file: ./config/examples/tegra234-sdcard.config
make-args: wolfboot.bin test-app/image_v1_signed.bin CROSS_COMPILE=aarch64-linux-gnu-

sim_tfm_smallstack_test:
uses: ./.github/workflows/test-build.yml
with:
Expand Down
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,13 @@ ifeq ($(TARGET),raspi3)
MAIN_TARGET:=wolfboot.bin
endif

# Tegra234 bare-metal BL33 boots from RAM (loaded by an earlier stage), so
# there is no contiguous flash factory.bin. Build the bootloader plus the
# signed payload, which the bundling scripts concatenate into the BL33 image.
ifeq ($(TARGET),tegra234)
MAIN_TARGET:=wolfboot.bin test-app/image_v1_signed.bin
endif

ifeq ($(TARGET),sim)
CFLAGS+=-fno-pie
LDFLAGS+=-no-pie
Expand Down
35 changes: 35 additions & 0 deletions arch.mk
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,41 @@ ifeq ($(ARCH),AARCH64)
SPI_TARGET=nxp
endif

ifeq ($(TARGET),tegra234)
# NVIDIA Jetson Orin (Tegra234, Cortex-A78AE): bare-metal wolfBoot as the
# BL33 firmware stage. The UEFI-application alternative is the aarch64_efi
# target. See hal/tegra234.c.
# -mstrict-align: wolfBoot runs with the MMU off, where every access is
# Device-nGnRnE and an unaligned access takes an alignment fault. Without
# it the compiler is free to emit unaligned accesses for struct copies.
ARCH_FLAGS=-mcpu=cortex-a78+crypto -march=armv8.2-a+crypto -mstrict-align
CFLAGS+=$(ARCH_FLAGS) -DCORTEX_A78
# wolfBoot links and runs at the BL33 (cpubl) load address; must agree with
# ORIGIN in hal/tegra234.ld or the RAM-boot overlap guard misjudges where
# wolfBoot lives.
WOLFBOOT_ORIGIN=0x272000000
# Bring-up: dump the state the prior stage handed wolfBoot (entry EL,
# SCTLR/MMU/cache bits, handoff x0/DTB pointer). Read-only; opt out for a
# quiet build.
ifeq ($(TEGRA234_HANDOFF_DUMP),1)
CFLAGS+=-DTEGRA234_HANDOFF_DUMP
endif
# SDMMC1 bring-up probe. Separate from the dump above because it MUTATES
# SoC state the booted OS inherits: it enables the SDMMC1 clock, deasserts
# its reset, and drives the SD power-rail GPIO. Off unless asked for.
ifeq ($(TEGRA234_SDMMC_PROBE),1)
CFLAGS+=-DTEGRA234_SDMMC_PROBE
endif
# MMU/WOLFBOOT_FDT/DUALBOOT + fdt.o come from the shared AARCH64 block.
# tegra234 stays MMU-off at runtime (1:1 physical); those flags only pull
# in the FDT/DTS codepath. EL2_HYPERVISOR+BOOT_EL1 add the EL2->EL1 drop
# with the DTB in x0 (config/examples/tegra234-linux.config).
ifeq ($(EL2_HYPERVISOR),1)
CFLAGS+=-DEL2_HYPERVISOR=1
endif
# BOOT_EL1 itself is emitted by options.mk; nothing to add here.
endif

# Default ARM ASM setting for unrecognized AARCH64 targets
ifeq ($(filter zynq versal nxp_ls1028a,$(TARGET)),)
NO_ARM_ASM?=1
Expand Down
45 changes: 45 additions & 0 deletions config/examples/tegra234-linux.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# NVIDIA Jetson Orin / Tegra234 - bare-metal BL33, DRAM-staged Linux boot.
#
# wolfBoot runs as the bare-metal BL33 (replacing edk2 UEFI), verifies a signed
# Linux kernel bundled into the BL33 image in DRAM, and boots it EL2->EL1 with a
# device tree - no storage driver required. This sidesteps the SDMMC1 controller
# bring-up (still open; see hal/tegra234.c). The kernel + DTB are bundled by
# tools/scripts/tegra234-mkpoc.sh.
#
# For the storage-based (SDHCI) variant, see tegra234.config + DISK_SDCARD.
ARCH=AARCH64
TARGET=tegra234
SIGN?=ECC384
HASH?=SHA384
DEBUG?=1
DEBUG_UART?=1
TEGRA234_HANDOFF_DUMP?=1
SPMATH?=1
NO_XIP?=1
# Skip GIC init before booting the payload (Orin is GICv3; BL31 set it up).
SKIP_GIC_INIT?=1
WOLFBOOT_SECTOR_SIZE?=0x1000
WOLFBOOT_NO_PARTITIONS=1
# Payload slot in the BL33 bundle (see hal/tegra234.h): bounds the test-app link.
WOLFBOOT_PARTITION_SIZE?=0x100000

# --- Linux boot payload plumbing ---------------------------------------------
# MMU/WOLFBOOT_FDT come from arch.mk for every aarch64 target; they only switch
# on the FDT/DTS code path (the two-argument do_boot that forwards a DTB
# pointer). tegra234 stays MMU-off at runtime, 1:1 physical, which is what the
# arm64 Linux boot protocol wants. EL2_HYPERVISOR+BOOT_EL1 add the EL2->EL1
# drop with the DTB in x0 (the Linux boot ABI).
EL2_HYPERVISOR=1
BOOT_EL1=1

# RAM staging: the bundled payload lives at BL33_BASE+0x200000 and the DTB at
# +0x300000 (see hal/tegra234.h). update_ram copies the kernel to LOAD_ADDRESS
# and relocates the DTB to LOAD_DTS_ADDRESS, both in low DRAM, 2 MB-aligned.
# LOAD_DTS_ADDRESS must stay above LOAD_ADDRESS + RAMBOOT_MAX_SIZE
# (0xA0000000 + 0x4000000 = 0xA4000000), otherwise the DTB relocation writes
# into the tail of a large image that already passed signature verification.
# The cap itself is enforced at runtime by wolfBoot_open_image_address()
# (src/image.c, no-partition branch).
WOLFBOOT_RAMBOOT_MAX_SIZE=0x4000000
WOLFBOOT_LOAD_ADDRESS?=0xA0000000
WOLFBOOT_LOAD_DTS_ADDRESS?=0xA5000000
39 changes: 39 additions & 0 deletions config/examples/tegra234-sdcard.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# NVIDIA Jetson Orin / Tegra234 - bare-metal BL33, microSD (SDHCI) boot.
#
# WORK IN PROGRESS, does not boot from card yet: the SDMMC1 controller will not
# latch SD Clock Enable at BL33 because its functional (axicif) clock is brought
# up inside closed MB2/BPMP firmware only for the boot device (eMMC), not for
# microSD. See the SDHCI section of hal/tegra234.c.
#
# This config exists so the Tegra SDHCI shim and the BPMP clock driver stay
# compiled (and CI-checked) while that bring-up is blocked. For the paths that
# do work on hardware, use tegra234.config or tegra234-linux.config.
ARCH=AARCH64
TARGET=tegra234
SIGN?=ECC384
HASH?=SHA384
DEBUG?=1
DEBUG_UART?=1
TEGRA234_HANDOFF_DUMP?=1
# Run the BPMP/SDMMC1 bring-up probe from hal_init. Unlike the handoff dump
# this mutates SoC state (clocks, resets, SD power GPIO), so it is opt-in and
# only enabled here, where the controller has to be brought up anyway.
TEGRA234_SDMMC_PROBE?=1
SPMATH?=1
NO_XIP?=1
# Skip GIC init before booting the payload (Orin is GICv3; BL31 set it up).
SKIP_GIC_INIT?=1
WOLFBOOT_SECTOR_SIZE?=0x1000
WOLFBOOT_NO_PARTITIONS=1
# Payload slot in the BL33 bundle (see hal/tegra234.h): bounds the test-app link.
WOLFBOOT_PARTITION_SIZE?=0x100000

# Boot from the external microSD (SDMMC1) via the generic SDHCI driver.
DISK_SDCARD=1

# Upper bound on the image copied into RAM before verification. Keep
# LOAD_DTS_ADDRESS above LOAD_ADDRESS + RAMBOOT_MAX_SIZE so the DTB relocation
# cannot land inside the loaded image.
WOLFBOOT_RAMBOOT_MAX_SIZE=0x4000000
WOLFBOOT_LOAD_ADDRESS?=0x90000000
WOLFBOOT_LOAD_DTS_ADDRESS?=0x95000000
38 changes: 38 additions & 0 deletions config/examples/tegra234.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# NVIDIA Jetson Orin / Tegra234 - bare-metal wolfBoot as the BL33 firmware stage
# (the edk2 UEFI / cpu-bootloader slot that ARM Trusted Firmware hands off to).
# This base config boots a bundled signed payload at EL2; for the EL2->EL1 +
# device-tree Linux boot contract use config/examples/tegra234-linux.config.
# To run under UEFI instead, use the aarch64_efi target.
# See docs/Targets.md, "NVIDIA Jetson Orin (NVIDIA Tegra234) BL33 firmware".
#
# Toolchain: bare-metal aarch64-none-elf-, or aarch64-linux-gnu- (wolfboot.bin
# links -ffreestanding -nostartfiles, so the Linux GNU toolchain works too;
# that is what CI uses).
ARCH=AARCH64
TARGET=tegra234
SIGN?=ECC384
HASH?=SHA384
DEBUG?=1
DEBUG_UART?=1
# Bring-up: print the handoff state (entry EL, SCTLR/MMU/cache, x0/DTB pointer)
# from hal_init. Set to 0 for a quiet build once the handoff is characterized.
TEGRA234_HANDOFF_DUMP?=1
SPMATH?=1
# Boot media is external (SD/eMMC/NVMe via ext_flash); no internal flash.
NO_XIP?=1
# Skip GIC init before booting the payload (Orin is GICv3; BL31 set it up).
SKIP_GIC_INIT?=1
WOLFBOOT_SECTOR_SIZE?=0x1000
WOLFBOOT_NO_PARTITIONS=1
# Hard upper bound on the image size accepted for a RAM load. Enforced at
# runtime by wolfBoot_open_image_address() (src/image.c, no-partition branch).
WOLFBOOT_RAMBOOT_MAX_SIZE=0x4000000
# Payload slot in the BL33 bundle: BUNDLE_OFFSET..DTB_OFFSET (see
# hal/tegra234.h). Bounds the test-app link so an oversized payload fails to
# build rather than colliding with the bundled DTB.
WOLFBOOT_PARTITION_SIZE?=0x100000
# DRAM staging. Keep the DTB target above LOAD_ADDRESS + RAMBOOT_MAX_SIZE
# (0x90000000 + 0x4000000 = 0x94000000), so update_ram's DTB relocation cannot
# land inside a large image that already passed signature verification.
WOLFBOOT_LOAD_ADDRESS?=0x90000000
WOLFBOOT_LOAD_DTS_ADDRESS?=0x95000000
68 changes: 68 additions & 0 deletions docs/Targets.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ This README describes configuration of supported targets.
* [NXP T2080 PPC](#nxp-qoriq-t2080-ppc)
* [Qemu x86-64 UEFI](#qemu-x86-64-uefi)
* [NVIDIA Jetson Orin (aarch64_efi)](#nvidia-jetson-orin-aarch64_efi)
* [NVIDIA Jetson Orin (NVIDIA Tegra234) BL33 firmware](#nvidia-jetson-orin-nvidia-tegra234-bl33-firmware)
* [Raspberry Pi pico 2 (rp2350)](#raspberry-pi-pico-rp2350)
* [RealTek RTL8735B (AmebaPro2)](#realtek-rtl8735b-amebapro2)
* [Renesas RA6M4](#renesas-ra6m4)
Expand Down Expand Up @@ -7966,6 +7967,73 @@ TCG2: PCR 9 (SHA256):

`activeBanks=0x6` is the SHA-256 (0x2) + SHA-384 (0x4) PCR banks; the kernel, its command line and the platform device tree are extended into PCR 9 in both, and wolfBoot then reads the PCR back (`TPM2_PCR_Read`) and prints it. An attestation client can compare PCR 9 -- and the TCG2 event log -- against known-good values to confirm exactly which kernel, command line and device tree wolfBoot verified and booted. Choose `MEASURED_PCR_A` to fit the platform's PCR allocation (0-7 are firmware-owned; 8-15 are for OS/loader use). Note the edk2 firmware separately measures the loaded `wolfboot.efi` image itself into its own PCRs via `LoadImage`, so the firmware-verifies-wolfBoot and wolfBoot-measures-kernel events are distinct entries in the log.

## NVIDIA Jetson Orin (NVIDIA Tegra234) BL33 firmware

wolfBoot can run on the NVIDIA Jetson Orin two ways: as an `aarch64_efi` UEFI application (documented separately), or - this `tegra234` target - as **bare-metal firmware** that replaces the **BL33** stage. BL33 is the normal-world bootloader that ARM Trusted Firmware (BL31) hands off to at EL2 non-secure with the MMU off; on Jetson it is the edk2 UEFI / cpu-bootloader (cpubl) slot. Running bare-metal instead of under UEFI puts wolfBoot much closer to the root of trust, with a far smaller trusted surface beneath it - wolfBoot owns its own console, clocks, and boot handoff.

On an unfused developer board the BL33 slot is directly replaceable: MB2 and the earlier stages are inside NVIDIA's signed/fused root of trust and would require NVIDIA signing tooling, but BL33 is not signature-enforced. The bare-metal HAL (`hal/tegra234.c`) provides the Tegra Combined UART (TCU) console, the ARMv8 generic timer, a BPMP IPC driver (clocks/resets over the CPU-NS IVC channel), and a "handoff dump" (entry EL, SCTLR/MMU/cache bits, handoff `x0`) enabled with `TEGRA234_HANDOFF_DUMP=1`.

Validated on hardware (Jetson Orin Nano dev kit, non-persistent RCM boot): wolfBoot runs as BL33 at EL2, verifies a signed payload with wolfCrypt (ECC384/SHA384), drops from EL2 to EL1, and hands off with a device tree in `x0` - the arm64 Linux boot contract - straight out of DRAM with no storage driver. The payload and DTB are bundled into the BL33 image (see `tools/scripts/tegra234-mkpoc.sh` and `config/examples/tegra234-linux.config`). Loading a full kernel from storage is still in progress, bounded by two limits: MB2 caps the BL33 image at 4 MB (so a full kernel cannot be bundled - it must be loaded from storage or a pre-staged DRAM location), and microSD (SDHCI, `DISK_SDCARD`) is blocked on the closed SDMMC1 controller bring-up. See `hal/tegra234.c` for the full boot-chain map and open questions.

Build the bootloader binary (no hardware needed to compile):

```
cp config/examples/tegra234.config .config
make wolfboot.bin test-app/image_v1_signed.bin CROSS_COMPILE=aarch64-linux-gnu-
```

Three example configs ship for this target:

| Config | Boot path |
|---|---|
| `tegra234.config` | Verify the bundled payload and boot it at EL2 (no exception-level change). The simplest path. |
| `tegra234-linux.config` | Verify, drop EL2 -> EL1, hand off with the DTB in `x0` (the arm64 Linux boot contract). Validated on hardware. |
| `tegra234-sdcard.config` | microSD (SDHCI) boot. **Work in progress** - it compiles and probes but does not boot from card yet; see the SDMMC1 note above. |

By default all three enable `TEGRA234_HANDOFF_DUMP`, which prints the entry state read-only. Only the microSD config additionally runs the BPMP/SDMMC1 bring-up probe, because enabling the SDMMC1 clock and releasing its reset is a lasting change to SoC state that the booted OS would inherit.

### BL33 image layout

There is no storage driver yet, so the signed payload and the device tree are bundled into the BL33 image itself at fixed offsets and read straight out of DRAM. MB2 loads the whole image at `0x272000000` and BL31 enters it there:

```
offset 0x000000 wolfBoot (must fit below 0x200000)
offset 0x200000 signed payload <- hal_get_primary_address()
offset 0x300000 raw DTB <- hal_get_dts_address()
0x400000 MB2 cpubl size cap - the image must stay under this
```

The offsets are `TEGRA234_BL33_BASE`, `TEGRA234_BUNDLE_OFFSET` and `TEGRA234_DTB_OFFSET` in `hal/tegra234.h`; the two bundling scripts below use the same values and must be kept in sync with it. wolfBoot checks for the FDT magic at the DTB offset and reports no device tree if nothing was bundled there, so a plain `make wolfboot.bin` does not hand the payload a stale pointer.

Note on what is signed: wolfBoot verifies the **payload** against its own key. The bundled **device tree** is not covered by that signature - it is protected only by whatever signs the BL33 image as a whole (on an unfused developer board, nothing). Treat the DTB as part of the firmware image's trust boundary, not the payload's.

### Bundling scripts

Both scripts take the same arguments and do the same work - copy a config into place, build wolfBoot and the signed test-app together (one `make` invocation, so both are signed with the same freshly generated key), check the size budget, then concatenate the pieces at the offsets above and overwrite `wolfboot.bin` with the finished BL33 image. They differ only in which config they build.

The DTB is a required input: pass a path as the first argument, or set `L4T` to your `Linux_for_Tegra` directory and the script picks up `kernel/dtb/tegra234-p3768-0000+p3767-0005-nv.dtb` (Orin Nano dev kit) from it. Both scripts run `make keysclean` and `make clean` first, so **each run generates a new signing key** - build the bootloader and the payload from the same run.

`tools/scripts/tegra234-mkbl33.sh` - uses `config/examples/tegra234.config`. wolfBoot verifies the bundled payload and boots it **at EL2**, i.e. at the same exception level it was entered at. The DTB pointer is still passed to the payload in `x0` (the FDT code path is enabled for every AArch64 target); what this config does not do is the EL2 -> EL1 drop. Useful for bringing up a new board or checking the console and the handoff dump.

```
tools/scripts/tegra234-mkbl33.sh /path/to/tegra234-<board>.dtb
```

`tools/scripts/tegra234-mkpoc.sh` - uses `config/examples/tegra234-linux.config` (`EL2_HYPERVISOR=1`, `BOOT_EL1=1`). wolfBoot verifies the payload, **drops from EL2 to EL1, and enters it with the DTB pointer in `x0`** - the arm64 Linux boot contract. This is the one to use for the Linux boot path.

```
# Pass your board's kernel DTB, or set L4T=/path/to/Linux_for_Tegra:
tools/scripts/tegra234-mkpoc.sh /path/to/tegra234-<board>.dtb
```

Both scripts abort if wolfBoot has grown past the payload offset, if the payload runs into the DTB offset, or if the finished bundle exceeds MB2's 4 MB cap - a size overrun is a build error rather than a silently corrupted image - and both print a size breakdown of each piece.

Useful environment variables: `L4T` (as above) and `CROSS_COMPILE` (the scripts default to `aarch64-linux-gnu-`, which is what CI uses; a bare-metal `aarch64-none-elf-` toolchain also works). Set `TEGRA234_HANDOFF_DUMP=0` in the config for a quiet build once the handoff is characterized.

wolfBoot is linked into the 2 MB below the payload offset, so an image that outgrows its slot fails at link time (`region DDR_MEM overflowed`) rather than being assembled into a broken bundle.

The resulting `wolfboot.bin` is flashed into the BL33 (`A_cpu-bootloader`) partition, or - as used for the validation above - loaded non-persistently over USB with the L4T `flash.sh --rcm-boot` flow, which leaves the on-board firmware untouched.

## Intel x86_64 with Intel FSP support

This setup is more complex than the UEFI approach described earlier, but allows
Expand Down
Loading
Loading