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
7 changes: 7 additions & 0 deletions .github/workflows/test-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ on:
make-args:
required: false
type: string
prep-run:
required: false
type: string

jobs:

Expand Down Expand Up @@ -42,6 +45,10 @@ jobs:
run: |
make -C tools/keytools && make -C tools/bin-assemble

- name: Pre-build setup
if: inputs.prep-run != ''
run: ${{inputs.prep-run}}

- name: Build wolfboot
run: |
make ${{inputs.make-args}}
Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/test-configs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,14 @@ jobs:
arch: host
config-file: ./config/examples/x86_64_efi.config

aarch64_efi_test:
uses: ./.github/workflows/test-build.yml
with:
arch: aarch64
config-file: ./config/examples/aarch64_efi.config
# Build gnu-efi for aarch64 (pinned in the script) before wolfboot.efi.
prep-run: ./tools/scripts/build-gnu-efi-aarch64.sh

zynqmp_test:
uses: ./.github/workflows/test-build-aarch64.yml
with:
Expand Down
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -421,3 +421,10 @@ sdcard.img

# wolfHSM STM32H5 TZ demo build output
port/stmicro/stm32h5-tz-wolfhsm/out/

# gnu-efi built for the AArch64 UEFI target and its cloned source
tools/gnu-efi-aarch64/
tools/gnu-efi-src/
# aarch64_efi working/staging dirs (build artifacts, not tracked)
aarch64_efi-stage/
tools/qemu-esp/
9 changes: 8 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,9 @@ SIGN_ENV=IMAGE_HEADER_SIZE=$(IMAGE_HEADER_SIZE) \


MAIN_TARGET=factory.bin
# PE/COFF output format for the wolfboot.efi objcopy rule. Overridden per
# target in arch.mk (e.g. pei-aarch64-little for aarch64_efi).
EFI_OBJCOPY_TARGET?=pei-x86-64
TARGET_H_TEMPLATE:=include/target.h.in

ifeq ($(TZEN),1)
Expand Down Expand Up @@ -293,6 +296,10 @@ ifeq ($(TARGET),x86_64_efi)
MAIN_TARGET:=wolfboot.efi
endif

ifeq ($(TARGET),aarch64_efi)
MAIN_TARGET:=wolfboot.efi
endif

ifeq ($(FSP), 1)
MAIN_TARGET:=wolfboot_stage1.bin
endif
Expand Down Expand Up @@ -379,7 +386,7 @@ wolfboot.efi: wolfboot.elf
$(Q)$(OBJCOPY) -j .rodata -j .text -j .sdata -j .data \
-j .dynamic -j .dynsym -j .rel \
-j .rela -j .reloc -j .eh_frame \
-O pei-x86-64 --subsystem=10 $^ $@
-O $(EFI_OBJCOPY_TARGET) --subsystem=10 $^ $@
@echo
@echo "\t[SIZE]"
$(Q)$(SIZE) wolfboot.efi
Expand Down
60 changes: 58 additions & 2 deletions arch.mk
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,20 @@ endif

## ARM Cortex-A
ifeq ($(ARCH),AARCH64)
CROSS_COMPILE?=aarch64-none-elf-
ifeq ($(TARGET),aarch64_efi)
# UEFI app: Linux GNU toolchain (freestanding EFI ABI), not aarch64-none-elf-
CROSS_COMPILE?=aarch64-linux-gnu-
else
CROSS_COMPILE?=aarch64-none-elf-
endif
CFLAGS+=-DARCH_AARCH64 -DFAST_MEMCPY
OBJS+=src/boot_aarch64.o src/boot_aarch64_start.o
ifeq ($(TARGET),aarch64_efi)
# UEFI app: gnu-efi CRT0 is the entry; do_boot is in boot_aarch64_efi.o.
# Skip the bare-metal reset (boot_aarch64_start.S) and EL2/GIC glue.
OBJS+=src/boot_aarch64_efi.o
else
OBJS+=src/boot_aarch64.o src/boot_aarch64_start.o
endif

ifeq ($(TARGET),zynq)
ARCH_FLAGS=-march=armv8-a+crypto
Expand Down Expand Up @@ -1664,6 +1675,37 @@ ifeq ($(TARGET),x86_64_efi)
UPDATE_OBJS:=src/update_ram.o
endif

