foundry
Foundry OS: the operating system built on the Rustbelt kernel
| author | Grok Bot <grokbot@localhost> | 2026-10-31 20:38:27 -0700 |
|---|---|---|
| commit | 9595759e94aea9facca6dfcf0ccd00b858773b82 | |
| parent | 36f23a8bbd5c36980020ed8a31c8f93b65980332 | |
| tree | browse files at this commit | |
Bump kernel pin to Rustbelt 0.21.1 (6bb2b25): clean shutdown, K12 done The kernel now unmounts every file system when pid 1 exits, so B:'s FAT dirty bit is clear. check-v2-image.sh fails on a dirty B: (was a WARN), expect-v2.txt expects the [shutdown] lines, and kernel-requests.md marks K12 done (reboot(169) still open, for K7).
| M | README.md | +1 -1 |
| M | c/README.md | +1 -1 |
| M | config.mk | +3 -3 |
| M | docs/boot-contract.md | +12 -6 |
| M | docs/bumping-the-kernel.md | +1 -0 |
| M | docs/kernel-requests.md | +29 -3 |
| M | docs/porting.md | +1 -1 |
| M | scripts/check-v2-image.sh | +8 -7 |
| M | scripts/expect-v2.txt | +7 -0 |
9 files changed, 63 insertions, 22 deletions
@@ -83,7 +83,7 @@ and the exFAT share at `/mnt/share`, and runs `/sbin/init` as process 1 one FAT32 partition at `/mnt/disk0`, the kernel runs `/mnt/disk0/bin/sh`). We build against **one exact kernel commit** (`KERNEL_REV` in `config.mk`,-now `50476f4` = Rustbelt 0.21.0). The build exports that commit from the+now `6bb2b25` = Rustbelt 0.21.1). 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.21.0 (Foundry OS, static-PIE)`.+- `hello-musl.c`: prints `hello from musl on Rustbelt 0.21.1 (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 ?= 50476f4-KERNEL_VERSION ?= 0.21.0+KERNEL_REV ?= 6bb2b25+KERNEL_VERSION ?= 0.21.1 # 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.21.0 (stage 21: root partition and init)+KERNEL_BANNER ?= Rustbelt 0.21.1 (stage 21: root partition and init) # ---- The image ------------------------------------------------------------- ARCH ?= x86_64
@@ -6,11 +6,11 @@ image was built, only that it follows the contract. ## Contract v2: a real root file system (in use since M3) -Pinned kernel: Rustbelt 0.21.0, commit 50476f4 (stage 21, which added-v2; it was kernel request K5). `make image` builds out/foundry.img with+Pinned kernel: Rustbelt 0.21.1, commit 6bb2b25 (stage 21 added v2, which+was kernel request K5; 0.21.1 added the clean shutdown, K12). `make image` builds out/foundry.img with scripts/mkimage-v2.sh; `make test` checks it. -### What the kernel does (Rustbelt 0.21.0)+### What the kernel does (Rustbelt 0.21.1) 1. **Root.** It mounts the **first GPT partition of type "Linux root (x86-64)"** (`4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709`, the freedesktop@@ -34,6 +34,11 @@ scripts/mkimage-v2.sh; `make test` checks it. - Orphaned processes are handed to pid 1 **only if it is named `init`**. pid 1 is never restarted: when it exits with code n (0-111), QEMU exits with **33 + 2n** (33 = pass); anything else gives 35.+ - **Shutdown (0.21.1+).** Before QEMU exits, the kernel syncs and+ unmounts every file system, newest mount first (`/` last), and logs+ one `[shutdown] unmounted PATH (TYPE)` line for each. So B: (FAT32)+ is left with its "dirty bit" clear, like a disk that was safely+ ejected. 5. **No root partition** → the kernel behaves exactly as under v1. ### What Foundry OS puts on the disk@@ -118,15 +123,16 @@ without the kernel, to prove another system can see it. - `orphan` (c/orphan.c): a grandchild left behind is adopted by pid 1 and reaped; init reports how many orphans it reaped (must be ≥ 1); - frames flat: init's memory line shows the same number before and after;-- QEMU exit 33 (init exited 0);+- QEMU exit 33 (init exited 0), after the kernel's `[shutdown] unmounted ...` lines; - afterwards, on the host (scripts/check-v2-image.sh): `e2fsck -fn` on A:,- `fsck.fat -n` on B:, `fsck.exfat -n` on C:, a single-FAT check, and+ `fsck.fat -n` on B: (its FAT dirty bit must be clear), `fsck.exfat -n`+ on C:, a single-FAT check, and scripts/exfat.py reads `from-foundry.txt` back and checks deleted files are gone. ## Contract v1: the old layout (still supported: `make test-v1`) -Used until M3; Rustbelt 0.21.0 still falls back to it when a disk has no+Used until M3; Rustbelt 0.21.x still falls back to it when a disk has no root partition. `make image-v1` builds it (out/foundry-v1.img) and `make test-v1` / `make compare` check it.
@@ -68,6 +68,7 @@ there, with its own target dir (`out/kernel-target`). |---|---|---|---| | 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). |+| 2026-10-31 | `50476f4` 0.21.0 | `6bb2b25` 0.21.1 | K12 ("K12.5") landed: when pid 1 exits, `boot::shutdown()` unmounts every file system newest-first and logs `[shutdown] unmounted PATH (TYPE)`, so B:'s FAT dirty bit is clear. `scripts/check-v2-image.sh` now FAILs on a dirty B: (it was a WARN) and expect-v2.txt has the `[shutdown]` lines. `uname -v` is unchanged; no fixture changes (47 programs); only version strings moved. | ## Known flaky checks
@@ -108,7 +108,33 @@ Follow-ups, in order (each optional): 0.21.0** and used since M3 (docs/boot-contract.md). That is what makes Foundry OS the owner of what runs at boot. -## K12: clean shutdown when pid 1 exits (small, new in M3)+## K12: clean shutdown when pid 1 exits (DONE: K12.5, Rustbelt 0.21.1)++> **Status: done in Rustbelt 0.21.1 (6bb2b25), Edward's "K12.5".** When+> pid 1 exits, `boot::shutdown()` syncs and unmounts every file system,+> newest mount first, before QEMU quits, and prints one line per mount:+>+> ```+> [shutdown] unmounted /mnt/share (exfat)+> [shutdown] unmounted /mnt/disk0 (fat32)+> [shutdown] unmounted /proc (proc)+> [shutdown] unmounted /dev (devfs)+> [shutdown] unmounted /tmp (ramfs)+> [shutdown] unmounted / (ext2)+> ```+>+> FAT32's unmount clears the dirty bit, so after `make test` the host's+> `fsck.fat -n` on B: is clean without any help. scripts/check-v2-image.sh+> now treats a set dirty bit as a hard **FAIL** (it was a WARN), and+> scripts/expect-v2.txt expects the `[shutdown]` lines.+>+> Still open, for later: `reboot` (syscall 169) is not implemented yet.+> When K7 adds it, `reboot(RB_POWER_OFF)` is meant to call the same+> `boot::shutdown()` and then end the QEMU run with an exit status we+> choose, so a `poweroff` command leaves the disks just as clean.++What we asked for (kept for the record):+ Found by `make test` (v2), which boots a writable copy of our image and checks it with `fsck` afterwards. ext2 (A:) and exFAT (C:) come back@@ -133,7 +159,7 @@ Linux will all offer to "repair" the disk. **Request:** when pid 1 exits (and in `reboot`/power-off later, K7), unmount or flush the mounted file systems before QEMU exits. For FAT that means calling fatfs's `unmount()`. Our host check-(scripts/check-v2-image.sh) prints a WARN for now instead of failing.+(scripts/check-v2-image.sh) printed a WARN instead of failing until 0.21.1. ## K13: users and permissions, enforced (OPEN; needed for M6) @@ -174,7 +200,7 @@ without it; **nice** = can wait. | What | How | Goes away when | |---|---|---|-| 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 kernel itself | `git archive $(KERNEL_REV)` (now 6bb2b25) → `out/kernel-src`, built in `out/kernel-target` | never (that's the point), but bumps are deliberate | | the 28 ABI fixtures (v2: in `B:/bin`, i.e. `/mnt/disk0/bin`, last on PATH) | compiled from `out/kernel-src/user` | the self-test stops running them (they move to the kernel's own tests) | | the C:/D:/E: test disks (v1 tests only) | `ext2.img`, `exfat.img`, `btrfs.img` from the kernel build | stays: they are kernel-owned fixtures | | the expected self-test lines | v1: copied into `scripts/run-headless.sh` (same 76 + 12 checks); v2: ours, in `scripts/expect-v2.txt` | — (ours now; kernel-version lines must be updated on a bump) |
@@ -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.21.0 (Foundry OS, static-PIE)"+c/hello-musl.c one line: "hello from musl on Rustbelt 0.21.1 (Foundry OS, static-PIE)" c/fprobe.c the libc self-check: 40 checks, "fprobe: all 40 checks passed" ```
@@ -24,14 +24,15 @@ done e2fsck -fn "$WORK/p1" > "$WORK/e2fsck.txt" 2>&1 && ok "e2fsck -fn: A: (ext2 root) is clean: $(tail -1 "$WORK/e2fsck.txt")" \ || { bad "e2fsck on A:"; cat "$WORK/e2fsck.txt"; }-# The kernel's FAT driver (the fatfs crate) sets the volume's "dirty" bit-# (boot sector byte 65, bit 0) on the first write and clears it only on-# unmount, which Rustbelt 0.21 never does before QEMU exits. That is a-# known kernel issue (docs/kernel-requests.md), so it is a warning here:-# clear the bit in our extracted copy, then check everything else.+# The FAT "dirty" bit (boot sector byte 65, bit 0): the kernel's FAT driver+# (the fatfs crate) sets it on the first write. Since Rustbelt 0.21.1 the+# kernel unmounts every file system when pid 1 exits (the "[shutdown]+# unmounted ..." lines), which clears it again. A set bit means the volume+# was not cleanly unmounted (K12, docs/kernel-requests.md): a hard failure. if [ $(( $(od -An -tu1 -j65 -N1 "$WORK/p2") & 1 )) = 1 ]; then- echo "[check-v2] WARN B: has the FAT dirty bit set (kernel never unmounts it; see docs/kernel-requests.md K12)"- printf '\x00' | dd of="$WORK/p2" bs=1 seek=65 conv=notrunc status=none+ bad "B: has the FAT dirty bit set: it was not cleanly unmounted (K12)"+else+ ok "B: FAT dirty bit is clear (the kernel unmounted it at shutdown)" fi fsck.fat -n "$WORK/p2" > "$WORK/fsck-fat.txt" 2>&1 && ok "fsck.fat -n: B: (FAT32) is clean: $(tail -1 "$WORK/fsck-fat.txt")" \ || { bad "fsck.fat on B:"; cat "$WORK/fsck-fat.txt"; }
@@ -18,6 +18,13 @@ mounted at /mnt/share [init] Foundry OS init, pid 1, on Rustbelt @KERNEL_VERSION@ (headless: running the self-test) [init] mounts: / ext2 [init] pid 1 (/sbin/init) exited:+# Kernel 0.21.1+ (K12): after pid 1 exits, every file system is unmounted,+# newest mount first, before QEMU stops. check-v2-image.sh then checks B:'s+# FAT dirty bit is clear.+[shutdown] unmounted /mnt/share (exfat)+[shutdown] unmounted /mnt/disk0 (fat32)+[shutdown] unmounted /tmp (ramfs)+[shutdown] unmounted / (ext2) # ---- drives and the root (A:) ---- A: / ext2