Repository navigation
ARM64 port, stages 0 to 3: shared groundwork, aarch64-unknown-toyos with std and the userland, and the loader and kernel reach the PL011 on virt - #524
Conversation
toyos-elf decodes EM_X86_64 and EM_AARCH64 and refuses any other e_machine
as UnknownMachine; Layout::parse takes the machine its caller runs and
refuses an image for the other one as WrongMachine before anything is
mapped. RelocKind names what a dynamic relocation asks for, and
RelocKind::from_raw(machine, r_type) is the one place a number is read, so
AArch64's RELATIVE, GLOB_DAT, JUMP_SLOT and TLS_{DTPMOD,DTPREL,TPREL} decode
to the same kinds x86-64's do. The kernel and the loader name their machine
through their arch module (ELF_MACHINE).
toyos-ld:
- The link's machine is the one its objects name. The first object decides
and an object for the other machine is refused by name; an object for any
third machine is refused. Before, anything that was not x86-64 was treated
as AArch64, and one x86-64 object turned a whole AArch64 link into x86-64.
- e_machine, the dynamic relocation types and the PE header's machine come
from that machine. The three copies of the .rela.dyn writer are one
function over one table.
- COFF relocation numbers are read per machine: ARM64 and AMD64 reuse the
same small integers. ARM64 COFF instruction relocations carry their addend
in the immediate they patch, decoded as LLD's applyArm64* do.
- An AArch64 GOT load (ADR_GOT_PAGE / LD64_GOT_LO12_NC) gets a GOT slot.
tests/machines.rs reads the outputs back with the object crate: an AArch64
PIE declares EM_AARCH64 and rebases with R_AARCH64_RELATIVE, its ADRP/ADD
pair reaches its target; an ARM64 PE declares 0xAA64 and its BL, ADRP/ADD
and ADDR64 land where the section layout says; a mixed link is refused.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
`src/arch.rs` is the one place the build system and the harness learn a machine's target triples (userland, kernel, loader), its QEMU, its firmware images, its guest CPU and how this host accelerates it. Every hardcoded `x86_64-unknown-*` and `qemu-system-x86_64` that was an architecture choice now reads it: - `Plan` carries an `Arch`, and `cargo run -- --arch <name>` sets it; with none it is x86-64, the architecture whose userland boots to a desktop. Kernel, loader and ROOT memo keys and staged artifact names carry it, and the ESP gets the removable-media loader name UEFI gives that architecture. An aarch64 image is `target/bootable.aarch64.img`; x86-64's keeps its name. - `GUEST_TARGETS` gains `aarch64-unknown-none-softfloat` and `aarch64-unknown-uefi`, so a sysroot carries the aarch64 kernel's and loader's libraries; the target list is part of the sysroot key. `USERLAND_ARCHS` names the architectures whose userland target the pinned compiler carries, and `HOSTED_ARCH` the one the hosted rustc runs on. - The aarch64 kernel target is the softfloat one, and the softfloat certification asks each architecture its own question (no SSE; no NEON or FP). - `kvm_usable`, `CPU_KVM` and `CPU_TCG` become `Arch::accel` and `Arch::cpu`: the host's own hypervisor (KVM on Linux, HVF on macOS) when the host is the guest's architecture, TCG otherwise. - `cargo run`'s launch and the CI instrument spawn the architecture's QEMU; `virt` gets a GICv3, SMMUv3 as its IOMMU and `ramfb` as its GOP. - clippy lints the kernel and the loader for both architectures, and the host job installs both architectures' bare targets. - `Plan::without_userland` builds a ROOT with no program in it, for a boot that ends before the kernel starts one. Behaviour on x86-64 is unchanged apart from one host class: an x86-64 guest on an Intel Mac now asks for HVF instead of TCG, which is the same rule an aarch64 guest on Apple silicon follows and which no one here has measured. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
… one MSI doorbell - `kernel/src/arch/mod.rs` selects `x86_64/` (and, next, `aarch64/`) and re-exports it: generic code keeps naming `crate::arch::…`, and a `target_arch` cfg appears in that file and inside the implementation only. Every x86 file moves under `arch/x86_64/` unchanged. - The syscall handlers are not architecture: `arch/syscall/*` moves to `kernel/src/syscall/`, and the entry gate, the one x86 file among them, is `arch/x86_64/syscall.rs` (`arch::syscall::init`). The build system's `SYSCALL_ENTRY_SYMBOL` follows the new path. - One interrupt guard. `arch::IrqGuard` replaces `hw::IrqGuard`, `arch::LogCommitGuard` and `serial::BackendGuard`'s private `SavedFlags`, which were three spellings of pushfq/cli/popfq. Two of them declared their `asm!` `nomem`, which tells the compiler the instruction touches no memory and lets it move loads and stores across `cli` and `popfq`: the scheduler's guarded region and the console lock's CAS were not compiler-ordered against the interrupt mask. The one guard is a compiler barrier on both edges, as `LogCommitGuard` already was. `log-unbracketed-reserve` keeps its staging through `IrqGuard::unclosed`, compiled only with `boot-actuators`. - One MSI doorbell: `arch::MSI_DOORBELL` and `arch::msi_message(dest, vector)`, used by PCI's compatibility message and by VT-d's remappable format and fault event, where `0xFEE0_0000` was written three times. Behaviour on x86-64 is unchanged except for the compiler ordering above. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…named
`arch::barrier` is the ordering a device's view of memory needs: `dma_wmb`
(stores before stores, as a device's DMA reads see them), `dma_rmb` (loads
of device-written memory after loads), what an MMIO store is preceded by and
what an MMIO load is followed by, and `scanout_flush` for the
write-combining panel. On x86-64 each is a compiler barrier, except the
scanout's `sfence`; TSO gives the rest for nothing. A `fence(Release)` is
not one of them on AArch64, where it is `dmb ish` and orders nothing a
device, outside the inner-shareable domain, observes.
`Mmio`'s contract is now Linux's `writel`/`readl`: every `write_*` is
ordered after the stores this CPU made before it, and every `read_*` before
the loads it makes after. That ordering was the `Sync` impl's unstated
assumption ("volatile accesses order correctly regardless of which CPU
issues them"), which held for x86 TSO and uncacheable memory only, and it
also was not true of the compiler: `volatile` orders a store against other
volatile accesses, not against the plain stores of a descriptor.
Every DMA ordering site now names its barrier:
- virtio: descriptors before the avail idx (`dma_wmb`), the avail idx before
the notify (the `Mmio` write), the used idx before the element it counts
(`dma_rmb`, both consumers).
- NVMe: the submission entry before the doorbell (the `Mmio` write). Fixed:
the completion entry and the data it completes were read with nothing
after the phase-tag poll to order them; `dma_rmb` does.
- xHCI: fixed, a TRB was written as one 16-byte volatile struct store,
whose order the compiler chooses, so a controller running the ring could
see the Cycle bit before the body; `TrbRing::put` writes the body,
`dma_wmb`, then the control word. Fixed, an event TRB was read whole
and its Cycle bit checked after; `next_event` reads the control word,
checks it, `dma_rmb`, then reads the TRB. Doorbells are `Mmio` writes.
- The black box's write-back is `arch::cache::write_back`, the panel's
`sfence` is `barrier::scanout_flush`: no x86 instruction is left in
either generic file.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…de OVMF `aavmf/AAVMF_CODE.fd` and `aavmf/AAVMF_VARS.fd` are QEMU 11.1.0's `share/qemu/edk2-aarch64-code.fd` and `edk2-arm-vars.fd`, unmodified, committed the way `ovmf/` is and hashed in `NOTICE` and the binary gate. `Arch::pflash` is the one place a launcher gets its two `-drive` values. AAVMF's variable store boots as a snapshot QEMU discards: the build is a DEBUG one, and with a read-only store `BdsDxe` asserts on its first `NorFlashWriteBuffer` (measured: `ASSERT_RETURN_ERROR (Status = Device Error)` at `PlatformBm.c(1069)`). With the snapshot, `qemu-system-aarch64 -M virt,gic-version=3 -accel hvf -cpu host` reaches the UEFI shell on this M4 Pro in 10 s, five of them the shell's own `startup.nsh` countdown. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…e under arch/x86_64 Code that is x86 whatever else changes moves under the architecture that has it, unchanged, so the next architecture does not compile it: - The PC platform's devices: the i8042, the I/O APIC, the CMOS RTC, the chipset's TCO watchdog and VT-d. Generic code reaches each by the concept it serves — `arch::keyboard_controller`, `arch::watchdog`, `arch::rtc`, `arch::iommu_unit` — and never by its name; `iommu`'s backend-neutral front (`crate::iommu`) calls `unit::` where it called `vtd::`, and its identifiers' constructors are `pub(crate)` rather than `pub(in crate::iommu)`, since the backend is no longer inside it. - `mm::paging` is the x86 PTE format, PCID and CR3 through and through; it moves to `arch/x86_64/paging.rs` and `crate::mm::paging` re-exports the architecture's. - `hw.rs` is the scheduler core's `Machine`/`Hw` implementation — x2APIC, the TSC, the `context_switch` frame and SYSRET's SS erratum — and moves to `arch/x86_64/hw.rs`; `crate::hw` names it. - `nmi_gate`, the `boot-actuators` harness for the SYSCALL entry's user-stack window, is about an x86 window that an AArch64 exception entry does not have. The gates that name files by path (sourcegate, kernelkeys, kernel-loom's tally include) follow them. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
… instruction
No `asm!`, no `extern "sysv64"` and no x86 register name is left outside
`kernel/src/arch/`. What each place asked of x86-64 it now asks of the
architecture by what it is:
- `arch::boot`: the loader's entry (`_start`, moved out of `main.rs`) and
the steps `kernel_main` takes at the points where one architecture differs
from another — the memory type before the panel, CR0 and the PAT after
the console, the AP trampoline's reserved page, interrupt bring-up
(MADT, reset register, local APIC, per-CPU block, I/O APIC, IDT, syscall
gate), the HPET-calibrated clock and the CMOS wall clock, the timer, the
i8042, starting the APs, and the interrupt-controller selftests.
`kernel_main` is `extern "C"` now that assembly calls it.
- `arch::irqchip` is the APIC and `arch::trap` the IDT to every generic
caller; `trap::report_panic`, `trap::kernel_backtrace` and
`trap::frame_interrupts_enabled` replace reads of `rbp` and `RFLAGS.IF`.
- `arch::cpu::{counter, frame_pointer, undefined_instruction,
thread_pointer, stated_counter_hz, run_on_stack}` replace `rdtsc`, `mov
rbp`, `ud2`, the FS base, CPUID leaves 15H/16H and the idle loop's stack
switch.
- `arch::entropy` is the CPU's random source (`RDRAND` here) for the
hasher's seed and `sys_random`.
- `arch::console_uart` is the 16550 at COM1; `drivers::serial` keeps the
lock, the backends and the line discipline, and `serial::init` takes the
RSDP an architecture that places its UART by table needs.
- `arch::pio` is the I/O port space, and ACPI refuses a SystemIO reset or
PM1a register on an architecture that has none.
- `arch::pmu` is the counter that samples a CPU by NMI; `hardlockup` keeps
the decision.
- `arch::percpu` carries the preempt count, the reschedule flag, the fault
state and the interrupt counters behind functions, not `gs:` offsets;
`irq_took!` lives with the handlers that use it.
- `arch::entry::initial_frame` lays out a new stack's first frame and the
three trampolines live beside it; `context_switch` is
`arch/x86_64/switch.rs`; the HPET calibration is `arch/x86_64/hpet.rs`
and `clock::set_counter` is what it hands the clock.
- The mapping vocabulary (`Prot`, `WindowProt`, `CachePolicy`,
`MmioPolicy`) is `mm::policy`, each architecture's page tables encode it,
and `CachePolicy::DeferToMtrr` is `Normal`. The GOP's memory-type line
asks `paging::scanout_memory_type`.
Behaviour on x86-64 is unchanged; every line the boot writes is the same.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…he owner put them The owner's rulings of 2026-09-26, written as tests: `src/sourcegate.rs` gains `ARCH_RULES`, a table of rules each stated as the only places its spellings may appear, beside the scans already there. It reads code only, over every `.rs` file outside the rust fork. 1. Assembly lives in an architecture's own module: `asm!`, `global_asm!`, `naked_asm!`, naked functions and `core::arch`/`std::arch` intrinsics appear only in `kernel/src/arch/<arch>/`, the loader's `src/arch/` and `toyos-abi`'s per-architecture syscall entry. 2. An architecture is selected in one place: `target_arch` appears only in the kernel's selector and its arch modules, the loader's arch module, toyos-abi's syscall entry, the build system's one reading of its host (`src/arch.rs`) and `toyos-cc`'s default target. 3. Generic kernel code reaches the machine through the arch interface: no path into `arch::x86_64` or `arch::aarch64` outside `kernel/src/arch/`. 4. A pure crate names no architecture: none of the above in any of the eleven crates `CLAUDE.md`'s layout marks pure, `toyos-sched` among them. Each rule has a fixture test that plants a violation and reds, naming the rule and its places, and the whole-repository test refuses a declared file exception that no longer holds a needle. No existing scan was folded in: the `BANS` table's rows are per-file counts, which a place rule does not express. To make rule 1 true of the loader, its x86 instructions move into `bootloader/src/arch/x86_64.rs`: the TSC read and the `IA32_TSC_ADJUST` line, the black box's `CLFLUSH`/`SFENCE` write-back, the TCO block's port I/O (refused by name on an architecture with no I/O port space) and the CR3 switch and entry jump. The `sysv64` the jump names stays there, because this target's own `"C"` is the Microsoft convention. `src/release.rs` asks `Arch::HOST` instead of spelling `target_arch`. What is left outside an arch module is declared, row by row, and filed at `issues/build/assembly-outside-an-arch-module-in-userland-and-guest-probes.md`: libc's square roots, string moves and `_start`, two userland framebuffer `sfence`s, and seventeen x86 guest probes. Every site in the kernel is gone. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…y concept `Origin` (who asked for a TLB invalidation) is a generic census, so it moves to kernel/src/invalidation.rs and the x86 tlb re-exports it. The xHCI driver's assertion about the shootdown ACK timeout belonged to the module that owns the timeout; it lives there now and the constant is private. The syscall path's NMI-gate calls go through `arch::syscall::note_entry` / `window_storm`, so dispatch and the scheduler name no x86 mechanism. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…virt
The loader builds for aarch64-unknown-uefi through toyos-ld (PE ARM64),
reads ROOT through firmware and hands off; the kernel's AArch64 entry
drops from EL2 when entered there, writes the control-register
declaration whole, turns on the loader's tables, installs its vectors and
reaches kernel_main; the console is the PL011 SPCR names. Measured under
`-M virt,gic-version=3` with AAVMF: HVF enters at EL1, TCG with
`virtualization=on` at EL2, and both print the boot log, the memory map,
the MADT's GIC structures and the GTDT's timers, then stop on the first
owed item with an EARLY PANIC on serial and on the ramfb panel.
Shared, and changed on x86 too:
- toyos-bootmap types a leaf by concept (Cache::{Firmware, Memory,
Device, Scanout}) under a Typing (x86: the MTRRs; AArch64: firmware's
write-back ranges, a part-memory page refused), encodes both
architectures' descriptors, and splits a scanout's partial 2 MiB pages
into 4 KiB leaves where the map types memory: ramfb is carved out of
RAM at 0xbc7a0000. The x86 plan is the plan it was.
- The halt every fatal path funnels into is generic (panic::halt_all_cpus);
the architecture stops the other CPUs (irqchip::stop_other_cpus) and
reports its fault stack (trap::report_fault_stack).
- The frame-pointer backtrace, the crash report's memory tail
(mm::report_on_crash), vma::Occupancy and acpi::inventory's table list
(arch::boot::ACPI_TABLES) moved to where both architectures reach them.
- panic_reboot names the counter, not the TSC, and the loader's jump takes
the image and reads the root out of KernelArgs on both architectures.
- toyos-ld checks a declared /MACHINE: against the objects, and drops
AArch64 mapping symbols ($x/$d), which repeat per section.
- toyos-acpi decodes SPCR, GTDT, and the MADT's GICC/GICD/GICR/ITS.
- The build system gives an architecture with no userland yet a ROOT with
none (Userland::of).
The AArch64 kernel builds with -Adead_code (kernel/.cargo/config.toml,
exit: the port's stage 7): until the syscall gate, the interrupt
controller and the scheduler exist there, what only they reach is
unreachable. Every stand-in for later-stage work is an owed! that panics
naming its stage.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
`Profile::Virt` is QEMU `virt` (GICv3, AAVMF, ramfb, the boot stick on xHCI, the PL011): a profile is a whole machine, so it is the profile that says which architecture a boot is, and `qemu_command` takes the firmware, binary, accelerator, CPU and machine from it. The image is built for that architecture, and one with no userland yet is refused the suite's programs. Two tests, both `Tier::Local` — every unsharded `cargo test`, never a CI shard, because no hosted runner boots AArch64 yet (the port's stage 8 measures that): - virt_early_panic: the boot log stage 3 promises on the PL011 (the PL011 SPCR placed, the control registers as declared, the memory map, the MADT's GIC, the GTDT) and the early panic on serial and on the panel, in its fatal colours. - virt_early_fault: an undefined instruction right after the console step reaches the vectors, which report the entry and the class and panic. Negative control, run on this tree: `VBAR_EL1` pointed 0x800 past the table (one added `add` in trap::install) — virt_early_fault goes red, the misrouted exception reporting itself as "synchronous from EL0 in AArch32". `Profile::Virt` boots with 2 GiB: AAVMF allocates from the top of RAM, which past 4 GiB is outside the loader's boot map (issues/boot-media/the-boot-map-reaches-4-gib-and-firmware-decides-what-lands-in-it.md). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
Every one of the 761 lines naming `Relaxed` under kernel/src (84 files) was read against who stores, who loads, on which CPU, and what the load goes on to trust. What changed: - The panic console's published descriptor was a seqlock over a plain cell: a painter's read of it raced the publisher's write, which the Rust memory model makes undefined whatever the sequence check afterwards says. It is now `drivers/panic_console/published.rs`, a seqlock whose payload is atomic words, compiled into kernel-loom and driven by `tests/panic_console_publish.rs` (a snapshot is one publication whole). Negative control, run: `--features seqlock-writer-fence-off` drops the publisher's Release fence and the model goes red with a torn snapshot. - panic.rs's crash evidence stored its lengths after the bytes "so a read mid-fill reports a short string" — with Relaxed on both sides that held for nothing, not even an NMI on the same CPU. Release/Acquire. - hardlockup's spin record promised "a non-zero lock means the site beside it was already stored" to a report another CPU writes. Release/Acquire. - The USB transport-fault staging (msc::fault, boot-actuators) armed the fault and its opcode Relaxed before the count a disk on another CPU takes; the count is now the Release/Acquire edge, loaded first. What stayed Relaxed, by kind: counters and statistics nothing else reads through; boot-time publications made before the other CPUs exist, which the roster's Release/Acquire release orders; per-CPU and same-thread state; diagnostics whose two words may tear and say so (i8042's unexplained bytes, a task's cpu_ns/running_since pair, nmi_gate's held sample); and the primitives kernel-loom already models, whose Relaxed arms are their negative controls. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
licence of AAVMF, and the loom control declared `cargo run -- --ci host` reddened on four things this branch did: - clippy's `manual_is_multiple_of` (toyos-bootmap, the AArch64 trap frame), a doc comment left dangling where irq_took! moved into the architecture, and a doc comment the architecture rules were inserted under (sourcegate's CORPUS); - the harness's `build_boot_image_with`, which takes its eighth argument (the architecture) under an allow that says why; - NOTICE's AAVMF section, which named its terms in the heading but carried no `SPDX-License-Identifier:` line for the licence gate to read; - `seqlock-writer-fence-off`, a new kernel-loom control that src/ci.rs's CONTROLS and the kernel's declared-feature list did not account for. After this, `cargo run -- --ci host`: 39 steps, all green (EXIT=0). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
`BackendGuard::try_lock` became `.is_ok().then_some(Self { _irq: irq })`
when the one IrqGuard replaced its hand-rolled flags (0840b25).
`then_some` builds its argument whether or not the exchange won, so a CPU
that lost the race built a guard, dropped it, and its drop stored
`BACKEND_LOCKED = false` under the CPU that held the backend. Two CPUs
then wrote the virtio-console at once and the second found the TX slot
taken: "vconsole: no tx slot", 254 of them in one fast-tier run, taking
the shared boot and 130 tests down beside other guests (each green alone).
Back to an `if`, as it was before the IrqGuard change.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…ther guests A fast-tier red on this branch, green alone, whose own log reads as a slow guest rather than the lost wake its sentence names. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
The AArch64 machine support (EM_AARCH64, R_AARCH64_RELATIVE, PE 0xAA64, COFF ARM64 relocations) changed toyos-ld's identity, and abi-split reds on a published crate that changed under the version it already had. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
check_compiler refused a linked worktree whose fork checkout's compiler/ was not the one the primary built stage2 from, so a target spec or any compiler change could be neither built nor tested before it landed. That contradicts the landed rule that every worktree builds the toolchain its own sources name. src/compiler.rs now resolves the compiler a worktree's fork checkout names: the primary's stage2 where its compiler/ is the one recorded, and otherwise a compiler of its own, built by bootstrap in the worktree's own fork checkout (build/toyos-compiler, host only, stage 2 of compiler/rustc and library, the primary's profile) and placed at rust/build/compilers/<key>/, keyed on the identity of compiler/, src/bootstrap, src/stage0 and Cargo.lock. The sysroot key already named the compiler's identity, so a sysroot built from it is a new key and is cloned from that compiler instead of the primary's. Nothing of the primary's is written: not its stage2, not its record, not the rustup toyos link (a sysroot is named by directory). The global lock is not taken for such a compiler; its own key lock (buildlock's Keyed, now shared with sysroots) orders before the sysroot key's. --worktree remove sweeps compilers no worktree records. hosted-rustc is refused in a worktree building with its own compiler: the hosted rustc is the primary's. compiler::source now also counts files git does not track under compiler/, which is what a new target spec is before its commit. Negative controls, each applied as a patch, built, run and restored: every worktree given the primary's compiler -> the coexistence test EXIT=101; a worktree's compiler placed beside the primary's stage2 -> EXIT=101. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
… issue files cite moved paths Two small tooling defects found while landing the port's stage 0: - The shared-host green arm of the ALONE line names Sched::Parallel as the cause, and nothing groups wide failures that share a headline: at f67863c one kernel defect (BackendGuard::try_lock) read as 130 scheduling classifications. - 46 citations in 35 issue files name paths this branch moved, beside at least 14 whole kernel/src paths already missing on main. Whether a gate belongs is the owner's: src/CLAUDE.md says documentation carries no gates. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
… GOT The aarch64-unknown-toyos std and userland link through toyos-ld, and their thread-locals are TLS descriptor sequences (adrp, ldr, add, blr): - In an executable a descriptor for a variable it defines relaxes to local-exec (movz x0; movk x0; nop; nop), and one another module defines to initial-exec (adrp x0; ldr x0; nop; nop) over a GOT slot named by R_AARCH64_TLS_TPREL. The offsets are TLS variant I: align_up(16, TLS_ALIGN) + the variable's offset in the block, where TLS_ALIGN is the 64 the layout and PT_TLS already used. - A shared library keeps the sequence, points it at a 16-byte descriptor pair per symbol, and asks the loader for it with R_AARCH64_TLSDESC. - R_AARCH64_TLSIE_* and R_AARCH64_TLSLE_ADD_TPREL_* are applied; a local-exec reference in a shared library is refused. - PREL64, LD_PREL_LO19, ADR_PREL_LO21, CONDBR19 and TSTBR14 are applied, with their ranges checked. Two defects the AArch64 std link exposed: an AArch64 GOT relocation looked only in the static GOT, so a shared library's import (its slot is in dyn_got, filled by GLOB_DAT) was an undefined symbol; and the ELF layout wrote x86-64 PLT stubs (FF 25) for every machine. The stub is now the machine's own (adrp x16; ldr x16; br x16 on AArch64). Tests read the outputs back with the object crate against the ABI's numbers. Mutations, each applied, built, run and restored: the TCB gap without the segment's alignment, EXIT=101 (the local-exec test); a descriptor's argument not relative to the block, EXIT=101 (the shared test); the dyn_got lookup removed, EXIT=101 (the import test). Differential oracle: one rustc-built object with two TLSDESC sequences, linked by LLVM's lld and by toyos-ld, is rewritten identically (movz; movk; nop; nop); the offsets differ only by each output's PT_TLS alignment (lld 8: 0x10/0x18; toyos-ld 64: 0x40/0x48). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…Arch64 The rust pin moves to 4874af48e26 (on toyos-inbox): the aarch64-unknown-toyos target spec, and std's `_start` and TLS descriptor resolver on AArch64 (variant I, `__toyos_tlsdesc_dynamic`). It is a compiler change, so this worktree builds its own compiler (src/compiler.rs) and the primary's is untouched. - The build: `GUEST_TARGETS` gains aarch64-unknown-toyos, and every architecture has a userland, so `USERLAND_ARCHS` and the plan's `Userland` switch (whose `Absent` arm nothing could reach any more, and whose `without_userland` nothing called) are deleted. `cargo run -- --arch aarch64 --build-only` builds std, libtoyos_c and the userland for AArch64 and puts them on ROOT. - libc: `_start`, memcpy/memmove/memset and the square roots move into `userland/libc/src/arch/`, one module per architecture, and gain their AArch64 versions (pair loads and stores with a byte tail, fsqrt). The copies were checked on this AArch64 host against its own copy_within/fill over 360,000 cases (lengths 0..300, offsets 0..20, overlapping both ways); a copy_backward whose chunk test was 8 instead of 16 died on the same check (SIGBUS, exit 138). - toyos-window's and metalprobe's scanout drain moves into an arch module (SFENCE; DSB ST on AArch64), and the ARCH_RULES rows for them and for libc become module places. toyos-window is published, so it bumps to 0.16.0: 0.15.0 is taken by #525 and #528. - calc, snake and doom are left off an AArch64 ROOT, by name at build time (`NOT_YET_BUILT`): softbuffer's and winit's toyos forks resolve the published toyos-window 0.2.0, whose framebuffer is x86-64 only (issues/build/the-toolkit-forks-resolve-an-x86-only-toyos-window.md). Everything else in the userland workspace, and toyos-ld and toyos-cc as guest programs, builds and links for AArch64. compiler.rs keys a compiler on the content of its sources alone: keying on the git spelling of compiler/ made committing what had been built as local changes a new compiler and a full rebuild. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…rough rust-lld The owner's ruling of 2026-09-26 moves ToyOS from toyos-ld to rust-lld: lld's output is accepted by toyos-elf and the loader, byte-reproducible and much smaller (snake 873 KB against 1.99 MB, because toyos-ld's GC roots every .eh_frame), and toyos-ld is frozen, to be deleted. This branch stops extending it: every toyos-ld change it made (the machine parameter, EM_AARCH64, R_AARCH64_RELATIVE, PE ARM64 and /MACHINE:, the AArch64 TLS descriptor output, the AArch64 GOT and PLT fixes, and the 0.3.0 bump) is reverted by this commit, leaving toyos-ld byte for byte main's. x86-64 links exactly as it did; the x86 switch to rust-lld is another branch's. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
By the owner's ruling of 2026-09-26. The rust pin moves to c3cb792df3f (on toyos-inbox): aarch64-unknown-toyos names rust-lld with the GNU flavour. The toolchain is built with `lld = true`, so every compiler and sysroot carries rust-lld, and bootstrap is told the AArch64 userland's linker (rust-lld, by name for the primary's stages and by path for a sysroot's std) with `rpath = false`: bootstrap's `-Wl,-rpath` is a cc driver's argument, and a guest std records no host library path. `Arch::links_through_toyos_ld` says which architecture still links through the frozen toyos-ld: x86-64, whose linking this branch does not change. The kernel's and the loader's AArch64 targets drop their toyos-ld override and link with the targets' own rust-lld. The loader takes the kernel as a static PIE at base 0, which rust-lld makes of the target's static-model code with `-pie -z notext`; position-independent codegen would reach symbols through GOT slots holding virtual addresses, which the entry cannot use with the MMU off. rust-lld's layout exposed a loader defect: the kernel's stack began at the image's last byte, wherever the linker ended it. lld ends this kernel at 0x…908, and on the M4 the first `stp x29, x30, [sp, #-16]!` took an SP alignment fault (ESR_EL1 0x9A000000, read through QEMU's gdb stub on a hardware breakpoint at firmware's synchronous vector); TCG does not check SCTLR_EL1.SA there, so only HVF saw it. The stack now starts on the next page. virt_early_panic and virt_early_fault: 2 passed, EXIT=0. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
AAVMF waits its platform boot timeout, five seconds, for a key before it boots, unless QEMU hands it a menu wait through fw_cfg, which only `-boot menu=on` does; `splash-time=0` makes that wait zero. Both launch paths pass it for AArch64 (`Arch::boot`); x86-64's is unchanged. Measured on this M4 Pro, QEMU's start to `EARLY PANIC:` with test-early-panic armed, the pinned DEBUG AAVMF, three runs each, interleaved in one session: 5.70, 5.69, 5.70 s without it (the loader's first line at 5.32-5.33 s), 0.69, 0.69, 0.70 s with it (the loader at 0.33 s). This is the delay the owner's ruling of 2026-09-26 meant to remove by moving to a RELEASE build, and it is not the DEBUG build's: Debian 12's RELEASE AAVMF (edk2 2022.11), on the boots where it reached the kernel, took 5.71 and 6.08 s. The RELEASE builds tried did not boot reliably either (Debian 13's 2025.02-8+deb13u1: never reached the loader in six boots; Debian 12's 2022.11-6+deb12u2: 2 of 5), so the pinned firmware stays. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
This branch's AAVMF entry named only BSD-2-Clause-Patent. QEMU builds its ArmVirtQemu firmware with NETWORK_TLS_ENABLE (roms/edk2-build.config at v11.1.0, [opts.common]), and edk2-stable202408's ArmVirt.dsc.inc links the bundled OpenSSL with TLS on (OpensslLib) or off (OpensslLibCrypto). The submodule pins OpenSSL 3.0.9 (openssl de90e54b, VERSION.dat), whose LICENSE.txt is Apache-2.0 and which ships no NOTICE; the text is added as licenses/Apache-2.0-OpenSSL.txt, and the section and both ledger rows say BSD-2-Clause-Patent AND Apache-2.0. OVMF's record has the same gap and is not this branch's: issues/build/ovmf-s-licence-record-names-no-openssl.md. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
Found running the fast-tier A/B for PR #524: three suites at once in one worktree, on main and on the branch alike, lost four of thirty runs to a C corpus link that found no libtoyos_libc.a mid-rebuild. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
At EL2 the loader reads HCR_EL2.E2H and ID_AA64MMFR4_EL1.E2H0 (by its encoding, S3_0_C0_C7_4, which an older CPU reads as zero) before ExitBootServices, says what it found on the firmware console, and refuses a CPU without FEAT_E2H0, where E2H is RES1 and the kernel's drop cannot clear it. The kernel's own tbz branch to refused_hcr_el2_e2h is deleted: the read-back compare that follows it already refuses an E2H held set, since the declaration has it clear, and no test could tell the two apart. virt_early_panic and virt_el2_drop now assert the loader's line. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
RelaCounts::for_executable in toyos-elf refuses a TLSDESC count, or a group too large for one allocation, and is the only way to an ExeReservation, the capacities parse_rela_entries reserves. The tlsdesc Vec, kept only to be refused, and its as_relas arm are deleted; the kernel cannot skip the refusal without losing the reservation. toyos-elf's tables test holds both refusals. Static::variant, which nothing called, is deleted. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
A compiler edit loop placed one 583 MB compiler per edit and reclaimed none until --worktree remove. choose now sweeps after it places one; the keyed idle guard keeps the one in use. sweep takes the rust dir it is to sweep, so a linked worktree's call does not resolve the primary again. The test shows a placement removing the compiler it replaced and keeping the one another worktree names. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
… has an owner Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…s branch Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…528) Conflicts, each resolved to carry both sides: - bootloader/src/main.rs: the boot map's ROOT_* names with #530's update imports. - src/image.rs: main's slotted layout, with `arch` threaded into create_boot_image and create_esp_volume for the removable loader's path. - src/build.rs: main's Parts, shipped_parts and signing, keyed per architecture: loader_key takes the arch, the embedded key and the floor scope; root_image_key the plan (config and arch) and the key; build() writes image_for(plan.arch). - tests/common/qemu.rs: a test's own firmware variables file where it names one, and otherwise the architecture's pflash. - toyos-window is 0.19.0, past main's 0.18.0, since its scanout drain moved into arch modules here; both lockfiles take main's and that version. - tests/common/update.rs names the x86-64 plan and image. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
#528's tests/iced-counter locks the path toyos-window, which the merge moved past main's 0.18.0. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
fpu_isolation's probe thread is joined as NotFound on main at 17eb66a, measured there by checking the base out in this worktree; the guest lanes leave the toolchain tarball in their TMPDIR. quiesce_stops_the_machine and sched_check_build recur under another worktree's load. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
|
Hand-back for round 2, at head BLOCKER 1, the hosted-rustc build. The hosted build now runs with
BLOCKER 2, the arch rules. A glob of NOTEs.
Merge. Gates. At
Filed. Main's defects the nightly exposed:
|
…he moved syscall and fpu paths This branch lets a worktree whose fork compiler differs build its own, and moves the syscall layer out of arch/ and fpu.rs into arch/x86_64/. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
|
Round-2 BLOCKERs closed and judged at 8c5be84: the hosted-rustc build runs with lld off and a host-only lld pass restores rust-lld (nightly 36273557690: portability-linux, portability-macos, build and host green; the negative control is 446447c's run 36263298060, red on all three trying to build LLVM for ToyOS); the arch rules refuse any glob or rename of core/std outside arch modules (revert EXIT=101). The guest lanes' reds trace to main (#529's TMPDIR check vs the toolchain tarball, fpu_isolation red at main's own base) and are filed. src/ and kernel/ CLAUDE.md updated by the orchestrator at 50a1939. Landing. |
#524 landed. Its move of kernel/src/arch/syscall/proc.rs to kernel/src/syscall/proc.rs carries the join fix. release.rs and sourcegate.rs merged clean. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
issues/build/a-guest-lane-leaves-the-toolchain-tarball-in-its-tmpdir.md is fixed by 80e657c: the install stages in a `toyos_tmpdir::TempDir`. issues/kernel/fpu-isolation-s-thread-join-answers-not-found-on-main.md is fixed by 05839b7: the join keeps the answer its collect gave. The landing it left unbisected is #513, whose `watch::wait_until` re-runs the predicate that collects. Nothing cites either file. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
Every conflict carries both sides: - rust: pinned to 209f10ded88 on the fork's wt-toyos-lld, which holds this branch's a84b4aa849c and main's c34ecdf0ab6 (merged as 3980fb86829, with none of toyos-inbox's other lines) and moves the linker, its flavor and -Bsymbolic into base::toyos for both ToyOS targets. - src/toolchain.rs: #524's write_config shape (Arch::ALL guest targets, HOST_LINKER_PIN, the reassembly and its rust-lld assertion), with every userland target naming rust-lld and rpath = false; the hosted build names ci-llvm's lld for the hosted architecture. toyos-ld's build, witness and install path stay deleted. - src/sysroot.rs: std_config(compiler, ...) names the compiler's rust-lld for every architecture; RECIPE keeps main's text and this branch's "linked by rust-lld; 3". - src/arch.rs: links_through_toyos_ld is deleted, since no architecture does. - src/build.rs: main's GuestEnv fields without PATH; src/release.rs: main's HOSTED_ARCH paths without the toyos-ld witness. - kernel/src/loader/tls.rs, toyos-elf/src/tls.rs: main's variant I/II layout, with variant II's executable placed at its extent (exe_extent). - toyos-bootmap: main's typed Plan, with the loader's image as a third input on both architectures, counted against one directory budget. - bootloader/src/main.rs: main's stack rounding kept as the one fix, main's enter_kernel, and the loader image read off LoadedImage into the Plan. - kernel/, bootloader/.cargo/config.toml: main's, without toyos-ld; the bootloader keeps /Brepro and /DEBUG:NONE. - kernel/src/arch/x86_64/paging.rs: main's; its WindowProt comment moved to kernel/src/mm/policy.rs and takes this branch's wording there. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
Conflicts, each resolved onto main's layout with this branch's change kept: - rust: pinned at 64a2050c484, this branch's 23783dc3524 merged with main's c34ecdf0ab6 in the fork (`wt-toyos-logtrack`), and merged into `toyos-inbox` (645d1c70732). `git merge-base --is-ancestor` gives 0 for both pins against it and for it against `origin/toyos-inbox`. Its `compiler/` is c34ecdf0ab6's byte for byte; the inbox head 26f662d303a was not taken because it also carries x86-64's switch to rust-lld, another branch's compiler change. - panic.rs: main moved `halt_all_cpus` here from `apic.rs`; this branch's version stops the other CPUs first and has no wait for `/log`, so `wait_for_log_file`, `LOG_FILE_DRAIN` and `LOG_DRAIN_EXPIRED` go from panic.rs as they went from apic.rs, and `tests/common/power.rs`'s check for that string, which the kernel no longer prints, goes with them. - serial.rs: this branch's two locks and burst writers over main's `arch::console_uart`, `serial_lock::BackendLock` and `IrqGuard`. The burst is `console_uart::TX_BURST`: 16 on the 16550, whose THRE means an empty FIFO, and 1 on the PL011, whose TXFF clear promises one byte. - clock.rs: the clock page is published from main's `set_counter`. - paging.rs: `translate_writable` on main's x86-64 `AddressSpace`, and an uninhabited stub on AArch64's. - virtio.rs: `publish` and `Doorbell` on main's barriers: the doorbell is an `Mmio` write, which orders the idx before it. - loader/tls.rs: main lays out variant I (TP+0 the DTV pointer, TP+8 zeroed); `toyos_abi::TCB_TID` is TP+8 there, so the tid write does not touch the DTV pointer. - toyos-window goes to 0.20.0, past main's 0.19.0; toyos-abi 0.16.0 and toyos 0.18.0 are already past main's. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
The merge took #524's `pub(crate)`; tests/common/compile.rs links every C test with the sysroot's rust-lld through it, from outside the crate. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
Red on main's nightlies at e8d7c9c and fd62f56 and on #524's, in two shapes; green alone on the dev host. No issue carried it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…nightly and this branch's Found in this branch's nightly run 36287592139; the same signature is in #524's branch nightly, so it predates the linker switch. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…, #531, #534) into the Netstack3 netd Two conflicts. tests/common/origin.rs: main added `volumes` to the imports this branch had split to name `segment`'s NEIGHBOUR and NEIGHBOUR_MAC; both kept. userland/Cargo.lock: main's lock taken whole and re-locked against this branch's manifests (`cargo metadata`), which adds the mirrored crates and their dependencies and drops smoltcp and its defmt. The rust gitlink fast-forwarded to main's fbf6ad143d8; this branch's pin (7a809b7591f) is its ancestor, and the branch made no fork commit of its own. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…ows, and file the harness's misreport quiesce_stops_the_machine was disabled behind a finding whose title and exit rested on the harness's message that the guest asked for a reboot and stayed up. PR #566's capture at 74f7d71 refutes that: writer 5's first pass ran from 2.170 s to 7.434 s, the job printed "5 of 6 writers reached their loop in 5s" at 6.874 s and exited 1, and it never printed "asking for the reset". No stop began. PR #524's capture at 235c5a5 shows the same with "3 of 6", and nightly run 36351950439 on PR #555 at d265676 with "4 of 6". - The slow pass is the defect quiesce_dump_holds_the_stopped's issue already tracks, whose exit names a first write-and-fsync pass over 5 s. That issue is renamed to what both tests show, and gains these sightings. Both rows point at it. - The stops finding is folded into it and deleted. It carried no durable line for a module header; its three sightings move with their evidence. Its a58abf5 sighting also had no stop: record, and whether that job printed its give-up line was not recorded. - stopped_boot waits its whole QMP budget and then calls returned_to_firmware before it reads the console, so a job that never asked is reported as a guest that asked. In the #566 capture every scheduler heartbeat from 10.750 s to 253.244 s was idle, and the test went red after 266 s. Filed as tooling, held by the orchestrator. - The park issue is renamed: its records show 0 block operations open, so both threads were running, and one was the held thread, which last::hold keeps spinning while a sweep counts 2. It now names each sighting's PR and head, adds the 98e803c stop that gave up, labels the dispose_yield suspect as a hypothesis, and records that woken_by_its_threads has no enabled caller. Its exit asks for an instrument that names each thread still running, and for that coverage back. - Deleted: the scratch log names and paths, "after a stop that reported every thread stopped", "on a loaded host", and the build/ park issue's "--known-red answers NO". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W6rME2DoqwjcYFStYHHY4j
A compiler is bootstrap's stage 2 of compiler/rustc and library, so the stage2 a store keeps carries a std for the host, which every guest crate's build scripts and proc macros link, and its key read nothing of library/: a fork commit moving only library/ kept the stored compiler with the std of the commit before. True of a worktree's compiler since #524 and of every compiler since the primary's became keyed. The orchestrator's ruling: fixed here, at the price of a compiler build for a fork commit that moves std (about 5 minutes on an idle host, on top of the freestanding libraries and the sysroot such a commit builds already). every_source_of_a_compiler_moves_its_key holds it. Every compiler's key moves, and each freestanding libraries' and sysroot's with it; the LLVM's does not. The two refusals in sysroot.rs of a product whose sources moved while it was built stay untested, since both run bootstrap inline: filed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RvnWQFcMuGqTHYhvSnTe8A
The ARM64 port (
issues/kernel/toyos-runs-on-arm64.md), stages 0 to 3, as one PR: the groundwork shared with x86, theaarch64-unknown-toyostarget with std and the userland, then the UEFI loader and the kernel reaching the PL011 on QEMUvirtunder HVF. AArch64 links through rust-lld (owner's ruling, 2026-09-26); x86-64 links as it did.What a boot does now, on this M4 Pro:
qemu-system-aarch64 -M virt,gic-version=3 -accel hvfwith AAVMF loads the loader off the USB stick. The loader reads ROOT through firmware (ROOT now carries the AArch64 userland), says at which EL firmware handed it the CPU (at EL2 withHCR_EL2.E2HandID_AA64MMFR4_EL1.E2H0, refusing by name a CPU whoseE2His RES1), and hands off. The kernel drops from EL2 when entered there, writes its control-register declaration whole, turns on the loader's tables, installs its vectors, finds the PL011 through SPCR, and prints the boot log, the memory map, the MADT's GIC structures and the GTDT's timers. It then stops at the first item a later stage owns, with anEARLY PANICon serial and on the ramfb panel:-cpu max: the kernel's own page tables (stage 4).Every stand-in for later work is an
owed!that panics naming its stage; none returns a guess.Stage 0: the shared groundwork, on x86
One
Arch(src/arch.rs) names every triple, QEMU binary, firmware, CPU and accelerator, and now which linker an architecture's guest binaries use (links_through_toyos_ld) and QEMU's-bootfor its firmware. The build,cargo run, CI and the harness read it.toyos-elftakes a machine:EM_AARCH64andR_AARCH64_RELATIVE, so the loader reads an AArch64 kernel. toyos-ld is byte for byte main's (see "The linker").The kernel's
arch/isx86_64/behind one selector (arch/mod.rs):IrqGuardreplaces three hand-rolled ones, and fixed theirnomemcompiler-ordering bug;Hwboundary live underarch/x86_64/, and generic code names each by concept (keyboard_controller,irqchip,trap,iommu_unit,cpu::counter, …).Mmiocarrieswritel/readlordering (arch::barrier: a compiler barrier on x86,DMB OSHST/OSHLDon AArch64). Every descriptor-then-doorbell site names its barrier (dma_wmb/dma_rmb). Three ordering bugs fixed on the way:TrbRing::putwrote the cycle bit with the body;The architecture rules (
src/sourcegate.rs,ARCH_RULES):target_archonly there or in a selector;arch::x86_64::/arch::aarch64::from generic code;core::arch/std::archcount in any spelling a line scan can resolve: the path, ause … asof it, anarchelement of acore::{…}group. A glob or a rename ofcore/stditself is refused outright, outside the places:use core::*;,core::{*},use core as k;,extern crate core as k;,core::{self as k},use {…, std as k}. What those bring into scope is a question for name resolution, which a line scan does not have; following each alias to a laterarchwould be one more pattern per spelling and never sound, while refusing the import is, and no file outside an arch module has another use for either (the whole-tree rule is green). A rename counts only in an import item, socore as u32on a variable andcrate::core as care not reds; the fixture holds both.Each rule has a fixture test that plants a violation and reds naming the rule and its places. libc, toyos-window and metalprobe now keep theirs in
arch/modules (stage 1). The guest probes are the declared rows left (issues/build/assembly-outside-an-arch-module-in-userland-and-guest-probes.md).The
Relaxedaudit: all 761 lines namingRelaxedunderkernel/src(84 files) read.drivers/panic_console/published.rs) with a loom model.panic.rs's crash evidence,hardlockup's spin record, and the USB fault staging.drivers/serial_lock.rs): a losttry_lockonce built the guard it had lost and released the holder's lock when it dropped. The lock is a file kernel-loom compiles, andkernel-loom/tests/serial_lock.rsmodels it.Mmio's contract says a write orders prior stores only, aswriteldoes: a doorbell that hands back entries this CPU read is ordered by dependency alone.Stage 1:
aarch64-unknown-toyos, std and the userlandc34ecdf0ab6, this branch's851c6bfcaa8merged with Where everything lives: the layout as ruled, and its first stage #531's80ea645f83b, and merged intotoyos-inboxas2434fb9d1bc.aarch64-unknown-toyosis the ToyOS base options with+v8a, 128-bit atomics and rust-lld (GNU flavour). Outline atomics are off: their helpers ask the OS through an auxiliary vector ToyOS does not have._startreads argc/argv off the stack and ends the frame chain.TPIDR_EL0, DTV in its first word.851c6bfcaa8deleted the one this branch had written with no caller, and the kernel refusesR_AARCH64_TLSDESCby name. An executable is refused on its count:RelaCounts::for_executablereturnsExeRefusal::TlsDescriptor(orTooLarge), and it is the only way to theExeReservationwhose capacitiesparse_rela_entriesreserves, so the kernel cannot skip the refusal and still reserve. A library is refused inrela::validate(RelocError::TlsDescriptor). The resolver returns in the port's stage 5 with its loader half and a dlopen test.userland/libc/src/arch/):_start, memcpy/memmove/memset and the square roots, one module per architecture, with their AArch64 versions (pair loads/stores with a byte tail,fsqrt).toyos-libc-copiescompiles that module for the host and holds every copy, fill and square root against Rust's own; each host architecture checks its own module.GUEST_TARGETSgains the AArch64 userland target. Every architecture has a userland, soUSERLAND_ARCHSand the plan'sUserlandswitch are deleted: itsAbsentarm was unreachable andwithout_userlanduncalled.cargo run -- --arch aarch64 --build-onlybuilds std,libtoyos_cand the userland for AArch64 and puts them on ROOT. This includes toyos-ld and toyos-cc as guest programs.NOT_YET_BUILT): calc, snake and doom. softbuffer's and winit's toyos forks resolve the publishedtoyos-window 0.2.0, whose framebuffer is x86-only (issues/build/the-toolkit-forks-resolve-an-x86-only-toyos-window.md). Existing Rust GUI apps on ToyOS: iced on one winit backend, paced redraws, forks on one branch per base #528 moved those forks and is merged here. Whether they now build for AArch64 was not measured, so the entry stands.DSB ST), so toyos-window is 0.19.0: main's is 0.18.0 (Existing Rust GUI apps on ToyOS: iced on one winit backend, paced redraws, forks on one branch per base #528).src/compiler.rs).check_compilerrefused a fork checkout whosecompiler/differed from the primary's, which contradicted the landed rule that every worktree builds the toolchain its own sources name.compiler/is the one the primary recorded, the worktree uses the primary'sstage2.build/toyos-compiler), with rust-lld. It is placed atrust/build/compilers/<key>/.src/identity.rs) ofcompiler/,src/bootstrap,src/tools,src/stage0andCargo.lock, thesrc/llvm-projectcommit, and the recipe. It is content only, so committing what was built as local changes is the same compiler.stage2is is refused by name; it used to read as empty and send every worktree to build its own.stage2, not its record, not the rustuptoyoslink (a sysroot is named by directory). The global lock is not taken.buildlock::Keyed, shared with sysroots) orders before the sysroot key's.--worktree removeand every placement sweep unnamed compilers; the one in use is held and stays, so an edit loop keeps one compiler, not one per edit.hosted-rustcis refused in such a worktree: the hosted rustc is the primary's.The linker
By the owner's ruling, AArch64 links through rust-lld and toyos-ld is frozen. This branch's toyos-ld work is reverted by a new commit, leaving toyos-ld identical to main: the machine parameter, the ARM64 PE, the AArch64 TLS descriptors, and the AArch64 GOT/PLT fixes. Net for the ruling's two commits: −750 lines (toyos-ld −784, the rust-lld wiring +34).
lld = true, which puts rust-lld in every stage's sysroot. Bootstrap names the AArch64 userland's linker rust-lld withrpath = false: bootstrap's-Wl,-rpathis a cc driver's argument.lld = false, and the hoststage2is then reassembled withlld = true(build_hosted_rustc,write_config). Underlld = true, bootstrap'sAssemblefor thex86_64-unknown-toyosstage-2 compiler ensuresLldWrapper, hencellvm::Lldand a cmake LLVM for a ToyOS host (forkcompile.rs:2474). Underlld = falsealone,Sysroot::runremoves the hoststage2on every assemble (compile.rs:1938), and the rust-lldstd_confignames goes with it. So the hosted build runs withlld = false, and the host-only build then runs once more underlld = true. That run compiles nothing new and reassemblesstage2with rust-lld. It is Link everything that boots with rust-lld, and freeze toyos-ld #532's shape, which the two reconcile on.stage2and naming it. Every AArch64 link namesrust-lldby name and finds it in the sysroot it is compiled with, and each sysroot is cloned fromstage2. A copy elsewhere would fixstd_configand leave every userland link without its linker. It would also be a second copy of bootstrap'scopy_lld_artifacts, andgcc-ld/would be left out.default-linker-linux-overrideis pinned"off"in every config that builds a host compiler (HOST_LINKER_PIN, inwrite_configand the worktree compiler'sconfig_text). Bootstrap ties that override tolldforx86_64-unknown-linux-gnu:lld = truesetsCFG_DEFAULT_LINKER_SELF_CONTAINED_LLD_CC, whichrustc_targetreads throughoption_env!. The toggle would rebuild the host rustc on Linux in both the hosted build and the reassembly."off"is what main'slld = falsealready gave, so no host rustc changes, and on macOS nothing changes at all. The worktree compiler's recipe moves with it: rebuilt here in 13 s.assert_toolchain_is_honestrefuses by name a toolchain with norust-lld(toolchain::rust_lld). The reassembly asserts it too.-pie -z notext. PIC codegen is not usable there: its GOT slots hold virtual addresses the MMU-off entry cannot use.…908. On the M4 the firststp x29, x30, [sp, #-16]!took an SP alignment fault (ESR_EL10x9A000000, read through QEMU's gdb stub on a hardware breakpoint at firmware's vector). TCG does not checkSCTLR_EL1.SAthere. The stack now starts on the next page.Stage 2: the loader on AArch64
aarch64-unknown-uefithrough rust-lld; reads ROOT through firmware block I/O.toyos-bootmaptypes a leaf by concept:Cache::{Firmware, Memory, Device, Scanout}under aTyping. On x86 that is the MTRRs, unchanged; on AArch64, firmware's write-back ranges, with a part-memory 2 MiB page refused. It encodes both architectures' descriptors, host-tested against the Arm ARM's bit positions. The oneMAIR_EL1is declared beside them.DEBUGbuild, pinned by hash inNOTICEandsrc/licence.rs. It carries OpenSSL 3.0.9 (Apache-2.0), now recorded: QEMU builds it withNETWORK_TLS_ENABLE, and ArmVirtQemu links the bundled OpenSSL either way.Arch::bootpasses-boot menu=on,splash-time=0for AArch64 on both launch paths (see Measured).TLS, in each machine's variant
The kernel built x86-64's variant II for every thread; the AArch64 std reads its DTV at
TP+0, and rust-lld resolves an AArch64 executable's own local-exec accesses toalign_up(16, p_align)above TP.toyos_elf::tlsnow lays out either variant from aStaticwhose alignments are powers of two by construction, withtpoffper variant. The kernel takes the variant from its ELF machine, places the executable first in variant I and last in variant II, and writes each TCB:[self, DTV]or[DTV, reserved]. x86-64's arithmetic is unchanged.Stage 3: the kernel on
virtkernel/src/arch/aarch64/:HCR_EL2first, anISB, and a read-back that halts inrefused_hcr_el2_readbackon any difference from the declaration, before any EL1 register is written; then the drop.E2Hheld set is one such difference, since the declaration has it clear, and the loader refuses that case by name first, where the console still prints. The kernel's separateE2Hbranch is deleted. At EL1, MMU off-then-on. The declaration's read-back asserts EL1 onSP_EL1;ELR,FAR, the registers and a backtrace, then panics;toyos-acpidecodes SPCR, GTDT and the MADT's GIC structures, as far as stage 3 reads them.panic::halt_all_cpus).-Adead_code -Aunfulfilled_lint_expectationsuntil stage 7 (issues/kernel/the-aarch64-kernel-builds-with-dead-code-allowed.md).The harness
Profile::Virtis QEMUvirt: GICv3, AAVMF, ramfb, the stick on xHCI, the PL011.Profile::VirtEl2is the same machine withvirtualization=onunder TCG-cpu max, where AAVMF hands the loader the CPU at EL2.Profile::archnames every variant, with no wildcard. Three tests are inTier::Local, because no hosted runner boots AArch64 yet (stage 8), and each waits on its last asserted line withdrain_until:virt_early_panic: the loader'sCPU: entered at EL1, the boot log on the PL011, and the panic on both channels in its fatal colours;virt_early_fault: an undefined instruction, reported by the vectors;virt_el2_drop: the loader'sCPU: entered at EL2, HCR_EL2.E2H …, ID_AA64MMFR4_EL1.E2H0 0x0,HCR_EL2read back as declared, dropped, and the early panic after it.The entry's EL2 writes of
CNTHCTL_EL2,CNTVOFF_EL2andCPTR_EL2can each be deleted andvirt_el2_dropstays green, because stage 3 reads no counter and runs no FP. The track now records those three mutations under stage 4, whose tests are the first that can red on them.Measured
Boot to
EARLY PANIC, AArch64 under HVF withtest-early-panicarmed, from QEMU's start. Pinned AAVMF, three runs per arm, interleaved in one session:-boot menu=on,splash-time=0virt_early_panicandvirt_early_faultnow take 2 s each (7 s before).The RELEASE AAVMF of the owner's ruling was tried and not adopted. The ruling's premise, that the delay is the DEBUG build's, is refuted, and the RELEASE builds tried do not boot reliably:
qemu-efi-aarch64_2025.02-8+deb13u1(licence checked: BSD-2-Clause-Patent AND Apache-2.0, OpenSSL 3.4.0 and Mbed TLS): never reached the loader in six boots.2022.11-6+deb12u2: 2 of 5 boots, and those took 5.71 and 6.08 s.AArch64 userland: the whole workspace but calc/snake/doom builds and links through rust-lld (
cargo run -- --arch aarch64 --build-onlyEXIT=0).initis anET_DYNAArch64 PIE whose PT_TLS is 64-aligned and whose only dynamic relocations are RELATIVE.The primary's bootstrap config, run once.
write_config's exact text for this host (lld = true, the host and six guest targets,toyos-ldforx86_64-unknown-toyos,rust-lldwithrpath = falseforaarch64-unknown-toyos), with one line added,build-dirpointing at the worktree fork checkout'sbuild/toyos-compiler, run as./x build --stage 2 --warnings warnon the fork atc3cb792df3f: EXIT=0 in 4 min 6 s. It built stage1 std for all seven targets and uplifted each to stage 2, andrust-lldis in the stage 2 sysroot. With thatstage2, a std hello-world links foraarch64-unknown-toyos(rust-lld, a static PIE) and forx86_64-unknown-toyos(toyos-ld), EXIT=0 each. What it does not exercise: a fresh build directory (it reused the keyed compiler's, 14 GB, which grew to 15 GB), and the primary's ownbuild/, which holdslld = falseartifacts. A fresh one needs more than the 21 to 29 GB this disk had free during the session; the primary's ownbuild/aarch64-apple-darwinis 47 GB.The hosted-rustc sequence, from a fresh clone (nightly run 36266825579 at
d8bee311; each job runsfull_bootstrap, then the hosted rustc, then the reassembly, then the sysroot). Bootstrap's ownBuild completedtimes:lld = false)lld = true)portability-linux(debian sid)portability-macosbuild(--ci toolchain, ubuntu)The reassembly compiled no crate on either host, which is what the Linux pin is for.
One compiler of a worktree's own costs 583 MB at
rust/build/compilers/<key>/and 15 GB of incremental build in the worktree's fork checkout (build/toyos-compiler). Its key takes 1.3 to 2.1 s to compute on this fork, 0.6 s of itsrc/tools.Mmioon x86, the x86 kernel built at3f3ae53e^and at3f3ae53ewith one toolchain:.textis0x1c4000both times; the text symbols sum to 1,753,507 and 1,753,519 bytes (+12) and the disassembly has 471,322 and 471,377 instructions (+55, +0.012%). The commit also moved NVMe's and xHCI's reads, so +55 is an upper bound on what the compiler barriers cost.virt's MPIDRs: with 17 CPUs (TCG,-cpu max, GICv3) the MADT names CPU 16mpidr=0x100, whichhardware_id() & 63puts on slot 0.Merging main (#530, #528)
origin/mainmoved tofd62f567during this round and is merged (a1a143f8). Seven conflicts, each resolved to carry both sides:bootloader/src/main.rs: the boot map'sROOT_*names, with Self-update, stage 1: signed A/B slots, ssh … update < image, swap over exec #530's update imports.src/image.rs: main's slotted disk, witharchthreaded intocreate_boot_imageandcreate_esp_volumefor the removable loader's path.src/build.rs: main'sParts,shipped_partsand signing, keyed per architecture.loader_keytakes the arch, the embedded key and the floor scope;root_image_keytakes the plan and the key;build()writesimage_for(plan.arch).tests/common/qemu.rs: a test's own firmware variables file where it names one, and otherwise the architecture's pflash.tests/iced-counter.tests/common/update.rsnames the x86-64 plan and image.The AArch64 loader reads the slotted layout:
virt_passes on the merge.Gates, each with its exit code
At
235c5a5b, the merge plus the iced-counter lock. The head,8c5be843, adds only issue files.cargo run -- --ci host: 45 steps, all green, EXIT=0. The whole output was read; itsFAILEDlines are the loom controls' expected reds and the worktree tests' planted refusals.cargo run -- --ci abi-split: EXIT=0 (toyos-window 0.18.0 -> 0.19.0). The first run, before the iced-counter lock followed, was EXIT=1 and named that lockfile.cargo test -- virt_: 3 passed, EXIT=0 (ata1a143f8, whose tree differs only in that lockfile). Before the merge it also passed after the last mutation below was restored.cargo test --manifest-path kernel-loom/Cargo.toml: 56 passed, EXIT=0cargo run -- --build-onlyandcargo run -- --arch aarch64 --build-only: EXIT=0 each (ata1a143f8)cargo test: 397 passed, 4 failed, 6 quarantined and green, EXIT=1.--known-redanswers NO for each. The host was loaded throughout by another worktree's spinner at 397% CPU, with a load average of 20 to 28.metal_job_reboot: green alone, and red on main in this PR's earlier A/B (below).lan_mdns_answer: main's SUN_LEN red.quiesce_stops_the_machine: stayed up for 266 s and was green alone. This isissues/kernel/quiesce-stops-the-machine-stayed-up-beside-other-guests.md; the recurrence is appended there.sched_check_build: 85 samples where its p90 needs 100, green alone. Its quarantine row covers another signature, and this one is appended to that row's issue.8846c021, the fast tier gave 400 passed, 2 failed, EXIT=1:lan_mdns_answer, andquiesce_wakes_on_the_last_exit(green alone; appended to its issue).d8bee311(https://github.com/ToyOSOrg/ToyOS/actions/runs/36266825579):portability-linuxsuccess,portability-macossuccess,buildsuccess,hostsuccess.portability-windowsfailed, as on main: it is the declaredcontinue-on-errorfrontier.nothing left in $TMPDIR: … toyos-toolchain.tar.zst. That is main's: the step comes from Every test's scratch goes with it, and a killed run's with the next #529 andrelease::installfrom before it, and main has had no nightly since Every test's scratch goes with it, and a killed run's with the next #529. Filed asissues/build/a-guest-lane-leaves-the-toolchain-tarball-in-its-tmpdir.md.screen_diag_boot,home_budget_refusal_retried,metal_job_reboot,usb_transport_breakandlog_flush_retryare each red on main's last nightly too (run 36228604597).fpu_isolationis not: it passed there. Checked out at main's17eb66a4in this worktree, with the fork at main's pin, it is red the same way (thread_joinanswers NotFound), so it is main's. Filed asissues/kernel/fpu-isolation-s-thread-join-answers-not-found-on-main.md.8c5be843(https://github.com/ToyOSOrg/ToyOS/actions/runs/36273557690):portability-linuxsuccess,portability-macossuccess,buildsuccess,hostsuccess. The hosted-rustc reassembly took 0:05 on Linux, 0:25 on macOS and 0:07 inbuild. Seven guest, tcg and audio lanes had finished at hand-back, and every one was red on the$TMPDIRtarball above. Their suite reds werehome_budget_refusal_retriedandfpu_isolation, both main's as above, plus two tests Storage stage 3, steps 1–4: toyos-blockring, SYS_DEVICE_DMA_MAP, blockd and partition sessions #525 added after main's last nightly, which no main nightly has run:blockd_survives_its_deathandpartition_claim_departure. Those two were not A/B'd, and I do not know whose they are.The two reds of the earlier fast tier (
fef790a6), by a same-session A/B. Main at17eb66a4in a scratch worktree (/Users/jan/Dev/jan/toyos-arm64-ab) and the branch atda1df5f0, five rounds, each round running both tests on both arms at once. Counts are of each run's own exit code; a run whose harness panicked before any guest booted, onissues/build/two-suites-in-one-worktree-race-on-the-c-corpus-libc.md's race, carries no verdict.metal_job_rebootQEMU died before ===READY===), 1 no verdictlan_mdns_answerpath must be shorter than SUN_LEN), 1 no verdictmetal_job_reboot's fast-tier red wasthe job drain carried no kernel output at all (24 bytes), thenALONE ... GREEN; the earlier A/B atd65446ccsaw that signature on main twice in five.lan_mdns_answerisissues/build/a-lane-s-tap-socket-path-is-past-sun-len-on-the-dev-host.md, which Where everything lives: the layout as ruled, and its first stage #531 filed; this branch's duplicate of it is deleted.The two checks
Negative controls, each applied as a patch, shown to build, run, and restored:
try_lockbuilt withthen_some(featureserial-try-lock-then-some, inCONTROLS)kernel-loom --test serial_lock, both modelsmsr hcr_el2deletedvirt_el2_drop(the boot halts silently)SPSR_EL2_TO_EL1=0x3C4virt_el2_drop(running at EL1 on SP_EL0)HCR_EL2declared withE2H, rerun with the kernel'sE2Hbranch deletedvirt_el2_drop(boot timed out beforeEARLY PANIC)e2h0 == 0refuses)virt_el2_drop: the loader prints…: REFUSED, HCR_EL2.E2H is RES1 …on the console and stopsvirt_el2_drop("CPU: entered at EL2, HCR_EL2.E2H " not on the PL011)446447caportability-macos,portability-linuxandbuildeach fail building LLVM forx86_64-unknown-toyos, reached fromAssemble→LldWrapper→llvm::Lld, thenthe hosted rustc build failed. On macOS cmake's compiler check fails (ld: unknown file type); on Linux,Building LLVM for x86_64-unknown-toyosfinds no ninjaassert_toolchain_is_honest'srust-lldrefusal deletedtoolchain::tests::a_toolchain_bin_without_cargo_is_one_rustup_narrates17f930eb's function hunks), the fixtures keptsourcegate::tests::a_pure_crate_that_names_an_architecture_is_redfor_executable's TLSDESC refusal deletedtoyos-elf --test tables(an_executable_s_reservation_is_had_only_through_its_refusals)compiler::tests(placing a compiler left the one it replaced)PAGE)virt_early_panicunder HVF: silent after the handoff, the image ending at…039toyos-elf --test tlsR_AARCH64_TLSDESCrefusal (rela::validate) deletedtoyos-elf --test tablescore::arch::as a substring, as beforesourcegatefixture tests, bothsweepwithout itskeyed_idleguardcompiler::testscompiler::testscopy_backward's chunk test reading 8, not 16toyos-libc-copiesEarlier on this branch, and unchanged: every worktree given the primary's compiler, and a worktree's compiler placed beside the primary's
stage2(compiler::tests, EXIT=101 each); the seqlock writer's fence removed (panic_console_publish, EXIT=101);VBAR_EL10x800 off (virt_early_fault, EXIT=1).Independent oracles:
aarch64-unknown-toyosexecutable with a 64-alignedPT_TLSaddresses.tdataoffsets 0 and 0x40 atTP+0x40andTP+0x80(add x0, x8, #0x40/#0x80), whichvariant_i_agrees_with_what_lld_linkedasserts.virtualization=on) and AAVMF, for the drop.SCTLR_EL1RES1 bits and the stack alignment TCG let pass, and reds the unrounded-stack control.copy_within,fillandsqrt, for libc's module on the host.virtand edk2's ArmVirtQemu; the Arm ARM, for every barrier and descriptor bit.portability-linux,portability-macos), and the fork's bootstrap source (compile.rs:1938,:2474;config.rs'sdefault_linux_linker_overrides) for why each config line is there.What I am unsure of
E2H0refusal is shown only by its inverted control: no QEMU CPU model lacks FEAT_E2H0, so the refusing arm has not run on a CPU that needs it.ID_AA64MMFR4_EL1is read by its encoding on the Arm ARM's word that the ID space reads as zero where a register is not implemented.stage2has norust-lld: the guest std it would link is already built by then. The nightly is the evidence, not a test of that premise.--arch aarch64 --build-onlyhas run only with this worktree's keyed compiler. The nightly's fresh-clone jobs build the x86 image; their sysroot step builds the AArch64 std too, with therust-lldstd_confignames in the reassembledstage2, but no AArch64 userland links there.PAR_EL1's inner nibble:boot_map_write_combiningaccepts0b0000as well as0b0100, because HVF reports0x40.src/CLAUDE.mdstill says a fork commit with anothercompiler/is refused; the orchestrator owns that file.What #532 adopts when it reconciles
write_configandstd_configBoth branches build the hosted rustc under
lld = falseand then rerun the host-only build underlld = true. What #532 does not have, and must take from here:HOST_LINKER_PINinwrite_configandcompiler::config_text:[target.<host>]withdefault-linker-linux-override = "off". Without it, Link everything that boots with rust-lld, and freeze toyos-ld #532'slld = !with_hosted_rustcflipsCFG_DEFAULT_LINKER_SELF_CONTAINED_LLD_CCfor anx86_64-unknown-linux-gnuhost between the two builds.rustc_targetreads it throughoption_env!, so the host rustc is rebuilt in both on Linux, and Linux host rustc's own default linker changes from main's.std_config(compiler, …): it takes thestage2of theCompilera worktree resolved (src/compiler.rs), nottoolchain::stage2(rust_dir), and namestoolchain::rust_lld(compiler)by path. A worktree whose fork has its owncompiler/builds its std with its own compiler's linker. Per architecture it loops overArch::ALL, andlinks_through_toyos_ld()chooses the linker: toyos-ld for x86-64 here. Link everything that boots with rust-lld, and freeze toyos-ld #532's x86 switch makes thatfalsefor both, and thetoyos_ldparameter goes.write_config's guest targets:Arch::ALL, withlinker = "rust-lld"andrpath = falsefor each target that does not link through toyos-ld. Link everything that boots with rust-lld, and freeze toyos-ld #532's hosted build namesci-llvm/bin/lldby path forx86_64-unknown-toyos, because the hosted build's sysroots carry no rust-lld. That stays Link everything that boots with rust-lld, and freeze toyos-ld #532's for x86. AArch64 keepsrust-lldby name, since it links nothing during the hosted build.rust_lld()and therust-lldrefusal inassert_toolchain_is_honestare Link everything that boots with rust-lld, and freeze toyos-ld #532's own shape and names (herepub(crate)); the refusal test is the same.compiler::RECIPEmoved to…, host linker pinned; 3.sysroot::RECIPEis untouched here. Link everything that boots with rust-lld, and freeze toyos-ld #532 moves it to…, linked by rust-lld; 3, which is right for the merge.Filed
issues/kernel/the-saved-kernel-context-names-x86-registers.md(stage 4)issues/kernel/msi-and-pin-routing-take-an-x86-vector-and-apic-id.md(stage 4)issues/kernel/the-boot-timing-handoff-is-named-for-the-tsc.md(stage 4)issues/kernel/the-crash-evidence-records-x86-fault-registers.md(stage 5)issues/kernel/the-aarch64-kernel-builds-with-dead-code-allowed.md(stage 7)issues/build/the-primary-rebuilds-its-compiler-on-compiler-alone.mdissues/build/the-toolkit-forks-resolve-an-x86-only-toyos-window.mdissues/build/ovmf-s-licence-record-names-no-openssl.mdissues/build/a-defect-only-contention-exposes-is-classified-as-a-wrong-sched.mdissues/build/issue-files-cite-paths-that-moved.md: citations that name paths this branch moved; whether a gate belongs is the owner's, sincesrc/CLAUDE.mdsays documentation carries no gates.issues/build/two-suites-in-one-worktree-race-on-the-c-corpus-libc.mdissues/kernel/fpu-isolation-s-thread-join-answers-not-found-on-main.md(main's, measured at17eb66a4)issues/build/a-guest-lane-leaves-the-toolchain-tarball-in-its-tmpdir.md(main's)issues/kernel/quiesce-dump-holds-the-stopped-reds-wide-with-usb-transport-breaks.md, the part-typed 2 MiB page to the boot-map issue, the port's stages toissues/kernel/toyos-runs-on-arm64.md, with the three EL2-write mutations stage 4 is the first to red, and the loaded recurrences ofquiesce_wakes_on_the_last_exit,quiesce_stops_the_machineandsched_check_buildto their issues.issues/build/the-primary-rebuilds-its-compiler-on-compiler-alone.mdis assigned to the ARM64 track, closed before its stage 4 lands.🤖 Generated with Claude Code
https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK