Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
085b67e
toyos-elf, toyos-ld: the machine is a parameter, not x86-64
Japabu Sep 26, 2026
c228cef
build: one Arch names every triple, QEMU, firmware, CPU and accelerator
Japabu Sep 26, 2026
0840b25
kernel: arch/ is x86_64/ behind one selector; syscalls, one IrqGuard,…
Japabu Sep 26, 2026
de9c8d6
Merge origin/main
Japabu Sep 26, 2026
3f3ae53
kernel: Mmio carries writel/readl ordering, and every DMA barrier is …
Japabu Sep 26, 2026
2200918
aavmf: QEMU's own edk2-stable202408 ArmVirtQemu firmware, pinned besi…
Japabu Sep 26, 2026
d9ba4df
kernel: the PC's own devices, the page tables and the Hw boundary liv…
Japabu Sep 26, 2026
662724e
kernel: generic code names the machine by concept, and carries no x86…
Japabu Sep 26, 2026
e83a204
Architecture rules: assembly, target_arch and arch paths live where t…
Japabu Sep 26, 2026
5834a92
kernel: shootdown origins, and the syscall window's watchdog, named b…
Japabu Sep 26, 2026
8a86c25
AArch64 stages 2 and 3: the loader and the kernel reach the PL011 on …
Japabu Sep 26, 2026
4f65770
harness: Profile::Virt boots AArch64 under HVF, in a local tier
Japabu Sep 26, 2026
f483270
Merge origin/main: the licence gate (#523)
Japabu Sep 26, 2026
5c3fcee
kernel: the Relaxed audit — one data race and three publications fixed
Japabu Sep 26, 2026
6deeb5c
The host job's gates, green on the port: clippy on both kernels, the
Japabu Sep 26, 2026
f67863c
Merge origin/main: the clean-room bcachefs keys (#526)
Japabu Sep 26, 2026
1e5ef5c
serial: a lost try_lock no longer releases the backend it lost to
Japabu Sep 26, 2026
73a8936
issues: blocking_read_window completed 26 of 500 round trips beside o…
Japabu Sep 26, 2026
1912cc4
toyos-ld: 0.3.0, because this branch changed what it builds
Japabu Sep 26, 2026
7e5ce2e
build: a worktree whose fork names another compiler builds its own
Japabu Sep 26, 2026
e32e876
issues: the harness reads a contention-exposed defect as a Sched, and…
Japabu Sep 26, 2026
398c7e2
toyos-ld: AArch64 TLS descriptors, variant I, and imports through the…
Japabu Sep 26, 2026
63820e9
The port's stage 1: aarch64-unknown-toyos, std and the userland for A…
Japabu Sep 26, 2026
a36ccb9
toyos-ld: back to main's, by the owner's ruling that AArch64 links th…
Japabu Sep 26, 2026
dbe4419
AArch64 links through rust-lld: the userland, the kernel and the loader
Japabu Sep 26, 2026
347f205
AArch64 boots without firmware's five-second wait
Japabu Sep 26, 2026
a083f3b
NOTICE: the pinned AAVMF carries OpenSSL, under Apache-2.0
Japabu Sep 26, 2026
4e6e584
issues: two suites in one worktree race on the C corpus's libc archive
Japabu Sep 26, 2026
a653561
issues: blocking_read_window reds on main too, at the same rate
Japabu Sep 26, 2026
396f5b4
Merge origin/main: every test's scratch goes with it (#529)
Japabu Sep 26, 2026
ce76523
issues: the LAN tests' sockets under the suite's TMPDIR pass the Unix…
Japabu Sep 26, 2026
de4e1ec
issues: drop an unmeasured claim from the socket-path issue
Japabu Sep 26, 2026
0bef702
serial: the backend lock is a file kernel-loom drives, and a lost try…
Japabu Sep 26, 2026
5a1b514
aarch64: HCR_EL2 before any EL1 register, read back, and a boot that …
Japabu Sep 26, 2026
5209a69
TLS: each machine's psABI variant, and R_AARCH64_TLSDESC refused by name
Japabu Sep 26, 2026
daae322
sourcegate: core::arch and std::arch are needles in any spelling
Japabu Sep 26, 2026
1ddfb48
compiler: the key names LLVM and the tools, and a missing primary rec…
Japabu Sep 26, 2026
eb11093
libc: the architecture module's copies, differentially on the host
Japabu Sep 26, 2026
1b69436
issues: the x86 left in generic code, each owned by a stage of the port
Japabu Sep 26, 2026
dbd4d74
Answer the review's notes: Mmio's store-only ordering, an inlined DF …
Japabu Sep 26, 2026
b8f7959
Merge origin/main: storage stage 3, blockd and SYS_DEVICE_DMA_MAP (#525)
Japabu Sep 26, 2026
f1ff3b3
kernel: #525's domain room and lent RAM, named the way this branch na…
Japabu Sep 26, 2026
fef790a
Lint the serial-lock model clean, and measure the MPIDR the slot issu…
Japabu Sep 26, 2026
da1df5f
Merge origin/main: the layout as ruled (#531)
Japabu Sep 26, 2026
aedb839
issues: the primary rebuilds its compiler on compiler/ alone
Japabu Sep 26, 2026
446447c
issues: the lane-socket path issue is main's, filed first
Japabu Sep 26, 2026
48dd778
toolchain: the hosted rustc builds without lld, then stage2 is reasse…
Japabu Sep 26, 2026
17f930e
sourcegate: a glob or a rename of core/std names the arch module
Japabu Sep 26, 2026
bc1fdaa
aarch64: the loader refuses a CPU whose HCR_EL2.E2H is RES1, by name
Japabu Sep 26, 2026
61d8180
kernel: an executable's TLS descriptor is refused on its count
Japabu Sep 26, 2026
df5c698
compiler: every placement sweeps the compilers nobody names
Japabu Sep 26, 2026
8846c02
issues: stage 4 owns the EL2 writes stage 3 cannot red; the key issue…
Japabu Sep 26, 2026
d8bee31
issues: quiesce_wakes_on_the_last_exit lost its READY again, on #524'…
Japabu Sep 26, 2026
a1a143f
Merge origin/main: self-update stage 1 (#530) and the toolkit forks (…
Japabu Sep 26, 2026
235c5a5
sdk: iced-counter's lock follows toyos-window to 0.19.0
Japabu Sep 26, 2026
8c5be84
issues: two main reds the nightly exposed, and two loaded recurrences
Japabu Sep 26, 2026
50a1939
src/ and kernel/ CLAUDE.md: a worktree builds its own compiler, and t…
Japabu Sep 26, 2026
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
4 changes: 4 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ members = [
"toyos-inspect",
"toyos-keymap",
"toyos-ld",
"toyos-libc-copies",
"toyos-logstream",
"toyos-manifest",
"toyos-mdns",
Expand Down
26 changes: 26 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,32 @@ instead — which would put it inside the "comes with QEMU" allowance and delete
6,291,456 bytes from the repository.



aavmf/*.fd — EDK II firmware for QEMU `virt`, BSD-2-Clause-Patent AND Apache-2.0
------------------------------------------------------------

AAVMF_CODE.fd 67,108,864 bytes
sha256 47765fe344818cbc464b1c14ae658fb4b854f5c2ceffa982411731eb4865594d
AAVMF_VARS.fd 67,108,864 bytes
sha256 b3b855c5a80310168051164986855692d1bdb06e67619856177965cd87c6774f

Copyright (c) 2019, TianoCore and contributors
Licence text: licenses/BSD-2-Clause-Patent-EDK2.txt
Copyright 1995-2023 The OpenSSL Project Authors
Licence text: licenses/Apache-2.0-OpenSSL.txt
SPDX-License-Identifier: BSD-2-Clause-Patent AND Apache-2.0

QEMU's own prebuilt ArmVirtQemu firmware, unmodified: `edk2-aarch64-code.fd`
and `edk2-arm-vars.fd` as QEMU 11.1.0 installs them under `share/qemu/`,
whose version string reads `edk2-stable202408-prebuilt.qemu.org` (a
`DEBUG_GCC5` build of 2024-09-12). The variable store is the template: every
boot gives it a writable snapshot QEMU discards, because this `DEBUG` build
asserts on a read-only store. QEMU builds it with `NETWORK_TLS_ENABLE`
(`roms/edk2-build.config`, `[opts.common]`), and edk2-stable202408's
ArmVirtQemu links edk2's bundled OpenSSL either way (`OpensslLib` with TLS,
`OpensslLibCrypto` without): OpenSSL 3.0.9, whose `LICENSE.txt` is the
Apache-2.0 text above and which carries no `NOTICE`.

tests/fixtures/gbae-v0.2.0-* — gbae, MIT
-----------------------------------------

Expand Down
Binary file added aavmf/AAVMF_CODE.fd
Binary file not shown.
Binary file added aavmf/AAVMF_VARS.fd
Binary file not shown.
2 changes: 1 addition & 1 deletion bootloader/.cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ rustflags = [
]

[target.x86_64-unknown-uefi]
linker = "toyos-ld"
linker = "toyos-ld"
2 changes: 1 addition & 1 deletion bootloader/rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[toolchain]
targets = ["x86_64-unknown-uefi"]
targets = ["x86_64-unknown-uefi", "aarch64-unknown-uefi"]
138 changes: 138 additions & 0 deletions bootloader/src/arch/aarch64.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
//! AArch64: the loader's every instruction Rust has no portable spelling for.

use toyos_abi::boot::KernelArgs;
use toyos_bootmap::Typing;

/// The machine the kernel image must be built for: the loader's own.
pub const ELF_MACHINE: toyos_elf::Machine = toyos_elf::Machine::Aarch64;

/// How the boot map's descriptors are encoded.
pub use toyos_bootmap::aarch64 as encoding;

/// How the boot map types memory: by firmware's map, since AArch64 has no
/// range registers to do it and every descriptor names its own type.
pub fn typing(write_back: &[(u64, u64)]) -> Typing<'_> {
Typing::ByMap(write_back)
}

/// The generic timer's virtual count, `CNTVCT_EL0`.
pub fn counter() -> u64 {
let count: u64;
// SAFETY: reads a counter EL1 and EL2 may always read; the `ISB` keeps the
// read in program order.
unsafe { core::arch::asm!("isb", "mrs {}, cntvct_el0", out(reg) count, options(nomem, nostack, preserves_flags)) };
count
}

/// What the loader's report says beside the counter: its rate. Where it counts
/// from is firmware's to say and no register here does.
pub fn counter_origin() -> alloc::string::String {
let hz: u64;
// SAFETY: reads a register EL1 and EL2 may always read.
unsafe { core::arch::asm!("mrs {}, cntfrq_el0", out(reg) hz, options(nomem, nostack, preserves_flags)) };
alloc::format!("CNTFRQ_EL0 {hz} Hz; the counter's origin is firmware's")
}

/// What the loader says about the CPU as firmware handed it over, or why the
/// kernel cannot run on it: entered at EL2 on a CPU without FEAT_E2H0,
/// `HCR_EL2.E2H` is RES1, so the kernel's entry cannot clear it and every
/// `_el1` register its drop programs would be EL2's own. Refused here, where
/// the console still prints; the entry's read-back of `HCR_EL2` stays as the
/// last line of defence.
pub fn cpu_as_entered() -> Result<Option<alloc::string::String>, alloc::string::String> {
let current: u64;
// SAFETY: reads `CurrentEL`, which EL1 and above may read.
unsafe { core::arch::asm!("mrs {}, currentel", out(reg) current, options(nomem, nostack, preserves_flags)) };
let el = (current >> 2) & 0b11;
if el != 2 {
return Ok(Some(alloc::format!("CPU: entered at EL{el}")));
}
let (hcr, mmfr4): (u64, u64);
// SAFETY: at EL2 both are readable. `ID_AA64MMFR4_EL1` by its encoding,
// `S3_0_C0_C7_4`, which sits in the ID space an older CPU reads as zero.
unsafe {
core::arch::asm!("mrs {}, hcr_el2", out(reg) hcr, options(nomem, nostack, preserves_flags));
core::arch::asm!("mrs {}, S3_0_C0_C7_4", out(reg) mmfr4, options(nomem, nostack, preserves_flags));
}
let e2h = (hcr >> 34) & 1;
let e2h0 = (mmfr4 >> 24) & 0xF;
let state = alloc::format!("CPU: entered at EL2, HCR_EL2.E2H {e2h}, ID_AA64MMFR4_EL1.E2H0 {e2h0:#x}");
if e2h0 != 0 {
return Err(alloc::format!(
"{state}: REFUSED, HCR_EL2.E2H is RES1 on a CPU without FEAT_E2H0, and the kernel's \
drop to EL1 needs it clear"
));
}
Ok(Some(alloc::format!("{state}: the kernel's entry writes E2H clear")))
}

/// The smallest data cache line on this machine, from `CTR_EL0.DminLine`.
fn line() -> u64 {
let ctr: u64;
// SAFETY: reads `CTR_EL0`, which every level may read.
unsafe { core::arch::asm!("mrs {}, ctr_el0", out(reg) ctr, options(nomem, nostack, preserves_flags)) };
4 << ((ctr >> 16) & 0xF)
}

/// Every line of `[at, at + len)` cleaned to the point of coherency and
/// invalidated, then `DSB SY` for their completion.
pub fn write_back(at: u64, len: usize) {
let step = line();
let mut addr = at & !(step - 1);
while addr < at + len as u64 {
// SAFETY: `DC CIVAC` cleans and invalidates the line holding an
// address the caller allocated; it changes no memory's contents.
unsafe { core::arch::asm!("dc civac, {}", in(reg) addr, options(nostack, preserves_flags)) };
addr += step;
}
// SAFETY: a barrier; waits for the maintenance above to complete.
unsafe { core::arch::asm!("dsb sy", options(nostack, preserves_flags)) };
}

/// AArch64 has no I/O port space: every caller checks [`pio::EXISTS`] first.
pub mod pio {
pub const EXISTS: bool = false;

/// # Safety
/// Never called: [`EXISTS`] is false.
pub unsafe fn outw(_port: u16, _value: u16) {
unreachable!("AArch64 has no I/O port space")
}

pub fn inw(_port: u16) -> u16 {
unreachable!("AArch64 has no I/O port space")
}
}

/// Hand the CPU to the kernel as firmware left it — its exception level, its
/// identity tables — at the image's physical entry, with `x0 = args`. The
/// kernel's entry switches to the boot map (`args.boot_pml4_addr`) itself (`kernel/src/arch/aarch64/boot.rs`),
/// because at EL2 only the kernel's own drop to EL1 can install it.
///
/// The image is cleaned to the point of coherency first: that entry fetches
/// instructions with the MMU off for a few of them, straight from memory.
///
/// # Safety
/// `args.boot_pml4_addr` is the boot map, `image` is the relocated kernel image, `entry_offset`
/// is its entry point's offset in it, and `args` stays where it is until the
/// kernel copies it.
pub unsafe fn enter_kernel(image: (u64, u64), entry_offset: u64, args: &KernelArgs) -> ! {
write_back(image.0, image.1 as usize);
let entry = image.0 + entry_offset;
// SAFETY: interrupts masked for good — the kernel's vectors are not
// installed yet — then every instruction cache line invalidated against
// the image just cleaned, and a branch to its entry with `x0 = args`, the
// boot protocol `toyos-abi::boot` and the kernel's `_start` define.
unsafe {
core::arch::asm!(
"msr daifset, #0xf",
"ic iallu",
"dsb ish",
"isb",
"br {entry}",
entry = in(reg) entry,
in("x0") args as *const KernelArgs,
options(noreturn),
);
}
}
7 changes: 7 additions & 0 deletions bootloader/src/arch/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//! The machine the loader runs on, and the only part of it that knows which one.

#[cfg_attr(target_arch = "x86_64", path = "x86_64.rs")]
#[cfg_attr(target_arch = "aarch64", path = "aarch64.rs")]
mod imp;

pub use imp::*;
119 changes: 119 additions & 0 deletions bootloader/src/arch/x86_64.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
//! x86-64: the loader's every instruction Rust has no portable spelling for.

use toyos_abi::boot::KernelArgs;

/// The machine the kernel image must be built for: the loader's own.
pub const ELF_MACHINE: toyos_elf::Machine = toyos_elf::Machine::X86_64;

/// How the boot map's entries are encoded.
pub use toyos_bootmap::x86_64 as encoding;

/// How the boot map types memory: by the MTRRs firmware programmed, beneath
/// entries that select plain memory.
pub fn typing(_write_back: &[(u64, u64)]) -> toyos_bootmap::Typing<'_> {
toyos_bootmap::Typing::Firmware
}

/// The time-stamp counter, which counts from reset.
pub fn counter() -> u64 {
// SAFETY: RDTSC reads a counter and nothing else; every x86-64 has it.
unsafe { core::arch::x86_64::_rdtsc() }
}

/// What the loader's report says beside the counter: `IA32_TSC_ADJUST`,
/// where CPUID says the CPU has it — every write to the TSC since reset is
/// added to it (Intel SDM Vol. 3B, "Time-Stamp Counter Adjustment"), so zero
/// is a counter firmware never wrote and the TSC is time since power-on.
pub fn counter_origin() -> alloc::string::String {
let max = core::arch::x86_64::__cpuid(0).eax;
// Leaf 7 exists when the maximum leaf reaches it.
if max < 7 || core::arch::x86_64::__cpuid_count(7, 0).ebx & (1 << 1) == 0 {
return alloc::string::String::from("IA32_TSC_ADJUST not on this CPU");
}
let (lo, hi): (u32, u32);
// SAFETY: the loader runs at CPL 0, and CPUID.07H:EBX[1] says the MSR exists.
unsafe {
core::arch::asm!("rdmsr", in("ecx") 0x3bu32, out("eax") lo, out("edx") hi, options(nomem, nostack))
};
alloc::format!("IA32_TSC_ADJUST {}", ((u64::from(hi) << 32) | u64::from(lo)) as i64)
}

/// `CLFLUSH`'s line on every x86-64 part.
const LINE: u64 = 64;

/// Every line of `[at, at + len)` written back out of this CPU's caches and
/// every other CPU's, before this returns.
pub fn write_back(at: u64, len: usize) {
let mut line = at & !(LINE - 1);
while line < at + len as u64 {
// SAFETY: `CLFLUSH` writes back and invalidates the line containing the
// address and touches nothing else; the caller names memory it
// allocated, and the instruction faults on nothing a canonical address
// can be.
unsafe {
core::arch::asm!("clflush [{addr}]", addr = in(reg) line as *const u8, options(nostack, preserves_flags));
}
line += LINE;
}
// SAFETY: `SFENCE` orders those writebacks ahead of whatever ends this
// machine; it touches no memory or register.
unsafe { core::arch::asm!("sfence", options(nostack, preserves_flags)) };
}

/// What the loader says about the CPU as firmware handed it over, or why the
/// kernel cannot run on it. A UEFI x86-64 loader runs in long mode at CPL 0,
/// the state the kernel's entry takes, so there is nothing to say or refuse.
pub fn cpu_as_entered() -> Result<Option<alloc::string::String>, alloc::string::String> {
Ok(None)
}

/// The I/O port space the chipset's TCO block answers in.
pub mod pio {
/// Whether this architecture has an I/O port space at all.
pub const EXISTS: bool = true;

/// # Safety
/// No fault in Ring 0; the caller owns which device answers at `port` and
/// what the word commands it to do. `kernel/src/arch/x86_64/cpu.rs` states
/// the same contract for the same instruction.
pub unsafe fn outw(port: u16, value: u16) {
// SAFETY: the caller's contract.
unsafe {
core::arch::asm!("out dx, ax", in("dx") port, in("ax") value, options(nomem, nostack, preserves_flags))
};
}

/// One word from an I/O port; safe because a read has no value a caller can
/// get wrong, as `kernel/src/arch/x86_64/cpu.rs`'s `inw` is.
pub fn inw(port: u16) -> u16 {
let value: u16;
// SAFETY: one instruction into the declared output, no memory operand.
unsafe {
core::arch::asm!("in ax, dx", out("ax") value, in("dx") port, options(nomem, nostack, preserves_flags));
}
value
}
}

/// Switch to the boot map at `args.boot_pml4_addr` and jump to the kernel
/// image's entry through the high half, handing it `args`.
///
/// # Safety
/// The boot map identity-maps the memory this code and its stack run from and
/// maps the kernel image at `PHYS_OFFSET`; `image` is that relocated image,
/// `entry_offset` its entry point's offset in it, and `args` stays where it is
/// until the kernel copies it.
pub unsafe fn enter_kernel(image: (u64, u64), entry_offset: u64, args: &KernelArgs) -> ! {
let root = args.boot_pml4_addr;
let entry = crate::PHYS_OFFSET + image.0 + entry_offset;
// SAFETY: the caller's contract: the switch keeps this code and stack
// mapped, and the jump lands in the image it mapped.
unsafe { core::arch::asm!("mov cr3, {}", in(reg) root, options(nostack)) };
// SAFETY: `kernel.elf`'s entry point takes `&KernelArgs` in `rdi` by the boot
// protocol `toyos-abi::boot` and the kernel side of it define between them —
// `sysv64`, because this target's own `"C"` is the Microsoft convention —
// and this bootloader has no way to check the callee's signature, only to
// keep its own side of that contract.
let entry: extern "sysv64" fn(&KernelArgs) -> ! = unsafe { core::mem::transmute(entry) };
entry(args)
}
27 changes: 4 additions & 23 deletions bootloader/src/blackbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,30 +252,11 @@ fn when(stamp: u64) -> String {
alloc::format!("{HEAD} the record below is from the boot armed at {}", Civil::from_unix_secs(stamp).stem())
}

/// Write the page back out of this CPU's caches, and every other CPU's.
///
/// The kernel's `blackbox::flush` is the same loop for the same reason; the
/// instruction is each binary's because `toyos-blackbox` forbids unsafe code,
/// and `toyos_blackbox::CACHE_LINE` is the one decision they share.
/// Write the page back out of every CPU's caches, as the kernel's
/// `blackbox::flush` does and for the same reason: a reset does not write dirty
/// lines back.
fn flush(page: Page) {
let mut line = 0usize;
while line < BYTES {
// SAFETY: `CLFLUSH` writes back and invalidates the line containing the
// address and touches nothing else; the address is inside the page this
// image allocated, and the instruction faults on nothing a canonical
// address can be.
unsafe {
core::arch::asm!(
"clflush [{addr}]",
addr = in(reg) (page.0 + line as u64) as *const u8,
options(nostack, preserves_flags),
);
}
line += toyos_blackbox::CACHE_LINE;
}
// SAFETY: `SFENCE` orders those writebacks ahead of whatever ends this
// machine; it touches no memory or register.
unsafe { core::arch::asm!("sfence", options(nostack, preserves_flags)) };
crate::arch::write_back(page.0, BYTES);
}

/// A sealed [`toyos_blackbox::Fault`] as lines for the log.
Expand Down
Loading
Loading