Skip to content

Commit f13a354

Browse files
committed
Add aarch64_efi target: wolfBoot as an AArch64 UEFI application
1 parent ff7fa35 commit f13a354

13 files changed

Lines changed: 747 additions & 4 deletions

File tree

.github/workflows/test-build.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ on:
1313
make-args:
1414
required: false
1515
type: string
16+
prep-run:
17+
required: false
18+
type: string
1619

1720
jobs:
1821

@@ -42,6 +45,10 @@ jobs:
4245
run: |
4346
make -C tools/keytools && make -C tools/bin-assemble
4447
48+
- name: Pre-build setup
49+
if: inputs.prep-run != ''
50+
run: ${{inputs.prep-run}}
51+
4552
- name: Build wolfboot
4653
run: |
4754
make ${{inputs.make-args}}

.github/workflows/test-configs.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,14 @@ jobs:
738738
arch: host
739739
config-file: ./config/examples/x86_64_efi.config
740740

741+
aarch64_efi_test:
742+
uses: ./.github/workflows/test-build.yml
743+
with:
744+
arch: aarch64
745+
config-file: ./config/examples/aarch64_efi.config
746+
# Build gnu-efi for aarch64 (pinned in the script) before wolfboot.efi.
747+
prep-run: ./tools/scripts/build-gnu-efi-aarch64.sh
748+
741749
zynqmp_test:
742750
uses: ./.github/workflows/test-build-aarch64.yml
743751
with:

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,3 +421,10 @@ sdcard.img
421421

422422
# wolfHSM STM32H5 TZ demo build output
423423
port/stmicro/stm32h5-tz-wolfhsm/out/
424+
425+
# gnu-efi built for the AArch64 UEFI target and its cloned source
426+
tools/gnu-efi-aarch64/
427+
tools/gnu-efi-src/
428+
# aarch64_efi working/staging dirs (build artifacts, not tracked)
429+
aarch64_efi-stage/
430+
tools/qemu-esp/

Makefile

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,9 @@ SIGN_ENV=IMAGE_HEADER_SIZE=$(IMAGE_HEADER_SIZE) \
265265

266266

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

270273
ifeq ($(TZEN),1)
@@ -293,6 +296,10 @@ ifeq ($(TARGET),x86_64_efi)
293296
MAIN_TARGET:=wolfboot.efi
294297
endif
295298

299+
ifeq ($(TARGET),aarch64_efi)
300+
MAIN_TARGET:=wolfboot.efi
301+
endif
302+
296303
ifeq ($(FSP), 1)
297304
MAIN_TARGET:=wolfboot_stage1.bin
298305
endif
@@ -379,7 +386,7 @@ wolfboot.efi: wolfboot.elf
379386
$(Q)$(OBJCOPY) -j .rodata -j .text -j .sdata -j .data \
380387
-j .dynamic -j .dynsym -j .rel \
381388
-j .rela -j .reloc -j .eh_frame \
382-
-O pei-x86-64 --subsystem=10 $^ $@
389+
-O $(EFI_OBJCOPY_TARGET) --subsystem=10 $^ $@
383390
@echo
384391
@echo "\t[SIZE]"
385392
$(Q)$(SIZE) wolfboot.efi

arch.mk

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,20 @@ endif
6363

6464
## ARM Cortex-A
6565
ifeq ($(ARCH),AARCH64)
66-
CROSS_COMPILE?=aarch64-none-elf-
66+
ifeq ($(TARGET),aarch64_efi)
67+
# UEFI app: Linux GNU toolchain (freestanding EFI ABI), not aarch64-none-elf-
68+
CROSS_COMPILE?=aarch64-linux-gnu-
69+
else
70+
CROSS_COMPILE?=aarch64-none-elf-
71+
endif
6772
CFLAGS+=-DARCH_AARCH64 -DFAST_MEMCPY
68-
OBJS+=src/boot_aarch64.o src/boot_aarch64_start.o
73+
ifeq ($(TARGET),aarch64_efi)
74+
# UEFI app: gnu-efi CRT0 is the entry; do_boot is in boot_aarch64_efi.o.
75+
# Skip the bare-metal reset (boot_aarch64_start.S) and EL2/GIC glue.
76+
OBJS+=src/boot_aarch64_efi.o
77+
else
78+
OBJS+=src/boot_aarch64.o src/boot_aarch64_start.o
79+
endif
6980

