Repository navigation
Conversation
An empty commit that opens the design round for stage 7.2 and step 6.3 of issues/the-kernel-is-small-interrupts-post-and-threads-wait.md. The design and its roast are the pull request's body; nothing is built, and the build drops this commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Design round 1. Nothing is built. This is the design and roast for stage 7.2 of
issues/the-kernel-is-small-interrupts-post-and-threads-wait.md, which is also step 6.3: the i8042 leaves the kernel, and/system/bin/ps2serverdrives it over theisaclaim #592 built. The branch is one empty commit onmainat1acaa362f, which the build drops.What this was read from:
mainat1acaa362f; the track andissues/every-driver-is-still-in-the-kernel.md;kernel/src/arch/x86_64/i8042/(1,364 lines),kernel/src/isa.rs,kernel/src/arch/x86_64/pio.rs,irq_ring,keyboard.rs,mouse.rs,device.rs,object/ops.rs, the supervisor's start and restart paths,kernel/ps2/, every i8042 issue; #877's design and its round-3 review; #885's and #890's rulings; and IBM's Personal System/2 Hardware Interface Technical Reference, October 1990, read page by page:The owner's rulings this design follows:
The split
The kernel keeps only what userland cannot do.
pio::fill_i8042_row(ports0x60and0x64, ISA lines 1 and 12) andcrate::isa, both unchanged. After the move nothing in the kernel speaks the 8042's protocol: the row names the function's ports and lines, and nothing else.isa::isrrecords the interrupt on the claim and posts its watch. That handler already serves theisarows (The panic console takes no input, and an isa claim grants a process exact ports #592), so the i8042's own vector0x24and handler go./system/bin/ps2servertakes everything else.It is
exempt.owns, since its job exists only on ToyOS: it owns the i8042 ToyOS claims for it. It is two packages, following soundserver's and netstack's shape:system/ps2server/ps2iskernel/ps2moved bygit mv, keeping the package nametoyos-ps2. It is pure and host-tested: the set-1 decoder and the 3-byte packet decoder as today, plus the probe and the byte stream as state machines over aControllertrait (status, data, command, write, clock). Bytes from a device are input from outside the program's trust, so the decoders stay a crate of their own (reviewer.md, Fit).system/ps2serveris the program: the claim, the ports throughtoyos::ioport, the poller, and the feed.The input feed: how key transitions reach the input stack
Today the driver calls
keyboard::handle_keyandmouse::handle_motionfrom the scheduler pass's drain. After the move, ps2server writes the same transitions into a claim.The ABI.
DeviceType::InputFeed = 11, spelled"input-feed"in adevicesrow.toyos_abi::input::FeedRecord, six bytes:kind: u8,code: u8,dx: i16,dy: i16. It is decoded once, intoyos-abi, into one of:Key { usage, pressed }:codeis a HID Keyboard-page usage. Usage 0 is refused.Pointer { buttons, dx, dy }: relative motion, screen-oriented, with buttons in boot-mouse order.i16makes the kernel'sdx * scaleoverflow unrepresentable: 32767 × 64 + 32767 < 2³¹.Lost: release everything this feed holds. It is sent on an overrun code or a dropped byte, today'sKeyOutcome::Lostand the drain'slost.dxordyon a non-pointer record, is refused.InvalidArgument, and nothing is applied. The holder's records are the only thing the kernel takes from it.toyos::InputFeed::send(&[FeedRecord]), andtoyos::IsaDev, withdescribe,irqandack, shaped asAcpiDevis.The kernel side is
Claimed::Feed(Feed)indevice.rs, about 120 lines:PointerSourceit claims at mint. When no source entry is free, the mint isExhausted.keyboard::declare_sourceandmouse::declare_source.Keyupdates the feed's set and callshandle_key.Pointercallshandle_motion(source, buttons, Relative { dx, dy }, 0).Lostreleases the feed's usages and callsmouse::release_buttons(source).mouse::unbind(source). So a dead holder never leaves a Shift or a drag stuck. This replaces the driver's machine-widekeyboard::release_all, which also released USB keyboards' keys.release_allgoes, since nothing else calls it.PointerSource::PS2goes, with its permanently reserved entry 0 andunbind's assert. PS/2's pointer now claims an entry like any other.HELD, the modifier union and the 512-event queues. Thekeyboardandmouseclaims' readers (the compositor and the console) see nothing new.Endowment order. The track's "a keyboard claim is refused while no source exists, which init's order of endowment then decides" becomes one rule. A program whose
devicesnamesinput-feedis started, and so endowed, before any program whosedevicesnameskeyboardormouse. That is one rank in the supervisor'sboot(), beside its storage-first rank. The decision moves intotoyos-manifest(start_order) so a host test holds it.Restart: a crashed ps2server comes back without the kernel noticing
The kernel runs only the claims' generic releases. ps2server's process ends, and its two claims drop:
isarow's drop masks the lines, empties the record and answers the polls;isa::process_ends);No kernel code knows ps2server. The only kernel lines are the row's generic
isa: the i8042's ports are pid N'sand… went back with pid N.The supervisor restarts it: its row is
restart = true, and the supervisor mints both claims again. Its existingCLAIM_RETURNretry covers the deferred release, a compromise thatissues/deferred-release-outlives-its-syscall.mdalready records.One supervisor change: a
restartrow is started again only when its process ended other than by a clean exit. A clean exit means the program has nothing to serve:NotFound;0x64reads0xFF.On
mainsuch a program would be restarted three times and then logged as a service whose ports closed. A kill (137), a handle fault (139) or a panic is restarted. diskserver and fileserver never exit cleanly, sinceservediverges.The new ps2server probes from whatever state the dead one left: interrupts on, scanning on, aux reporting on, or a byte in the output buffer. The first thing it does is disable both ports and empty the output buffer. What was typed while no ps2server ran is not delivered:
0xF5"clears its output buffer" (K 21). The aux reset runs again, about 400 ms on the T14 (issues/t14-keyboard-will-not-report-its-scancode-set.md).The probe and the stream, following IBM's reference
The probe keeps today's sequence and decisions, and departs from them only where IBM's reference says otherwise:
The scan-set query is made with translation off. IBM says to disable translate mode while asking for the set, so the controller does not translate the answer (K 23; C 8, the note on bit 6). Today the kernel asks with translation on and reads the translated byte (
0x41for set 2). ps2server reads the raw set:0x02→ set 2, and translation is turned on at arming. IBM: "for keyboard operations that are compatible with IBM Personal Computers, the Type 1 controller is placed in the translate mode" (C 8).0x01→ set 1, with translation off.0x03, or anything else → the keyboard is disabled by name, since there is no decoder for it.A polled read waits 7 µs after the status read that showed the output buffer full (C 12: "When polling the Type 1 controller for the output-buffer-full condition, wait 7 microseconds … before reading the output buffer"). Root
CLAUDE.mdallows a fixed delay where a hardware document mandates one, and it is cited at the site.A write is made only with IBF and OBF both clear (C 12, C 6). Today the kernel waits on IBF alone. A byte found waiting before a write is one nothing asked for: it is read and named among the undecoded bytes.
Device exchanges wait on the interrupt, not a spin. Controller commands (
0x20,0x60,0xAA,0xAB,0xA9,0xAD/0xAE,0xA7/0xA8) are polled with interrupts off in the config byte, as today: the probe writes that byte itself, and firmware may have left its bits clear. Before the device stage, the probe sets the port's interrupt bit. IBM: bit 0 raises IRQ 1 "when it places keyboard or command controller response data into its output buffer", and bit 1 raises IRQ 12 for aux data (C 9). Then every keyboard and aux answer is waited for on the claim's record, bounded by the stage's deadline:0xFAacks;0xFA 0xAA id.No CPU spins through the T14's ~400 ms aux reset. A byte from the other port during an exchange is fed to the stream, not taken as the answer. Today's
read_datatakes any byte (mod.rs:802).Every bounded wait looks at its condition once more after its deadline. A preempted ps2server can lengthen a wait, but never turn a late answer into silence.
The stage budgets stay as today (250, 500, 750 and 600 ms). They are liveness ceilings, and a working machine never spends them. The one bound IBM sets that decides an outcome is the set byte's 20 ms, which A2 takes.
The steady state takes one byte per interrupt:
AUXBsaying which port.ackthe claim.The controller cannot load another byte until this one is read. It holds the clock low (C 13, step 5; K 38), and the keyboard keeps up to 16 bytes in its FIFO meanwhile (K 19). So each record announces exactly the byte read after it, and a record with nothing behind it is a harmless empty wake. Edge or level, no edge is lost: a level line is masked by
isa::isruntil ps2server'sack.The mouse framer's gap becomes the device's own idle time. It is the claim record's
last_nanosfor this byte minus the clock at ps2server's read of the previous aux byte, both on the clock page's clock. Today the gap is between two ISR reads. In userland, a delayed read inhibits the device (C 13), so the gap between arrival times includes ps2server's own delay. A preemption longer thanPACKET_GAP_NSinside a packet would then split it, which is the shapeissues/i8042-mouse-ends-four-packets-short-with-a-clean-exit.mdmeasured on the host. Measured from the host's release, a slow ps2server can delay a resync but never split a packet.MouseDecoder::feed(byte, idle_ns)replacesfeed(byte, now_ns)and loseslast_ns. The 5 ms threshold stands on C 14's clock figures: 60–100 µs a bit, so 10–16.7 kHz.The aux self-reset (
0xAA 0x00mid-stream) re-enables reporting as today: keyboard port off,0xD4 0xF4, then the ack waited on the record, keyboard port on. After 3 failures the pointer is written off by clearing the aux interrupt bit and sending0xA7. No kernelioapicmask is needed, so the kernel's 30 ms interrupts-off re-enable (mod.rs:931) goes.A wedged controller: OBF still set after 16 reads in one wake. ps2server disables both ports, says so, and exits non-zero. The supervisor's restart limit, 3 in 10 s, then gives up on it. This replaces the kernel's
QUARANTINE.Diagnostics kept:
ps2server:fori8042:;Partial::blame);Not carried: the
HEALTHverdict machine, with its quiet verdict, the arming-edge bookkeeping,to_screenand the idle loop'sverdict_due. In userland, the armed line followed by no counters line already says the pin never asserted.The boot console, the T14 and the AMD laptop
device.rs:190). Now they wait in the keyboard and are emptied by the probe. ps2server always probes after the kernel's xHCI legacy handoff, so no SMM trap answers on0x60/0x64.images/console.toml(--console-boot, the shell on the panel) starts ps2server. The new rank mints its feed before the console'skeyboardclaim.images/system.tomldoes the same for the compositor.tests/images/metalcase.tomlstarts it, because that image is the flashed machine's shape.images/diag.tomldoes not, since nothing there reads input. The clause in its header about thei8042:line on the panel goes, because that line no longer exists.tests/images/testcases.tomldoes not, so theisarows still find the row free.0xF0with0xEE(issues/t14-keyboard-will-not-report-its-scancode-set.md). Whether it still does with translation off is measured by M1. If it does, the Refused arm decides set 2 translated from firmware'scfg 0x77, as today. The boot in seven that firmware hands over cfg0x00is still disabled by name (issues/t14-hands-over-an-uninitialised-8042.md, unchanged). Its TrackPoint answers reset, id0x00, rate and resolution, and is armed as today.0xF0and0x00and sends no set byte (Design: the AMD bench's desktop — keyboard, volume keys, HDA controller choice, native rate, codec volume #877's issue, measured with translation on). With translation off, M1 shows whether it answers. If it does, ps2server reads the set and A2's arm is not reached there. If it does not, A2's Unanswered arm decides.#877
#877's A2 and A3 are designs, not built, and both edit files this change moves.
Unansweredarm and its 21.125 ms wait) becomes an arm of ps2server's decision table, unchanged, with the same line. Design: the AMD bench's desktop — keyboard, volume keys, HDA controller choice, native rate, codec volume #877's round-2 review found no host tier for A2's sort intoSilentorUnansweredwhile it sat in the kernel's port I/O. Against the fake controller, it gets one.SET1_E0entries and the widened range test) lands in the movedkey.rsanddecode.rs.system/ps2server/ps2.i8042: kbd set2+xlat (assumed, …)line. After the move that line is ps2server's, and whichever lands second renames it.M1: one cheap measurement before the build
One T14 boot and one AMD-bench boot of a scratch kernel that never lands. Its i8042 probe makes the
0xF0 0x00query with config bit 6 clear, as K 23 asks, and logs the answer. That tells which arm each laptop takes under IBM's procedure, and so which line the metal row holds each bench to. The patch is about 10 lines inquery_scancode_set's caller. The orchestrator runs it, since every metal bench is his.What is deleted
Whole files, counted at
1acaa362fwithwc -l:kernel/src/arch/x86_64/i8042/mod.rskernel/src/arch/x86_64/i8042/tally.rskernel/src/arch/x86_64/idt/i8042.rskernel/src/arch/aarch64/keyboard_controller.rskernel/loom/tests/i8042_tally.rstests/guest/rust-tests/src/bin/isa_claim_refused.rs(its premise, a kernel that drives the i8042, goes)Hunks, about 70 lines in all, each an estimate until the build counts it:
idt/mod.rs: the0x24vector,I8042_VECTORandmod i8042(4);x86_64/mod.rs:i8042and thekeyboard_controllerseam (2);aarch64/mod.rs(1) andboot.rs(1);sched/driver.rs:verdict_dueandservice(3);irq_ring'sI8042(3);irq_census'si8042source (3), with its parsers intests/common/irqcensus.rsandtests/checks.rs;i8042-withheldactuator (4), with its uses intests/toyos.rs,tests/metal/src/lib.rs,tests/common/power.rsandtests/common/isa.rs;drivers/acpi.rs'siapc_boot_arch(5), andlib/acpi's, with its three tests (about 35), whose one caller only logged it: "Logged, never obeyed" (mod.rs:981);keyboard::release_all(13), with its block ininput_merge_test(7);mouse.rs'sPS2entry (about 8);kernel/loom/src/lib.rs'si8042_tallymodule (6).kernel/ps2(1,023 lines) leaveskernel/forsystem/ps2server/ps2.kernel/src: 63,836 lines at1acaa362f(find kernel/src -name '*.rs' -print0 | xargs -0 cat | wc -l). The change takes out about 1,430 and adds about 120 for the feed, so it ends near 62,530, about 1,300 fewer. The build reports the measured number.Two interrupts-off sections go with the driver:
mod.rs:1192-1242), polled against the probe's budget;IrqGuard, bounded at 30 ms (mod.rs:931).Step 6's rule has the windows measured against step 2's readings with the
mask_windowsrow, by the orchestrator.Issues:
the-i8042-tallys-release-acquire-pair-has-no-negative-control.mdandscreen-diag-boot-leaves-no-i8042-line-on-the-panel.mdare closed. Neither exit can be met once its subject, the tally and the kernel's panel line, is deleted. Whether that closes them is the orchestrator's call.Checks
Under #890, the kernel's part is tested as the kernel's integration, ps2server as units and modules, with one very cheap smoke test.
Host (
cargo run -- --ci host):toyos-ps2'sdecode.rsandfuzz.rs, moved. The mouse tests are rewritten toidle_ns, and one new test puts a host delay longer thanPACKET_GAP_NSinside a packet and asserts the packet survives.probe.rs, against a scripted fake controller whose machine shapes are:0x02);0xEEwith cfg0x77→ armed; with cfg0x30→ disabled);0xFF);The fake also refuses, by panicking, each of IBM's rules above when broken:
stream.rs:Lost;toyos-abi:FeedRecord's decode refusals.toyos-manifest:start_orderputs a feeder before a reader.The kernel's integration:
The
isarowsisa_ports_are_the_binders_aloneandisa_lines_reach_their_holderstay as they are, lessi8042-withheld. They hold the claim and the post on the T14.A new T14 row,
input_feed, rides the existingtestcasesboot as one more job, so it costs no extra flash. A job holds two feeds and thekeyboardandmousereaders. It asserts:aarrive as Shift andawithMOD_SHIFT;Why it is a metal row and not a guest test: nothing in it needs QEMU, and a metal row is the cheaper tier. Its binary goes in
RUST_SKIP.The smoke test.
console_image_boots, an existing QEMU boot ofimages/console.tomlonProfile::Metal, additionally must say ps2server's armed lines for both ports. That needs no new boot.Why it needs QEMU: QEMU's i8042 answers the set query, so this is the one place the read-the-set arm meets an implementation that is not ours. Neither laptop answers it today (M1 may change that).
Metal, run by the orchestrator:
ps2server_armsridesMETALCASE, adding no flash, on the T14 and on the AMD bench (--metal-host). The judge holds each bench to its line from M1:aux id 0x00 … reporting onas well;i8042=source in the IRQ census;isaclaim's own.mask_windows, base against branch, by step 2's rule.input_feed's release on a holder's death;isarows' release;ended; started again, a second armed line, and a counters line counting keys.Negative controls:
ps2server_armsgo red, since nothing saysps2server:.probe.rsred.Feed::dropreleasing nothing:input_feedgoes red.start_order's test goes red.Oracles: IBM's reference, at the pages cited. QEMU's i8042 and keyboard, an implementation that is not ours. Both laptops' ECs. The HID Usage Tables, for the decoder's existing tables.
Where the moved code rested on Linux
kernel/src/arch/x86_64/i8042/mod.rs:892-895: the set is read and never written, citing "Linux'satkbd_select_set, EDK2'sPs2KeyboardDxe". Now it rests on ToyOS's own reading: the T14's EC answers0xF0 0x02with0xEE. A read that answers needs no write, and K 22–23 documents both.kernel/ps2/src/key.rs:4and:172: set 2 behind translation is called "Linux's default configuration". Now it is C 8, the PC-compatible mode.issues/t14-keyboard-will-not-report-its-scancode-set.mdjustifies the Refused arm as "exactly what Linux'si8042.c/atkbddo", and withholds0xF2byatkbd_skip_getid. Now the arm rests on C 8 and K 22 (set 2 after power-on or reset), and ps2server sends the keyboard no0xF2. The issue file is not edited (rootCLAUDE.md, Prose).Roast
0xFEand0xD1reach the CPU's reset (C 10–11). The I/O bitmap cannot see a value.issues/the-i8042s-holder-holds-the-machines-reset-line.mdrecords this, owned by the power broker's track. Until now only test jobs held the row, and from this change on, every shipped boot's ps2server does. It is still true, and still tracked.restartrow.isa::ackstops refusing a row with no level line, and answersOk. ps2server cannot know its lines' triggers: the claim's description is the port set, and the MADT decides. So it acks every wake, which costs one syscall a wake on an edge row. The alternative, trigger bits in the description, is an ABI change for one reader.Lost, and the feed's keys are released. Nothing is timed on QEMU, and the T14's numbers are reported, not judged.ps2server_arms.0xAA,0xABand0xA9are not in IBM's PS/2 controller chapter, whose list (C 8–11) runs 20–3F, 60–7F, A4–A8, AD, AE, C0–C2, D0–D4, E0, F0–FF. They are kept because the kernel sends them every boot and QEMU and both laptops answer them. Their reference, the PC AT one, is not read here.0xFF,0xF2,0xF3 0x64,0xE8 0x03,0xF4) are carried as the kernel sends them. The build cites them or says it cannot./logonly.0xFF) is considered and not designed. It would put every keyboard in set 2 by IBM's own word (K 22), with no inference, and would close the T14's uninitialised-8042 boot. But it adds 300–500 ms of BAT to every start (K 20), and an EC that does not implement it would lose its keyboard. That is the owner's question onissues/t14-hands-over-an-uninitialised-8042.md, which records it as his.What I am unsure of
input_feedon the guest suite as well. As a metal row, it runs only when the orchestrator runs the T14.ackmakes it not matter.the-i8042-tallys-…andscreen-diag-boot-…close in this change or are left for the orchestrator.rg 'started again' tests/finds none), and this design adds none: the rule is one comparison a reader checks.No gate is claimed: nothing is built.
🤖 Generated with Claude Code
https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy