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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,9 +194,8 @@ attempt.
![ToyOS booting on a ThinkPad T14](first-boot.jpg)

The screenshot is the laptop's own panel. The machine has no serial port, so
the kernel renders its log and its panics to the framebuffer, and pages them
with PageUp and PageDown polled straight off the keyboard controller after
every CPU has halted. It is reporting a real bug: the page cache sized an index
the kernel renders its log and its panics to the framebuffer. It is reporting a
real bug: the page cache sized an index
from the disk's block count, which fit in QEMU's test image and wanted 238 MB
on a 244 GB drive.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pointing here:
`_mm_sfence` after writing a write-combining framebuffer. Userland has no
portable way to say "drain my stores to the scanout"; the SDK (`toyos/src`)
owes one, and it is also only changed under an ABI brief.
- Twenty guest probes in `tests/toyos-rust-tests/src/bin/`, whose subject
- The guest probes in `tests/toyos-rust-tests/src/bin/`, whose subject
is an x86 instruction (`rdgsbase`, `fxsave64`, `int1`, x87 control words)
or the raw `syscall` gate with arguments no SDK call will pass. They run in
the x86-64 suite, which is the only suite until the harness gains its arch
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ their clocks, and no `METAL` row judges them.
- **Input.** A boot with no i8042 is no slower than one with it
(`i8042_absent`). The i8042 counters repeat at most once per 10 s and only
when the pin asserted (`i8042_health_cadence`), and the idle loop does not
spin on the controller (`i8042_health`'s idle trips). The fatal path's panel
holds while a key is held (`panic_key_holds`).
spin on the controller (`i8042_health`'s idle trips).
- **USB.** The connect settle ends on the device appearing and not at
`EMPTY_BUS_NS` (`xhci_slow_connect`). A disk call ends inside
`toyos_xhci::call::AFTER_BREAK`, a staged break skips its data-phase wait, the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,7 @@ anything touching the boot path.
these to be read-verified only: TCG reports every CPU feature present, so the
missing-bit path cannot run.
4. **The on-screen console. If this fails, do not flash.** Every screen test.
Confirm the muted profile actually removes the UART, and that the paging test
is driven by a timer rather than a keypress — input may be dead on the
machine. *False pass:* the late-panic gate passes with the capture routine's
Confirm the muted profile actually removes the UART. *False pass:* the late-panic gate passes with the capture routine's
body replaced by a bare return, so these cover rendering, not capture.
5. **Input, which is the milestone and which a verdict once omitted entirely.**
The 2026-08-01 verdict recorded GO over six sections and a seventeen-row
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
status: open
kind: defect
opened: 2026-09-28
---

# The i8042's holder holds the machine's reset line

An `isa` claim on the i8042 (`kernel/src/arch/x86_64/pio.rs`'s `GRANTABLE`)
opens ports 0x60 and 0x64 to the process that binds it, through the TSS I/O
permission bitmap, and the bitmap grants a port or refuses it: it cannot see
the value written. The controller's command port takes `0xFE`, which pulses the
CPU's reset line, and `0xD1`, which writes its output port, where the same
line lives (IBM PC AT Technical Reference, the 8042's commands). So whichever
process drives the keyboard can reset the machine whenever it likes: no memory
is exposed and the kernel does not crash, but a userland bug in that one
program ends every other one without a word in the log.

Neither way out is free: filtering the command byte means the kernel decoding
the controller's protocol, a syscall or a trapped instruction per access in
place of the bitmap; keeping 0x64 in the kernel means the keyboard driver is
not wholly userland, which the owner ruled it must be.

A recorded weakness of the power broker's track
(`issues/isolation/the-power-broker-authority-with-a-human-in-the-loop.md`),
which owns it (owner ruling).

**Exit**: resetting the machine is the power broker's decision alone: the
keyboard's holder reaches the controller's reset line only through the broker,
or not at all.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
status: open
kind: tooling
opened: 2026-09-29
---

# The I/O permission bitmap has no silicon reading

What an `isa` claim opens, and every port it leaves closed, rests on the TSS I/O
permission bitmap and its limit (`kernel/src/arch/x86_64/pio.rs`,
`kernel/src/arch/x86_64/percpu.rs`). The guest tests that read it
(`isa_ports_are_the_binders_alone`, `isa_ports_close_on_one_cpu`) run locally
under TCG, whose `check_io` is QEMU's own implementation of the check; the
processor's reading comes only from KVM, which only `nightly.yml` runs, and from
metal, which has not run them.

**Exit**: both tests green on a KVM run of the branch that carries the bitmap,
or on the T14.
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,6 @@ so the native shape is smaller:
Unstaffed until the owner opens it; sequenced naturally with the userland/
product era. What must not happen meanwhile is the accident this track
exists to prevent: `POWER` spreading to more manifest rows.

Its recorded weakness: the i8042's holder resets the machine without it
(`issues/isolation/the-i8042s-holder-holds-the-machines-reset-line.md`).
2 changes: 2 additions & 0 deletions issues/kernel/every-driver-is-still-in-the-kernel.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ What is left of the staged work:
3. Done: **the capability itself** is `DeviceType::PciFunction` plus
`SYS_DEVICE_BAR_MAP` and `SYS_DEVICE_DMA_ALLOC`, with config space readable
and unwritable and the interrupt delivered as a record on the claim.
4. **The i8042 (PS/2)**: staged as stage 7 of
`issues/kernel/the-kernel-is-small-interrupts-post-and-threads-wait.md`.

Two constraints that were not obvious before the code was read:

Expand Down
18 changes: 18 additions & 0 deletions issues/kernel/the-isa-port-switchs-cost-is-unmeasured.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
status: open
kind: tooling
opened: 2026-09-29
---

# The `isa` port switch's cost on the scheduler hot path is unmeasured

`arch::pio::switch_to` (`kernel/src/arch/x86_64/pio.rs`) runs on every context
switch, from `KernelHw::switch`: per `GRANTABLE` row one `BOUND` load, one
bitmap byte read, and on a change one bitmap write per port. On x86-64 that is
one row on every switch of every CPU, whether or not any process holds a claim.
Nothing has measured what it adds to a switch, and a timing verdict comes only
from metal.

**Exit**: the T14's switch cost with and without the call, from one metal run,
recorded in the commit that closes this; or the switch skips the rows entirely
while no row is bound, with the skip's cost measured the same way.
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,7 @@ times:
10. **usbd**, stage 5's second half: the whole xHCI moves, HID to the
keyboard claim and mass storage over `toyos-blockring`, and the kernel
USB bridge is deleted. **What must work with no userland stays off
USB**: the panic console pages its report by itself and needs no
keyboard, and steers only from the i8042 it polls; the kernel's one
USB**: the kernel's one
hotkey, Ctrl+Alt+D (`kernel/src/keyboard.rs`, the blocked-task dump),
is recognised on the i8042's transitions and no longer on a USB
keyboard's, which from here reach the kernel only as usbd's keyboard
Expand All @@ -145,6 +144,21 @@ times:
to their device's `Watch`, and the device's thread does the work. The
per-CPU IRQ relay, the driver list in the scheduler pass and the idle
special cases are deleted.
7. **The i8042 leaves the kernel.** Owner ruling, 2026-09-28: drivers are
userland, and a dead kernel takes no input, with no emergency way.
1. **The panic console takes no input, and an `isa` claim grants a process
exact ports through the TSS I/O permission bitmap and its ISA lines as
records** (`kernel/src/isa.rs`). **Done** (#592).
2. **ps2server**, the server over that claim, feeding the kernel's keyboard
and mouse streams so Ctrl+Alt+D and the merge with USB HID stay where
they are; the kernel's driver, its vector, its actuators and the
`keyboard_controller` seam deleted. Constraints: the harness paces typed
input on the kernel's `i8042: drain bytes=` trace (`shell_type_once`,
every `i8042-trace` boot), every boot config that types needs the server,
and a keyboard claim is refused while no source exists, which init's
order of endowment then decides. **Exit**: every keyboard and mouse guest
test green with no i8042 code in the kernel, and typing resumes after
ps2server is killed and restarted.

## Standing

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@ with no fallback by design (`kernel/src/drivers/acpi.rs:277-283`). Where the
table named none, `arm` returns `Bound::Held`
(`kernel/src/panic_reboot.rs:134-145`) and `hold_the_panel` loops
`while bound.is_armed()` (`kernel/src/drivers/panic_console/mod.rs:647-650`),
which never ends. A keypress also retires the bound, which is right for a
machine with somebody in front of it and is exactly wrong for one running
unattended.
which never ends.

**On the T14 that last bound is the one thing already known not to work.**
`issues/hardware/an-armed-tco-has-never-reset-the-t14.md` records that no claim
Expand Down Expand Up @@ -61,5 +59,5 @@ prints it.

**Exit condition**: a boot that takes a fatal event on the T14 with nobody in
front of it either ends itself and leaves a record naming the fault, or the run
shows which of `Bound::Held` and the retired-by-a-keypress path held it — read
off the machine, not argued from the tree.
shows that `Bound::Held` held it — read off the machine, not argued from the
tree.

This file was deleted.

3 changes: 1 addition & 2 deletions issues/panic-path/no-console-between-boot-and-terminal.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ it by saying where the log will be, and cannot put a line in it.
(`kernel/src/drivers/panic_console/mod.rs:639`), and a compositor claiming the
framebuffer sets it. So on `bootable.img` the last kernel screenful ever painted
is the one at `Boot: complete`, the desktop overwrites it a few tens of
milliseconds later, and no key pauses it: `page_forever` is reached only from
`halt_all_cpus`, so a *successful* boot never pages.
milliseconds later, and no key pauses it.

**The durable answer landed and is not this.** "A log sink that survives
userland" is `/system/bin/logd`: the kernel keeps the record ring and the console and
Expand Down
6 changes: 6 additions & 0 deletions kernel/src/actuator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ actuators! {
/// Cap the i8042 ISR at 4 bytes and answer empty until the mute verdict is out; `service` then polls the rest, so the verdict beats the sequence on every boot instead of on a loaded shard's luck.
i8042_split_burst = "i8042-split-burst";

/// Hold the i8042's quarantine between its two steps, in two scheduler
/// passes, until an `isa` claim begun after the first has been answered:
/// the claim lands between them on every boot. A granted claim then raises
/// the flood again, as an ISR still in flight at the mask would.
isa_claim_straddles_quarantine = "isa-claim-straddles-quarantine";

/// Script the input core directly at end of boot.
test_input_merge = "test-input-merge";

Expand Down
15 changes: 9 additions & 6 deletions kernel/src/arch/aarch64/keyboard_controller.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
//! The platform's own keyboard controller. An Arm machine has none: its
//! keyboards are USB, and the panic panel's key poll reads nothing here.

/// No byte ever waits.
pub fn poll_byte() -> Option<(u8, bool)> {
None
}
//! keyboards are USB.

/// Nothing to decide about a controller that is not there.
pub fn verdict_due() -> bool {
Expand All @@ -16,3 +11,11 @@ pub fn service() {}

/// Nothing to report.
pub fn report_line() {}

/// Nothing floods: no `isa` claim is ever granted here.
#[cfg(feature = "boot-actuators")]
pub fn raise_flood() {}

/// Nothing to wake: no quarantine runs here.
#[cfg(feature = "boot-actuators")]
pub fn wake_irq_cpu() {}
26 changes: 25 additions & 1 deletion kernel/src/arch/aarch64/pio.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,33 @@
//! The I/O port space: AArch64 has none.
//! The I/O port space: AArch64 has none, and so no ISA function to grant.

use core::convert::Infallible;

use crate::process::Pid;

/// Whether this architecture has an I/O port space at all. Firmware tables
/// that name a port are only honoured where it does.
pub const EXISTS: bool = false;

/// Nothing: every `isa` claim is refused as naming no function.
pub const GRANTABLE: &[crate::isa::Grantable] = &[];

/// No line is routed where no row exists.
pub type Line = Infallible;

/// Never called: [`GRANTABLE`] has no row to route.
pub fn route(_row: usize, _irq: u8) -> Result<Line, alloc::string::String> {
unreachable!("AArch64 has no ISA bus")
}

pub fn set_masked(line: Line, _masked: bool) {
match line {}
}

/// Never called: nothing is bound where nothing can be claimed.
pub fn switch_to(_pid: Option<Pid>) {
unreachable!("AArch64 has no I/O permission bitmap")
}

/// Never called: every caller checks [`EXISTS`] first.
pub unsafe fn outb(_port: u16, _value: u8) {
unreachable!("AArch64 has no I/O port space")
Expand Down
1 change: 1 addition & 0 deletions kernel/src/arch/x86_64/hw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,7 @@ impl Hw for KernelHw {
crate::preempt::set_count(incoming.preempt);
percpu::set_current_tid(incoming.id.map(|id| id.1));
percpu::set_current_pid(incoming.id.map(|id| id.0));
super::pio::switch_to(incoming.id.map(|id| id.0));
match incoming.id {
Some(_) => {
// Here, not in the pass: this is the one place a task (not idle) becomes what a
Expand Down
Loading
Loading