diff --git a/CLAUDE.md b/CLAUDE.md index 577449ced1b..0a5cff196ad 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -18,7 +18,8 @@ An operating system built from scratch in Rust, held to a production-grade engin There are no spec documents. Rules live where they are enforced — a gate, a module header, the redlist, the review prompt — and everything else is an issue. Free text that merely describes the tree rots and is deleted, not -maintained. +maintained. A `CLAUDE.md` never holds a list that a manifest, a directory or a +gate already answers; it points at that source instead. A subdirectory `CLAUDE.md` loads when a file in that subtree is `Read`, and not from `Bash`. A rule whose violation is unrecoverable or invisible stays here; everything else lives where the work is. @@ -44,7 +45,7 @@ A subdirectory `CLAUDE.md` loads when a file in that subtree is `Read`, and not **Syscall ABI** — `toyos-abi/`: struct layouts, syscall numbers, typed wrappers; completely unstable, read the code. Never add or change a syscall without discussion; a deleted syscall's number is retired, never reused. `toyos/` builds on it with typed handles, IPC framing, ports, namespaces and `surface` — userland uses `toyos`, the kernel uses `toyos-abi` only. -**Capabilities** — a process holds exactly what its parent moved into it, and among kernel objects there is nothing it can name to get more. No registry, no connect-by-name, no pid-as-authority: `/system/bin/init` builds every program's namespace and device claims from `system.toml` before spawning it, and a handle a process does not hold is a bug in that process — the kernel ends it rather than answering a word it can ignore. **Isolation is non-negotiable, and the filesystem is inside it**: a process names only the paths in the view its parent built for it, the unit of isolation is the program, and a user is the part of the tree a session was handed. +**Capabilities** — a process holds exactly what its parent moved into it, and among kernel objects there is nothing it can name to get more. No registry, no connect-by-name, no pid-as-authority: `/system/bin/init` builds every program's namespace and device claims from `system.toml` before spawning it, and a handle a process does not hold is a bug in that process — the kernel ends it rather than answering a word it can ignore. **Isolation is non-negotiable, and the filesystem is inside it**: a process names only the paths in the view its parent built for it, the unit of isolation is the program, and a user is the part of the tree a session was handed. Not yet true of files: the kernel still resolves every path against one machine-wide tree until the storage track's per-program views land. **CPU state** — a CPU's control registers come from one declaration, applied by the BSP and by every AP and asserted on each; no read-modify-write decides what either holds. @@ -54,7 +55,7 @@ A subdirectory `CLAUDE.md` loads when a file in that subtree is `Read`, and not ## Dependencies -Only **Rust** and **QEMU** (for development). The rules: no binary outside those two — a macOS binary is a hard no, and "only for tests" does not soften it; only general and widely used crates — one that does *our* job we write ourselves, and a driver crate never; no Python; third-party crates are used as published, and a fork carries a change written to upstream quality and goes when upstream has it. No upstream pull requests are sent for now: ToyOS needs more attention and more contributors before upstream projects take it seriously, and upstreams tend to refuse AI-first projects and their contributions. A third-party source ToyOS cannot build without changing it is carried as an unmodified-source packaging mirror with a byte-identity gate, not as a fork. The north star is **self-hosting**: nothing — build, test, or verification — rests on a host binary. Ask of anything new: could this ever run inside ToyOS? Self-hosting means ToyOS rebuilds itself on ToyOS and reproduces the host's bytes; a bootstrap from source with no binary seed is out of scope. +Only **Rust** and **QEMU** (for development), on any host OS and architecture — the development machine is nothing special. The rules: no binary outside those two — a macOS binary is a hard no, and "only for tests" does not soften it; only general and widely used crates — one that does *our* job we write ourselves, and a driver crate never; no Python; third-party crates are used as published, and a fork carries a change written to upstream quality and goes when upstream has it. No upstream pull requests are sent for now: ToyOS needs more attention and more contributors before upstream projects take it seriously, and upstreams tend to refuse AI-first projects and their contributions. A third-party source ToyOS cannot build without changing it is carried as an unmodified-source packaging mirror with a byte-identity gate, not as a fork. The north star is **self-hosting**: nothing — build, test, or verification — rests on a host binary. Ask of anything new: could this ever run inside ToyOS? Self-hosting means ToyOS rebuilds itself on ToyOS and reproduces the host's bytes; a bootstrap from source with no binary seed is out of scope. Vendor firmware a device verifies by its maker's signature may be shipped: pinned by version and hash, redistributable unmodified, recorded in `NOTICE`, and loaded only by that device's own driver through its IOMMU domain; it never executes on the CPU. @@ -74,38 +75,7 @@ The testing rules live where they are enforced: known reds in `src/redlist.rs`, ## Repository layout -``` -src/ Build system (the root cargo project, package name: toyos-build; its Cargo.toml is also the host workspace, and a gate reds on a crate that joins neither members nor exclude) -kernel/ Kernel -kernel-loom/ Loom models of the kernel's lock-free concurrency, beside the kernel and not in it -toyos-userbound/ Every decision the kernel makes about the user/kernel boundary, pure -toyos-elide/ Log elision decisions, pure -toyos-proclife/ The process/thread lifecycle's decisions — pure, interleaving-checked -bootloader/ UEFI bootloader -userland/ All userland programs -toyos-abi/ Kernel ABI (types, constants, syscall numbers, syscall wrappers) -toyos/ Userland SDK (typed handles, IPC, ports, namespaces, surface, shm, net) -toyos-manifest/ The one definition of `/system/etc/system.manifest` -toyos-wallclock/ The calendar, and the zone offset userland has to recover — pure -toyos-keymap/ Layouts, dead-key composition, key translation, layout detection -toyos-fat32/ FAT32 driver, read + write; no format path by design -toyos-fat32-check/ FAT32 checker from Microsoft's fatgen103 — the outside judge -toyos-elf/ ELF64 decoding (no_std, no alloc, forbid(unsafe_code)) -toyos-symbols/ Backtrace symbol lookup: locating an ELF's symbol tables and budgeting the demangled name (no_std, no alloc, forbid(unsafe_code)) -toyos-gpt/ GPT parser (no_std, no alloc, forbid(unsafe_code)) -toyos-hda/ HDA codec decoding and output-path selection, pure -toyos-mixer/ The mixer's decisions — samples, gain, dither, quantize — pure, corpus-certified -toyos-pci/ MSI and MSI-X capability decoding, pure -toyos-dma/ Every bound and alignment a DMA view checks — pure, forbid(unsafe_code) -toyos-blockhold/ Who holds each span of a block device, and whose flush answers for the writes its disk lost — pure -toyos-desktop/ Every decision the compositor makes, pure -toyos-ld/ Custom linker -toyos-cc/ Custom C compiler -rust/ Rust compiler/std fork (submodule) -tests/ Integration tests (QEMU-based) -issues/ The issue tracker: one file per issue, typed by kind — see its README -system.toml What to build and boot -``` +The root `Cargo.toml`'s `[workspace]` `members` and `exclude` lists account for every crate in the tree, and `src/hostws.rs` reds on one in neither; every package they name says what it is in its `description`, and a gate there reds on one without. ## Workflow diff --git a/Cargo.toml b/Cargo.toml index b6f9cc4f205..ccc0d35460f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -96,6 +96,7 @@ exclude = [ [package] name = "toyos-build" +description = "The build system: toolchain, kernel, bootloader, userland and image, the QEMU harness, CI jobs, and the gates the tree is held to." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/bcachefs/Cargo.toml b/bcachefs/Cargo.toml index f4837372ed7..3c5177cae10 100644 --- a/bcachefs/Cargo.toml +++ b/bcachefs/Cargo.toml @@ -1,5 +1,6 @@ [package] name = "bcachefs" +description = "The /home filesystem: a read side of the real bcachefs on-disk format." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/bootloader/Cargo.toml b/bootloader/Cargo.toml index 9f03ccfdc9a..7e57aadd824 100644 --- a/bootloader/Cargo.toml +++ b/bootloader/Cargo.toml @@ -1,5 +1,6 @@ [package] name = "bootloader" +description = "The UEFI bootloader, which loads the kernel and hands it the machine." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/issues/audio/thorough-tier-reds-on-unmodified-main.md b/issues/audio/thorough-tier-reds-on-unmodified-main.md index 8d8b1803b83..575001d5335 100644 --- a/issues/audio/thorough-tier-reds-on-unmodified-main.md +++ b/issues/audio/thorough-tier-reds-on-unmodified-main.md @@ -154,7 +154,7 @@ the instrument refusing — stopped' after the last client removal — the device is still running with no clients`. That is filed apart as `gate-a-suspend-structure-verdict-unread`. -The exit code is fixed in `.github/workflows/gate-a.yml` (`set -o pipefail`, the +The exit code is fixed in `35383398^:.github/workflows/gate-a.yml` (`set -o pipefail`, the idiom every other workflow in `.github/` already uses). Nothing about how a verdict is *reached* changed. diff --git a/issues/boot-media/log-flush-retry-two-older-failure-modes-with-no-home.md b/issues/boot-media/log-flush-retry-two-older-failure-modes-with-no-home.md deleted file mode 100644 index be1ba2f83d2..00000000000 --- a/issues/boot-media/log-flush-retry-two-older-failure-modes-with-no-home.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -status: open -kind: tooling -opened: 2026-09-08 ---- - -# `log_flush_retry` has two older failure modes that lost their tracker file - -`issues/boot-media/log-flush-retry-reds-two-ways-at-two-in-five.md` recorded -four ways `cargo test --test toyos-build -- --nightly log_flush_retry` had -been seen to red; that file was deleted (`d5c2d9c9`) once ROOT-in-memory gave -the `[hung]` arm's own mechanism — a stick going offline while userland is -still paged from it — a fix and six green runs. Two of its other ways were not -re-seen in those six runs and so were not carried anywhere: - -- **The `[hung]` arm missing its "transport broke on SCSI" line.** One of the - file's two originally-recorded assertions: the wide run reds on - `the deadman never declared the volume failed` while the *alone* re-run of - the same boot reds instead on - `no "transport broke on SCSI" in the log, so the staged hung device never - met its recovery` — two different assertions from the same staged fault, - which was the file's reason for treating this as more than a scheduling - classification. -- **A boot timeout before `===READY===`.** Measured 2026-09-07 on three - separate points (`main` 71ed50cf, `metal-suite` 7f16914d and f63bce1d), each - run alone: `log_flush_retry: [qemu] Boot timed out waiting for - ===READY===`, with the boot never coming up at all — a third shape beside - the two assertion failures, seen before ROOT-in-memory and on a branch that - does not carry it. - -Neither mode was seen in the six runs that closed the deleted file, so neither -is known fixed by that work. - -## Exit condition - -Re-measure `log_flush_retry` (wide and alone) enough times, on a tree that -carries ROOT-in-memory, to say whether either mode still occurs; if seen -again, it is disabled with a `src/redlist.rs` row and this file, or it is -fixed at its owner (`kernel/src/drivers/xhci` for the transport line, the boot -harness for the timeout). If not seen in that many runs, this file closes with -the count that supports it. diff --git a/issues/build/a-lane-s-tap-socket-path-is-past-sun-len-on-the-dev-host.md b/issues/build/a-lane-s-tap-socket-path-is-past-sun-len-on-the-dev-host.md deleted file mode 100644 index a055bb032a6..00000000000 --- a/issues/build/a-lane-s-tap-socket-path-is-past-sun-len-on-the-dev-host.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -status: open -kind: tooling -opened: 2026-09-26 ---- - -# A lane's tap socket path is past `SUN_LEN` on the dev host - -`lan_mdns_answer` reds on the macOS dev host, wide and alone: - -``` -connect to QEMU's /private/var/folders/gr/mr4_fg4n34jb417sx1g5cgxc0000gp/T/toyos-tmp-89085-0/tests-0/lane-3/tap-out-0.sock: path must be shorter than SUN_LEN -``` - -That path is 104 bytes, and macOS's `sun_path` holds 104 including the NUL. -`tests/common/segment.rs`'s `Tap::in_lane` puts both sockets in -`lane::dir()`, which since `toyos-tmpdir` is -`$TMPDIR/toyos-tmp--/tests-/lane-/`, and the dev host's -`$TMPDIR` resolves to 57 bytes (`/private/var/folders/…/T/`) before any of -that. A five-digit pid is enough to cross the limit. Seen on -`wt/toyos-layout` after it merged `origin/main` at `e48604c0`; nothing on that -branch touches the lane or the tap. - -## Exit condition - -A tap socket's path fits `sun_path` on every host the suite runs on, and -`lan_mdns_answer` is green on the dev host. diff --git a/issues/build/a-lane-s-tap-socket-path-outgrows-sun-len-on-the-dev-host.md b/issues/build/a-lane-s-tap-socket-path-outgrows-sun-len-on-the-dev-host.md index 230a3d13bbe..7e81a9cf3d2 100644 --- a/issues/build/a-lane-s-tap-socket-path-outgrows-sun-len-on-the-dev-host.md +++ b/issues/build/a-lane-s-tap-socket-path-outgrows-sun-len-on-the-dev-host.md @@ -1,23 +1,37 @@ --- status: open -kind: defect +kind: tooling opened: 2026-09-26 --- # A lane's tap socket path outgrows `SUN_LEN` on the dev host -`lan_mdns_answer` reds wide and alone with `connect to QEMU's -/private/var/folders/gr/mr4_fg4n34jb417sx1g5cgxc0000gp/T/toyos-tmp-70685-0/tests-0/lane-7/tap-out-0.sock: -path must be shorter than SUN_LEN`. `common::segment::Tap::in_lane` puts the -two sockets in the lane's scratch directory, and on this macOS host that -directory sits under `$TMPDIR`, so the path is 104 bytes, past the 103 a -`sockaddr_un` holds before its terminating NUL on macOS. - -Seen in the fast tier twice in one session: at `origin/main` checked out in -the `toyos-guiplat` worktree (alone with this message, wide as `QEMU died -before ===READY===`), and at PR #528's head after it merged `e48604c0` (this -message wide and alone). `cargo run --- --known-red lan_mdns_answer` answers NO. - -**Exit**: the socket paths fit a `sockaddr_un` wherever the scratch -directory is, with `lan_mdns_answer` green on this host. +`lan_mdns_answer` reds on the macOS dev host, wide and alone: + +``` +connect to QEMU's /private/var/folders/gr/mr4_fg4n34jb417sx1g5cgxc0000gp/T/toyos-tmp-89085-0/tests-0/lane-3/tap-out-0.sock: path must be shorter than SUN_LEN +``` + +That path is 104 bytes, past the 103 a `sockaddr_un` holds before its +terminating NUL on macOS. `tests/common/segment.rs`'s `Tap::in_lane` puts both +sockets in `lane::dir()`, which since `toyos-tmpdir` is +`$TMPDIR/toyos-tmp--/tests-/lane-/`, and the dev host's +`$TMPDIR` resolves to 57 bytes (`/private/var/folders/…/T/`) before any of +that. A five-digit pid is enough to cross the limit. + +Seen three times on 2026-09-26, each on a tree whose diff touches neither the +lane nor the tap: + +- on `wt/toyos-layout` after it merged `origin/main` at `e48604c0` (pid 89085, + `lane-3`, the capture above); +- in the fast tier at `origin/main` checked out in the `toyos-guiplat` + worktree: alone with this message, wide as `QEMU died before ===READY===`; +- in the fast tier at PR #528's head after it merged `e48604c0` (pid 70685, + `lane-7`), this message wide and alone. + +`cargo run -- --known-red lan_mdns_answer` answers NO. + +## Exit condition + +A tap socket's path fits `sun_path` on every host the suite runs on, wherever +the scratch directory is, and `lan_mdns_answer` is green on the dev host. diff --git a/issues/build/a-shards-boot-width-does-not-price-its-tests.md b/issues/build/a-shards-boot-width-does-not-price-its-tests.md index 226617ce122..1fe190cea2a 100644 --- a/issues/build/a-shards-boot-width-does-not-price-its-tests.md +++ b/issues/build/a-shards-boot-width-does-not-price-its-tests.md @@ -11,7 +11,7 @@ reference and multiplies every liveness ceiling by the result; every shard print it (`host: fastest boot N ms against the reference 1320 ms — liveness ceilings paid at Wx width`). The proposal was to spend the same factor on the *duration profile*: divide each shard's measured prices by its width in -`src/durations.rs`'s merge, so `src/tiers.rs`'s ceiling compares like with like +`35383398^:src/durations.rs`'s merge, so `src/tiers.rs`'s ceiling compares like with like across shards of different speed, with timer-anchored names exempt because a fixed wait does not shrink on a fast host. @@ -132,7 +132,7 @@ renormalize. ## What is still true and is not this The two-*machine* gap — twelve hosted EPYC shards against one T14 lane, -1.35–1.37x apart on an idle host, recorded in `src/durations.rs`'s header with +1.35–1.37x apart on an idle host, recorded in `35383398^:src/durations.rs`'s header with the committed profile's `shards=` column naming which partition took each price — is untouched by any of the above: that measurement is a gap between machines, not a within-lane shard factor. This file says only that the diff --git a/issues/build/a-sigkilled-harness-leaves-its-qemu-children-running.md b/issues/build/a-sigkilled-harness-leaves-its-qemu-children-running.md index 4d218284ce8..943ff8ae8cb 100644 --- a/issues/build/a-sigkilled-harness-leaves-its-qemu-children-running.md +++ b/issues/build/a-sigkilled-harness-leaves-its-qemu-children-running.md @@ -13,7 +13,7 @@ then reclaims the killed run's directory — the very images those QEMU processes still have open — and unlinks it while the guest is still alive, holding the disk invisibly until the guest itself exits. -This is not a regression: the retired `src/scratch.rs` design (kept a killed +This is not a regression: the retired `96c2f83d^:src/scratch.rs` design (kept a killed run's directory for 24 hours) had the identical gap for a killed run's QEMU children, so #529 (which replaced that design) found it and correctly did not block on it. It is unfixed either way and worth its own entry. diff --git a/issues/build/a-std-fork-moved-for-an-unlanded-abi-is-rebuilt-against-mains-abi.md b/issues/build/a-std-fork-moved-for-an-unlanded-abi-is-rebuilt-against-mains-abi.md index a1ca23c6b53..f65f8a3f480 100644 --- a/issues/build/a-std-fork-moved-for-an-unlanded-abi-is-rebuilt-against-mains-abi.md +++ b/issues/build/a-std-fork-moved-for-an-unlanded-abi-is-rebuilt-against-mains-abi.md @@ -22,7 +22,7 @@ error: could not compile `std` (lib) due to 2 previous errors thread 'main' panicked at src/toolchain.rs:1583:5: ``` -Afterwards `rust/build/aarch64-apple-darwin/stage2/` held only `lib/`, and +Afterwards `rust/build/aarch64-apple-darwin/stage2/` held only a `lib` directory, and `rustc -vV` in `userland/` answered "'rustc' is not installed for the custom toolchain 'toyos'". The lock log shows a second process (pid 31197) holding the same step just before, so the step had already been attempted once. diff --git a/issues/build/assembly-outside-an-arch-module-in-userland-and-guest-probes.md b/issues/build/assembly-outside-an-arch-module-in-userland-and-guest-probes.md index c358cb10faa..6035bb6f8a7 100644 --- a/issues/build/assembly-outside-an-arch-module-in-userland-and-guest-probes.md +++ b/issues/build/assembly-outside-an-arch-module-in-userland-and-guest-probes.md @@ -9,7 +9,7 @@ opened: 2026-09-26 The owner's ruling of 2026-09-26 puts every `asm!`, `global_asm!`, `naked_asm!`, naked function and `core::arch::*` intrinsic inside an architecture's own module: `kernel/src/arch//`, the bootloader's -`src/arch/`, and `toyos-abi`'s per-arch syscall entry. `src/sourcegate.rs`'s +`bootloader/src/arch/`, and `toyos-abi`'s per-arch syscall entry. `src/sourcegate.rs`'s `ARCH_RULES` enforces it. The kernel and the loader now hold none outside those; what is left is declared in that table as an exception, each row pointing here: diff --git a/issues/build/blocking-read-window-completed-21-of-500-round-trips-beside-other-guests.md b/issues/build/blocking-read-window-completed-21-of-500-round-trips-beside-other-guests.md deleted file mode 100644 index 3beb62541eb..00000000000 --- a/issues/build/blocking-read-window-completed-21-of-500-round-trips-beside-other-guests.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -status: open -kind: finding -opened: 2026-09-26 ---- - -# `blocking_read_window` completed 21 of 500 round trips beside other guests - -Fast tier at `62ef89a4` (PR #525's branch, other worktrees' guests running): -`blocking_read_stress: only 21 of 500 round trips completed inside 3s — a wake -was not delivered`; the process's own line gave `syscall_wall=3087ms` and -`cpu=1703ms` for pid 7, and cpu0 took 169 xHCI interrupts in the window. The -harness's re-run alone was green. `cargo run -- --known-red` answers NO. - -In the same session the fast tier ran six times, three on the branch and three -on `main` at `d65446cc`, interleaved: this test was red once on the branch and -never on `main`, while `main`'s third run had five reds of its own that the -branch never showed. The branch's kernel differs from `main` in the claim -DMA paths, which this test does not reach, and in one boot log line. - -**Exit**: a cause — a lost wake, or a 3 s budget a starved host cannot meet — -and, if it is the budget, the bound derived rather than measured. diff --git a/issues/build/blocking-read-window-completed-26-of-500-beside-other-guests.md b/issues/build/blocking-read-window-completed-26-of-500-beside-other-guests.md deleted file mode 100644 index 45649d3551b..00000000000 --- a/issues/build/blocking-read-window-completed-26-of-500-beside-other-guests.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -status: open -kind: finding -opened: 2026-09-26 ---- - -# `blocking_read_window` completed 26 of 500 round trips beside other guests - -Fast tier at `1e5ef5c1` (PR #524's branch; the host carried the branch's own -twelve guest slots and another worktree's suite at the same time): -`blocking_read_stress: only 26 of 500 round trips completed inside 3s — a wake -was not delivered`. The harness's re-run alone was green in 2 s -(`at least 193 held windows a post landed in (0 -> 256)`). `cargo run -- ---known-red blocking_read_window` answers NO. - -What the red run's own log says against its sentence: the two processes spent -`cpu=1639ms` and `cpu=1998ms` of the 3.5 s they ran (`syscall_wall=3535ms` and -`3599ms`), and cpu0 took 522 interrupts, 456 of them xHCI — a guest that was -running and slow, not one parked on a wake that never came. So the verdict's -cause is unread: the test waits host seconds and names a lost wake when they -run out, which a starved guest satisfies as well as a lost wake does. - -Main reproduces it. A same-session A/B, runs of main (`d65446cc`) and of the -branch started together so both arms carried one load (six suites at once): -main 16 of 17 green, the branch 17 of 18, and each arm's one red is this -sentence (`only 26 of 500` on main, `only 27 of 500` on the branch; the -branch's red spent `cpu=1568ms` and `cpu=1532ms` of its window). The rate is -the same on both arms, so the branch did not move it. - -**Exit**: the verdict tells a lost wake from a slow guest (the round trips' -progress over the window, not only the count at its end), and a cause for -this run. diff --git a/issues/build/blocking-read-window-reds-beside-other-guests.md b/issues/build/blocking-read-window-reds-beside-other-guests.md new file mode 100644 index 00000000000..aea2f17d339 --- /dev/null +++ b/issues/build/blocking-read-window-reds-beside-other-guests.md @@ -0,0 +1,45 @@ +--- +status: open +kind: finding +opened: 2026-09-26 +--- + +# `blocking_read_window` reds beside other guests, naming a lost wake a slow guest also satisfies + +The verdict, in the fast tier: `blocking_read_stress: only N of 500 round trips +completed inside 3s — a wake was not delivered`. The harness's re-run alone is +green. `cargo run -- --known-red blocking_read_window` answers NO. + +Sightings, all on 2026-09-26: + +- **26 of 500** at `1e5ef5c1` (PR #524's branch; the host carried the + branch's own twelve guest slots and another worktree's suite at the same + time). The re-run alone was green in 2 s (`at least 193 held windows a post + landed in (0 -> 256)`). +- **21 of 500** at `62ef89a4` (PR #525's branch, other worktrees' guests + running). The process's own line gave `syscall_wall=3087ms` and + `cpu=1703ms` for pid 7, and cpu0 took 169 xHCI interrupts in the window. In + the same session the fast tier ran six times, three on the branch and three + on `main` at `d65446cc`, interleaved: this test was red once on the branch + and never on `main`, while `main`'s third run had five reds of its own that + the branch never showed. The branch's kernel differs from `main` in the + claim DMA paths, which this test does not reach, and in one boot log line. +- **On `main` too, at the same rate.** A same-session A/B, runs of `main` + (`d65446cc`) and of PR #524's branch started together so both arms carried + one load (six suites at once): `main` 16 of 17 green, the branch 17 of 18, + and each arm's one red is this sentence (`only 26 of 500` on `main`, `only 27 + of 500` on the branch; the branch's red spent `cpu=1568ms` and `cpu=1532ms` + of its window). The rate is the same on both arms, so no branch moved it. + +**What the red runs' own logs say against the sentence.** In the 26-of-500 +run the two processes spent `cpu=1639ms` and `cpu=1998ms` of the 3.5 s they ran +(`syscall_wall=3535ms` and `3599ms`), and cpu0 took 522 interrupts, 456 of them +xHCI — a guest that was running and slow, not one parked on a wake that never +came. So the verdict's cause is unread: the test waits host seconds and names a +lost wake when they run out, which a starved guest satisfies as well as a lost +wake does. + +**Exit**: the verdict tells a lost wake from a slow guest (the round trips' +progress over the window, not only the count at its end), and a cause for these +runs — a lost wake, or a 3 s budget a starved host cannot meet, and if it is +the budget, the bound derived rather than measured. diff --git a/issues/build/building-the-image-once-and-shipping-it-cannot-shorten-the-matrix.md b/issues/build/building-the-image-once-and-shipping-it-cannot-shorten-the-matrix.md index fe0afed5a44..789fb654677 100644 --- a/issues/build/building-the-image-once-and-shipping-it-cannot-shorten-the-matrix.md +++ b/issues/build/building-the-image-once-and-shipping-it-cannot-shorten-the-matrix.md @@ -53,7 +53,7 @@ attempts of run `31389081797`. **What is still on the floor and is not this.** The 52–59 s `deps` step is a package install repeated in every guest job on every run, and it is not a build -at all: `.github/ci-image/Dockerfile` bakes those packages into a published +at all: `35383398^:.github/ci-image/Dockerfile` bakes those packages into a published image, and the cutover retires the step once the first published digest exists for the guest workflows to pin. diff --git a/issues/build/clippy-stage-two-is-lints-one-at-a-time.md b/issues/build/clippy-stage-two-is-lints-one-at-a-time.md index 8ae61d45bab..cc889c0c357 100644 --- a/issues/build/clippy-stage-two-is-lints-one-at-a-time.md +++ b/issues/build/clippy-stage-two-is-lints-one-at-a-time.md @@ -22,9 +22,9 @@ Stage one (#132) put default clippy on every PR and it runs today: the `host` job's `clippy` step lints the host workspace (`--workspace --all-targets`), the kernel under `x86_64-unknown-none` in both feature arms, the bootloader under `x86_64-unknown-uefi` and `toyos-abi`, each with `-D warnings` and the six -adopted lints (`.github/workflows/host-tests.yml:199-220`). `userland/` is the +adopted lints (`35383398^:.github/workflows/host-tests.yml:199-220`). `userland/` is the one tree it cannot reach, and that step says why at -`.github/workflows/host-tests.yml:130-137`: `x86_64-unknown-toyos` is a custom +`35383398^:.github/workflows/host-tests.yml:130-137`: `x86_64-unknown-toyos` is a custom target and the `toyos` toolchain has no `cargo-clippy` component. This entry carries stage two: every `pedantic`/`nursery` lint measured on all three trees, adopted or rejected @@ -47,9 +47,9 @@ the group). | lint | count | why | |---|---:|---| -| `checked_conversions` | 1 | `kernel/src/arch/syscall.rs`'s device-register-write syscall bounded a `u64` against `u32::MAX` with a manual `as` comparison, then cast twice more in the body. Restructured through `u32::try_from` — one conversion, no repeated casts, at a trust-boundary site that is exactly what this bar is for. | +| `checked_conversions` | 1 | `4a98107f^:kernel/src/arch/syscall.rs`'s device-register-write syscall bounded a `u64` against `u32::MAX` with a manual `as` comparison, then cast twice more in the body. Restructured through `u32::try_from` — one conversion, no repeated casts, at a trust-boundary site that is exactly what this bar is for. | | `manual_midpoint` | 3 | `(a + b) / 2` can overflow near the integer's max; `T::midpoint` can't. Two in a `toyos-desktop` test, one in `toyos-sched/sim`'s binary-search shrinker — no realistic overflow today, but the fix is free and closes the class everywhere, forever. | -| `redundant_clone` | 6 | Real, not the false-positive-prone lint its `nursery` placement suggested — every instance checked was a clone of a binding never used again after (verified by hand, not by trusting the lint): `kernel/src/arch/syscall.rs`, a `kernel-loom` test, `toyos-cc`'s parser (a double clone — the match scrutinee was already an owned value), and three in `tests/toyos.rs`. | +| `redundant_clone` | 6 | Real, not the false-positive-prone lint its `nursery` placement suggested — every instance checked was a clone of a binding never used again after (verified by hand, not by trusting the lint): `4a98107f^:kernel/src/arch/syscall.rs`, a `kernel-loom` test, `toyos-cc`'s parser (a double clone — the match scrutinee was already an owned value), and three in `tests/toyos.rs`. | | `unchecked_time_subtraction` | 1 | `tests/toyos.rs`: a bare `Duration - Duration` that panics identically today either way — `.checked_sub().expect(msg)` says why it can't underflow instead of hiding the same panic behind an operator. No behavior change; an honesty win at zero cost. | | `unnecessary_semicolon` | 2 | Two dead `;` after `if` statements in `kernel/src/process.rs` and `kernel/src/main.rs`. Nothing to weigh. | | `default_trait_access` | 1 | `bootloader/src/main.rs`'s UEFI `open()` call passed `Default::default()` where `FileAttribute::default()` names the type. One site, one import. | @@ -147,7 +147,7 @@ before adopting: | `iommu/` | 8 | **adopted 2026-08-22**, under the reduction ruling. 8 findings (8 `unsafe` blocks in all); **5 removed, 3 documented, 0 filed** — the area's whole remainder is two window constructions and a `clflush`. | | `log/` | 2 | **adopted 2026-08-22**, under the reduction ruling. 2 findings (9 `unsafe` blocks in all, seven of them documented before this pass); **0 removed, 2 documented, 1 filed** — and the filed one was built the same day: `LogRecord` has the safe `as_bytes` its six siblings carry, so `log::user`'s hand-rolled slice is gone and the area's remainder is one block (`shard::initialize_zeroed`, irreducible). | | `object/` | 1 | **adopted** — the one site is `object::shm::Pages`'s `Send`/`Sync` pair, part of the finding filed above | -| `completion/` | 0 undocumented (3 unsafe sites, all already carrying a `SAFETY:`/`Safety:` comment predating this pass) | already documented | +| `completion` (since deleted) | 0 undocumented (3 unsafe sites, all already carrying a `SAFETY:`/`Safety:` comment predating this pass) | already documented | | **adopted** | **389** — the whole kernel (`mm` 35 + `elf` 23 + `loader` 8 + `object` 1 + root files 76 + `drivers` 121 + `arch` 107 + `sched` 8 + `iommu` 8 + `log` 2) | gated at the source, because the kernel is one crate with no `-p` scoping to hang a lint on. Every area sweep opened its entry module (`mm/mod.rs`, `object/mod.rs`, `elf/mod.rs`, `loader/mod.rs`, `drivers/mod.rs`, `arch/mod.rs`, `sched/mod.rs`, `iommu/mod.rs`, `log/mod.rs`) with `#![warn(clippy::undocumented_unsafe_blocks)]`; the root-file sweep could not — there is no entry module above them but the crate root — so it **inverted the form**: `main.rs` carries one crate-level `#![warn(...)]`, and an `#[allow(...)]` on a `mod` line is the form a tree not yet swept would take. Both compose with `host-tests.yml`'s existing `-D warnings` on the two kernel invocations, so no command line changed. The module attributes are redundant under the crate one and are left where they are — each still records its own area's status. | | **remaining** | **0** | measured 2026-08-22, after the last two sweeps (`arch`, and `sched`+`iommu`+`log`) merged, with `--force-warn clippy::undocumented_unsafe_blocks` over both kernel invocations: no finding anywhere in `kernel/src`. The `allow` list in `main.rs` is empty, which is the state this row was opened to reach; a new tree that cannot be gated the day it appears goes on that list and comes off it by the pull request that sweeps it. | diff --git a/issues/build/cpal-winit-softbuffer-prs-are-now-sendable.md b/issues/build/cpal-winit-softbuffer-prs-are-now-sendable.md index d6857ac0a4c..52138598996 100644 --- a/issues/build/cpal-winit-softbuffer-prs-are-now-sendable.md +++ b/issues/build/cpal-winit-softbuffer-prs-are-now-sendable.md @@ -5,7 +5,7 @@ opened: 2026-09-04 --- `forks.toml`'s `pr` field for `cpal`, `winit` and `softbuffer` names each -fork's precondition as "sendable once toyos-abi/toyos/toyos-window are on -crates.io". They published 2026-09-04 (run 33832842328), so the precondition +fork's precondition as sendable once `toyos-abi`, `toyos` and `toyos-window` are on +crates.io. They published 2026-09-04 (run 33832842328), so the precondition is met for all three; the sibling-tier forks (target state: disappear) are ready to open against upstream, unopened. diff --git a/issues/build/defect-events.md b/issues/build/defect-events.md index 59863eb2907..278dceceebf 100644 --- a/issues/build/defect-events.md +++ b/issues/build/defect-events.md @@ -56,8 +56,8 @@ ledger was written. (`PS2_QUEUE_SIZE`) and past it drops one byte at a time with no signal, reproduced deterministically on QEMU 11.1 by putting 22 transitions through one `input-send-event`. Closed - `issues/kernel/two-i8042-verdicts-red-together-on-one-ci-shard.md` and - `issues/build/i8042-keyboard-pays-a-lost-sentinel-and-reds-the-durations-gate.md`, + `2cbb3e0d^:issues/kernel/two-i8042-verdicts-red-together-on-one-ci-shard.md` and + `2cbb3e0d^:issues/build/i8042-keyboard-pays-a-lost-sentinel-and-reds-the-durations-gate.md`, retired two redlist rows. - **The census lag** — origin: pre-existing. discoverer: automated gate @@ -113,7 +113,7 @@ ledger was written. screendump` mode, `kernel_heartbeat`'s clean-exit-before-`===READY===` family, PR #202's direction-flag silent reset) to W^X, to the NX bit, or to the boot-time CPU-feature assertion at - `kernel/src/arch/control_regs.rs:238-242` that panics if a CPU lacks the NX + `kernel/src/arch/x86_64/control_regs.rs:238-242` that panics if a CPU lacks the NX bit W^X depends on. Whoever placed this row in the brief holds the citation this entry is missing; append it here rather than re-deriving it once found. diff --git a/issues/build/every-worktree-builds-its-own-copy-of-the-same-crates.md b/issues/build/every-worktree-builds-its-own-copy-of-the-same-crates.md index 14ff24f16ef..13fb2bca52b 100644 --- a/issues/build/every-worktree-builds-its-own-copy-of-the-same-crates.md +++ b/issues/build/every-worktree-builds-its-own-copy-of-the-same-crates.md @@ -10,7 +10,7 @@ Twenty-four linked worktrees hold twenty-four copies of one compilation. The primary checkout's `target/` is 16 GB, and cargo's share of it is 12.5 GB (`du`, 2026-08-19): `debug` 11 GB, `x86_64-unknown-toyos` 895 MB, `release` 286 MB, `aarch64-apple-darwin` 285 MB. The remaining 3.5 GB — `bootable*.img`, -`nvme.img` 1.0 GB, the staged `kernel-*`/`bootloader.efi-*` copies, `stamps/` — +`nvme.img` 1.0 GB, the staged `kernel-*`/`bootloader.efi-*` copies, `target/stamps/` — is the build system's own output and is **per-worktree by design**: `kernel_key` hashes profile and features and not content, and `buildlock::artifact` is a lock under `/.build-locks`. Only cargo's 12.5 GB is a candidate for diff --git a/issues/build/fork-branches-have-no-upstream.md b/issues/build/fork-branches-have-no-upstream.md index 6dab82f2c98..3ca0e158eeb 100644 --- a/issues/build/fork-branches-have-no-upstream.md +++ b/issues/build/fork-branches-have-no-upstream.md @@ -6,7 +6,7 @@ opened: 2026-08-07 # A `toyos` branch mostly has no upstream, so `git status` cannot say if it is pushed -13 of the 16 consumed and PR branches across `forks/` have no tracking ref: +13 of the 16 consumed and PR branches across the fork checkouts in `../forks` have no tracking ref: `git for-each-ref --format='%(refname:short)|%(upstream:short)' refs/heads` gives `NO UPSTREAM` for cpal, ctrlc, getrandom (all three), mio, raw-window-handle, socket2, softbuffer, stacker, target-lexicon, tokio and winit. Only libloading, diff --git a/issues/build/issue-files-cite-paths-that-moved.md b/issues/build/issue-files-cite-paths-that-moved.md deleted file mode 100644 index 7f8760805cf..00000000000 --- a/issues/build/issue-files-cite-paths-that-moved.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -status: open -kind: tooling -opened: 2026-09-26 ---- - -# Issue files cite source paths that no longer exist - -An issue names the site it is about by path, and a path is the claim a reader -checks first. Moving a source file leaves every issue that cites it pointing at -nothing, and nothing notices. - -Measured on PR #524's branch at `73a89365` (the port's stage 0 moved the -kernel's x86 code under `kernel/src/arch/x86_64/`, the syscall handlers out of -`arch/`, and `hw.rs`): every `kernel/`, `src/`, `userland/`, `tests/`, -`bootloader/` and `toyos-*/` path written in `issues/`, checked against that -tree and against `origin/main`: - -- 46 citations in 35 files name a path that exists on `main` and not on the - branch: the branch moved them. Among them `kernel/src/arch/syscall/*.rs`, - `kernel/src/hw.rs`, `kernel/src/mm/paging.rs`, `kernel/src/arch/apic.rs`, - `kernel/src/arch/idt/*.rs` and `kernel/src/drivers/watchdog.rs`. -- Already on `main`, before this branch: at least 14 citations in 11 files of - a whole `kernel/src/` path that does not exist (`kernel/src/arch/syscall.rs`, - `kernel/src/inbox.rs`, `kernel/src/log_file.rs`, - `kernel/src/completion/mod.rs` among them). The wider count, 153 in 95 files, - also holds crate-relative spellings (`sched/dump.rs`) that the check cannot - tell from rot. - -**Whether a gate belongs with it.** `src/CLAUDE.md` says documentation carries -no gates, and an issue is prose; a red gate over `issues/` contradicts that -rule, so it is the owner's to decide. What the tree already requires of a -deleted document (its citations go in the same merge) is the rule a move needs -too, and an on-demand check that lists every cited path that does not resolve -(offline, beside `--check-forks`) would let the mover do it. A gate is what -makes the mover's duty hold. The on-demand check only makes it cheap. - -**Exit condition**: every whole-repository path written in `issues/` resolves -in the tree that holds it, and the owner has ruled whether a move that strands -one is refused by a gate or caught on demand. diff --git a/issues/build/memmap2-fork-is-unreachable-code.md b/issues/build/memmap2-fork-is-unreachable-code.md index 032d88e14f7..2a73d14fca0 100644 --- a/issues/build/memmap2-fork-is-unreachable-code.md +++ b/issues/build/memmap2-fork-is-unreachable-code.md @@ -10,10 +10,10 @@ opened: 2026-07-30 `target_os = "toyos"` to a `Vec` implementation at all 8 sites, and `rust/Cargo.toml` is the only manifest that patches memmap2 at all — userland's duplicate entry resolved to nothing and was deleted 2026-08-01. So no ToyOS code -path calls any memmap2 API. `src/toyos.rs` is compiled and never called; the +path calls any memmap2 API. `memmap2/src/toyos.rs` is compiled and never called; the fork's only load-bearing content is the `0.9.10 → 0.2.1` version relabel that satisfies rustc's pin. -Either delete `src/toyos.rs` and let `stub.rs` serve, or drop the toyos gate in +Either delete `memmap2/src/toyos.rs` and let `stub.rs` serve, or drop the toyos gate in `rustc_data_structures` (the only two APIs rustc uses, `map_copy_read_only` and `map_anon`, are correct in the fork). Exactly one of the two should exist. Three real bugs in that module were found and fixed 2026-07-28 — see `forks.toml`. diff --git a/issues/build/mio-deregister-fd-leaves-a-pending-poll-live.md b/issues/build/mio-deregister-fd-leaves-a-pending-poll-live.md index 1951fdfe9f5..1bcf19f948e 100644 --- a/issues/build/mio-deregister-fd-leaves-a-pending-poll-live.md +++ b/issues/build/mio-deregister-fd-leaves-a-pending-poll-live.md @@ -6,7 +6,7 @@ opened: 2026-08-16 # mio's ToyOS selector deregisters a token but not the kernel's poll on it -`src/sys/toyos/selector.rs` in the mio fork (currently pinned at `e8068c2`, +`mio/src/sys/toyos/selector.rs` in the mio fork (currently pinned at `e8068c2`, `userland/Cargo.lock`) keeps its own registration list rather than asking the kernel to track interest: @@ -32,7 +32,7 @@ notification for a resource it was promised was gone. ## Why there is nothing to cancel with -There used to be an ABI op for this. `toyos-abi/src/io_uring.rs` op code 2 was +There used to be an ABI op for this. `toyos-abi/src/inbox.rs` op code 2 was `IORING_OP_POLL_REMOVE`, retired in PR #89 (`c41b831`, "abi: four names retired, and the number each one held") as caller-less. The retirement's own reasoning is recorded at the site: diff --git a/issues/build/parallel-tests-red-under-other-suites.md b/issues/build/parallel-tests-red-under-other-suites.md index 04047234b5c..7f609aac64a 100644 --- a/issues/build/parallel-tests-red-under-other-suites.md +++ b/issues/build/parallel-tests-red-under-other-suites.md @@ -521,7 +521,7 @@ mechanism for it. **It contradicts a retirement rather than joining a class.** All three of the name's earlier rows in `src/redlist.rs` are retired: the two dev-host `ALONE: GREEN` rows by the single-word tally in - `kernel/src/drivers/i8042/tally.rs` (2026-08-17), which made `N interrupts + `kernel/src/arch/x86_64/i8042/tally.rs` (2026-08-17), which made `N interrupts and 0 bytes` unprintable, and the CI row by "the verdict revises itself once" (2026-08-28) — a mute line said while a decoder still holds the run is `HEALTH_MUTE_BLIND`, the first blamed byte moves it to `HEALTH_MUTE_SAID` diff --git a/issues/build/python-and-cc-are-declared.md b/issues/build/python-and-cc-are-declared.md index 3482400abcd..a374604a592 100644 --- a/issues/build/python-and-cc-are-declared.md +++ b/issues/build/python-and-cc-are-declared.md @@ -40,7 +40,7 @@ Rust bootstrap again as an incidental fix; do not soften the entry either. `src/toolchain.rs:749` picks `./x` when `rust/x` exists, which it does. That file is a `/bin/sh` script whose whole job is `SEARCH="python3 python py python2 uv"`, -and it execs `x.py` → `src/bootstrap/bootstrap.py` (55,550 bytes). So a clean +and it execs `x.py` → `rust/src/bootstrap/bootstrap.py` (55,550 bytes). So a clean clone cannot build a toolchain without Python 3. It is upstream's bootstrap and not our code, which is why it is stated rather than blamed — but the bar has no upstream exemption, and `bootstrap.py` can never run inside ToyOS. diff --git a/issues/build/the-ack-delay-abi-doc-names-one-cpu.md b/issues/build/the-ack-delay-abi-doc-names-one-cpu.md index 2ccedc42b5f..aa9d5253f7e 100644 --- a/issues/build/the-ack-delay-abi-doc-names-one-cpu.md +++ b/issues/build/the-ack-delay-abi-doc-names-one-cpu.md @@ -11,12 +11,12 @@ opened: 2026-09-14 > Make the last CPU a shootdown waits for answer `arg` nanoseconds late, and > take it away again. -The kernel arm no longer picks a CPU by arithmetic. `kernel/src/arch/tlb.rs`'s +The kernel arm no longer picks a CPU by arithmetic. `kernel/src/arch/x86_64/tlb.rs`'s `debug_arm_ack_delay` holds each other CPU's acknowledgement back for `arg` nanoseconds in turn, takes one shootdown against each, and returns the smallest wait any of them cost the initiator; the arming is then left standing against every other CPU until a disarm or the end of a fresh `ARM_WINDOW_NANOS` -(`kernel/src/arch/tlb.rs:230`, two seconds), whichever comes first. So the one +(`kernel/src/arch/x86_64/tlb.rs:230`, two seconds), whichever comes first. So the one sentence userland reads to learn what action 12 does describes a selection the kernel does not make, omits the answer it returns, and says nothing about how long what it leaves behind lasts. diff --git a/issues/build/the-host-job-runs-the-toolchain-the-runner-ships.md b/issues/build/the-host-job-runs-the-toolchain-the-runner-ships.md index cdefcfb32c3..79f14184869 100644 --- a/issues/build/the-host-job-runs-the-toolchain-the-runner-ships.md +++ b/issues/build/the-host-job-runs-the-toolchain-the-runner-ships.md @@ -6,7 +6,7 @@ opened: 2026-09-03 # The host job tracks whatever toolchain `macos-latest` ships, and a runner roll reds every open pull request at once -`.github/workflows/host-tests.yml`'s `host` job installs no Rust toolchain: it +`35383398^:.github/workflows/host-tests.yml`'s `host` job installs no Rust toolchain: it runs `rustc -vV; cargo -V; rustup component add clippy` on whatever `macos-latest` ships that day, and there is no root `rust-toolchain.toml` — only `kernel/`, `bootloader/` and `userland/` pin one, each to a target list @@ -56,7 +56,7 @@ own `set -e` never reached before the script died on the first red pipeline — site under the kernel's own two clippy invocations (`kernel/src/loader/start.rs:146`), plus one unrelated new lint the kernel arm alone surfaced, `clippy::map_or_identity` -(`kernel/src/arch/syscall/dispatch.rs:278`). All were confirmed clean under +(`kernel/src/syscall/dispatch.rs:278`). All were confirmed clean under `RUSTUP_TOOLCHAIN=1.98.0 cargo run -- --clippy` and unchanged under the default 1.97.1 after the fix. diff --git a/issues/build/the-primary-rebuilds-its-compiler-on-compiler-alone.md b/issues/build/the-primary-rebuilds-its-compiler-on-compiler-alone.md index 39616126a87..fa20c6f6990 100644 --- a/issues/build/the-primary-rebuilds-its-compiler-on-compiler-alone.md +++ b/issues/build/the-primary-rebuilds-its-compiler-on-compiler-alone.md @@ -4,13 +4,13 @@ kind: tooling opened: 2026-09-26 --- -# The primary rebuilds its compiler on `compiler/` alone +# The primary rebuilds its compiler on `rust/compiler/` alone `src/toolchain.rs` rebuilds the primary's toolchain when the stamp over `rust/compiler/` changes, and `compiler::record` writes that tree as the compiler the primary's `stage2` is. A fork commit that moves only -`src/bootstrap`, `src/tools`, `src/stage0`, `Cargo.lock` or the LLVM submodule -leaves the primary on the compiler it had, and a worktree whose `compiler/` +`rust/src/bootstrap`, `rust/src/tools`, `rust/src/stage0`, `rust/Cargo.lock` or the LLVM submodule +leaves the primary on the compiler it had, and a worktree whose `rust/compiler/` matches the record is handed that compiler too. A worktree's own compiler is keyed on all of them (`compiler::key`, PR #524), so the two answers to "which compiler do these sources name" differ. @@ -21,4 +21,4 @@ lands. **Exit condition**: the primary's rebuild and its record read the same sources `compiler::key` does, and a worktree compares against that, shown by a test -in which a fork moving only `src/tools` gets a compiler of its own. +in which a fork moving only `rust/src/tools` gets a compiler of its own. diff --git a/issues/build/the-shard-profile-has-no-refresh-path.md b/issues/build/the-shard-profile-has-no-refresh-path.md index 39d7df8027e..62bbe379ac0 100644 --- a/issues/build/the-shard-profile-has-no-refresh-path.md +++ b/issues/build/the-shard-profile-has-no-refresh-path.md @@ -9,7 +9,7 @@ opened: 2026-09-24 `tests/test-durations` is what every nightly shard prices its partition against (`tests/toyos.rs`'s `shard_pricing`), and nothing writes it any more: the shards no longer upload their measurements and `--merge-durations` is -gone with `src/durations.rs`. A test added after the file's last refresh is +gone with `35383398^:src/durations.rs`. A test added after the file's last refresh is priced at the harness's default, and one whose cost moved keeps its old price. What that costs is balance, never a verdict: every name still lands in exactly diff --git a/issues/build/the-swarm-is-not-yet-falsifiable.md b/issues/build/the-swarm-is-not-yet-falsifiable.md index 6b8ef872f03..879a39d5871 100644 --- a/issues/build/the-swarm-is-not-yet-falsifiable.md +++ b/issues/build/the-swarm-is-not-yet-falsifiable.md @@ -213,14 +213,14 @@ issues/hardware/the-bot-scsi-machine-is-still-hand-written-in-the-kernel.md issues/hardware/the-t14-touchpad-is-i2c-hid-and-unbuilt.md issues/hardware/there-is-no-wifi.md issues/isolation/the-power-broker-authority-with-a-human-in-the-loop.md -issues/kernel/arm64-is-a-decision-nobody-has-made.md +8a277bb2^:issues/kernel/arm64-is-a-decision-nobody-has-made.md issues/kernel/cpu-time-is-a-band-and-not-a-reservation.md issues/kernel/every-driver-is-still-in-the-kernel.md issues/kernel/every-interrupt-lands-on-the-boot-cpu.md issues/kernel/logging-records-from-every-producer-and-a-kernel-that-waits-on-nobody.md issues/kernel/nothing-charges-kernel-memory-to-a-process.md issues/kernel/page-global-is-a-decision-nobody-has-made.md -issues/kernel/scheduler-policy-behavior-has-no-quantified-suite.md +b68e2328^:issues/kernel/scheduler-policy-behavior-has-no-quantified-suite.md issues/kernel/the-capability-end-state-is-twelve-answers.md issues/kernel/the-iommu-refuses-nothing-yet.md issues/kernel/the-kernel-still-parses-what-userland-writes.md diff --git a/issues/build/the-third-party-corpus-is-in-no-machine-read-ledger.md b/issues/build/the-third-party-corpus-is-in-no-machine-read-ledger.md index c529d98a565..9e9dd4ee00f 100644 --- a/issues/build/the-third-party-corpus-is-in-no-machine-read-ledger.md +++ b/issues/build/the-third-party-corpus-is-in-no-machine-read-ledger.md @@ -13,7 +13,7 @@ tracks **plus every third-party source corpus**"*. The corpus is `tests/testcases/` --- 365 tracked files, of which **363** are third-party across `tinycc/` and `pp_tcc/` (the other two are its own `LICENSE` -and a `system.toml` that is ours): TinyCC's `tests/tests2` and `tests/pp` under +and a `system.toml` that is ours): TinyCC's `tinycc/tests/tests2` and `tinycc/tests/pp` under LGPL-2.1 plus picoc under BSD-3-Clause. They are compiler *input* rather than linked code, so their terms do not reach this repository's own, but the attribution is still owed and @@ -25,7 +25,7 @@ what is whose *"with the counts it was established from"*. reads that licence's own per-population numbers, counts what `git` tracks under each population, and reds when they disagree; it also refuses a tracked file under the corpus that no population attributes, and the one name `NOTICE` says -is not to come back --- `tests/testcases/tinycc/46_grep.c`, "Copyright (C) 1980, +is not to come back --- `b2771acc^:tests/testcases/tinycc/46_grep.c`, "Copyright (C) 1980, DECUS", *"but not for profit"*, deleted rather than attributed on 2026-08-08. An arrival and a deletion are both caught. diff --git a/issues/build/the-tooling-is-a-review-prompt-and-three-workflows.md b/issues/build/the-tooling-is-a-review-prompt-and-three-workflows.md index 3072f645938..c07376ec4ec 100644 --- a/issues/build/the-tooling-is-a-review-prompt-and-three-workflows.md +++ b/issues/build/the-tooling-is-a-review-prompt-and-three-workflows.md @@ -10,11 +10,11 @@ The rules a prompt can read off a branch move into `.claude/agents/reviewer.md`, and the gates that held them go. - A test is green and fast or it is deleted in the same pull request and filed; - then `src/redlist.rs`, `src/tiers.rs`, `src/durations.rs` and + then `src/redlist.rs`, `src/tiers.rs`, `35383398^:src/durations.rs` and `tests/test-durations` have no subject and go. - The toolchain is content-addressed by the four trees that produce it, one directory per hash, never mutated; then the sysroot claim, `src/buildlock.rs` and `src/worktree.rs` go. - The nine workflows become three — `pr`, `nightly`, `publish`; then - `src/mergehealth.rs` and `gate-stage` go, and the ABI-lands-alone rule moves - into the review prompt. + `a5b25a75^:src/mergehealth.rs` and `gate-stage` go, and the ABI-lands-alone + rule moves into the review prompt. diff --git a/issues/design-debt/mio-s-toyos-waker-never-drains-its-pipe.md b/issues/design-debt/mio-s-toyos-waker-never-drains-its-pipe.md index 947d698dd02..5faf47fdea6 100644 --- a/issues/design-debt/mio-s-toyos-waker-never-drains-its-pipe.md +++ b/issues/design-debt/mio-s-toyos-waker-never-drains-its-pipe.md @@ -6,10 +6,10 @@ opened: 2026-09-26 # mio's ToyOS waker never drains its pipe -`src/sys/toyos/waker.rs` on the mio fork (`ToyOSOrg/mio`, branch `toyos`) +`mio/src/sys/toyos/waker.rs` on the mio fork (`ToyOSOrg/mio`, branch `toyos`) writes a byte to a pipe per `wake()` and ignores a full pipe, and the selector registers the read end under the waker's token; nothing in -`src/sys/toyos/` ever reads that pipe. `toyos::wake` is the wake pipe the +`mio/src/sys/toyos/` ever reads that pipe. `toyos::wake` is the wake pipe the tree now has once (a `Bell` whose `take` empties it), and logd, soundd and `window::Waiter` use it; mio could not take it as a swap, because draining is the selector's to do on the waker's token and the selector has no such step. diff --git a/issues/design-debt/netd-spends-two-poller-slots-per-piped-connection.md b/issues/design-debt/netd-spends-two-poller-slots-per-piped-connection.md index 0d4736d64e5..23300c420f2 100644 --- a/issues/design-debt/netd-spends-two-poller-slots-per-piped-connection.md +++ b/issues/design-debt/netd-spends-two-poller-slots-per-piped-connection.md @@ -21,5 +21,5 @@ or more is held to 111. Exit condition: one registration per connection. netd's side of a connection is one kernel `Connection` object, which `read_source` and `write_source` both resolve (`kernel/src/object/ops.rs`), so one `OP_WATCH` -carries `READABLE | WRITABLE` (`kernel/src/inbox.rs`, `process_watch`), and +carries `READABLE | WRITABLE` (`kernel/src/inbox/mod.rs`, `process_watch`), and `POLL_HANDLES_PER_PIPED` is deleted. diff --git a/issues/design-debt/redesign-the-log-subsystem.md b/issues/design-debt/redesign-the-log-subsystem.md index 630050398ca..bc4264bb0b7 100644 --- a/issues/design-debt/redesign-the-log-subsystem.md +++ b/issues/design-debt/redesign-the-log-subsystem.md @@ -2,7 +2,6 @@ status: open kind: track opened: 2026-08-08 -decided: 2026-08-19 --- # Redesign the log subsystem, and re-shape `kernel/src` @@ -22,7 +21,7 @@ work in a scheduler-adjacent path, and fails alone. The original question, recorded verbatim because it was the owner asking: *"should we redesign and rewrite the log subsystem and rethink if the current -file/folder structure of the kernel makes sense?"* (`kernel/src/log.rs`). What +file/folder structure of the kernel makes sense?"* (`c31e9f97^:kernel/src/log.rs`). What follows is the evidence that made it decidable. **The log subsystem, as it was when this was written.** Six places, no core: @@ -46,8 +45,8 @@ writers and readers never observe a torn record" (`kernel/src/log/shard.rs:1`). The record type is not the kernel's at all — `LogRecord` comes from `toyos_abi::log`, imported at `kernel/src/log/mod.rs:19` and filled at `:158`. The three files the table -calls the log's own are gone: `kernel/src/log.rs`, -`kernel/src/drivers/log_ring.rs` and `kernel/src/log_file.rs` are none of them +calls the log's own are gone: `c31e9f97^:kernel/src/log.rs`, +`ee8369c9^:kernel/src/drivers/log_ring.rs` and `9ca7631a^:kernel/src/log_file.rs` are none of them in the tree. The file sink is not a kernel module at all — `/system/bin/logd` is an ordinary user process, and "the kernel keeps the record ring and the console; every policy about files — where they go, what they are called, how many there @@ -80,16 +79,16 @@ carrying explicit backpressure — a slow sink drops-and-counts, never blocks, does no unbounded work in a scheduler-adjacent path, and fails alone. **The layout half, re-measured.** `kernel/src` is **50 flat `.rs` files** -(`ls kernel/src/*.rs | wc -l`) beside ten directories — `arch/`, `completion/`, -`drivers/`, `elf/`, `iommu/`, `loader/`, `log/`, `mm/`, `object/`, `sched/`. +(`ls kernel/src/*.rs | wc -l`) beside ten directories — `arch`, `completion`, +`drivers`, `elf`, `iommu`, `loader`, `log`, `mm`, `object`, `sched`. The 39-beside-seven figure this entry opened with is three directories and eleven files out of date; `log/` is one of the six the review named as the target and it exists. `elf.rs` and `loader.rs` became directories in `42b29c9`, which is the precedent. The flat set mixes a filesystem adapter, an IPC primitive, two input devices, a page cache, io_uring, the process table and two cfg-gated test actuators at one -level. The review's target was subsystem directories (fs/, ipc/, input/, -proc/, log/, time/), and the `syscall.rs` split already forces at least one. +level. The review's target was subsystem directories (fs, ipc, input, +proc, log, time), and the `syscall.rs` split already forces at least one. Cost, so the question is priced: a directory move is `git mv` plus `mod` lines, it touches no logic, and it collides with every worktree in flight — which is @@ -100,7 +99,7 @@ Two smaller layout items ride the same answer. `usb_gate.rs` (242 lines) and call (`kernel/src/main.rs:34`, `:36`, `:408`, `:537`) and are never in an ordinary build, but they sit interleaved with production sources; the review's target is one -`kernel/src/gates/` directory so that what test machinery exists is auditable +`gates` directory under `kernel/src/` so that what test machinery exists is auditable in one listing. And `input_merge_test` is the tell that pure logic is trapped in the kernel: the merge state machine (one held-set, one button-merge, both bounded) is host-testable with synthetic multi-source streams, after which the diff --git a/issues/design-debt/softbuffer-reads-a-toyos-window-behind-its-lock.md b/issues/design-debt/softbuffer-reads-a-toyos-window-behind-its-lock.md index 360185bc107..c6101db10c0 100644 --- a/issues/design-debt/softbuffer-reads-a-toyos-window-behind-its-lock.md +++ b/issues/design-debt/softbuffer-reads-a-toyos-window-behind-its-lock.md @@ -10,7 +10,7 @@ The winit backend keeps each `window::Window` in an `Arc>`, and the event loop takes the lock to read events: `poll_event(&mut self)` replaces the window's shared buffer on a resize. The raw window handle winit hands out is the address of that `Window`, and softbuffer's ToyOS backend -(`src/backends/toyos.rs` on `ToyOSOrg/softbuffer`) dereferences it without the +(`softbuffer/src/backends/toyos.rs` on `ToyOSOrg/softbuffer`) dereferences it without the lock to blit and present. A surface presented from a thread other than the event loop's therefore races the resize: a present into the buffer the loop just dropped. The toolkits in the tree present from the loop's own thread, so diff --git a/issues/design-debt/toyos-dns-decodes-what-a-published-crate-decodes.md b/issues/design-debt/toyos-dns-decodes-what-a-published-crate-decodes.md index 822cbb1f6a0..98f90fd5258 100644 --- a/issues/design-debt/toyos-dns-decodes-what-a-published-crate-decodes.md +++ b/issues/design-debt/toyos-dns-decodes-what-a-published-crate-decodes.md @@ -1,6 +1,6 @@ --- status: open -kind: design-debt +kind: defect opened: 2026-09-26 --- diff --git a/issues/design-debt/what-is-owed-on-file-size.md b/issues/design-debt/what-is-owed-on-file-size.md index 0398fe22577..306c88deed9 100644 --- a/issues/design-debt/what-is-owed-on-file-size.md +++ b/issues/design-debt/what-is-owed-on-file-size.md @@ -38,7 +38,7 @@ what survives it is a different question. **Answered 2026-08-24, in the review-completion wave:** -- `arch/syscall.rs` → `kernel/src/arch/syscall/` (12 files; `dispatch.rs` is +- `arch/syscall.rs` → `kernel/src/syscall/` (12 files; `dispatch.rs` is where every user pointer the ABI takes is decoded — the seam the note asked for). A move-proof regenerated every new file from the original's line ranges; no function body changed. The split made two facts visible and filed: diff --git a/issues/diagnostics/a-record-cannot-name-thread-zero.md b/issues/diagnostics/a-record-cannot-name-thread-zero.md index 36c1aea2835..3003c5330b0 100644 --- a/issues/diagnostics/a-record-cannot-name-thread-zero.md +++ b/issues/diagnostics/a-record-cannot-name-thread-zero.md @@ -23,7 +23,7 @@ every `tid=` in the T14 boot logs then committed: **738 `tid=0` against 49 `tid=1`** — the value the formatter drops is the one almost every line carries. The kernel's own sentinel is a third value again: `PerCpu::current_tid` is -`u32::MAX` when no thread is running (`kernel/src/arch/percpu.rs:85`), which the +`u32::MAX` when no thread is running (`kernel/src/arch/x86_64/percpu.rs:85`), which the formatter would render as `tid=4294967295` on every line a kernel thread logs. ## What is in the tree now diff --git a/issues/diagnostics/the-lanleasecase-boot-is-a-third-t14-flash-for-one-exit-code.md b/issues/diagnostics/the-lanleasecase-boot-is-a-third-t14-flash-for-one-exit-code.md index 59b5f0950e0..2b1db4a1aa5 100644 --- a/issues/diagnostics/the-lanleasecase-boot-is-a-third-t14-flash-for-one-exit-code.md +++ b/issues/diagnostics/the-lanleasecase-boot-is-a-third-t14-flash-for-one-exit-code.md @@ -37,7 +37,7 @@ the `lan_lease_report` metal row in `tests/toyos.rs` with `LANLEASECASE` and and netd's `--exit-with-lease` with `tests/e1000leasecase` and the `lan_lease_report` QEMU registration, the arm that proves the channel. -An earlier form of this arm, `--exit-with-phy-outcome` on `tests/lanphycase`, +An earlier form of this arm, `--exit-with-phy-outcome` on `d409139f^:tests/lanphycase`, ended right after the bring-up with the PHY's outcome; its codes are still `Verdict::NotLeased`'s, so a code read off one of its boots means what `toyos_i219::phy::Outcome` says. diff --git a/issues/filesystem/log-flush-retry-deadman-arm.md b/issues/filesystem/log-flush-retry-deadman-arm.md index 945fb975ca7..6878799120f 100644 --- a/issues/filesystem/log-flush-retry-deadman-arm.md +++ b/issues/filesystem/log-flush-retry-deadman-arm.md @@ -37,6 +37,39 @@ The first boot of the same test passes, so the retry half is sound; what is unresolved is whether the second boot's refusal is the deadman it is supposed to stage or a different error arriving first, and the console does not say which. -**Exit condition.** The deadman boot produces the record the test reads, or the -test reads the record that boot actually produces — decided by which error -`SYS_FSYNC` returned, which is a value nothing on that console prints today. +## Two older ways it reddened, not re-seen since ROOT-in-memory + +`d5c2d9c9^:issues/boot-media/log-flush-retry-reds-two-ways-at-two-in-five.md` +recorded four ways `log_flush_retry` had been seen to red; `d5c2d9c9` deleted +it once ROOT-in-memory gave the `[hung]` arm's own mechanism — a stick going +offline while userland is still paged from it — a fix and six green runs. Two +of its other ways were not re-seen in those six runs, so neither is known fixed +by that work: + +- **The `[hung]` arm missing its "transport broke on SCSI" line.** One of that + file's two originally-recorded assertions: the wide run reds on + `the deadman never declared the volume failed` while the *alone* re-run of + the same boot reds instead on + `no "transport broke on SCSI" in the log, so the staged hung device never + met its recovery` — two different assertions from the same staged fault, + which was that file's reason for treating this as more than a scheduling + classification. +- **A boot timeout before `===READY===`.** Measured 2026-09-07 on three + separate points (`main` 71ed50cf, `metal-suite` 7f16914d and f63bce1d), each + run alone: `log_flush_retry: [qemu] Boot timed out waiting for + ===READY===`, with the boot never coming up at all — a third shape beside + the two assertion failures, seen before ROOT-in-memory and on a branch that + does not carry it. + +## Exit condition + +The deadman boot produces the record the test reads, or the test reads the +record that boot actually produces — decided by which error `SYS_FSYNC` +returned, which is a value nothing on that console prints today. + +And `log_flush_retry` is re-measured (wide and alone) enough times, on a tree +that carries ROOT-in-memory, to say whether either older mode still occurs; if +seen again, it is disabled with a `src/redlist.rs` row and this file, or it is +fixed at its owner (`kernel/src/drivers/xhci` for the transport line, the boot +harness for the timeout). If not seen in that many runs, that half closes with +the count that supports it. diff --git a/issues/hardware/anonymous-mmap-is-not-demand-paged.md b/issues/hardware/anonymous-mmap-is-not-demand-paged.md index cc54919521e..91029c7e12f 100644 --- a/issues/hardware/anonymous-mmap-is-not-demand-paged.md +++ b/issues/hardware/anonymous-mmap-is-not-demand-paged.md @@ -13,7 +13,7 @@ blast radius. **`sys_mmap` allocates and maps the whole region up front.** `PageAlloc::new` is called for the full rounded size before anything is mapped, and -`alloc_and_map` maps every 2 MiB page of it (`kernel/src/arch/syscall.rs`'s +`alloc_and_map` maps every 2 MiB page of it (`kernel/src/syscall/vm.rs`'s `sys_mmap`). So a first touch of a fresh anonymous mapping is an ordinary store and never a `#PF`, and a program that reserves a large region pays for all of it immediately. Measured: `syscall_cost` mapping 128 MiB and touching one byte per diff --git a/issues/hardware/tco2-sts-clearing-is-verified-on-qemu-only.md b/issues/hardware/tco2-sts-clearing-is-verified-on-qemu-only.md index bcda4615315..0934fc8a468 100644 --- a/issues/hardware/tco2-sts-clearing-is-verified-on-qemu-only.md +++ b/issues/hardware/tco2-sts-clearing-is-verified-on-qemu-only.md @@ -6,7 +6,7 @@ opened: 2026-09-06 # Whether writing `TCO2_STS` back clears it is verified on QEMU only -`kernel/src/drivers/watchdog.rs`'s `arm` writes `TCO_SECOND_TO_STS` and +`kernel/src/arch/x86_64/watchdog.rs`'s `arm` writes `TCO_SECOND_TO_STS` and `TCO_BOOT_STS` back so that a reset is reported by the boot after it and not by every boot after it. That the write clears them is established for QEMU, whose store masks both bits out (`hw/acpi/ich9_tco.c:167`). Whether a Tiger Lake-LP diff --git a/issues/isolation/a-provided-name-cannot-reach-an-undeclared-child.md b/issues/isolation/a-provided-name-cannot-reach-an-undeclared-child.md index 077028802ca..4d020edc898 100644 --- a/issues/isolation/a-provided-name-cannot-reach-an-undeclared-child.md +++ b/issues/isolation/a-provided-name-cannot-reach-an-undeclared-child.md @@ -17,7 +17,7 @@ The **direct** path still cannot, and the reason is no longer the ABI. `NAMESPACE_KEEP_ALL` (`toyos-abi/src/syscall.rs`), `Builder::keep_all` is its SDK spelling (`toyos/src/namespace.rs`), `sys_namespace_build` carries the base's whole entry set when the bit is set and refuses a bit it does not define -(`kernel/src/arch/syscall/ipc.rs`), and `endowment_denied`'s +(`kernel/src/syscall/ipc.rs`), and `endowment_denied`'s `the_base_plus_one_more_name` asserts both halves in a guest. **What is left is the std fork, and only the std lane can do it.** diff --git a/issues/isolation/a-reset-stops-xhci-and-leaves-every-claimed-pci-function-armed.md b/issues/isolation/a-reset-stops-xhci-and-leaves-every-claimed-pci-function-armed.md index 6e3d6fae364..c21224f5bfd 100644 --- a/issues/isolation/a-reset-stops-xhci-and-leaves-every-claimed-pci-function-armed.md +++ b/issues/isolation/a-reset-stops-xhci-and-leaves-every-claimed-pci-function-armed.md @@ -11,7 +11,7 @@ opened: 2026-09-16 register that ends the machine, and each stops every xHCI controller ([`stop::before_reset`]) before it does". Five paths reach those two: -- `sys_reboot` (`kernel/src/arch/syscall/machine.rs:113`) → `quiesce` (`:121`) +- `sys_reboot` (`kernel/src/syscall/machine.rs:113`) → `quiesce` (`:121`) → `acpi::reboot` (`:122`; `acpi.rs:283`) → `reset_now` (`acpi.rs:290`); - `sys_shutdown` (`machine.rs:103`) → `quiesce` (`:107`) → `acpi::shutdown` (`:108`; `acpi.rs:325`); diff --git a/issues/isolation/dtv-capacity-is-a-workload-bound.md b/issues/isolation/dtv-capacity-is-a-workload-bound.md index 22934e9fa4e..e6faf69bcec 100644 --- a/issues/isolation/dtv-capacity-is-a-workload-bound.md +++ b/issues/isolation/dtv-capacity-is-a-workload-bound.md @@ -46,6 +46,6 @@ argues it: a fixed bound over a quantity the workload sets is a defect rather than a policy by this tree's own rule, and this one's refusal has no recoverable form — the 65th TLS-carrying module is an `rtabort!` at an arbitrary point, not an error a `dlopen` caller can handle. The syscall split moved the refusal: it -is `kernel/src/arch/syscall/vm.rs`'s `tls_alloc_block`, not `arch/syscall.rs:2724`. +is `kernel/src/syscall/vm.rs`'s `tls_alloc_block`, not `arch/syscall.rs:2724`. Owed by the Ring-3 loader move, which is where the DTV stops being the kernel's to size. diff --git a/issues/kernel/a-120000-ms-boot-deadline-fired-132859-ms-late-on-the-t14.md b/issues/kernel/a-120000-ms-boot-deadline-fired-132859-ms-late-on-the-t14.md index 61a14ade298..0516d530fa0 100644 --- a/issues/kernel/a-120000-ms-boot-deadline-fired-132859-ms-late-on-the-t14.md +++ b/issues/kernel/a-120000-ms-boot-deadline-fired-132859-ms-late-on-the-t14.md @@ -9,7 +9,7 @@ opened: 2026-09-16 `kernel/src/deadline.rs` reads the bound off the parameter line (`claim`, `:150-162`), turns it into a TSC deadline once there is a clock (`start`, `:168-183`), and `poll` (`:194-200`) compares one relaxed load of `AT_TSC` -against `rdtsc` from the timer interrupt entry — `kernel/src/arch/idt/timer.rs:71` +against `rdtsc` from the timer interrupt entry — `kernel/src/arch/x86_64/idt/timer.rs:71` (`:79`) in Ring 0 and `:96` in Ring 3 — and calls `expire` (`:208-225`), which seals the record and writes the reset register through `acpi::reset_now`. `start` arms the other half of the same parameter at `:182`, diff --git a/issues/kernel/a-claim-spends-device-addresses-its-slot-never-gets-back.md b/issues/kernel/a-claim-spends-device-addresses-its-slot-never-gets-back.md index f606499c432..ddeed4e7fd8 100644 --- a/issues/kernel/a-claim-spends-device-addresses-its-slot-never-gets-back.md +++ b/issues/kernel/a-claim-spends-device-addresses-its-slot-never-gets-back.md @@ -8,7 +8,7 @@ opened: 2026-09-26 Every `SYS_DEVICE_DMA_ALLOC` grant of a claim is placed at a fresh address of its slot's domain (`kernel/src/pcidev/mod.rs`, `dma_alloc`), and the domain -never hands an address out twice (`kernel/src/iommu/vtd/table.rs`, +never hands an address out twice (`kernel/src/arch/x86_64/vtd/table.rs`, `Domain::reserve`). A holder bounded to `MAX_GRANT_TOTAL` per claim can still claim, allocate and die over and over — a service a supervisor restarts does exactly this — and each claim spends up to that much of the slot's addresses @@ -16,7 +16,7 @@ and the remapping tables under them, which are never freed. The domain running dry is a refusal (`ResourceExhausted`) and not a crash, but the slot is then dead for the rest of the boot, and the tables are memory nothing returns. Nor is running dry always a refusal: every table under a fresh address comes -from `Tables::alloc` (`kernel/src/iommu/vtd/table.rs`), whose +from `Tables::alloc` (`kernel/src/arch/x86_64/vtd/table.rs`), whose `expect("no physical memory for a remapping table")` panics the kernel when physical memory runs out first, and no second-level table a claim made is ever freed. diff --git a/issues/kernel/a-disk-operation-can-spin-past-the-tlb-ack-tripwire-before-its-break.md b/issues/kernel/a-disk-operation-can-spin-past-the-tlb-ack-tripwire-before-its-break.md index 0330efdf0df..ee0f09fa33f 100644 --- a/issues/kernel/a-disk-operation-can-spin-past-the-tlb-ack-tripwire-before-its-break.md +++ b/issues/kernel/a-disk-operation-can-spin-past-the-tlb-ack-tripwire-before-its-break.md @@ -6,7 +6,7 @@ opened: 2026-09-22 # A disk operation can spin past the TLB-ack tripwire before its break -`arch::tlb::ACK_TIMEOUT` (5 s) is held above xHCI's `CALL_AFTER_BREAK` +`time::DEAF_CPU` (5 s), the TLB-ack tripwire, is held above xHCI's `CALL_AFTER_BREAK` (4.75 s), "the longest a disk call spins with `IF` clear once its transport has broken". But the call's bound is measured from the wait that broke, and a USB disk operation holds the controller lock — `IF` clear — from its first command: @@ -19,7 +19,7 @@ constants; no boot has been seen to do it. ## Exit condition -The whole of one operation's `IF`-clear spin is under `ACK_TIMEOUT` by +The whole of one operation's `IF`-clear spin is under `DEAF_CPU` by construction — the call bound opens where the operation does, or a later batch starts only with a whole call's bound still inside it — and a staged boot in which the first batch spends most of the budget and the next one breaks shows diff --git a/issues/kernel/a-domains-addresses-are-never-given-back.md b/issues/kernel/a-domains-addresses-are-never-given-back.md index 6deb3a22547..009a8132a15 100644 --- a/issues/kernel/a-domains-addresses-are-never-given-back.md +++ b/issues/kernel/a-domains-addresses-are-never-given-back.md @@ -6,7 +6,7 @@ opened: 2026-09-03 # A domain's addresses are never given back -`Domain::reserve` (`kernel/src/iommu/vtd/table.rs:213-224`) is a monotonic +`Domain::reserve` (`kernel/src/arch/x86_64/vtd/table.rs:213-224`) is a monotonic bump: an address is never handed out twice, unmapped or not, and `unmap` returns no range. A driver that maps and unmaps repeatedly therefore consumes its domain's device address space for good. The display is the first such diff --git a/issues/kernel/a-double-fault-on-cpu-1-under-a-wide-suite.md b/issues/kernel/a-double-fault-on-cpu-1-under-a-wide-suite.md index 45940b00bc5..4c4522cc9a8 100644 --- a/issues/kernel/a-double-fault-on-cpu-1-under-a-wide-suite.md +++ b/issues/kernel/a-double-fault-on-cpu-1-under-a-wide-suite.md @@ -117,7 +117,7 @@ Pids come from `IdMap`, which starts at zero and never reuses; pid 1 is the **That pid excludes the idle stack, which is the only stack in this kernel whose overflow the CPU can turn into a `#DF`.** `percpu::current_pid`/`current_tid` are written on every context switch from the incoming task's id -(`kernel/src/hw.rs`), and the idle context's id is `None` +(`kernel/src/arch/x86_64/hw.rs`), and the idle context's id is `None` (`sched/driver::enter_idle_loop` sets both to `None` before it moves `rsp`). A live pid therefore means CPU 1 was running a task. The idle stack is the one with an unmapped page under it (`IDLE_GUARD_SIZE`, `guard_kernel_page`), and an diff --git a/issues/kernel/a-driver-is-tested-on-the-host-and-its-real-implementation-is-one-instruction-deep.md b/issues/kernel/a-driver-is-tested-on-the-host-and-its-real-implementation-is-one-instruction-deep.md index 1adf6297ea9..15850d30006 100644 --- a/issues/kernel/a-driver-is-tested-on-the-host-and-its-real-implementation-is-one-instruction-deep.md +++ b/issues/kernel/a-driver-is-tested-on-the-host-and-its-real-implementation-is-one-instruction-deep.md @@ -11,7 +11,7 @@ boot, the suite's flakiness is the host's load, and a stub of the hardware's own rules would have caught this month's hardware bugs before a machine did: a wait that checked its deadline only on an empty ring, a port acknowledgement that disables the port, a controller reset that is not what a device sees -(`issues/kernel/a-usb-wait-checks-its-deadline-only-on-an-empty-ring.md` and +(`46f4c14d^:issues/kernel/a-usb-wait-checks-its-deadline-only-on-an-empty-ring.md` and the reset ruling in `kernel/src/drivers/acpi.rs`). Owner direction, 2026-09-07: the logic inside every driver is tested on the host against stubs that implement the hardware's behavior, errors and unpredictability; QEMU keeps a diff --git a/issues/kernel/a-job-list-hangs-with-interrupts-on-and-the-deadline-ends-it.md b/issues/kernel/a-job-list-hangs-with-interrupts-on-and-the-deadline-ends-it.md index 8831cb4027f..00d6871659a 100644 --- a/issues/kernel/a-job-list-hangs-with-interrupts-on-and-the-deadline-ends-it.md +++ b/issues/kernel/a-job-list-hangs-with-interrupts-on-and-the-deadline-ends-it.md @@ -14,7 +14,7 @@ tip — came back after **187 s**, so its boot spent the whole **That narrows it past the hang this branch already fixed.** A deadline that fires is a machine where some CPU was still taking a timer interrupt, so this is not the all-CPUs-deaf shape of -`kernel/src/hw.rs`'s missing re-arm; and `crate::hardlockup` sealed nothing, so +`kernel/src/arch/x86_64/hw.rs`'s missing re-arm; and `crate::hardlockup` sealed nothing, so no CPU sat with `IF` clear for half the bound either. What is left is a CPU spinning with interrupts enabled, or a wait that never returns, while the timer goes on ticking — a lock nobody releases, a retry loop with no end, or a device @@ -22,7 +22,7 @@ wait that re-arms its own bound. It is intermittent **across job lists**: `ccorpus` had passed, `testcases-mkdir` hung in run 22, `metaldevicecase` in run 20 showed the same span of -`LOCK CONTENTION ... at src/vfs.rs:32` under stick writes. +`LOCK CONTENTION` at `kernel/src/vfs.rs:32` under stick writes. **The record exists and could not be read.** The deadline seals `EXPIRED` and the tail of the log ring into the black box, and the next loader pass prints it diff --git a/issues/kernel/a-page-in-the-device-refused-is-reported-as-a-segfault.md b/issues/kernel/a-page-in-the-device-refused-is-reported-as-a-segfault.md index 7b844ec4079..06060996819 100644 --- a/issues/kernel/a-page-in-the-device-refused-is-reported-as-a-segfault.md +++ b/issues/kernel/a-page-in-the-device-refused-is-reported-as-a-segfault.md @@ -11,7 +11,7 @@ one line and leaves the fault unhandled (`kernel/src/process.rs`, the `FileBacked` arm of the demand-page fill: "`` is backed by a file byte `` that the device would not read; leaving the fault unhandled"). The exception path then reports the program's own fault -(`kernel/src/arch/idt/exceptions.rs`, the `theirs` arm): `SEGFAULT tid=…: +(`kernel/src/arch/x86_64/idt/exceptions.rs`, the `theirs` arm): `SEGFAULT tid=…: execute unmapped address at …` for a text page, `read unmapped address` for data. The process dies as a program bug would, and its exit is the one a wild pointer gives, so a supervisor, a test or a user reading the crash @@ -32,7 +32,7 @@ from `/home` or `/boot`, and any file mapped from them, reaches it. ## Owner The demand-fault path in `kernel/src/process.rs` and the exception report in -`kernel/src/arch/idt/exceptions.rs`. +`kernel/src/arch/x86_64/idt/exceptions.rs`. ## Exit condition diff --git a/issues/kernel/a-processs-memory-is-a-byte-total-that-reads-zero-under-contention.md b/issues/kernel/a-processs-memory-is-a-byte-total-that-reads-zero-under-contention.md index c5c9ef9278e..f7a52d3b124 100644 --- a/issues/kernel/a-processs-memory-is-a-byte-total-that-reads-zero-under-contention.md +++ b/issues/kernel/a-processs-memory-is-a-byte-total-that-reads-zero-under-contention.md @@ -16,7 +16,7 @@ tree cannot do honestly today. a plateau.** `SYS_SYSINFO` carries a live per-process byte total: `demand_pages + mmap_regions -+ dynamic_tls_blocks + loaded_libs`, summed at `kernel/src/arch/syscall/machine.rs:146-156` ++ dynamic_tls_blocks + loaded_libs`, summed at `kernel/src/syscall/machine.rs:146-156` into the entry's `memory` word. A userland `mmap` becomes an `mmap_regions` entry, so a leaked thread stack is inside that number. **But the sum is taken under `try_lock`, and the failure arm writes `0`** (`machine.rs:155-157`). A @@ -29,7 +29,7 @@ the two fault counts. Subtracting frees from allocations would not give a live count even if `free_count` were exported, and it is not (`kernel/src/process.rs:502`, absent from the ABI struct) — **the two counters count different populations.** `alloc_count` is bumped at four sites including the demand-page fill -(`kernel/src/arch/syscall/vm.rs:117`, `:142`, `:374`, `kernel/src/process.rs:1432`); +(`kernel/src/syscall/vm.rs:117`, `:142`, `:374`, `kernel/src/process.rs:1432`); `free_count` at two, `munmap` and TLS teardown (`vm.rs:161`, `process.rs:189`). Demand-page release is never counted at all — `demand_pages` is cleared wholesale at teardown (`process.rs:965`) — so the difference folds in every demand fill ever diff --git a/issues/kernel/a-reclaimed-function-spends-a-remapping-entry-it-never-returns.md b/issues/kernel/a-reclaimed-function-spends-a-remapping-entry-it-never-returns.md index 5844d4f9635..d200855ab15 100644 --- a/issues/kernel/a-reclaimed-function-spends-a-remapping-entry-it-never-returns.md +++ b/issues/kernel/a-reclaimed-function-spends-a-remapping-entry-it-never-returns.md @@ -6,7 +6,7 @@ opened: 2026-09-24 # A re-claimed PCI function spends an interrupt remapping entry it never returns -`iommu::vtd::interrupt::allocate` (`kernel/src/iommu/vtd/interrupt.rs`) hands +`iommu::vtd::interrupt::allocate` (`kernel/src/arch/x86_64/vtd/interrupt.rs`) hands out the next entry of a 256-entry table (`ENTRIES`) by bumping `used`, and no path gives one back. Every `pcidev` claim arms MSI or MSI-X through `interrupt::msi`, so every claim of a function takes a new entry, and its diff --git a/issues/kernel/a-shared-boot-stopped-answering-and-no-capture-says-why.md b/issues/kernel/a-shared-boot-stopped-answering-and-no-capture-says-why.md index 487c84edfbc..c4e48715bc8 100644 --- a/issues/kernel/a-shared-boot-stopped-answering-and-no-capture-says-why.md +++ b/issues/kernel/a-shared-boot-stopped-answering-and-no-capture-says-why.md @@ -39,12 +39,12 @@ runs). Its guest prints and then passes. Those are the two parked counts and the reporter cadence the 2026-08-29 sighting read as proof of a wedged machine. A console read parks on a -10 ms re-poll (`CONSOLE_REPOLL`, `kernel/src/arch/syscall/io.rs`), so an idle +10 ms re-poll (`CONSOLE_REPOLL`, `kernel/src/syscall/io.rs`), so an idle shared boot waiting for its next command is parked at almost every sample. **Site: unknown.** The only candidate the surviving capture line supports is a `SYS_THREAD_JOIN` whose wake was lost: it parks on the target thread's own watch -at `Deadline::never()` (`kernel/src/arch/syscall/proc.rs:171`), and nothing else +at `Deadline::never()` (`kernel/src/syscall/proc.rs:171`), and nothing else would leave a whole boot idle straight after a sibling thread's clean exit. `issues/kernel/thread-exits-completion-post-is-the-second-one.md` owns that path's two posts. diff --git a/issues/kernel/a-shutdown-on-a-held-usb-disk-left-a-cpu-deaf-to-a-tlb-shootdown.md b/issues/kernel/a-shutdown-on-a-held-usb-disk-left-a-cpu-deaf-to-a-tlb-shootdown.md index ce832b72938..ea42025699d 100644 --- a/issues/kernel/a-shutdown-on-a-held-usb-disk-left-a-cpu-deaf-to-a-tlb-shootdown.md +++ b/issues/kernel/a-shutdown-on-a-held-usb-disk-left-a-cpu-deaf-to-a-tlb-shootdown.md @@ -30,7 +30,7 @@ branch no longer starts that thread there, which would remove this boot's trigger and not the deaf CPU. `a-disk-operation-can-spin-past-the-tlb-ack-tripwire-before-its-break.md` is -the arithmetic for one disk operation outrunning `ACK_TIMEOUT`; this is a boot +the arithmetic for one disk operation outrunning `time::DEAF_CPU`; this is a boot that did outrun it, in `quiesce`, across several operations each inside its own budget. Whether `quiesce` holds `IF` clear between them is not measured. diff --git a/issues/kernel/a-zero-byte-pipe-write-wakes-the-readers-watch.md b/issues/kernel/a-zero-byte-pipe-write-wakes-the-readers-watch.md index d68c0a3d38a..5bae5e2a1de 100644 --- a/issues/kernel/a-zero-byte-pipe-write-wakes-the-readers-watch.md +++ b/issues/kernel/a-zero-byte-pipe-write-wakes-the-readers-watch.md @@ -6,9 +6,9 @@ opened: 2026-09-26 # A zero-byte pipe write wakes the reader's watch -`sys_write_nonblock` (`kernel/src/arch/syscall/io.rs`) wakes the pipe's +`sys_write_nonblock` (`kernel/src/syscall/io.rs`) wakes the pipe's readers after every write that returns `Ok(n)`, `n == 0` included, and -`complete_pending_for_event` (`kernel/src/inbox.rs`) completes a pending +`complete_pending_for_event` (`kernel/src/inbox/mod.rs`) completes a pending `READABLE` watch on that wake as ready without looking at the ring. So a zero-byte write, which moves nothing, completes the reader's watch as readable while the reader's `read` still answers `WouldBlock`. diff --git a/issues/kernel/an-ap-loads-the-idt-before-its-control-registers.md b/issues/kernel/an-ap-loads-the-idt-before-its-control-registers.md index 4640c282f23..810acf4483d 100644 --- a/issues/kernel/an-ap-loads-the-idt-before-its-control-registers.md +++ b/issues/kernel/an-ap-loads-the-idt-before-its-control-registers.md @@ -7,7 +7,7 @@ opened: 2026-09-07 # An AP loads the IDT before its control registers, so a fault there triple-faults `percpu::init_bsp` applies the control registers before it loads the IDT: every -entry stub in `kernel/src/arch/idt` saves SSE state, and `fxsave` without +entry stub in `kernel/src/arch/x86_64/idt` saves SSE state, and `fxsave` without `CR4.OSFXSR` is `#UD`, so a fault taken between the two would raise `#UD` inside its own handler, double-fault into the same stub, and reset the machine. That ordering is stated at the site. diff --git a/issues/kernel/every-random-byte-is-one-rdrand.md b/issues/kernel/every-random-byte-is-one-rdrand.md index 4b7a4be39c0..73ed637b3d9 100644 --- a/issues/kernel/every-random-byte-is-one-rdrand.md +++ b/issues/kernel/every-random-byte-is-one-rdrand.md @@ -6,7 +6,7 @@ opened: 2026-09-24 # Every random byte is one RDRAND, with no generator behind it -`SYS_RANDOM` (`kernel/src/arch/syscall/io.rs`) answers every request with +`SYS_RANDOM` (`kernel/src/syscall/io.rs`) answers every request with `RDRAND` values copied straight to the caller. The kernel has no random generator of its own. Every key sshd mints, every future TLS session and every nonce therefore rests on one instruction from one vendor. There is no second diff --git a/issues/kernel/every-wait-in-this-kernel-is-a-spin.md b/issues/kernel/every-wait-in-this-kernel-is-a-spin.md index 511185188d1..48c60b6eda1 100644 --- a/issues/kernel/every-wait-in-this-kernel-is-a-spin.md +++ b/issues/kernel/every-wait-in-this-kernel-is-a-spin.md @@ -9,7 +9,7 @@ opened: 2026-08-12 **The heading and the paragraph under it are the state at opening, not the state of the tree.** What exists now: the completion core, where every wait in the kernel rechecks one predicate and a waiter lends a watch to the object it -waits on (`kernel/src/completion/mod.rs:1-8`); typed durations; and a sleep +waits on (`aaddf38a^:kernel/src/completion/mod.rs:1-8`, since folded into `kernel/src/watch.rs`); typed durations; and a sleep lock a contender parks on — written, loom-driven, and still `#![allow(dead_code)]` "until a kernel static converts to it" (`kernel/src/sleeplock.rs:1-9`), because none has. What has not moved is the diff --git a/issues/kernel/io-uring-enter-trips-the-one-queue-invariant.md b/issues/kernel/io-uring-enter-trips-the-one-queue-invariant.md index 224de2d6a5d..e8cbe881fe2 100644 --- a/issues/kernel/io-uring-enter-trips-the-one-queue-invariant.md +++ b/issues/kernel/io-uring-enter-trips-the-one-queue-invariant.md @@ -92,7 +92,7 @@ it again should re-read this: the backtrace will not look like the one above. `WaitQueue::wake_one`/`wake_all` popped a waiter and cleared its flag as two steps, so a waiter withdrawing in between found `dequeue` empty and its own -flag still set; `toyos-sched/loom/tests/loom_ticket.rs`'s +flag still set; `aaddf38a^:toyos-sched/loom/tests/loom_ticket.rs`'s `cancel_and_wake_agree_on_who_won` reds on that schedule. Both clear under the list lock now. It is a hole in the primitive rather than the capture above's path — `scheduler::wake_sched` claims through `wake_direct`, and no kernel diff --git a/issues/kernel/nothing-asserts-that-a-claim-answers-no-configuration-write.md b/issues/kernel/nothing-asserts-that-a-claim-answers-no-configuration-write.md index 1bcb3c4c862..d8df7f2197e 100644 --- a/issues/kernel/nothing-asserts-that-a-claim-answers-no-configuration-write.md +++ b/issues/kernel/nothing-asserts-that-a-claim-answers-no-configuration-write.md @@ -7,7 +7,7 @@ opened: 2026-09-08 # Nothing asserts that a claim answers no configuration write `SYS_DEVICE_REG_WRITE` on a `RegTarget::PciConfig` target is refused -`NotSupported` in `kernel/src/arch/syscall/device.rs`, and no test in any tier +`NotSupported` in `kernel/src/syscall/device.rs`, and no test in any tier reads that refusal. It is what a handed-over MSI function's safety rests on: its message address and data are words of configuration space rather than a table in a BAR, so nothing is withheld from the holder and the whole of the boundary is diff --git a/issues/kernel/one-mapping-is-written-in-two-ledgers.md b/issues/kernel/one-mapping-is-written-in-two-ledgers.md index 6f1b931ce43..c10a12ac9c7 100644 --- a/issues/kernel/one-mapping-is-written-in-two-ledgers.md +++ b/issues/kernel/one-mapping-is-written-in-two-ledgers.md @@ -9,7 +9,7 @@ opened: 2026-08-15 A live `mmap` is recorded twice. `ProcessData::mmap_regions` (`kernel/src/process.rs`) holds an `MmapRegion` — address, length, and the `PageAlloc` that owns the physical memory. `AddressSpace::regions` -(`kernel/src/mm/paging.rs`) holds a `Region` at the same address with the same +(`kernel/src/arch/x86_64/paging.rs`) holds a `Region` at the same address with the same length — and that one is the source of truth for placement: `find_gap` reads it and nothing else. @@ -32,7 +32,7 @@ exactly the same defect, and no test would see it until a placement collided. the only ledger. What that has to answer first: - **Accounting.** `alloc_count`, `free_count` and `peak_memory` are summed over - `mmap_regions` at `kernel/src/arch/syscall.rs`, and `SYS_SYSINFO`'s per-process + `mmap_regions` at `kernel/src/syscall/machine.rs`, and `SYS_SYSINFO`'s per-process memory line sums `_pages` over it under a `try_lock` on the process data — a `try_lock` the crash report depends on, so that reader may not move to a lock it can block on. @@ -53,10 +53,9 @@ next person to add a placement path should find this before writing it. **2026-08-25, promoted to `defect`.** Both ledgers are still live and still agree only because two functions are read carefully: `ProcessData::mmap_regions` at `kernel/src/process.rs:742` and `AddressSpace::regions` at -`kernel/src/mm/paging.rs:546`. The paths in this file predate the syscall split — -the writers are now `kernel/src/arch/syscall/vm.rs` and the `SYS_SYSINFO` -accounting sum is `kernel/src/arch/syscall/machine.rs:266`, not -`kernel/src/arch/syscall.rs`. An invariant with no checker and an open extension +`kernel/src/arch/x86_64/paging.rs:546`. The writers are +`kernel/src/syscall/vm.rs`, and the `SYS_SYSINFO` accounting sum is +`kernel/src/syscall/machine.rs:266`. An invariant with no checker and an open extension point is a defect in the shape and it already produced one kernel panic; the consolidation this file specifies is the fix. Owed by whoever next adds a placement path or a fourth `sys_mmap` arm. diff --git a/issues/kernel/only-one-allocation-can-be-made-to-fail-and-only-at-one-site.md b/issues/kernel/only-one-allocation-can-be-made-to-fail-and-only-at-one-site.md index c27a9034089..44abceaabf4 100644 --- a/issues/kernel/only-one-allocation-can-be-made-to-fail-and-only-at-one-site.md +++ b/issues/kernel/only-one-allocation-can-be-made-to-fail-and-only-at-one-site.md @@ -15,9 +15,9 @@ is not the gap; the gap is below. **What exists, and why it is not enough.** `debug_action::HEAP_AT_CEILING_PAGE_ALIGNED` (a `pub const` in `toyos-abi/src/syscall.rs`'s `debug_action` module, `:709` and `:723`; -dispatched at `kernel/src/arch/syscall/dispatch.rs:538`) drives `debug_heap_alloc` at +dispatched at `kernel/src/syscall/dispatch.rs:538`) drives `debug_heap_alloc` at `MAX_HEAP_ALLOC` with 4096-byte alignment, which the page source cannot back; -`kernel/src/arch/syscall/debug.rs:25-27` reports the null as +`kernel/src/syscall/debug.rs:25-27` reports the null as `ResourceExhausted` rather than unwrapping it, and `tests/toyos-rust-tests/src/bin/heap_ceiling.rs:131` asserts exactly that. So a test-only actuator does exist, a shipped test does read it, and the claim that diff --git a/issues/kernel/page-global-is-a-decision-nobody-has-made.md b/issues/kernel/page-global-is-a-decision-nobody-has-made.md index 47d33f9ef6b..e43cfab717b 100644 --- a/issues/kernel/page-global-is-a-decision-nobody-has-made.md +++ b/issues/kernel/page-global-is-a-decision-nobody-has-made.md @@ -2,7 +2,6 @@ status: open kind: track opened: 2026-08-19 -decided: 2026-08-20 --- # There is no `PAGE_GLOBAL` anywhere, and the owner decided: turn it on @@ -18,7 +17,7 @@ the KVM instrument so the improvement is a number against a number. TCG cannot price it (tests/CLAUDE.md: the local guest has no such cache model), which is one more reason it rides the measured era. -`CR4.PGE` is absent from `kernel/src/arch/control_regs.rs`'s declaration and no +`CR4.PGE` is absent from `kernel/src/arch/x86_64/control_regs.rs`'s declaration and no `PAGE_GLOBAL` exists in `kernel/src/mm/`. With PCID active, every address space therefore caches a private copy of the kernel's direct map, and every `flush_tlb_all` — which is `INVPCID` all-context — throws away the kernel's own @@ -30,7 +29,7 @@ affected and this kernel has no KPTI, so global kernel pages are available. place, and the bits left out are as much of the declaration as the bits in — means `PGE`'s absence should be *stated* whichever way it goes. -**What the answer would have to revisit.** `kernel/src/mm/paging.rs` now derives +**What the answer would have to revisit.** `kernel/src/arch/x86_64/paging.rs` now derives every invalidation from the entry a write replaced and discharges it with `INVPCID` type 0 or `INVLPG`. Neither touches a global translation, which is sound only because no entry in this kernel is global; the module header says so diff --git a/issues/kernel/spawned-process-never-starts.md b/issues/kernel/spawned-process-never-starts.md index 2b86d023085..7ade9abb9a8 100644 --- a/issues/kernel/spawned-process-never-starts.md +++ b/issues/kernel/spawned-process-never-starts.md @@ -40,7 +40,7 @@ Shell 28 is the healthy control the same log offers: 95 syscalls, `spawn 3` paired with `waitpid 3`, and it went on from `ls /system/bin` to `free` and then `doom`. So neither a lost exit notification nor a missed wakeup is involved, and `sys_waitpid` registering on the park lot before it reads the table -(`kernel/src/arch/syscall.rs:1067`) is doing its job. +(`4a98107f^:kernel/src/arch/syscall.rs`; `SYS_WAITPID` is retired since) is doing its job. **Refuted, and it was the first hypothesis because it was a same-day change** (#129, `85a8433`): a child that takes a surface grab and dies without releasing diff --git a/issues/kernel/the-capability-end-state-is-twelve-answers.md b/issues/kernel/the-capability-end-state-is-twelve-answers.md index 968c16899fa..081358c1d49 100644 --- a/issues/kernel/the-capability-end-state-is-twelve-answers.md +++ b/issues/kernel/the-capability-end-state-is-twelve-answers.md @@ -32,25 +32,22 @@ taken, not a queue waiting on anybody. Four of the rulings are enforced in code, each at a site that demands a right where none was demanded before: `SYS_SHUTDOWN` takes a `SysCap` carrying -`Rights::POWER` (`kernel/src/arch/syscall/machine.rs:46-48`); `SYS_SYSINFO`'s +`Rights::POWER` (`kernel/src/syscall/machine.rs:46-48`); `SYS_SYSINFO`'s roster takes `Rights::ROSTER`, demanded only once the buffer has room for an -entry (`kernel/src/arch/syscall/machine.rs:84`, `:94`), spelled `roster` in +entry (`kernel/src/syscall/machine.rs:84`, `:94`), spelled `roster` in `toyos-manifest/src/lib.rs:80`; and `SYS_PROCESS_OPEN` takes `Rights::MANAGE` -(`kernel/src/arch/syscall/proc.rs:89-91`), which is what makes question 3's +(`kernel/src/syscall/proc.rs:89-91`), which is what makes question 3's "a pid is not authority" checkable — the ABI says so at the field (`toyos-abi/src/syscall.rs:1882-1884`). -**Every `kernel/src/arch/syscall.rs:NNN` citation below is a dead pointer.** -That file is not in the tree; the syscalls are `kernel/src/arch/syscall/`, -twelve files with `dispatch.rs` decoding every user pointer. Below this -paragraph the file is named on **20 lines**, carrying **19 fully-spelled -`kernel/src/arch/syscall.rs:NNN` citations** — the twentieth names the file with -no line number at all — plus the bare `` `:NNN` `` continuations hanging off -them, which are deliberately not counted: a bare continuation in this file may -equally belong to `kernel/src/process.rs` or `kernel/src/object/ops.rs`, so the -number would need a reading rather than a match. All of them are kept as the -sections they were taken from, to be re-taken rather than trusted — a split -moves every line, so re-pointing them needs a re-read, not arithmetic. +**Every `4a98107f^:kernel/src/arch/syscall.rs` citation below points into +history**: that one-file syscall layer is what `4a98107f` split, and the +syscalls are `kernel/src/syscall/` now, twelve files with `dispatch.rs` +decoding every user pointer. Its line numbers were taken earlier still, so +they and the bare `` `:NNN` `` continuations hanging off them — which may +equally belong to `kernel/src/process.rs` or `kernel/src/object/ops.rs` — are +kept as the sections they were taken from, to be re-taken rather than trusted: +a split moves every line, so re-pointing them needs a re-read, not arithmetic. ## 1. What constitutes authority? — COMMITTED @@ -64,7 +61,7 @@ koid into access to anything" (`kernel/src/object/mod.rs:74`), and an endowment label is "a *local name* in one process's own table and buys nothing to guess" (`toyos-abi/src/syscall.rs:315`). Every syscall states the right it needs at its own arm — `Rights::NONE` where the handle alone is the authority -(`kernel/src/arch/syscall.rs:309`, `:442`, `:1032`) — because "a right left +(`4a98107f^:kernel/src/arch/syscall.rs:309`, `:442`, `:1032`) — because "a right left unstated is a right each call site invents" (`toyos-abi/src/handle.rs:53`). The qualification is question 5's ambient set. @@ -84,10 +81,10 @@ era opened deliberately, never a retrofit. **Two sites disagree.** The root `CLAUDE.md`'s Capabilities paragraph says "a process holds exactly what its parent moved into it, and there is nothing it can name to get more". The dispatch does not: `SYS_OPEN` resolves any absolute path -against the machine's one VFS (`kernel/src/arch/syscall.rs:1233`), gated only by +against the machine's one VFS (`4a98107f^:kernel/src/arch/syscall.rs:1233`), gated only by a per-*mount* `user_may_modify` whose whole subject is protecting `/boot` (`kernel/src/vfs.rs:288`); `SYS_SPAWN` starts any binary on that filesystem by -path (`kernel/src/arch/syscall.rs:359`); `SYS_DLOPEN` loads any `.so` by path +path (`4a98107f^:kernel/src/arch/syscall.rs:359`); `SYS_DLOPEN` loads any `.so` by path (`:504`); `SYS_SHUTDOWN` powers the machine off with no handle and no right (`:328`). So the answer today is **no**, and the sentence in `CLAUDE.md` is true of kernel objects and false of the filesystem. @@ -105,7 +102,7 @@ closed. Identity-only, with one named exception that is itself gated. Every arm taking a pid: `SYS_GETPID` answers the caller's own -(`kernel/src/arch/syscall.rs:490`), and `SYS_PROCESS_OPEN` turns a pid into a +(`4a98107f^:kernel/src/arch/syscall.rs:490`), and `SYS_PROCESS_OPEN` turns a pid into a `Process` handle only when the caller also presents a `SysCap` carrying `Rights::MANAGE` (`:1602`), which the kernel mints once, for `/system/bin/init` (`kernel/src/loader/mod.rs:938`). `ProcessStats.pid` says so at the field: "Not @@ -113,10 +110,10 @@ authority — nothing takes a pid but `SYS_PROCESS_OPEN`, which takes a `SysCap` beside it" (`toyos-abi/src/syscall.rs:1803`). Tids are process-local names: `SYS_THREAD_JOIN` resolves through `thread_sched(caller, tid)` and `collect_thread_zombie(table, tid, parent_pid)`, both keyed on the caller's own -pid (`kernel/src/arch/syscall.rs:2393`, `kernel/src/process.rs:1412`, `:848`). +pid (`4a98107f^:kernel/src/arch/syscall.rs:2393`, `kernel/src/process.rs:1412`, `:848`). Four pid-addressed syscalls were deleted and their numbers retired rather than reused — 26 `SYS_WAITPID`, 33 `SYS_FIND_PID`, 37 `SYS_GRANT_SHARED`, 65 -`SYS_KILL` (`kernel/src/arch/syscall.rs:63`). +`SYS_KILL` (`4a98107f^:kernel/src/arch/syscall.rs:63`). ## 4. Can a process enumerate objects it lacks authority over? — RULED 2026-08-20, IMPLEMENTED 2026-08-22 @@ -124,7 +121,7 @@ reused — 26 `SYS_WAITPID`, 33 `SYS_FIND_PID`, 37 `SYS_GRANT_SHARED`, 65 `Rights::ROSTER` on a `SysCap` (`toyos-abi/src/handle.rs`), spelled `roster` in `toyos_manifest`'s `SYSCAP_RIGHTS` (`toyos-manifest/src/lib.rs`), demanded by `sys_sysinfo` before a single per-process entry is collected or written -(`kernel/src/arch/syscall.rs`), and endowed by `system.toml` to `toybox` — +(`4a98107f^:kernel/src/arch/syscall.rs`), and endowed by `system.toml` to `toybox` — which is what `/system/bin/ps` is under another name — exactly as `logread` is endowed to `logd`. The machine header the same call answers first stays ambient, which is question 5's committed set: `free`, netd's memory budget and the compositor's @@ -138,7 +135,7 @@ The rest of the object graph was clean when this was audited and is unchanged. No syscall lists another process's handles; a `Namespace` answers `lookup` and has no listing operation at all (`kernel/src/object/namespace.rs:59`); `SYS_ENDOWMENTS` answers the caller's own table -(`kernel/src/arch/syscall.rs:1707`); reading the machine's log is gated on +(`4a98107f^:kernel/src/arch/syscall.rs:1707`); reading the machine's log is gated on `Rights::LOG` (`:1683`) precisely because it is "every process's business and no process's right by default"; the per-kind object census is `SYS_DEBUG`, which a shipping kernel does not have (`:704`, `:792`). `SYS_READDIR` enumerates any @@ -172,7 +169,7 @@ table** can still reach: accumulators) but not the roster after it; - **object creation, which confers nothing over anything that exists** — `SYS_PIPE`, `SYS_PORT_CREATE` ("needs no right and grants none — a port with - no clients is not authority", `kernel/src/arch/syscall.rs:1731`), + no clients is not authority", `4a98107f^:kernel/src/arch/syscall.rs:1731`), `SYS_SHM_CREATE`, `SYS_INBOX_SETUP`; - **and four that reach past the process** — the whole filesystem by path (`SYS_OPEN`, `SYS_READDIR`, `SYS_DELETE`, `SYS_MKDIR`, `SYS_RMDIR`, @@ -193,7 +190,7 @@ Every path that puts an entry in a table was walked. The only rights-taking constructor a caller can reach is `HandleEntry::duplicate`, which refuses without `Rights::DUP` and refuses a set that is not `subset_of` the source's (`kernel/src/object/handle.rs:170`); `SYS_HANDLE_DUP` -(`kernel/src/arch/syscall.rs:2596`) and `SYS_HANDLE_DUP_AT` (`:2624`) both go +(`4a98107f^:kernel/src/arch/syscall.rs:2596`) and `SYS_HANDLE_DUP_AT` (`:2624`) both go through it, so a device claim — created with no `DUP` (`kernel/src/object/ops.rs:47`) — cannot be copied at either. The five `HandleEntry::new` sites are all fresh objects, never a re-rating of a held one: @@ -231,7 +228,7 @@ and nothing else is:** The child's cwd is not implicit: `SpawnArgs` states it (`cwd_ptr`/`cwd_len`), and the kernel starts the child there or refuses the spawn — `InvalidArgument` for a path that is not absolute, `NotFound` for one naming no directory — with -no default to the caller's (`spawn_cwd` in `kernel/src/arch/syscall/fs.rs`). +no default to the caller's (`spawn_cwd` in `kernel/src/syscall/fs.rs`). std states `Command::current_dir`, or the caller's own directory when there is none, and `tests/toyos-rust-tests/src/bin/spawn_cwd.rs` holds both routes to it. @@ -248,7 +245,7 @@ authority init decides per program" — the machine's only one is minted with `TRANSFER` at `kernel/src/loader/mod.rs:946`. The mechanisms are `SYS_HANDLE_SEND`/`SYS_HANDLE_RECV` over a `Connection`, where the connection and every handle must carry `TRANSFER`, no handle may be named twice, and the -connection may not be sent over itself (`kernel/src/arch/syscall.rs:2086`, +connection may not be sent over itself (`4a98107f^:kernel/src/arch/syscall.rs:2086`, `:2090`); and `SpawnArgs::endow`. Nothing else crosses a process boundary — the handles `install_buffer` writes are the kernel handing a claim's holder its own buffers, not a transfer (`kernel/src/object/device.rs:49`). @@ -264,7 +261,7 @@ needs it. **Nothing in the tree has decided it.** There is no `Thread` row in `kobject!` (`kernel/src/object/mod.rs:278`), so a thread is not something a handle can name. `SYS_THREAD_SPAWN` answers a bare `Tid` -(`kernel/src/arch/syscall.rs:2376`), `SYS_THREAD_JOIN` takes one and resolves it +(`4a98107f^:kernel/src/arch/syscall.rs:2376`), `SYS_THREAD_JOIN` takes one and resolves it only inside the caller's own process (`:2393`), there is no thread-kill and no cross-process thread operation, and a `Process` handle's `MANAGE` retires every thread at once (`kernel/src/process.rs:1960`). This is the shape that resulted @@ -287,14 +284,14 @@ a build-time fact rather than a runtime race. At boot the kernel mints one full-rights `SysCap` for `/system/bin/init` and nothing else can construct one (`kernel/src/loader/mod.rs:938`). init reads the manifest and calls `SYS_DEVICE_CLAIM` per declared class (`userland/init/src/main.rs:506`), which -demands `Rights::DEVICE` on a `SysCap` (`kernel/src/arch/syscall.rs:1627`) and +demands `Rights::DEVICE` on a `SysCap` (`4a98107f^:kernel/src/arch/syscall.rs:1627`) and takes the class exclusively (`kernel/src/device.rs:56`). The claim comes back **without `Rights::DUP`**, so endowing it is the only expressible form and init provably no longer holds it (`kernel/src/object/ops.rs:47`). Every device-driving syscall then presents that handle and the kernel checks the *class*, not merely the type: "a process holding the NIC has no more business setting the resolution than one holding nothing" -(`kernel/src/arch/syscall.rs:895`). `SYS_RT_ENTER` and `SYS_PROCESS_OPEN` are +(`4a98107f^:kernel/src/arch/syscall.rs:895`). `SYS_RT_ENTER` and `SYS_PROCESS_OPEN` are the same shape on `Rights::RT` and `Rights::MANAGE` (`:1655`, `:1602`), narrowed per program by `toyos_manifest::syscap_rights` (`toyos-manifest/src/lib.rs:73`) — `system.toml` grants exactly two, `logread` @@ -302,7 +299,7 @@ to `logd` and `rt` to `soundd`. One inconsistency, known and unobservable: three arms demand three different rights on the same claim handle — `Rights::WRITE` -(`kernel/src/arch/syscall.rs:902`), `Rights::READ` (`:1136`) and `Rights::NONE` +(`4a98107f^:kernel/src/arch/syscall.rs:902`), `Rights::READ` (`:1136`) and `Rights::NONE` (`:1032`). No claim handle can ever carry a narrower set, because narrowing needs `DUP` and a claim has none, so the three are the same test today. @@ -313,14 +310,14 @@ needs `DUP` and a claim has none, so the three are the same test today. no replace", and a narrower one is a *new* object built from an existing one (`kernel/src/object/namespace.rs:1`). `SYS_NAMESPACE_OPEN` demands `Rights::READ` on a namespace handle and there is no second place to ask -(`kernel/src/arch/syscall.rs:1861`); `SYS_NAMESPACE_BUILD` demands +(`4a98107f^:kernel/src/arch/syscall.rs:1861`); `SYS_NAMESPACE_BUILD` demands `Rights::TRANSFER` on every added connector and resolves kept names against the base before installing anything (`:1754`). A process with no `svc` endowment resolves no name at all, and there is no registry to fall back to: 85 `SYS_LISTEN` and 87 `SYS_CONNECT` are retired numbers -(`kernel/src/arch/syscall.rs:77`, `:78`). The **filesystem** path space is the +(`4a98107f^:kernel/src/arch/syscall.rs:77`, `:78`). The **filesystem** path space is the other thing the word could mean, and it is ambient process state -(`kernel/src/arch/syscall.rs:1234`) — questions 2 and 5 hold that half. +(`4a98107f^:kernel/src/arch/syscall.rs:1234`) — questions 2 and 5 hold that half. ## 12. Is CPU time an explicit schedulable/budget authority, or is process-level fair scheduling sufficient? — OPEN @@ -328,7 +325,7 @@ Held by `issues/kernel/cpu-time-is-a-band-and-not-a-reservation.md`, which already carries the commitment and the measurements; nothing is added here. Today the only CPU-time authority is a band: `SYS_RT_ENTER` puts the calling process in the real-time band on `Rights::RT` -(`kernel/src/arch/syscall.rs:1653`), with no budget, no period and no admission +(`4a98107f^:kernel/src/arch/syscall.rs:1653`), with no budget, no period and no admission — "no entity is promised anything a number can check", measured at 93.3 ms of audio starvation behind a fair storm. diff --git a/issues/kernel/the-deadline-header-promises-any-interrupt-and-only-the-timer-entry-polls.md b/issues/kernel/the-deadline-header-promises-any-interrupt-and-only-the-timer-entry-polls.md index bcea1f94302..6a53445ac9f 100644 --- a/issues/kernel/the-deadline-header-promises-any-interrupt-and-only-the-timer-entry-polls.md +++ b/issues/kernel/the-deadline-header-promises-any-interrupt-and-only-the-timer-entry-polls.md @@ -16,7 +16,7 @@ interrupt: it is polled from the timer entry." `poll` says the narrower thing (`deadline.rs:185-186`): "Whether this machine's bound has passed; the timer interrupt entry's, in both rings, and nothing else's." Its call sites are exactly two, both in -`kernel/src/arch/idt/timer.rs`: the Ring 0 naked entry's `call {deadline}` +`kernel/src/arch/x86_64/idt/timer.rs`: the Ring 0 naked entry's `call {deadline}` (`:71`, `deadline = sym crate::deadline::poll` at `:79`, placed there because "a CPU spinning on a ticket still takes this interrupt", `:61-63`) and `timer_handler`, which opens at `:92`: its first statement is diff --git a/issues/kernel/the-global-pipe-lock-spans-a-user-copy.md b/issues/kernel/the-global-pipe-lock-spans-a-user-copy.md index 1306bc2f8b5..a4ab14f071b 100644 --- a/issues/kernel/the-global-pipe-lock-spans-a-user-copy.md +++ b/issues/kernel/the-global-pipe-lock-spans-a-user-copy.md @@ -16,13 +16,13 @@ The bulk copy is inside that closure, not outside it. * `pipe::try_write` (`pipe.rs:244`) calls `backing.ring.write(buf.len(), |off, dst| buf.read_at(off, dst))` at `pipe.rs:254`, same acquisition. * `Ring::read`/`Ring::write` (`toyos-abi/src/ring.rs:141`, `:178`) hand the closure one or two contiguous runs; the closure is `UserBytesMut::write_at` / `UserBytes::read_at` (`kernel/src/user_ptr.rs:206`, `:164`), a `copy_nonoverlapping` of the whole run. -The size is bounded only by the ring: `PIPE_SIZE = PAGE_2M` (`pipe.rs:104`), `PAGE_2M = 2 * 1024 * 1024` (`toyos-userbound/src/span.rs:27`), and `capacity = total_size - size_of::()` (`ring.rs:60`) with `RingHeader` `#[repr(C, align(64))]` holding one `AtomicU32` (`ring.rs:24-27`) — so **2,097,088 bytes** is the largest single copy under the lock. Nothing above caps it: `SYS_READ`/`SYS_WRITE` pass the userland length straight through (`kernel/src/arch/syscall/dispatch.rs:110-116`), `object::ops::try_read` hands the full window to `pipe::try_read` (`kernel/src/object/ops.rs:339-340`), and the only other bound, `user_ptr::window` (`user_ptr.rs:269`), requires physical contiguity — which a demand-paged 2 MiB frame satisfies exactly. +The size is bounded only by the ring: `PIPE_SIZE = PAGE_2M` (`pipe.rs:104`), `PAGE_2M = 2 * 1024 * 1024` (`toyos-userbound/src/span.rs:27`), and `capacity = total_size - size_of::()` (`ring.rs:60`) with `RingHeader` `#[repr(C, align(64))]` holding one `AtomicU32` (`ring.rs:24-27`) — so **2,097,088 bytes** is the largest single copy under the lock. Nothing above caps it: `SYS_READ`/`SYS_WRITE` pass the userland length straight through (`kernel/src/syscall/dispatch.rs:110-116`), `object::ops::try_read` hands the full window to `pipe::try_read` (`kernel/src/object/ops.rs:339-340`), and the only other bound, `user_ptr::window` (`user_ptr.rs:269`), requires physical contiguity — which a demand-paged 2 MiB frame satisfies exactly. A pipe's **first** write is worse, because the page is allocated lazily under the same lock. `try_write` calls `pipe.back()` (`pipe.rs:250`), which calls `pmm::alloc_page(pmm::Category::Pipe)` (`pipe.rs:148`). That takes `BITMAP` nested inside `PIPES` (`kernel/src/mm/pmm.rs:221`), linearly scans up to the whole physical bitmap for a free frame (`pmm.rs:224-241`), and then `write_bytes(..., 0, PAGE_2M)` — a 2 MiB zeroing (`pmm.rs:233-237`) — before `Ring::new` and the user copy that follows it. ## What queues behind it -Everything pipe-shaped in the kernel goes through the same static: `create` (`pipe.rs:191`), `map_page` (`pipe.rs:206`), `has_data`/`has_space` (`pipe.rs:261`, `:267`) — which the inbox readiness predicate calls per registered source (`kernel/src/inbox.rs:767-768`) and the blocking read/write wrappers call per attempt (`kernel/src/arch/syscall/io.rs:76`, `:162`) — every `PipeReader`/`PipeWriter` clone and close (`pipe.rs:74-80`, `close_read`), and `add_inbox_watcher`/`remove_inbox_watcher` (`pipe.rs:338`, `:348`). A compositor client, netd, logd and the shell all contend the same word. +Everything pipe-shaped in the kernel goes through the same static: `create` (`pipe.rs:191`), `map_page` (`pipe.rs:206`), `has_data`/`has_space` (`pipe.rs:261`, `:267`) — which the inbox readiness predicate calls per registered source (`kernel/src/inbox/mod.rs:767-768`) and the blocking read/write wrappers call per attempt (`kernel/src/syscall/io.rs:76`, `:162`) — every `PipeReader`/`PipeWriter` clone and close (`pipe.rs:74-80`, `close_read`), and `add_inbox_watcher`/`remove_inbox_watcher` (`pipe.rs:338`, `:348`). A compositor client, netd, logd and the shell all contend the same word. ## Impact, stated at its real size diff --git a/issues/kernel/the-reset-word-is-spelled-twice.md b/issues/kernel/the-reset-word-is-spelled-twice.md index ef86d102e7e..5c561bb6dd1 100644 --- a/issues/kernel/the-reset-word-is-spelled-twice.md +++ b/issues/kernel/the-reset-word-is-spelled-twice.md @@ -6,7 +6,7 @@ opened: 2026-09-07 # The word a metal verdict turns on is spelled twice, and neither speller reads the other -`kernel/src/arch/syscall/machine.rs:86` writes `quiesce("Rebooting.")` and +`kernel/src/syscall/machine.rs:86` writes `quiesce("Rebooting.")` and `src/bootlog.rs:15` declares `REBOOTING: &str = "Rebooting."` as the last line a passing boot must leave. Two literals, no shared declaration: reword the kernel's and every metal run refuses with `the log's last line is … and not diff --git a/issues/kernel/the-split-window-tlb-cost-is-unpriced.md b/issues/kernel/the-split-window-tlb-cost-is-unpriced.md index 933715e4d21..3d0733abb65 100644 --- a/issues/kernel/the-split-window-tlb-cost-is-unpriced.md +++ b/issues/kernel/the-split-window-tlb-cost-is-unpriced.md @@ -11,7 +11,7 @@ W^X maps one 2 MiB window per binary at 4 KiB granularity — the window where the boot set's text can be write-protected and 0 % of its data can be made non-executable (measured 2026-08-20 over 20 binaries and 33 windows; the numbers and the rejected alternatives are in `WindowProt`'s doc comment in -`kernel/src/mm/paging.rs`). The design question is settled by those numbers. The +`kernel/src/arch/x86_64/paging.rs`). The design question is settled by those numbers. The *cost* is not measured, and this host cannot measure it. 512 4 KiB entries replace one 2 MiB entry for that window, so a program whose hot @@ -30,7 +30,7 @@ send it to 2 MiB-aligning `toyos-ld`'s segments, which was measured at +4 MiB of physical memory per process. **2026-08-25, promoted to `defect`.** Checked at the site: `WindowProt`'s doc -comment in `kernel/src/mm/paging.rs` carries the design measurement — 20 +comment in `kernel/src/arch/x86_64/paging.rs` carries the design measurement — 20 binaries, 33 windows, 20 mixed, 48.9 % and 0 % — and says nothing about what the 512 4 KiB entries cost the TLB, so the number lives nowhere in the tree. That makes it a measurement owed, in the same class as the AP control-register delta diff --git a/issues/kernel/thread-exits-completion-post-is-the-second-one.md b/issues/kernel/thread-exits-completion-post-is-the-second-one.md index ef1c9f40dbd..e7b9739b119 100644 --- a/issues/kernel/thread-exits-completion-post-is-the-second-one.md +++ b/issues/kernel/thread-exits-completion-post-is-the-second-one.md @@ -20,7 +20,7 @@ scheduler::exit_current(code); `exit_current` reaches `driver::pass(Dispose::Exit)`, and the pass after that one drops the task's payload through `Hw::release` -(`kernel/src/hw.rs`), which ends with `TaskHandle::publish_released` — +(`kernel/src/arch/x86_64/hw.rs`), which ends with `TaskHandle::publish_released` — and that posts `Gone(Closed)` **on the same watch**, which its own comment states in those words: *"the retirer is armed on this thread's own watch — the same subject a joiner uses, and the reason the release no longer needs a queue diff --git a/issues/kernel/toyos-runs-on-arm64.md b/issues/kernel/toyos-runs-on-arm64.md index 78e2baceb0c..69091d6b29f 100644 --- a/issues/kernel/toyos-runs-on-arm64.md +++ b/issues/kernel/toyos-runs-on-arm64.md @@ -6,7 +6,7 @@ opened: 2026-09-26 # ToyOS runs on ARM64: QEMU `virt` first, ACPI only, EL1 only -Supersedes `issues/kernel/arm64-is-a-decision-nobody-has-made.md`, which is +Supersedes `8a277bb2^:issues/kernel/arm64-is-a-decision-nobody-has-made.md`, which is folded in below and deleted. It keeps that file's settled points: compile-time dispatch (one `cfg_attr(path)` module choice, no `dyn Arch`, no `Kernel`, ~20 distinct symbols behind ~145 `crate::arch::` paths), a 4 KiB granule with @@ -42,7 +42,7 @@ Every x86 guest on this host runs under TCG emulation instead — there is no target for now. - **The memory-model audit is stage 0's**, not discovered on real ARM hardware: the kernel's `Relaxed` orderings and `Mmio`'s barrier semantics are - real latent defects on x86 too (`issues/kernel/the-stops-no-lost-wake-claim-rests-on-x86-locked-rmws.md` + real latent defects on x86 too (`aaddf38a^:issues/kernel/the-stops-no-lost-wake-claim-rests-on-x86-locked-rmws.md` is one instance). - **`toyos-cc` gets no AArch64 backend yet.** Doom and tinycc stay x86-only until userland runs on ARM, and that is decided again then. @@ -88,7 +88,7 @@ code for hardware no ARM board in scope has. `sched/driver.rs:1019-1062`), `irq_census.rs` 19, `nmi_gate.rs` 16, `main.rs` 13, `drivers/serial.rs` 13, `hw.rs` 10, `blackbox.rs` 6, `iommu/vtd/table.rs` 6, `panic_console/mod.rs:1294` 1, `xhci/wait/mod.rs:36` 1. Userland plus -`toyos-abi`: 54 lines (`libc/memory.rs` 23, `toyos-abi/syscall.rs` 20). The +`toyos-abi`: 54 lines (`libc/memory.rs` 23, `toyos-abi/src/syscall.rs` 20). The rust fork's std has two naked-asm sites: `_start` (`rust/library/std/src/sys/pal/toyos/mod.rs:44`) and `__tls_get_addr` reading `fs:[8]` (`pal/toyos/tls.rs:43`). @@ -158,7 +158,7 @@ refuses anything but `EM_X86_64` (`toyos-elf/src/header.rs:24,75`). (`toyos-abi/src/syscall.rs:678,703`: `syscall` and `svc #0`). `toyos-sched` (8,099 lines) is pure behind `Machine`/`Hw` (`toyos-sched/src/hw.rs:88-158`: `now`, `set_timer`, `stop_timer`, -`irq_guard`, `halt`, `need_resched`, `switch`), with `kernel/src/hw.rs` as +`irq_guard`, `halt`, `need_resched`, `switch`), with `kernel/src/arch/x86_64/hw.rs` as the one x86 implementation and a simulator as the other. PCI is ECAM/MMIO-only (`drivers/pci.rs:134-154`), no `0xCF8`. NVMe, xHCI and virtio have no ISA dependence beyond TSC-based waits. The bootloader is the `uefi` @@ -258,7 +258,7 @@ Each stage names its exit; "measured" means a number from a run. `gate.rs` moves out of `arch/`. The MSI doorbell becomes one arch-provided constant. `IrqGuard`/`LogCommitGuard` become one arch primitive. The loom model owed by - `issues/kernel/the-stops-no-lost-wake-claim-rests-on-x86-locked-rmws.md` + `aaddf38a^:issues/kernel/the-stops-no-lost-wake-claim-rests-on-x86-locked-rmws.md` lands, and the `Mmio` barrier contract above is written and asserted. **Exit**: x86 builds and passes unchanged. `rg 'x86_64-unknown' src/` names one `Arch` table. diff --git a/issues/panic-path/a-fatal-event-stands-down-both-bounds-and-may-leave-nothing-to-end-the-machine.md b/issues/panic-path/a-fatal-event-stands-down-both-bounds-and-may-leave-nothing-to-end-the-machine.md index 25401d29336..dc6c2cff938 100644 --- a/issues/panic-path/a-fatal-event-stands-down-both-bounds-and-may-leave-nothing-to-end-the-machine.md +++ b/issues/panic-path/a-fatal-event-stands-down-both-bounds-and-may-leave-nothing-to-end-the-machine.md @@ -6,9 +6,9 @@ opened: 2026-09-14 # A fatal event stands down both bounds, and on a machine that cannot reset itself nothing is left to end the boot -`apic::halt_all_cpus` (`kernel/src/arch/apic.rs:228`) calls +`apic::halt_all_cpus` (`kernel/src/arch/x86_64/apic.rs:228`) calls `crate::hardlockup::stand_down()` and `crate::deadline::stand_down()` before it -holds the panel (`kernel/src/arch/apic.rs:235-236`). That is deliberate and the +holds the panel (`kernel/src/arch/x86_64/apic.rs:235-236`). That is deliberate and the deadline's own header says so — "a panic in progress, which is not a gap but a stand-down: `apic::halt_all_cpus` calls `stand_down` before it holds the panel, so a panic report is never replaced by an expiry" diff --git a/issues/panic-path/no-console-between-boot-and-terminal.md b/issues/panic-path/no-console-between-boot-and-terminal.md index 22bc38a1580..370a5b8da86 100644 --- a/issues/panic-path/no-console-between-boot-and-terminal.md +++ b/issues/panic-path/no-console-between-boot-and-terminal.md @@ -43,7 +43,7 @@ milliseconds later, and no key pauses it: `page_forever` is reached only from userland" is `/system/bin/logd`: the kernel keeps the record ring and the console and writes no file at all, logd owns `/log` and puts one file per boot there named for the wall clock, `src/build.rs`'s `every_boot_config_runs_logd` refuses a boot -config that omits it, and `kernel/src/log_file.rs` is deleted. Both ways of +config that omits it, and `9ca7631a^:kernel/src/log_file.rs` is deleted. Both ways of reading that file need what this window denies: pulling the stick takes the machine out of the session, and `cat /log/` from the desktop needs input, which is the case this was opened for — a dead keyboard and a dead TrackPoint, diff --git a/kernel-loom/Cargo.toml b/kernel-loom/Cargo.toml index a389a4896db..e59a463e7d3 100644 --- a/kernel-loom/Cargo.toml +++ b/kernel-loom/Cargo.toml @@ -1,5 +1,6 @@ [package] name = "kernel-loom" +description = "Loom models of the kernel's lock-free concurrency, over the kernel's own sources compiled beside the kernel and not in it." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 70caaf26652..29f0c4af7eb 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -1,5 +1,6 @@ [package] name = "kernel" +description = "The ToyOS kernel: resource management, scheduling, process lifecycle, filesystem and device arbitration." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/kernel/src/arch/aarch64/paging.rs b/kernel/src/arch/aarch64/paging.rs index b287a4449c9..29fa4e7bf8d 100644 --- a/kernel/src/arch/aarch64/paging.rs +++ b/kernel/src/arch/aarch64/paging.rs @@ -9,7 +9,7 @@ use core::convert::Infallible; use crate::mm::UserAddr; -pub use crate::mm::policy::{CachePolicy, MmioPolicy, Prot, WindowProt}; +use crate::mm::policy::{CachePolicy, MmioPolicy, Prot, WindowProt}; use crate::sync::Lock; use crate::vma::{Occupancy, Region, RegionKind}; use crate::MemoryMapEntry; diff --git a/kernel/src/arch/aarch64/tlb.rs b/kernel/src/arch/aarch64/tlb.rs index dae8edfbe64..df6dec4b13b 100644 --- a/kernel/src/arch/aarch64/tlb.rs +++ b/kernel/src/arch/aarch64/tlb.rs @@ -3,7 +3,7 @@ //! plus `DSB ISH` once the kernel owns its page tables, the port's stage 4. -pub use crate::invalidation::Origin; +use crate::invalidation::Origin; pub fn log_census() { owed!("TLB invalidation", "stage 4") diff --git a/kernel/src/arch/x86_64/control_regs.rs b/kernel/src/arch/x86_64/control_regs.rs index 2688da246b3..3ad0eb2ff98 100644 --- a/kernel/src/arch/x86_64/control_regs.rs +++ b/kernel/src/arch/x86_64/control_regs.rs @@ -3,7 +3,7 @@ //! else may write any of the three. Each register is written whole: `CR0` //! and `EFER` are constants, `CR4` is required bits plus whatever optional //! bits this CPU offers. `EFER.NXE` lets bit 63 of a paging entry mean *not -//! executable* ([`Prot`](crate::mm::paging::Prot)). +//! executable* ([`Prot`](crate::mm::policy::Prot)). use core::sync::atomic::{AtomicU64, Ordering}; diff --git a/kernel/src/arch/x86_64/hpet.rs b/kernel/src/arch/x86_64/hpet.rs index 5a49311540a..76e8a704fef 100644 --- a/kernel/src/arch/x86_64/hpet.rs +++ b/kernel/src/arch/x86_64/hpet.rs @@ -2,7 +2,7 @@ //! stated rate every part can be trusted for, so the boot measures it. use crate::log; -use crate::mm::paging::MmioPolicy; +use crate::mm::policy::MmioPolicy; use crate::time::{Delay, Duration}; use super::cpu; diff --git a/kernel/src/arch/x86_64/ioapic.rs b/kernel/src/arch/x86_64/ioapic.rs index fdfd4aa9de1..b765e84cc8f 100644 --- a/kernel/src/arch/x86_64/ioapic.rs +++ b/kernel/src/arch/x86_64/ioapic.rs @@ -12,7 +12,7 @@ use alloc::vec::Vec; use core::fmt::Write; use crate::iommu::Delivery; -use crate::mm::paging::MmioPolicy; +use crate::mm::policy::MmioPolicy; use crate::log; use crate::mm::Mmio; use crate::sync::Lock; diff --git a/kernel/src/arch/x86_64/mtrr.rs b/kernel/src/arch/x86_64/mtrr.rs index 7d25220f4ce..a7abe1011ea 100644 --- a/kernel/src/arch/x86_64/mtrr.rs +++ b/kernel/src/arch/x86_64/mtrr.rs @@ -1,7 +1,7 @@ //! What memory type firmware gave a physical range. //! //! Read-only: firmware owns these registers, the kernel programs none. A -//! mapping with [`CachePolicy::Normal`](crate::mm::paging::CachePolicy) +//! mapping with [`CachePolicy::Normal`](crate::mm::policy::CachePolicy) //! selects PAT entry 0 (WB), so what this module reports is the effective //! type; the exception is [`effective_under_wc`], where WC outvotes the MTRR //! instead of deferring to it. diff --git a/kernel/src/arch/x86_64/paging.rs b/kernel/src/arch/x86_64/paging.rs index d9f00b95bd0..33657f357fa 100644 --- a/kernel/src/arch/x86_64/paging.rs +++ b/kernel/src/arch/x86_64/paging.rs @@ -15,7 +15,7 @@ use crate::hasher::HashMap; use toyos_pcid::{Alloc, Pcid, PcidPool}; use crate::mm::{UserAddr, PAGE_2M}; -pub use crate::mm::policy::{CachePolicy, MmioPolicy, Prot, WindowProt}; +use crate::mm::policy::{CachePolicy, MmioPolicy, Prot, WindowProt}; use crate::arch::control_regs::PcidActive; use crate::arch::cpu::Invpcid; use crate::sync::Lock; @@ -277,7 +277,7 @@ pub fn flush_tlb_all() { pub struct Cr3(u64); /// The address space a CPU runs in, as the architecture names its root: CR3. -pub type Root = Cr3; +pub use Cr3 as Root; impl Cr3 { pub fn current() -> Self { @@ -352,7 +352,7 @@ fn alloc_pcid() -> Option { match pool.alloc() { Alloc::Ready(p) => return Some(PcidGuard(p)), Alloc::NeedsFlush => { - crate::arch::tlb::shootdown(crate::arch::tlb::Origin::Pcid); + crate::arch::tlb::shootdown(crate::invalidation::Origin::Pcid); pool.reclaim(); } Alloc::Exhausted => return None, @@ -889,7 +889,7 @@ pub fn load_kernel_flush() { /// sibling's stale entry, which is SDM Vol. 3A §11.12.4 undefined behaviour. pub fn map_mmio(phys: u64, size: u64, policy: MmioPolicy) -> crate::mm::Mmio { let mmio = kernel().lock().map_mmio(phys, size, policy.cache()); - crate::arch::tlb::shootdown(crate::arch::tlb::Origin::Mmio); + crate::arch::tlb::shootdown(crate::invalidation::Origin::Mmio); // Read back off the table and logged beside firmware's MTRR verdict: the // boot's own evidence that no register window trusts firmware. let installed = diff --git a/kernel/src/arch/x86_64/tlb.rs b/kernel/src/arch/x86_64/tlb.rs index 1a4228c57bc..df1ecc0ddcd 100644 --- a/kernel/src/arch/x86_64/tlb.rs +++ b/kernel/src/arch/x86_64/tlb.rs @@ -13,13 +13,12 @@ use core::sync::atomic::{AtomicU64, Ordering}; use crate::shootdown::{Generation, Shootdown}; -use crate::time::{Duration, Tripwire}; use super::{apic, percpu, smp}; static SHOOTDOWN: Shootdown = Shootdown::new(); -pub use crate::invalidation::Origin; +use crate::invalidation::Origin; /// Issuer-side census; `irq_census`'s `tlb` column is the receiver side, and a /// delivery the two disagree on is an uncounted issuing path. @@ -58,18 +57,6 @@ pub fn log_census() { ); } -/// Set above xHCI's `CALL_AFTER_BREAK`, the longest a disk call spins with `IF` -/// clear once its transport has broken, so no legitimate wait trips it; that -/// assertion below holds the order. -const ACK_TIMEOUT: Tripwire = Tripwire::absurd( - Duration::from_secs(5), - "above the longest IF-clear device spin a target can be inside", -); - -// A disk call spins with interrupts off, so one that outlasted this tripwire -// would panic another CPU over a device. -const _: () = assert!(crate::drivers::xhci::CALL_AFTER_BREAK.nanos() < ACK_TIMEOUT.nanos()); - /// Spins between deadline checks; `nanos_since_boot`'s 128-bit divide is too /// costly to call on every iteration. const SPINS_PER_DEADLINE_CHECK: u32 = 1024; @@ -147,11 +134,11 @@ fn wait_for(me: usize, cpu: u32, generation: Generation) { spins = 0; let now = crate::clock::nanos_since_boot(); match deadline { - None => deadline = Some(now.saturating_add(ACK_TIMEOUT.nanos())), + None => deadline = Some(now.saturating_add(crate::time::DEAF_CPU.nanos())), Some(at) if now >= at => panic!( "tlb: cpu {cpu} has not flushed for generation {generation:?} in {}ns — \ it is not taking interrupts", - ACK_TIMEOUT.nanos(), + crate::time::DEAF_CPU.nanos(), ), Some(_) => {} } diff --git a/kernel/src/arch/x86_64/vtd/mod.rs b/kernel/src/arch/x86_64/vtd/mod.rs index aa589e0e576..4c242f376d5 100644 --- a/kernel/src/arch/x86_64/vtd/mod.rs +++ b/kernel/src/arch/x86_64/vtd/mod.rs @@ -21,7 +21,7 @@ use alloc::vec::Vec; use crate::drivers::acpi::TableError; use crate::drivers::pci::PciDevice; use crate::iommu::{AddressWidth, StreamId}; -use crate::mm::paging::MmioPolicy; +use crate::mm::policy::MmioPolicy; use crate::mm::Mmio; use crate::sync::Lock; use crate::time::{Duration, Tripwire}; diff --git a/kernel/src/clock.rs b/kernel/src/clock.rs index f91c7e43e80..439b8c83a4d 100644 --- a/kernel/src/clock.rs +++ b/kernel/src/clock.rs @@ -53,7 +53,7 @@ fn publish_page(counter_at_boot: u64, period_fs: u64) { /// the ABI names. A region of its own, so no `mmap` can land on it and a /// fault in it is refused rather than filled. pub fn map_page(space: &mut crate::mm::paging::AddressSpace) { - use crate::mm::paging::{CachePolicy, Prot}; + use crate::mm::policy::{CachePolicy, Prot}; let phys = PAGE_PHYS.load(Acquire); assert!(phys != 0, "clock: an address space was built before the clock page"); let at = crate::UserAddr::new(toyos_abi::clock::CLOCK_PAGE); diff --git a/kernel/src/drivers/gop.rs b/kernel/src/drivers/gop.rs index 0f992815ca3..1eb49d28318 100644 --- a/kernel/src/drivers/gop.rs +++ b/kernel/src/drivers/gop.rs @@ -2,7 +2,7 @@ use alloc::boxed::Box; use toyos_abi::syscall::SyscallError; -use crate::mm::paging::{CachePolicy, MmioPolicy}; +use crate::mm::policy::{CachePolicy, MmioPolicy}; use crate::mm::{PAGE_2M, align_2m_checked, DirectMap}; use crate::gpu::{Gpu, GpuInfo}; use crate::log; diff --git a/kernel/src/drivers/hda.rs b/kernel/src/drivers/hda.rs index daf8f95b3d6..f8676aa5a4f 100644 --- a/kernel/src/drivers/hda.rs +++ b/kernel/src/drivers/hda.rs @@ -16,7 +16,7 @@ use toyos_hda::stream; use super::pci::PciDevice; use crate::log; -use crate::mm::paging::{CachePolicy, MmioPolicy}; +use crate::mm::policy::{CachePolicy, MmioPolicy}; use crate::mm::Mmio; use crate::object::shm::Region; use crate::sync::Lock; diff --git a/kernel/src/drivers/nvme.rs b/kernel/src/drivers/nvme.rs index b8674aa532d..470bdd0cd9e 100644 --- a/kernel/src/drivers/nvme.rs +++ b/kernel/src/drivers/nvme.rs @@ -13,7 +13,7 @@ use crate::mm::Mmio; use super::pci::PciDevice; use super::DmaPool; use crate::block::{self, BlockDevice, BlockError, BlockResult, DeviceId}; -use crate::mm::paging::MmioPolicy; +use crate::mm::policy::MmioPolicy; use crate::log; use crate::mm::{Dma, Unaligned}; use crate::scheduler::Operation; diff --git a/kernel/src/drivers/panic_console/mod.rs b/kernel/src/drivers/panic_console/mod.rs index 71ed2178f64..e25301c6456 100644 --- a/kernel/src/drivers/panic_console/mod.rs +++ b/kernel/src/drivers/panic_console/mod.rs @@ -24,7 +24,7 @@ use toyos_ps2::{KeyDecoder, KeyOutcome}; use crate::log; use crate::panic_reboot::Bound; use crate::time::{Budget, Cadence, Duration}; -use crate::mm::paging::MmioPolicy; +use crate::mm::policy::MmioPolicy; use crate::mm::{self, DirectMap, align_2m}; /// 1 bpp 8x16, codepoints 0x20..=0x7E, one byte per row, bit 7 leftmost. diff --git a/kernel/src/drivers/pci.rs b/kernel/src/drivers/pci.rs index 0deb9bf9a4c..897b341f94a 100644 --- a/kernel/src/drivers/pci.rs +++ b/kernel/src/drivers/pci.rs @@ -5,7 +5,7 @@ use core::sync::atomic::{AtomicU32, Ordering}; use toyos_pci::{bar, bridge, caps, msi, msix}; use crate::mm::Mmio; -use crate::mm::paging::MmioPolicy; +use crate::mm::policy::MmioPolicy; use crate::log; const VENDOR_ID: u64 = 0x00; diff --git a/kernel/src/drivers/virtio.rs b/kernel/src/drivers/virtio.rs index f3bd735bf38..5af06658097 100644 --- a/kernel/src/drivers/virtio.rs +++ b/kernel/src/drivers/virtio.rs @@ -4,7 +4,7 @@ use toyos_untrusted::{Refused, Untrusted}; use crate::mm::Mmio; use super::pci::PciDevice; -use crate::mm::paging::MmioPolicy; +use crate::mm::policy::MmioPolicy; use crate::log; const VIRTIO_PCI_CAP_COMMON_CFG: u8 = 1; diff --git a/kernel/src/drivers/virtio_gpu.rs b/kernel/src/drivers/virtio_gpu.rs index d81f73c42cb..74b12a434f2 100644 --- a/kernel/src/drivers/virtio_gpu.rs +++ b/kernel/src/drivers/virtio_gpu.rs @@ -8,7 +8,7 @@ use crate::iommu::{DeviceSpace, IommuError}; use crate::mm::{Dma, Unaligned, PAGE_2M}; use crate::gpu::{FLAG_HARDWARE_CURSOR, Gpu, GpuInfo}; use crate::log; -use crate::mm::paging::CachePolicy; +use crate::mm::policy::CachePolicy; use crate::object::shm::{Pages, Region}; const VIRTIO_VENDOR: u16 = 0x1AF4; diff --git a/kernel/src/drivers/virtio_sound.rs b/kernel/src/drivers/virtio_sound.rs index befdb9b15d8..f4787219c49 100644 --- a/kernel/src/drivers/virtio_sound.rs +++ b/kernel/src/drivers/virtio_sound.rs @@ -19,7 +19,7 @@ use super::virtio::{BufDir, UsedRingConsumer, VirtioDevice, Virtqueue, Virtqueue VIRTIO_F_VERSION_1}; use super::DmaPool; use crate::log; -use crate::mm::paging::CachePolicy; +use crate::mm::policy::CachePolicy; use crate::mm::{Dma, Mmio}; use crate::object::shm::Region; use crate::sync::Lock; diff --git a/kernel/src/drivers/xhci/mod.rs b/kernel/src/drivers/xhci/mod.rs index f5c2efb8bea..c9b52fd727a 100644 --- a/kernel/src/drivers/xhci/mod.rs +++ b/kernel/src/drivers/xhci/mod.rs @@ -316,11 +316,15 @@ pub(crate) const AFTER_BREAK: toyos_xhci::call::Bounds = toyos_xhci::call::AFTER const _: () = assert!(AFTER_BREAK.wait == USB_TIMEOUT_NS); /// Everything one disk call may spin for from the start of the wait its transport broke on. -pub(crate) const CALL_AFTER_BREAK: crate::time::Budget = crate::time::Budget::of( +const CALL_AFTER_BREAK: crate::time::Budget = crate::time::Budget::of( crate::time::Duration::from_nanos(AFTER_BREAK.whole()), "every wait is clipped to where the rungs still ahead of it begin, so the last rung runs whatever was spent before it and the call ends here", ); +// A disk call spins with interrupts off, so one that outlasted this tripwire +// would panic another CPU over a device. +const _: () = assert!(CALL_AFTER_BREAK.nanos() < crate::time::DEAF_CPU.nanos()); + // A disk whose device left under the port rung's reset is waited for no longer than the rungs from that reset on were given to spend on it. const _: () = assert!( diff --git a/kernel/src/drivers/xhci/wait/boot.rs b/kernel/src/drivers/xhci/wait/boot.rs index e815cff2004..493b58fb362 100644 --- a/kernel/src/drivers/xhci/wait/boot.rs +++ b/kernel/src/drivers/xhci/wait/boot.rs @@ -6,7 +6,7 @@ use core::sync::atomic::Ordering; use crate::log; use crate::time::{Budget, Cadence, Duration}; -use crate::mm::paging::MmioPolicy; +use crate::mm::policy::MmioPolicy; use crate::mm::Mmio; use crate::drivers::pci::PciDevice; use crate::drivers::DmaPool; diff --git a/kernel/src/drivers/xhci/wait/msc.rs b/kernel/src/drivers/xhci/wait/msc.rs index 19bce936a87..8f1fe5421b7 100644 --- a/kernel/src/drivers/xhci/wait/msc.rs +++ b/kernel/src/drivers/xhci/wait/msc.rs @@ -2526,7 +2526,7 @@ pub fn storage_flush(index: usize, losses: &mut u64) -> BlockResult { /// (`toyos_xhci::call`): opened by the first break or by finding the disk /// held, carried across every command, the hold and the command sent again, /// and closed here. The caller spins with `IF` clear for all of it, which is -/// why `CALL_AFTER_BREAK` is held under the TLB-ack tripwire. +/// why `CALL_AFTER_BREAK` is held under `time::DEAF_CPU`, the TLB-ack tripwire. /// /// **The hold only waits for a verdict.** It ends at the disk's own window /// (`toyos_xhci::identity::RETURN_WINDOW`), past which the disk is lost and the diff --git a/kernel/src/elf/mod.rs b/kernel/src/elf/mod.rs index 7de691c4777..9df1d73a336 100644 --- a/kernel/src/elf/mod.rs +++ b/kernel/src/elf/mod.rs @@ -116,8 +116,8 @@ impl LoadedLib { /// Protection for the page at `offset`: exec below the writable window, /// write inside it, read-only above — over-permissive, never under, for /// an unusual segment layout. - fn page_prot(&self, offset: u64) -> crate::mm::paging::Prot { - use crate::mm::paging::Prot; + fn page_prot(&self, offset: u64) -> crate::mm::policy::Prot { + use crate::mm::policy::Prot; if offset < self.rw_lo { Prot::ReadExec } else if offset < self.rw_hi { @@ -133,7 +133,7 @@ impl LoadedLib { /// A `Shared` module's split window holds a shared tail of `.text` plus /// the private copy, byte-identical there, so `ReadExec` is safe over either. pub fn map_into(&self, pt: &crate::process::PageTables) -> Option { - use crate::mm::paging::WindowProt; + use crate::mm::policy::WindowProt; let (image_phys, image_size) = match &self.memory { LibMemory::Owned(alloc) => ( @@ -163,7 +163,7 @@ impl LoadedLib { } } }; - let mut prot = WindowProt::uniform(crate::mm::paging::Prot::Read); + let mut prot = WindowProt::uniform(crate::mm::policy::Prot::Read); let mut page = 0; while page < PAGE_2M { prot.set(page, self.page_prot(offset + page)); diff --git a/kernel/src/loader/mod.rs b/kernel/src/loader/mod.rs index 4e1230eacc1..ddd821de37b 100644 --- a/kernel/src/loader/mod.rs +++ b/kernel/src/loader/mod.rs @@ -26,7 +26,7 @@ use alloc::vec::Vec; use crate::elf; use crate::object::{ops, HandleTable, KObjectRef}; -use crate::mm::paging::{CachePolicy, Prot}; +use crate::mm::policy::{CachePolicy, Prot}; use crate::mm::{PAGE_2M, PAGE_BYTES}; use crate::process::{ ElfInfo, Endowments, OwnedAlloc, PageAlloc, PageFaultTrace, PageTables, Pid, @@ -169,8 +169,8 @@ fn insert_elf_regions( /// /// `PF_W | PF_X` refuses the write, not the execution: taking `X` away would /// let a hostile ELF run as data instead. -fn segment_prot(seg: &toyos_elf::Segment) -> crate::mm::paging::Prot { - use crate::mm::paging::Prot; +fn segment_prot(seg: &toyos_elf::Segment) -> crate::mm::policy::Prot { + use crate::mm::policy::Prot; if seg.flags().executable() { Prot::ReadExec } else if seg.flags().writable() { diff --git a/kernel/src/loader/tls.rs b/kernel/src/loader/tls.rs index b91085263dd..466738d9af1 100644 --- a/kernel/src/loader/tls.rs +++ b/kernel/src/loader/tls.rs @@ -49,7 +49,7 @@ impl TlsBlock { /// when `pt` has no room. pub fn publish(self, pt: &PageTables) -> Option<(MappedPages, u64, usize)> { let tp_offset = self.tp_offset; - let pages = self.frames.publish(pt, crate::mm::paging::Prot::ReadWrite, |frames, at| { + let pages = self.frames.publish(pt, crate::mm::policy::Prot::ReadWrite, |frames, at| { if crate::actuator::tls_rebase_window() { rebase_window::hold(frames, at); } diff --git a/kernel/src/main.rs b/kernel/src/main.rs index bada52f0578..d6a9722b5e5 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -111,7 +111,7 @@ mod late_panic { } } -use crate::mm::paging::MmioPolicy; +use crate::mm::policy::MmioPolicy; use alloc::boxed::Box; use alloc::sync::Arc; use arch::{cpu, percpu, smp}; diff --git a/kernel/src/mm/unmapped.rs b/kernel/src/mm/unmapped.rs index 88807027927..9a07826f637 100644 --- a/kernel/src/mm/unmapped.rs +++ b/kernel/src/mm/unmapped.rs @@ -16,7 +16,7 @@ impl Unmapped { impl Drop for Unmapped { fn drop(&mut self) { - crate::arch::tlb::shootdown(crate::arch::tlb::Origin::Unmap); + crate::arch::tlb::shootdown(crate::invalidation::Origin::Unmap); // SAFETY: the wrapped value is never taken before this drop. unsafe { ManuallyDrop::drop(&mut self.0) }; } diff --git a/kernel/src/object/shm.rs b/kernel/src/object/shm.rs index a72186ba921..581ce8fc13e 100644 --- a/kernel/src/object/shm.rs +++ b/kernel/src/object/shm.rs @@ -9,7 +9,7 @@ use alloc::vec::Vec; use toyos_abi::syscall::SyscallError; -use crate::mm::paging::{CachePolicy, Prot}; +use crate::mm::policy::{CachePolicy, Prot}; use crate::mm::{align_2m_checked, pmm, Unmapped, PAGE_2M}; use crate::process::{PageTables, Pid}; use crate::sync::Lock; diff --git a/kernel/src/pcidev/mod.rs b/kernel/src/pcidev/mod.rs index 498c99ffa3e..404eecd2767 100644 --- a/kernel/src/pcidev/mod.rs +++ b/kernel/src/pcidev/mod.rs @@ -113,7 +113,7 @@ use toyos_pci::{af, aperture, bar, express, msix, placement, pm, probe}; use crate::device::{Claim, ClaimError}; use crate::drivers::pci::{NoCapability, PciDevice, Unarmed}; use crate::iommu::{DeviceSpace, IommuError}; -use crate::mm::paging::{CachePolicy, MmioPolicy}; +use crate::mm::policy::{CachePolicy, MmioPolicy}; use crate::mm::{align_2m, DirectMap, Mmio, PAGE_2M}; use crate::object::shm::{Region, SharedMemObject}; use crate::sync::Lock; diff --git a/kernel/src/process.rs b/kernel/src/process.rs index b89fa88ae70..75758bec2d8 100644 --- a/kernel/src/process.rs +++ b/kernel/src/process.rs @@ -13,7 +13,7 @@ use alloc::sync::Arc; use alloc::vec::Vec; use core::ptr::NonNull; use crate::arch::percpu; -use crate::mm::paging::{CachePolicy, Prot, WindowProt}; +use crate::mm::policy::{CachePolicy, Prot, WindowProt}; use crate::mm::{PAGE_2M, PAGE_BYTES}; use crate::object::{ops, HandleTable}; use crate::sync::Lock; @@ -635,7 +635,7 @@ pub fn revoke_pipe_maps(maps: &mut Vec, pt: &PageTables, pipe: pipe::Pi }); } // Outside the block: it waits, and a sibling can be spinning on this lock with IF clear. - crate::arch::tlb::shootdown(crate::arch::tlb::Origin::Pipe); + crate::arch::tlb::shootdown(crate::invalidation::Origin::Pipe); } /// One live `mmap` and its physical pages; the range's registration in the address space's `regions` is separate (placement search, `munmap`). diff --git a/kernel/src/syscall/ipc.rs b/kernel/src/syscall/ipc.rs index 91dc54d9422..b0b573e77fc 100644 --- a/kernel/src/syscall/ipc.rs +++ b/kernel/src/syscall/ipc.rs @@ -8,7 +8,7 @@ use alloc::vec::Vec; use crate::watch; -use crate::mm::paging::Prot; +use crate::mm::policy::Prot; use crate::object::{ops, port, KObjectRef}; use crate::time::Deadline; use crate::user_ptr::SyscallContext; diff --git a/kernel/src/syscall/vm.rs b/kernel/src/syscall/vm.rs index 43a646cbb62..fb52c5345a4 100644 --- a/kernel/src/syscall/vm.rs +++ b/kernel/src/syscall/vm.rs @@ -12,7 +12,7 @@ //! shoots down and waits, and a sibling thread can be spinning on that same //! lock with `IF` clear. -use crate::mm::paging::{CachePolicy, Prot}; +use crate::mm::policy::{CachePolicy, Prot}; use crate::vma::Occupancy; use crate::user_ptr::UserBytesMut; use crate::UserAddr; @@ -260,7 +260,7 @@ pub(super) fn sys_dlopen(ctx: &crate::user_ptr::SyscallContext, path: &str, init // `map_window`'s shootdown reached only this CPU, so the rest of the // machine is told here. if matches!(lib.memory, crate::elf::LibMemory::Shared { .. }) { - crate::arch::tlb::shootdown(crate::arch::tlb::Origin::Dlopen); + crate::arch::tlb::shootdown(crate::invalidation::Origin::Dlopen); } if vaddr != lib.user_base { lib.user_base = vaddr; @@ -283,7 +283,7 @@ pub(super) fn sys_dlopen(ctx: &crate::user_ptr::SyscallContext, path: &str, init process::with_process_data(|_data| { pt.lock().free_and_unmap(base); }); - crate::arch::tlb::shootdown(crate::arch::tlb::Origin::Dlopen); + crate::arch::tlb::shootdown(crate::invalidation::Origin::Dlopen); }); let lib_tls = lib.tls().and_then(toyos_elf::TlsSegment::occupied); diff --git a/kernel/src/time.rs b/kernel/src/time.rs index 85cf1c1479a..1c62f9c7ada 100644 --- a/kernel/src/time.rs +++ b/kernel/src/time.rs @@ -217,6 +217,13 @@ impl fmt::Display for Tripwire { } } +/// How long a CPU waits for another to take an interrupt before calling it +/// deaf and panicking — the TLB shootdown's acknowledgement wait is this. +pub const DEAF_CPU: Tripwire = Tripwire::absurd( + Duration::from_secs(5), + "no CPU that is not wedged goes five seconds without taking an interrupt", +); + /// A wall-clock allowance whose expiry is a **degraded answer**, never a panic. #[derive(Clone, Copy)] pub struct Budget { diff --git a/kernel/src/vma.rs b/kernel/src/vma.rs index bf2618eb101..38903b6b58f 100644 --- a/kernel/src/vma.rs +++ b/kernel/src/vma.rs @@ -1,7 +1,7 @@ use alloc::sync::Arc; use crate::file_backing::FileBacking; -use crate::mm::paging::Prot; +use crate::mm::policy::Prot; use crate::mm::PAGE_2M; use toyos_userbound::Window; diff --git a/src/CLAUDE.md b/src/CLAUDE.md index 82b6c66c417..c84ab85686f 100644 --- a/src/CLAUDE.md +++ b/src/CLAUDE.md @@ -19,7 +19,7 @@ Loads when you read a file under `src/` — the root cargo project, package name ## The host's locks and slots -- **Sysroots are content-addressed** (`src/sysroot.rs`): one per key — the identity (`src/identity.rs`, so a comment is no change) of `toyos-abi/src`, `toyos/src`, `userland/libc/src` and their manifests, the std fork's `library/` and `src/bootstrap/`, and the compiler — at `rust/build/sysroots//`, made by whichever worktree first needs it and never written again. Every build compiles against its own key's, so two worktrees with different ABIs never refuse or wait for each other; the only shared step is the primary's compiler, which a sysroot build reads under the global lock in shared mode. A new key costs one std build of the three guest targets; `--worktree remove` sweeps the keys no worktree records. +- **Sysroots are content-addressed** (`src/sysroot.rs`): one per key — the identity (`src/identity.rs`, so a comment is no change) of `toyos-abi/src`, `toyos/src`, `userland/libc/src` and their manifests, the std fork's `rust/library/` and `rust/src/bootstrap/`, and the compiler — at `rust/build/sysroots//`, made by whichever worktree first needs it and never written again. Every build compiles against its own key's, so two worktrees with different ABIs never refuse or wait for each other; the only shared step is the primary's compiler, which a sysroot build reads under the global lock in shared mode. A new key costs one std build of the three guest targets; `--worktree remove` sweeps the keys no worktree records. - **The std fork is built per worktree, and nothing but the primary's own sync moves the primary's `rust/`.** A linked worktree's `rust/` becomes, on its first build, a git worktree of the primary's fork repository at the commit its tree pins — that is where the fork is edited, committed and pinned. A worktree whose fork `compiler/` differs from the one the primary built builds its own compiler, keyed by that source and placed beside the primary's without touching it. If that checkout later falls behind the commit its tree pins (a merge moved the pin), the build moves the checkout to it itself, fetching from the primary's repository first if it holds the commit, unless the checkout has local changes, which it refuses to move out from under. - `src/buildlock.rs` serialises the stateful phases in two scopes: `Global` (the primary's compiler and the rustup link — one directory in `.git/`, shared by every worktree) and `Worktree` (the crate-target cleans, and this worktree's std build). Only `./x.py` typed by hand in `rust/` escapes it. - **Never kill a build that has taken the global lock** — the kill removes the shell wrappers, not the bootstrap, which inherits the file descriptor and runs on regardless; a toolchain rebuild interrupted or unobserved this way can leave `stage2/bin` without a `cargo`. @@ -33,7 +33,6 @@ Loads when you read a file under `src/` — the root cargo project, package name ## Caveats that bite every agent -- **Documentation carries no gates** — `src/redlist.rs` resolves doc paths only because it gates a Rust table, not a corpus. - **Every CI lane is GitHub-hosted and no workflow may name a self-hosted label** — a `runs-on:` naming one queues until it times out rather than failing, so `src/ci.rs`'s `workflows_run_against_main_on_hosted_runners` refuses it; a measurement owed on hardware goes to the metal loop, not to a runner. - **A workflow job that runs in a container adds `safe.directory` itself** — `actions/checkout` sets it into a temporary global config it discards when its step ends, so the first git command a container step runs after checkout dies on a dubiously-owned repository. - **A `stage1-std//dist/deps` temp-dir error means a concurrent build**, never a broken checkout; never repair or force-rebuild the toolchain. diff --git a/src/assets.rs b/src/assets.rs index cdc102447e6..759ad2719f7 100644 --- a/src/assets.rs +++ b/src/assets.rs @@ -1,7 +1,6 @@ use std::collections::BTreeSet; use std::fs; use std::path::{Path, PathBuf}; -use std::process::Command; /// Rasterize `codepoints` into `cell_width * cell_height` 8-bit alpha cells, /// laid out one cell after another. The pixel size is the largest at which @@ -216,21 +215,7 @@ pub fn regen_panic_font(root: &Path) { /// again. A build that cannot find out what is committed refuses, because it /// cannot honestly build an image either. fn tracked(dir: &Path) -> BTreeSet { - let out = Command::new("git") - .args(["-C", &dir.display().to_string(), "ls-files", "-z"]) - .output() - .unwrap_or_else(|e| panic!("asking git what it tracks under {}: {e}", dir.display())); - assert!( - out.status.success(), - "git could not list {}: {}", - dir.display(), - String::from_utf8_lossy(&out.stderr).trim() - ); - String::from_utf8_lossy(&out.stdout) - .split('\0') - .filter(|line| !line.is_empty()) - .map(PathBuf::from) - .collect() + crate::sysroot::tracked_files(dir, &[]).unwrap_or_else(|e| panic!("{e}")).into_iter().map(PathBuf::from).collect() } /// The paths `declared` names under `dir` that are not there, in the order they @@ -367,6 +352,7 @@ pub fn collect(dirs: &[String], programs: &BTreeSet<&str>) -> Vec<(String, Vec, found: &BTreeSet) -> Vec Result, &'static str> { + let doc: toml::Value = manifest.parse().expect("a manifest is TOML"); + let Some(package) = doc.get("package") else { return Ok(None) }; + match package.get("description").and_then(|d| d.as_str()).map(str::trim) { + Some(d) if !d.is_empty() => Ok(Some(d.to_string())), + Some(_) => Err("a blank `description`"), + None => Err("no `description`"), + } +} + /// The tables in `manifest` that cargo reads only from a workspace root. /// /// Parsed as TOML and not scanned as text, for `src/sourcegate.rs`'s reason: @@ -435,6 +451,41 @@ mod tests { ); } + /// **Every package the `[workspace]` table names says what it is.** Root + /// `CLAUDE.md` points here instead of listing the crates, so a crate that + /// arrives without a `description` is one nothing describes. + #[test] + fn every_package_the_workspace_names_has_a_description() { + let root = repo_root(); + let mut missing = Vec::new(); + let mut described = 0; + for dir in members(&root).into_iter().chain(excluded(&root)) { + let Ok(text) = std::fs::read_to_string(root.join(&dir).join("Cargo.toml")) else { continue }; + match description(&text) { + Ok(Some(_)) => described += 1, + Ok(None) => {} + Err(why) => missing.push(format!("{dir}/Cargo.toml has {why}")), + } + } + assert!(described > 40, "only {described} packages read; the walk is reading no workspace"); + assert!( + missing.is_empty(), + "a package the root Cargo.toml names says what it is in one line of its own \ + [package] `description`:\n {}", + missing.join("\n "), + ); + } + + /// Teeth for the rule above. + #[test] + fn a_package_without_a_description_is_refused_and_a_virtual_workspace_is_not() { + assert_eq!(description("[package]\nname = \"a\"\ndescription = \"An a.\"\n"), Ok(Some("An a.".into()))); + assert_eq!(description("[package]\nname = \"a\"\n"), Err("no `description`")); + assert_eq!(description("[package]\nname = \"a\"\ndescription = \" \"\n"), Err("a blank `description`")); + assert_eq!(description("# description = \"x\"\n[package]\nname = \"a\"\n"), Err("no `description`")); + assert_eq!(description("[workspace]\nmembers = [\"a\"]\n"), Ok(None)); + } + /// Cargo reads `[profile]` and `[patch]` from the workspace root and /// **silently ignores both in a member** — it warns, into output nobody /// reads on a green build. For `toyos-ld` and `toyos-cc` that is not diff --git a/src/licence.rs b/src/licence.rs index 45753e3158e..a8dc1c73b9f 100644 --- a/src/licence.rs +++ b/src/licence.rs @@ -1151,22 +1151,6 @@ fn metadata( serde_json::from_slice(&out).map_err(|e| format!("cargo metadata printed no JSON: {e}")) } -/// The tracked files `pathspecs` name, root-relative. -fn ls_files(root: &Path, pathspecs: &[String]) -> Result, String> { - let out = run( - Command::new("git") - .args(["ls-files", "-z", "--"]) - .args(pathspecs) - .current_dir(root), - "git ls-files", - )?; - Ok(String::from_utf8_lossy(&out) - .split('\0') - .filter(|f| !f.is_empty()) - .map(String::from) - .collect()) -} - /// The fork's `library/`, checked out at the commit this tree pins. A checkout /// whose `rust/` was never initialised — a CI runner's — fetches that commit /// alone. @@ -1264,7 +1248,7 @@ pub fn judge(root: &Path) -> Result { packages: local.dirs.iter().filter_map(|d| relative(d)).filter(|d| !d.is_empty()).collect(), named: BTreeSet::new(), }; - let tracked = ls_files(root, &[])?; + let tracked = crate::sysroot::tracked_files(root, &[])?; let names: BTreeSet<&str> = COMMITTED_FILES.iter().map(|(p, ..)| file_name(p)).collect(); let sources = tracked.iter().filter(|f| under(&shipping.packages, f) && f.ends_with(".rs")); let read = sources @@ -1282,7 +1266,7 @@ pub fn judge(root: &Path) -> Result { let sections = sections(¬ice); let mut files = BTreeMap::new(); for section in §ions { - let named = ls_files(root, &[format!(":(glob){}", section.path)])?; + let named = crate::sysroot::tracked_files(root, &[&format!(":(glob){}", section.path)])?; files.insert(section.path.clone(), named); } judge_notice(§ions, &files, COMMITTED_FILES, &shipping, &mut report); diff --git a/src/sdkversion.rs b/src/sdkversion.rs index c3a6e016873..586585f21b1 100644 --- a/src/sdkversion.rs +++ b/src/sdkversion.rs @@ -300,8 +300,8 @@ mod tests { fn every_lockfile_resolves_the_published_crates_from_the_tree() { let root = Path::new(env!("CARGO_MANIFEST_DIR")); let mut seen = 0; - for lockfile in crate::pr::git(root, &["ls-files", "*Cargo.lock"]).unwrap().lines() { - let lock: toml::Table = std::fs::read_to_string(root.join(lockfile)).unwrap().parse().unwrap(); + for lockfile in crate::sysroot::tracked_files(root, &["*Cargo.lock"]).unwrap() { + let lock: toml::Table = std::fs::read_to_string(root.join(&lockfile)).unwrap().parse().unwrap(); for package in lock.get("package").and_then(|p| p.as_array()).into_iter().flatten() { let name = package["name"].as_str().unwrap(); if PUBLISHED.iter().any(|k| k.name == name) { diff --git a/src/sourcegate.rs b/src/sourcegate.rs index ba7fdb0624a..f328ac9ed43 100644 --- a/src/sourcegate.rs +++ b/src/sourcegate.rs @@ -1225,24 +1225,6 @@ fn used_actions(text: &str) -> Vec<(String, usize)> { .collect() } -/// Every `.rs` file `git` tracks under `tree`, repository-relative — the list -/// the walks above are held against, read from something that is not a walk. -#[cfg(test)] -fn tracked_rust_files(root: &Path, tree: &str) -> std::collections::BTreeSet { - let out = std::process::Command::new("git") - .args(["ls-files", "-z", "--", tree]) - .current_dir(root) - .output() - .unwrap_or_else(|e| panic!("git ls-files {tree}: {e}")); - assert!(out.status.success(), "git ls-files {tree} failed"); - String::from_utf8(out.stdout) - .expect("git ls-files is not UTF-8") - .split('\0') - .filter(|p| p.ends_with(".rs")) - .map(str::to_string) - .collect() -} - // ── Architecture rules ────────────────────────────────────────────────────── /// One architecture rule, stated as where its spellings may appear: a set of @@ -2058,7 +2040,8 @@ mod tests { rust_files(&root.join(tree), &mut files); let walked: std::collections::BTreeSet = files.iter().map(|p| rel(&root, p)).collect(); - let tracked = tracked_rust_files(&root, tree); + let tracked: std::collections::BTreeSet = + crate::sysroot::tracked_files(&root, &[tree]).unwrap_or_else(|e| panic!("{e}")).into_iter().filter(|p| p.ends_with(".rs")).collect(); assert!( tracked.len() > 1, "git tracks {} .rs file(s) under {tree}, so this floor is not one", @@ -2480,16 +2463,10 @@ mod tests { #[test] fn every_committed_binary_file_is_declared() { let root = repo_root(); - let out = std::process::Command::new("git") - .args(["ls-files", "-z"]) - .current_dir(&root) - .output() - .unwrap_or_else(|e| panic!("git ls-files: {e}")); - assert!(out.status.success(), "git ls-files failed"); - let listing = String::from_utf8(out.stdout).expect("git ls-files is not UTF-8"); + let listing = crate::sysroot::tracked_files(&root, &[]).unwrap_or_else(|e| panic!("{e}")); let mut found: Vec<(String, String)> = Vec::new(); - for name in listing.split('\0').filter(|s| !s.is_empty()) { + for name in &listing { let Ok(bytes) = std::fs::read(root.join(name)) else { continue }; if !is_binary(&bytes) && !name.starts_with("assets/") { continue; @@ -2547,15 +2524,8 @@ mod tests { let licence_path = format!("{CORPUS}/LICENSE"); let licence = std::fs::read_to_string(root.join(&licence_path)) .unwrap_or_else(|e| panic!("{licence_path}: {e}")); - let out = std::process::Command::new("git") - .args(["ls-files", "-z"]) - .current_dir(&root) - .output() - .unwrap_or_else(|e| panic!("git ls-files: {e}")); - assert!(out.status.success(), "git ls-files failed"); - let listing = String::from_utf8(out.stdout).expect("git ls-files is not UTF-8"); - let tracked: Vec<&str> = listing.split('\0').filter(|s| !s.is_empty()).collect(); - let under_corpus: Vec<&&str> = + let tracked = crate::sysroot::tracked_files(&root, &[]).unwrap_or_else(|e| panic!("{e}")); + let under_corpus: Vec<&String> = tracked.iter().filter(|f| f.starts_with(&format!("{CORPUS}/"))).collect(); assert!( under_corpus.len() > CORPUS_OURS.len(), @@ -2578,7 +2548,7 @@ mod tests { } } for file in &under_corpus { - let attributed = CORPUS_OURS.contains(file) + let attributed = CORPUS_OURS.contains(&file.as_str()) || CORPUS_POPULATIONS .iter() .any(|p| file.starts_with(&format!("{CORPUS}/{p}/"))); diff --git a/src/sysroot.rs b/src/sysroot.rs index 5fbc008d8a3..348d3001837 100644 --- a/src/sysroot.rs +++ b/src/sysroot.rs @@ -584,25 +584,44 @@ fn path_str(path: &Path) -> &str { path.to_str().unwrap_or_else(|| panic!("{} is not UTF-8", path.display())) } -pub(crate) fn git_bytes(dir: &Path, args: &[&str]) -> Vec { +/// `Err` names the command, the directory and what git said. +fn git_try(dir: &Path, args: &[&str]) -> Result, String> { let out = Command::new("git") .args(args) .current_dir(dir) .output() - .unwrap_or_else(|e| panic!("run git in {}: {e}", dir.display())); - assert!( - out.status.success(), - "git {args:?} in {}: {}", - dir.display(), - String::from_utf8_lossy(&out.stderr).trim() - ); - out.stdout + .map_err(|e| format!("run git in {}: {e}", dir.display()))?; + if !out.status.success() { + let stderr = String::from_utf8_lossy(&out.stderr); + return Err(format!("git {args:?} in {}: {}", dir.display(), stderr.trim())); + } + Ok(out.stdout) +} + +pub(crate) fn git_bytes(dir: &Path, args: &[&str]) -> Vec { + git_try(dir, args).unwrap_or_else(|e| panic!("{e}")) } pub(crate) fn git_out(dir: &Path, args: &[&str]) -> String { String::from_utf8_lossy(&git_bytes(dir, args)).into_owned() } +/// The files `git` tracks under `dir` that `pathspecs` name, every one when +/// there are none, relative to `dir`. A name that is not UTF-8 is refused by +/// name rather than rewritten into one git does not track. +pub(crate) fn tracked_files(dir: &Path, pathspecs: &[&str]) -> Result, String> { + let args = [&["ls-files", "-z", "--"][..], pathspecs].concat(); + let listing = git_try(dir, &args)?; + let names = listing.split(|b| *b == 0).filter(|f| !f.is_empty()); + names + .map(|f| { + String::from_utf8(f.to_vec()).map_err(|_| { + format!("git tracks {:?} in {}, a name that is not UTF-8", String::from_utf8_lossy(f), dir.display()) + }) + }) + .collect() +} + fn git_run(dir: &Path, args: &[&str]) { let ok = Command::new("git") .args(args) diff --git a/tests/CLAUDE.md b/tests/CLAUDE.md index 6c90d844e88..4e5d5e966cb 100644 --- a/tests/CLAUDE.md +++ b/tests/CLAUDE.md @@ -23,4 +23,4 @@ The mechanics live where the work is: profiles and shapes in `tests/common/`, re - **A stimulus sent through a channel that can silently lose it is verified before its effect is asserted** — QEMU's PS/2 queue drops the seventeenth byte, so typed input paces against the guest's report (`shell_type_once`); a guest's console reaches the host as whole lines only, so a partial line exists on no channel. - **A harness field that can be silently inert is this suite's worst defect class** — where two options can describe the same guest they refuse each other by name, and an image is asked what it is armed with. - **A test whose premise is arranged by a defect passes for the wrong reason** — a staging device's resource has to survive its own enumeration. -- **Host suites** run with plain `cargo test` inside `toyos-sched/`, `toyos-ps2/`, `toyos-gpt/`, `toyos-elf/`, `toyos-cc/`, `toyos-ld/`, `toyos-hda/`, `toyos-pci/`, `toyos-xhci/`, `toyos-desktop/`, `toyos-keymap/`, `bcachefs/`, `kernel-loom/`, `toyos-userbound/`, `toyos-elide/`, `toyos-fat32/`, `toyos-fat32-check/`, `toyos-abi/`, `toyos-manifest/`, `toyos-wallclock/`, `toyos-mixer/`, `toyos-dma/` and `toyos/`; any userland crate is host-testable with the host triple (`cargo test --target "$(rustc -vV | sed -n 's/^host: //p')"`), which is the whole of what `calc` is gated by. `kernel-loom/` and `toyos-sched/loom` are the memory-ordering checks — x86 TSO hides a missing acquire edge from every guest test. +- **Host suites** are the root `Cargo.toml`'s `[workspace]` members, the SDK and every userland crate `src/userlandhost.rs` finds a test in; `src/ci.rs`'s `host` runs them all. `kernel-loom/` and `toyos-sched/loom` are the memory-ordering checks — x86 TSO hides a missing acquire edge from every guest test. diff --git a/tests/metal-profile.toml b/tests/metal-profile.toml index fa0ece05879..c7636091b61 100644 --- a/tests/metal-profile.toml +++ b/tests/metal-profile.toml @@ -131,7 +131,7 @@ measured = 46 # is a floor and not a measurement, which is a red whatever the machine did. The # two shootdown rows are bounded by the reading, half again — the margin this # file uses everywhere a first reading becomes a ceiling — because -# `arch::tlb::ACK_TIMEOUT` is two million times the reading and a ceiling nothing +# `time::DEAF_CPU` is two million times the reading and a ceiling nothing # can reach is a row that cannot red. [[number]] @@ -145,7 +145,7 @@ measured = 16 name = "tlb.latencycase.p50_ns" unit = "ns" ceiling = 3387 -ceiling_from = "2,258 ns measured, and half again — 50 % over one machine's one reading, which is what this file gives every ceiling derived from a first one. `kernel arch::tlb::ACK_TIMEOUT` is 5 s and panics the machine; it bounds the wait and is not a ceiling this row could ever red on" +ceiling_from = "2,258 ns measured, and half again — 50 % over one machine's one reading, which is what this file gives every ceiling derived from a first one. `kernel time::DEAF_CPU` is 5 s and panics the machine; it bounds the wait and is not a ceiling this row could ever red on" measured = 2258 [[number]] diff --git a/toyos-acpi/Cargo.toml b/toyos-acpi/Cargo.toml index bf29d25f0c3..c242e4d6663 100644 --- a/toyos-acpi/Cargo.toml +++ b/toyos-acpi/Cargo.toml @@ -7,6 +7,7 @@ [package] name = "toyos-acpi" +description = "ACPI table decoding over firmware-supplied bytes: a decoded value or a named refusal, never a panic." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-blackbox/Cargo.toml b/toyos-blackbox/Cargo.toml index 3a0d62719f6..ec8ce9c0251 100644 --- a/toyos-blackbox/Cargo.toml +++ b/toyos-blackbox/Cargo.toml @@ -1,5 +1,6 @@ [package] name = "toyos-blackbox" +description = "The page a boot leaves for the next boot to find, and the three things it can say." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-blockhold/Cargo.toml b/toyos-blockhold/Cargo.toml index fc77535926a..347567dec6b 100644 --- a/toyos-blockhold/Cargo.toml +++ b/toyos-blockhold/Cargo.toml @@ -8,6 +8,7 @@ [package] name = "toyos-blockhold" +description = "Who holds each span of a block device, and whose flush answers for the writes its disk lost, pure." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-blockring/Cargo.toml b/toyos-blockring/Cargo.toml index f9cbb2b18ca..b1fdba046d7 100644 --- a/toyos-blockring/Cargo.toml +++ b/toyos-blockring/Cargo.toml @@ -13,6 +13,7 @@ [package] name = "toyos-blockring" +description = "The block protocol between a block service and its client: one shared session of request and completion rings and an arena." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-bootmap/Cargo.toml b/toyos-bootmap/Cargo.toml index 98c8f99ae7a..e54f4983c20 100644 --- a/toyos-bootmap/Cargo.toml +++ b/toyos-bootmap/Cargo.toml @@ -6,6 +6,7 @@ [package] name = "toyos-bootmap" +description = "The bootloader's transient page tables, decided rather than built, pure." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-cc/Cargo.toml b/toyos-cc/Cargo.toml index 7878c8fdfab..e7d8df37fce 100644 --- a/toyos-cc/Cargo.toml +++ b/toyos-cc/Cargo.toml @@ -1,5 +1,6 @@ [package] name = "toyos-cc" +description = "Minimal C compiler: it exists to bootstrap tinycc and compile doomgeneric, not to grow." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-desktop/Cargo.toml b/toyos-desktop/Cargo.toml index 5c2e2aab06b..2caa27ee49a 100644 --- a/toyos-desktop/Cargo.toml +++ b/toyos-desktop/Cargo.toml @@ -7,6 +7,7 @@ [package] name = "toyos-desktop" +description = "Every decision the compositor makes, pure." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-dma/Cargo.toml b/toyos-dma/Cargo.toml index ad0344b3a09..7a4e29b17f4 100644 --- a/toyos-dma/Cargo.toml +++ b/toyos-dma/Cargo.toml @@ -11,6 +11,7 @@ [package] name = "toyos-dma" +description = "Every bound and alignment a DMA view or a device register window checks, pure, forbid(unsafe_code)." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-dns/Cargo.toml b/toyos-dns/Cargo.toml index 47d49b253af..2c2331d2c2d 100644 --- a/toyos-dns/Cargo.toml +++ b/toyos-dns/Cargo.toml @@ -5,6 +5,7 @@ [package] name = "toyos-dns" +description = "A stub DNS resolver's decisions (RFC 1035): the question, which datagram answers it, what it says and when to ask again, pure." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-elf/Cargo.toml b/toyos-elf/Cargo.toml index 7732ad530c0..7542cfac479 100644 --- a/toyos-elf/Cargo.toml +++ b/toyos-elf/Cargo.toml @@ -6,6 +6,7 @@ [package] name = "toyos-elf" +description = "ELF64 decoding of untrusted program images (no_std, no alloc, forbid(unsafe_code))." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-elide/Cargo.toml b/toyos-elide/Cargo.toml index d784535db69..6a59f6c70a6 100644 --- a/toyos-elide/Cargo.toml +++ b/toyos-elide/Cargo.toml @@ -13,6 +13,7 @@ [package] name = "toyos-elide" +description = "Log elision decisions: what a too-wide value keeps, and which records past a site's allowance are left out, pure." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-fat32-check/Cargo.toml b/toyos-fat32-check/Cargo.toml index fb2cc1287d3..d8b7d350b59 100644 --- a/toyos-fat32-check/Cargo.toml +++ b/toyos-fat32-check/Cargo.toml @@ -8,6 +8,7 @@ [package] name = "toyos-fat32-check" +description = "FAT32 volume checker written from Microsoft's fatgen103, the outside judge for every volume this project writes." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-fat32/Cargo.toml b/toyos-fat32/Cargo.toml index 04618071767..0eb076144c5 100644 --- a/toyos-fat32/Cargo.toml +++ b/toyos-fat32/Cargo.toml @@ -16,6 +16,7 @@ [package] name = "toyos-fat32" +description = "FAT32 driver, read and write, over a byte-addressed volume; no format path by design." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-gpt/Cargo.toml b/toyos-gpt/Cargo.toml index e2b9d2199d0..ab77b6628a7 100644 --- a/toyos-gpt/Cargo.toml +++ b/toyos-gpt/Cargo.toml @@ -6,6 +6,7 @@ [package] name = "toyos-gpt" +description = "GPT parser that finds the partition firmware booted from (no_std, no alloc, forbid(unsafe_code))." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-hda/Cargo.toml b/toyos-hda/Cargo.toml index 8b590798307..a6c8e37fdc2 100644 --- a/toyos-hda/Cargo.toml +++ b/toyos-hda/Cargo.toml @@ -7,6 +7,7 @@ [package] name = "toyos-hda" +description = "HDA codec decoding and output-path selection, pure." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-i219/Cargo.toml b/toyos-i219/Cargo.toml index a8c02605c9c..99b65bdb070 100644 --- a/toyos-i219/Cargo.toml +++ b/toyos-i219/Cargo.toml @@ -12,6 +12,7 @@ [package] name = "toyos-i219" +description = "Every decision the Intel I219 network driver makes, and none of the instructions that carry them out." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-inspect/Cargo.toml b/toyos-inspect/Cargo.toml index 007ded79e7e..6d69715a117 100644 --- a/toyos-inspect/Cargo.toml +++ b/toyos-inspect/Cargo.toml @@ -1,5 +1,6 @@ [package] name = "toyos-inspect" +description = "The inspect protocol: what a running owner of a device or service says about its own state, now." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-libc-copies/Cargo.toml b/toyos-libc-copies/Cargo.toml index 97fe87c4b00..49816bb3e1e 100644 --- a/toyos-libc-copies/Cargo.toml +++ b/toyos-libc-copies/Cargo.toml @@ -1,5 +1,6 @@ [package] name = "toyos-libc-copies" +description = "A host differential test of userland libc's architecture module: its copies, fills and square roots against core's." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-logstream/Cargo.toml b/toyos-logstream/Cargo.toml index 0a132e0745d..7a8e97e04f5 100644 --- a/toyos-logstream/Cargo.toml +++ b/toyos-logstream/Cargo.toml @@ -1,5 +1,6 @@ [package] name = "toyos-logstream" +description = "A boot's log as logd writes and serves it: a program's line beside the kernel's records, init's frame, and a late reader's replay." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-manifest/Cargo.toml b/toyos-manifest/Cargo.toml index 1dfdd9da8b0..f72eecb69eb 100644 --- a/toyos-manifest/Cargo.toml +++ b/toyos-manifest/Cargo.toml @@ -1,5 +1,6 @@ [package] name = "toyos-manifest" +description = "The one definition of /system/etc/system.manifest: what every program in an image may hold, written by the build and read by init." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-mdns/Cargo.toml b/toyos-mdns/Cargo.toml index 46f82a2ca48..49c2a38f29a 100644 --- a/toyos-mdns/Cargo.toml +++ b/toyos-mdns/Cargo.toml @@ -5,6 +5,7 @@ [package] name = "toyos-mdns" +description = "Multicast DNS (RFC 6762) for one machine's own .local name." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-mixer/Cargo.toml b/toyos-mixer/Cargo.toml index 20c7417880d..6b888734b14 100644 --- a/toyos-mixer/Cargo.toml +++ b/toyos-mixer/Cargo.toml @@ -18,6 +18,7 @@ [package] name = "toyos-mixer" +description = "The mixer's decisions (samples, gain, dither, quantize), pure and corpus-certified." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-net-tcp/Cargo.toml b/toyos-net-tcp/Cargo.toml index 5717405a0ab..def9c04ae32 100644 --- a/toyos-net-tcp/Cargo.toml +++ b/toyos-net-tcp/Cargo.toml @@ -5,6 +5,7 @@ name = "toyos-net-tcp" version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" +description = "ToyOS's TCP, pure: the caller hands in the time, the secrets, parsed segments and user calls." publish = false [dependencies] diff --git a/toyos-net-wire/Cargo.toml b/toyos-net-wire/Cargo.toml index 5deac74c9f8..84159312a08 100644 --- a/toyos-net-wire/Cargo.toml +++ b/toyos-net-wire/Cargo.toml @@ -2,5 +2,6 @@ name = "toyos-net-wire" version = "0.1.0" edition = "2021" +description = "Ethernet, ARP, IPv4, ICMPv4, IGMP, UDP and TCP wire formats, parsed in place and built (no_std, forbid(unsafe_code))." license = "MIT OR Apache-2.0" publish = false diff --git a/toyos-pci/Cargo.toml b/toyos-pci/Cargo.toml index 9db9c0e6deb..44a402a007a 100644 --- a/toyos-pci/Cargo.toml +++ b/toyos-pci/Cargo.toml @@ -8,6 +8,7 @@ [package] name = "toyos-pci" +description = "A PCI function's BARs and its MSI and MSI-X capabilities, decoded as pure functions." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-pcid/Cargo.toml b/toyos-pcid/Cargo.toml index 402a1d21534..19a5c5d5c0a 100644 --- a/toyos-pcid/Cargo.toml +++ b/toyos-pcid/Cargo.toml @@ -17,6 +17,7 @@ [package] name = "toyos-pcid" +description = "Which PCID a new address space gets, and when a returned one may be reissued, pure." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-proclife/Cargo.toml b/toyos-proclife/Cargo.toml index 9e5fca535d2..edf59ee2a3d 100644 --- a/toyos-proclife/Cargo.toml +++ b/toyos-proclife/Cargo.toml @@ -17,6 +17,7 @@ [package] name = "toyos-proclife" +description = "The process and thread lifecycle's decisions, pure and interleaving-checked." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-ps2/Cargo.toml b/toyos-ps2/Cargo.toml index fa54595ad93..4f9dc9a2e5a 100644 --- a/toyos-ps2/Cargo.toml +++ b/toyos-ps2/Cargo.toml @@ -4,6 +4,7 @@ [package] name = "toyos-ps2" +description = "PS/2 wire decoding: scancodes and mouse packets in, HID usages and pointer deltas out, pure." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-quiesce/Cargo.toml b/toyos-quiesce/Cargo.toml index 82a2a1ef01d..3674884906c 100644 --- a/toyos-quiesce/Cargo.toml +++ b/toyos-quiesce/Cargo.toml @@ -1,5 +1,6 @@ [package] name = "toyos-quiesce" +description = "Every decision the machine's stop makes, and none of its effects." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-rootimage/Cargo.toml b/toyos-rootimage/Cargo.toml index 71a934c6c35..6ebdede04b2 100644 --- a/toyos-rootimage/Cargo.toml +++ b/toyos-rootimage/Cargo.toml @@ -7,6 +7,7 @@ [package] name = "toyos-rootimage" +description = "ROOT in memory: every decision the loader and the kernel make about the image, pure." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-sched/Cargo.toml b/toyos-sched/Cargo.toml index 27cbaad4f06..7cd818e04e7 100644 --- a/toyos-sched/Cargo.toml +++ b/toyos-sched/Cargo.toml @@ -5,6 +5,7 @@ [package] name = "toyos-sched" +description = "The scheduler core: a sans-IO state machine the kernel and the host simulator drive through one Hw boundary." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-sched/loom/Cargo.toml b/toyos-sched/loom/Cargo.toml index 9c13059b368..e6a978c80a9 100644 --- a/toyos-sched/loom/Cargo.toml +++ b/toyos-sched/loom/Cargo.toml @@ -1,5 +1,6 @@ [package] name = "toyos-sched-loom" +description = "Loom models of the toyos-sched primitives, over the same sources compiled a second time." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-sched/sim/Cargo.toml b/toyos-sched/sim/Cargo.toml index c29a97c3605..eed75e2558b 100644 --- a/toyos-sched/sim/Cargo.toml +++ b/toyos-sched/sim/Cargo.toml @@ -1,5 +1,6 @@ [package] name = "toyos-sched-sim" +description = "The deterministic host simulator for toyos-sched: machine, explorer, shrinker and scenario corpus." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-swap/Cargo.toml b/toyos-swap/Cargo.toml index 510bf16509b..4e5297e966b 100644 --- a/toyos-swap/Cargo.toml +++ b/toyos-swap/Cargo.toml @@ -1,5 +1,6 @@ [package] name = "toyos-swap" +description = "Replacing a running service's binary: what swap, init and the host say about it, and init's decisions, pure." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-symbols/Cargo.toml b/toyos-symbols/Cargo.toml index 20651416d38..9fc7edb5d3c 100644 --- a/toyos-symbols/Cargo.toml +++ b/toyos-symbols/Cargo.toml @@ -10,6 +10,7 @@ [package] name = "toyos-symbols" +description = "Backtrace symbol lookup: locating an ELF's symbol tables and budgeting the demangled name (no_std, no alloc, forbid(unsafe_code))." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-tco/Cargo.toml b/toyos-tco/Cargo.toml index b09dd2744a7..acd7dabb70f 100644 --- a/toyos-tco/Cargo.toml +++ b/toyos-tco/Cargo.toml @@ -7,6 +7,7 @@ [package] name = "toyos-tco" +description = "Every bound a boot arms and the metal loop waits on, and Intel's TCO watchdog register block and where a chipset keeps it." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-tmpdir/Cargo.toml b/toyos-tmpdir/Cargo.toml index c90ea44e390..b20df924d3a 100644 --- a/toyos-tmpdir/Cargo.toml +++ b/toyos-tmpdir/Cargo.toml @@ -3,6 +3,7 @@ [package] name = "toyos-tmpdir" +description = "A scratch directory that is gone when its holder is, on every way out." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-transport/Cargo.toml b/toyos-transport/Cargo.toml index cd1fbf10a09..2b7ec21bd63 100644 --- a/toyos-transport/Cargo.toml +++ b/toyos-transport/Cargo.toml @@ -10,6 +10,7 @@ name = "toyos-transport" version = "0.1.0" edition = "2021" +description = "The transport every client/server session runs over: rings, an arena of runs and a tag table, decided over words an adapter hands in." license = "MIT OR Apache-2.0" publish = false diff --git a/toyos-untrusted/Cargo.toml b/toyos-untrusted/Cargo.toml index 83e3dc98028..954395819e7 100644 --- a/toyos-untrusted/Cargo.toml +++ b/toyos-untrusted/Cargo.toml @@ -11,6 +11,7 @@ [package] name = "toyos-untrusted" +description = "A number that crossed a trust boundary, and the four ways it gets out." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-update/Cargo.toml b/toyos-update/Cargo.toml index 3d24ef3ef42..09d026ca537 100644 --- a/toyos-update/Cargo.toml +++ b/toyos-update/Cargo.toml @@ -5,6 +5,7 @@ [package] name = "toyos-update" +description = "A signed image, the slots it installs into, and every decision the loader and update make about one, pure." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-userbound/Cargo.toml b/toyos-userbound/Cargo.toml index 2a27bbb14ed..c067012f3fb 100644 --- a/toyos-userbound/Cargo.toml +++ b/toyos-userbound/Cargo.toml @@ -12,6 +12,7 @@ [package] name = "toyos-userbound" +description = "Every decision the kernel makes about the user/kernel boundary, pure." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-userpin/Cargo.toml b/toyos-userpin/Cargo.toml index 1467d06327c..cd3cbad2ac4 100644 --- a/toyos-userpin/Cargo.toml +++ b/toyos-userpin/Cargo.toml @@ -7,6 +7,7 @@ # test, not a library the kernel links. [package] name = "toyos-userpin" +description = "The user-copy window's pin invariant, checked over every park interleaving." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-wallclock/Cargo.toml b/toyos-wallclock/Cargo.toml index 13c15365699..b3ec41c11b9 100644 --- a/toyos-wallclock/Cargo.toml +++ b/toyos-wallclock/Cargo.toml @@ -1,5 +1,6 @@ [package] name = "toyos-wallclock" +description = "The calendar, and the zone offset userland has to recover, pure." version = "0.1.0" edition = "2024" license = "MIT OR Apache-2.0" diff --git a/toyos-xhci/Cargo.toml b/toyos-xhci/Cargo.toml index 59f2bb0c180..e0e19625e2c 100644 --- a/toyos-xhci/Cargo.toml +++ b/toyos-xhci/Cargo.toml @@ -5,6 +5,7 @@ [package] name = "toyos-xhci" +description = "The xHCI root-hub port machine, as a decision separated from its effects." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/toyos-xhci/sim/Cargo.toml b/toyos-xhci/sim/Cargo.toml index b74e6e1cddc..64873765858 100644 --- a/toyos-xhci/sim/Cargo.toml +++ b/toyos-xhci/sim/Cargo.toml @@ -1,5 +1,6 @@ [package] name = "toyos-xhci-sim" +description = "The host simulator for the xHCI port machine." version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0"