7081
ifeq ($(TARGET),zynq)
7182
ARCH_FLAGS=-march=armv8-a+crypto
@@ -1664,6 +1675,37 @@ ifeq ($(TARGET),x86_64_efi)
16641675
UPDATE_OBJS:=src/update_ram.o
16651676
endif
16661677

1678+
ifeq ($(TARGET),aarch64_efi)
1679+
# Generic AArch64 UEFI application (validated on NVIDIA Jetson Orin Nano).
1680+
# Build gnu-efi for aarch64 first: ./tools/scripts/build-gnu-efi-aarch64.sh
1681+
# (override the install path with GNU_EFI_PATH=... if needed).
1682+
USE_GCC_HEADLESS=0
1683+
GNU_EFI_PATH?=tools/gnu-efi-aarch64
1684+
GNU_EFI_LIB_PATH?=$(GNU_EFI_PATH)/lib
1685+
GNU_EFI_INC_PATH?=$(GNU_EFI_PATH)/include
1686+
GNU_EFI_CRT0=$(GNU_EFI_LIB_PATH)/crt0-efi-aarch64.o
1687+
GNU_EFI_LSCRIPT=$(GNU_EFI_LIB_PATH)/elf_aarch64_efi.lds
1688+
CFLAGS += -fpic -ffreestanding -fno-stack-protector -fno-stack-check \
1689+
-fshort-wchar -mstrict-align
1690+
CFLAGS += -I$(GNU_EFI_INC_PATH) -I$(GNU_EFI_INC_PATH)/efi \
1691+
-I$(GNU_EFI_INC_PATH)/efi/aarch64 \
1692+
-DTARGET_aarch64_efi -DWOLFBOOT_DUALBOOT
1693+
# avoid using of fixed LOAD_ADDRESS, uefi target uses dynamic location
1694+
CFLAGS += -DWOLFBOOT_NO_LOAD_ADDRESS
1695+
# AArch64 PE/COFF output format for objcopy (see the wolfboot.efi rule).
1696+
# This binutils exposes it as pei-aarch64-little (not efi-app-aarch64).
1697+
EFI_OBJCOPY_TARGET=pei-aarch64-little
1698+
# --allow-multiple-definition: gnu-efi's libefi init.o (pulled in for
1699+
# InitializeLib) also defines memset/memcpy; wolfBoot's src/string.o comes
1700+
# first in link order and wins.
1701+
LDFLAGS = -shared -Bsymbolic --allow-multiple-definition \
1702+
-L$(GNU_EFI_LIB_PATH) -T$(GNU_EFI_LSCRIPT)
1703+
LD_START_GROUP = $(GNU_EFI_CRT0)
1704+
LD_END_GROUP = -lgnuefi -lefi
1705+
LD = $(CROSS_COMPILE)ld
1706+
UPDATE_OBJS:=src/update_ram.o
1707+
endif
1708+
16671709
ifeq ($(ARCH),sim)
16681710
USE_GCC_HEADLESS=0
16691711
LD = gcc
@@ -1914,6 +1956,19 @@ endif
19141956