ifeq ($(TARGET),aarch64_efi)
# Generic AArch64 UEFI application (validated on NVIDIA Jetson Orin Nano).
# Build gnu-efi for aarch64 first: ./tools/scripts/build-gnu-efi-aarch64.sh
# (override the install path with GNU_EFI_PATH=... if needed).
USE_GCC_HEADLESS=0
GNU_EFI_PATH?=tools/gnu-efi-aarch64
GNU_EFI_LIB_PATH?=$(GNU_EFI_PATH)/lib
GNU_EFI_INC_PATH?=$(GNU_EFI_PATH)/include
GNU_EFI_CRT0=$(GNU_EFI_LIB_PATH)/crt0-efi-aarch64.o
GNU_EFI_LSCRIPT=$(GNU_EFI_LIB_PATH)/elf_aarch64_efi.lds
CFLAGS += -fpic -ffreestanding -fno-stack-protector -fno-stack-check \
-fshort-wchar -mstrict-align
CFLAGS += -I$(GNU_EFI_INC_PATH) -I$(GNU_EFI_INC_PATH)/efi \
-I$(GNU_EFI_INC_PATH)/efi/aarch64 \
-DTARGET_aarch64_efi -DWOLFBOOT_DUALBOOT
# avoid using of fixed LOAD_ADDRESS, uefi target uses dynamic location
CFLAGS += -DWOLFBOOT_NO_LOAD_ADDRESS
# AArch64 PE/COFF output format for objcopy (see the wolfboot.efi rule).
# This binutils exposes it as pei-aarch64-little (not efi-app-aarch64).
EFI_OBJCOPY_TARGET=pei-aarch64-little
# --allow-multiple-definition: gnu-efi's libefi init.o (pulled in for
# InitializeLib) also defines memset/memcpy; wolfBoot's src/string.o comes
# first in link order and wins.
LDFLAGS = -shared -Bsymbolic --allow-multiple-definition \
-L$(GNU_EFI_LIB_PATH) -T$(GNU_EFI_LSCRIPT)
LD_START_GROUP = $(GNU_EFI_CRT0)
LD_END_GROUP = -lgnuefi -lefi
LD = $(CROSS_COMPILE)ld
UPDATE_OBJS:=src/update_ram.o
endif

ifeq ($(ARCH),sim)
USE_GCC_HEADLESS=0
LD = gcc
Expand Down Expand Up @@ -1914,6 +1956,19 @@ endif

