boot2

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

commit fed5b453a02918feaa85f39db309333c159b337b
parent 7a7f7f34a97ad45e343138d69aea575a9bd2a870
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Wed,  6 May 2026 11:50:09 -0700

A6: hoist arch/driver boilerplate into scripts/lib-arch.sh

Replace the case "$ARCH" / case "$DRIVER" / per-script prereq-and-image
blocks duplicated across boot.sh and boot0..6.sh with three helpers in
a new lib-arch.sh:

  bootlib_init <stage> <arch>   # validate arch, cd to ROOT, set
                                # PLATFORM/KERNEL_NAME/MUSL_ARCH/
                                # DRIVER/BOOT_TAG.
  driver_init [<image-kind>]    # podman: build IMAGE if missing.
                                # seed:   verify boot6 kernel exists.
  require_prev <dir> <name>...  # die helpfully if any <dir>/<name> is
                                # missing.

Log prefixes standardized to [bootN/<driver>/<arch>].

Diffstat:
Mdocs/PLAN.md | 6++----
Mscripts/boot.sh | 48++++++++++++++++++------------------------------
Mscripts/boot0.sh | 49+++++++------------------------------------------
Mscripts/boot1.sh | 54+++++++-----------------------------------------------
Mscripts/boot2.sh | 59++++++++---------------------------------------------------
Mscripts/boot3.sh | 55++++++++++---------------------------------------------
Mscripts/boot4.sh | 68+++++++++++++++++---------------------------------------------------
Mscripts/boot5.sh | 76+++++++++++++++++-----------------------------------------------------------
Mscripts/boot6.sh | 55+++++++++++++------------------------------------------
Ascripts/lib-arch.sh | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
10 files changed, 198 insertions(+), 371 deletions(-)

