boot2

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

commit 22ad7bf0fac945c332a1f325cd292508bedb4953
parent 5757513857ba4807e5642573a662659b4a4ad398
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Fri, 24 Apr 2026 17:05:32 -0700

Pass ARCH via env, drop arg-list noise from script invocations

Every script in scripts/ now reads ARCH from its environment and derives
the per-arch fixed paths (P1/P1-$ARCH.M1, vendor/seed/$ARCH/ELF.hex2,
build/$ARCH/tools/, build/$ARCH/m1pp, build/$ARCH/.work/$NAME) from
that. Only the variable per-call inputs (source, output) are passed
positionally.

Make's PODMAN macro now adds `-e ARCH=$(1)`, so any boot-* script
invoked through it sees the arch automatically. Lint runs `ARCH=$* sh
scripts/lint.sh <src>` from the recipe.

Concretely:
  boot1.sh             ARCH only       (was <arch> <out-dir>)
  boot2.sh             ARCH only       (already)
  boot-build-p1.sh     ARCH + src out  (was 6 positional args)
  boot-build-p1pp.sh   ARCH + src out  (was 8 positional args)
  lint.sh              ARCH + src(s)   (was <table> <src>...)
  run-tests.sh         passes ARCH=    via container env to boot-* calls

Make rule recipes go from multi-line continuations to single calls:
  $(call PODMAN,$*) sh scripts/boot-build-p1.sh M1pp/M1pp.P1 $@

Verified: make all + make hello + make run print "Hello, World!"; aarch64
m1pp suite 20/20 in ~6s.

Diffstat:
MMakefile | 27++++++++++++---------------
Mscripts/boot-build-p1.sh | 28+++++++++++++++-------------
Mscripts/boot-build-p1pp.sh | 42++++++++++++++++++++++--------------------
Mscripts/boot1.sh | 17++++++++---------
Mscripts/boot2.sh | 27+++++++--------------------
Mscripts/lint.sh | 11+++++++----
Mscripts/run-tests.sh | 27+++++++--------------------
7 files changed, 78 insertions(+), 101 deletions(-)