## Update mechanism
ifeq ($(ARCH),AARCH64)
ifeq ($(TARGET),aarch64_efi)
# UEFI app: UEFI owns MMU/FDT, so skip the -DMMU/-DWOLFBOOT_FDT DTS path and
# fdt.o/gpt.o (like x86_64_efi). update_ram.o is set in the block above.
# DEBUG=1: route wolfBoot_printf to the UEFI console (gnu-efi Print).
ifeq ($(DEBUG),1)
CFLAGS += -DWOLFBOOT_DEBUG_EFI=1
endif
# Drop -Werror for this target: WOLFBOOT_DEBUG_EFI pulls gnu-efi headers into
# every TU and efidebug.h redefines the -DDEBUG object macro as a function
# macro. That cpp macro-redefinition warning has no -W name, so it can't be
# scoped with -Wno-error=<name>; our own sources are kept warning-clean.
CFLAGS := $(filter-out -Werror,$(CFLAGS))
else
CFLAGS+=-DMMU -DWOLFBOOT_FDT -DWOLFBOOT_DUALBOOT
OBJS+=src/fdt.o
# src/gpt.c provides the CRC32 helpers reused by update_ram.c's uImage
Expand All @@ -1934,6 +1989,7 @@ ifeq ($(ARCH),AARCH64)
# RAM-based boot from external flash (default)
UPDATE_OBJS:=src/update_ram.o
endif
endif
else
ifeq ($(DUALBANK_SWAP),1)
CFLAGS+=-DWOLFBOOT_DUALBOOT
Expand Down
25 changes: 25 additions & 0 deletions config/examples/aarch64_efi.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# AArch64 UEFI application - wolfBoot as a UEFI app (the AArch64 sibling of the
# x86_64_efi target). Validated on the NVIDIA Jetson Orin Nano (Tegra234).
#
# wolfBoot builds as an AArch64 UEFI application (wolfboot.efi) launched by the
# platform UEFI firmware. It reads kernel.img/update.img from the EFI Simple
# File System, verifies them, and boots via UEFI LoadImage/StartImage.
# Modeled on config/examples/x86_64_efi.config.
#
# Prerequisite: build gnu-efi for AArch64 first (one-time):
# ./tools/scripts/build-gnu-efi-aarch64.sh
# then: cp config/examples/aarch64_efi.config .config && make
#
# Crypto starts on ED25519/SHA256 (matches the proven x86 EFI target) to
# de-risk the first build. Switch to ECC384/SHA384 once the port builds and
# runs: set SIGN?=ECC384, HASH?=SHA384, SPMATH=1 (pulls in SP math objects).
ARCH=AARCH64
TARGET=aarch64_efi
WOLFBOOT_SMALL_STACK=1
SIGN?=ED25519
HASH?=SHA256
DEBUG=1
SPMATH=0
# required for keytools
WOLFBOOT_SECTOR_SIZE?=0x1000
WOLFBOOT_NO_PARTITIONS=1
110 changes: 110 additions & 0 deletions docs/Targets.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ This README describes configuration of supported targets.
* [NXP T10xx PPC (T1024 / T1040)](#nxp-qoriq-t10xx-ppc-t1024--t1040)
* [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)
* [Raspberry Pi pico 2 (rp2350)](#raspberry-pi-pico-rp2350)
* [Renesas RA6M4](#renesas-ra6m4)
* [Renesas RX65N](#renesas-rx65n)
Expand Down Expand Up @@ -7785,6 +7786,115 @@ You can `Ctrl-C` or login as `root` and power off qemu with `poweroff`



## NVIDIA Jetson Orin (aarch64_efi)

The `aarch64_efi` target builds wolfBoot as an AArch64 UEFI application (`wolfboot.efi`), the direct counterpart of the [Qemu x86-64 UEFI](#qemu-x86-64-uefi) target. It uses only UEFI Boot Services (no SoC-specific registers), so the same binary runs on any AArch64 UEFI platform. It has been validated on the NVIDIA Jetson Orin Nano Developer Kit (Tegra234), where the on-module UEFI firmware (edk2-nvidia) launches it after the NVIDIA-signed early boot chain (BootROM -> MB1 -> MB2 -> UEFI). wolfBoot reads the next-stage image from the EFI Simple File System, authenticates it with wolfCrypt, and boots it via the UEFI `LoadImage`/`StartImage` services (an AArch64 Linux `Image` is itself a PE/COFF EFI-stub application).

### Prerequisites

* An AArch64 GNU toolchain (`aarch64-linux-gnu-gcc`)
* gnu-efi built for AArch64 (the host distro package is usually x86-only, so build it with the helper script below)
* For emulation: `qemu-system-aarch64` plus the AArch64 UEFI firmware (AAVMF, package `qemu-efi-aarch64`)

On a debian-like system:

```
apt install git make gcc-aarch64-linux-gnu dosfstools mtools
apt install qemu-system-arm qemu-efi-aarch64 # emulation (optional)
```

Build the AArch64 gnu-efi runtime once (installs into `tools/gnu-efi-aarch64/`):

```
./tools/scripts/build-gnu-efi-aarch64.sh
```

### Configuration

An example configuration is provided in [config/examples/aarch64_efi.config](config/examples/aarch64_efi.config). It selects `ARCH=AARCH64`, `TARGET=aarch64_efi`, and a signature/hash algorithm (ED25519/SHA256 by default). No partition addresses are required -- UEFI provides the storage and dynamic image placement.

### Building

```
cp config/examples/aarch64_efi.config .config
make
```

This produces `wolfboot.efi`, a PE32+ AArch64 EFI application (objcopy output format `pei-aarch64-little`).

### Signing a payload

Sign the image to boot (an AArch64 Linux `Image`, or any EFI application for testing) with the generated key, tagging it with a version. wolfBoot looks for `kernel.img` and `update.img` on the volume it was launched from and boots the higher valid version:

```
./tools/keytools/sign --ed25519 --sha256 Image wolfboot_signing_private_key.der 1
cp Image_v1_signed.bin kernel.img
```

### Running in QEMU

```
./tools/scripts/aarch64-efi-qemu.sh # add --gdb to debug with gdb-multiarch
```

The script exposes a scratch directory to the UEFI firmware as a FAT ESP, copies `wolfboot.efi` (and `kernel.img` from `aarch64_efi-stage/` if present), and auto-runs it.

### Deploying on the Jetson Orin Nano

The Jetson UEFI auto-boots removable media via `\EFI\BOOT\BOOTAA64.EFI`. Place wolfBoot and a signed payload on a FAT32 partition:

```
\EFI\BOOT\BOOTAA64.EFI <- wolfboot.efi
\kernel.img <- signed payload (read from the volume root)
\cmdline.txt <- optional Linux kernel command line (see below)
```

Insert the card and power on; UEFI auto-launches wolfBoot, which verifies and boots the payload. The debug console on the Orin Nano Developer Kit is the J14 button header (not the 40-pin), 115200 8N1. Example output:

```
Image base: 0x25E5D4000
Opening file: kernel.img, size: 57969
Checking integrity...done
Verifying signature...done
Successfully selected image in part: 0
Firmware Valid
Booting at 0x5E254100
Staging kernel at address 5E254100, size: 57969
```

### Booting Linux

An AArch64 Linux `Image` carries a PE/COFF EFI stub, so wolfBoot boots it with the same `LoadImage`/`StartImage` path used above -- no initrd or bare-metal handoff is needed when the kernel has built-in MMC/ext4 drivers (the NVIDIA L4T kernel does). This has been validated end to end on the Jetson Orin Nano: wolfBoot verifies the signed kernel and boots NVIDIA Jetson Linux (L4T R36.4.4, `5.15.148-tegra`) all the way to an Ubuntu 22.04 login prompt.

Obtain a Tegra234-compatible kernel and root filesystem from the [NVIDIA Jetson Linux (L4T)](https://developer.nvidia.com/embedded/jetson-linux) BSP. The driver package (`Jetson_Linux_R36.x.x_aarch64.tbz2`) contains `Linux_for_Tegra/kernel/Image` and `Linux_for_Tegra/kernel/dtb/tegra234-*.dtb`; the matching `Tegra_Linux_Sample-Root-Filesystem_*.tbz2` provides the rootfs.

Sign the kernel `Image` with the wolfBoot key and name it `kernel.img`:

```
./tools/keytools/sign --ed25519 --sha256 Linux_for_Tegra/kernel/Image \
wolfboot_signing_private_key.der 1
cp Linux_for_Tegra/kernel/Image_v1_signed.bin kernel.img
```

Lay out the microSD as a FAT ESP plus a rootfs partition and place:

```
FAT (p1): \EFI\BOOT\BOOTAA64.EFI <- wolfboot.efi (UEFI auto-boots this)
\kernel.img <- signed L4T kernel
\cmdline.txt <- kernel command line (below)
ext4 (p2): the L4T sample root filesystem
```

`cmdline.txt` (read by wolfBoot and passed to the kernel via `LoadOptions`):

```
root=/dev/mmcblk0p2 rw rootwait console=ttyTCU0,115200
```

On power-up the Jetson UEFI auto-boots `\EFI\BOOT\BOOTAA64.EFI`; wolfBoot verifies `kernel.img` and hands off to the kernel, which receives the real Tegra234 device tree from the UEFI configuration table (`EFI stub: Using DTB from configuration table`), mounts `mmcblk0p2`, and brings up systemd and the login on the J14 debug console (`ttyTCU0`).

Security note: the plaintext `\cmdline.txt` is not covered by the image signature. For a production trust chain the command line should be authenticated -- baked into the signed image, or supplied via the device-tree `/chosen` bootargs. An initramfs-based flow (rather than a direct `root=` mount) would additionally need initrd support via the `LINUX_EFI_INITRD_MEDIA_GUID` LoadFile2 protocol, which this target does not currently implement.

## Intel x86_64 with Intel FSP support

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