mirror of https://github.com/wolfSSL/wolfBoot.git
Add aarch64_efi target: wolfBoot as an AArch64 UEFI application
parent
0dfad2dbc1
commit
34941079e1
|
|
@ -13,6 +13,9 @@ on:
|
|||
make-args:
|
||||
required: false
|
||||
type: string
|
||||
prep-run:
|
||||
required: false
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
|
||||
|
|
@ -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}}
|
||||
|
|
|
|||
|
|
@ -764,6 +764,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:
|
||||
|
|
|
|||
|
|
@ -427,3 +427,11 @@ port/stmicro/stm32h5-tz-wolfhsm/out/
|
|||
|
||||
# CI-only generated Libero settings stub (see tools/ci/gen_mpfs_libero_stub.sh)
|
||||
tools/ci/mpfs_libero_stub/
|
||||
# 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/
|
||||
# UEFI Secure Boot keys/certs generated by tools/scripts/sign-efi-secureboot.sh
|
||||
tools/efi-secureboot-keys/
|
||||
|
|
|
|||
9
Makefile
9
Makefile
|
|
@ -313,6 +313,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)
|
||||
|
|
@ -345,6 +348,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
|
||||
|
|
@ -431,7 +438,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
|
||||
|
|
|
|||
60
arch.mk
60
arch.mk
|
|
@ -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
|
||||
|
|
@ -1933,6 +1944,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
|
||||
|
|
@ -2193,6 +2235,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
|
||||
|
|
@ -2213,6 +2268,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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
166
docs/Targets.md
166
docs/Targets.md
|
|
@ -34,6 +34,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)
|
||||
* [RealTek RTL8735B (AmebaPro2)](#realtek-rtl8735b-amebapro2)
|
||||
* [Renesas RA6M4](#renesas-ra6m4)
|
||||
|
|
@ -7783,6 +7784,171 @@ 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.
|
||||
|
||||
### Root of trust: enrolling wolfBoot into UEFI Secure Boot
|
||||
|
||||
The steps above give wolfBoot verifying the kernel. To close the remaining gap -- the firmware verifying `wolfboot.efi` itself -- enroll wolfBoot into UEFI Secure Boot so the platform refuses to launch an unsigned or tampered `wolfboot.efi`. This makes the chain continuous: firmware -> `wolfboot.efi` -> kernel.
|
||||
|
||||
`tools/scripts/sign-efi-secureboot.sh` generates a Platform Key (PK), Key Exchange Key (KEK) and signature-database (db) key/cert, signs `wolfboot.efi` with the db key, and emits the signed variable updates (`.auth`) for enrollment:
|
||||
|
||||
```
|
||||
./tools/scripts/sign-efi-secureboot.sh wolfboot.efi
|
||||
```
|
||||
|
||||
This produces `wolfboot.efi.signed` and, under `tools/efi-secureboot-keys/`, the `PK`/`KEK`/`db` key and cert plus the signed variable updates (`db.auth`, `KEK.auth`, `PK.auth`). (It requires `sbsigntool`, `efitools`, and `openssl`; `sbsign` may print benign "gaps between PE/COFF sections" warnings for gnu-efi images -- signing and `sbverify` still pass.)
|
||||
|
||||
The Jetson edk2 UEFI Shell `setvar` cannot enroll authenticated variables (it has no file input and cannot set the time-based-authenticated attribute), so enroll from the UEFI **setup menu**, which reads the certificate files from the ESP. The menu's "Enroll ... Using File" expects DER-encoded certificates; convert the generated PEM certs once:
|
||||
|
||||
```
|
||||
cd tools/efi-secureboot-keys
|
||||
for k in PK KEK db; do openssl x509 -in $k.crt -outform DER -out $k.cer; done
|
||||
```
|
||||
|
||||
Copy `db.cer`, `KEK.cer`, `PK.cer` and `wolfboot.efi.signed` onto the ESP (deploy the signed image as `\EFI\BOOT\BOOTAA64.EFI`). Name the cert files with a **lowercase** `.cer` extension: the edk2 file explorer decides a file is a certificate by a case-sensitive suffix match against `.cer`/`.der`/`.crt`, so an uppercase `DB.CER` (as FAT stores a plain 8.3 name) is rejected with "Unsupported file type!". Copy them with lowercase names (e.g. `mcopy db.cer ::/db.cer`) so the VFAT lowercase flag is set. Then, on the board (over the serial console -- use `minicom` or `screen`, not a plain pass-through, so the full-screen menu renders):
|
||||
|
||||
1. Reset and press `ESC` at the firmware banner (`ESC to enter Setup`) to enter Setup.
|
||||
2. `Device Manager` -> `Secure Boot Configuration`.
|
||||
3. Set `Secure Boot Mode` -> `Custom Mode` (reveals `Custom Secure Boot Options`).
|
||||
4. Under `Custom Secure Boot Options`, enroll in order -- `db`, then `KEK`, then `PK` (enrolling PK is what turns Secure Boot enforcing and exits Setup Mode):
|
||||
- `DB Options` -> `Enroll Signature` -> `Enroll Signature Using File` -> select `db.cer` -> accept the default signature-owner GUID -> `Commit Changes and Exit`.
|
||||
- `KEK Options` -> `Enroll KEK` -> `Enroll KEK Using File` -> `KEK.cer` -> `Commit Changes and Exit`.
|
||||
- `PK Options` -> `Enroll PK` -> `Enroll PK Using File` -> `PK.cer` -> `Commit Changes and Exit`.
|
||||
5. Confirm `Current Secure Boot State: Enabled`, then reset.
|
||||
|
||||
On reboot the firmware verifies `\EFI\BOOT\BOOTAA64.EFI` against `db`, so the signed wolfBoot launches (`Verifying signature...done`). To prove enforcement, replace `BOOTAA64.EFI` with an unsigned `wolfboot.efi`; the firmware refuses it with a Security Violation. Secure Boot is fully reversible: in the same menu set `Secure Boot Mode` -> `Standard Mode`, or delete the PK, to return the board to Setup Mode.
|
||||
|
||||
Important -- sign the kernel for Secure Boot too. wolfBoot boots the kernel through the UEFI `LoadImage`/`StartImage` services, and with Secure Boot enforcing, `LoadImage` also verifies the kernel image against `db`. A kernel that is only wolfBoot-signed is rejected by `LoadImage` (wolfBoot prints `LoadImage failed: 0x<status>`), even though wolfBoot's own `Verifying signature...done` passed. Sign the raw `Image` with the db key (sbsign) first, then wolfBoot-sign the result, so the kernel is trusted by both UEFI (`db`, for `LoadImage`) and wolfBoot (wolfCrypt):
|
||||
|
||||
```
|
||||
sbsign --key tools/efi-secureboot-keys/db.key --cert tools/efi-secureboot-keys/db.crt \
|
||||
--output Image.sb Linux_for_Tegra/kernel/Image
|
||||
./tools/keytools/sign --ed25519 --sha256 Image.sb wolfboot_signing_private_key.der 1
|
||||
cp Image_v1_signed.bin kernel.img # note: sign drops the .sb, output is Image_v1_signed.bin
|
||||
```
|
||||
|
||||
The full enforced chain is then firmware -> (Secure Boot) -> wolfBoot -> (wolfCrypt + Secure Boot) -> kernel -> Linux.
|
||||
|
||||
Test this without hardware first: the QEMU + AAVMF helper (`tools/scripts/aarch64-efi-qemu.sh`) uses OVMF/AAVMF variable storage that supports enrolling the same keys, so you can confirm the signed binary launches and a wrong-key binary is refused before touching the board.
|
||||
|
||||
Note on the Jetson dev kit: the OP-TEE console prints "Test UEFI variable auth key is being used" / "UEFI variable protection is not fully enabled", i.e. the variable store is in a development state (`SetupMode=1`), which is why menu enrollment works without a prior platform key. A production device would additionally fuse-lock the variable store (see below).
|
||||
|
||||
### Root of trust: NVIDIA fuse provisioning (production; not performed here)
|
||||
|
||||
UEFI Secure Boot above is enforced by the edk2 firmware. On a production Jetson the firmware chain (BootROM -> MB1 -> MB2 -> cpu-bootloader/UEFI) is itself made tamper-resistant by burning the NVIDIA security fuses from the L4T BSP:
|
||||
|
||||
- PKC (public-key crypto) fuses: burn the SHA of your signing public key so the BootROM only accepts an NVIDIA-signing-chain that you control.
|
||||
- SBK (secure boot key) fuses: optionally encrypt the boot binaries.
|
||||
|
||||
These are burned with `odmfuse.sh`/`tegrasign` from `Linux_for_Tegra/` and are irreversible. They are the final production step and are intentionally NOT part of this port -- the development board stays in unfused/dev mode. Consult the NVIDIA Jetson Linux "Secure Boot" documentation for the current `odmfuse.sh` procedure for your module before burning anything. Once fused, the fused firmware enforces UEFI Secure Boot, which enforces `wolfboot.efi`, which enforces the kernel -- a complete hardware root of trust.
|
||||
|
||||
## Intel x86_64 with Intel FSP support
|
||||
|
||||
This setup is more complex than the UEFI approach described earlier, but allows
|
||||
|
|
|
|||
|
|
@ -0,0 +1,411 @@
|
|||
/* aarch64_efi.c
|
||||
*
|
||||
* Copyright (C) 2026 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfBoot.
|
||||
*
|
||||
* wolfBoot is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfBoot is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
||||
*/
|
||||
|
||||
/* Generic AArch64 UEFI-application HAL (validated on NVIDIA Jetson Orin Nano,
|
||||
* Tegra234 / Cortex-A78AE).
|
||||
*
|
||||
* wolfBoot runs here as an AArch64 UEFI application (wolfboot.efi), launched
|
||||
* by the platform UEFI firmware. It reads the kernel image from the EFI
|
||||
* Simple File System, verifies it with wolfCrypt, and hands off to it via
|
||||
* the UEFI LoadImage/StartImage services (the AArch64 Linux Image is itself
|
||||
* a PE/COFF EFI-stub application). UEFI owns DRAM/MMU/GIC state, so this HAL
|
||||
* needs no bare-metal init; the flash ops are stubs and storage/console come
|
||||
* from UEFI Boot Services. This is the AArch64 sibling of hal/x86_64_efi.c. */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <target.h>
|
||||
|
||||
#include "image.h"
|
||||
#include "loader.h"
|
||||
#include "printf.h"
|
||||
|
||||
#ifdef TARGET_aarch64_efi
|
||||
|
||||
/* -DDEBUG (object macro from DEBUG=1) collides with gnu-efi efidebug.h's
|
||||
* DEBUG(a); drop it before the EFI headers (unused here). */
|
||||
#undef DEBUG
|
||||
|
||||
#include <efi/efi.h>
|
||||
#include <efi/efilib.h>
|
||||
|
||||
#ifdef __WOLFBOOT
|
||||
void hal_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
void hal_prepare_boot(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#define PAGE_SIZE 0x1000
|
||||
#define EFI_DEVICE_PATH_PROTOCOL_HW_TYPE 0x01
|
||||
#define EFI_DEVICE_PATH_PROTOCOL_MEM_SUBTYPE 0x03
|
||||
|
||||
static EFI_SYSTEM_TABLE *gSystemTable;
|
||||
static EFI_HANDLE gImageHandle;
|
||||
EFI_PHYSICAL_ADDRESS kernel_addr;
|
||||
EFI_PHYSICAL_ADDRESS update_addr;
|
||||
|
||||
/* Optional Linux kernel command line, read from \cmdline.txt on the ESP and
|
||||
* handed to the kernel EFI stub via LoadOptions (see aarch64_efi_do_boot). */
|
||||
static CHAR16 *kernel_cmdline = NULL;
|
||||
static UINTN kernel_cmdline_bytes = 0;
|
||||
|
||||
int RAMFUNCTION hal_flash_write(uintptr_t address, const uint8_t *data, int len)
|
||||
{
|
||||
(void)address; (void)data; (void)len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RAMFUNCTION hal_flash_unlock(void)
|
||||
{
|
||||
}
|
||||
|
||||
void RAMFUNCTION hal_flash_lock(void)
|
||||
{
|
||||
}
|
||||
|
||||
int RAMFUNCTION hal_flash_erase(uintptr_t address, int len)
|
||||
{
|
||||
(void)address; (void)len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* hal_get_primary_address(void)
|
||||
{
|
||||
return (void*)kernel_addr;
|
||||
}
|
||||
|
||||
void* hal_get_update_address(void)
|
||||
{
|
||||
return (void*)update_addr;
|
||||
}
|
||||
|
||||
void *hal_get_dts_address(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *hal_get_dts_update_address(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void panic()
|
||||
{
|
||||
while(1) {}
|
||||
}
|
||||
|
||||
void RAMFUNCTION aarch64_efi_do_boot(const uint32_t *boot_addr)
|
||||
{
|
||||
const uint32_t *size;
|
||||
const uint8_t* manifest = ((const uint8_t*)boot_addr) - IMAGE_HEADER_SIZE;
|
||||
MEMMAP_DEVICE_PATH mem_path_device[2];
|
||||
EFI_HANDLE kernelImageHandle;
|
||||
EFI_STATUS status;
|
||||
EFI_LOADED_IMAGE *kernel_li = NULL;
|
||||
EFI_GUID lipGuid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
|
||||
|
||||
size = (const uint32_t *)(manifest + 4);
|
||||
|
||||
/* Guard against a zero-size image: EndingAddress below would underflow and
|
||||
* an empty range would be handed to LoadImage (and the TCG2 measurement). */
|
||||
if (*size == 0) {
|
||||
wolfBoot_printf("invalid zero-size image\n");
|
||||
panic();
|
||||
}
|
||||
|
||||
mem_path_device->Header.Type = EFI_DEVICE_PATH_PROTOCOL_HW_TYPE;
|
||||
mem_path_device->Header.SubType = EFI_DEVICE_PATH_PROTOCOL_MEM_SUBTYPE;
|
||||
mem_path_device->MemoryType = EfiLoaderData;
|
||||
mem_path_device->StartingAddress = (EFI_PHYSICAL_ADDRESS)(uintptr_t)boot_addr;
|
||||
/* MEMMAP_DEVICE_PATH EndingAddress is inclusive (last valid byte). */
|
||||
mem_path_device->EndingAddress =
|
||||
(EFI_PHYSICAL_ADDRESS)((uintptr_t)boot_addr + *size - 1);
|
||||
SetDevicePathNodeLength(&mem_path_device->Header,
|
||||
sizeof(MEMMAP_DEVICE_PATH));
|
||||
|
||||
SetDevicePathEndNode(&mem_path_device[1].Header);
|
||||
|
||||
wolfBoot_printf("Staging kernel at address %p, size: %u\n", (void*)boot_addr, *size);
|
||||
status = uefi_call_wrapper(gSystemTable->BootServices->LoadImage,
|
||||
6,
|
||||
0, /* bool */
|
||||
gImageHandle,
|
||||
(EFI_DEVICE_PATH*)mem_path_device,
|
||||
(void*)(uintptr_t)boot_addr,
|
||||
*size,
|
||||
&kernelImageHandle);
|
||||
if (status != EFI_SUCCESS) {
|
||||
wolfBoot_printf("LoadImage failed: 0x%lx\n", (unsigned long)status);
|
||||
panic();
|
||||
}
|
||||
|
||||
/* Pass the kernel command line (from \cmdline.txt) to the loaded image via
|
||||
* LoadOptions for the Linux EFI stub. For a production trust chain the
|
||||
* cmdline should be authenticated (in the signed image or DT /chosen), not
|
||||
* a plaintext file. A direct root= boot needs no initrd; an initramfs flow
|
||||
* would add it via LINUX_EFI_INITRD_MEDIA_GUID (LoadFile2). */
|
||||
if (kernel_cmdline != NULL) {
|
||||
status = uefi_call_wrapper(gSystemTable->BootServices->HandleProtocol, 3,
|
||||
kernelImageHandle, &lipGuid,
|
||||
(void**)&kernel_li);
|
||||
if (status == EFI_SUCCESS && kernel_li != NULL) {
|
||||
kernel_li->LoadOptions = kernel_cmdline;
|
||||
kernel_li->LoadOptionsSize = (UINT32)kernel_cmdline_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
status = uefi_call_wrapper(gSystemTable->BootServices->StartImage,
|
||||
3,
|
||||
kernelImageHandle, 0, NULL);
|
||||
if (status != EFI_SUCCESS) {
|
||||
wolfBoot_printf("StartImage failed: 0x%lx\n", (unsigned long)status);
|
||||
panic();
|
||||
}
|
||||
}
|
||||
|
||||
static UINT64 FileSize(EFI_FILE_HANDLE FileHandle)
|
||||
{
|
||||
EFI_FILE_INFO *FileInfo;
|
||||
UINT64 ret;
|
||||
|
||||
FileInfo = LibFileInfo(FileHandle);
|
||||
if (FileInfo == NULL) {
|
||||
panic();
|
||||
return 0; /* Never reached, for static analyzer */
|
||||
}
|
||||
|
||||
ret = FileInfo->FileSize;
|
||||
FreePool(FileInfo);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static EFI_FILE_HANDLE GetVolume(EFI_HANDLE image)
|
||||
{
|
||||
EFI_GUID fsGuid = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
|
||||
EFI_GUID lipGuid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
|
||||
EFI_LOADED_IMAGE *loaded_image = NULL;
|
||||
EFI_FILE_IO_INTERFACE *IOVolume;
|
||||
EFI_FILE_HANDLE Volume;
|
||||
EFI_STATUS status;
|
||||
|
||||
status = uefi_call_wrapper(BS->HandleProtocol, 3,
|
||||
image, &lipGuid, (void **) &loaded_image);
|
||||
if (status != EFI_SUCCESS)
|
||||
panic();
|
||||
|
||||
status = uefi_call_wrapper(BS->HandleProtocol, 3,
|
||||
loaded_image->DeviceHandle,
|
||||
&fsGuid, (VOID*)&IOVolume);
|
||||
if (status != EFI_SUCCESS)
|
||||
panic();
|
||||
|
||||
status = uefi_call_wrapper(IOVolume->OpenVolume, 2, IOVolume, &Volume);
|
||||
|
||||
if (status != EFI_SUCCESS)
|
||||
panic();
|
||||
|
||||
return Volume;
|
||||
}
|
||||
|
||||
static EFI_FILE_HANDLE openFile(CHAR16 *file, EFI_FILE_HANDLE volume)
|
||||
{
|
||||
EFI_FILE_HANDLE file_handle;
|
||||
EFI_STATUS status;
|
||||
|
||||
/* Open() ignores Attributes for read-only access (they only apply with
|
||||
* EFI_FILE_MODE_CREATE); pass 0 so stricter firmware does not reject it. */
|
||||
status = uefi_call_wrapper(volume->Open, 5,
|
||||
volume,
|
||||
&file_handle,
|
||||
file,
|
||||
EFI_FILE_MODE_READ,
|
||||
0);
|
||||
|
||||
if (status != EFI_SUCCESS)
|
||||
file_handle = NULL;
|
||||
|
||||
return file_handle;
|
||||
}
|
||||
|
||||
static int open_kernel_image(EFI_FILE_HANDLE vol, CHAR16 *filename,
|
||||
EFI_PHYSICAL_ADDRESS *_addr, uint32_t *sz)
|
||||
{
|
||||
EFI_FILE_HANDLE file;
|
||||
EFI_STATUS status;
|
||||
UINTN readsz;
|
||||
UINTN pages;
|
||||
|
||||
/* Always leave *_addr well-defined so the caller's "no image" check is
|
||||
* reliable: 0 on any failure, the loaded address only on full success. */
|
||||
*_addr = 0;
|
||||
*sz = 0;
|
||||
file = openFile(filename, vol);
|
||||
if (file == NULL)
|
||||
return -1;
|
||||
|
||||
*sz = FileSize(file);
|
||||
pages = (*sz / PAGE_SIZE) + 1;
|
||||
wolfBoot_printf("Opening file: %s, size: %u\n", filename, *sz);
|
||||
status = uefi_call_wrapper(BS->AllocatePages,
|
||||
4,
|
||||
AllocateAnyPages,
|
||||
EfiLoaderData,
|
||||
pages, _addr);
|
||||
if (status != EFI_SUCCESS) {
|
||||
wolfBoot_printf("AllocatePages failed: 0x%lx\n", (unsigned long)status);
|
||||
*_addr = 0;
|
||||
uefi_call_wrapper(file->Close, 1, file);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* EFI_FILE Read() takes UINTN *BufferSize and VOID *Buffer. On AArch64
|
||||
* uefi_call_wrapper is a native passthrough (no arg casting), so pass the
|
||||
* correct types explicitly rather than a uint32_t pointer and an integer
|
||||
* address. */
|
||||
readsz = (UINTN)*sz;
|
||||
status = uefi_call_wrapper(file->Read, 3, file, &readsz,
|
||||
(void*)(uintptr_t)*_addr);
|
||||
*sz = (uint32_t)readsz;
|
||||
uefi_call_wrapper(file->Close, 1, file); /* done with the file */
|
||||
if (status != EFI_SUCCESS) {
|
||||
wolfBoot_printf("file Read failed: 0x%lx\n", (unsigned long)status);
|
||||
uefi_call_wrapper(BS->FreePages, 2, *_addr, pages);
|
||||
*_addr = 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
if (*sz < IMAGE_HEADER_SIZE) {
|
||||
wolfBoot_printf("Image smaller than the header\n");
|
||||
uefi_call_wrapper(BS->FreePages, 2, *_addr, pages);
|
||||
*_addr = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read an optional \cmdline.txt (ASCII) from the ESP into a widechar buffer
|
||||
* for the kernel LoadOptions. No-op (leaves kernel_cmdline NULL) if absent. */
|
||||
static void read_cmdline(EFI_FILE_HANDLE vol)
|
||||
{
|
||||
EFI_FILE_HANDLE file;
|
||||
EFI_STATUS status;
|
||||
UINT64 sz;
|
||||
UINTN readsz, i, n;
|
||||
uint8_t *ascii = NULL;
|
||||
CHAR16 *wide = NULL;
|
||||
|
||||
file = openFile(L"cmdline.txt", vol);
|
||||
if (file == NULL)
|
||||
return; /* optional */
|
||||
|
||||
sz = FileSize(file);
|
||||
if (sz == 0 || sz > 4096) {
|
||||
uefi_call_wrapper(file->Close, 1, file);
|
||||
return;
|
||||
}
|
||||
|
||||
status = uefi_call_wrapper(BS->AllocatePool, 3, EfiLoaderData,
|
||||
(UINTN)sz, (void**)&ascii);
|
||||
if (status != EFI_SUCCESS || ascii == NULL) {
|
||||
uefi_call_wrapper(file->Close, 1, file);
|
||||
return;
|
||||
}
|
||||
|
||||
readsz = (UINTN)sz;
|
||||
status = uefi_call_wrapper(file->Read, 3, file, &readsz, ascii);
|
||||
uefi_call_wrapper(file->Close, 1, file); /* done with the file */
|
||||
if (status != EFI_SUCCESS) {
|
||||
FreePool(ascii);
|
||||
return;
|
||||
}
|
||||
|
||||
/* trim trailing CR/LF/whitespace */
|
||||
n = (UINTN)readsz;
|
||||
while (n > 0 && (ascii[n-1] == '\n' || ascii[n-1] == '\r' ||
|
||||
ascii[n-1] == ' ' || ascii[n-1] == '\t'))
|
||||
n--;
|
||||
|
||||
status = uefi_call_wrapper(BS->AllocatePool, 3, EfiLoaderData,
|
||||
(n + 1) * sizeof(CHAR16), (void**)&wide);
|
||||
if (status != EFI_SUCCESS || wide == NULL) {
|
||||
FreePool(ascii);
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < n; i++)
|
||||
wide[i] = (CHAR16)ascii[i];
|
||||
wide[n] = 0;
|
||||
FreePool(ascii);
|
||||
|
||||
kernel_cmdline = wide;
|
||||
kernel_cmdline_bytes = (n + 1) * sizeof(CHAR16);
|
||||
wolfBoot_printf("Kernel cmdline (%d chars) from cmdline.txt\n", (int)n);
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
|
||||
{
|
||||
CHAR16 *kernel_filename = L"kernel.img";
|
||||
CHAR16 *update_filename = L"update.img";
|
||||
EFI_LOADED_IMAGE *loaded_image = NULL;
|
||||
EFI_FILE_HANDLE vol;
|
||||
EFI_STATUS status;
|
||||
uint32_t kernel_size, update_size;
|
||||
|
||||
InitializeLib(ImageHandle, SystemTable);
|
||||
gSystemTable = SystemTable;
|
||||
gImageHandle = ImageHandle;
|
||||
|
||||
status = uefi_call_wrapper(SystemTable->BootServices->HandleProtocol,
|
||||
3,
|
||||
ImageHandle,
|
||||
&LoadedImageProtocol,
|
||||
(void **)&loaded_image);
|
||||
|
||||
if (status == EFI_SUCCESS)
|
||||
wolfBoot_printf("Image base: 0x%lx\n", loaded_image->ImageBase);
|
||||
vol = GetVolume(ImageHandle);
|
||||
read_cmdline(vol);
|
||||
/* open_kernel_image() leaves *_addr == 0 on failure (and frees any pages),
|
||||
* so the "no image" check below is reliable; the sizes are logged inside
|
||||
* and not needed here. */
|
||||
(void)open_kernel_image(vol, kernel_filename, &kernel_addr, &kernel_size);
|
||||
(void)open_kernel_image(vol, update_filename, &update_addr, &update_size);
|
||||
(void)kernel_size;
|
||||
(void)update_size;
|
||||
|
||||
if (kernel_addr == 0 && update_addr == 0) {
|
||||
wolfBoot_printf("No image to load\n");
|
||||
panic();
|
||||
}
|
||||
|
||||
wolfBoot_start();
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
#endif /* TARGET_aarch64_efi */
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# aarch64_efi is a UEFI application target; the link uses the linker script
|
||||
# provided by gnu-efi (elf_aarch64_efi.lds via GNU_EFI_LSCRIPT). This is an
|
||||
# empty file to appease the Makefile (mirrors hal/x86_64_efi.ld).
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/* boot_aarch64_efi.c
|
||||
*
|
||||
* Copyright (C) 2026 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfBoot.
|
||||
*
|
||||
* wolfBoot is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfBoot is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
||||
*/
|
||||
|
||||
/* AArch64 UEFI-application boot glue (generic AArch64 UEFI target).
|
||||
*
|
||||
* When wolfBoot is built as a UEFI application the gnu-efi CRT0 is the entry
|
||||
* point and efi_main() (hal/aarch64_efi.c) does all the work, so none of the
|
||||
* bare-metal reset / EL2->EL1 / cache-teardown path in boot_aarch64.c applies.
|
||||
* This provides the do_boot() the updater (src/update_ram.c) calls; it simply
|
||||
* forwards the verified image to the UEFI LoadImage/StartImage handoff in
|
||||
* hal/aarch64_efi.c. Mirrors src/boot_x86_64.c. */
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "image.h"
|
||||
#include "loader.h"
|
||||
#include "wolfboot/wolfboot.h"
|
||||
|
||||
#ifdef TARGET_aarch64_efi
|
||||
|
||||
/* aarch64_efi never defines MMU (UEFI owns MMU/FDT), so do_boot has only the
|
||||
* app-only form; it forwards the verified image to the LoadImage/StartImage
|
||||
* handoff in hal/aarch64_efi.c. */
|
||||
extern void RAMFUNCTION aarch64_efi_do_boot(const uint32_t *boot_addr);
|
||||
|
||||
void RAMFUNCTION do_boot(const uint32_t *app_offset)
|
||||
{
|
||||
aarch64_efi_do_boot(app_offset);
|
||||
}
|
||||
|
||||
#endif /* TARGET_aarch64_efi */
|
||||
|
|
@ -156,7 +156,7 @@ const char Store_hdr[] = "\n"
|
|||
"#define KEYSTORE_SECTION __attribute__((section (\"__KEYSTORE,__keystore\")))\n"
|
||||
"#elif defined(__CCRX__) || defined(WOLFBOOT_RENESAS_RSIP) || defined(WOLFBOOT_RENESAS_TSIP) || defined(WOLFBOOT_RENESAS_SCEPROTECT)\n"
|
||||
"#define KEYSTORE_SECTION /* Renesas RX */\n"
|
||||
"#elif defined(TARGET_x86_64_efi)\n"
|
||||
"#elif defined(TARGET_x86_64_efi) || defined(TARGET_aarch64_efi)\n"
|
||||
"#define KEYSTORE_SECTION\n"
|
||||
"#else\n"
|
||||
"#define KEYSTORE_SECTION __attribute__((section (\".keystore\")))\n"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
#!/bin/bash
|
||||
# Run the aarch64_efi wolfboot.efi in QEMU aarch64 UEFI for fast iteration/debug.
|
||||
# No hardware / USB stick needed: a scratch dir is exposed to UEFI as a FAT ESP.
|
||||
#
|
||||
# ./tools/scripts/aarch64-efi-qemu.sh # run, auto-launch wolfboot.efi
|
||||
# ./tools/scripts/aarch64-efi-qemu.sh --gdb # freeze at reset for gdb (-s -S)
|
||||
# ./tools/scripts/aarch64-efi-qemu.sh --fresh-vars # reset the UEFI NVRAM store
|
||||
#
|
||||
# In another shell for --gdb:
|
||||
# gdb-multiarch wolfboot.elf -ex 'target remote :1234'
|
||||
set -e
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
ESP="$ROOT/tools/qemu-esp"
|
||||
CODE=/usr/share/AAVMF/AAVMF_CODE.fd
|
||||
VARS_SRC=/usr/share/AAVMF/AAVMF_VARS.fd
|
||||
|
||||
for f in "$CODE" "$VARS_SRC"; do
|
||||
[ -r "$f" ] || { echo "Missing $f -- install qemu-efi-aarch64"; exit 1; }
|
||||
done
|
||||
command -v qemu-system-aarch64 >/dev/null || { echo "Install qemu-system-arm"; exit 1; }
|
||||
|
||||
mkdir -p "$ESP"
|
||||
cp "$ROOT/wolfboot.efi" "$ESP"/
|
||||
cp "$ROOT/aarch64_efi-stage/kernel.img" "$ESP"/ 2>/dev/null || true
|
||||
# optional Linux kernel command line (M3): read by wolfboot as \cmdline.txt
|
||||
cp "$ROOT/aarch64_efi-stage/cmdline.txt" "$ESP"/ 2>/dev/null || true
|
||||
# auto-run wolfboot.efi from the UEFI shell
|
||||
printf 'fs0:\r\nwolfboot.efi\r\n' > "$ESP/startup.nsh"
|
||||
|
||||
# Parse flags (order-independent).
|
||||
GDB_ARGS=""
|
||||
FRESH_VARS=0
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--gdb) GDB_ARGS="-s -S" ;;
|
||||
--fresh-vars) FRESH_VARS=1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Preserve the UEFI variable store (including any Secure Boot key enrollment)
|
||||
# across runs; recreate it only when missing or when --fresh-vars is given.
|
||||
VARS="$ROOT/tools/qemu-esp/AAVMF_VARS.fd"
|
||||
if [ ! -f "$VARS" ] || [ "$FRESH_VARS" = "1" ]; then
|
||||
cp "$VARS_SRC" "$VARS"
|
||||
fi
|
||||
|
||||
if [ -n "$GDB_ARGS" ]; then
|
||||
echo "GDB mode: qemu frozen. In another shell:"
|
||||
echo " gdb-multiarch $ROOT/wolfboot.elf -ex 'target remote :1234'"
|
||||
fi
|
||||
|
||||
exec qemu-system-aarch64 \
|
||||
-machine virt -cpu cortex-a72 -m 1024 -smp 1 \
|
||||
-drive if=pflash,format=raw,file="$CODE",readonly=on \
|
||||
-drive if=pflash,format=raw,file="$VARS" \
|
||||
-drive format=raw,file=fat:rw:"$ESP" \
|
||||
-net none -nographic $GDB_ARGS
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
#!/bin/bash
|
||||
# Build gnu-efi for AArch64 (for the wolfBoot aarch64_efi UEFI target).
|
||||
# Installs crt0-efi-aarch64.o, elf_aarch64_efi.lds, libgnuefi.a, libefi.a
|
||||
# and the EFI headers into tools/gnu-efi-aarch64/ inside the wolfBoot tree.
|
||||
#
|
||||
# GNU_EFI_REF is pinned to a released tag so the runtime CRT0/linker-script/libs
|
||||
# are reproducible and auditable (the aarch64_efi link depends on the exact
|
||||
# revision -- see the --allow-multiple-definition note in arch.mk). Override:
|
||||
# CROSS_COMPILE=... GNU_EFI_REF=... ./build-gnu-efi-aarch64.sh
|
||||
set -e
|
||||
|
||||
CROSS_COMPILE="${CROSS_COMPILE:-aarch64-linux-gnu-}"
|
||||
GNU_EFI_REF="${GNU_EFI_REF:-4.0.4}"
|
||||
|
||||
# Resolve wolfBoot root (this script lives in tools/scripts/)
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
PREFIX="$ROOT/tools/gnu-efi-aarch64"
|
||||
SRC="$ROOT/tools/gnu-efi-src"
|
||||
|
||||
echo "== gnu-efi aarch64 build =="
|
||||
echo " CROSS_COMPILE=$CROSS_COMPILE"
|
||||
echo " PREFIX=$PREFIX"
|
||||
|
||||
# gnu-efi upstream (Nigel Croxon, the gnu-efi maintainer). Pin to $GNU_EFI_REF;
|
||||
# if the tree already exists, force it onto that ref (reproducible re-runs).
|
||||
if [ ! -d "$SRC/.git" ]; then
|
||||
git clone --depth 1 -b "$GNU_EFI_REF" https://github.com/ncroxon/gnu-efi "$SRC"
|
||||
else
|
||||
git -C "$SRC" fetch --depth 1 origin tag "$GNU_EFI_REF" 2>/dev/null \
|
||||
|| git -C "$SRC" fetch --depth 1 origin "$GNU_EFI_REF"
|
||||
git -C "$SRC" checkout -q "$GNU_EFI_REF" 2>/dev/null \
|
||||
|| git -C "$SRC" checkout -q FETCH_HEAD
|
||||
make -C "$SRC" ARCH=aarch64 clean >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
make -C "$SRC" ARCH=aarch64 CROSS_COMPILE="$CROSS_COMPILE" -j"$(nproc)"
|
||||
make -C "$SRC" ARCH=aarch64 CROSS_COMPILE="$CROSS_COMPILE" \
|
||||
PREFIX=/ INSTALLROOT="$PREFIX" install
|
||||
|
||||
echo "== installed files =="
|
||||
find "$PREFIX" -name 'crt0-efi-aarch64.o' -o -name 'elf_aarch64_efi.lds' \
|
||||
-o -name 'libgnuefi.a' -o -name 'libefi.a' | sort
|
||||
echo "== done =="
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
#!/bin/bash
|
||||
# Sign wolfboot.efi for UEFI Secure Boot and generate the PK/KEK/db enrollment
|
||||
# material, so the platform firmware itself verifies wolfBoot before launching
|
||||
# it (closing the code-signing gap: firmware -> wolfboot.efi -> kernel).
|
||||
#
|
||||
# This is the UEFI Secure Boot layer of the wolfBoot root of trust. It is
|
||||
# independent of wolfBoot's own wolfCrypt verification of the payload, and of
|
||||
# any NVIDIA fuse provisioning (documented separately in docs/Targets.md).
|
||||
#
|
||||
# Requires: sbsigntool (sbsign, sbverify) and efitools (cert-to-efi-sig-list,
|
||||
# sign-efi-sig-list) and openssl. On Debian/Ubuntu:
|
||||
# sudo apt install sbsigntool efitools openssl uuid-runtime
|
||||
#
|
||||
# Usage:
|
||||
# ./tools/scripts/sign-efi-secureboot.sh [wolfboot.efi]
|
||||
# Overrides (env):
|
||||
# KEYDIR directory for the generated keys/certs (default tools/efi-secureboot-keys)
|
||||
# EFI_BIN the PE image to sign (default: first arg, else wolfboot.efi)
|
||||
# CN common-name prefix for the generated certs (default "wolfBoot")
|
||||
#
|
||||
# SECURITY: the generated PK/KEK/db private keys are UNENCRYPTED (openssl
|
||||
# -nodes) and long-lived -- intended for lab/dev enrollment. They are created
|
||||
# owner-only (umask 077 below); keep them off shared machines. For production,
|
||||
# protect the signing keys (HSM or an encrypted key store) and treat this
|
||||
# script only as the enrollment mechanism.
|
||||
set -e
|
||||
|
||||
# Restrict permissions on everything this script creates: it writes private
|
||||
# keys (PK/KEK/db .key), which must not be group/world-readable.
|
||||
umask 077
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
KEYDIR="${KEYDIR:-$ROOT/tools/efi-secureboot-keys}"
|
||||
EFI_BIN="${EFI_BIN:-${1:-$ROOT/wolfboot.efi}}"
|
||||
CN="${CN:-wolfBoot}"
|
||||
|
||||
for tool in openssl sbsign sbverify cert-to-efi-sig-list sign-efi-sig-list; do
|
||||
command -v "$tool" >/dev/null 2>&1 || {
|
||||
echo "ERROR: required tool '$tool' not found." >&2
|
||||
echo " install: sudo apt install sbsigntool efitools openssl uuid-runtime" >&2
|
||||
exit 1
|
||||
}
|
||||
done
|
||||
|
||||
if [ ! -f "$EFI_BIN" ]; then
|
||||
echo "ERROR: EFI image '$EFI_BIN' not found (build wolfboot.efi first)." >&2
|
||||
exit 1
|
||||
fi
|
||||
# Resolve to an absolute path: we cd into KEYDIR below, after which a relative
|
||||
# image path would no longer resolve.
|
||||
EFI_BIN="$(cd "$(dirname "$EFI_BIN")" && pwd)/$(basename "$EFI_BIN")"
|
||||
|
||||
mkdir -p "$KEYDIR"
|
||||
# Resolve to an absolute path: we cd into KEYDIR below but still reference
|
||||
# "$KEYDIR/db.key" etc. afterward, which would break for a relative KEYDIR.
|
||||
KEYDIR="$(cd "$KEYDIR" && pwd)"
|
||||
cd "$KEYDIR"
|
||||
|
||||
# A stable GUID identifies the owner of the enrolled variables; persist it so
|
||||
# repeated runs reuse the same identity.
|
||||
if [ ! -f guid.txt ]; then
|
||||
if command -v uuidgen >/dev/null 2>&1; then
|
||||
uuidgen > guid.txt
|
||||
else
|
||||
# Fallback: derive a GUID from openssl if uuid-runtime is absent.
|
||||
openssl rand -hex 16 | sed 's/\(........\)\(....\)\(....\)\(....\)\(............\)/\1-\2-\3-\4-\5/' > guid.txt
|
||||
fi
|
||||
fi
|
||||
GUID="$(cat guid.txt)"
|
||||
echo "== UEFI Secure Boot key owner GUID: $GUID =="
|
||||
|
||||
# Generate the Platform Key (PK), Key Exchange Key (KEK) and signature
|
||||
# database (db) key/cert if they do not already exist. Self-signed X.509,
|
||||
# 2048-bit RSA, long validity for lab/dev use.
|
||||
gen_key() {
|
||||
name="$1"; subject="$2"
|
||||
if [ ! -f "$name.key" ] || [ ! -f "$name.crt" ]; then
|
||||
echo "== generating $name key/cert ($subject) =="
|
||||
echo " WARNING: $name.key is an UNENCRYPTED private key (dev/lab use)" >&2
|
||||
openssl req -new -x509 -newkey rsa:2048 -nodes -sha256 -days 3650 \
|
||||
-subj "/CN=$subject/" -keyout "$name.key" -out "$name.crt"
|
||||
fi
|
||||
# ESL (EFI signature list) form of the cert, tagged with our owner GUID.
|
||||
cert-to-efi-sig-list -g "$GUID" "$name.crt" "$name.esl"
|
||||
}
|
||||
|
||||
gen_key PK "$CN PK"
|
||||
gen_key KEK "$CN KEK"
|
||||
gen_key db "$CN db"
|
||||
|
||||
# Build the signed variable updates (.auth) for enrollment:
|
||||
# PK is signed by PK (self)
|
||||
# KEK is signed by PK
|
||||
# db is signed by KEK
|
||||
echo "== building signed enrollment variables (.auth) =="
|
||||
sign-efi-sig-list -g "$GUID" -k PK.key -c PK.crt PK PK.esl PK.auth
|
||||
sign-efi-sig-list -g "$GUID" -k PK.key -c PK.crt KEK KEK.esl KEK.auth
|
||||
sign-efi-sig-list -g "$GUID" -k KEK.key -c KEK.crt db db.esl db.auth
|
||||
|
||||
# Sign the wolfBoot PE image with the db key so the firmware accepts it.
|
||||
SIGNED="${EFI_BIN}.signed"
|
||||
echo "== signing $EFI_BIN with db key -> $SIGNED =="
|
||||
sbsign --key "$KEYDIR/db.key" --cert "$KEYDIR/db.crt" \
|
||||
--output "$SIGNED" "$EFI_BIN"
|
||||
sbverify --cert "$KEYDIR/db.crt" "$SIGNED"
|
||||
|
||||
echo "== done =="
|
||||
echo "Signed image: $SIGNED"
|
||||
echo "Enroll on the target (UEFI setup / KeyTool / QEMU AAVMF vars) in order:"
|
||||
echo " db <- $KEYDIR/db.auth"
|
||||
echo " KEK <- $KEYDIR/KEK.auth"
|
||||
echo " PK <- $KEYDIR/PK.auth (enabling PK turns Secure Boot enforcing)"
|
||||
echo "Then deploy $SIGNED as \\EFI\\BOOT\\BOOTAA64.EFI (or your boot path)."
|
||||
Loading…
Reference in New Issue