Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ There are no spec documents. A rule a reader can check lives in an agent's promp

Vendor firmware a device or CPU verifies by its maker's signature may be shipped: pinned by version and hash, redistributable unmodified, recorded in `NOTICE`. A device's is loaded only by its own driver through its IOMMU domain and never executes on the CPU; CPU microcode is loaded by the kernel. `NOTICE` names every committed third-party file with its hash, upstream and licence.

- **toyos-ld** — frozen: everything links with rust-lld, and toyos-ld stays only as the linker inside ToyOS until lld runs there, then goes.
- **rust/** — Rust compiler/std fork with ToyOS platform support (submodule). Auto-bootstraps; kept current with upstream. Its rules: `.claude/agents/implementer.md`, "A fork".

## Build & test
Expand Down
14 changes: 0 additions & 14 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 0 additions & 35 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ members = [
"toyos-i219",
"toyos-inspect",
"toyos-keymap",
"toyos-ld",
"toyos-logstream",
"toyos-manifest",
"toyos-microcode",
Expand Down Expand Up @@ -234,40 +233,6 @@ opt-level = 2
[profile.dev]
opt-level = 2

[profile.dev.package.toyos-ld]
opt-level = 3

# The one profile every guest binary is built with. Optimised, because an
# unoptimised guest mismeasures everything under TCG;
# debug-assertions and overflow-checks on, because fail-fast beats speed here.
# `--release` would turn the last two off silently.
#
# It is declared here rather than in `toyos-ld/Cargo.toml` because cargo
# ignores `[profile]` in a workspace member and that is one now. It is the only
# member the profile reaches: it is a `[programs]` entry in `system.toml` as
# well as a host crate, so `src/build.rs` builds it a second time for
# `x86_64-unknown-toyos`. `kernel/`, `bootloader/` and `userland/` are excluded
# from this workspace and each declares its own copy.
#
# `incremental` stays at the dev default (on), which switches rustc's MIR
# inliner off for every guest crate — LLVM inlines regardless, and a
# non-incremental kernel edit loop measures 4-10x the wall clock. A codegen
# backend with no inliner of its own (cranelift) reopens that trade.
[profile.toyos]
inherits = "dev"
opt-level = 2
debug = true
# DWARF stays out of the image: symbols name a frame, and `debug = true` is kept
# for the codegen it decides.
strip = "debuginfo"
debug-assertions = true
overflow-checks = true

# Cargo does not carry the profile's `strip` onto a package with an override of
# its own, and this one has one in `dev`.
[profile.toyos.package.toyos-ld]
strip = "debuginfo"

[[test]]
name = "toyos-build"
path = "tests/toyos.rs"
Expand Down
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# ToyOS

An operating system written from scratch in Rust — bootloader, kernel, drivers,
userland, and the compiler and linker that build them.
userland, and the compiler that builds them.

```
git clone https://github.com/Japabu/toyos
Expand Down Expand Up @@ -167,8 +167,7 @@ TinyCC's expectations.
**LLVM's linker.** `rust-lld`, the one the Rust toolchain carries, links
everything that runs on ToyOS, plus everything that runs before it: the UEFI
bootloader as PE32+, the kernel and every userland program as
position-independent ELF. `toyos-ld`, the linker this project wrote, is frozen:
it is the one linker that runs inside ToyOS, until LLD does.
position-independent ELF.

**A real Rust target.** `x86_64-unknown-toyos` lives in ToyOS's fork of the
compiler, with a prebuilt `std` in the sysroot. One `rustc` invocation turns an
Expand Down
15 changes: 3 additions & 12 deletions issues/anonymous-mmap-is-not-demand-paged.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ kind: defect
opened: 2026-08-08
---

# Anonymous `mmap` is not demand-paged, and `.bss` is written into the file
# Anonymous `mmap` is not demand-paged

Two claims this tree makes about itself that its own code does not keep. Found
A claim this tree makes about itself that its own code does not keep. Found
by `wt/toyos-fpu` while building a page-fault workload for `fpu_isolation`, and
recorded rather than fixed: both are memory-subsystem changes with their own
recorded rather than fixed: it is a memory-subsystem change with its own
blast radius.

**`sys_mmap` allocates and maps the whole region up front.** `PageAlloc::new` is
Expand All @@ -23,15 +23,6 @@ of nothing else. Whether it should be lazy is a design question with a real
answer either way — the eager path is simpler and cannot fail late — but the
description and the code should agree.

**`toyos-ld` materializes `.bss` into the file.** A `static mut [u8; 16 MiB]`
that is all zeroes produced an 18 MB binary, and a 128 MiB one produced a
135 MB binary and a 465 MB initrd; both are in the same run's `initrd: adding`
lines. A `PT_LOAD`'s `filesz` therefore equals its `memsz`, which also means the
loader's `Anonymous` tail (`loader/mod.rs`, `file_backed_end < seg_end`) is
always empty — that branch exists and nothing in the tree can reach it. Zeroed
pages a linker never has to write are the whole point of `.bss`, and this is
paid in image size, in initrd size, and in every copy of both.

The one demand-paged thing a userland program can still reach is a *writable
file-backed* page — `demand_paging_sse` and `fpu_isolation` both use one — at
2 MiB of test image per fault.
2 changes: 1 addition & 1 deletion issues/cargo-package-fails-in-a-linked-worktree.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ opened: 2026-09-26

# `cargo package`/`cargo publish --dry-run` cannot run inside a linked worktree

`cargo package -p <any workspace member>` (tried `toyos-abi` and `toyos-ld`)
`cargo package -p <any workspace member>` (tried `toyos-abi`)
reds with a bare `error: No such file or directory (os error 2)` in a linked
worktree, right after cargo's own trace logs
`found a git repo` and `found (git) Cargo.toml`, inside
Expand Down
4 changes: 2 additions & 2 deletions issues/clippy-stage-two-is-lints-one-at-a-time.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Measuring the full `pedantic` + `nursery` groups (`-W` only, not gated) across
the host workspace, kernel and bootloader found **~9,000 findings** across
~90 distinct lints (up from the 1,684-on-the-kernel-alone figure this entry
carried before — that number was kernel only; the host workspace, including
`toyos-ld` and `toyos-cc`, carries most of the rest). Six lints earned a
`toyos-cc`, carries most of the rest). Six lints earned a
place in the gate. Everything else stayed off, each for a reason below.

## Adopted
Expand Down Expand Up @@ -108,7 +108,7 @@ before adopting:
`toyos-gpt/src/lib.rs`'s three-clause partition-bounds check
(`partition.first_lba > partition.last_lba || partition.first_lba <
header.first_usable_lba || partition.last_lba > header.last_usable_lba`)
and `toyos-ld/src/emit_macho.rs`'s Mach-O header-size sum both look like
and a since-deleted linker's Mach-O header-size sum both look like
copy-paste bugs to the lint's cross-clause heuristic and are correct as
written — verified against what each is actually checking, not assumed.
- **`large_stack_arrays` / `large_stack_frames` (4 findings)**: flagged a
Expand Down
14 changes: 6 additions & 8 deletions issues/debug-true-produces-no-debug-info.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,17 @@ opened: 2026-08-07

# `debug = true` produces no debug info, because the linker drops it

`toyos-ld` matches `SectionKind::Debug | DebugString | Linker | Note | Metadata`
and `continue`s (`collect.rs:410-416`), so **no binary this project produces has
a `.debug_*` section**. Verified with `readelf -S` on the kernel, the compositor
and toybox: the sections are `.text .strtab .symtab .rela.dyn .data
.eh_frame_hdr .dynamic .shstrtab` and nothing else.
Every `[profile.toyos]` but the loader's sets `strip = "debuginfo"`, and
`readelf -SW` on the x86-64 kernel, compositor and toybox finds **no DWARF
section** in any of the three: no `.debug_*` section but rustc's
`.debug_gdb_scripts`, which is no DWARF.

`[profile.toyos]` states `debug = true` in every crate root, so rustc emits
DWARF into every object file and the linker throws all of it away. The cost is
compile time and has not been measured. The consequence for diagnostics is that
a backtrace can carry a **name** and never a line number or an inlined frame, on
any path — `.symtab`/`.strtab` is the whole of what survives, and it is 32.2% of
the 92,138,384 bytes of ELF this tree ships. Keeping `.debug_line` in `toyos-ld`
is what would change that, and it is not planned.
the 92,138,384 bytes of ELF this tree ships.

**2026-08-29: the cost measured, and the cheap exit measured shut.** Cold
guest build (`cargo run -- --build-only`, kernel+bootloader+userland+tests
Expand All @@ -30,5 +28,5 @@ differently, and a kernel-only A/B shows `.data` +0x50, `.rela.dyn` +0x48
(three relocations) and a moved `.text` tail — debuginfo changes rustc's
codegen, not just the metadata. So turning it off ships different bytes on
every binary, and the choice between paying the 27%, shipping the
debuginfo-free codegen, and teaching `toyos-ld` to keep `.debug_line` is one
debuginfo-free codegen, and keeping `.debug_line` is one
decision, not a cleanup.
10 changes: 3 additions & 7 deletions issues/root-in-memory-costs-its-whole-size.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ audio run carries its one binary, and the shared list runs on boots of one lane
in turn, each carrying at most `SHARED_BOOT_BYTES` (64 MiB) of test binaries;
`qemu::carrying` adds what those binaries spawn or load. A boot that runs none
carries `tests/testcases`'s own programs, 13 MiB (3328 blocks), where every test
boot carried the whole 619 MiB catalogue before. The one part past the bound is
`demand_window_race`, 131 MiB, nearly all of it the `.bss` zeros `toyos-ld`
writes into the file (`issues/anonymous-mmap-is-not-demand-paged.md`),
which a guest that mounts ROOT off a disk never reads and a guest here holds.
boot carried the whole 619 MiB catalogue before.

**Every resident `/system` page is in RAM twice.** `ReadOnlyBacking::read_page`
(`kernel/src/file_backing.rs`) copies each page a program touches out of the
Expand All @@ -49,7 +46,6 @@ complete K ms`; a test boot on QEMU reads 1390, 108 (14) and 344.
Not measured: the T14. What that run owes is that line off the stick and off
NVMe, against the base's `Boot: complete`.

Exit: either measured and accepted by the owner as the price, or `.bss` left
out of the file by `toyos-ld` and the file cache serving the image's own page
rather than a copy, before the self-update's signature check makes the whole
Exit: either measured and accepted by the owner as the price, or the file
cache serving the image's own page rather than a copy, before the self-update's signature check makes the whole
read a fixed cost.
5 changes: 2 additions & 3 deletions issues/the-hosted-rustc-names-a-linker-toyos-does-not-have.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ opened: 2026-09-26

`x86_64-unknown-toyos` names `rust-lld`, and the rustc `src/toolchain.rs`
builds for a ToyOS host carries that target spec into the guest, where no
`rust-lld` exists: LLD runs on ToyOS only once clang and libc++ do. The linker
that does run there is the frozen `/system/bin/toyos-ld`, which that rustc
reaches only when told `-C linker=toyos-ld`. No image ships the hosted rustc
`rust-lld` exists: LLD runs on ToyOS only once clang and libc++ do, and no
other linker runs there. No image ships the hosted rustc
today — `src/build.rs` refuses `hosted-rustc = true` until its licences are
read — so nothing links through it yet.

Expand Down
2 changes: 1 addition & 1 deletion issues/the-split-window-tlb-cost-is-unpriced.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ The instrument is the T14 with `--metal-sim`'s real shape and a workload that
loops across the boundary, comparing against a kernel built before this change.
Nothing depends on the answer today: a bad one does not send the design back to
2 MiB granularity, since 2 MiB granularity does not protect anything. It would
send it to 2 MiB-aligning `toyos-ld`'s segments, which was measured at +4 MiB of
send it to 2 MiB-aligning the linker'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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ passes it to the library crates' cargo profile
are built without. The libraries' assertions were never chosen: `std_config`'s
doc comment takes the profile so the libraries are built as the compiler was.
The guest profile keeps debug assertions on in ToyOS's own code "because
fail-fast beats speed here" (`kernel/Cargo.toml`, `userland/Cargo.toml`, the
root `Cargo.toml`); whether the libraries should too is the decision nobody
fail-fast beats speed here" (`kernel/Cargo.toml`, `userland/Cargo.toml`); whether the libraries should too is the decision nobody
made.

So every kernel and program ships `alloc`'s and `core`'s `debug_assert!`s,
Expand Down
4 changes: 2 additions & 2 deletions issues/the-tooling-is-a-review-prompt-and-three-workflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ and the gates that held them go.
- `kernel/`, `bootloader/` and `userland/` inherit a redirected
`build.target-dir` unless their `.cargo/config.toml` sets
`target-dir = "target"`.
- `stage_artifact` in `src/build.rs` builds its path outside
`hostws::target_dir`.
- `stage_artifact` in `src/build.rs` writes `root/target/…` whatever
`build.target-dir` redirects.
- `cargo clean` follows a shared target dir.
- Artifact size, bootstrap delta and CI cache keys are unmeasured.
- The nine workflows become three — `pr`, `nightly`, `publish`; then
Expand Down
23 changes: 9 additions & 14 deletions issues/toyos-builds-itself.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,10 @@ with lld, one build of one fork, `ToyOSOrg/llvm-project`. The C
library stays `userland/libc`, ours. Each stage lands on x86-64 first and on
AArch64 one step behind, on `issues/toyos-runs-on-arm64.md`'s track.

- **M2 — clang and lld as a package inside ToyOS; toyos-ld gone.** clang, lld
- **M2 — clang and lld as a package inside ToyOS.** clang, lld
and their runtime built *for* ToyOS on the host and installed by
`/system/bin/pkg`; `clang hello.c && ./a.out` works in the guest. *Exit*:
the in-guest compile-and-run test passes, and toyos-ld — today the only
linker a ToyOS process can run — is deleted with its crate, its
`[programs]` row and its tests.
the in-guest compile-and-run test passes.
- **M3 — an LLVM-backed rustc inside ToyOS.** The
hosted rustc carries LLVM instead of Cranelift. *Exit*: a
Rust program compiled and run inside ToyOS by the hosted rustc.
Expand Down Expand Up @@ -114,13 +112,12 @@ enough for an LLVM build tree
memory beyond what 2 MiB process pages allow
(`issues/process-memory-is-2-mib-pages-and-that-caps-the-process-count.md`).

**What M2 must do to delete toyos-ld.** It is frozen and links nothing the host
builds; what keeps it is that it is the one linker a ToyOS process can run,
shipped as `/system/bin/toyos-ld` by `system.toml`'s `[programs]` row and named
by the ToyOS-hosted rustc
(`issues/the-hosted-rustc-names-a-linker-toyos-does-not-have.md`). It
goes when lld runs in the guest: the row, the crate and its host tests go together, the hosted rustc names `rust-lld`, and
the published crates.io crate is yanked.
**No linker runs inside ToyOS** (owner, 2026-10-04: "Just get rid of toyos
ld"). `/system/bin/toyos-ld` went with its crate, its `[programs]` row and its
host tests, and nothing replaced it: a ToyOS process cannot link an object, and
the ToyOS-hosted rustc names no linker the guest has
(`issues/the-hosted-rustc-names-a-linker-toyos-does-not-have.md`). *Exit*: the
commit that lands M2's compile-and-run test, which links inside the guest.

**What stops M2: LLVM, clang and lld built for a ToyOS host**, in the order
each blocks the next, as
Expand All @@ -136,9 +133,7 @@ measures it.
- Test: `issues/a-worktree-cannot-build-a-hosted-rustc-of-its-own.md`.

M3's exit then waits on a linker in the guest
(`issues/the-hosted-rustc-names-a-linker-toyos-does-not-have.md`), which
toyos-ld is not: it refuses every executable with thread-local storage, so
every std program.
(`issues/the-hosted-rustc-names-a-linker-toyos-does-not-have.md`).

**Also owed, by milestone.**
- M2: the host triple a ToyOS-hosted LLVM records
Expand Down
5 changes: 2 additions & 3 deletions issues/toyos-has-no-c-compiler-inside-it.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,12 @@ opened: 2026-09-27
# ToyOS has no C compiler inside it

Until 2026-09-27 every image carried `/system/bin/toyos-cc`, a C compiler that
ran on ToyOS, beside `/system/bin/toyos-ld`. clang replaced toyos-cc on the host
ran on ToyOS. clang replaced toyos-cc on the host
and toyos-cc was deleted with its `system.toml` row, so a C program can be
compiled *for* ToyOS and not *on* it. Nothing in the suite ran the in-guest
compiler, so no test went with it.

`toyos-ld` is still in the image, frozen, and links objects a ToyOS process
already has; nothing in the image makes one from C.
Nothing in the image makes an object from C, nor links one.

**Exit**: the self-hosting track's M2 (`issues/toyos-builds-itself.md`) —
clang and lld installed as a package, and a guest test that compiles `hello.c`
Expand Down
8 changes: 2 additions & 6 deletions issues/toyos-runs-on-arm64.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,7 @@ across `src/` and `tests/`. The harness hardwires q35
(`tests/common/qemu.rs:4246-4266`). `kvm_usable()` is
`cfg!(target_arch = "x86_64") && …` (`src/lib.rs:65`). The rust fork has only
`x86_64_unknown_toyos.rs`; its base `base/toyos.rs` is arch-neutral.
`toyos-ld` already parses and applies AArch64 relocations for its Mach-O host
output (`collect.rs` 80 `Aarch64` mentions, `reloc.rs` 69) but hardwires
`EM_X86_64` (`emit_elf.rs:1021`), `R_X86_64_RELATIVE`
(`emit_elf.rs:1228,1313,1399`) and `IMAGE_FILE_MACHINE_AMD64`
(`emit_pe.rs:143`) on output, and has no AArch64 TLS relocations. `toyos-elf`
`toyos-elf`
refuses anything but `EM_X86_64` (`toyos-elf/src/header.rs:24,75`).

**Already abstracted.** The syscall stub already has both arms
Expand All @@ -168,7 +164,7 @@ most of `arch/tlb.rs`'s 303-line IPI shootdown machinery unnecessary — a
contract change, not a port); the memory model (the 711 `Relaxed` orderings
and every doorbell-after-descriptor site — undiscovered TSO reliance is the
one cost nobody can estimate from a grep); userland TLS and the toolchain
(variant I and TLSDESC across `toyos-ld`, the kernel loader and std); the test harness (33,907 lines in
(variant I and TLSDESC across the kernel loader and std); the test harness (33,907 lines in
`tests/common/`, written against q35, i8042, OVMF, KVM, `intel-iommu`).

## Sharing, not forking
Expand Down
5 changes: 5 additions & 0 deletions kernel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,11 @@ heap-lockspin = ["pass-spin"]
# Optimised, because an unoptimised guest mismeasures everything under TCG;
# debug-assertions and overflow-checks on, because fail-fast beats speed here.
# `--release` would turn the last two off silently.
#
# `incremental` stays at the dev default (on), which switches rustc's MIR
# inliner off for every guest crate — LLVM inlines regardless, and a
# non-incremental kernel edit loop measures 4-10x the wall clock. A codegen
# backend with no inliner of its own (cranelift) reopens that trade.
[profile.toyos]
inherits = "dev"
opt-level = 2
Expand Down
Loading
Loading