19151957
## Update mechanism
19161958
ifeq ($(ARCH),AARCH64)
1959+
ifeq ($(TARGET),aarch64_efi)
1960+
# UEFI app: UEFI owns MMU/FDT, so skip the -DMMU/-DWOLFBOOT_FDT DTS path and
1961+
# fdt.o/gpt.o (like x86_64_efi). update_ram.o is set in the block above.
1962+
# DEBUG=1: route wolfBoot_printf to the UEFI console (gnu-efi Print).
1963+
ifeq ($(DEBUG),1)
1964+
CFLAGS += -DWOLFBOOT_DEBUG_EFI=1
1965+
endif
1966+
# Drop -Werror for this target: WOLFBOOT_DEBUG_EFI pulls gnu-efi headers into
1967+
# every TU and efidebug.h redefines the -DDEBUG object macro as a function
1968+
# macro. That cpp macro-redefinition warning has no -W name, so it can't be
1969+
# scoped with -Wno-error=<name>; our own sources are kept warning-clean.
1970+
CFLAGS := $(filter-out -Werror,$(CFLAGS))
1971+
else
19171972
CFLAGS+=-DMMU -DWOLFBOOT_FDT -DWOLFBOOT_DUALBOOT
19181973
OBJS+=src/fdt.o
19191974
# src/gpt.c provides the CRC32 helpers reused by update_ram.c's uImage
@@ -1934,6 +1989,7 @@ ifeq ($(ARCH),AARCH64)
19341989
# RAM-based boot from external flash (default)
19351990
UPDATE_OBJS:=src/update_ram.o
19361991
endif
1992+
endif
19371993
else
19381994
ifeq ($(DUALBANK_SWAP),1)
19391995
CFLAGS+=-DWOLFBOOT_DUALBOOT

config/examples/aarch64_efi.config

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# AArch64 UEFI application - wolfBoot as a UEFI app (the AArch64 sibling of the
2+
# x86_64_efi target). Validated on the NVIDIA Jetson Orin Nano (Tegra234).
3+
#
4+
# wolfBoot builds as an AArch64 UEFI application (wolfboot.efi) launched by the
5+
# platform UEFI firmware. It reads kernel.img/update.img from the EFI Simple
6+
# File System, verifies them, and boots via UEFI LoadImage/StartImage.
7+
# Modeled on config/examples/x86_64_efi.config.
8+
#
9+
# Prerequisite: build gnu-efi for AArch64 first (one-time):
10+
# ./tools/scripts/build-gnu-efi-aarch64.sh
11+
# then: cp config/examples/aarch64_efi.config .config && make
12+
#
13+
# Crypto starts on ED25519/SHA256 (matches the proven x86 EFI target) to
14+
# de-risk the first build. Switch to ECC384/SHA384 once the port builds and
15+
# runs: set SIGN?=ECC384, HASH?=SHA384, SPMATH=1 (pulls in SP math objects).
16+
ARCH=AARCH64
17+
TARGET=aarch64_efi
18+
WOLFBOOT_SMALL_STACK=1
19+
SIGN?=ED25519
20+
HASH?=SHA256
21+
DEBUG=1
22+
SPMATH=0
23+
# required for keytools
24+
WOLFBOOT_SECTOR_SIZE?=0x1000
25+
WOLFBOOT_NO_PARTITIONS=1

