# The boot contract: what the kernel expects from the Foundry OS image The Rustbelt kernel and Foundry OS live in different repos. This page is the whole agreement between them. The kernel never needs to know *how* the 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 scripts/mkimage-v2.sh; `make test` checks it. ### What the kernel does (Rustbelt 0.21.0) 1. **Root.** It mounts the **first GPT partition of type "Linux root (x86-64)"** (`4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709`, the freedesktop *Discoverable Partitions* type) that holds **ext2** at **`/`**. Only ext2, and there is no `root=` option to pick another one. 2. **Share.** It mounts the first **"Microsoft basic data"** partition holding **exFAT** at **`/mnt/share`**, read-write. Limits: no rename, no timestamps, no volume-dirty flag. A TexFAT volume (two FATs) is mounted read-only, so our share has one FAT (mkfs.exfat's default). 3. **Other disks/partitions** get `/mnt/disk0`, `/mnt/disk1`, ... in device order (the root and the share don't use a number). It also provides a ramfs at `/tmp`, devfs at `/dev`, proc at `/proc`. 4. **pid 1.** It runs the first of `/sbin/init`, `/bin/init`, `/bin/sh` that starts, with the console as its terminal: - argv `["init"]` (`["sh"]` for the sh fallback); environment `PATH=/sbin:/bin:/usr/bin:/mnt/disk0/bin HOME=/ TERM=linux`. - Headless kernel build: argv `["init", "selftest"]` (sh: `["sh", "/scripts/selftest.sh"]`) plus `RUSTBELT_HEADLESS=1`. - `RUSTBELT_INIT_ARGS` / `RUSTBELT_INIT_ENV` at kernel *build* time add arguments and variables. There are no run-time boot options. - 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. 5. **No root partition** → the kernel behaves exactly as under v1. ### What Foundry OS puts on the disk One disk, GPT, three partitions (sizes in config.mk): | # | GPT type | file system | size | mounted at | rbsh drive | from | |---|----------------------|-------------|--------|--------------|------------|-----------| | 1 | Linux root (x86-64) | ext2 (1 KiB blocks), label `foundry-root` | 32 MiB | `/` | **A:** | `root/` | | 2 | Microsoft basic data | FAT32, label `FOUNDRY` | 40 MiB | `/mnt/disk0` | **B:** | `rootfs/` | | 3 | Microsoft basic data | exFAT (1 FAT), label `SHARE` | 16 MiB | `/mnt/share` | **C:** | `share/` | **Why one disk and not three?** A real computer installs an OS on one disk with several partitions, and the kernel looks for the root and the share by partition type, so one image file is enough: one file to copy, one `-drive` for QEMU, and the letters can't get mixed up by the order of disks. (Several disks would also work; nothing in the kernel needs one.) `make test` attaches *only* this disk. The kernel's own ext2/exFAT/btrfs test disks are checked by `make test-v1` and `make test-reference`. **A: (root, ext2)** holds the system: - `/sbin/init`: Foundry OS init (rust/init). It is named `init`, so the kernel hands it orphans. It starts **rbsh** as the login shell on the console and on `/dev/hvc0` (when QEMU has a virtio console), starts a new one when a shell exits (it gives up on a terminal when the shell dies within a second of starting 5 times in a row, or can't be started at all), and reaps every orphan it is given. Headless (`init selftest` or `RUSTBELT_HEADLESS=1`) it runs `rbsh /scripts/selftest.sh` on the console, waits up to 5 s for all leftover processes, prints `[init] memory: N frames in use before the self-test, M after ...` (the kernel does not print a frame report under v2), and exits with the shell's status. - `/bin`: **rbsh** (`/bin/rbsh`), **dash** (`/bin/dash`, and `/bin/sh`, a symlink to it), the Rust tools (`ls`, `cat`, `cp`, ...: first on PATH), our C programs (`hello-musl`, `fprobe`, `orphan`). - `/etc` (passwd, group, hosts, resolv.conf, motd), `/scripts` (the self-test), and empty `/usr/bin`, `/tmp`, `/root`, `/home/tyler`, `/mnt/disk0`, `/mnt/share`. **B: (data, FAT32)** holds `readme.txt`, `notes/` and, in `/bin`, the kernel's **ABI test programs** (`forktest`, `rawabi`, `piehello`, ... 28 of them, built from the pinned kernel by `make fixtures`). They are last on PATH (`/mnt/disk0/bin`), so the self-test runs them by name and they never hide one of our tools. **C: (share, exFAT)** holds `README.TXT`. scripts/exfat.py writes it there, because mkfs.exfat can only create an empty volume. ### Drive letters and the share, for beginners Linux has one tree of folders starting at `/`. Each disk (or partition) is *mounted* on a folder in that tree: its files appear under that folder. rbsh adds DOS-style **drive letters** on top, as shortcuts: - **A:** is `/`, the root: the whole system. `A:/bin/ls` is `/bin/ls`. - **B:** is `/mnt/disk0`, the data disk (FAT32, the format of old USB sticks). - **C:** is `/mnt/share`, the share. - More disks would be D:, E:, ... (`drives` lists them). `cd C:` takes you to the share; `ls B:/bin` lists the test programs. Other programs (dash, C programs) don't know the letters and use the real paths. **The share** is a place for files that go between Foundry OS and other computers. It uses **exFAT**, the format of SD cards and USB sticks, which Windows, macOS and Linux all read and write, so the partition can be copied out of the image and opened elsewhere. In Foundry OS you can create, write and delete files there (not rename them yet). `make test` writes `from-foundry.txt` to it and then reads it back *on the host*, without the kernel, to prove another system can see it. ### How `make test` checks this contract - the banner; the kernel's mount lines (`mounted at / (root partition)`, `mounted at /mnt/share`); init's start line and its mount list; - `drives` shows `A: / ext2`, `B: /mnt/disk0 fat32`, `C: /mnt/share exfat`; - rbsh writes to A: and C:; dash (root/scripts/v2-test.sh) checks the environment, PATH order, `/bin/sh` → dash, and writes, reads back and deletes files on A:, B: and C:; - the kernel's stages (processes, threads, network, virtio...) as under v1; - `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); - 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 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 root partition. `make image-v1` builds it (out/foundry-v1.img) and `make test-v1` / `make compare` check it. 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). 2. **Format.** A **GPT** partition table with one partition starting at sector 2048 (1 MiB), type "Microsoft basic data", formatted **FAT32**, volume label `TYLERDISK` (kept from the kernel for now). 64 MiB total. 3. **Contents.** - **`/bin/sh`**: the program the kernel starts. In Foundry OS this is **rbsh** (crate `rust/rbsh`, binary `rbsh`), copied under the name `sh`. - `/scripts/selftest.sh`: the self-test, for headless kernels. - Programs live in `/bin`. Each is a **static** ELF: either a **static-PIE** (`ET_DYN` without `PT_INTERP`, since 0.19.0; what Foundry OS builds) or an `ET_EXEC` linked anywhere in user space, which since 0.20.0 starts at `0x40_0000`, so a plain `gcc -static` program at the usual `0x400000` runs (until 0.19.0 it had to be at or above `0x80_0040_0000`). Dynamic programs are refused. 4. **What the kernel provides.** `/` is a ramfs the kernel builds itself (with `/tmp`, `/dev`, `/proc`, `/mnt`). It runs `/mnt/disk0/bin/sh` with `PATH=/bin:/mnt/disk0/bin HOME=/ TERM=linux`, and restarts it when it exits. A headless kernel (`--features headless`) instead runs `sh /mnt/disk0/scripts/selftest.sh` once and powers QEMU off with the shell's status: QEMU exit code **33 = pass**, 35 = fail. 5. **Other disks belong to the kernel.** The ext2, exFAT and btrfs test disks are built by the kernel's build into `out/kernel-target`. Our run scripts attach them after our disk, so `C:`, `D:`, `E:` keep their letters. Under v1 the image must also carry the kernel's ABI test programs (`forktest`, `rawabi`, ...), because the self-test runs them. They are built from the pinned kernel source (`make fixtures`); see [kernel-requests.md](kernel-requests.md) for how that dependency goes away.