Skip to content
Open
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
5 changes: 5 additions & 0 deletions uboot-custom-imx8mp-evk/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.avocado/
.avocado-state
.downloads/
keys/*.key
keys/*.crt
49 changes: 49 additions & 0 deletions uboot-custom-imx8mp-evk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
language: C
targets:
- imx8mp-evk
topics:
- cross-compilation
- bootloader
- secure-boot
---

# Custom U-Boot (HAB + FIT signature) for i.MX 8M Plus EVK

A reference runtime that demonstrates how to cross-compile a custom
`imx-boot` bundle (TF-A BL31 + U-Boot SPL/proper + DDR firmware) from
source using the Avocado SDK and replace the upstream-installed
bootloader on an NXP i.MX 8M Plus EVK.

- Cross-compile NXP's `uboot-imx`, `imx-atf`, and `imx-mkimage` for
imx8mp inside the SDK container, bundled with NXP's redistributable
DDR4 firmware.
- Enable `CONFIG_IMX_HAB=y` so the resulting flash.bin is HAB-ready
(parses CSF blobs, can be signed against the SoC's SRK fuses).
- Enable `CONFIG_FIT_SIGNATURE=y` plus a placeholder /signature node in
the U-Boot control DTB. After build, `insert-fit-pubkey.sh` runs
`mkimage -K` to inject your RSA pubkey into the placeholder and
rebuilds flash.bin — letting you rotate FIT signing keys without
rebuilding U-Boot itself.
- Use the `runtimes.<n>.packages.<name>.{compile,install}` hook (mirrors
the extension `packages.<dep>.{compile,install}` form) to drop the
built `imx-boot` into the runtime build dir, where stone bundles it
into the os-bundle in place of any upstream artifact.

## Why custom U-Boot?

The upstream Avocado-provided `imx-boot` is unsigned and uses a generic
`/signature` configuration. Two reasons you'd build your own:

1. **HAB closure.** Closing HAB on a production device requires fusing
the SRK hash and shipping a flash.bin signed against that key. You
need control over the bootloader binary to attach the right CSF, and
you typically tie HAB closure to a hardware certification milestone.
2. **FIT image verification.** With `CONFIG_FIT_SIGNATURE=y` and a
pubkey embedded in the U-Boot DTB, the bootloader will refuse to
load a kernel/initramfs that wasn't signed with the matching private
key. The dtsi placeholder lets you ship the same flash.bin to many
units and rotate the pubkey without a U-Boot rebuild.

See [getting_started.md](getting_started.md) for the build, the
`mkimage -K` post-build flow, and notes on closing HAB.
85 changes: 85 additions & 0 deletions uboot-custom-imx8mp-evk/avocado.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
default_target: imx8mp-evk
supported_targets:
- imx8mp-evk

distro:
release: 2024
channel: edge

##
## Runtimes
##

runtimes:
dev:
extensions:
- avocado-ext-dev
- avocado-ext-sshd-dev
- avocado-bsp-{{ avocado.target.board }}
- config-dev
packages:
avocado-runtime: '*'

# Cross-compile imx-boot from source via the SDK and drop it into
# the runtime build dir, overriding any upstream-provided imx-boot.
# The compile step runs uboot-compile.sh (TF-A + U-Boot + DDR FW
# bundled by imx-mkimage); the install step copies flash.bin to
# $AVOCADO_RUNTIME_BUILD_DIR/imx-boot so stone bundles ours.
uboot:
compile: uboot
install: uboot-install.sh

##
## Extensions
##

extensions:
avocado-ext-dev:
source:
type: package
version: '*'

avocado-ext-sshd-dev:
source:
type: package
version: '*'

avocado-bsp-{{ avocado.target.board }}:
source:
type: package
version: '*'

config-dev:
version: 0.1.0

users:
root:
password: ''

##
## SDK
##

sdk:
image: docker.io/avocadolinux/sdk:{{ avocado.distro.release }}-{{ avocado.distro.channel }}

container_args:
- --network=host

compile:
uboot:
compile: uboot-compile.sh
clean: uboot-clean.sh

# Native build tools for U-Boot, TF-A, and imx-mkimage. nativesdk-*
# packages are installed into the SDK host sysroot (not the target
# sysroot), so they go here at the top level alongside
# avocado-sdk-toolchain — same pattern as the linux-custom-kernel
# reference.
packages:
avocado-sdk-toolchain: '*'
nativesdk-bc: '*'
nativesdk-bison: '*'
nativesdk-flex: '*'
nativesdk-openssl-bin: '*'
nativesdk-dtc: '*'
175 changes: 175 additions & 0 deletions uboot-custom-imx8mp-evk/getting_started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
# Getting Started — Custom U-Boot for i.MX 8M Plus EVK

This guide walks you through cross-compiling a custom imx-boot
bundle from source and flashing it onto an i.MX 8M Plus EVK. The
reference matches the pins nxp-imx/meta-imx ships at
`scarthgap-6.6.36-2.1.0` (uboot-imx `lf_v2024.04`, imx-atf `lf_v2.10`,
imx-mkimage `lf-6.6.36_2.1.0`, firmware-imx 8.25-27879f8) with HAB and
FIT signature support enabled, and replaces the bootloader Avocado
would otherwise pull in.

## Prerequisites

- Linux host (Ubuntu 22.04+, Fedora 39+) — or macOS with Docker Desktop.
- [Docker](https://www.docker.com/products/docker-desktop/) running.
- Avocado CLI: see [docs.peridio.com](https://docs.peridio.com).
- An i.MX 8M Plus EVK in serial-download (USB-OTG) mode for flashing
via `uuu-emmc`.

## Initialize

```bash
avocado init --reference uboot-custom-imx8mp-evk uboot-imx8mp-evk
cd uboot-imx8mp-evk
```

## Install

```bash
avocado install -f
```

This pulls the Avocado SDK container and installs `nativesdk-bc`,
`nativesdk-bison`, `nativesdk-flex`, `nativesdk-openssl`,
`nativesdk-dtc`, `nativesdk-util-linux` — the build deps needed by
U-Boot, TF-A, and imx-mkimage.

## Build

```bash
avocado build
```

The build runs `uboot-compile.sh` inside the SDK container, which:

1. Saves `CROSS_COMPILE` / `ARCH` from the SDK env, then unsets the
userspace `CC`, `CFLAGS`, `LDFLAGS` exports that fight the U-Boot
and TF-A build systems (same trick the Linux kernel reference uses).
2. Clones `uboot-imx` (branch `lf_v2024.04`, SRCREV
`de16f4f1`), `imx-atf` (branch `lf_v2.10`, SRCREV `28affcae`),
`imx-mkimage` (branch `lf-6.6.36_2.1.0`, SRCREV `4622115c`), and
downloads `firmware-imx-8.25-27879f8.bin` from NXP's mirror — the
exact pins meta-imx ships at scarthgap-6.6.36-2.1.0.
3. Appends `patches/avocado.cfg` + `patches/env-mmc.cfg` onto
`imx8mp_evk_defconfig`, and adds `#include
"avocado-fit-signature.dtsi"` to `imx8mp-evk-u-boot.dtsi` so the
control DTB carries a placeholder /signature node.
4. Builds TF-A BL31, builds U-Boot, generates a redundant `uboot.env`
from `patches/avocado-imx8mp-evk.txt`.
5. Stages the binaries into `iMX8M/` and runs
`make SOC=iMX8MP flash_evk` to produce `flash.bin` (== `imx-boot`).

`uboot-install.sh` then drops `flash.bin` and `uboot.env` into
`$AVOCADO_RUNTIME_BUILD_DIR`, so the stone bundle assembled by
`avocado build` carries our bootloader.

You can also rebuild only the bootloader without re-assembling the
runtime:

```bash
avocado sdk compile uboot
```

## Inject a FIT signing pubkey (the "replace later" workflow)

The bootloader you just built has `/signature/key-rt-prod` as a
placeholder — empty `rsa,modulus` etc. To actually have it verify FIT
images, populate that node with your dev pubkey:

```bash
avocado sdk run -E -- bash insert-fit-pubkey.sh
avocado build
```

`insert-fit-pubkey.sh`:

1. Generates `keys/dev.key` + `keys/dev.crt` if missing.
2. Builds a throwaway FIT image referencing `key-name-hint = "dev"`.
3. Runs `mkimage -F -K …` so mkimage extracts the pubkey from
`keys/dev.key` and patches it into the placeholder /signature node
*in the already-built U-Boot DTB* — no full U-Boot rebuild.
4. Re-runs `imx-mkimage` to fold the patched DTB back into a fresh
`flash.bin`.

Re-running `avocado build` then re-stages the updated bootloader in the
runtime build dir.

To rotate the key, drop a new `keys/dev.key` (or change `KEY_NAME` in
the script) and re-run `insert-fit-pubkey.sh` + `avocado build`. The
core bootloader binary doesn't change — only the pubkey block in the
control DTB.

To sign your real FIT images (kernel + dtb + initramfs) for this
bootloader to accept:

```bash
mkimage -F -k keys -r <fit-image.itb>
```

## Provision the EVK

Put the EVK in serial-download mode (set boot DIPs SW4 to
`0011 0010 0010 1000`, the documented serial-download position — see
the EVK user manual; do not press an arbitrary BOOT+RESET combo) and
plug a USB-C cable into the OTG port:

```bash
avocado provision -r dev --profile uuu-emmc
```

uuu hands `flash.bin` to the boot ROM via SDPS, then writes the OS
bundle (rootfs / initramfs / kernel) to eMMC. Reset the board with
DIPs back to eMMC boot — your custom HAB-ready U-Boot runs first.

## Closing HAB (production-only)

The build leaves HAB *open* — i.e., this flash.bin will boot on any
imx8mp without signature checks. Closing HAB is a one-way fuse blow:
**do not do this on a dev board you want to recover.**

The full procedure is documented in NXP's
[i.MX Secure Boot on AHAB and HAB CST](https://www.nxp.com/docs/en/application-note/AN12056.pdf)
guide. Sketch:

1. Generate SRK / CSF / IMG keys with NXP's CST.
2. Run `cst -i csf-spl.txt` and `cst -i csf-uboot.txt` against the
`flash.bin` produced by this reference. Use the `imx_log` block at
the start of the build output to find the load addresses CST needs.
3. Append the resulting CSF blobs to `flash.bin` at the offsets the
first stage reports.
4. Verify with `hab_status` from the U-Boot prompt — it should report
"No HAB Events Found!" before you close.
5. Blow the SRK_HASH fuse via `fuse prog`. Once closed, only flash.bin
binaries signed by your SRK chain will boot.

## Customize

### Different NXP release line

Bump the branch + SRCREV pairs at the top of `uboot-compile.sh` to
match the meta-imx tag for the release line you want. The canonical
source is the matching `recipes-bsp/{imx-atf,u-boot,imx-mkimage}/*.bb`
and `recipes-bsp/firmware-imx/firmware-imx-*.inc` in
[nxp-imx/meta-imx](https://github.com/nxp-imx/meta-imx) — open the
files at the tag for your release line and copy the
`SRCBRANCH` / `SRCREV` / firmware-imx PV + IMX_SRCREV_ABBREV values
verbatim.

### Different i.MX 8M variant

Swap `UBOOT_DEFCONFIG`, `ATF_PLATFORM`, `MKIMAGE_SOC`, and
`MKIMAGE_TARGET` in `uboot-compile.sh`. Check
`imx-mkimage/iMX8M/soc.mak` for the available targets and the staging
filenames each one expects.

### Customize the boot env

Edit `patches/avocado-imx8mp-evk.txt`. The format is mkenvimage(1) —
each line is a `key=value` U-Boot env entry.

### Different supported_targets

This reference is hard-coded to imx8mp-evk because of the imx-mkimage
target name. To support multiple boards, switch on
`{{ avocado.target.board }}` in `avocado.yaml` and gate the
`uboot-compile.sh` choices off `$AVOCADO_TARGET`.
Loading