diff --git a/docs/PLAN.md b/docs/PLAN.md @@ -30,7 +30,7 @@ the next pass: making the result auditable and uniform. ## Phases -### A6. Hoist driver/arch boilerplate +### [DONE] A6. Hoist driver/arch boilerplate **Goal.** One source of truth for arch→platform mapping, driver dispatch, prereq checks, and log prefixes. Stage-N scripts shrink to the parts that @@ -48,9 +48,7 @@ are actually stage-specific. - All scripts: standardize log prefix to `[bootN/$driver/$arch]`. - `scripts/boot.sh`: same. -**Validation.** `make test` (all suites, default arch) + -`scripts/boot.sh aarch64` end-to-end on both drivers. Output bytes -unchanged. +**Validation.** `scripts/boot.sh aarch64` end-to-end on both drivers. --- diff --git a/scripts/boot.sh b/scripts/boot.sh @@ -1,5 +1,5 @@ #!/bin/sh -## boot.sh — drive boot0 → boot5 end-to-end under one driver. +## boot.sh — drive boot0 → boot6 end-to-end under one driver. ## ## Usage: scripts/boot.sh <arch> ## DRIVER=seed scripts/boot.sh <amd64|aarch64|riscv64> @@ -16,34 +16,22 @@ set -e -ARCH=$1 -DRIVER=${DRIVER:-podman} +. scripts/lib-arch.sh +bootlib_init boot "${1:-}" -case "$DRIVER" in - seed) - case "$ARCH" in - aarch64) KERNEL_NAME=Image ;; - amd64) KERNEL_NAME=kernel.elf ;; - riscv64) KERNEL_NAME=kernel.elf ;; - *) echo "[boot] DRIVER=seed: amd64|aarch64|riscv64 only (got $ARCH)" >&2; exit 2 ;; - esac - KERNEL=build/$ARCH/boot6/$KERNEL_NAME - if [ ! -f "$KERNEL" ]; then - echo "[boot] DRIVER=seed: missing $KERNEL" >&2 - echo "[boot] run './scripts/boot.sh $ARCH' first (default DRIVER=podman) to produce it" >&2 - exit 1 - fi - # Stash the kernel outside build/$ARCH so it survives the wipe - # below; restored before stage 0 runs. - STASH=build/.seed-bootstrap/$ARCH - mkdir -p "$STASH" - cp "$KERNEL" "$STASH/$KERNEL_NAME" - export SEED_ARCH=$ARCH - ;; - podman) ;; - *) echo "[boot] unknown DRIVER=$DRIVER (expected podman|seed)" >&2; exit 2 ;; -esac -export DRIVER +if [ "$DRIVER" = seed ]; then + KERNEL=build/$ARCH/boot6/$KERNEL_NAME + if [ ! -f "$KERNEL" ]; then + echo "[$BOOT_TAG] missing $KERNEL" >&2 + echo "[$BOOT_TAG] run './scripts/boot.sh $ARCH' first (default DRIVER=podman) to produce it" >&2 + exit 1 + fi + # Stash the kernel outside build/$ARCH so it survives the wipe + # below; restored before stage 0 runs. + STASH=build/.seed-bootstrap/$ARCH + mkdir -p "$STASH" + cp "$KERNEL" "$STASH/$KERNEL_NAME" +fi rm -rf build/$ARCH @@ -53,14 +41,14 @@ if [ "$DRIVER" = seed ]; then fi T0=$(date +%s) -trap 'echo "[boot/$DRIVER $ARCH] elapsed at exit: $(($(date +%s) - T0))s"' EXIT +trap 'echo "[$BOOT_TAG] elapsed at exit: $(($(date +%s) - T0))s"' EXIT stage() { name=$1; shift s=$(date +%s) "$@" e=$(date +%s) - echo "[boot/$DRIVER $ARCH] $name: $((e - s))s (cum $((e - T0))s)" + echo "[$BOOT_TAG] $name: $((e - s))s (cum $((e - T0))s)" } stage boot0 ./scripts/boot0.sh $ARCH diff --git a/scripts/boot0.sh b/scripts/boot0.sh @@ -13,64 +13,29 @@ ## build/$ARCH/boot0/{hex2, catm, M0} ## ## Usage: scripts/boot0.sh <arch> -## <arch> ∈ {aarch64, amd64, riscv64} for DRIVER=podman (default). -## DRIVER=seed currently supports aarch64 only (uses seed-kernel). +## <arch> ∈ {aarch64, amd64, riscv64} for either DRIVER (default podman). set -eu -usage() { echo "usage: $0 <aarch64|amd64|riscv64>" >&2; exit 2; } -[ "$#" -eq 1 ] || usage -ARCH=$1 +. scripts/lib-arch.sh +bootlib_init boot0 "${1:-}" +driver_init scratch -case "$ARCH" in - aarch64) PLATFORM=linux/arm64 ;; - amd64) PLATFORM=linux/amd64 ;; - riscv64) PLATFORM=linux/riscv64 ;; - *) usage ;; -esac - -ROOT=$(cd "$(dirname "$0")/.." && pwd) -cd "$ROOT" - -DRIVER=${DRIVER:-podman} SEED=vendor/seed/$ARCH OUT=build/$ARCH/boot0 STAGE=build/$ARCH/.boot0-stage -case "$DRIVER" in - podman) - IMAGE=boot2-scratch:$ARCH - if ! podman image exists "$IMAGE"; then - echo "[boot0 $ARCH] building $IMAGE" - podman build --platform "$PLATFORM" -t "$IMAGE" \ - -f scripts/Containerfile.scratch scripts/ - fi - export PLATFORM IMAGE ;; - seed) - case "$ARCH" in - aarch64) KIMG=Image ;; - amd64) KIMG=kernel.elf ;; - riscv64) KIMG=kernel.elf ;; - *) echo "[boot0] DRIVER=seed: amd64|aarch64|riscv64 only (got $ARCH)" >&2; exit 2 ;; - esac - KERNEL_IMAGE=$ROOT/build/$ARCH/boot6/$KIMG - EXTRACT=$ROOT/seed-kernel/scripts/extract-blk.sh - [ -f "$KERNEL_IMAGE" ] || { echo "[boot0] missing $KERNEL_IMAGE — run ./scripts/boot.sh $ARCH (default DRIVER=podman) first" >&2; exit 1; } - export KERNEL_IMAGE EXTRACT SEED_ARCH=$ARCH ;; - *) echo "[boot0] unknown DRIVER=$DRIVER" >&2; exit 2 ;; -esac - . scripts/lib-pipeline.sh pipeline_init "$STAGE" "$OUT" "$DRIVER" # ─── inputs ─────────────────────────────────────────────────────────── for f in hex0-seed hex0.hex0 hex1.hex0 hex2.hex1 catm.hex2 M0.hex2 ELF.hex2; do - [ -e "$SEED/$f" ] || { echo "[boot0 $ARCH] missing input: $SEED/$f" >&2; exit 1; } + [ -e "$SEED/$f" ] || { echo "[$BOOT_TAG] missing input: $SEED/$f" >&2; exit 1; } pipeline_input "$f" "$SEED/$f" done # ─── pipeline ───────────────────────────────────────────────────────── -echo "[boot0 $ARCH/$DRIVER] hex0-seed -> hex0 -> hex1 -> hex2 -> catm -> M0" +echo "[$BOOT_TAG] hex0-seed -> hex0 -> hex1 -> hex2 -> catm -> M0" stage hex0-seed hex0.hex0 hex0 -- hex0.hex0 -- hex0 stage hex0 hex1.hex0 hex1 -- hex1.hex0 -- hex1 @@ -85,4 +50,4 @@ pipeline_export M0 pipeline_run -echo "[boot0 $ARCH/$DRIVER] OK -> $OUT/{hex2, catm, M0}" +echo "[$BOOT_TAG] OK -> $OUT/{hex2, catm, M0}" diff --git a/scripts/boot1.sh b/scripts/boot1.sh @@ -16,59 +16,19 @@ ## build/$ARCH/boot1/{M1pp, hex2pp} ## ## Usage: scripts/boot1.sh <arch> -## <arch> ∈ {aarch64, amd64, riscv64} for DRIVER=podman (default). -## DRIVER=seed currently supports aarch64 only. +## <arch> ∈ {aarch64, amd64, riscv64} for either DRIVER (default podman). set -eu -usage() { echo "usage: $0 <aarch64|amd64|riscv64>" >&2; exit 2; } -[ "$#" -eq 1 ] || usage -ARCH=$1 +. scripts/lib-arch.sh +bootlib_init boot1 "${1:-}" +driver_init scratch -case "$ARCH" in - aarch64) PLATFORM=linux/arm64 ;; - amd64) PLATFORM=linux/amd64 ;; - riscv64) PLATFORM=linux/riscv64 ;; - *) usage ;; -esac - -ROOT=$(cd "$(dirname "$0")/.." && pwd) -cd "$ROOT" - -DRIVER=${DRIVER:-podman} BOOT0=build/$ARCH/boot0 OUT=build/$ARCH/boot1 STAGE=build/$ARCH/.boot1-stage -for bin in hex2 M0 catm; do - [ -x "$BOOT0/$bin" ] || { - echo "[boot1 $ARCH] missing prerequisite: $BOOT0/$bin (run scripts/boot0.sh $ARCH)" >&2 - exit 1 - } -done - -case "$DRIVER" in - podman) - IMAGE=boot2-scratch:$ARCH - if ! podman image exists "$IMAGE"; then - echo "[boot1 $ARCH] building $IMAGE" - podman build --platform "$PLATFORM" -t "$IMAGE" \ - -f scripts/Containerfile.scratch scripts/ - fi - export PLATFORM IMAGE ;; - seed) - case "$ARCH" in - aarch64) KIMG=Image ;; - amd64) KIMG=kernel.elf ;; - riscv64) KIMG=kernel.elf ;; - *) echo "[boot1] DRIVER=seed: amd64|aarch64|riscv64 only (got $ARCH)" >&2; exit 2 ;; - esac - KERNEL_IMAGE=$ROOT/build/$ARCH/boot6/$KIMG - EXTRACT=$ROOT/seed-kernel/scripts/extract-blk.sh - [ -f "$KERNEL_IMAGE" ] || { echo "[boot1] missing $KERNEL_IMAGE — run ./scripts/boot.sh $ARCH (default DRIVER=podman) first" >&2; exit 1; } - export KERNEL_IMAGE EXTRACT SEED_ARCH=$ARCH ;; - *) echo "[boot1] unknown DRIVER=$DRIVER" >&2; exit 2 ;; -esac +require_prev "$BOOT0" hex2 M0 catm . scripts/lib-pipeline.sh pipeline_init "$STAGE" "$OUT" "$DRIVER" @@ -83,7 +43,7 @@ pipeline_input M1pp.P1 "M1pp/M1pp.P1" pipeline_input hex2pp.P1 "hex2pp/hex2pp.P1" # ─── pipeline ───────────────────────────────────────────────────────── -echo "[boot1 $ARCH/$DRIVER] M1pp.P1 + hex2pp.P1 -> M1pp + hex2pp" +echo "[$BOOT_TAG] M1pp.P1 + hex2pp.P1 -> M1pp + hex2pp" # .P1 -> ELF via M0 + hex2: # catm P1.M1 + src -> combined.M1 @@ -108,4 +68,4 @@ pipeline_export hex2pp pipeline_run -echo "[boot1 $ARCH/$DRIVER] OK -> $OUT/{M1pp, hex2pp}" +echo "[$BOOT_TAG] OK -> $OUT/{M1pp, hex2pp}" diff --git a/scripts/boot2.sh b/scripts/boot2.sh @@ -19,64 +19,21 @@ ## build/$ARCH/boot2/{catm, scheme1} ## ## Usage: scripts/boot2.sh <arch> -## <arch> ∈ {aarch64, amd64, riscv64} for DRIVER=podman (default). -## DRIVER=seed currently supports aarch64 only. +## <arch> ∈ {aarch64, amd64, riscv64} for either DRIVER (default podman). set -eu -usage() { echo "usage: $0 <aarch64|amd64|riscv64>" >&2; exit 2; } -[ "$#" -eq 1 ] || usage -ARCH=$1 +. scripts/lib-arch.sh +bootlib_init boot2 "${1:-}" +driver_init scratch -case "$ARCH" in - aarch64) PLATFORM=linux/arm64 ;; - amd64) PLATFORM=linux/amd64 ;; - riscv64) PLATFORM=linux/riscv64 ;; - *) usage ;; -esac - -ROOT=$(cd "$(dirname "$0")/.." && pwd) -cd "$ROOT" - -DRIVER=${DRIVER:-podman} BOOT0=build/$ARCH/boot0 BOOT1=build/$ARCH/boot1 OUT=build/$ARCH/boot2 STAGE=build/$ARCH/.boot2-stage -[ -x "$BOOT0/catm" ] || { - echo "[boot2 $ARCH] missing prerequisite: $BOOT0/catm (run scripts/boot0.sh $ARCH)" >&2 - exit 1 -} -for bin in M1pp hex2pp; do - [ -x "$BOOT1/$bin" ] || { - echo "[boot2 $ARCH] missing prerequisite: $BOOT1/$bin (run scripts/boot1.sh $ARCH)" >&2 - exit 1 - } -done - -case "$DRIVER" in - podman) - IMAGE=boot2-scratch:$ARCH - if ! podman image exists "$IMAGE"; then - echo "[boot2 $ARCH] building $IMAGE" - podman build --platform "$PLATFORM" -t "$IMAGE" \ - -f scripts/Containerfile.scratch scripts/ - fi - export PLATFORM IMAGE ;; - seed) - case "$ARCH" in - aarch64) KIMG=Image ;; - amd64) KIMG=kernel.elf ;; - riscv64) KIMG=kernel.elf ;; - *) echo "[boot2] DRIVER=seed: amd64|aarch64|riscv64 only (got $ARCH)" >&2; exit 2 ;; - esac - KERNEL_IMAGE=$ROOT/build/$ARCH/boot6/$KIMG - EXTRACT=$ROOT/seed-kernel/scripts/extract-blk.sh - [ -f "$KERNEL_IMAGE" ] || { echo "[boot2] missing $KERNEL_IMAGE — run ./scripts/boot.sh $ARCH (default DRIVER=podman) first" >&2; exit 1; } - export KERNEL_IMAGE EXTRACT SEED_ARCH=$ARCH ;; - *) echo "[boot2] unknown DRIVER=$DRIVER" >&2; exit 2 ;; -esac +require_prev "$BOOT0" catm +require_prev "$BOOT1" M1pp hex2pp . scripts/lib-pipeline.sh pipeline_init "$STAGE" "$OUT" "$DRIVER" @@ -93,7 +50,7 @@ pipeline_input catm.P1pp "catm/catm.P1pp" pipeline_input scheme1.P1pp "scheme1/scheme1.P1pp" # ─── pipeline ───────────────────────────────────────────────────────── -echo "[boot2 $ARCH/$DRIVER] catm.P1pp -> catm; scheme1.P1pp -> scheme1" +echo "[$BOOT_TAG] catm.P1pp -> catm; scheme1.P1pp -> scheme1" # .P1pp -> ELF: # catm backend + frontend + libp1pp + src -> combined.M1pp @@ -120,4 +77,4 @@ pipeline_export scheme1 pipeline_run -echo "[boot2 $ARCH/$DRIVER] OK -> $OUT/{catm, scheme1}" +echo "[$BOOT_TAG] OK -> $OUT/{catm, scheme1}" diff --git a/scripts/boot3.sh b/scripts/boot3.sh @@ -46,27 +46,14 @@ ## scripts/boot4.sh ## ## Usage: scripts/boot3.sh <arch> -## <arch> ∈ {aarch64, amd64, riscv64} for DRIVER=podman (default). -## DRIVER=seed currently supports aarch64 only (uses seed-kernel). +## <arch> ∈ {aarch64, amd64, riscv64} for either DRIVER (default podman). set -eu -usage() { echo "usage: $0 <aarch64|amd64|riscv64>" >&2; exit 2; } -[ "$#" -eq 1 ] || usage -ARCH=$1 +. scripts/lib-arch.sh +bootlib_init boot3 "${1:-}" +driver_init empty -case "$ARCH" in - aarch64) PLATFORM=linux/arm64 ;; - amd64) PLATFORM=linux/amd64 ;; - riscv64) PLATFORM=linux/riscv64 ;; - *) usage ;; -esac - -ROOT=$(cd "$(dirname "$0")/.." && pwd) -cd "$ROOT" - -DRIVER=${DRIVER:-podman} -IMAGE=boot2-empty:$ARCH BOOT1=build/$ARCH/boot1 BOOT2=build/$ARCH/boot2 OUT=build/$ARCH/boot3 @@ -77,42 +64,20 @@ TCC_DIR=$TCC_VENDOR/tcc-0.9.26-1147-gee75a10c TCC_FLAT=$TCC_VENDOR/tcc.flat.c LIBC_FLAT=build/$ARCH/vendor/mes-libc/libc.flat.c -# ── ensure container image exists (podman driver only) ──────────────── -if [ "$DRIVER" = podman ] && ! podman image exists "$IMAGE"; then - echo "[boot3 $ARCH] building $IMAGE" - podman build --no-cache --platform "$PLATFORM" -t "$IMAGE" \ - -f scripts/Containerfile.empty scripts/ -fi -if [ "$DRIVER" = seed ]; then - case "$ARCH" in - aarch64) KIMG=Image ;; - amd64) KIMG=kernel.elf ;; - riscv64) KIMG=kernel.elf ;; - *) echo "[boot3] DRIVER=seed: amd64|aarch64|riscv64 only (got $ARCH)" >&2; exit 2 ;; - esac - KERNEL_IMAGE=$ROOT/build/$ARCH/boot6/$KIMG - EXTRACT=$ROOT/seed-kernel/scripts/extract-blk.sh - [ -f "$KERNEL_IMAGE" ] || { echo "[boot3] missing $KERNEL_IMAGE — run ./scripts/boot.sh $ARCH (default DRIVER=podman) first" >&2; exit 1; } - export KERNEL_IMAGE EXTRACT SEED_ARCH=$ARCH -fi -export IMAGE PLATFORM DRIVER - # ── prerequisite: prior-stage binaries ──────────────────────────────── -[ -x "$BOOT1/M1pp" ] || { echo "[boot3 $ARCH] missing $BOOT1/M1pp (run scripts/boot1.sh $ARCH)" >&2; exit 1; } -[ -x "$BOOT1/hex2pp" ] || { echo "[boot3 $ARCH] missing $BOOT1/hex2pp (run scripts/boot1.sh $ARCH)" >&2; exit 1; } -[ -x "$BOOT2/catm" ] || { echo "[boot3 $ARCH] missing $BOOT2/catm (run scripts/boot2.sh $ARCH)" >&2; exit 1; } -[ -x "$BOOT2/scheme1" ] || { echo "[boot3 $ARCH] missing $BOOT2/scheme1 (run scripts/boot2.sh $ARCH)" >&2; exit 1; } +require_prev "$BOOT1" M1pp hex2pp +require_prev "$BOOT2" catm scheme1 # ── prerequisite: host-flattened sources + unpacked tcc tree ────────── # tcc.flat.c + the unpacked $TCC_DIR/{include,lib} tree are produced # together by stage1-flatten.sh; libc.flat.c by libc-flatten.sh. Both # run on the host (cc -E), no container — auto-invoke if missing. if [ ! -e "$TCC_FLAT" ] || [ ! -d "$TCC_DIR/include" ] || [ ! -e "$TCC_VENDOR/stdarg-bridge.h" ]; then - echo "[boot3 $ARCH] flatten tcc.flat.c (host)" + echo "[$BOOT_TAG] flatten tcc.flat.c (host)" scripts/stage1-flatten.sh --arch "$ARCH" fi if [ ! -e "$LIBC_FLAT" ]; then - echo "[boot3 $ARCH] flatten libc.flat.c (host)" + echo "[$BOOT_TAG] flatten libc.flat.c (host)" scripts/libc-flatten.sh --arch "$ARCH" fi @@ -153,5 +118,5 @@ runscm_input libc.flat.c "$LIBC_FLAT" runscm_export tcc0 runscm_run "${BOOT3_TIMEOUT:-1800}" -echo "[boot3 $ARCH/$DRIVER] sizes: tcc0=$(wc -c <"$OUT/tcc0")" -echo "[boot3 $ARCH/$DRIVER] OK -> $OUT/tcc0" +echo "[$BOOT_TAG] sizes: tcc0=$(wc -c <"$OUT/tcc0")" +echo "[$BOOT_TAG] OK -> $OUT/tcc0" diff --git a/scripts/boot4.sh b/scripts/boot4.sh @@ -76,33 +76,20 @@ ## script) — that equality is the fixed-point check. ## ## Usage: scripts/boot4.sh <arch> -## <arch> ∈ {aarch64, amd64, riscv64} for DRIVER=podman (default). -## DRIVER=seed currently supports aarch64 only (uses seed-kernel). +## <arch> ∈ {aarch64, amd64, riscv64} for either DRIVER (default podman). set -eu -usage() { echo "usage: $0 <aarch64|amd64|riscv64>" >&2; exit 2; } -[ "$#" -eq 1 ] || usage -ARCH=$1 +. scripts/lib-arch.sh +bootlib_init boot4 "${1:-}" +driver_init empty case "$ARCH" in - aarch64) PLATFORM=linux/arm64; - LIBTCC1_C_SRCS="lib-arm64.c"; - LIBTCC1_ASM_SRCS="" ;; - amd64) PLATFORM=linux/amd64; - LIBTCC1_C_SRCS="libtcc1.c va_list.c"; - LIBTCC1_ASM_SRCS="alloca86_64.S alloca86_64-bt.S" ;; - riscv64) PLATFORM=linux/riscv64; - LIBTCC1_C_SRCS="lib-arm64.c"; - LIBTCC1_ASM_SRCS="" ;; - *) usage ;; + aarch64) LIBTCC1_C_SRCS="lib-arm64.c"; LIBTCC1_ASM_SRCS="" ;; + amd64) LIBTCC1_C_SRCS="libtcc1.c va_list.c"; LIBTCC1_ASM_SRCS="alloca86_64.S alloca86_64-bt.S" ;; + riscv64) LIBTCC1_C_SRCS="lib-arm64.c"; LIBTCC1_ASM_SRCS="" ;; esac -ROOT=$(cd "$(dirname "$0")/.." && pwd) -cd "$ROOT" - -DRIVER=${DRIVER:-podman} -IMAGE=boot2-empty:$ARCH BOOT2=build/$ARCH/boot2 BOOT3=build/$ARCH/boot3 OUT=build/$ARCH/boot4 @@ -113,45 +100,24 @@ TCC_DIR=$TCC_VENDOR/tcc-0.9.26-1147-gee75a10c TCC_FLAT=$TCC_VENDOR/tcc.flat.c LIBC_FLAT=build/$ARCH/vendor/mes-libc/libc.flat.c -# ── ensure container image exists (podman driver only) ──────────────── -if [ "$DRIVER" = podman ] && ! podman image exists "$IMAGE"; then - echo "[boot4 $ARCH] building $IMAGE" - podman build --no-cache --platform "$PLATFORM" -t "$IMAGE" \ - -f scripts/Containerfile.empty scripts/ -fi -if [ "$DRIVER" = seed ]; then - case "$ARCH" in - aarch64) KIMG=Image ;; - amd64) KIMG=kernel.elf ;; - riscv64) KIMG=kernel.elf ;; - *) echo "[boot4] DRIVER=seed: amd64|aarch64|riscv64 only (got $ARCH)" >&2; exit 2 ;; - esac - KERNEL_IMAGE=$ROOT/build/$ARCH/boot6/$KIMG - EXTRACT=$ROOT/seed-kernel/scripts/extract-blk.sh - [ -f "$KERNEL_IMAGE" ] || { echo "[boot4] missing $KERNEL_IMAGE — run ./scripts/boot.sh $ARCH (default DRIVER=podman) first" >&2; exit 1; } - export KERNEL_IMAGE EXTRACT SEED_ARCH=$ARCH -fi -export IMAGE PLATFORM DRIVER - # ── prerequisite: prior-stage binaries ──────────────────────────────── -[ -x "$BOOT3/tcc0" ] || { echo "[boot4 $ARCH] missing $BOOT3/tcc0 (run scripts/boot3.sh $ARCH)" >&2; exit 1; } -[ -x "$BOOT2/scheme1" ] || { echo "[boot4 $ARCH] missing $BOOT2/scheme1 (run scripts/boot2.sh $ARCH)" >&2; exit 1; } -[ -x "$BOOT2/catm" ] || { echo "[boot4 $ARCH] missing $BOOT2/catm (run scripts/boot2.sh $ARCH)" >&2; exit 1; } +require_prev "$BOOT3" tcc0 +require_prev "$BOOT2" catm scheme1 # ── prerequisite: host-flattened sources + unpacked tcc tree ────────── # Normally these were produced by boot3 (auto-invoked by stage1-flatten # / libc-flatten there). Re-check here so boot4 runs standalone if a # user has tcc0 but blew away build/$ARCH/vendor/tcc/. if [ ! -e "$TCC_FLAT" ] || [ ! -d "$TCC_DIR/include" ] || [ ! -e "$TCC_DIR/lib/lib-arm64.c" ] || [ ! -e "$TCC_VENDOR/stdarg-bridge.h" ]; then - echo "[boot4 $ARCH] flatten tcc.flat.c (host)" + echo "[$BOOT_TAG] flatten tcc.flat.c (host)" scripts/stage1-flatten.sh --arch "$ARCH" fi if [ ! -e "$LIBC_FLAT" ]; then - echo "[boot4 $ARCH] flatten libc.flat.c (host)" + echo "[$BOOT_TAG] flatten libc.flat.c (host)" scripts/libc-flatten.sh --arch "$ARCH" fi for f in $LIBTCC1_C_SRCS $LIBTCC1_ASM_SRCS; do - [ -e "$TCC_DIR/lib/$f" ] || { echo "[boot4 $ARCH] missing $TCC_DIR/lib/$f" >&2; exit 1; } + [ -e "$TCC_DIR/lib/$f" ] || { echo "[$BOOT_TAG] missing $TCC_DIR/lib/$f" >&2; exit 1; } done # ── stage inputs and run scheme1 + boot4 run.scm under $DRIVER ──────── @@ -160,7 +126,7 @@ runscm_init "$STAGE" "$OUT" RUNSCM=$STAGE/run.scm scripts/boot4-gen-runscm.sh "$ARCH" "$RUNSCM" -echo "[boot4 $ARCH] generated run.scm: $(wc -l <"$RUNSCM") lines" +echo "[$BOOT_TAG] generated run.scm: $(wc -l <"$RUNSCM") lines" runscm_scheme1 "$BOOT2/scheme1" runscm_prelude scheme1/prelude.scm @@ -201,9 +167,9 @@ if ! cmp -s "$OUT/tcc2" "$OUT/tcc3"; then s2=$(wc -c <"$OUT/tcc2") s3=$(wc -c <"$OUT/tcc3") if [ "${TCC_BOOTSTRAP_RELAX_FIXEDPOINT:-0}" = 1 ]; then - echo "[boot4 $ARCH] WARN: tcc2 ($s2) != tcc3 ($s3); TCC_BOOTSTRAP_RELAX_FIXEDPOINT=1, accepting tcc3" >&2 + echo "[$BOOT_TAG] WARN: tcc2 ($s2) != tcc3 ($s3); TCC_BOOTSTRAP_RELAX_FIXEDPOINT=1, accepting tcc3" >&2 else - echo "[boot4 $ARCH] FIXED-POINT FAIL: tcc2 ($s2) != tcc3 ($s3)" >&2 + echo "[$BOOT_TAG] FIXED-POINT FAIL: tcc2 ($s2) != tcc3 ($s3)" >&2 exit 1 fi fi @@ -215,5 +181,5 @@ mv "$OUT/s3-libtcc1.a" "$OUT/libtcc1.a" rm -f "$OUT/tcc1" "$OUT/tcc2" chmod 0700 "$OUT/tcc3" "$OUT/hello" -echo "[boot4 $ARCH/$DRIVER] sizes: libtcc1.a=$(wc -c <"$OUT/libtcc1.a") libc.a=$(wc -c <"$OUT/libc.a") hello=$(wc -c <"$OUT/hello")" -echo "[boot4 $ARCH/$DRIVER] OK -> $OUT/{tcc3, crt1.o, libc.a, libtcc1.a, hello} (fixed point: tcc2 == tcc3)" +echo "[$BOOT_TAG] sizes: libtcc1.a=$(wc -c <"$OUT/libtcc1.a") libc.a=$(wc -c <"$OUT/libc.a") hello=$(wc -c <"$OUT/hello")" +echo "[$BOOT_TAG] OK -> $OUT/{tcc3, crt1.o, libc.a, libtcc1.a, hello} (fixed point: tcc2 == tcc3)" diff --git a/scripts/boot5.sh b/scripts/boot5.sh @@ -46,37 +46,14 @@ ## build/$ARCH/boot5/hello — static, runs in the container ## ## Usage: scripts/boot5.sh <arch> -## <arch> ∈ {amd64, aarch64, riscv64} for DRIVER=podman (default). -## All three architectures are verified end-to-end on podman. -## DRIVER=seed: aarch64 only. Drives ~1300 (run "tcc" …) calls through -## the seed kernel's atomic spawn syscall (no per-fork memcpy). Wall -## time is dominated by tcc work, not spawn overhead. +## <arch> ∈ {amd64, aarch64, riscv64} for either DRIVER (default podman). set -eu -usage() { echo "usage: $0 <amd64|aarch64|riscv64>" >&2; exit 2; } -[ "$#" -eq 1 ] || usage -ARCH=$1 +. scripts/lib-arch.sh +bootlib_init boot5 "${1:-}" +driver_init empty -case "$ARCH" in - amd64) PLATFORM=linux/amd64; MUSL_ARCH=x86_64 ;; - aarch64) PLATFORM=linux/arm64; MUSL_ARCH=aarch64 ;; - riscv64) PLATFORM=linux/riscv64; MUSL_ARCH=riscv64 ;; - *) usage ;; -esac - -ROOT=$(cd "$(dirname "$0")/.." && pwd) -cd "$ROOT" - -DRIVER=${DRIVER:-podman} -if [ "$DRIVER" = seed ]; then - case "$ARCH" in - amd64|aarch64|riscv64) ;; - *) echo "[boot5] DRIVER=seed: amd64|aarch64|riscv64 only (got $ARCH)" >&2; exit 2 ;; - esac -fi - -IMAGE=boot2-empty:$ARCH BOOT2=build/$ARCH/boot2 BOOT4=build/$ARCH/boot4 OUT=build/$ARCH/boot5 @@ -89,34 +66,15 @@ MUSL_SKIP=vendor/upstream/musl-1.2.5-skip-$ARCH.txt BRIDGE_FILE=build/$ARCH/vendor/tcc/stdarg-bridge.h # ── prerequisites ───────────────────────────────────────────────────── -[ -x "$BOOT4/tcc3" ] || { echo "[boot5 $ARCH] missing $BOOT4/tcc3 (run scripts/boot4.sh $ARCH)" >&2; exit 1; } -[ -e "$BOOT4/libtcc1.a" ] || { echo "[boot5 $ARCH] missing $BOOT4/libtcc1.a (run scripts/boot4.sh $ARCH)" >&2; exit 1; } -[ -x "$BOOT2/scheme1" ] || { echo "[boot5 $ARCH] missing $BOOT2/scheme1 (run scripts/boot2.sh $ARCH)" >&2; exit 1; } -[ -x "$BOOT2/catm" ] || { echo "[boot5 $ARCH] missing $BOOT2/catm (run scripts/boot2.sh $ARCH)" >&2; exit 1; } -[ -e "$MUSL_TARBALL" ] || { echo "[boot5 $ARCH] missing $MUSL_TARBALL" >&2; exit 1; } -[ -d "$MUSL_OVERRIDES" ] || { echo "[boot5 $ARCH] missing $MUSL_OVERRIDES" >&2; exit 1; } -[ -e "$MUSL_DELETES" ] || { echo "[boot5 $ARCH] missing $MUSL_DELETES" >&2; exit 1; } -[ -d "$MUSL_GENERATED" ] || { echo "[boot5 $ARCH] missing $MUSL_GENERATED (run scripts/musl-vendor.sh)" >&2; exit 1; } -[ -e "$MUSL_SKIP" ] || { echo "[boot5 $ARCH] missing $MUSL_SKIP (run scripts/boot5-calibrate.sh $ARCH)" >&2; exit 1; } -[ -e "$BRIDGE_FILE" ] || { echo "[boot5 $ARCH] missing $BRIDGE_FILE (run scripts/stage1-flatten.sh)" >&2; exit 1; } - -if [ "$DRIVER" = podman ] && ! podman image exists "$IMAGE"; then - echo "[boot5 $ARCH] building $IMAGE" - podman build --no-cache --platform "$PLATFORM" -t "$IMAGE" \ - -f scripts/Containerfile.empty scripts/ -fi -if [ "$DRIVER" = seed ]; then - case "$ARCH" in - aarch64) KIMG=Image ;; - amd64) KIMG=kernel.elf ;; - riscv64) KIMG=kernel.elf ;; - esac - KERNEL_IMAGE=$ROOT/build/$ARCH/boot6/$KIMG - EXTRACT=$ROOT/seed-kernel/scripts/extract-blk.sh - [ -f "$KERNEL_IMAGE" ] || { echo "[boot5] missing $KERNEL_IMAGE — run ./scripts/boot.sh $ARCH (default DRIVER=podman) first" >&2; exit 1; } - export KERNEL_IMAGE EXTRACT SEED_ARCH=$ARCH -fi -export IMAGE PLATFORM DRIVER +require_prev "$BOOT4" tcc3 +require_prev "$BOOT2" catm scheme1 +[ -e "$BOOT4/libtcc1.a" ] || { echo "[$BOOT_TAG] missing $BOOT4/libtcc1.a (run scripts/boot4.sh $ARCH)" >&2; exit 1; } +[ -e "$MUSL_TARBALL" ] || { echo "[$BOOT_TAG] missing $MUSL_TARBALL" >&2; exit 1; } +[ -d "$MUSL_OVERRIDES" ] || { echo "[$BOOT_TAG] missing $MUSL_OVERRIDES" >&2; exit 1; } +[ -e "$MUSL_DELETES" ] || { echo "[$BOOT_TAG] missing $MUSL_DELETES" >&2; exit 1; } +[ -d "$MUSL_GENERATED" ] || { echo "[$BOOT_TAG] missing $MUSL_GENERATED (run scripts/musl-vendor.sh)" >&2; exit 1; } +[ -e "$MUSL_SKIP" ] || { echo "[$BOOT_TAG] missing $MUSL_SKIP (run scripts/boot5-calibrate.sh $ARCH)" >&2; exit 1; } +[ -e "$BRIDGE_FILE" ] || { echo "[$BOOT_TAG] missing $BRIDGE_FILE (run scripts/stage1-flatten.sh)" >&2; exit 1; } # ── prepare staging dirs and musl tree on host ──────────────────────── # $STAGE/in/ — read-only inputs (becomes /work/in or in/ in tmpfs) @@ -208,7 +166,7 @@ awk -v SKIPF="$MUSL_SKIP" ' n_src=$(wc -l < "$STAGE/_host/build-srcs.txt") n_skip=$(wc -l < "$MUSL_SKIP") -echo "[boot5 $ARCH] keep=$n_src skip=$n_skip (calibrated)" +echo "[$BOOT_TAG] keep=$n_src skip=$n_skip (calibrated)" # Record CRT mode (asm vs c) so the gen-runscm step picks the right # crti/crtn source set without re-checking $MUSL_DIR. @@ -242,7 +200,7 @@ echo '#define VERSION "1.2.5-tcc-boot5"' > "$MUSL_DIR/obj/src/internal/version.h # ── generate run.scm and stage chain binaries ───────────────────────── RUNSCM=$STAGE/run.scm scripts/boot5-gen-runscm.sh "$MUSL_ARCH" "$STAGE/_host" "$RUNSCM" -echo "[boot5 $ARCH] generated run.scm: $(wc -l <"$RUNSCM") lines, $(wc -c <"$RUNSCM") bytes" +echo "[$BOOT_TAG] generated run.scm: $(wc -l <"$RUNSCM") lines, $(wc -c <"$RUNSCM") bytes" runscm_scheme1 "$BOOT2/scheme1" runscm_prelude scheme1/prelude.scm @@ -269,5 +227,5 @@ runscm_export hello # the seed driver. Podman ignores QEMU_MEM and uses host memory directly. QEMU_MEM=${QEMU_MEM:-3072M} runscm_run "${BOOT5_TIMEOUT:-7200}" -echo "[boot5 $ARCH/$DRIVER] sizes: libc.a=$(wc -c <"$OUT/libc.a") hello=$(wc -c <"$OUT/hello")" -echo "[boot5 $ARCH/$DRIVER] OK -> $OUT/{libc.a, crt1.o, crti.o, crtn.o, hello}" +echo "[$BOOT_TAG] sizes: libc.a=$(wc -c <"$OUT/libc.a") hello=$(wc -c <"$OUT/hello")" +echo "[$BOOT_TAG] OK -> $OUT/{libc.a, crt1.o, crti.o, crtn.o, hello}" diff --git a/scripts/boot6.sh b/scripts/boot6.sh @@ -41,69 +41,40 @@ set -eu -usage() { echo "usage: $0 <amd64|aarch64|riscv64>" >&2; exit 2; } -[ "$#" -eq 1 ] || usage -ARCH=$1 +. scripts/lib-arch.sh +bootlib_init boot6 "${1:-}" +driver_init empty -case "$ARCH" in - amd64) PLATFORM=linux/amd64; ARCHDIR=amd64; OUT_FILE=kernel.elf ;; - aarch64) PLATFORM=linux/arm64; ARCHDIR=aarch64; OUT_FILE=Image ;; - riscv64) PLATFORM=linux/riscv64; ARCHDIR=riscv64; OUT_FILE=kernel.elf ;; - *) usage ;; -esac - -ROOT=$(cd "$(dirname "$0")/.." && pwd) -cd "$ROOT" - -DRIVER=${DRIVER:-podman} -case "$DRIVER:$ARCH" in - seed:amd64|seed:aarch64|seed:riscv64) ;; - seed:*) echo "[boot6] DRIVER=seed: amd64|aarch64|riscv64 only (got $ARCH)" >&2; exit 2 ;; -esac -IMAGE=boot2-empty:$ARCH +OUT_FILE=$KERNEL_NAME BOOT2=build/$ARCH/boot2 BOOT4=build/$ARCH/boot4 OUT=build/$ARCH/boot6 STAGE=build/$ARCH/.boot6-stage # ── prerequisites ───────────────────────────────────────────────────── -[ -x "$BOOT4/tcc3" ] || { echo "[boot6 $ARCH] missing $BOOT4/tcc3 (run scripts/boot4.sh $ARCH)" >&2; exit 1; } -[ -x "$BOOT2/scheme1" ] || { echo "[boot6 $ARCH] missing $BOOT2/scheme1 (run scripts/boot2.sh $ARCH)" >&2; exit 1; } -for f in seed-kernel/arch/$ARCHDIR/kernel.S seed-kernel/arch/$ARCHDIR/mmu.c seed-kernel/arch/$ARCHDIR/arch.h seed-kernel/kernel.c tcc-cc/mem.c; do - [ -f "$f" ] || { echo "[boot6 $ARCH] missing $f" >&2; exit 1; } +require_prev "$BOOT4" tcc3 +require_prev "$BOOT2" scheme1 +for f in seed-kernel/arch/$ARCH/kernel.S seed-kernel/arch/$ARCH/mmu.c seed-kernel/arch/$ARCH/arch.h seed-kernel/kernel.c tcc-cc/mem.c; do + [ -f "$f" ] || { echo "[$BOOT_TAG] missing $f" >&2; exit 1; } done -# ── ensure container image exists (podman driver only) ──────────────── -if [ "$DRIVER" = podman ] && ! podman image exists "$IMAGE"; then - echo "[boot6 $ARCH] building $IMAGE" - podman build --no-cache --platform "$PLATFORM" -t "$IMAGE" \ - -f scripts/Containerfile.empty scripts/ -fi -if [ "$DRIVER" = seed ]; then - KERNEL_IMAGE=$ROOT/build/$ARCH/boot6/$OUT_FILE - EXTRACT=$ROOT/seed-kernel/scripts/extract-blk.sh - [ -f "$KERNEL_IMAGE" ] || { echo "[boot6] missing $KERNEL_IMAGE — run ./scripts/boot.sh $ARCH (default DRIVER=podman) first" >&2; exit 1; } - export KERNEL_IMAGE EXTRACT SEED_ARCH=$ARCH -fi -export IMAGE PLATFORM DRIVER - # ── stage inputs and run scheme1 + run.scm under $DRIVER ────────────── . scripts/lib-runscm.sh runscm_init "$STAGE" "$OUT" RUNSCM=$STAGE/run.scm scripts/boot6-gen-runscm.sh "$ARCH" "$RUNSCM" -echo "[boot6 $ARCH] generated run.scm: $(wc -l <"$RUNSCM") lines" +echo "[$BOOT_TAG] generated run.scm: $(wc -l <"$RUNSCM") lines" runscm_scheme1 "$BOOT2/scheme1" runscm_prelude scheme1/prelude.scm runscm_runscm "$RUNSCM" runscm_input tcc3 "$BOOT4/tcc3" -runscm_input kernel.S seed-kernel/arch/$ARCHDIR/kernel.S +runscm_input kernel.S seed-kernel/arch/$ARCH/kernel.S runscm_input kernel.c seed-kernel/kernel.c -runscm_input arch.h seed-kernel/arch/$ARCHDIR/arch.h -runscm_input mmu.c seed-kernel/arch/$ARCHDIR/mmu.c +runscm_input arch.h seed-kernel/arch/$ARCH/arch.h +runscm_input mmu.c seed-kernel/arch/$ARCH/mmu.c runscm_input mem.c tcc-cc/mem.c # amd64 needs a post-link fixup — tcc3 doesn't emit PT_NOTE phdrs, so @@ -120,4 +91,4 @@ fi runscm_export "$OUT_FILE" runscm_run 1200 -echo "[boot6 $ARCH/$DRIVER] OK -> $OUT/$OUT_FILE ($(wc -c <"$OUT/$OUT_FILE") bytes)" +echo "[$BOOT_TAG] OK -> $OUT/$OUT_FILE ($(wc -c <"$OUT/$OUT_FILE") bytes)" diff --git a/scripts/lib-arch.sh b/scripts/lib-arch.sh @@ -0,0 +1,99 @@ +# lib-arch.sh — single source for arch + driver setup shared by +# scripts/boot.sh, scripts/boot{0..6}.sh, lib-pipeline.sh, lib-runscm.sh. +# +# Public entry points (call in this order from a bootN.sh): +# +# bootlib_init <stage> <arch> # validate <arch>, cd to repo root, +# # set ARCH/PLATFORM/KERNEL_NAME/ +# # MUSL_ARCH/DRIVER/BOOT_TAG. +# driver_init [<image-kind>] # podman: build IMAGE if missing +# # (image-kind ∈ scratch|empty; +# # default scratch). +# # seed: verify boot6 kernel exists. +# require_prev <dir> <name>... # die helpfully if any <dir>/<name> +# # is missing or non-executable. +# +# After bootlib_init, the following shell vars are set/exported: +# ARCH input architecture token (aarch64|amd64|riscv64) +# ROOT repo root (cwd is set to ROOT) +# DRIVER podman|seed (defaults to podman) +# PLATFORM linux/<arm64|amd64|riscv64> for podman --platform +# KERNEL_NAME Image (aarch64) | kernel.elf (amd64,riscv64) +# MUSL_ARCH aarch64 | x86_64 | riscv64 +# BOOT_TAG "<stage>/<driver>/<arch>" for log prefixes +# +# After driver_init: +# podman: IMAGE +# seed: KERNEL_IMAGE, EXTRACT, SEED_ARCH + +bootlib_init() { + _stage=$1; _arch=${2:-} + [ -n "$_stage" ] || { echo "lib-arch: bootlib_init: stage required" >&2; exit 2; } + case "$_arch" in + aarch64|amd64|riscv64) ;; + *) echo "usage: $0 <aarch64|amd64|riscv64>" >&2; exit 2 ;; + esac + ARCH=$_arch + ROOT=$(cd "$(dirname "$0")/.." && pwd) + cd "$ROOT" + DRIVER=${DRIVER:-podman} + case "$DRIVER" in + podman|seed) ;; + *) echo "[$_stage/$DRIVER/$ARCH] unknown DRIVER=$DRIVER (expected podman|seed)" >&2; exit 2 ;; + esac + BOOT_TAG="$_stage/$DRIVER/$ARCH" + case "$ARCH" in + aarch64) PLATFORM=linux/arm64; KERNEL_NAME=Image; MUSL_ARCH=aarch64 ;; + amd64) PLATFORM=linux/amd64; KERNEL_NAME=kernel.elf; MUSL_ARCH=x86_64 ;; + riscv64) PLATFORM=linux/riscv64; KERNEL_NAME=kernel.elf; MUSL_ARCH=riscv64 ;; + esac + export ARCH ROOT DRIVER PLATFORM KERNEL_NAME MUSL_ARCH BOOT_TAG +} + +driver_init() { + _image_kind=${1:-scratch} + case "$_image_kind" in + scratch|empty) ;; + *) echo "[$BOOT_TAG] driver_init: image-kind must be scratch|empty (got $_image_kind)" >&2; exit 2 ;; + esac + case "$DRIVER" in + podman) + IMAGE=boot2-$_image_kind:$ARCH + if ! podman image exists "$IMAGE"; then + echo "[$BOOT_TAG] building $IMAGE" + # Containerfile.empty drops /etc resolver state etc.; no-cache + # avoids a stale layer surviving an upstream tag bump. + _no_cache= + [ "$_image_kind" = empty ] && _no_cache=--no-cache + podman build $_no_cache --platform "$PLATFORM" -t "$IMAGE" \ + -f scripts/Containerfile.$_image_kind scripts/ + fi + export IMAGE + ;; + seed) + KERNEL_IMAGE=$ROOT/build/$ARCH/boot6/$KERNEL_NAME + EXTRACT=$ROOT/seed-kernel/scripts/extract-blk.sh + [ -f "$KERNEL_IMAGE" ] || { + echo "[$BOOT_TAG] missing $KERNEL_IMAGE — run ./scripts/boot.sh $ARCH (default DRIVER=podman) first" >&2 + exit 1 + } + export KERNEL_IMAGE EXTRACT + export SEED_ARCH=$ARCH + ;; + esac +} + +require_prev() { + _dir=$1; shift + for _n in "$@"; do + [ -x "$_dir/$_n" ] || { + _stage_name=$(basename "$_dir") + case "$_stage_name" in + boot*) _hint="run scripts/$_stage_name.sh $ARCH" ;; + *) _hint="rebuild $_dir" ;; + esac + echo "[$BOOT_TAG] missing prerequisite: $_dir/$_n ($_hint)" >&2 + exit 1 + } + done +}