mirror of https://github.com/wolfSSL/wolfBoot.git
156 lines
7.4 KiB
Bash
Executable File
156 lines
7.4 KiB
Bash
Executable File
#!/bin/bash
|
|
# prepare_emmc.sh - build the eMMC/SD layout for the CM4 wolfBoot disk-boot test.
|
|
#
|
|
# Creates a GPT with:
|
|
# p1 = boot (FAT32) : RPi firmware (start4.elf, fixup4.dat), config.txt,
|
|
# kernel8.img (= wolfBoot, built from cm4_emmc.config)
|
|
# p2 = image_a (raw) : signed disk_app ELF, version 1 (BOOT_PART_A=1)
|
|
# p3 = image_b (raw) : signed disk_app ELF, version 2 (BOOT_PART_B=2)
|
|
#
|
|
# wolfBoot (loaded to RAM by the RPi firmware) then drives the BCM2711 EMMC2
|
|
# controller to read p2/p3, verifies ECC384/SHA384, and boots the higher
|
|
# version. Prove-out prints ">>> CM4 DISK APP OK <<<" over the mini-UART (AUX).
|
|
#
|
|
# Usage:
|
|
# # 1) build wolfBoot first:
|
|
# # cp config/examples/cm4_emmc.config .config
|
|
# # make wolfboot.bin CROSS_COMPILE=aarch64-none-elf- (add DEBUG_SDHCI for bring-up)
|
|
# # 2) prep artifacts only (no root):
|
|
# # bash tools/scripts/cm4/prepare_emmc.sh
|
|
# # 3) with the eMMC presented via rpiboot as /dev/sdN, write it (root):
|
|
# # sudo bash tools/scripts/cm4/prepare_emmc.sh /dev/sdN
|
|
#
|
|
# Copyright (C) 2026 wolfSSL Inc. GPLv3 (see wolfBoot COPYING).
|
|
set -euo pipefail
|
|
|
|
ROOT="$(cd "$(dirname "$0")/../../.." && pwd)" # wolfboot repo root
|
|
HERE="$ROOT/tools/scripts/cm4"
|
|
CROSS="${CROSS_COMPILE:-aarch64-none-elf-}"
|
|
KEY="${PRIVATE_KEY:-$ROOT/wolfboot_signing_private_key.der}"
|
|
IMG_HDR="${IMAGE_HEADER_SIZE:-1024}"
|
|
# Pin the RPi firmware to a specific ref for reproducibility - these blobs are
|
|
# the first link of the boot chain. Override with FWREF=<tag|sha> (a tag like
|
|
# 1.20240529, or a full commit sha).
|
|
FWREF="${FWREF:-1.20240529}"
|
|
FWBASE="https://raw.githubusercontent.com/raspberrypi/firmware/${FWREF}/boot"
|
|
DEV="${1:-}"
|
|
|
|
# ---- artifact prep (no root) ----------------------------------------------
|
|
prep_artifacts() {
|
|
# Idempotent: if everything is already staged (e.g. prepped without sudo,
|
|
# then re-run under sudo to write the device where the cross-toolchain is
|
|
# not in PATH), skip the rebuild. Set FORCE_PREP=1 to force.
|
|
if [ "${FORCE_PREP:-0}" != "1" ] && \
|
|
[ -f "$HERE/disk_app_v1_signed.bin" ] && \
|
|
[ -f "$HERE/disk_app_v2_signed.bin" ] && \
|
|
[ -f "$HERE/fw/kernel8.img" ] && [ -f "$HERE/fw/start4.elf" ] && \
|
|
[ -f "$HERE/fw/config.txt" ]; then
|
|
echo "== artifacts already staged in $HERE (skip prep; FORCE_PREP=1 to rebuild) =="
|
|
return 0
|
|
fi
|
|
|
|
[ -f "$ROOT/wolfboot.bin" ] || { echo "!! build wolfboot.bin first (cm4_emmc.config)"; exit 1; }
|
|
[ -f "$KEY" ] || { echo "!! signing key not found: $KEY"; exit 1; }
|
|
|
|
echo "== building + signing disk_app (v1, v2) =="
|
|
"${CROSS}gcc" -nostdlib -nostartfiles -Wl,-Ttext=0x10000000 \
|
|
-o "$HERE/disk_app.elf" "$HERE/disk_app.S"
|
|
( cd "$ROOT" && \
|
|
IMAGE_HEADER_SIZE="$IMG_HDR" ./tools/keytools/sign --ecc384 --sha384 \
|
|
"$HERE/disk_app.elf" "$KEY" 1 >/dev/null && \
|
|
IMAGE_HEADER_SIZE="$IMG_HDR" ./tools/keytools/sign --ecc384 --sha384 \
|
|
"$HERE/disk_app.elf" "$KEY" 2 >/dev/null )
|
|
ls -la "$HERE"/disk_app_v1_signed.bin "$HERE"/disk_app_v2_signed.bin
|
|
|
|
echo "== fetching RPi BCM2711 firmware =="
|
|
for f in start4.elf fixup4.dat bcm2711-rpi-cm4.dtb; do
|
|
[ -f "$HERE/fw/$f" ] || { mkdir -p "$HERE/fw"; curl -fsSL -o "$HERE/fw/$f" "$FWBASE/$f"; }
|
|
done
|
|
|
|
cat > "$HERE/fw/config.txt" <<'EOF'
|
|
arm_64bit=1
|
|
kernel=kernel8.img
|
|
# wolfBoot is linked absolutely at 0x200000; pin the load address to match.
|
|
kernel_address=0x200000
|
|
enable_uart=1
|
|
uart_2ndstage=1
|
|
dtoverlay=disable-bt
|
|
init_uart_clock=48000000
|
|
init_uart_baud=115200
|
|
EOF
|
|
cp "$ROOT/wolfboot.bin" "$HERE/fw/kernel8.img"
|
|
echo "== artifacts ready in $HERE (fw/ + disk_app_v{1,2}_signed.bin) =="
|
|
}
|
|
|
|
# ---- device write (root) --------------------------------------------------
|
|
write_device() {
|
|
local t root_src root_pk P1 P2 P3 MP ok p
|
|
[ "$(id -u)" -eq 0 ] || { echo "!! writing $DEV requires root"; exit 1; }
|
|
for t in sgdisk partprobe mkfs.vfat lsblk udevadm; do
|
|
command -v "$t" >/dev/null || { echo "!! missing required tool: $t"; exit 1; }
|
|
done
|
|
[ -b "$DEV" ] || { echo "!! not a block device: $DEV"; exit 1; }
|
|
# Refuse the disk currently backing '/' (a real safety check, not a
|
|
# bench-specific node name). Override with ALLOW_ANY_DISK=1.
|
|
root_src="$(findmnt -no SOURCE / 2>/dev/null || true)"
|
|
root_pk="$(lsblk -no PKNAME "$root_src" 2>/dev/null | head -1)"
|
|
if [ "${ALLOW_ANY_DISK:-0}" != "1" ]; then
|
|
# Fail closed: an empty PKNAME (LVM/btrfs/overlay root, empty root_src)
|
|
# would otherwise leave the guard comparing against a bare "/dev/".
|
|
[ -n "$root_pk" ] || { echo "!! cannot determine root disk (lsblk PKNAME empty); set ALLOW_ANY_DISK=1 to override"; exit 1; }
|
|
if [ "$DEV" = "/dev/$root_pk" ]; then
|
|
echo "!! refusing to erase the root disk $DEV (set ALLOW_ANY_DISK=1 to override)"; exit 1
|
|
fi
|
|
fi
|
|
echo "== target: $DEV =="; lsblk -o NAME,SIZE,FSTYPE,LABEL "$DEV"
|
|
read -rp "Repartition and ERASE $DEV? [type YES] " ok
|
|
[ "$ok" = "YES" ] || { echo "aborted"; exit 1; }
|
|
|
|
# A/B partitions are 32M - far larger than the signed disk_app test payload
|
|
# (~64KB). They intentionally need not match WOLFBOOT_PARTITION_SIZE (0x4000000
|
|
# = 64M) in cm4_emmc.config: the disk path reads only the image at the
|
|
# partition start, so the partition just has to be big enough to hold it.
|
|
echo "== GPT: p1 boot 128M / p2 image_a 32M / p3 image_b 32M =="
|
|
sgdisk --zap-all "$DEV" >/dev/null
|
|
sgdisk -n 1:2048:+128M -t 1:0700 -c 1:boot "$DEV" >/dev/null
|
|
sgdisk -n 2:0:+32M -t 2:8300 -c 2:image_a "$DEV" >/dev/null
|
|
sgdisk -n 3:0:+32M -t 3:8300 -c 3:image_b "$DEV" >/dev/null
|
|
partprobe "$DEV"; udevadm settle
|
|
P1="${DEV}1"; P2="${DEV}2"; P3="${DEV}3"
|
|
# some kernels use pN naming for nvme/mmc; handle 'p' suffix
|
|
[ -b "$P1" ] || { P1="${DEV}p1"; P2="${DEV}p2"; P3="${DEV}p3"; }
|
|
# Assert every node exists before any dd: a missing node makes dd silently
|
|
# create a regular file instead of writing the partition.
|
|
for p in "$P1" "$P2" "$P3"; do
|
|
[ -b "$p" ] || { echo "!! partition node $p missing (partprobe/udev race?)"; exit 1; }
|
|
done
|
|
|
|
echo "== FAT boot partition ($P1) =="
|
|
mkfs.vfat -n BOOT "$P1" >/dev/null
|
|
MP=$(mktemp -d)
|
|
# Clean up the mount point on exit even if mount/cp fails under set -e. A
|
|
# RETURN trap does NOT fire when set -e aborts inside a function; EXIT does.
|
|
# The mountpoint -q guard makes it idempotent with the explicit umount below.
|
|
trap 'mountpoint -q "$MP" && umount "$MP"; rmdir "$MP" 2>/dev/null || true' EXIT
|
|
mount "$P1" "$MP"
|
|
cp "$HERE"/fw/start4.elf "$HERE"/fw/fixup4.dat "$HERE"/fw/bcm2711-rpi-cm4.dtb \
|
|
"$HERE"/fw/config.txt "$HERE"/fw/kernel8.img "$MP"/
|
|
sync; umount "$MP"
|
|
|
|
echo "== raw signed images -> A ($P2, v1), B ($P3, v2) =="
|
|
dd if="$HERE/disk_app_v1_signed.bin" of="$P2" bs=4k conv=fsync status=none
|
|
dd if="$HERE/disk_app_v2_signed.bin" of="$P3" bs=4k conv=fsync status=none
|
|
sync
|
|
echo "== done. Set BOOT switch OFF, power-cycle, and watch the CM4 mini-UART console. =="
|
|
}
|
|
|
|
prep_artifacts
|
|
# if/else (not an AND-OR list): calling write_device as the left operand of
|
|
# '&&' would disable set -e for its whole body, so a failed sgdisk/dd would be
|
|
# silently ignored and a half-written eMMC reported as success.
|
|
if [ -n "$DEV" ]; then
|
|
write_device
|
|
else
|
|
echo "(no device given; artifacts only. Re-run with: sudo bash $0 /dev/sdN)"
|
|
fi
|