RustForge

foundry

Foundry OS: the operating system built on the Rustbelt kernel

foundry / docs / kernel-requests.md
5326 bytes ยท raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# Requests to the kernel side (Rustbelt)

The kernel is Edward's lane; Foundry OS never edits `/workspace/rust-kernel`.
These are requests. The full list (K0-K11) with background is in
[proposal.md]proposal.md, section 8.

## How M1 works without any kernel change

The kernel's `build.rs` always builds *all* programs in its `user/` and its
own `target/disk.img`. That does **not** stop us: the disk image is not
inside the kernel's boot image. It is just a `-drive` argument to QEMU. So
`make test`/`make run` boot the kernel's unchanged boot image with **our**
`out/foundry.img` as the first virtio disk. The kernel's own `disk.img`
(built into our `out/kernel-target`) is only used as the reference for
`make compare` and `make test-reference`.

No workaround in the kernel build was needed. The only costs today:

- the kernel build still compiles the moved programs (wasted seconds), and
- the kernel's own `cargo run`/`cargo test` use the kernel's `disk.img`, not
  Foundry OS's. `Cargo.toml`'s `run-args`/`test-args` say
  `file=target/disk.img`, a relative path that also assumes the default
  target dir.

## K1: `RUSTBELT_DISK0` (small, optional for M1, wanted before the kernel deletes the moved programs)

**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
it is unset.

```sh
RUSTBELT_DISK0=/workspace/foundry/out/foundry.img cargo test   # kernel tests on the Foundry OS image
```

The exact change, 16 added lines in `build.rs` only. Tested in a scratch
clone at a55b41d (`/workspace/foundry-scratch/rk`, branch `k1-experiment`,
own target dir): `cargo build` with the variable set produced a
`disk.img` byte-identical to `out/foundry.img`.

```diff
diff --git a/build.rs b/build.rs
index cc5505b..1f432dc 100644
--- a/build.rs
+++ b/build.rs
@@ -130,6 +130,9 @@ fn main() {
     println!("cargo:rerun-if-changed=vdso");
 
     build_vdso(&manifest, &out_dir);
+    // K1: an OS image from outside (Foundry OS's `make image`) as disk 0.
+    println!("cargo:rerun-if-env-changed=RUSTBELT_DISK0");
+    let external_disk0 = env::var_os("RUSTBELT_DISK0").map(PathBuf::from);
     for program in PROGRAMS {
         build_program(&user_dir, &out_dir, program);
     }
@@ -139,7 +142,10 @@ fn main() {
     for &(program, kind) in MUSL_PROGRAMS {
         build_musl_program(&user_dir, &out_dir, program, kind);
     }
-    build_disk(&manifest, &out_dir);
+    match &external_disk0 {
+        Some(img) => use_external_disk(&manifest, img),
+        None => build_disk(&manifest, &out_dir),
+    }
     build_ext2_disks(&manifest, &out_dir);
     build_exfat_disks(&manifest, &out_dir);
     build_btrfs_disks(&manifest, &out_dir);
@@ -263,6 +269,16 @@ fn run(cmd: &mut Command, stdin: Option<&str>) {
     assert!(status.success(), "{cmd:?} failed");
 }
 
+/// K1: copy an OS image built elsewhere to target/disk.img (instead of
+/// building one), so `cargo run`/`cargo test` boot it as disk 0.
+fn use_external_disk(manifest: &Path, img: &Path) {
+    println!("cargo:rerun-if-changed={}", img.display());
+    let target_dir = env::var("CARGO_TARGET_DIR").map(PathBuf::from).unwrap_or_else(|_| manifest.join("target"));
+    fs::create_dir_all(&target_dir).unwrap();
+    fs::copy(img, target_dir.join("disk.img"))
+        .unwrap_or_else(|e| panic!("RUSTBELT_DISK0={}: {e}", img.display()));
+}
+
 fn build_disk(manifest: &Path, out_dir: &Path) {
     let target_dir = env::var("CARGO_TARGET_DIR").map(PathBuf::from).unwrap_or_else(|_| manifest.join("target"));
     fs::create_dir_all(&target_dir).unwrap();
```

Follow-ups, in order (each optional):

1. When `RUSTBELT_DISK0` is set, also skip `build_program` for the moved
   programs (`sh ls cat wc grep mkdir rm touch cp sleep ifconfig ping host
   fetch httpd getty tile random sysinfo`). Their binaries only go into the
   disk the kernel no longer builds.
2. Proposal step 9: once both repos have been green for a while, delete
   the moved programs from `user/`. The kernel keeps its fixtures and a
   frozen `ulib.rs`. `tests/shell.rs` includes `user/shparse.rs`; it would
   either keep a copy or test only through the booted shell.
3. K5 (contract v2: ext2 root, `/sbin/init` as pid 1). That is what makes
   Foundry OS the owner of what runs at boot.

## What Foundry OS still takes from the kernel repo

| 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 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) |
| `scripts/http-reply.sh`, `scripts/hvc0-session.txt` | copied at a55b41d | โ€” (ours now) |
| Debian's `musl-gcc` | builds the 5 musl fixtures, as the kernel does | the fixtures leave the image |