RustForge

foundry

Foundry OS: the operating system built on the Rustbelt kernel

authorGrok Bot <grokbot@localhost>2026-10-31 19:39:06 -0700
commit15035eada55eda065a0ceb6f556a1eb576fa2867
parent3ca471d9454c01b98f035fe0ee9b070c1c66b9ee
treebrowse files at this commit
Bump kernel pin to Rustbelt 0.21.0 (50476f4); add make k1-check (RUSTBELT_DISK0 gives a byte-identical disk.img)
MMakefile+7 -1
MREADME.md+1 -1
Mc/README.md+1 -1
Mconfig.mk+3 -3
Mdocs/boot-contract.md+1 -1
Mdocs/bumping-the-kernel.md+1 -0
Mdocs/kernel-requests.md+11 -1
Mdocs/plan.md+1 -1
Mdocs/porting.md+1 -1
Mscripts/build-kernel.sh+3 -0
Ascripts/k1-check.sh+39 -0
Mscripts/run-headless.sh+2 -2

12 files changed, 71 insertions, 12 deletions

@@ -22,7 +22,7 @@ ALL_BIN    := $(BIN_ORDER) $(FOUNDRY_C) $(FOUNDRY_PORTS) PORTS_OUT  := $(OUT)/ports  .DEFAULT_GOAL := image-.PHONY: help image rust fixtures cprogs ports staging kernel kernel-src run test test-reference compare musl clean distclean+.PHONY: help image rust fixtures cprogs ports staging kernel kernel-src run test test-reference compare k1-check musl clean distclean  help: 	@echo "Foundry OS (userland for the Rustbelt kernel $(KERNEL_VERSION) @ $(KERNEL_REV))"@@ -38,6 +38,7 @@ help: 	@echo "  make test           headless boot + the shell self-test + expected-line checks" 	@echo "  make test-reference  the same headless test, but booting the kernel's own disk.img (baseline)" 	@echo "  make compare        compare out/$(IMAGE_NAME) with the kernel's own disk.img"+	@echo "  make k1-check       build the kernel with RUSTBELT_DISK0=our image (K1) and check it boots ours" 	@echo "  make musl           fetch (cache/) + build musl $(MUSL_VERSION) into out/sysroot" 	@echo "  make clean          rm -rf out   (distclean also removes cache/)" @@ -106,6 +107,11 @@ test-reference: kernel compare: image kernel 	FOUNDRY_BIN="$(FOUNDRY_C) $(FOUNDRY_PORTS)" FOUNDRY_FILES="$(FOUNDRY_FILES)" scripts/compare-image.sh $(IMAGE) $(KTARGET)/disk.img $(OUT) +# K1 (kernel 0.21.0+): the kernel's own build takes our image as disk 0.+# Uses its own target dir, so out/kernel-target (the reference) is untouched.+k1-check: image kernel-src+	scripts/k1-check.sh $(IMAGE) $(KSRC) $(OUT)/kernel-k1-target "$(RUST_PROGRAMS)"+ # ---- toolchain --------------------------------------------------------------- # musl is built once; delete out/sysroot (or run `make musl`) to rebuild it. musl:
@@ -71,7 +71,7 @@ contain. QEMU gets our image as its first virtio disk, the kernel mounts it at `/mnt/disk0` (the shell calls it drive `B:`) and runs `/mnt/disk0/bin/sh`.  We build against **one exact kernel commit** (`KERNEL_REV` in `config.mk`,-now `5291332` = Rustbelt 0.20.0). The build exports that commit from the+now `50476f4` = Rustbelt 0.21.0). The build exports that commit from the kernel repo's git store with `git archive` and compiles it in `out/`, so the kernel repo itself is never changed. To move to a newer kernel, follow [docs/bumping-the-kernel.md](docs/bumping-the-kernel.md).
@@ -6,7 +6,7 @@ are installed in `/bin` and the self-test runs them (the "Foundry OS" block at the end of `rootfs/scripts/selftest.sh`; expected output in `scripts/expect-foundry.txt`). -- `hello-musl.c`: prints `hello from musl on Rustbelt 0.20.0 (Foundry OS, static-PIE)`.+- `hello-musl.c`: prints `hello from musl on Rustbelt 0.21.0 (Foundry OS, static-PIE)`. - `fprobe.c`: the libc self-check, a small, deterministic cousin of   Clarke's `mprobe2` probe (clarke-scratch/exp16..exp19). 40 checks: ELF   and auxv, files in /tmp, fork/exec/wait, pipes, signals, threads and
@@ -7,12 +7,12 @@ # into out/kernel-src and build it there, with its own target dir. Bump # KERNEL_REV deliberately (and re-run `make test`) when the kernel moves on. KERNEL_DIR     ?= /workspace/rust-kernel-KERNEL_REV     ?= 5291332-KERNEL_VERSION ?= 0.20.0+KERNEL_REV     ?= 50476f4+KERNEL_VERSION ?= 0.21.0 # The first line the kernel prints (also `ver` and uname's "version"). The # self-test checks for it, so it changes with every pin bump; see # docs/bumping-the-kernel.md.-KERNEL_BANNER  ?= Rustbelt 0.20.0 (stage 20: gcc -static and timerfd)+KERNEL_BANNER  ?= Rustbelt 0.21.0 (stage 21: root partition and init)  # ---- The image ------------------------------------------------------------- ARCH           ?= x86_64
@@ -6,7 +6,7 @@ image was built, only that it follows the contract.  ## Contract v1: "today, written down" (in use now) -Pinned kernel: Rustbelt 0.20.0, commit 5291332 (was 0.19.0, a55b41d). No kernel change needed.+Pinned kernel: Rustbelt 0.21.0, commit 50476f4 (was 0.20.0, 5291332). No kernel change needed.  1. **Where.** The OS image is the **first virtio-blk disk** QEMU is given.    The kernel mounts it at `/mnt/disk0` (drive `B:` in rbsh).
@@ -67,6 +67,7 @@ there, with its own target dir (`out/kernel-target`). | Date | From | To | Notes | |---|---|---|---| | 2026-10-31 | `a55b41d` 0.19.0 | `5291332` 0.20.0 | User space now starts at 0x40_0000, so plain `gcc -static` / `musl-gcc -static` ET_EXEC programs run: the fixture build lost its `-Ttext-segment`/crt1/`_DYNAMIC` tricks. New fixtures `exechello`, `execthreads` (piethreads.c as ET_EXEC), `timerfdtest`: /bin has 47 programs. timerfd (283/286/287) works. |+| 2026-10-31 | `5291332` 0.20.0 | `50476f4` 0.21.0 | Boot contract v2 (K5): GPT "Linux root (x86-64)" ext2 partition mounted at `/`, `/sbin/init` as pid 1, its exit status ends QEMU (33 + 2n); exFAT "basic data" partition at `/mnt/share`. K1 landed (`make k1-check`). `msync` validates and returns 0. No fixture changes (47 programs); only the version lines of the expect list changed. The kernel build now also makes `rootfs-gpt.img` (its own v2 test disk; we don't use it). |  ## Known flaky checks 
@@ -24,6 +24,16 @@ No workaround in the kernel build was needed. The only costs today:  ## K1: `RUSTBELT_DISK0` (small, optional for M1, wanted before the kernel deletes the moved programs) +> **Status: landed in Rustbelt 0.21.0 (50476f4).** Edward took this patch+> and went further: `CARGO_TARGET_DIR` is honored everywhere (his scripts+> and `Cargo.toml` paths, via the new `scripts/qemu.sh` runner), and the 19+> moved programs aren't even built when `RUSTBELT_DISK0` is set.+> `make k1-check` builds the pinned kernel that way in its own target dir+> (`out/kernel-k1-target`) and checks both: the kernel's `disk.img` is+> byte-identical to `out/foundry.img`, and no moved program was built.+> We still build the reference kernel *without* the variable, because+> `make test-reference` and `make compare` need the kernel's own disk.img.+ **Request:** in `build.rs`, if the environment variable `RUSTBELT_DISK0` is set to an image file, copy that file to `$CARGO_TARGET_DIR/disk.img` (or `target/disk.img`) instead of building the FAT32 disk. Nothing changes when@@ -101,7 +111,7 @@ Follow-ups, in order (each optional):  | What | How | Goes away when | |---|---|---|-| the kernel itself | `git archive $(KERNEL_REV)` (now 5291332) → `out/kernel-src`, built in `out/kernel-target` | never (that's the point), but bumps are deliberate |+| the kernel itself | `git archive $(KERNEL_REV)` (now 50476f4) → `out/kernel-src`, built in `out/kernel-target` | never (that's the point), but bumps are deliberate | | the 25 ABI fixtures in `/bin` | compiled from `out/kernel-src/user` | the self-test stops running them (they move to the kernel's own tests) or contract v2 | | the C:/D:/E: test disks | `ext2.img`, `exfat.img`, `btrfs.img` from the kernel build | stays: they are kernel-owned fixtures | | the expected self-test lines | copied into `scripts/run-headless.sh` (same 76 + 12 checks) | — (ours now; kernel-version lines must be updated on a bump) |
@@ -10,7 +10,7 @@ | 4 | Root file system | Later: an **ext2 root**, so `A:` = `/` is the OS disk. The FAT32 disk stays as a **data disk** (`B:`). Plus an **exFAT data partition** for sharing files with Windows/macOS. M1 keeps contract v1 (today's layout). | | 5 | Name clashes | The Rust tools stay the default. busybox/sbase applets go under **`/usr/lib/busybox`**, off the default `PATH` (call them by full path, or add the directory to `PATH` yourself). | | 6 | Tarballs | **Not in git.** A git-ignored download cache (`cache/`), each file pinned by SHA256 (`toolchain/musl/musl.sha256` + `fetch.sh`; `ports/<name>/<name>.sha256`). |-| 7 | Which kernel | A **pinned commit**: `KERNEL_REV` in `config.mk` (now `5291332`, Rustbelt 0.20.0; was `a55b41d`, 0.19.0), bumped deliberately: see [bumping-the-kernel.md](bumping-the-kernel.md). |+| 7 | Which kernel | A **pinned commit**: `KERNEL_REV` in `config.mk` (now `50476f4`, Rustbelt 0.21.0; before that `5291332`, 0.20.0), bumped deliberately: see [bumping-the-kernel.md](bumping-the-kernel.md). | | 8 | Box packages | Small build packages are OK (`make`, `bzip2`, `musl-tools`, `autoconf`). |  ## Disk layout later (contract v2, not built yet)
@@ -61,7 +61,7 @@ kernel tests it with its own fixtures (`exechello`, `execthreads`, ## Our own C programs (c/)  ```-c/hello-musl.c   one line: "hello from musl on Rustbelt 0.20.0 (Foundry OS, static-PIE)"+c/hello-musl.c   one line: "hello from musl on Rustbelt 0.21.0 (Foundry OS, static-PIE)" c/fprobe.c       the libc self-check: 40 checks, "fprobe: all 40 checks passed" ``` 
@@ -16,6 +16,9 @@ # images into out/kernel-target: disk.img (the reference for `make compare`) # and the kernel-owned test disks ext2.img, exfat.img, btrfs.img, which # `make run`/`make test` attach as drives C:, D:, E: (contract v1, point 5).+# So this build must NOT set RUSTBELT_DISK0 (K1, kernel 0.21.0+): that would+# replace the reference disk.img with ours. `make k1-check` does that in a+# separate target dir instead. # # Usage: scripts/build-kernel.sh KERNEL_DIR KERNEL_REV OUT_DIR [--export-only] set -euo pipefail
@@ -0,0 +1,39 @@+#!/usr/bin/env bash+# K1 check (kernel 0.21.0+): the kernel's own build can take our image as+# its disk 0. With RUSTBELT_DISK0=out/foundry.img, the kernel's build.rs+# copies our image to $CARGO_TARGET_DIR/disk.img instead of making its own,+# and skips the programs that moved to Foundry OS. This builds the pinned+# kernel that way, in its OWN target dir (out/kernel-k1-target, so the+# normal out/kernel-target with the kernel's reference disk.img is left+# alone), and checks:+#   1. the disk.img it ends up with is byte-identical to our image;+#   2. none of the moved programs (sh, ls, cat, ...) was built.+# After that, the kernel's own tooling (`cargo run`, `cargo test`, its+# scripts/run-headless.sh) boots Foundry OS: run it in out/kernel-src with+# the same two variables, e.g.+#   cd out/kernel-src && RUSTBELT_DISK0=../foundry.img \+#       CARGO_TARGET_DIR=../kernel-k1-target cargo run+#+# Usage: scripts/k1-check.sh IMAGE KERNEL_SRC TARGET_DIR "MOVED PROGRAMS"+set -euo pipefail+IMAGE=$(realpath "$1") SRC=$(realpath "$2") T=$(realpath -m "$3") MOVED=$4+rm -rf "$T" && mkdir -p "$T"     # fresh, so whatever is in it was built just now+( cd "$SRC" && RUSTBELT_DISK0="$IMAGE" CARGO_TARGET_DIR="$T" cargo build --features headless )+ok=1+if cmp -s "$IMAGE" "$T/disk.img"; then+    echo "[k1-check] $T/disk.img is byte-identical to $IMAGE"+else+    echo "[k1-check] FAIL: $T/disk.img differs from $IMAGE"; ok=0+fi+# build.rs puts the user programs it builds in its OUT_DIR+# ($T/x86_64-rust_kernel/debug/build/rustbelt/HASH/out/).+# Only the files directly in out/ (out/rootfs-tree/bin/sh is the kernel's+# boot-v2 test init, not a moved program).+built=$(find "$T" -path '*/build/rustbelt/*/out/*' -type f | grep -E '/build/rustbelt/[^/]+/out/[^/]+$' | xargs -n1 basename | sort -u)+[ -n "$built" ] || { echo "[k1-check] FAIL: found no build.rs output at all"; ok=0; }+echo "[k1-check] build.rs built $(wc -l <<<"$built") files, e.g. $(grep -x -m1 forktest <<<"$built" || echo '(no forktest!)')"+for p in $MOVED; do+    if printf '%s\n' "$built" | grep -qx "$p"; then echo "[k1-check] FAIL: moved program $p was still built"; ok=0; fi+done+[ $ok = 1 ] && echo "[k1-check] OK: the kernel build uses Foundry OS's image as disk 0 and skips the $(wc -w <<<"$MOVED") moved programs"+[ $ok = 1 ]
@@ -12,8 +12,8 @@ #                     ext2.img, exfat.img, btrfs.img: drives C:, D:, E:) #   OUT_DIR           where logs go (out/) # Env (the Makefile sets these from config.mk; they change with the kernel pin):-#   KERNEL_BANNER   the kernel's first line, e.g. "Rustbelt 0.20.0 (stage 20: ...)"-#   KERNEL_VERSION  e.g. 0.20.0+#   KERNEL_BANNER   the kernel's first line, e.g. "Rustbelt 0.21.0 (stage 21: ...)"+#   KERNEL_VERSION  e.g. 0.21.0 #   BIN_COUNT       how many programs the image has in /bin (`dir bin | wc -l`) #   EXTRA_EXPECT    optional file: more lines the log must contain, one per #                   line (Foundry OS's own checks; # starts a comment)