|
| 1 | +#!/usr/bin/env bash |
| 2 | +# build-initramfs.sh — generate the first-boot provisioning initramfs.cpio.gz |
| 3 | +# from tracked/pinned sources (the in-forge generator; replaces the hand-built, |
| 4 | +# never-committed blob that was issue-class with #6/#8). |
| 5 | +# |
| 6 | +# The provisioning initramfs is what boot.img's ramdisk node carries: a static |
| 7 | +# busybox shell + /init + ubiprog. On FIRST boot /init rewrites the SPI-NAND |
| 8 | +# rootfs partition through the kernel's reliable write path (ubiprog), working |
| 9 | +# around the rkbin loader's weak programming of some erase blocks (PEBs 3/4…). |
| 10 | +# A marker file makes later boots skip the rewrite and switch_root to the real |
| 11 | +# buildroot rootfs. See board/aes/initramfs/init + document/notes/26. |
| 12 | +# |
| 13 | +# Reproducibility: everything is built from source under the forge toolchain |
| 14 | +# (Arm GNU gcc 15.2, arm-none-linux-gnueabihf) — NO ATK vendor-sdk dependency: |
| 15 | +# busybox ← pins/busybox (upstream 1.36.1 tarball, sha256-pinned) — static |
| 16 | +# ubiprog ← board/aes/rootfs/ubiprog.c (tracked) — static |
| 17 | +# /init ← board/aes/initramfs/init (tracked) |
| 18 | +# gcc 15.2 builds busybox 1.36.1 clean + static and reproduces the hand-built |
| 19 | +# original byte-for-byte on SIZE (busybox 1414692 B, ubiprog 357400 B). |
| 20 | +# |
| 21 | +# Output: ${BRINGUP}/fit/initramfs.cpio.gz (the path pack-fit.sh reads; correctly |
| 22 | +# gitignored as a regenerated artifact, NOT a missing input). Idempotent: skips |
| 23 | +# the cpio repack when busybox + ubiprog + /init are unchanged. |
| 24 | +# |
| 25 | +# Usage: |
| 26 | +# scripts/build-initramfs.sh [--out <cpio.gz>] [--clean] |
| 27 | +# --out <path> output cpio.gz (default: board/aes/fit/initramfs.cpio.gz) |
| 28 | +# --clean force a full rebuild (rm busybox build + cpio) |
| 29 | +# Seam: bash-first; arg parsing leaves a Python seam for later. |
| 30 | +set -euo pipefail |
| 31 | +_SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) |
| 32 | +# shellcheck disable=SC1091 |
| 33 | +source "${_SCRIPT_DIR}/lib/env.sh" # PROJECT_ROOT + BRINGUP + SRC_DIR |
| 34 | +# shellcheck disable=SC1091 |
| 35 | +source "${_SCRIPT_DIR}/lib/log.sh" |
| 36 | +# shellcheck disable=SC1091 |
| 37 | +source "${_SCRIPT_DIR}/lib/toolchain.sh" # CROSS_COMPILE / ARCH / TOOLCHAIN_BIN_DIR |
| 38 | + |
| 39 | +OUT_CPIO="${BRINGUP}/fit/initramfs.cpio.gz" |
| 40 | +CLEAN=0 |
| 41 | +while [[ $# -gt 0 ]]; do |
| 42 | + case "$1" in |
| 43 | + --out) OUT_CPIO="$2"; shift 2;; |
| 44 | + --clean) CLEAN=1; shift;; |
| 45 | + -h|--help) sed -n '2,30p' "$0"; exit 0;; |
| 46 | + *) die "unknown arg: $1";; |
| 47 | + esac |
| 48 | +done |
| 49 | + |
| 50 | +check_toolchain || die "toolchain not on PATH. Run: source scripts/env-setup.sh" |
| 51 | +CC="${CROSS_COMPILE}gcc" |
| 52 | +# Reproducibility: pin the build timestamp so two builds converge (busybox embeds |
| 53 | +# a build date; gcc respects SOURCE_DATE_EPOCH). gzip -n (below) drops the gzip |
| 54 | +# header mtime. Not byte-identical to the hand-built original (that used ATK gcc |
| 55 | +# 10.3) but functionally + size-equivalent and rebuildable from source. |
| 56 | +export SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-0}" |
| 57 | + |
| 58 | +# --- inputs (all tracked/pinned in-repo) --- |
| 59 | +PIN="${PROJECT_ROOT}/pins/busybox" |
| 60 | +[[ -f "$PIN" ]] || die "busybox pin missing: $PIN" |
| 61 | +read -r BB_URL BB_SHA256 < <(grep -vE '^[[:space:]]*#' "$PIN" | grep -vE '^[[:space:]]*$' | head -1) |
| 62 | +[[ -n "$BB_URL" && -n "$BB_SHA256" ]] || die "busybox pin malformed (want '<url> <sha256>'): $PIN" |
| 63 | +BB_VER="busybox-1.36.1" # matches the pinned tarball's top-level dir |
| 64 | +BB_SRC="${PROJECT_ROOT}/${SRC_DIR:-third_party/src}/${BB_VER}" |
| 65 | +UBIPROG_SRC="${BRINGUP}/rootfs/ubiprog.c" |
| 66 | +INIT_SRC="${BRINGUP}/initramfs/init" |
| 67 | +for f in "$UBIPROG_SRC" "$INIT_SRC"; do |
| 68 | + [[ -f "$f" ]] || die "initramfs source missing: $f" |
| 69 | +done |
| 70 | + |
| 71 | +# --- ensure busybox source: download + sha256-verify + extract (idempotent) --- |
| 72 | +ensure_busybox_source() { |
| 73 | + [[ -d "$BB_SRC" ]] && return 0 |
| 74 | + local tarball="${BB_SRC}.tar.bz2" |
| 75 | + mkdir -p "$(dirname "$BB_SRC")" |
| 76 | + log_info "downloading $BB_VER ($BB_URL)" |
| 77 | + # forge standard: curl first (web-access-via-curl memory), wget fallback |
| 78 | + if ! curl -fL --retry 3 --connect-timeout 30 -o "$tarball" "$BB_URL" 2>/dev/null; then |
| 79 | + wget -q --tries=3 --timeout=30 -O "$tarball" "$BB_URL" |
| 80 | + fi |
| 81 | + log_info "verifying sha256 ($BB_SHA256)" |
| 82 | + echo "$BB_SHA256 $tarball" | sha256sum -c - >/dev/null \ |
| 83 | + || { rm -f "$tarball"; die "busybox tarball sha256 mismatch ($BB_URL). pin may be wrong."; } |
| 84 | + log_info "extracting" |
| 85 | + tar xf "$tarball" -C "$(dirname "$BB_SRC")" |
| 86 | + rm -f "$tarball" |
| 87 | + [[ -d "$BB_SRC" ]] || die "busybox extract did not create $BB_SRC" |
| 88 | +} |
| 89 | + |
| 90 | +# --- build static busybox (in-tree; incremental) --- |
| 91 | +build_busybox() { |
| 92 | + local bb="$BB_SRC/busybox" |
| 93 | + # force-rebuild path (--clean or missing binary) |
| 94 | + if [[ "$CLEAN" == 1 ]]; then rm -f "$bb" "$BB_SRC/.config"; fi |
| 95 | + [[ -x "$bb" ]] && return 0 |
| 96 | + ( cd "$BB_SRC" |
| 97 | + [[ -f .config ]] || make ARCH="$ARCH" CROSS_COMPILE="$CROSS_COMPILE" defconfig >/dev/null |
| 98 | + # static link (no libc in the ramdisk). sed is idempotent. |
| 99 | + sed -i 's/^# CONFIG_STATIC is not set$/CONFIG_STATIC=y/' .config |
| 100 | + grep -q 'CONFIG_STATIC=y' .config || echo 'CONFIG_STATIC=y' >> .config |
| 101 | + log_info "building static busybox (gcc 15.2)…" |
| 102 | + make ARCH="$ARCH" CROSS_COMPILE="$CROSS_COMPILE" -j"$(nproc)" >/dev/null |
| 103 | + ) |
| 104 | + [[ -x "$bb" ]] || die "busybox build produced no binary ($BB_SRC)" |
| 105 | + file "$bb" | grep -q 'statically linked' || die "busybox is NOT static (CONFIG_STATIC=y?)" |
| 106 | +} |
| 107 | + |
| 108 | +# --- assemble the ramdisk tree + pack cpio.gz --- |
| 109 | +pack_cpio() { |
| 110 | + local root |
| 111 | + root=$(mktemp -d) |
| 112 | + trap 'rm -rf "$root"' RETURN |
| 113 | + # dir layout (matches the board-verified cpio) |
| 114 | + mkdir -p "$root"/{bin,sbin,proc,sys,dev,etc,usr/bin,usr/sbin,tmp,root} |
| 115 | + # static binaries |
| 116 | + cp "$BB_SRC/busybox" "$root/bin/busybox"; chmod +x "$root/bin/busybox" |
| 117 | + log_info "building static ubiprog (ubiprog.c)…" |
| 118 | + "$CC" -static -O2 -s "$UBIPROG_SRC" -o "$root/bin/ubiprog" |
| 119 | + # minimal pre-created applet symlinks (/init calls the rest via /bin/busybox |
| 120 | + # full-path or after `busybox --install -s` at boot). Matches the original. |
| 121 | + local a |
| 122 | + for a in sh mount umount ls cat echo uname mkdir ps dmesg pwd vi; do |
| 123 | + ln -sf busybox "$root/bin/$a" |
| 124 | + done |
| 125 | + # /init (the provisioning script) |
| 126 | + cp "$INIT_SRC" "$root/init"; chmod +x "$root/init" |
| 127 | + |
| 128 | + mkdir -p "$(dirname "$OUT_CPIO")" |
| 129 | + log_info "packing cpio.gz → $OUT_CPIO" |
| 130 | + ( cd "$root" && find . | cpio -o -H newc --quiet ) | gzip -9 -n > "$OUT_CPIO" |
| 131 | + log_ok "initramfs.cpio.gz → $OUT_CPIO ($(stat -c%s "$OUT_CPIO") B)" |
| 132 | + log_ok " busybox=$(stat -c%s "$root/bin/busybox") B ubiprog=$(stat -c%s "$root/bin/ubiprog") B" |
| 133 | +} |
| 134 | + |
| 135 | +ensure_busybox_source |
| 136 | +build_busybox |
| 137 | +pack_cpio |
| 138 | +log_info "next: scripts/forge.sh pack (pack-fit incbin's it into boot.img)" |
0 commit comments