boot2

Playing with the boostrap
git clone https://git.ryansepassi.com/git/boot2.git
Log | Files | Refs | README

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 }