lib-arch.sh (9909B)
1 # lib-arch.sh — single source for arch + driver setup shared by 2 # boot/boot.sh, boot/boot{0..7}.sh, lib-pipeline.sh, lib-runscm.sh. 3 # 4 # Public entry points (call in this order from a bootN.sh): 5 # 6 # bootlib_init <stage> <arch> # validate <arch>, cd to repo root, 7 # # set ARCH/PLATFORM/KERNEL_NAME/ 8 # # MUSL_ARCH/DRIVER/BOOT_STAGE/BOOT_TAG. 9 # driver_init [<image-kind>] # set OUT/STAGE; podman: build IMAGE 10 # # if missing (image-kind ∈ busybox| 11 # # empty; default busybox); seed: 12 # # verify boot6 kernel exists. 13 # require_src # die if build/$ARCH/src/ missing. 14 # require_prev <dir> <name>... # die if any <dir>/<name> is 15 # # missing or non-executable. 16 # require_file <path> [<hint>] # die if <path> missing; print a 17 # # uniform diagnostic with hint. 18 # 19 # After bootlib_init, the following shell vars are set/exported: 20 # ARCH input architecture token (aarch64|amd64|riscv64|riscv32) 21 # ROOT repo root (cwd is set to ROOT) 22 # DRIVER podman|seed (defaults to podman) 23 # PLATFORM target platform for podman, empty when the controller 24 # container deliberately stays native (riscv32) 25 # PODMAN_PLATFORM_ARGS optional `--platform=...` argument 26 # KERNEL_NAME Image (aarch64) | kernel.elf (amd64,riscv64) 27 # MUSL_ARCH aarch64 | x86_64 | riscv64 (empty before the RV32 TCC port) 28 # TCC_SUPPORTED 1 when boot3+ is implemented, otherwise 0 29 # BOOT_TAG "<stage>/<driver>/<arch>" for log prefixes 30 # BOOT_STAGE stage name as passed in (boot0|boot1|...) 31 # 32 # After driver_init (boot stages only — prep-* skip it): 33 # OUT build/$ARCH/$DRIVER/$BOOT_STAGE (stage output dir) 34 # STAGE build/$ARCH/$DRIVER/.$BOOT_STAGE-stage (scratch staging dir) 35 # podman: IMAGE 36 # seed: KERNEL_IMAGE, EXTRACT, SEED_ARCH 37 38 bootlib_init() { 39 _stage=$1; _arch=${2:-} 40 [ -n "$_stage" ] || { echo "lib-arch: bootlib_init: stage required" >&2; exit 2; } 41 case "$_arch" in 42 aarch64|amd64|riscv64|riscv32) ;; 43 *) echo "usage: $0 <aarch64|amd64|riscv64|riscv32>" >&2; exit 2 ;; 44 esac 45 ARCH=$_arch 46 ROOT=$(cd "$(dirname "$0")/.." && pwd) 47 cd "$ROOT" 48 DRIVER=${DRIVER:-podman} 49 case "$DRIVER" in 50 podman|seed) ;; 51 *) echo "[$_stage/$DRIVER/$ARCH] unknown DRIVER=$DRIVER (expected podman|seed)" >&2; exit 2 ;; 52 esac 53 BOOT_STAGE=$_stage 54 BOOT_TAG="$_stage/$DRIVER/$ARCH" 55 BOOT_T0=$(date +%s) 56 case "$ARCH" in 57 aarch64) PLATFORM=linux/arm64; KERNEL_NAME=Image; MUSL_ARCH=aarch64; TCC_SUPPORTED=1 ;; 58 amd64) PLATFORM=linux/amd64; KERNEL_NAME=kernel.elf; MUSL_ARCH=x86_64; TCC_SUPPORTED=1 ;; 59 riscv64) PLATFORM=linux/riscv64; KERNEL_NAME=kernel.elf; MUSL_ARCH=riscv64; TCC_SUPPORTED=1 ;; 60 # No OCI image platform names RV32. Keep the controller image native 61 # and let the host's qemu-riscv32 binfmt handler execute target tools. 62 riscv32) PLATFORM=; KERNEL_NAME=; MUSL_ARCH=; TCC_SUPPORTED=0 ;; 63 esac 64 PODMAN_PLATFORM_ARGS= 65 [ -n "$PLATFORM" ] && PODMAN_PLATFORM_ARGS="--platform=$PLATFORM" 66 if [ "$ARCH" = riscv32 ] && [ "$DRIVER" = seed ]; then 67 echo "[$BOOT_TAG] DRIVER=seed is unavailable for riscv32 (boot6 requires the unimplemented TCC target)" >&2 68 exit 2 69 fi 70 export ARCH ROOT DRIVER PLATFORM PODMAN_PLATFORM_ARGS KERNEL_NAME MUSL_ARCH \ 71 TCC_SUPPORTED BOOT_TAG BOOT_STAGE BOOT_T0 72 trap _bootlib_finish EXIT 73 } 74 75 # _bootlib_finish — EXIT trap installed by bootlib_init. Prints 76 # `[$BOOT_TAG] done in Xs (cum Ys)` (or `failed after Xs` on error). 77 # On success, records the elapsed time so later stages can sum the 78 # chain. Cumulative = sum of all per-stage .timing files relevant to 79 # the current $ARCH/$DRIVER. 80 _bootlib_finish() { 81 _exit=$? 82 [ -n "${BOOT_T0:-}" ] || return 0 83 _elapsed=$(( $(date +%s) - BOOT_T0 )) 84 if [ "$_exit" != 0 ]; then 85 echo "[$BOOT_TAG] failed after ${_elapsed}s (exit=$_exit)" >&2 86 return 0 87 fi 88 # Record this stage's time. Boot stages have OUT (set by driver_init); 89 # the orchestrator (BOOT_STAGE=boot) doesn't write — its time would 90 # double-count. Other stages without OUT (prep-src) write 91 # to a per-arch sidecar dir. 92 if [ "$BOOT_STAGE" != boot ]; then 93 if [ -n "${OUT:-}" ] && [ -d "$OUT" ]; then 94 echo "$_elapsed" > "$OUT/.timing" 95 elif [ -d "build/$ARCH" ]; then 96 mkdir -p "build/$ARCH/.timings" 97 echo "$_elapsed" > "build/$ARCH/.timings/$BOOT_STAGE" 98 fi 99 fi 100 # Cumulative: sum boot-stage timings for this driver + driver- 101 # independent prep timings. Glob may not match — guard each path. 102 _cum=0 103 for _f in \ 104 "build/$ARCH/$DRIVER"/*/.timing \ 105 "build/$ARCH/.timings"/* 106 do 107 [ -f "$_f" ] || continue 108 _v=$(cat "$_f" 2>/dev/null) || continue 109 case "$_v" in *[!0-9]*|'') continue ;; esac 110 _cum=$((_cum + _v)) 111 done 112 echo "[$BOOT_TAG] done in ${_elapsed}s (cum ${_cum}s)" 113 } 114 115 driver_init() { 116 _image_kind=${1:-busybox} 117 case "$_image_kind" in 118 busybox|empty) ;; 119 *) echo "[$BOOT_TAG] driver_init: image-kind must be busybox|empty (got $_image_kind)" >&2; exit 2 ;; 120 esac 121 OUT=build/$ARCH/$DRIVER/$BOOT_STAGE 122 STAGE=build/$ARCH/$DRIVER/.$BOOT_STAGE-stage 123 export OUT STAGE 124 case "$DRIVER" in 125 podman) 126 # RV32 has no OCI platform identifier. Resolve the Podman 127 # server's native architecture here (rather than the client 128 # host's uname) and use that for the controller image. Target 129 # executables still run through the server's RV32 binfmt entry. 130 if [ "$ARCH" = riscv32 ]; then 131 _controller_arch=$(podman info --format '{{.Host.Arch}}') 132 case "$_controller_arch" in 133 aarch64) _controller_arch=arm64 ;; 134 x86_64) _controller_arch=amd64 ;; 135 arm64|amd64|riscv64) ;; 136 *) echo "[$BOOT_TAG] unsupported Podman controller architecture: $_controller_arch" >&2; exit 2 ;; 137 esac 138 PLATFORM=linux/$_controller_arch 139 PODMAN_PLATFORM_ARGS="--platform=$PLATFORM" 140 export PLATFORM PODMAN_PLATFORM_ARGS 141 fi 142 IMAGE=boot2-$_image_kind:$ARCH 143 _build_image=0 144 if ! podman image exists "$IMAGE"; then 145 _build_image=1 146 elif [ "$ARCH" = riscv32 ]; then 147 _image_arch=$(podman image inspect "$IMAGE" --format '{{.Architecture}}') 148 [ "$_image_arch" = "$_controller_arch" ] || { 149 echo "[$BOOT_TAG] rebuilding $IMAGE for native controller $_controller_arch (was $_image_arch)" 150 _build_image=1 151 } 152 fi 153 if [ "$_build_image" = 1 ]; then 154 echo "[$BOOT_TAG] building $IMAGE" 155 # Containerfile.empty drops /etc resolver state etc.; no-cache 156 # avoids a stale layer surviving an upstream tag bump. 157 _no_cache= 158 [ "$_image_kind" = empty ] && _no_cache=--no-cache 159 podman build $_no_cache $PODMAN_PLATFORM_ARGS -t "$IMAGE" \ 160 -f boot/containers/Containerfile.$_image_kind boot/containers/ 161 fi 162 export IMAGE 163 ;; 164 seed) 165 # DRIVER=seed always consumes the podman-built boot6 kernel — 166 # tcc2 is platform-agnostic but we settled on a single canonical 167 # build location to reduce surface area. 168 KERNEL_IMAGE=$ROOT/build/$ARCH/podman/boot6/$KERNEL_NAME 169 EXTRACT=$ROOT/seed-kernel/scripts/extract-blk.sh 170 [ -f "$KERNEL_IMAGE" ] || { 171 echo "[$BOOT_TAG] missing $KERNEL_IMAGE — run ./boot/boot.sh $ARCH (default DRIVER=podman) first" >&2 172 exit 1 173 } 174 export KERNEL_IMAGE EXTRACT 175 export SEED_ARCH=$ARCH 176 ;; 177 esac 178 } 179 180 # Stop at the intentional RV32 boundary with one consistent diagnostic. 181 # Keep this in the shared library so direct boot3..boot7 invocations fail 182 # before probing for TCC, musl, or seed-kernel inputs that do not exist yet. 183 require_tcc_target() { 184 [ "$TCC_SUPPORTED" = 1 ] || { 185 echo "[$BOOT_TAG] TCC support is intentionally unimplemented for $ARCH; RV32 currently ends after boot2" >&2 186 exit 2 187 } 188 } 189 190 require_prev() { 191 _dir=$1; shift 192 for _n in "$@"; do 193 [ -x "$_dir/$_n" ] || { 194 _stage_name=$(basename "$_dir") 195 case "$_stage_name" in 196 boot*) _hint="run boot/$_stage_name.sh $ARCH" ;; 197 *) _hint="rebuild $_dir" ;; 198 esac 199 echo "[$BOOT_TAG] missing prerequisite: $_dir/$_n ($_hint)" >&2 200 exit 1 201 } 202 done 203 } 204 205 # require_src — assert build/$ARCH/src/ exists (the canonical generated 206 # source tree built by bootprep/prep-src.sh). Every bootN.sh needs it. 207 require_src() { 208 [ -d "build/$ARCH/src" ] || { 209 echo "[$BOOT_TAG] missing build/$ARCH/src — run bootprep/prep-src.sh $ARCH" >&2 210 exit 1 211 } 212 } 213 214 # require_file <path> [<hint>] — assert <path> exists; print a uniform 215 # "[$BOOT_TAG] missing <path> — <hint>" diagnostic on failure. 216 require_file() { 217 _path=$1; _hint=${2:-} 218 [ -e "$_path" ] || { 219 if [ -n "$_hint" ]; then 220 echo "[$BOOT_TAG] missing $_path — $_hint" >&2 221 else 222 echo "[$BOOT_TAG] missing $_path" >&2 223 fi 224 exit 1 225 } 226 }