docs/Targets.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ This README describes configuration of supported targets.
3333
* [NXP T10xx PPC (T1024 / T1040)](#nxp-qoriq-t10xx-ppc-t1024--t1040)
3434
* [NXP T2080 PPC](#nxp-qoriq-t2080-ppc)
3535
* [Qemu x86-64 UEFI](#qemu-x86-64-uefi)
36+
* [NVIDIA Jetson Orin (aarch64_efi)](#nvidia-jetson-orin-aarch64_efi)
3637
* [Raspberry Pi pico 2 (rp2350)](#raspberry-pi-pico-rp2350)
3738
* [Renesas RA6M4](#renesas-ra6m4)
3839
* [Renesas RX65N](#renesas-rx65n)
@@ -7785,6 +7786,115 @@ You can `Ctrl-C` or login as `root` and power off qemu with `poweroff`
77857786
77867787
77877788
7789+
## NVIDIA Jetson Orin (aarch64_efi)
7790+
7791+
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).
7792+
7793+
### Prerequisites
7794+
7795+
* An AArch64 GNU toolchain (`aarch64-linux-gnu-gcc`)
7796+
* gnu-efi built for AArch64 (the host distro package is usually x86-only, so build it with the helper script below)
7797+
* For emulation: `qemu-system-aarch64` plus the AArch64 UEFI firmware (AAVMF, package `qemu-efi-aarch64`)
7798+
7799+
On a debian-like system:
7800+
7801+
```
7802+
apt install git make gcc-aarch64-linux-gnu dosfstools mtools
7803+
apt install qemu-system-arm qemu-efi-aarch64 # emulation (optional)
7804+
```
7805+
7806+
Build the AArch64 gnu-efi runtime once (installs into `tools/gnu-efi-aarch64/`):
7807+
7808+
```
7809+
./tools/scripts/build-gnu-efi-aarch64.sh
7810+
```
7811+
7812+
### Configuration
7813+
7814+
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.
7815+
7816+
### Building
7817+
7818+
```
7819+
cp config/examples/aarch64_efi.config .config
7820+
make
7821+
```
7822+
7823+
This produces `wolfboot.efi`, a PE32+ AArch64 EFI application (objcopy output format `pei-aarch64-little`).
7824+
7825+
### Signing a payload
7826+
7827+
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:
7828+
7829+
```
7830+
./tools/keytools/sign --ed25519 --sha256 Image wolfboot_signing_private_key.der 1
7831+
cp Image_v1_signed.bin kernel.img
7832+
```
7833+
7834+
### Running in QEMU
7835+
7836+
```
7837+
./tools/scripts/aarch64-efi-qemu.sh # add --gdb to debug with gdb-multiarch
7838+
```
7839+
7840+
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.
7841+
7842+
### Deploying on the Jetson Orin Nano
7843+
7844+
The Jetson UEFI auto-boots removable media via `\EFI\BOOT\BOOTAA64.EFI`. Place wolfBoot and a signed payload on a FAT32 partition:
7845+
7846+
```
7847+
\EFI\BOOT\BOOTAA64.EFI <- wolfboot.efi
7848+
\kernel.img <- signed payload (read from the volume root)
7849+
\cmdline.txt <- optional Linux kernel command line (see below)
7850+
```
7851+
7852+
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:
7853+
7854+
```
7855+
Image base: 0x25E5D4000
7856+
Opening file: kernel.img, size: 57969
7857+
Checking integrity...done
7858+
Verifying signature...done
7859+
Successfully selected image in part: 0
7860+
Firmware Valid
7861+
Booting at 0x5E254100
7862+
Staging kernel at address 5E254100, size: 57969
7863+
```
7864+
7865+
### Booting Linux
7866+
7867+
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.
7868+
7869+
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.
7870+
7871+
Sign the kernel `Image` with the wolfBoot key and name it `kernel.img`:
7872+
7873+
```
7874+
./tools/keytools/sign --ed25519 --sha256 Linux_for_Tegra/kernel/Image \
7875+
wolfboot_signing_private_key.der 1
7876+
cp Linux_for_Tegra/kernel/Image_v1_signed.bin kernel.img
7877+
```
7878+
7879+
Lay out the microSD as a FAT ESP plus a rootfs partition and place:
7880+
7881+
```
7882+
FAT (p1): \EFI\BOOT\BOOTAA64.EFI <- wolfboot.efi (UEFI auto-boots this)
7883+
\kernel.img <- signed L4T kernel
7884+
\cmdline.txt <- kernel command line (below)
7885+
ext4 (p2): the L4T sample root filesystem
7886+
```
7887+
7888+
`cmdline.txt` (read by wolfBoot and passed to the kernel via `LoadOptions`):
7889+
7890+
```
7891+
root=/dev/mmcblk0p2 rw rootwait console=ttyTCU0,115200
7892+
```
7893+
7894+
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`).
7895+
7896+
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.
7897+
77887898
## Intel x86_64 with Intel FSP support
77897899
77907900
This setup is more complex than the UEFI approach described earlier, but allows

0 commit comments

Comments
 (0)