diff --git a/Makefile b/Makefile @@ -42,7 +42,10 @@ TOOLS_DIR := $(OUT_DIR)/tools IMAGE_STAMP := $(OUT_DIR)/.image # All container invocations go through this. Just env/args + one named -# script in scripts/; never inline shell. +# script in scripts/; never inline shell. Every script reads ARCH from +# the env and derives all its fixed paths (P1 table, ELF header, tools +# dir, etc.) from that — only per-call args (source, output) are passed +# positionally. # # --tmpfs /tmp: backstop for the stage0 tools' per-byte fputc/fgetc. Without # it /tmp falls through to the container overlay (still backed by virtiofs @@ -50,6 +53,7 @@ IMAGE_STAMP := $(OUT_DIR)/.image # byte. Real RAM tmpfs collapses that to local memory access. PODMAN = podman run --rm --pull=never --platform $(PLATFORM_$(1)) \ --tmpfs /tmp:size=512M \ + -e ARCH=$(1) \ -v $(CURDIR):/work -w /work boot2-busybox:$(1) # --- Targets -------------------------------------------------------------- @@ -90,8 +94,7 @@ $(TOOLS_M0): build/%/tools/M0: scripts/boot1.sh build/%/.image \ vendor/seed/%/hex1.hex0 vendor/seed/%/hex2.hex1 \ vendor/seed/%/catm.hex2 vendor/seed/%/M0.hex2 \ vendor/seed/%/ELF.hex2 - mkdir -p build/$*/tools - $(call PODMAN,$*) sh scripts/boot1.sh $* build/$*/tools + $(call PODMAN,$*) sh scripts/boot1.sh # --- Pre-pruned P1 backend tables ----------------------------------------- # @@ -132,22 +135,16 @@ P1_BUILD_DEPS = scripts/lint.sh scripts/boot-build-p1.sh \ vendor/seed/%/ELF.hex2 P1/P1-%.M1 $(M1PP_BINS): build/%/m1pp: M1pp/M1pp.P1 $(P1_BUILD_DEPS) - sh scripts/lint.sh P1/P1-$*.M1 M1pp/M1pp.P1 - $(call PODMAN,$*) sh scripts/boot-build-p1.sh \ - P1/P1-$*.M1 M1pp/M1pp.P1 vendor/seed/$*/ELF.hex2 \ - build/$*/.work/m1pp build/$*/tools $@ + ARCH=$* sh scripts/lint.sh M1pp/M1pp.P1 + $(call PODMAN,$*) sh scripts/boot-build-p1.sh M1pp/M1pp.P1 $@ $(POKEM_BINS): build/%/pokem: pokem/pokem.P1 $(P1_BUILD_DEPS) - sh scripts/lint.sh P1/P1-$*.M1 pokem/pokem.P1 - $(call PODMAN,$*) sh scripts/boot-build-p1.sh \ - P1/P1-$*.M1 pokem/pokem.P1 vendor/seed/$*/ELF.hex2 \ - build/$*/.work/pokem build/$*/tools $@ + ARCH=$* sh scripts/lint.sh pokem/pokem.P1 + $(call PODMAN,$*) sh scripts/boot-build-p1.sh pokem/pokem.P1 $@ $(HELLO_BINS): build/%/hello: $(HELLO_SRC) $(P1_BUILD_DEPS) - sh scripts/lint.sh P1/P1-$*.M1 $(HELLO_SRC) - $(call PODMAN,$*) sh scripts/boot-build-p1.sh \ - P1/P1-$*.M1 $(HELLO_SRC) vendor/seed/$*/ELF.hex2 \ - build/$*/.work/hello build/$*/tools $@ + ARCH=$* sh scripts/lint.sh $(HELLO_SRC) + $(call PODMAN,$*) sh scripts/boot-build-p1.sh $(HELLO_SRC) $@ run: $(OUT_DIR)/hello $(IMAGE_STAMP) $(call PODMAN,$(ARCH)) ./$(OUT_DIR)/hello diff --git a/scripts/boot-build-p1.sh b/scripts/boot-build-p1.sh @@ -1,12 +1,12 @@ #!/bin/sh ## boot-build-p1.sh — in-container .P1/.M1 -> ELF. ## -## Assumes a pre-pruned P1 backend table (P1/P1-<arch>.M1). No prune step -## at build time — that one-time work lives in scripts/prune-p1-table.sh -## and the result is checked in. +## Pure transformation. Caller (the Makefile) ensures every fixed-path +## input below already exists. Only the variable per-call inputs (source, +## output binary) come in as args. ## ## Pipeline: -## cat <table> <src> -> /tmp/combined.M1 +## cat <P1/P1-$ARCH.M1> <src> -> /tmp/combined.M1 ## M0 /tmp/combined.M1 -> /tmp/prog.hex2 ## catm /tmp/elf.hex2 /tmp/prog.hex2 -> /tmp/linked.hex2 ## hex2-0 /tmp/linked.hex2 -> $OUT @@ -14,20 +14,22 @@ ## Stages through /tmp because the stage0 tools do one syscall per byte; ## virtiofs round-trips would dominate otherwise. ## -## Usage: boot-build-p1.sh <table.M1> <src> <ELF.hex2> <work-dir> -## <tools-dir> <out> +## Env: ARCH=aarch64|amd64|riscv64 +## Usage: boot-build-p1.sh <src> <out> set -eu -[ "$#" -eq 6 ] || { echo "usage: $0 <table.M1> <src> <ELF.hex2> <work-dir> <tools-dir> <out>" >&2; exit 2; } +: "${ARCH:?ARCH must be set}" +[ "$#" -eq 2 ] || { echo "usage: ARCH=<arch> $0 <src> <out>" >&2; exit 2; } -TABLE=$1 -SRC=$2 -ELF_HDR=$3 -WORK=$4 -TOOLS=$5 -OUT=$6 +SRC=$1 +OUT=$2 +TABLE=P1/P1-$ARCH.M1 +ELF_HDR=vendor/seed/$ARCH/ELF.hex2 +TOOLS=build/$ARCH/tools +NAME=$(basename "$SRC" | sed 's/\.[^.]*$//') +WORK=build/$ARCH/.work/$NAME mkdir -p "$WORK" "$(dirname "$OUT")" cat "$TABLE" "$SRC" > /tmp/combined.M1 diff --git a/scripts/boot-build-p1pp.sh b/scripts/boot-build-p1pp.sh @@ -1,33 +1,35 @@ #!/bin/sh ## boot-build-p1pp.sh — in-container .P1pp -> ELF. ## -## Pipeline: -## cat <P1-arch.M1pp> <P1.M1pp> <src.P1pp> -> /tmp/combined.M1pp -## m1pp /tmp/combined.M1pp -> /tmp/expanded.M1 -## M0 /tmp/expanded.M1 -> /tmp/prog.hex2 -## catm /tmp/elf.hex2 /tmp/prog.hex2 -> /tmp/linked.hex2 -## hex2-0 /tmp/linked.hex2 -> $OUT +## Pure transformation. Caller (the Makefile) ensures every fixed-path +## input below already exists, including the per-arch self-hosted m1pp +## ELF binary (build/$ARCH/m1pp, built by boot2.sh / boot-build-p1.sh). ## -## Uses the per-arch self-hosted m1pp ELF (built by boot2.sh / -## boot-build-p1.sh against M1pp/M1pp.P1). +## Pipeline: +## cat <P1/P1-$ARCH.M1pp> <P1/P1.M1pp> <src> -> /tmp/combined.M1pp +## m1pp /tmp/combined.M1pp -> /tmp/expanded.M1 +## M0 /tmp/expanded.M1 -> /tmp/prog.hex2 +## catm /tmp/elf.hex2 /tmp/prog.hex2 -> /tmp/linked.hex2 +## hex2-0 /tmp/linked.hex2 -> $OUT ## -## Usage: boot-build-p1pp.sh <backend.M1pp> <frontend.M1pp> <src.P1pp> -## <ELF.hex2> <work-dir> <m1pp-bin> -## <tools-dir> <out> +## Env: ARCH=aarch64|amd64|riscv64 +## Usage: boot-build-p1pp.sh <src> <out> set -eu -[ "$#" -eq 8 ] || { echo "usage: $0 <backend.M1pp> <frontend.M1pp> <src.P1pp> <ELF.hex2> <work-dir> <m1pp-bin> <tools-dir> <out>" >&2; exit 2; } +: "${ARCH:?ARCH must be set}" +[ "$#" -eq 2 ] || { echo "usage: ARCH=<arch> $0 <src> <out>" >&2; exit 2; } -BACKEND=$1 -FRONTEND=$2 -SRC=$3 -ELF_HDR=$4 -WORK=$5 -M1PP_BIN=$6 -TOOLS=$7 -OUT=$8 +SRC=$1 +OUT=$2 +BACKEND=P1/P1-$ARCH.M1pp +FRONTEND=P1/P1.M1pp +ELF_HDR=vendor/seed/$ARCH/ELF.hex2 +TOOLS=build/$ARCH/tools +M1PP_BIN=build/$ARCH/m1pp +NAME=$(basename "$SRC" .P1pp) +WORK=build/$ARCH/.work/$NAME mkdir -p "$WORK" "$(dirname "$OUT")" cat "$BACKEND" "$FRONTEND" "$SRC" > /tmp/combined.M1pp diff --git a/scripts/boot1.sh b/scripts/boot1.sh @@ -3,8 +3,11 @@ ## ## In-container script. Brings up M0/hex2-0/catm from the ~400-byte ## hex0-seed by chaining stage0-posix's first three phases. All produced -## binaries are target-arch Linux ELF and land in $OUT as: -## hex0, hex1, hex2-0, catm, M0 +## binaries are target-arch Linux ELF. +## +## Inputs (read): vendor/seed/$ARCH/{hex0-seed,hex0.hex0,hex1.hex0, +## hex2.hex1,catm.hex2,M0.hex2,ELF.hex2} +## Outputs: build/$ARCH/tools/{hex0,hex1,hex2-0,catm,M0} ## ## Phase map (stage0-posix mescc-tools-{seed,mini}-kaem.kaem phases 0-3): ## 0) hex0-seed + hex0.hex0 -> hex0 @@ -14,16 +17,11 @@ ## 3a) catm : ELF.hex2 + M0.hex2 -> M0.combined.hex2 ## 3b) hex2-0 : M0.combined.hex2 -> M0 ## -## Inputs are read from vendor/seed/<arch>/ (vendored upstream). -## -## Usage: boot1.sh <arch> <out-dir> -## arch: aarch64 | amd64 | riscv64 -## out-dir: path (relative to /work) where tool binaries should land +## Env: ARCH=aarch64|amd64|riscv64 set -eu -ARCH=$1 -OUT=$2 +: "${ARCH:?ARCH must be set}" case "$ARCH" in aarch64|amd64|riscv64) ;; @@ -31,6 +29,7 @@ case "$ARCH" in esac S=vendor/seed/$ARCH +OUT=build/$ARCH/tools mkdir -p "$OUT" ## Build everything in /tmp (RAM tmpfs — see PODMAN macro in Makefile), diff --git a/scripts/boot2.sh b/scripts/boot2.sh @@ -2,34 +2,21 @@ ## boot2.sh — stage 2 of the bootstrap chain. ## ## In-container script. Builds the M1pp expander and pokem ELFs from the -## checked-in pre-pruned P1 backend table (P1/P1-<arch>.M1) plus their -## sources. Calls scripts/boot-build-p1.sh internally. +## checked-in pre-pruned P1 backend table (P1/P1-$ARCH.M1) plus their +## sources, by calling scripts/boot-build-p1.sh. ## -## Output (relative to repo root, /work in container): -## build/<arch>/m1pp, build/<arch>/pokem +## Outputs: build/$ARCH/m1pp, build/$ARCH/pokem ## -## Usage: boot2.sh <arch> +## Env: ARCH=aarch64|amd64|riscv64 set -eu -[ "$#" -eq 1 ] || { echo "usage: $0 <arch>" >&2; exit 2; } - -ARCH=$1 +: "${ARCH:?ARCH must be set}" case "$ARCH" in aarch64|amd64|riscv64) ;; *) echo "boot2.sh: unsupported arch '$ARCH'" >&2; exit 1 ;; esac -TABLE=/work/P1/P1-$ARCH.M1 -ELF_HDR=/work/vendor/seed/$ARCH/ELF.hex2 -TOOLS=/work/build/$ARCH/tools -OUT=/work/build/$ARCH - -sh /work/scripts/boot-build-p1.sh \ - "$TABLE" /work/M1pp/M1pp.P1 "$ELF_HDR" \ - "$OUT/.work/m1pp" "$TOOLS" "$OUT/m1pp" - -sh /work/scripts/boot-build-p1.sh \ - "$TABLE" /work/pokem/pokem.P1 "$ELF_HDR" \ - "$OUT/.work/pokem" "$TOOLS" "$OUT/pokem" +sh scripts/boot-build-p1.sh M1pp/M1pp.P1 build/$ARCH/m1pp +sh scripts/boot-build-p1.sh pokem/pokem.P1 build/$ARCH/pokem diff --git a/scripts/lint.sh b/scripts/lint.sh @@ -13,17 +13,20 @@ ## quoted literals (can span lines and carry prose like "usage: lisp") ## and `#`/`;` line comments, so the pass is written in Python. ## -## Usage: lint.sh <p1_arch.M1> <prog.M1> [<prog.M1> ...] +## Env: ARCH=aarch64|amd64|riscv64 +## Usage: lint.sh <prog.M1> [<prog.M1> ...] ## Exit: 0 on success; 1 + diagnostic on any missing token; 2 on misuse. set -eu -if [ "$#" -lt 2 ]; then - echo "usage: $0 <p1_arch.M1> <prog.M1> [<prog.M1> ...]" >&2 +: "${ARCH:?ARCH must be set}" + +if [ "$#" -lt 1 ]; then + echo "usage: ARCH=<arch> $0 <prog.M1> [<prog.M1> ...]" >&2 exit 2 fi -exec python3 - "$@" <<'PYEOF' +exec python3 - "P1/P1-$ARCH.M1" "$@" <<'PYEOF' import re import sys diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh @@ -63,6 +63,7 @@ run_in_container() { arch=$1; shift podman run --rm --pull=never --platform "$(platform_of "$arch")" \ --tmpfs /tmp:size=512M \ + -e "ARCH=$arch" \ -v "$REPO":/work -w /work \ "boot2-busybox:$arch" "$@" } @@ -134,18 +135,12 @@ run_m1pp_suite() { actual=$([ -e "$outfile" ] && cat "$outfile" || echo "") elif [ -e "$m1_src" ]; then bin=build/$arch/m1pp-tests/$name - table=P1/P1-$arch.M1 - elf=vendor/seed/$arch/ELF.hex2 - work=build/$arch/.work/m1pp-tests/$name - tools=build/$arch/tools - if ! sh scripts/lint.sh "$table" "$m1_src" >/dev/null 2>&1 \ - || ! run_in_container "$arch" sh scripts/boot-build-p1.sh \ - "$table" "$m1_src" "$elf" "$work" "$tools" "$bin" \ + if ! ARCH=$arch sh scripts/lint.sh "$m1_src" >/dev/null 2>&1 \ + || ! run_in_container "$arch" sh scripts/boot-build-p1.sh "$m1_src" "$bin" \ >/dev/null 2>&1; then report "$label" FAIL - sh scripts/lint.sh "$table" "$m1_src" 2>&1 | sed 's/^/ /' >&2 || true - run_in_container "$arch" sh scripts/boot-build-p1.sh \ - "$table" "$m1_src" "$elf" "$work" "$tools" "$bin" \ + ARCH=$arch sh scripts/lint.sh "$m1_src" 2>&1 | sed 's/^/ /' >&2 || true + run_in_container "$arch" sh scripts/boot-build-p1.sh "$m1_src" "$bin" \ 2>&1 | sed 's/^/ /' >&2 || true continue fi @@ -190,18 +185,10 @@ run_p1_suite() { for arch in $ARCHES; do label="[$arch] $name" bin=build/$arch/p1-tests/$name - backend=P1/P1-$arch.M1pp - frontend=P1/P1.M1pp - elf=vendor/seed/$arch/ELF.hex2 - work=build/$arch/.work/p1-tests/$name - m1pp_bin=build/$arch/m1pp - tools=build/$arch/tools - if ! run_in_container "$arch" sh scripts/boot-build-p1pp.sh \ - "$backend" "$frontend" "$fixture" "$elf" "$work" "$m1pp_bin" "$tools" "$bin" \ + if ! run_in_container "$arch" sh scripts/boot-build-p1pp.sh "$fixture" "$bin" \ >/dev/null 2>&1; then report "$label" FAIL - run_in_container "$arch" sh scripts/boot-build-p1pp.sh \ - "$backend" "$frontend" "$fixture" "$elf" "$work" "$m1pp_bin" "$tools" "$bin" \ + run_in_container "$arch" sh scripts/boot-build-p1pp.sh "$fixture" "$bin" \ 2>&1 | sed 's/^/ /' >&2 || true continue fi