Skip to content

ps2server: the i8042 driver leaves the kernel, and the owner of input merges every source - #899

Draft
Japabu wants to merge 11 commits into
mainfrom
wt/toyos-ps2server
Draft

Japabu wants to merge 11 commits into
mainfrom
wt/toyos-ps2server

Conversation

@Japabu

@Japabu Japabu commented Oct 12, 2026 •

Copy link
Copy Markdown
Collaborator

Built, at 26c867374: the build febfd21e8; merges of origin/main at 6080e839d, 1a81b5926 and bc8429e71 (#886); fix round 1 (6016e657b, a42489c7c); and fix round 2, 1ca170b2b to 26c867374, which answers build review round 1. This is 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/ps2server drives it over the isa claim that #592 built. It hands every key and pointer report to the owner of input, which merges every source.

The design below was accepted in the orchestrator's rulings on round 5, and this build applies them:

  • every command byte holds bit 4 and the System Flag (0x75, 0x56, 0x57/0x75);
  • the fake holds the keyboard interface as bit 4, and panics on a 0x20 written with it clear;
  • issues/t14-hands-over-an-uninitialised-8042.md is closed.

It also applies the ruling on m06, so the 0xAD after the keyboard stage is gone. And it applies the ruling on BLOCKER 2: a loss is scoped to the device that lost something.

Lines:

  • git diff --shortstat origin/main...26c867374: 94 files, +3,589 −2,555.
  • kernel/src is 64,375 lines at the base bc8429e71 and 62,633 here, which is −1,742.
  • Fix round 2 alone (git diff --numstat 6248cd756 26c867374) is 20 files, +382 −121:
    • kernel/pure/pointers.rs +85, 41 of them its tests. About 20 of the rest moved out of mouse.rs, which goes −26 +7.
    • bus.rs +70, 43 of them its test. Its Records::first is hw.rs's wait loop moved, and hw.rs is ±23.
    • isa_claim_refused's binary, row, judge and actuator, +63.
    • Everything else is under 50 lines a file.

Fix round 2: build review round 1, answered

NOTE 1, the merge. origin/main at bc8429e71 is merged at 6248cd756. #886's microcode row and this branch's ps2server_arms were each added after metal_sim_scanout_wc in tests/toyos.rs, and both are kept, each on METALCASE. Every gate below is at the merged head.

BLOCKER 1, metal at a42489c7c: published, cited and judged. The orchestrator ran the four requests at a42489c7c (comment). Every reading is masked with the orchestrator's mask, and a grep for the machine fields counts 0. They are at metal-a42489c7c/. boot.txt is left out: its BIOS line keeps a version the mask does not cover.

Run Judge Exit Logs
T14, the tree ps2server_arms isa_ports_are_the_binders_alone isa_lines_reach_their_holder mask_windows 0, 4 passed judge; kernel logs: metalcase, isa, windowscase
AMD bench, the tree ps2server_arms 0, 1 passed judge, kernel log
T14, c01's base arm mask_windows 0 judge, kernel log
T14, c02: the row's release keeps its record isa_lines_reach_their_holder 1, red as wanted judge, kernel log
  • The T14 arms with the aux device: aux reset, 0xaa 367475 us after its 0xfa, then armed, command byte 0x57 — keyboard on, aux on.
  • The AMD bench arms without it: aux absent — its reset went unanswered in 600 ms, then armed, command byte 0x75 — keyboard on, aux off.
  • c02 reds after as the exit wants, and the track's 7.2 close rests on that red.

The three other rows metalcase carries, judged with --metal-readback over those metalcase readbacks. This touches no machine, and the judge code is this head's.

Bench Rows Exit Log
T14 metal_sim_scanout_wc blackbox_done_chain machine_reboot 0, 3 passed judge
AMD bench the same 0, 3 passed judge

usb_reset_hands_devices_back also needs the device boot, so it is in this head's request below, along with every row this round changes.

BLOCKER 2, a loss on one device released the other's state: fixed as the ruling states.

  • ps2server sends LOST only for the keyboard's overrun (0x00).
  • A pointer byte the framer discards is counted and sends nothing, and so does an aux byte while the pointer is owed its enable or written off. Every packet states its buttons whole.
  • The write-off sends one Pointer record for source 0 with no buttons, and no key release.
  • MSG_LOST's contract (wire.rs) is now "every key this source holds is released; its pointers keep their buttons".
  • The compositor ends only key state on LOST. The console already did, since it holds no Pointer.
  • Tests:
    • three_failed_reenables_write_the_pointer_off_with_0xa7 now wants the zero-buttons report.
    • New: a_discarded_aux_byte_sends_nothing. Shift is held, then an aux byte with bit 3 clear arrives. The records are Shift's make alone, and the framer counted one discard.
    • The compositor's arm is one deleted line in a binary with no host build, and nothing tests it. Design: one userland input owner for the ARM desktop #868's inputserver takes that code.

BLOCKER 3, PointerSource's allocation had no test: the allocation moves to kernel::pointers::Sources in the kernel's pure library, where host tests reach it. mouse.rs keeps PointerSource and calls it.

  • The tests:
    • two claims take 0 and 1;
    • numbers given back are the next claims', lowest first (0, then 200, then 201);
    • with all 256 held, a claim is refused until one is given back;
    • a number given back twice panics.
  • The review's mutation: unbind's fetch_and is now give_back's. m18 replaces it with a load, and three of the tests go red.
  • What no test reaches: deleting the IN_USE.give_back call from unbind is a deletion a reader sees.

BLOCKER 4, the KernelDriven refusal had no test. The arm is reachable: a FADT RESET_REG at port 0x64 is declared to the kernel by power::init_reset. So the claim on the i8042's row must be refused.

  • The actuator: isa-port-declared (a boot parameter of the test kernel) declares 0x64 to the kernel at boot, after the row is filled.
  • The row: the T14 row isa_claim_refused_on_a_kernel_port runs test_rs_isa_claim_refused under it. The claim must answer PermissionDenied, and the kernel must say isa: the i8042's ports 0x64+1 are the isa-port-declared actuator's.
  • Its control, c04: the return Err(KernelDriven) is removed, and the log line stays, so only the job can red. It is staged for the T14 below.
  • Why a metal row and not a cheaper tier: the refusal is claim_row against the kernel's own table of declared ports, so a host test cannot reach it. A metal row sits before QEMU in root CLAUDE.md's order, and the two isa rows beside it are metal rows too.
  • Housekeeping: the binary is in RUST_SKIP, so the shared block does not run it on a boot where the claim is granted. The actuator is in FLASHABLE, with what it touches.

BLOCKER 5, two things shipped for tests alone: both are gone.

  • Probed::Armed loses keyboard. The probe's tests now hold the armed line and the command byte: the T14's 0x57 — keyboard on, aux on, and the 0xFC shape's 0x56 — keyboard off, aux on.
  • rel_scale_for is private, and lib.rs no longer exports it. Its aspect test moves into merge.rs. The motion test states the 1920x1080 scales as numbers, 36 across and 64 down.

NOTE 2, the whole change reverted on metal:

  • c01 as it stood cannot reach the judge. It reverts the production change and keeps the tests, so tests/images/metalcase.toml names a ps2server the reverted tree does not build, and staging stops at the build. The stage log is for QEMU. On metal it fails with "no program ps2server".
  • c05 is c01 with that one image also at the base. It builds, and it is staged for the T14 below, where ps2server_arms must go red with no ps2server: kbd reset line.
  • On QEMU, c01 at the new base bc8429e71 reds console_image_boots with exit 1 and "STALLED: waiting for ps2server to arm the i8042" (log).

NOTE 3, the five-button mask: it moves out of mouse::report and into toyos-usbhid's Pointer::decode. It reads toyos_input::wire::BUTTONS, the one declaration the owner's decode also refuses past.

  • The test: a_button_past_the_fifth_is_dropped.
  • The fuzz oracle now wants the masked byte.
  • The control: m19 removes the mask, and both tests go red.

NOTE 4, rule 5 had no test: Hw's wait is now bus::Records::first, and Hw supplies the clock, the record and the park.

  • The test: a_record_landing_as_the_deadline_passes_is_found preempts the waiter between every two of its steps. The record lands as the deadline passes.
  • The control: m20 reads the clock after the look, and the test goes red.

NOTE 5, a wedged controller in the quiet stage: the stage drains before its first write as well as after it.

  • The effect: a wedged output buffer is found by 32 reads, said with what they read, and no write is made into it (C 14). A write the controller never takes now says what it drained first.
  • The test: a_wedged_output_buffer_is_said_after_32_reads_with_nothing_written.
  • The control: m21 drops the first drain, and the test goes red. Without that drain, the 0xAD reads for 250 ms.

NOTE 6, the duplicated accept loop: left, by my call.

  • What is shared: pumping a connection and mapping RxStep to a Record or an end reason, about 15 lines.
  • What differs: the compositor re-arms its Poller and routes pointers to a Pointer. The console reads readiness by token and holds no Pointer.
  • Why there is no home for it: a shared helper needs toyos::ipc and toyos-input together. toyos-input is linked into the kernel through toyos-usbhid, so it cannot depend on toyos, and a third crate for 15 lines adds more than it deletes. Design: one userland input owner for the ARM desktop #868's inputserver makes the owner one program, and the second copy goes with it.

NOTE 7, the usbkbd lines: #901's body does not own them; it names no Linux line in kernel/usbhid. But no new issue is needed. issues/the-tree-names-linux-as-an-authority.md (assigned, the orchestrator's sweep) owns them by its exit, git grep -n -i linux -- kernel … prints nothing, and that covers kernel/usbhid/src/keyboard.rs:21 and :27. It also covers kernel/usbhid/src/pointer.rs:17 (usbmouse), which NOTE 3 now makes a file this diff edits. Neither line is edited here.

NOTE 8, prose: the "staged, 2" and "the orchestrator's to run" rows are replaced by the readings above.

Gates, each its command's own exit

Gate Head Exit Log
cargo run -- --ci host 26c867374 0, "77 step(s), all green" host-ci.log
cargo run -- --build-only 26c867374 0 build-only.log
cargo test, the whole guest suite 26c867374 0, 53 passed of 53 guest-suite.log
The three metalcase rows over the a42489c7c readbacks, --metal-readback 26c867374's judges 0 on each bench above
The metal request, staged with --metal-readback 26c867374 2 each, staged and not judged request, stage logs beside it

The request at 26c867374 (the orchestrator's to run):

Image directory Bench Judge with Want
metal-head: isa, isa-port-declared, metalcase, metaldevicecase T14 ps2server_arms microcode metal_sim_scanout_wc blackbox_done_chain machine_reboot usb_reset_hands_devices_back isa_ports_are_the_binders_alone isa_lines_reach_their_holder isa_claim_refused_on_a_kernel_port exit 0
metal-head-amd: metalcase AMD bench ps2server_arms microcode metal_sim_scanout_wc blackbox_done_chain machine_reboot exit 0, aux absent
metal-c05-whole-change-reverted-metalcase: metalcase T14 ps2server_arms exit 1
metal-c04-claim-ignores-kernel-ports: isa-port-declared T14 isa_claim_refused_on_a_kernel_port exit 1

The smoke test's own lines come from the guest suite, under QEMU's i8042, whose keyboard answers the reset as K 11 and 13 say: ps2server: kbd reset, …, then ps2server: armed, command byte 0x57 — keyboard on, aux on, which console_image_boots awaits whole.

Negative controls and mutations

Each control is applied as a checked patch, run, its exit kept, and reversed with the tree left clean. Each log carries its diff.

Mutation Test Exit What went red
c01: the whole production change reverted onto the base bc8429e71, the tests kept cargo test -- console_image_boots, at 26c867374 1 (log) "STALLED: waiting for ps2server to arm the i8042"
c05: c01, with tests/images/metalcase.toml at the base too ps2server_arms on the T14 staged, 2 the orchestrator's to run (NOTE 2)
c04: claim_row names the kernel's port and grants the claim isa_claim_refused_on_a_kernel_port on the T14 staged, 2 the orchestrator's to run (BLOCKER 4)
c02: the row's release keeps its record isa_lines_reach_their_holder on the T14 1, at a42489c7c above
the hold absent (the run at 27c052cea) ps2server_arms on both benches FAIL (judge) a recorded real failure: the boot ended mid-probe
m16: a discarded aux byte sends LOST ps2server 101 (log) a_discarded_aux_byte_sends_nothing
m17: the write-off sends LOST ps2server 101 (log) three_failed_reenables_write_the_pointer_off_with_0xa7
m18: give_back frees nothing the kernel's pure library 101 (log) a_number_given_back_is_the_next_claims, every_number_held_refuses_a_claim_until_one_is_given_back, a_number_given_back_twice_panics
m19: no five-button mask toyos-usbhid 101 (log) a_button_past_the_fifth_is_dropped, every_report_is_read_or_refused_and_nothing_else
m20: the clock read after the look ps2server 101 (log) a_record_landing_as_the_deadline_passes_is_found
m21: no drain before the first write ps2server 101 (log) a_wedged_output_buffer_is_said_after_32_reads_with_nothing_written
m01–m15, but m06 as fix round 1 lists them: C 14's read delay and write rule, C 9, C 12's 0x20, C 11's interrupt, bit 4 in the aux stage, the System Flag, the keyboard reset, the 0xAA bound, the supervisor's waiter, the source's release, the buttons' OR, the framer's gaps, the re-enable's found byte 101 each, at 26c867374 (summary; each log beside its patch) each the test it names

Oracles:

  • C, K, K88 and TP at the pages cited, from which the fake's panics and shapes are written;
  • QEMU's i8042, keyboard and PS/2 mouse, an implementation that is not ours, in the smoke test;
  • both benches' ECs: read by M1 and M2 for the fake's T14 and AMD shapes, and by ps2server_arms, which passed on both at a42489c7c.

Where the build answers what the design left open, or differs from it

  1. A wedged controller is said and exited on, with no write. The design had ps2server disable both interfaces first. C 14 forbids a write with the output buffer full, which is the wedge's defining state.
    • The quiet stage drains before its first write, so it never writes into one (fix round 2).
    • The claim's release masks both lines when the process ends.
  2. A wake's records go one frame each, after t_end, by Connection::send_bytes. The SDK has no single write of several frames, and the owner buffers each frame whole either way.
  3. The console holds a Merge and no Pointer, because it draws no pointer.
    • A source's pointer records are decoded and moved by nothing. One that is refused drops the source.
    • Its row is service = true, because the manifest refuses a row that serves a port without it (ServesWithoutService).
    • Its HOME is /state/console, and the shell it starts runs on its own row.
  4. Every compositor row serves input, including tests/images/panelcase.toml's and tests/images/clipboardcase.toml's, since the compositor takes the acceptor as it takes prompt's.
  5. A USB pointer's buttons are masked to boot-mouse order's five in toyos-usbhid's decode, so every record the kernel's stream carries decodes (fix round 2 moved it from mouse::report).
  6. LOST is the keyboard's overrun alone, and releases keys alone (the ruling on BLOCKER 2). The pointer's write-off sends a zero-buttons report, and a discarded aux byte sends nothing. A failed re-enable is retried at once, three times in all, then 0xA7.
  7. Neither a keyboard nor a pointer to serve exits 1, said by name, so the supervisor's restart limit gives up on it by name. The design named only the exit-0 cases.
  8. The probe's lines are the new stages' own; the kernel's i8042: lines described stages that are gone.
  9. toyos::endow::isa joins pci_function and partition, the one lookup of an isa: claim by its spelling.
  10. The supervisor's waiter panics if process_wait refuses its own process handle. It used to ignore the answer; now the exit code decides.
  11. acpi_mediated_access asks for the test kernel by kernel_features: ACTUATOR_KERNEL, for its Global Lock actuator. i8042-withheld was what selected that kernel.
  12. isa_claim_refused comes back as the T14 row isa_claim_refused_on_a_kernel_port, under the isa-port-declared actuator (fix round 2). Its first premise, a kernel driving the i8042, is gone. The refusal it tests is still reached by a FADT reset register at 0x64.
  13. iapc_boot_arch's short-FADT test is kept as the century test it also was, named a_fadt_that_ends_at_the_century_byte_reads_it.
  14. The fake refuses every controller command but those the design sends or names (0x20, 0x60, 0xA7, 0xA8, 0xAD, 0xAE, 0xD4).
    • 0xAA, 0xAB and any other command C does not list panic as reserved.
    • A command C lists that no shape models panics with its own message.
  15. The track's step 7.2, as issues: the kernel-is-small track's input steps and exits deliver to the owner of input over the input port, and step 10's exit checks the kernel's streams deleted #904 rewrote it, is marked done, and step 6.3 with it, and "What is left" loses its first item.
  16. Every metalcase boot ends on test_rs_ps2server_hold (fix round 1), and its image serves test-runner log.
  17. PointerSource's allocation is kernel::pointers::Sources in the kernel's pure library (fix round 2), host-tested. mouse.rs keeps the type.
  18. Hw's wait is bus::Records::first (fix round 2), so rule 5's order is host-tested.

What I am unsure of

  • A controller that drops every 0x60. It now takes the arming's 0x20 with its keyboard interface enabled, since no 0xAD stands behind bit 4.
    • Its read-back still names the byte that did not take, and the ruling accepted one mechanism.
    • The fake models a drop of the arming's byte alone, and no test holds a drop of every byte.
  • The input port's name. Design: one userland input owner for the ARM desktop #868's draft names its compositor-facing protocol input too; whichever lands second renames.
  • The two issues whose subjects go, the-i8042-tallys-release-acquire-pair-has-no-negative-control.md and screen-diag-boot-leaves-no-i8042-line-on-the-panel.md. Neither is edited, and closing them is the orchestrator's call.
  • The compositor's LOST arm has no test. Its binary has no host build, and the change is one deleted line.

The design, as accepted

M1 has run, and decided what round 2 left to it. The scout kernel of b3637916a replaced the i8042 probe with the exchanges ps2server was then designed to make, and logged each answer, on both benches; both judges exited 0. Its readings are every scout-ps2: line of each boot, masked: the T14's and the AMD bench's. M1 below maps each reading to what it decided, and which of its decisions the reset at every start superseded.

M2 has run, and both benches' keyboards answer IBM's Reset from firmware's state and from a scanning set-2 keyboard. The orchestrator ran the scout 2b3433af7 (row scout_ps2_reset) once on each bench; both judges exited 0. The masked kernel logs and judges: the T14's (judge) and the AMD bench's (judge). On the reading, the orchestrator ruled that the keyboard is reset at every start, one path, so TOYOS_RESTARTED goes; this round applies it. M2 below maps each reading to what it decided. One of them is the orchestrator's to accept: the T14's 0xAA comes 516 ms after the ack, 16 ms past IBM's 500 ms, and the design accepts it inside the keyboard stage's 750 ms ceiling.

What this was read from: main at 858da4d42; the track at #900's text; #868; #877's design; #901's design and its ruling; kernel/src/arch/x86_64/i8042/, kernel/src/isa.rs, keyboard.rs, mouse.rs, device.rs, input_merge_test.rs, drivers/xhci/hid.rs, kernel/usbhid/, the compositor's and the console's input paths, the supervisor's start, waiter and restart paths, lib/manifest, kernel/ps2/; and IBM's references, read whole for the controller, the keyboard and the pointing device:

  • C and K: IBM Personal System/2 Hardware Interface Technical Reference — Common Interfaces, 84F9735, October 1990, its "Keyboard and Auxiliary Device Controller" and "Keyboards" sections as revised September 1991 (scan with text), cited with the section's own page. This is the latest revision IBM published, and it supersedes the earlier scans round 1 cited: its command list adds 0xA9 and drops nothing else, and its keyboard section loses the old K 19 buffer size and K 38 data-output text.
  • K88: the same book's May 1988 edition (scan), "Keyboards (101- and 102-Key)", pages 39–40, for the keyboard's data output, which the 1991 revision no longer carries.
  • TP: IBM's TrackPoint System Version 4.0 Engineering Specification, 4 April 2002 (archived), the T14's pointing device's own document, which states the PS/2 auxiliary-device protocol it implements.
  • The pointing device in IBM's PS/2 references: no edition of the Hardware Interface Technical Reference (1987's 68X2330, May 1988, October 1990) and none of the Model 25, 30 and 80 technical references has a pointing-device section giving the device's commands; the Model 30 reference gives the BIOS's INT 15h C2 interface, which names functions and not bytes. TP names IBM's "PS/2 Mouse Architecture Types 1 and 2" as the definition it conforms to; that document is not published anywhere I could reach. So every aux command below is cited to TP, and a pointing device that is not a TrackPoint stands on TP's statement that it is "completely compatible with the PS/2 Mouse".

The rulings this design follows:

The split

The kernel keeps only the claim and the post, both unchanged since #592: pio::fill_i8042_row (ports 0x60 and 0x64, ISA lines 1 and 12), crate::isa, and isa::isr, which records the interrupt on the claim and posts its watch. Nothing in the kernel speaks the 8042's protocol afterwards. The panic path is unchanged: it reads no port of the i8042 and takes no input.

ps2server never calls isa::ack, and the claim does not change. M1 read IRQ 1 and IRQ 12 as edge, active high, on both benches (GSI 1 and GSI 12), so round 2's change to answer Ok on a row with no level line is deleted with the level arm it served. On a machine whose MADT makes either line level, the claim keeps that line masked until a first ack that never comes (isa.rs:26-29): no record arrives, every probe stage runs out its ceiling, and the keyboard is disabled and the pointer found absent by name, with the stage's line saying whether OBF was set when it gave up.

/system/bin/ps2server takes everything else: the probe, both decoders, the aux self-reset's re-enable, the naming of undecoded bytes and the counters. It is exempt.owns: its job exists only on ToyOS. Two packages, as soundserver and netstack are:

  • system/ps2server/ps2 is kernel/ps2 moved by git mv, package name toyos-ps2 kept, its decoders and both test files unchanged. Device bytes are input from outside the program's trust, so the decoders stay a crate of their own.
  • system/ps2server is the program: the claim, the ports through toyos::ioport, the probe and the stream as state machines over a Controller trait (status, data, command, write, clock), host-tested against a scripted fake, and the connection to the owner of input.

Where key transitions go: the owner of input, over IPC

The owner is the program that owns the screen, until #868 lands. #868 is an open design with no code, so ps2server delivers to the compositor in images/system.toml and tests/images/metalcase.toml, and to /system/bin/console in images/console.toml, the shell on the panel, whose machine is the T14. When #868's inputserver lands it takes the acceptor and ps2server's row does not change.

The port. The owner serves input, and ps2server's row receives it. Each connection is one source; usbd joins as more connections under #901. The owner is the server, so it never blocks on a source:

  • frames through ipc::FrameRx, buffered whole;
  • at most four sources, a fifth refused by name;
  • a frame that does not decode drops its source by name;
  • a source whose connection ends, by its process ending or by its drop, has everything it held released.

The build refuses input to every row but ps2server's, as it refuses prompt (build/build.rs): a connector is the authority to type into the focused window.

The records are the kernel's own, so one decode serves every source:

  • A key is toyos_abi::input::RawKeyEvent: a HID Keyboard-page usage, and in modifiers nothing but MOD_RELEASED. Usage 0 and any other modifier bit are refused.
  • A pointer report is toyos_abi::input::MouseEvent, which gains what a device said: source, the pointer it came from; buttons, that pointer's own; scroll; kind (relative or absolute); and x, y as i16, the delta or the position in 0..=32767. An unknown kind, a button outside boot-mouse order's five bits or an absolute position outside the range is refused. Scroll and absolute motion are in the record because the xHCI path delivers both today and usbd will (round 1's NOTE 6). ps2server's one pointer is source 0.
  • LOST, a frame with no payload. As accepted it released everything the source held, and ps2server sent it on a keyboard overrun code or a byte it had to drop. The ruling on build review round 1's BLOCKER 2 narrows it: it is sent on the keyboard's overrun alone and releases keys alone. A pointer's state ends on a zero-buttons report or the connection's end.

The merge moves to userland whole. lib/input (toyos-input) holds it, because two programs share it and it decodes a peer's words. It is #![no_std], #![forbid(unsafe_code)] and depends on toyos-abi alone, never on toyos, so toyos-usbhid, a kernel dependency, links it (round 3's NOTE 4); the kernel links only what it calls, Keys.

  • Keys, one keyboard's held usages, and the modifiers-first transitions between two sets of them, moved out of toyos-usbhid's keyboard.rs (Usages, hold, Transitions, Pass). toyos-usbhid's Keyboard keeps the report decode and holds a Keys. This is the one held-set type in the tree (round 1's BLOCKER 10).
  • Merge, the owner's: a Keys per source; a transition is delivered when it changes the union of every source's set, and every delivered key carries the union's modifiers. A source that ends is Keys::hold(empty), delivered usage by usage where the union changes, so a key another source still holds stays held.
  • Pointer, the owner's: each pointer's buttons, keyed by its connection and its record's source, ORed into what it delivers, so one pointer's zero-buttons report never clears a button another holds; a connection's end clears every pointer it carried. One position, which relative motion moves and absolute motion sets, relative motion scaled by rel_scale_for (moved from the kernel with its test).

The kernel keeps no merge (round 3's NOTE 3, ruling 4), since a userland merge over a kernel merge would be two merges:

  • keyboard.rs loses HELD, modifiers_of, modifiers() and release_all, and queues each transition as it comes. It is one source to the owner, which dedupes it with that source's Keys exactly as HELD did: two USB keyboards holding one usage are one hold, and either one's release ends it.
  • mouse.rs loses BUTTONS, merged_buttons, LAST_X, LAST_Y, both scales, set_screen, rel_scale_for and PointerSource::PS2, and queues each report as the device gave it, with its PointerSource as the record's source. release_buttons(source) queues that source's zero-buttons record. PointerSource's allocation (IN_USE, claim, unbind) stays: it names a USB pointer, and merges nothing.
  • Both streams go with usbd (S1 of the xHCI move into usbd: a FAT entry that grows its file is written a cache flush after its chain and data, held by a module test that stops the disk inside a flush epoch #901, the track's step 10), when no kernel source is left. That is in the track's step 7.2 text below, with its exit.

A keyboard or mouse claim stops being evidence (round 1's BLOCKER 8, the deletion it offers). SOURCE_EXISTS, declare_source and source_exists go from both modules, with try_claim's two checks and xHCI's two declares (drivers/xhci/wait/boot.rs:154-155). A claim is granted on every machine. On AArch64, where nothing feeds either stream, the compositor no longer exits at its claim: it runs with no input source until #868 gives that machine one. That is the state #868 itself starts from ("the compositor exits because no input source can supply its keyboard and mouse handles").

No endowment order is needed (round 1's BLOCKER 9): ports exist before any program runs (sdk/toyos/src/port.rs), so ps2server's first frame waits in its connection until the owner accepts it, and no claim depends on another being minted first.

Restart

  • ps2server dies. The kernel runs the claim's generic release: the row's drop masks the lines, empties the record and answers the polls (isa.rs:125-138), and the ports go back with the process. The owner sees the connection end and releases its keys and buttons. No kernel code knows ps2server.
  • The supervisor sees it end (round 3's BLOCKER 1, ruling 2). Today close_when_it_ends, the only waiter, is spawned only for a start that kept acceptors (system/supervisor/src/main.rs:726), so a restart row that serves no port, as ps2server's does not, is never seen to end and never restarted. Supervisor change 1: a waiter is spawned for every start of a row that serves a port or is restart. It reaches ps2server alone: diskserver and fileserver, the only restart rows in any image, serve ports and are waited on today, and a row that is neither still has no waiter.
  • Supervisor change 2: a restart row is started again only when its process ended other than by exit 0; one that exits 0 is not, and its ports, if any, close as a non-restart row's do. ps2server exits 0 only when it has nothing to serve: no isa claim (AArch64), port 0x64 reading 0xFF (no controller), or the owner's port answering Gone. diskserver and fileserver never exit 0.
  • No third change: round 4's TOYOS_RESTARTED is deleted by the M2 ruling. Every start of ps2server is the same start, so it needs no fact about its predecessor.
  • The two decisions are one function in toyos-manifest, beside RESTARTS and Program::restart, which the supervisor calls at the spawn and in the waiter: whether a start is waited on, and whether an end is owed a new start. Its module test is in toyos-manifest, because the supervisor's binary builds against std::os::toyos and has no host build: a restart row with no port is waited on; a row that is neither is not; an end other than exit 0 of a restart row is owed a start; exit 0 is not; a non-restart row is never owed one. Its control: change 1 reverted (the waiter for ported starts only) reds the first.
  • Every ps2server resets the keyboard and keeps no memory (round 1's BLOCKER 1; round 3's ruling 1 as round 4's M2 ruling extends it). The /tmp/ps2server/firmware file and its isolation hole are deleted, and so is any reading of what firmware or a predecessor left: The probe is one sequence, the same on a boot's first start and on a restart.
  • What was typed while no ps2server ran is not delivered: the reset reinitializes the keyboard (K 13), and ps2server drains what it held before. The new connection is a new source to the owner.

The probe, following IBM

  1. The keyboard is reset, and its scan set is IBM's default after a reset, never queried (round 4's M2 ruling). The command byte is written with the keyboard's interrupt bit and the translate bit set, then 0xFF, answered 0xFA and then the completion code 0xAA "between 300 and 500 milliseconds after a Reset command is acknowledged" (K 11), after which "the keyboard defaults to scan-code set 2" (K 13) and "keyboard scanning begins" (K 11). So every start arms set 2 translated, the mode C 10 gives for PC compatibility, whatever firmware or a predecessor left, a set-1 keyboard from POST (K 1) included.
    • 0xAA is accepted whenever it comes inside the keyboard stage's 750 ms ceiling, and IBM's window is enforced at neither end: M2 read the T14's 516 ms after the ack, 16 ms past K 11's 500, and the AMD bench's 2.2 ms and 1.3 ms, far inside its 300. Both decide the same, set 2 translated, and K 11's window decides nothing ps2server does. The 750 ms ceiling leaves the T14 234 ms.
    • A 0xFC, any other byte for 0xFA or 0xAA, or no 0xAA inside the ceiling disables the keyboard by name, with the byte or the silence in its line.
    • The translate bit is set before the reset, not after, so the 0xAA arrives through the translation the stream will run under; M2 read 0xAA that way on both benches, with the command byte 0x65 before and after each reset.
    • The AMD bench's EC translates whatever bit 6 says (round 3's NOTE 1, M1's Read ID 0xAB 0x41: K 13, Figure 2, gives AB 83, and C 5, Figure 1, translates 83 to 41). The reset sets bit 6, so the stream runs as the EC does regardless; the fake's AMD shape translates regardless of bit 6.
    • Deleted with the query: the scan-set query 0xF0 0x00 (K 14) and its three arms (Told 0x02, Refused, Unanswered), the inference from firmware's translate bit, round 2's 0x01 arm, 0xF5, and the keyboard's 0xF4, since the reset leaves it scanning (K 11). With them goes round 3's NOTE 2, which arm the T14's refusal takes.
  2. A polled read waits 7 µs after the status read that showed the output buffer full (C 14), a fixed delay a hardware document mandates, cited at its site.
  3. A write is made only with IBF and OBF both clear (C 14). What a byte found waiting before a write is depends on when (round 3's BLOCKER 2):
    • before the keyboard scans (the drain, and the keyboard stage up to the reset's 0xAA), it is drained, with the rest, by name: the reset reinitializes the keyboard (K 13), and nothing typed then is delivered;
    • from the moment the keyboard scans, a byte with AUXB clear (C 7) is a keystroke and is fed to the keyboard's decoder, never dropped (round 1's BLOCKER 4); one with AUXB set is the aux device's and goes to its stage or decoder. That holds through the aux stage, the arming and the steady state.
  4. Device answers wait on the interrupt; controller answers are polled with the keyboard interface disabled. Before each device stage the probe sets that port's interrupt bit, and IBM has the controller raise IRQ 1 "when it places keyboard or command controller response data into its output buffer" and IRQ 12 for aux data (C 11). M1 counted the vector across every answer on both benches, with only that port's bit set: every answer byte raised the line, the T14's 0xEE included, and each exchange's deliveries equal its answers but for the T14's 0xF0, whose second delivery is the 0xEE the next exchange found waiting (round 3's NOTE 5). The delivery can trail OBF by a few microseconds (the AMD bench's 0xF0 ack: OBF 101 µs after the write, delivery 103 µs); ps2server reads status only after the record, so the lag never reaches it. M2 read the same of the reset: both its answer bytes raised the line on both benches, the 0xAA half a second after the 0xFA on the T14 included, one delivery each. The one controller command that draws a controller answer, the arming's 0x20, is written while the keyboard interface is disabled, under the bit 4 that the aux stage's and the arming's 0x60 each write (the ruling on m06), so the answer it draws, with AUXB clear, is the controller's and no keystroke's (round 3's BLOCKER 2b; round 5's NOTE 1): bit 4 is "Disable Keyboard" (C 11), what 0xAD sets, which "disables the keyboard interface by driving the 'clock' line low. Data is not received while the interface is disabled" (C 12). 0xAD, 0xAE, 0x60, 0xA7, 0xA8 and 0xD4 draw no controller answer.
    • The keyboard interface is command-byte bit 4 (C 11, "Disable Keyboard"): 0xAD sets it and 0xAE clears it (C 12), and a 0x60 writes it with the rest of the byte. So every 0x60 the probe writes states bit 4, and one written with it clear would re-enable the keyboard as 0xAE does (round 5's BLOCKER 1).
    • Bit 2, the System Flag, is written 1 in every 0x60 (round 5's NOTE 3, as ruled): C 11 places "the value written to this bit … in the system flag bit of the Controller Status register" and gives 0 after a power-on reset; 1 is the value M2 ran with (0x65) and firmware left on both benches (0x77). The arming's 0x20 read-back holds it.
  5. Every bounded wait looks at its condition once more after its deadline, so a preempted ps2server can lengthen a wait but never turn a late answer into silence. The stage ceilings stay today's (250, 750 and 600 ms); they bound liveness, and the one bound that decides an outcome is the reset's 0xAA inside the keyboard stage's 750 ms. The slowest answers read are the T14 keyboard's 0xAA, 516 ms after its 0xFA (M2), inside the keyboard stage's 750 ms, and the T14 TrackPoint's 0xAA, 367 ms after its reset's 0xFA (M1), inside TP's 500 ms and the aux stage's 600 ms.

The sequence, every command cited (round 1's BLOCKER 6, round 3's BLOCKER 2, the ruling on m06):

  1. Quiet: 0xAD, 0xA7 (disable the keyboard and aux interfaces, C 12); drain up to 32 bytes. Firmware's command byte is not read: nothing is decided from it, and the arming writes the byte whole.
  2. Keyboard stage: 0x60 (C 11) with the keyboard's interrupt bit alone, the System Flag, the keyboard interface still disabled (bit 4 = 1), the aux clock off and translation on: 0x75; 0xAE (C 12), which clears bit 4; 0xFF (K 13), its 0xFA and 0xAA. The keyboard now scans.
  3. Aux stage: 0x60 with the aux interrupt bit alone, the System Flag, the keyboard interface disabled again (bit 4 = 1), the aux clock on and translation on: 0x56; 0xA8 (C 12); 0xD4 (C 13) before each of 0xFF, answered 0xFA 0xAA 0x00 within the 500 ms diagnostics (TP 9, 12), and 0xF4 (TP 13). From that 0x60 on, the keyboard holds what it would send: "if the 'clock' line is inactive (low), data is stored in the keyboard buffer" (K88 39; C 15, step 5). So up to the arming's 0xAE no keystroke can load the output buffer, hold OBF against the aux answer, or be read as the command byte (round 3's BLOCKER 2a–c). Dropped: 0xF2, since the id is the byte after 0xAA; 0xF3 0x64, since 100 reports per second is the reset's default (TP 9); and 0xE8 0x03, which "has no effect for the TrackPoint controller" (TP 17).
  4. Arming: 0x60 with the keyboard's interrupt bit, the System Flag, the keyboard interface still disabled (bit 4 = 1) and translation on, and the aux interrupt bit and clock on only if the aux stage armed the device: 0x57 with it armed, 0x75 with it absent; 0x20 reads it back, written with OBF clear and answered by the controller alone, since the keyboard's interface is disabled and an aux byte carries AUXB. Then 0xAE, last: the keyboard sends what it held, and the stream begins.
  • Dropped: 0xAA and 0xAB, which IBM's list does not have ("commands not listed are reserved", C 9), and 0xA9, which it has (C 12) but which no decision needs: an aux device that is absent or broken is found by its reset going unanswered. With them goes today's inference that 0xA8 clearing bit 5 means a second port exists.
  • Write-off: after three failed re-enables the pointer is written off with 0xA7 alone, which stops the aux interface's clock (C 12), so IRQ 12 never rises again.
  • One edge IBM leaves open: a keystroke past its 10th clock when the aux stage's 0x60 disables the interface is completed by the keyboard (K88 39) and "not received" by the controller (C 12). If both hold, that one byte is lost, once per probe; no bench has shown it, and it is in the roast.

The stream

One wake drains the output buffer:

  1. Wait on the claim; take the record.
  2. While OBF is set, up to 32 bytes: wait 7 µs, read one byte, feed it to the decoder AUXB names.
  3. Read the clock after the status read that found OBF clear: t_end.
  4. Send the owner the wake's records (built: one frame each, after t_end).

Both lines are edge on every measured machine, and the controller raises its line whenever it loads a byte (C 11; M1, one delivery per answer byte), so a byte that loads after step 2's last status read raises its own wake, and a byte is never left in the buffer with no wake behind it. A byte that loads during step 2 is read there, and its wake finds OBF clear and reads nothing.

A wedged controller (round 1's BLOCKER 3) is OBF still set after 32 reads in one wake. That is no state IBM's protocol produces, since the output buffer empties when it is read (C 7), so ps2server says so with the last bytes it read and exits non-zero (built without the disabling write the design had: C 14 forbids a write with OBF full); the supervisor, now waiting on its row, starts it again, and its restart limit (RESTARTS ends in RESTART_WINDOW_SECS) gives up on it by name. The kernel's QUARANTINE goes.

The mouse framer's clock (round 1's BLOCKERs 2b and 5). IBM never says the controller holds a device off while its output buffer is full: the system "can" hold the clock (C 15, step 5), "can inhibit the keyboard until the system processes the input" (K88 40), "may inhibit the TrackPoint controller until it processes the data" (TP 50). M1 read both behaviours, and no lost byte, each with the first answer byte of a Read ID or Read Device Type left unread for 50 ms:

  • the T14's TrackPoint: the next byte loaded 1,000 µs after the held byte's read, inside one byte's 0.66–1.1 ms on the wire (11 bits at C 16's 30–50 µs clock phases), so the device was held off and sent on release;
  • the T14's keyboard: the next two loaded 30 and 40 µs after each read before, far less than one byte's time, so the EC had queued them;
  • the AMD bench's keyboard: 178 µs, then 2,005 µs;
  • every byte asked for arrived (3 of 3, 2 of 2, 3 of 3).

The framer covers both:

  • A wake's first aux byte is fed with the gap first_nanos − t_end of the previous wake, saturating at 0. Every later byte of the same wake is fed with a gap of 0.
  • first_nanos is when the wake's first interrupt landed (DeviceIrqRecord). A queued byte is read in the wake that read the one before it, at a gap of 0. A held-off device's next byte loads one byte's time after the read that released it, and that read is before t_end, so its gap is at most that byte's time: M1's 1,000 µs, under PACKET_GAP_NS's 5 ms.
  • So the gap is never longer than the device's own idle time, whatever ps2server's delays, and a slow ps2server can delay a resync but never split a packet. A gap longer than PACKET_GAP_NS happens only when the device itself went quiet: at 100 reports per second (TP 14) a packet's three bytes take at most 3.3 ms at C 16's 30–50 µs clock phases, and the line then idles about 6.7 ms.
  • The decoder is unchanged: ps2server keeps a device clock, the running sum of those gaps, and passes it as feed's now_ns. Every toyos-ps2 test moves unchanged.

The aux self-reset (0xAA 0x00 mid-stream) re-enables reporting with 0xD4 0xF4 once OBF and IBF are clear, and the device's 0xFA arrives in the stream as an aux byte the stream is waiting for, with a 25 ms ceiling (TP 46). Keyboard bytes meanwhile go to the keyboard's decoder. No interface is disabled, and nothing runs with interrupts off. The kernel's 30 ms interrupts-off re-enable (mod.rs:931) goes.

Diagnostics kept: the probe's lines, now the new stages' own; undecoded runs named whole (Partial::blame); the counters line, at most every 10 s and only when an interrupt arrived since the last. Not carried: the HEALTH verdict machine, the arming-edge bookkeeping, to_screen and the idle loop's verdict_due.

M1: what the benches read, and what each reading decided

The scout kernel of b3637916a (scout-ps2) ran in place of the i8042 probe, once on each bench, staged by cargo test --test toyos-build -- --metal --metal-readback <dir> scout_ps2; both judges exited 0. Every exchange read each byte 7 µs after the status read that showed it and wrote only with IBF and OBF clear (C 14), with only that port's interrupt bit set. The readings: T14, AMD bench.

What it read T14 AMD bench What it decided
Firmware's command byte, after 0xAD 0xA7 0x77 0x77 Round 4: Refused and Unanswered decided set 2 translated from it. Superseded by the reset at every start: nothing reads it
IRQ 1's and IRQ 12's line, as the MADT resolves them GSI 1 and GSI 12, edge, high the same The level arm goes: no ack in ps2server, no change to isa::ack, t_end in place of t_ack
Each answer byte with the vector's deliveries every byte raised the line; each exchange's deliveries equal its answers but for 0xF0's, whose second is the 0xEE's every byte raised the line; each exchange's deliveries equal its answers Rule 4 stands: device answers wait on the interrupt
The set query with translation off: 0xF0, then 0x00 0xFA, then 0xEE waiting before the argument's write; 0x00 drew no byte in 100 ms 0xFA, 0xFA, then no byte in 120 ms Round 4: the T14 reached Refused and the AMD bench Unanswered. Superseded: neither bench tells its set, and the reset at every start makes the question moot; the query and its arms go
The aux device's reset 0xFF 0xFA at 2.5 ms, 0xAA 367 ms later, 0x00 no byte, and no delivery, in 1 s The T14's pointer arms; the AMD bench's reset goes unanswered and its pointer is absent
A Read ID or Read Device Type with its first byte held 50 ms keyboard 0xFA 0xAB 0x84, later bytes 30 and 40 µs after each read; TrackPoint 0xFA 0x00, 1,000 µs keyboard 0xFA 0xAB 0x41 with bit 6 written and read back clear, 178 µs and 2,005 µs The hold: the T14's TrackPoint is held off while OBF is full and its EC queues keyboard bytes; no byte is lost on either. The framer's first_nanos − t_end covers both (The stream). The AMD bench's EC translates regardless of bit 6 (83 → 41, C 5)

M1's scout branch is deleted.

M2: whether each bench's keyboard answers a reset in time

The scout wt/toyos-ps2server-m2 at 2b3433af7, main at 858da4d42 and one commit, never landed: its kernel feature scout-ps2-reset replaced the i8042 probe with two keyboard resets and logged each answer byte with its time from the read before it and the vector's deliveries at its read. It wrote the command byte with the keyboard's interrupt bit alone and the aux clock off, and left the translate bit as firmware left it (set on both benches, so 0x65), the state the keyboard stage writes. The first reset was made from firmware's state, the second from the state the first left, a scanning set-2 keyboard behind translation, which is what a restarted ps2server finds. After each it logged every byte for 1 s and read the command byte back. The orchestrator ran its row scout_ps2_reset, one boot per bench; both judges exited 0. The readings, masked: T14, AMD bench.

M2 read T14 AMD bench What it decided
Firmware's command byte; IRQ 1's line 0x77; GSI 1, edge, high the same M1's reading, again; nothing new
The reset from firmware's state: 0xFA, then 0xAA 0xFA 80 µs after the write; 0xAA 516,362 µs after the 0xFA's log line 0xFA 581 µs after the write; 0xAA 2,170 µs after The reset at every start stands on both benches (the ruling, on this row): firmware's state answers it
The reset from a scanning set-2 keyboard: 0xFA, then 0xAA 0xFA 79 µs; 0xAA 516,406 µs after 0xFA 226 µs; 0xAA 1,320 µs after A restart finds what a first start finds; one path serves both, and TOYOS_RESTARTED goes
How the times are measured each 0xAA time is from the scout's clock reading after the 0xFA's log line, not after its read (last = now() follows log! in scout.rs, round 5's NOTE 5); the reset's summary line, write to last read, bounds the difference under 1 ms the same No decision moves
0xAA against K 11's 300–500 ms 16 ms late, both times about 298 ms early, both times The 0xAA is accepted whenever it comes inside the keyboard stage's 750 ms ceiling, and IBM's window is enforced at neither end. The orchestrator's to accept: this is the row the staged table put to the orchestrator, "0xAA later than 500 ms"
The vector's deliveries one per answer byte, 2 per reset the same Rule 4 holds for the reset: its 0xAA is waited on, not polled
Bytes in the 1 s after each reset none none Nothing follows the 0xAA: the stage waits for nothing more
The command byte read back after each reset 0x65, unchanged 0x65, unchanged The reset changes no controller state; the arming writes the byte whole regardless
0xFC or another byte never never The refusal arms stay, held by the fake alone

The fake's T14 and AMD shapes are these readings.

#877

#877's A2 and A3 are designs, not built, and both edit files this change moves or deletes. A3's three SET1_E0 entries land in the moved key.rs, which still decodes set 2 behind translation; whichever lands second carries the other's lines. A2 has no arm left in ps2server: it decides the AMD bench's unanswered set query from the translate bit, and ps2server makes no query. If A2 lands first, this change deletes its lines with the kernel's i8042 driver; if this change lands first, A2 has nothing to edit and is superseded. Round 5 ruled it: superseded if this lands first.

The track's step 7.2, and its exit

The track's step 7.2 says ps2server feeds "the kernel's keyboard and mouse streams so the merge with USB HID stays where it is". The owner's ruling on #899 replaces that, so the build rewrites the step, the issue it takes up, to say: ps2server delivers over IPC to the owner of input; the kernel's merge is deleted, and its streams carry its own sources' records unmerged until usbd takes the last of them, which deletes them. The exit, mapped (round 1's BLOCKER 11):

The exit asks What meets it
No i8042 code in the kernel: arch/x86_64/i8042/, idt/i8042.rs and both keyboard_controllers gone from kernel/src, and toyos-ps2 gone from kernel/Cargo.toml The deletions below, and kernel/Cargo.toml's toyos-ps2 line. The build runs git grep -n -i -e i8042 -e keyboard_controller -e toyos_ps2 -- kernel and names what is left (round 5's NOTE 4): pio::fill_i8042_row and isa's docs, the row the claim is minted from, which name the function's ports and lines and speak no protocol; acpi_mode.rs:222's doc, which orders the SCI's row after the i8042's row; and kernel/cpuvuln/fixtures/tcg/console.txt:306-311, a captured guest console that is data, not kernel code. arch/x86_64/mod.rs:8-10's header, whose keyboard_controller example and i8042 entry go stale, is edited in the build
A kernel test that a claim whose holder dies is handed to the next claimant, the stream delivering its transitions and none of the dead holder's, red on a kernel that keeps the dead holder as the source ps2server's kernel claim is the isa row, so the test is isa_lines_reach_their_holder, the T14 row, with one new role: dies holds the claim, has the keyboard acknowledge so a record is pending, and ends without reading it; after then claims the row and must find no record. Its control: the row's release with IRQ[row].clear() removed (isa.rs:131) keeps the dead holder's record, and after goes red. The step's text is rewritten to name the isa claim
ps2server's restart mid-packet held by a_lost_scancode_byte_never_wedges_the_prefix_state and a_stream_truncated_at_any_offset_resyncs_within_two_packets Both move with toyos-ps2 unchanged, since the decoder's API does not change; the restart that reaches them is the supervisor's change 1, held by its module test

The superseded "typing resumes after ps2server is killed and restarted" and round 1's hand check are gone.

What is deleted

Whole files, at 858da4d42:

File Lines
kernel/src/arch/x86_64/i8042/mod.rs 1,284
kernel/src/arch/x86_64/i8042/tally.rs 80
kernel/src/arch/x86_64/idt/i8042.rs 6
kernel/src/arch/aarch64/keyboard_controller.rs 13
kernel/src/input_merge_test.rs 155
kernel/loom/tests/i8042_tally.rs 214
issues/t14-hands-over-an-uninitialised-8042.md, closed (round 5's NOTE 2, as ruled): its subject, the fallback from firmware's byte, is deleted by the reset at every start, which reads no byte firmware left —

Hunks, each an estimate until the build counts it:

  • The kernel's merge: keyboard.rs (about 45), mouse.rs (about 75), device.rs's two source checks and set_screen call (about 12), xHCI's two declares; the test-input-merge actuator in actuator.rs, its call in main.rs, and its guest test and list entries in tests/toyos.rs and tests/metal/src/lib.rs. Why that guest test goes: it held the kernel's merge, and there is none left; the merge it checked, a shifted key across sources, a repeated make, a released source's buttons, is toyos-input's, held by its host tests below, the cheaper tier.
  • The driver's seams: the 0x24 vector, I8042_VECTOR and mod i8042 in idt/mod.rs; i8042 and the keyboard_controller seam in both arch modules and aarch64/boot.rs; verdict_due and service in sched/driver.rs; irq_ring's I8042; irq_census's i8042 source with its parsers in tests/common/irqcensus.rs and tests/checks.rs; the i8042-withheld actuator with its uses in tests/toyos.rs, tests/metal/src/lib.rs, tests/common/power.rs and tests/common/isa.rs; kernel/loom/src/lib.rs's i8042_tally; drivers/acpi.rs's and lib/acpi's iapc_boot_arch with its three tests, whose one caller only logged it.
  • kernel/ps2 (1,023 lines) leaves kernel/ for system/ps2server/ps2; the "like toyos-ps2" clause in five other crates' Cargo.toml headers goes with it.

kernel/src: 64,063 lines at 858da4d42. About 1,900 go and none is added, so about 62,150 remain; the build reports the measured number. Two interrupts-off sections go: the arming write and its read-back (mod.rs:1192-1242) and the aux re-enable's IrqGuard; mask_windows measures them base against branch by step 2's rule.

Added in userland, estimated: ps2server about 750 lines with its tests' fake; toyos-input about 280, plus what moves out of toyos-usbhid; the compositor and the console about 100 between them; the supervisor about 15 and toyos-manifest about 50 with its test.

Checks

Host (cargo run -- --ci host):

  • toyos-ps2's decode.rs and fuzz.rs, moved unchanged.
  • ps2server's probe.rs, against a scripted fake controller. Its shapes:
    • QEMU's: the reset answered 0xFA 0xAA, and a PS/2 mouse;
    • the T14's as M1 and M2 read it: the reset answered 0xFA at 80 µs and 0xAA 516 ms after; the TrackPoint's 0xFA 0xAA 0x00;
    • the AMD bench's: the reset answered 0xFA at 581 µs and 0xAA 2.2 ms after; no aux answer; and its EC translating whatever bit 6 says;
    • a keystroke during the aux reset, which reaches the keyboard's decoder after the arming's 0xAE, and is never drained, read as the command byte or let hold OBF against the aux answer;
    • a start after firmware or a dead predecessor left bit 6 clear and the keyboard in set 1 (K 1): the stage sets bit 6 before the reset, and arms set 2 translated;
    • the reset's BAT answered 0xFC; its 0xAA coming 1 ms after the 750 ms ceiling; and no 0xFA: each disables the keyboard by name;
    • absent (0xFF); a dropped command-byte write.
  • The fake holds the keyboard interface as command-byte bit 4 (C 11, C 12): 0xAD sets it, 0xAE clears it, and 0x60 writes it with the rest of the byte; a keystroke the script offers while bit 4 is set waits in the fake keyboard, as K88 39 has it, and loads only once bit 4 clears.
  • The fake panics on each IBM rule broken: a data read sooner than 7 µs after its status; a write with IBF or OBF set; a reserved controller command (0xAA, 0xAB, anything C does not list); a 0x20 written while the keyboard interface is enabled; a device answer read before its interrupt.
  • ps2server's stream.rs: an overrun code sends LOST; an undecoded run is named whole; OBF still set after 32 reads ends the stream by name; a keyboard byte found waiting before the re-enable's write reaches the keyboard's decoder; three failed re-enables write the pointer off with 0xA7; and a packet survives a delay longer than PACKET_GAP_NS between any of its bytes' reads and the next wake, which arrival-to-arrival gaps would split.
  • toyos-input: every refusal of a key, pointer and LOST frame; Shift from one source and a from another arrive as a shifted a; a repeated make from one source delivers nothing; a source's end releases what it held and nothing another source holds; two pointers' buttons OR, and one's zero-buttons report clears none of the other's; a connection's end clears its pointers' buttons; relative motion scales by the screen and absolute motion sets; rel_scale_for's test, moved.
  • toyos-usbhid: its tests unchanged over Keys.
  • toyos-manifest: the supervisor's restart decisions, as Restart lists them.

The kernel's integration (T14 rows): isa_ports_are_the_binders_alone and isa_lines_reach_their_holder, on the plain testcases boot now that no kernel takes the row, with isa_lines' new dies role as the exit's test.

The smoke test: console_image_boots, an existing QEMU boot of images/console.toml, also requires ps2server's armed line for the keyboard. No new boot is added. Why QEMU: the guest suite is the one merge-queue check that starts ps2server under the supervisor from a shipped image, so it is what catches a manifest row, a claim or a connection that breaks the start; the metal rows reach the same, but only when the orchestrator runs them. QEMU's i8042 and keyboard are an implementation that is not ours, answering the reset with K 11 and 13 as the oracle.

Metal, run by the orchestrator:

  • ps2server_arms rides METALCASE on both benches: on each, the keyboard reset and armed set 2 translated, the line naming the 0xAA's time after the ack; on the T14 the aux device armed, on the AMD bench the aux device absent; no i8042= source in the IRQ census.
  • mask_windows, base against branch.

Negative controls, each run and reported at the build:

  • the whole change reverted onto the base: ps2server_arms and the smoke test red, since nothing says ps2server:;
  • one mutation per IBM rule: probe.rs red;
  • the aux stage's 0x60 written with bit 4 clear (round 5's BLOCKER 1): the keystroke-during-the-aux-reset shape and the dropped-command-byte shape red;
  • the System Flag written 0: the 0x20 read-back's shapes red;
  • the reset removed from the keyboard stage, which then arms as firmware left it: the set-1, bit-6-clear shape red;
  • the 0xAA's bound narrowed to K 11's 500 ms: the T14 shape red, which is M2's reading holding the ceiling where it is;
  • the supervisor's waiter for ported starts only: toyos-manifest's test red;
  • the row's release keeping its record: isa_lines_reach_their_holder red;
  • the owner's release on a source's end removed, and the buttons' OR replaced by the last report's: toyos-input's tests red;
  • arrival-to-arrival gaps in place of the device clock: stream.rs's packet test red.

Oracles: C, K, K88 and TP at the pages cited; QEMU's i8042 and keyboard, an implementation that is not ours; both benches' ECs, read by M1 and M2; the HID Usage Tables, for the decoder's tables.

Where the moved code rested on Linux

  • i8042/mod.rs:892-895, the set read and never written, citing Linux's atkbd_select_set and EDK2's Ps2KeyboardDxe. Now the set is neither read nor written: the reset leaves set 2 (K 13), and M2 read both benches' keyboards answer it.
  • kernel/ps2/src/key.rs:4 and :172, set 2 behind translation as "Linux's default configuration". Now C 10, the PC-compatible mode.
  • issues/t14-keyboard-will-not-report-its-scancode-set.md justifies the Refused arm by Linux's i8042.c/atkbd and withholds 0xF2 by atkbd_skip_getid. Now there is no Refused arm and no query, and ps2server sends no Read ID. The issue file is not edited.
  • kernel/usbhid/src/keyboard.rs:21 and :27 cite Linux in the header of the file Keys moves out of, and kernel/usbhid/src/pointer.rs:17 in the file the five-button mask moves into. issues/the-tree-names-linux-as-an-authority.md owns all three by its exit (git grep -n -i linux -- kernel …), so they stay where they are.

Roast

  • ps2server holds the machine's reset line in every shipped boot. 0xFE and 0xD1 reach the CPU's reset (C 13). issues/the-i8042s-holder-holds-the-machines-reset-line.md records it, owned by the power broker's track; until now only test jobs held the row.
  • The kernel's streams stay until usbd, carrying their own sources unmerged. The owner merges them with ps2server's, so a USB keyboard and the T14's keyboard share one modifier set as today.
  • An ABI change for the mouse stream, made because the owner cannot integrate a pointer whose position the kernel already integrated, nor OR buttons whose source the record does not carry. Relative and absolute motion reach the owner as the device gave them.
  • AArch64's compositor runs with no input until Design: one userland input owner for the ARM desktop #868, where today it exits at its keyboard claim.
  • The owner of input is two programs until Design: one userland input owner for the ARM desktop #868, the compositor and the console, each holding a Merge and a Pointer.
  • The supervisor gains two rules: it waits on every restart row, and a clean exit is not restarted. Both reach every restart row.
  • Every start costs a keyboard reset: M2 read the T14's BAT at 516 ms after the ack, a boot wait the query it replaces did not have, before the aux reset's 367 ms (M1); the AMD bench's costs 2 ms. A start also loses what was typed while no ps2server ran, and on a first start, what was typed before ps2server's reset.
  • The T14's keyboard answers 16 ms past IBM's 500 ms, and the design accepts it because the keyboard stage's ceiling is a liveness bound, not IBM's window; a keyboard that takes longer than 750 ms is disabled by name. Neither bench was read to drift: the T14's two resets are 44 µs apart.
  • A machine whose MADT makes IRQ 1 or 12 level gets no PS/2 input, named in the log; neither bench is one.
  • A keystroke in flight when the aux stage's 0x60 disables the interface may be lost, once per probe, where K88 39 and C 12 disagree; no bench has shown it.
  • One more context switch on the keystroke path, and ps2server is a normal thread. The keyboard's own buffer absorbs a delay and its overrun code becomes LOST; nothing is timed on QEMU, and the T14's numbers are reported, not judged.
  • Following IBM changes every machine's exchange: a reset in place of today's set read, the 7 µs, writes only with OBF clear, the keyboard's interface disabled for every controller command after its stage, three controller commands and three aux commands fewer. QEMU is read by the smoke test, and the benches were read by M1 and M2 and are held by ps2server_arms.
  • The diag image loses its panel line for the i8042; ps2server's lines are in /log only.

Design-round questions, since ruled

Round 5's rulings decided the 0xAA bound, #877's A2 (superseded by this landing first, or deleted with the kernel driver if A2 lands first) and the uninitialised-8042 issue, which this build closes.

🤖 Generated with Claude Code

https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy

@Japabu

Japabu commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator Author

Design review, round 1: head 8bb4bff05

Judged against origin/main at b6a556546, which carries #900's track text, #902's clean-room rule and #893's sweep, and against #877's design as accepted at dc0d0bef2. git log origin/main..8bb4bff05 is one empty commit, and git diff --shortstat is empty: production 0, tests 0. The body estimates kernel/src 1,300 lines smaller. IBM's two chapters were read page by page from the scans the body links (C = controller, K = keyboard).

What holds up:

  • Every IBM quotation in rules 1 to 3 is on the page cited: C 8 bit 6 and its note, K 23, C 12, C 9, K 19, K 20 to 22, and C 14's T3/T4.
  • No Linux reference is added. The three Linux citations in the moved code (kernel/ps2/src/key.rs:4, :172 and i8042/mod.rs:893-894) go.
  • Minting input-feed is gated on SysCap DEVICE, which only the supervisor holds (kernel/src/syscall/device.rs:184, object/syscap.rs).

BLOCKER

  1. PR body, Restart and the probe's rule 1: after a restart, the translate bit the Refused and Unanswered arms read is not firmware's.
    • before is read from the controller (i8042/mod.rs:1014). On a boot's first probe, that is firmware's byte. On every later probe, it is the byte the dead ps2server left.
    • If ps2server dies after arming, the bit is its own earlier decision read back as evidence.
    • If it dies inside the probe, after rule 1 has cleared bit 6 for the query and before arming, the restart reads translate off. The keyboard is then disabled by name on exactly the two benches whose arm is an inference: the T14 (Refused) and the AMD bench (The AMD bench's desktop: HDA controller choice, native rate, codec volume and the volume keys #877's A2, Unanswered).
    • The fake's "controller left by a dead holder, which arms" covers only the steady state.
    • The design must say where firmware's byte survives a restart, or what a restart decides without it.
  2. PR body, the steady state and "What I am unsure of", IRQ 1 and 12: edge versus level does matter, in two places.
    • (a) isa.rs:26-29 and :184-188: a level line is masked from the mint until the holder's first ack. The design acks only after a wake, so on a level row the first device exchange of the probe never wakes, and every exchange runs out its deadline.
    • (b) isa::isr stamps the record when the interrupt is delivered (isa.rs:258-265). A level line stays masked from step 3's read to step 6's ack, so the next byte's last_nanos is the ack's time, not the byte's arrival. A preemption longer than PACKET_GAP_NS between the read and the ack then splits a packet, which is what "never split a packet" rules out.
    • Ack once before the first wait, and ack right after the read. Or read both laptops' MADTs and state the trigger.
  3. PR body, "A wedged controller": the wedge rule cannot fire on the loop the design specifies.
    • The steady state reads one byte per wake, so there are never "16 reads in one wake".
    • If OBF stays set on an edge line, no new edge comes: ps2server parks for ever and says nothing.
    • On a level line, every ack re-raises the interrupt: ps2server makes one wake per byte, without end. That is the spinning CPU QUARANTINE (i8042/mod.rs:457-459) exists to prevent.
    • The replacement for the quarantine is dead as designed, and stream.rs's "a wedged controller ends the stream" tests a loop the design does not run.
  4. PR body, rule 3 against the aux self-reset: a keystroke is dropped.
    • "A byte found waiting before a write … is read and named among the undecoded bytes" applies to the steady-state re-enable's first write, 0xAD.
    • A keyboard byte sitting in OBF there is a keystroke. A dropped break code is a stuck key until the next Lost.
    • This also contradicts rule 4, where a byte from the other port is fed to the stream. Data loss.
  5. PR body, "The steady state takes one byte per interrupt" and the mouse framer: the cited pages say can, not does.
    • C 13 step 5: "The system can hold the 'clock' signal inactive to inhibit the next transmission."
    • K 38: "the system can inhibit the keyboard until the system processes the input."
    • Neither says the Type 1 controller holds the clock until OBF is read. Yet the design's "cannot load another byte until this one is read" and "a delayed read inhibits the device" stand on these pages as a statement of fact.
    • Find the page that states it, or state the claim as unbacked and measure it.
  6. PR body, Roast, 0xAA/0xAB/0xA9 and the aux commands: kept because test machines answer them, against the reference the design says it follows.
    • C 7: "commands not listed are reserved". C 8 to 11 list none of the three.
    • The reason given is that QEMU and both laptops answer them. That is a choice made on a test machine's capability, a BLOCKER under Fit.
    • The PC AT reference and IBM's pointing-device chapter are cheap to read, and the design ships five aux commands with no citation ("the build cites them or says it cannot").
    • Read both before acceptance, then cite each command or drop it.
  7. PR body, M1: the measurement is too narrow for what the design now assumes.
    • Rule 4 makes every device answer wait on IRQ 1 or 12, and the roast admits only the metal rows can say whether each EC raises its line for 0xFA, the set byte and the aux 0xFA 0xAA id.
    • The same scratch boot can count the vector between each write and its answer. Measure that before building.
    • M1's set-query result also decides A2's fate:
  8. PR body, the input feed's mint: keyboard and mouse claims stop being evidence, and the design keeps the check and adds machinery to feed it.
    • declare_source is a one-way flag (kernel/src/keyboard.rs:16-21 and mouse.rs:10-15). Minting a feed sets both.
    • So every image naming ps2server grants the compositor a keyboard and a mouse whether anything can ever feed them:
      • AArch64, whose images/system.toml is the same file and whose isa claim is NotFound;
      • a legacy-free x86 machine;
      • a T14 boot whose keyboard is disabled by name (issues/t14-hands-over-an-uninitialised-8042.md);
      • any machine with no aux device.
    • That is a silent default in place of a refusal by name. On AArch64 it changes the compositor from refused to running with dead input, which the design does not mention.
    • Either declare the source when the holder arms, or delete SOURCE_EXISTS, declare_source and source_exists in both modules, the two try_claim checks, xHCI's two declares (drivers/xhci/wait/boot.rs:154-155) and the proposed rank. The deletion is the smaller design.
  9. PR body, "Endowment order" and Checks, toyos-manifest: the rank moves crates only so a host test can hold it.
  10. PR body, "The kernel side": the feed's 256-bit held set and its release on drop duplicate toyos_usbhid::keyboard::Keyboard.
    • held: Usages and release() at kernel/usbhid/src/keyboard.rs:86-87 and :119 are the same thing: one keyboard's held usages, and every one of them released when it goes.
    • One type serves both sources, so a second one is a sibling of something the tree already has.
  11. PR body, Checks and Issues: the design answers an exit that The small-kernel track says what is done, what is left, and in which order #900 replaced, and then marks the stage done.
    • On main, step 7.2's exit is:
      • no i8042 code in the kernel, and toyos-ps2 gone from kernel/Cargo.toml;
      • a kernel test that a keyboard claim whose holder dies is handed to the next claimant, and the keyboard stream delivers its transitions and none of the dead holder's, red on a kernel that keeps the dead holder as the source;
      • the restart mid-packet held by a_lost_scancode_byte_never_wedges_the_prefix_state and a_stream_truncated_at_any_offset_resyncs_within_two_packets.
    • The body argues the superseded "typing resumes after ps2server is killed and restarted", by composition and a hand check.
    • Map each check onto the current exit:
      • name which row is the owed kernel test, and its red-on-base control;
      • say that both toyos-ps2 tests move unchanged.
    • A close whose exit is not shown met is a BLOCKER.
  12. PR body, "One new thing, the input feed", and Roast, "The kernel keeps a merge that userland could own": a kernel addition the design itself says userland could own, with the userland shape left unweighed.
    • The feed is new ABI, a new class and about 120 kernel lines. It exists only because the merge stays in the kernel.
    • A userland owner can hold the kernel's existing keyboard and mouse claims for USB HID beside ps2server's port, and merge both in userland. That needs no feed, no rank and no claim-evidence change. Design: one userland input owner for the ARM desktop #868, open, proposes exactly that, "rather than another kernel injection API".
    • The track keeps the merge where it is, but it is not an owner ruling (2026-09-28 rules only that drivers are userland).
    • Weigh the two on cost, or put the choice to the orchestrator before this is built.

NOTE

  1. PR body, "What is deleted": tests/guest/rust-tests/src/bin/isa_claim_refused.rs is already gone from main (Integration tests outside the kernel are cut, and unit and module tests hold what they checked #893), so the row and its 23 lines come out of the count.
  2. PR body, "The kernel side": Claim's contract is "a move-only proof that a device is claimed; at most one exists per device" (kernel/src/device.rs:34-37). A non-exclusive Claimed::Feed, which is no device, breaks that sentence.
    • The feed is the authority to type into the focused window as the user. The build should refuse input-feed to every row but ps2server's, as it refuses swap and update (build/build.rs).
  3. PR body, Restart: a feed is never refused AlreadyExists, so CLAIM_RETURN (supervisor/src/main.rs:2430) does not wait for the old one. Its deferred drop can queue key-ups after the new feed's first key, and HELD is keyed by usage, not by source. State it, or order the feed's mint after the isa row's.
  4. PR body, Roast, isa::ack: the reason given is to spare an ABI change, which Fit refuses as a reason. The merit reason is that the holder need not know its triggers and the kernel unmasks what is level. State that one.
  5. PR body, the ABI: the record's decode is a kernel decision about the user/kernel boundary, which toyos-userbound's description claims. Put it there, or say why toyos-abi follows IsaId::from_wire.
  6. PR body, Roast, usbd: FeedRecord carries no scroll and no absolute motion. The xHCI path passes both today (drivers/xhci/hid.rs:82), so "every source is a feed" after usbd needs a different record. Say so.
  7. PR body, Checks, input_feed: one job holding two feeds collides on the endowment label dev:input-feed (supervisor/src/main.rs:2607). "Every malformed write is refused" repeats the toyos-abi host tests. Keep the one kernel claim, the apply-all-or-none.
  8. PR body, rule 1, Refused: C 8 and K 22 give the keyboard's default set. That firmware's translate bit means set 2 is the design's inference, not IBM's statement (prose).
  9. PR body, the smoke test: K 23 is the oracle for the Told arm, and QEMU is the environment it runs in (prose).
  10. PR body, The AMD bench's desktop: HDA controller choice, native rate, codec volume and the volume keys #877: A2 as accepted keeps its decision in a match with no decision-table test (round 1, B4). The fake controller's AMD shape tests the runtime sort, which B4 allowed. The build adds no table test beside it.

SEND BACK

@Japabu

Japabu commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator Author

Ruling on BLOCKER 12, where key transitions from a userland driver go. Userland owns the merge. "Userland first: the kernel takes on only what userland cannot." Once the driver is in userland, the kernel's part in input is a relay that userland can own. So there is no input-feed claim and no kernel injection API. ps2server, and usbd under #901, deliver key transitions over IPC to the userland owner of input, which is the inputserver #868 proposes, or the compositor until #868 lands. The design states which, and the kernel's keyboard stream goes when its last kernel source leaves. This ruling covers #901's feed question too.

The other BLOCKERs are taken as written. In particular, M1 runs before acceptance as a scout (it is mine to run on both laptops), and the design answers #900's current 7.2 exit.

@Japabu

Japabu commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator Author

Design round 2: the answer to round 1's review

Round 1's review: #899 (comment). The owner's ruling on its BLOCKER 12: #899 (comment). The body at the new head is the design. This comment maps each finding to what changed, preferring the deletion wherever the review offered one.

The authority for the protocol is now IBM's latest revision, read whole: the October 1990 Hardware Interface Technical Reference — Common Interfaces (84F9735), whose controller and keyboard sections were revised in September 1991 (C, K). Its keyboard section lost the old K 19 and K 38, so K88 (May 1988, pages 39–40) is cited for the keyboard's data output. For the pointing device, no IBM PS/2 reference I could reach has a section giving the device's commands: the 1987, 1988 and 1990 Hardware Interface Technical References, and the Model 25, 30 and 80 technical references, give either nothing or the BIOS's INT 15h C2. The device's own document is IBM's TrackPoint System Version 4.0 Engineering Specification (TP), which states the PS/2 auxiliary-device protocol the T14's TrackPoint implements. No other implementation is cited.

M1 is staged as a scout and runs before acceptance: wt/toyos-ps2server-m1 at b3637916a.

BLOCKERs

  1. Firmware's translate bit across a restart. M1 decides whether any bench needs it. If neither bench reaches Refused or Unanswered, both arms are deleted, a refused or unanswered query disables the keyboard by name, and no restart needs firmware's byte. If one does, firmware's byte survives in /tmp/ps2server/firmware, the kernel's in-memory tmpfs, which lives one boot: written by the boot's first ps2server after 0xAD 0xA7 (which never touch bit 6) and before its first 0x60, so a restart that finds no file reads a byte nobody but firmware wrote. Body: Restart, last bullet. The cost, /tmp being every process's until the per-program views land, is in the roast.

  2. Edge versus level.

    • (a) The claim is acked once after it is bound and before the first wait, so a level line is open for the probe's first exchange. Body: The probe, rule 4.
    • (b) The framer no longer reads arrival-to-arrival time. A wake's first aux byte is fed the gap from the previous wake's post-ack clock to this wake's first_nanos, saturating at 0, and every other byte of the wake a gap of 0. A byte that loads while a level line is masked is stamped at the unmask, before that clock, so its gap is 0: the gap can only understate the device's idle time, and no delay of ps2server's splits a packet. Body: The stream, "The mouse framer's clock".
    • M1 also reads both benches' IRQ 1 and IRQ 12 triggers from their MADTs.
  3. The wedge rule could not fire. One wake now drains the output buffer, up to 32 bytes. OBF still set after 32 reads is the wedge: ps2server disables both interfaces, names the bytes, and exits non-zero. An edge line cannot strand a byte, since every byte that loads raises an edge. A level line held with OBF clear contradicts C 9 and is not designed for; it would cost a userland thread, never the kernel. stream.rs tests the loop the design runs. Body: The stream.

  4. A keystroke dropped before the re-enable's write. In the steady state a byte found waiting before a write is a byte of the stream, fed to the decoder of the port AUXB names; only the probe's first step drains. The re-enable no longer disables the keyboard interface at all: it is 0xD4 0xF4 with the 0xFA awaited in the stream, and keyboard bytes meanwhile go to the keyboard. stream.rs has the test. Body: The probe, rule 3, and The stream, "The aux self-reset".

  5. "Can", not "does". Stated as unbacked: C 15 step 5, K88 40 and TP 50 all say the system can or may inhibit. The design no longer depends on it (the framer's clock, BLOCKER 2b), and M1 measures it: the first byte of a keyboard Read ID and of an aux Read Device Type is held unread for 50 ms and each later byte is timed from the read before it. Body: The stream and M1.

  6. Reserved commands and uncited aux commands. 0xAA and 0xAB are dropped: C does not list them, and "commands not listed are reserved" (C 9). 0xA9 is listed in the 1991 revision (C 12), but no decision needs it, since an absent aux device is found by its reset going unanswered, so it is dropped too, and with it the inference that 0xA8 clearing bit 5 means a second port exists. Of the aux commands, 0xFF and 0xF4 stay (TP 12, 13). 0xF2 goes, since the id follows 0xAA; 0xF3 0x64 goes, since 100 reports per second is the reset default (TP 9); 0xE8 0x03 goes, since it "has no effect for the TrackPoint controller" (TP 17). Every command kept is cited. Body: The probe, "The sequence".

  7. M1 too narrow, run after acceptance. M1 is staged now, before acceptance, and widened to read IRQ 1's and IRQ 12's triggers, the vector's deliveries behind every keyboard and aux answer, the set query with translation off, and the held buffer. Each set-query outcome's consequence is stated: both benches answering deletes Refused, Unanswered and The AMD bench's desktop: HDA controller choice, native rate, codec volume and the volume keys #877's A2; the AMD bench answering deletes Unanswered and A2; the AMD bench still silent keeps A2 as accepted. An answer with no interrupt behind it withdraws rule 4 before anything is built. Body: M1.

  8. Claims stop being evidence. The deletion: SOURCE_EXISTS, declare_source and source_exists go from keyboard.rs and mouse.rs, with try_claim's two checks and xHCI's two declares. No feed exists to need them. The consequence the review names is stated: on AArch64 the compositor runs with no input source until Design: one userland input owner for the ARM desktop #868, where today it exits at its claim. Body: Where key transitions go.

  9. The rank moved crates for a test. Gone, with the move and its test: with no kernel source there is no endowment order to decide, and ports exist before any program runs. Body: Where key transitions go, last paragraph.

  10. The feed's held set duplicated toyos_usbhid::keyboard::Keyboard. No feed. The owner's merge needs one held set per source, so Keys (the set and its modifiers-first transitions: Usages, hold, Transitions, Pass) moves out of toyos-usbhid into lib/input (toyos-input), and toyos-usbhid's Keyboard and every source of the owner's Merge hold one. One type in the tree. Body: Where key transitions go, "The merge moves to userland whole".

  11. The exit answered was superseded. The body maps The small-kernel track says what is done, what is left, and in which order #900's current step 7.2 exit row by row:

    • No i8042 code in the kernel, toyos-ps2 out of kernel/Cargo.toml: the deletions, with the git grep the build runs and what it leaves (the claim's row).
    • The kernel test the step owes: ps2server's kernel claim is the isa row, so the test is the T14 row isa_lines_reach_their_holder with one new role. dies holds the claim, has the keyboard acknowledge so a record is pending, and ends without reading it; after must then find no record. Its control is the row's release with IRQ[row].clear() removed (isa.rs:131), which keeps the dead holder's record and reds after. The track's step text is rewritten in the same change, the issue it takes up, to name the isa claim and the owner ruling.
    • The restart mid-packet: both toyos-ps2 tests move unchanged, since the decoder's API does not change; ps2server passes a device clock as now_ns.

    The superseded "typing resumes after a kill" argument and the hand check are gone. Body: The track's step 7.2, and its exit.

  12. A kernel addition userland could own. Ruled by the owner: userland owns the merge. There is no input-feed, no new class, no kernel injection API. ps2server delivers over IPC to the owner of input: Design: one userland input owner for the ARM desktop #868 is open and unbuilt, so the owner is the compositor (images/system.toml, tests/images/metalcase.toml) and /system/bin/console (images/console.toml), each serving an input port that ps2server's row receives. When Design: one userland input owner for the ARM desktop #868's inputserver lands it takes the acceptor. The kernel's own merge goes too: its streams carry its sources unmerged until usbd takes the last of them (S1 of the xHCI move into usbd: a FAT entry that grows its file is written a cache flush after its chain and data, held by a module test that stops the disk inside a flush epoch #901), and the mouse stream reports motion as the device gave it, so that one integrator, the owner's, places the pointer. Body: Where key transitions go.

NOTEs

  1. isa_claim_refused.rs is out of the deletion table and its count.
  2. No feed and no Claimed::Feed, so Claim's contract is untouched. The authority to type into the focused window is now a connector to input, and the build refuses input to every row but ps2server's, as it refuses prompt.
  3. No feed to order. The owner keys held state by source, so a dead ps2server's late releases cannot release a key the restarted one pressed: the union only loses a usage no live source holds.
  4. isa::ack answering Ok on a row with no level line now gives the merit reason only: the holder cannot know its triggers, and the kernel unmasks whichever is level.
  5. No record crosses into the kernel. The input port's frames are decoded by the owner, in toyos-input, a crate of its own because it decodes a peer's words.
  6. Scroll and absolute motion are in the record: MouseEvent carries kind and x, y as the device gave them, for the kernel stream and the port alike, so usbd needs no other record.
  7. No input_feed row. What it held is toyos-input's host tests: two sources' modifiers merged, a source's end released.
  8. Stated as the design's inference, with K 1's own warning that some POSTs set the keyboard to set 1.
  9. Stated: K 14 is the Told arm's oracle and QEMU the environment it runs in.
  10. No decision-table test is added for A2; the fake controller's AMD shape is its host test.

@Japabu Japabu changed the title Design: the i8042 leaves the kernel for /system/bin/ps2server (stage 7.2, step 6.3) Design: the i8042 leaves the kernel for /system/bin/ps2server, which delivers to the userland owner of input (stage 7.2, step 6.3) Oct 13, 2026
@Japabu

Japabu commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator Author

Design round 3: what M1 decided

The orchestrator ran the scout b3637916a on both benches; both judges exited 0. Every scout-ps2: line, masked: T14, AMD bench. The body at the same head is the design, with M1 mapping each reading to its decision. Each arm no measured machine reaches is deleted.

  • The lines: IRQ 1 → GSI 1 and IRQ 12 → GSI 12, edge, active high, on both benches. The level arm goes: ps2server never calls isa::ack, round 2's change to isa::ack is deleted, and the framer measures from t_end, the clock after the status read that found OBF clear, in place of t_ack. A level-line machine is outside reach and gets no PS/2 input, named in the log (roast).
  • Rule 4, answers wait on the interrupt: stands. On both benches every answer byte raised the line, and each exchange's deliveries equal its answers. The delivery trails OBF by a few microseconds (AMD 0xF0 ack: OBF at 101 µs, delivery at 103 µs), which ps2server never sees, since it reads status only after the record.
  • The set query, B1's arms and A2's fate. The T14 acks 0xF0, then has 0xEE waiting before the argument is written, and 0x00 draws nothing in 100 ms: Refused. The AMD bench acks both bytes and sends no set byte in the 120 ms after: Unanswered. So both arms stay, The AMD bench's desktop: HDA controller choice, native rate, codec volume and the volume keys #877's A2 stays as accepted, and B1's "a bench does" arm holds: firmware's byte crosses a restart in /tmp/ps2server/firmware. Both benches' firmware byte is 0x77, translate on: set 2 translated. Told 0x02 is reached by QEMU alone, under the smoke test. 0x01, which no machine in reach answers, is deleted, and set 1 is disabled by name.
  • B5's hold: both behaviours, no loss. With the first answer byte left unread for 50 ms, the T14's TrackPoint sent its next byte 1,000 µs after the read, one byte's time on the wire, so it was held off. The T14's EC queued its keyboard's bytes (30 and 40 µs after each read). The AMD bench's keyboard took 178 and 2,005 µs. Every byte asked for arrived. The framer's gap first_nanos − t_end covers both: a queued byte is read in the same wake at gap 0, and a held byte's gap is at most one byte's time, under PACKET_GAP_NS's 5 ms.
  • The aux device: the T14's TrackPoint answers its reset 0xFA 0xAA 0x00, the 0xAA 367 ms after the 0xFA, inside TP's 500 ms and the 600 ms stage. The AMD bench's reset draws no byte and no delivery in 1 s, so its pointer is absent and its keyboard is already armed. ps2server_arms holds each bench to these lines.

The scout's worktree, its local branch and wt/toyos-ps2server-m1 on the remote are deleted. Nothing is built.

@Japabu

Japabu commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator Author

Design review, round 3: head 74aea28dc

Judged against origin/main at 858da4d42. git log origin/main..74aea28dc is one empty commit, and git diff --shortstat is empty: production 0, tests 0. The body estimates kernel/src about 1,750 lines smaller and userland about 1,050 larger, so production shrinks. I read the M1 readings at ToyOSOrg/evidence 3639cf42a, and the scout that produced them (b3637916a, kernel/src/arch/x86_64/i8042/scout.rs), line by line. I checked the IBM citations in the text layers of 84F9735 (C, K) and of the TrackPoint specification (TP).

Round 1's BLOCKERs

  1. OPEN. The translate bit a restart reads is still not necessarily firmware's.
    • /tmp is the kernel's tmpfs, mounted UserAccess::ReadWrite for every process (kernel/src/main.rs:478).
    • If any process deletes /tmp/ps2server/firmware, the next restart "finds none". It then reads the dead holder's bit 6 as firmware's, which is round 1's defect exactly: a keyboard disabled by name on the T14 or the AMD bench.
    • If any process plants the file, it picks the wire format, and that includes the first ps2server of a boot, since nothing tells it apart from a restart except the file's absence.
    • The design adds a new isolation hole, and records it only in the roast, with no issue, owner or exit.
    • Two ways out, or put the choice to the orchestrator:
      • (a) A restart resets the keyboard (0xFF). After a reset the keyboard sends set 2 (K 13; C 10's note on bit 6, "following power-on or a keyboard reset, the keyboard transmits using scan code set 2"), so a restart needs no memory at all. It costs 300–500 ms of BAT on a restart only, beside the aux reset's 500 ms a restart already pays. It needs one reading: that the T14's EC answers 0xFF.
      • (b) The authority that survives the process is held where only ps2server's row reaches it.
  2. CLOSED. Both logs say IRQ1 -> GSI 1 edge/high and IRQ12 -> GSI 12 edge/high. The level arm is deleted, and the framer measures from t_end.
  3. CLOSED. One wake drains up to 32 bytes, and OBF still set after them is the wedge.
  4. CLOSED for the steady state. The same loss reappears in the probe; see new BLOCKER 2.
  5. CLOSED. Stated as unbacked, and measured. Each claim matches its log line:
    • the TrackPoint's 0x00 came 1,000 µs after the held read;
    • the TrackPoint's unheld 0x00 after 0xAA also came at 999 µs;
    • the T14's keyboard took 30 and 40 µs;
    • the AMD bench's keyboard took 178 and 2,005 µs.
  6. CLOSED. Each citation is on its page:
    • C 9: "commands not listed are reserved";
    • C 12 lists 0xA9 under change bars and goes from A9 to AD, so 0xAA and 0xAB are not listed;
    • TP's Set Resolution "has no effect for the TrackPoint controller";
    • TP's reset: AA 00 within 500 ms, with 100 reports per second as the default.
  7. CLOSED. M1 ran before acceptance, and every consequence the body states was taken.
  8. CLOSED by the deletion.
  9. CLOSED.
  10. CLOSED. Keys is the one held set.
  11. CLOSED as a mapping. The control holds: removing IRQ[row].clear() (kernel/src/isa.rs:131) leaves the record that after must not find, and nothing else clears it before claim_row. Its restart row rests on a restart that new BLOCKER 1 shows does not happen.
  12. CLOSED per the owner's ruling: there is no feed, and transitions go to the owner over IPC. The kernel's remaining button OR is NOTE 3.

Round 1's NOTEs 1–10 are closed. NOTE 2's refusal has its table: build/build.rs:597-604, beside prompt and swap.

BLOCKER

  1. system/supervisor/src/main.rs:726: ps2server is never restarted.
    • The waiter close_when_it_ends (:781), the only thing that sets kept.ended and wakes restart_ended, is spawned only if !kept.acceptors.is_empty().
    • ps2server serves no port (it receives input), so its end is never seen, and restart = true on its row does nothing.
    • That leaves four parts of the design resting on a restart that never happens:
      • Restart;
      • B1's file;
      • the wedge rule's "the supervisor's restart limit then gives up on it";
      • the exit's restart-mid-packet row.
    • The design names one supervisor change, the clean-exit rule. It needs this second one too: a waiter for every restart row, whether it serves a port or not. State it, and what it reaches.
  2. PR body, "The sequence" and rules 3 and 4: the probe's aux stage runs with the keyboard scanning, and the design does not say how its bytes are kept apart.
    • "The keyboard is armed before the aux stage starts", so keystrokes flow through the aux reset: 367 ms on the T14, at boot or at a restart while someone is typing. Three outcomes follow:
      • (a) Rule 3's probe arm drains a byte found waiting before a write. Before an aux-stage write, that byte is a keystroke, and a drained break code is a stuck key: round 1's B4 data loss again, this time in the probe.
      • (b) Setting the aux interrupt bit, and arming, take a 0x60, and the fake's "dropped command-byte write" shape implies a 0x20 read-back. C 11 raises IRQ 1 for "keyboard or command controller response data", and AUXB is clear for both. So a keystroke is read as the command byte, and the real command byte is then fed to the keyboard's decoder. Rule 4's "controller commands are polled with both interrupt bits clear" cannot hold once the keyboard is armed.
      • (c) With the keyboard's interrupt bit clear during the aux stage, a keystroke holds OBF. The aux answer cannot load and IRQ 12 never rises, so the pointer is written off as absent because someone typed.
    • M1 never ran this case: the scout stopped the keyboard's clock for the whole aux stage (scout.rs:246, | CFG_PORT1_CLOCK_OFF).
    • Name the sequence. Either:
      • 0xAD around the aux stage and the arming, since the keyboard holds its bytes while its clock is inhibited (C 15, K88 40); or
      • rule 3's steady-state arm from the keyboard's 0xF4 on, with no 0x20 after it.
    • Add the fake shape "a keystroke during the aux reset".

NOTE

  1. PR body, M1 table, "A Read ID …": the AMD bench answered 0xAB 0x41 with bit 6 written 0 and read back 0.
    • The scout's set_config verifies the write, and no "did not take" line appears.
    • K 13, Figure 2, gives AB 83 for a 101/102-key keyboard, and C 5, Figure 1, translates 83 to 41. So the AMD's EC translates whatever bit 6 says.
    • No decision changes, since no set byte came. But the AMD bench never sees a query "with translation off", and a set byte from it would arrive as 0x41 (C 5: 02 → 41), which rule 1 refuses.
    • State the reading, and make the fake's AMD shape translate regardless of bit 6.
  2. PR body, rule 1, Refused: which of the two Refused paths the T14 takes is unmeasured.
    • The scout waited 20 ms after every exchange before its next write (scout.rs:182), so the 0xEE was always waiting before 0x00.
    • ps2server writes 0x00 as soon as the ack's wake comes, and the 0xEE's delay after the 0xFA was not logged.
    • So the "refusal after the argument's write" path, which writes 0x00 while the keyboard may still be sending 0xEE, has never run on the T14.
    • ps2server_arms catches a failure. Say which path the build expects.
  3. PR body, "The kernel's streams stop merging": the kernel keeps a merge after the ruling gives the merge to userland.
    • mouse.rs's per-PointerSource button OR stays until usbd, while MouseEvent is being changed anyway.
    • Either carry the source in the record and let the owner's Pointer OR the buttons, or say why the OR stays.
  4. PR body, "The merge moves to userland whole": kernel/usbhid, a kernel dependency, would then depend on lib/input.
    • lib/input also carries the owner's Merge, its Pointer and the input port's frame decode.
    • Say that toyos-input takes no toyos dependency, so the kernel can link it, or split Keys from the protocol.
  5. PR body, rule 4 and the M1 table: "each exchange's deliveries equal its answers" is false of the T14's 0xF0 exchange. That exchange logged 1 answer and 2 deliveries; the second delivery is the 0xEE's (prose).

The design is not ready to build.

SEND BACK

@Japabu

Japabu commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator Author

Rulings on round 3 (#899 (comment)).

  1. Restart memory (round-1 B1): option (a). A restart resets the keyboard (0xFF) and needs no memory, so the /tmp file and its isolation hole go. The one reading it needs, that each bench's controller answers 0xFF, is taken by a scout boot I run before acceptance.
  2. The supervisor restart (BLOCKER 1): the design names the second supervisor change, a waiter for every restart row whether it serves a port or not, and what it reaches. It has a supervisor module test.
  3. The aux stage beside a live keyboard (BLOCKER 2): 0xAD around the aux stage and the arming, since the keyboard holds its bytes while its clock is inhibited (C 15). Add the fake shape "a keystroke during the aux reset".
  4. The NOTEs are taken as written. For NOTE 3, the source travels in the record and the owner ORs the buttons, so the kernel keeps no merge.

@Japabu

Japabu commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator Author

Round 4 mapping, answering round 3's review under the rulings. Head unchanged, 74aea28dc, one empty commit; the body is the design.

Round 1's BLOCKER 1, still open in round 3 (the /tmp file): ruling 1, option (a). The file and its isolation hole are deleted. A restart, told by the supervisor's TOYOS_RESTARTED, sets the translate bit, resets the keyboard (0xFF), takes 0xFA and 0xAA (K 11, K 13) and arms set 2 translated with no query and no memory. A dead predecessor's bit 6 no longer decides anything. The first start still queries and decides from firmware's bit; a reset at every start stays the owner's question (body, Restart).

  • The reading it needs, M2, is staged and has not run: wt/toyos-ps2server-m2 at 2b3433af7, row scout_ps2_reset, two resets per boot (from firmware's state, then from a reset keyboard's), every answer timed against K 11's 300–500 ms. The body's M2 table says what each outcome decides; a bench that does not answer goes back to you for option (b).

BLOCKER 1 (the supervisor never restarts ps2server): ruling 2. Restart names it as supervisor change 1, a waiter for every start of a row that serves a port or is restart, and says what it reaches: ps2server alone, since diskserver and fileserver serve ports. Change 2 is the clean-exit rule. Change 3 is TOYOS_RESTARTED, which ruling 1 needs and ruling 2 did not name; it is in What I am unsure of.

  • The decisions are one function in toyos-manifest, with its module test there, because the supervisor's binary builds against std::os::toyos and has no host build.
  • Its control is the waiter restored to ported starts only.
  • The wedge rule and the exit's restart row now say they rest on change 1.

BLOCKER 2 (the aux stage beside a live keyboard): ruling 3. The sequence is now five numbered steps, with 0xAD after the keyboard scans and the arming's 0xAE last.

  • (a) Rule 3 drains a waiting byte only before the keyboard scans. From 0xF4's ack, or a restart's 0xAA, a byte with AUXB clear is a keystroke and goes to the decoder.
  • (b) Rule 4 now writes every controller command but 0xAE with the keyboard interface disabled, so an AUXB-clear answer is the controller's (C 12: "Data is not received while the interface is disabled"). The arming's 0x20 read-back is made under 0xAD.
  • (c) No keystroke can hold OBF during the aux stage, because the keyboard stores its data while its clock is low (K88 39).
  • Added: the fake shape "a keystroke during the aux reset"; the fake panics on a controller command written with the keyboard interface enabled; and the control "0xAD removed reds that shape".
  • One edge is stated in the roast rather than closed. K88 39 has the keyboard finish a byte past its 10th clock, and C 12 has the controller not receive it, so one keystroke per probe may be lost there.

NOTE 1 (the AMD bench's EC translates regardless of bit 6): stated in rule 1 and in the M1 table's Read ID row (AB 83 → AB 41, K 13 and C 5). The fake's AMD shape translates whatever bit 6 says.

NOTE 2 (which Refused path the T14 takes): stated as unmeasured. The build expects neither, both decide Refused, the fake holds both, ps2server's line names the path taken, and ps2server_arms reds the T14 on anything but Refused.

NOTE 3 (the kernel's button OR): ruling 4. MouseEvent carries source, mouse.rs loses BUTTONS and merged_buttons, and the owner's Pointer ORs per connection and source.

  • PointerSource's allocation stays, since it names a USB pointer and merges nothing.
  • With no merge left in the kernel, input_merge_test.rs and its test-input-merge guest test go whole. The body says why: the merge is toyos-input's, and its host tests hold what that test checked.

NOTE 4 (toyos-usbhid would depend on lib/input): toyos-input is #![no_std], #![forbid(unsafe_code)], and depends on toyos-abi alone, never toyos, so the kernel can link it. It links only Keys.

NOTE 5 (deliveries equal answers is false of the T14's 0xF0): corrected in rule 4 and in the M1 table, where the second delivery is the 0xEE's.

M2 is staged for both benches and handed to the orchestrator to run.

@Japabu Japabu changed the title Design: the i8042 leaves the kernel for /system/bin/ps2server, which delivers to the userland owner of input (stage 7.2, step 6.3) ps2server: the i8042 leaves the kernel for /system/bin/ps2server (design) Oct 13, 2026
@Japabu Japabu changed the title ps2server: the i8042 leaves the kernel for /system/bin/ps2server (design) Design: the i8042 leaves the kernel for /system/bin/ps2server, which delivers to the userland owner of input (stage 7.2, step 6.3) Oct 13, 2026
@Japabu

Japabu commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator Author

M2 run by the orchestrator, and a ruling. The scout 2b3433af7, scout_ps2_reset, judged exit 0 on both the T14 and the AMD bench. The readbacks are under /Users/jan/.claude/jobs/2280e09e/tmp/scratchpad/agents/899-r4/m2/{t14,amd}/scout-ps2-reset/.

Ruling: the keyboard is reset at every start, not only on a restart. It is one path, and it needs no restart variable, so TOYOS_RESTARTED, the third supervisor change, goes. The cost is a keyboard BAT at boot, beside the aux reset the probe already pays. Settle the M2 table from the readings, and publish them masked on pr-899.

@Japabu

Japabu commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator Author

M2's readings, and what each decided. Published masked on pr-899 at 09a8c7e: each bench's whole kernel log and judge (mask check 0 in every file). The scout-ps2-reset: lines are T14 L276-290 and AMD bench L246-259.

  • Both resets answered 0xFA then 0xAA on both benches, from firmware's state and again from a scanning set-2 keyboard. T14: 0xFA at 80 µs and 79 µs after the write, 0xAA 516,362 µs and 516,406 µs after the 0xFA's read. AMD bench: 0xFA at 581 µs and 226 µs, 0xAA 2,170 µs and 1,320 µs after. Decided: the reset works on both benches, so your ruling applies. The keyboard stage is 0x60 (keyboard interrupt bit, translate on, aux clock off), 0xAE, 0xFF, 0xFA, 0xAA on every start. The query, its three arms, 0xF5, the keyboard's 0xF4, the quiet stage's 0x20 and TOYOS_RESTARTED are deleted, and the supervisor gains two rules, not three.
  • 0xAA against K 11's 300–500 ms: the T14 is 16 ms late both times, and the AMD bench is about 298 ms early both times. Decided: 0xAA is accepted at any time inside the keyboard stage's 750 ms ceiling, and IBM's window is enforced at neither end. That leaves the T14 234 ms of margin. This is the "later than 500 ms" row the staged table left to you, so I need your acceptance. A negative control (bound narrowed to 500 ms reds the T14 shape) holds it.
  • The vector fired once per answer byte, 2 per reset, on both benches. Decided: rule 4 covers the reset, so 0xAA is waited on and not polled.
  • No byte came in the 1 s after any reset. Decided: after 0xAA the stage waits for nothing more.
  • The command byte read back 0x65 after each reset, unchanged; firmware's byte was 0x77 and IRQ 1 was GSI 1 edge/high, both as M1 read them. Decided: the reset changes no controller state, the arming writes the byte whole, and nothing reads firmware's byte any more.
  • No 0xFC and no other byte on either bench. Decided: the refusal arms stay, and only the fake holds them.

M1's firmware-byte and set-query rows are marked superseded in the body. Three more things are yours: #877's A2, which has no arm left in ps2server; whether the owner's sign-off is still owed on issues/t14-hands-over-an-uninitialised-8042.md, since round 4 left the reset at every start to that issue; and the 750 ms bound above. The scout wt/toyos-ps2server-m2 is deleted, both locally and on origin.

@Japabu

Japabu commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator Author

Rulings on round 5's open items.

  1. BAT timing: the keyboard stage accepts 0xAA within its 750 ms limit. IBM states 500 ms, and the T14 measured 516 ms, so the limit rests on that measurement and the body cites both. No lower bound.
  2. The AMD bench's desktop: HDA controller choice, native rate, codec volume and the volume keys #877's A2: with the query gone, A2 has nothing left to decide in ps2server. Whichever of The AMD bench's desktop: HDA controller choice, native rate, codec volume and the volume keys #877 and ps2server: the i8042 driver leaves the kernel, and the owner of input merges every source #899 lands second deletes A2's arm.
  3. Reset at every start: decided here, under the owner's delegation of technical calls. The open question in issues/t14-hands-over-an-uninitialised-8042.md is answered by this design and closes when it lands.

@Japabu

Japabu commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator Author

Design review, round 5: head 74aea28dc

git log origin/main..74aea28dc is one empty commit, and git diff --shortstat origin/main...74aea28dc is empty, so production and tests are both 0. The body's own estimate is kernel/src about 1,900 lines smaller and userland about 1,200 larger, before the moves, so production shrinks. origin/main is now 1b080a3f1, and nothing between 858da4d42 and it touches the files the design reads (git diff --stat over the i8042 driver, isa.rs, keyboard.rs, mouse.rs, device.rs, kernel/ps2, kernel/usbhid, the supervisor, lib/manifest, the images and build/build.rs is empty).

What I read for this round:

  • the M2 readings at ToyOSOrg/evidence 09a8c7e44: both kernel logs whole and both judges;
  • the scout that produced them, 2b3433af7's scout.rs, line by line;
  • C 11–12 in the text layer of 84F9735.

Earlier BLOCKERs

  • Round 1's BLOCKER 1 (the translate bit across a restart): CLOSED. Under the reset ruling every start resets the keyboard. The /tmp file, its isolation hole and TOYOS_RESTARTED are all gone, and nothing reads firmware's byte or a predecessor's byte.
  • Round 3's BLOCKER 1 (ps2server is never restarted): CLOSED. Supervisor change 1 is a waiter for every row that serves a port or is restart. The restart decisions are one function in toyos-manifest with a module test, and its control is the waiter restored to ported starts only. I checked system/supervisor/src/main.rs:726 and :781: the waiter is spawned only when !kept.acceptors.is_empty(), and process_wait returns the i32 that change 2 needs.
  • Round 3's BLOCKER 2 (the aux stage beside a live keyboard): CLOSED as ruled. 0xAD now comes after the keyboard scans and the arming's 0xAE comes last, and the fake has the shape "a keystroke during the aux reset" with its control. One hole in how that is held is new BLOCKER 1 below.

The rulings

  • Round 3, rulings 1–4: met.
    • The keyboard is reset with no memory.
    • The supervisor waits on every restart row, with a module test.
    • 0xAD is around the aux stage and the arming.
    • NOTE 3 is applied: source travels in MouseEvent, BUTTONS, merged_buttons and the positions leave mouse.rs, and the owner's Pointer ORs the buttons.
    • NOTE 4 is applied: toyos-input is no_std and depends on toyos-abi alone.
  • The reset ruling (6138142028): met. There is one path and TOYOS_RESTARTED is gone, so the supervisor gains two rules, not three.
  • Round 5, ruling 1 (0xAA accepted inside 750 ms, no lower bound): met. The body cites both K 11's 500 ms and M2's 516 ms. The negative control (the bound narrowed to 500 ms reds the T14 shape) can fail.
  • Round 5, ruling 2 (A2): met in substance. "The AMD bench's desktop: HDA controller choice, native rate, codec volume and the volume keys #877" says the second to land deletes A2's arm.
  • Round 5, ruling 3: not applied. See NOTE 2.

M2's decisions against the readings

Each row matches its log line.

  • Firmware's byte and IRQ 1: 0x77 and GSI 1 edge/high on both benches.
  • The T14: 0xFA came at 80 µs and 79 µs. 0xAA came 516,362 µs and 516,406 µs after the read before; the two are 44 µs apart.
  • The AMD bench: 0xFA came at 581 µs and 226 µs, and 0xAA 2,170 µs and 1,320 µs after.
  • The vector: it was delivered once per answer byte, and the second delivery was already counted when the 0xAA was read. So "the 0xAA is waited on" holds.
  • The quiet second: no byte came in the 1 s after either reset.
  • The command byte: it read back 0x65 after every reset.
  • The refusal arms: no 0xFC and no other byte came.

The scout made its resets with the state the keyboard stage writes:

  • the keyboard's interrupt bit alone;
  • the aux clock off;
  • translate on;
  • 0xAE.

So the readings decide what the body says they decide.

BLOCKER

  1. PR body, "The sequence", step 4, and "Checks": the aux stage's 0x60 does not say it keeps the keyboard interface disabled, and no planned test fails if it does not.
    • What IBM says: C 12 states that 0xAD "sets bit 4 of the Controller Command byte to 1" and 0xAE "clears bit 4 … to 0". C 11 names bit 4 "Disable Keyboard". So a 0x60 written whole with bit 4 clear re-enables the keyboard in the middle of the aux reset, which is round 3's BLOCKER 2 again.
    • Where the design is silent: step 4 gives the byte as "the aux interrupt bit alone and the aux clock on" and says nothing about bit 4. Only step 5's "keyboard clock still off" implies it.
    • Why no test catches it: the planned control removes the 0xAD itself. A fake that tracks the interface by the 0xAD and 0xAE commands alone still passes the mutation that matters.
    • The mutation: step 4's command byte written with bit 4 clear.
    • The test it must turn red: probe.rs's "a keystroke during the aux reset" shape.
    • Required:
      • Step 4's and step 5's command bytes each state bit 4 = 1.
      • The fake holds the keyboard interface as command-byte bit 4: set by 0xAD, cleared by 0xAE, and written by 0x60 (C 11, C 12).
      • That mutation is added to the negative controls.

NOTE

  1. PR body, rule 4 and "The fake panics on each IBM rule broken": the rule contradicts the sequence. The rule says "every controller command but 0xAE itself is written while the keyboard interface is disabled". But step 3's 0xAD is written with the interface enabled, and so is the quiet stage's 0xAD after a predecessor that died in steady state. A fake built to that rule panics on the design's own sequence. Fix it one of two ways:

    • exempt 0xAD, which draws no answer;
    • or state the rule as what it protects: the controller answer the arming's 0x20 draws is read with the interface disabled.
  2. PR body, "What I am unsure of", third bullet: round 5's ruling 3 is not applied. It reads "the open question in issues/t14-hands-over-an-uninitialised-8042.md is answered by this design and closes when it lands". The body still says "This change does not edit the issue".

    • The build closes that file by issues/README.md's procedure.
    • Its subject, the firmware-byte fallback, is deleted by the reset, so the close's exit is met.
    • The body's three "unsure" items 1–3 are now ruled.
  3. PR body, "The probe", the command bytes: bit 2 (System Flag) is never given a value. Firmware's byte is no longer read, so the value ps2server writes is a new decision.

    • C 11 says "the value written to this bit is placed in the system flag bit of the Controller Status register", and that it is 0 after a power-on reset.
    • M2 measured with bit 2 = 1 (0x65), which is firmware's 0x77 on both benches. issues/t14-hands-over-an-uninitialised-8042.md records a T14 hand-over with it 0.
    • State the value and its source, and hold it with the 0x20 read-back.
  4. PR body, the exit table's first row: the list of what git grep -n -i -e i8042 -e keyboard_controller -e toyos_ps2 -- kernel leaves is short. It names pio::fill_i8042_row and isa's docs. The grep also finds:

    • kernel/cpuvuln/fixtures/tcg/console.txt:306-311, a captured guest console with four i8042 lines;
    • kernel/src/arch/x86_64/acpi_mode.rs:223;
    • kernel/src/arch/x86_64/mod.rs:8-10, whose keyboard_controller example goes stale.

    Name each, or delete the stale doc lines in the build.

  5. PR body, the M2 table: "516,362 µs after the 0xFA's read" is timed from after the 0xFA's log line, not its read. In scout.rs, last = now() follows log!. The reset's summary line (516 ms from write to the last read) bounds the difference under 1 ms, so no decision moves.

LAND AFTER NAMED CHANGES is not reachable while BLOCKER 1 is open. Once it is closed, the design is ready to build.

SEND BACK

@Japabu

Japabu commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator Author

Rulings on round 5's review (#899 (comment)). BLOCKER 1 and NOTEs 1, 2, 4 and 5 are taken as written. NOTE 3: the System Flag (bit 2) is written as 1, the value M2 ran with (0x65), citing C 11, and the 0x20 read-back holds it. With these, the design is accepted and goes to build. No further design review: the build review checks them.

The kernel keeps the `isa` claim on the i8042's ports and lines and speaks
none of its protocol. /system/bin/ps2server probes the controller after
IBM's PS/2 Hardware Interface Technical Reference (84F9735) and the
TrackPoint System Version 4.0 Engineering Specification: the keyboard is
reset at every start and armed set 2 translated, every command byte holds
the System Flag and the keyboard interface disabled until the arming's
0xAE, a byte is read 7 us after the status read that showed it and written
only with both buffers empty, and a device's answers wait on its interrupt.
The stream feeds the pointer's framer the device's own idle time, so a
slow reader never splits a packet.

ps2server hands each key and pointer report to the owner of input over the
new `input` port, which only it may receive. The owner (the compositor, or
the console on its image) merges every source with toyos-input: a key or a
button is held while any source holds it, and a source's end releases
what it held and nothing another holds. The kernel's merge goes: its
keyboard and mouse streams carry its USB sources' records unmerged, and
MouseEvent carries what a device said - its source, its own buttons,
scroll, and relative or absolute motion.

The supervisor waits on every start of a row that serves a port or is
`restart`, and starts a `restart` row again only when it ended other than
by exit 0 (toyos-manifest's Program::supervised, with its module test).

Deleted: the kernel's i8042 driver and tally, its vector, the
keyboard_controller seam, irq_ring's and the census's i8042 source, the
i8042-withheld and test-input-merge actuators and the input-merge guest
row, the loom tally model, iapc_boot_arch, isa_claim_refused (its premise,
a kernel driving the controller, is gone), and
issues/t14-hands-over-an-uninitialised-8042.md, whose fallback from
firmware's command byte the reset at every start deletes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
Japabu added a commit that referenced this pull request Oct 13, 2026
…release, not a keyboard claim

Step 10's exit named "the keyboard's by stage 7 step 2's kernel test on
usbd's claim", and stage 7 step 2 refused a keyboard claim while no
source existed and owed a kernel test of a keyboard claim handed to the
next claimant. Under the input ruling (#899, applied to usbd on #901)
neither driver holds a keyboard claim, so all three presupposed a kernel
feed that is gone.

Step 10's exit now holds a key usbd held at its death to the owner of
input's `toyos-input` release test, as #901's accepted design maps it.
Stage 7 step 2 names ps2server's one kernel claim, the `isa` row, and its
kernel test is `isa_lines_reach_their_holder`'s dead holder leaving no
record to the next, as #899's accepted design maps it. Taken here by the
ruling on #904 that widened its fence.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
Japabu added a commit that referenced this pull request Oct 13, 2026
… owner of input, not the screen's

Review round 1 on #904. Step 10's exit now fails while `keyboard.rs`,
`mouse.rs`, `input_merge_test.rs` or a `Keyboard`/`Mouse` class in
`sdk/abi` remains. Step 10 and stage 7 step 2 deliver to the owner of
input, as the ruling names it, not the screen's owner. The `toyos-input`
test's controls are #901's two, exactly. Stage 7 step 2 states the
kernel merge's deletion in #899's words.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
@Japabu
Japabu force-pushed the wt/toyos-ps2server branch from 74aea28 to 27c052c Compare October 13, 2026 05:47
@Japabu Japabu changed the title Design: the i8042 leaves the kernel for /system/bin/ps2server, which delivers to the userland owner of input (stage 7.2, step 6.3) ps2server: the i8042 driver leaves the kernel, and the owner of input merges every source Oct 13, 2026
@Japabu

Japabu commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator Author

Ruling on the 0xAD finding: the aux stage's own 0x60 sets bit 4, which disables the keyboard interface just as 0xAD does. So 0xAD is redundant, and it goes: one mechanism, not two. This replaces round 3's ruling 3 on that point. The dropped-command-byte test is the test that holds bit 4.

Japabu and others added 3 commits October 13, 2026 08:18
#894 adds the clipboard port beside the input port: both are held alone,
both are served by the compositor, and its poll budget counts both.
#904 rewrote step 7.2 of the track; its text is kept, marked done.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
…and metalcase holds until the probe ends

The first metal run found two reds, neither of them the probe's:

- `ps2server_arms`, on both benches: `metalcase` runs no job, so its
  test-runner rebooted 3 ms after the supervisor started ps2server (T14,
  inside the keyboard's 516 ms reset) and 1 ms after the keyboard's line
  (AMD, inside the aux stage's 600 ms). The kernel's probe ran during boot;
  ps2server's runs after it. `test_rs_ps2server_hold`, on `acpi_hold`'s
  pattern, ends every `metalcase` boot once ps2server has said it armed or
  the kernel has recorded its exit; the image serves test-runner `log`.
- `isa_lines_reach_their_holder`: a claim's first interrupt is announced
  once per claim (the release re-arms it), and `dies` read away the stale
  record its own `0x20` raised under firmware's `0x47`, so the kernel
  announced it twice. `dies` now reads no record at all, which is also
  what its name says, and the count of one holds on any firmware byte.

The orchestrator's ruling on m06 removes the `0xAD` after the keyboard
stage: the aux stage's `0x60` sets bit 4, which disables the keyboard
interface as `0xAD` does (C 11, C 12). The dropped-command-byte shape now
drops the arming's byte alone, so the aux stage's bit 4 is what the `0x20`
runs under, and is what it reads back (`0x56`).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
#894's `clipboardcase` landed with a compositor row from before this
branch, and the compositor takes the `input` acceptor as it takes
`prompt`'s, so it panicked at its start on that boot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
@Japabu

Japabu commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator Author

Metal at a42489c7c, run by the orchestrator from agents/899-fix1/ (the images are hash-checked by the runner, and each stick's log was saved before its flash):

run judge exit
T14 metal ps2server_arms isa_ports_are_the_binders_alone isa_lines_reach_their_holder mask_windows 0, 4 passed
AMD metalcase ps2server_arms 0, 1 passed
T14 metal-base (c01) mask_windows 0
T14 control (c02) isa_lines_reach_their_holder 1, red as wanted
  • The T14 arms with the aux device: aux reset, 0xaa 367475 us after its 0xfa, armed, command byte 0x57 — keyboard on, aux on.
  • The AMD bench arms without it: aux absent — its reset went unanswered in 600 ms, armed, command byte 0x75 — keyboard on, aux off.

Readbacks and judge logs are beside each image under agents/899-fix1/.

@Japabu

Japabu commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator Author

Build review, round 1: head a42489c7c

Judged against origin/main at the merge base 1a81b5926. I read the design rulings (6137234392, 6137901485, 6138142028, 6138193460, 6138254697, 6139222704), the orchestrator's metal comment (6140050203), ToyOSOrg/evidence at 6746b0a, and the local readbacks under agents/899-fix1/.

Lines. git diff --shortstat origin/main...a42489c7c: 90 files, +3,325 −2,552, net +773. Split by path, production is +2,449 −1,983 and tests plus issues are +876 −569. The in-file #[cfg(test)] modules (probe.rs 186, stream.rs 125, lib/manifest 41, build/build.rs 17) belong on the test side, so production is about +2,080 −1,983, net about +100, and tests are net about +680. kernel/src goes from 64,129 to 62,398 lines, which matches the body. I accept the production growth: the driver leaves the kernel, and what replaces it is a state machine tested against a fake.

Earlier BLOCKER

  • Design round 5, BLOCKER 1 (bit 4 in the aux stage's 0x60): CLOSED.
    • probe.rs:53-54 and :283 set CB_KEYBOARD_DISABLED in every command byte.
    • The fake holds bit 4 through 0x60 (fake.rs:419) and panics on a 0x20 written while it is clear (fake.rs:395).
    • The m07 log is exit 101. It reds a_command_byte_the_controller_dropped_is_said_by_name with "C 12: 0x20 written while the keyboard interface is enabled", and it reds a_keystroke_during_the_aux_reset_reaches_the_keyboard_after_the_arming.

Checked, with no finding

  • The 15 points, beyond the ones below: I read each against the rulings.
  • The 0xAD ruling: applied. No 0xAD follows the keyboard stage.
  • The console as a service: its HOME becomes /state/console. std's direct spawn takes the shell's HOME from the supervisor's answer, not from the console's own, so the shell keeps the session home (sdk/std/sys/process.rs:447-469).
  • A wedged controller: stream.rs:162-165 returns Wedged and main.rs:60 exits 1, with no write. The 32-read bound is held by an_output_buffer_full_after_32_reads_ends_the_stream_by_name.
  • Mutations and controls: m01–m05, m07–m15 and c01 are each exit 101 or 1 at a42489c7c. Each log reds the test the body names.
  • c02: the local readback at a42489c7c panics at isa_lines.rs:263, in after, with "a record of 2".
  • Clean-room: the new code cites IBM's C, K, K88 and TP and the M1/M2 readings, and no implementation. QEMU is used only as a black-box oracle. The moved toyos-ps2 swaps its two Linux justifications for C 10, and its tables are unchanged.
  • IAPC_BOOT_ARCH: deleting it is right. The T14's FADT says bit 1 is clear (metal-27c052cea/t14/base-windowscase-kernel.log, "FADT rev 6 iapc_boot_arch=0x0011"), yet the machine has an i8042.

BLOCKER

  1. PR body, "Gates" and "Metal": the metal readings at a42489c7c are neither published nor cited. The body cites only metal-27c052cea/ and the staged requests ("staged, 2"). The green arms, the c01 base arm and the c02 red exist only in the local scratchpad. Publish these masked (their boot.txt and kernel logs still carry the machine fields unmasked) and cite them from the body:

    • metal/judge-head.log, and metal/{metalcase,isa,windowscase}/{kernel.log,verdict.txt};
    • metal-amd/judge-head.log and metal-amd/metalcase/{kernel.log,verdict.txt};
    • metal-base/judge-base.log and metal-base/windowscase/kernel.log;
    • metal-control-isa-release/judge-ctl.log and metal-control-isa-release/isa/kernel.log.

    The change also alters the boot four more rows read: metalcase now carries ps2server and a hold job. Those rows are metal_sim_scanout_wc, usb_reset_hands_devices_back, blackbox_done_chain and machine_reboot (tests/toyos.rs:816-858), and none was judged at this head. --metal-readback over the existing metalcase readbacks judges three of them without touching a machine. usb_reset_hands_devices_back also needs the device boot. The track's 7.2 close rests on c02's red, which today is only in the scratchpad.

  2. system/ps2server/src/stream.rs:232-235, :246-249 and :266-268, with lib/input/src/wire.rs:24, system/compositor/src/session.rs:826 and system/console/src/main.rs:379: a loss on one device releases the other device's state. LOST ends the whole source: the owner runs Merge::end and Pointer::end.

    • Pointer to keyboard: a pointer byte the framer discards, or the write-off, releases every key ps2server's keyboard holds. With Ctrl or Shift held during a resync, the owner drops the modifier while it is still down, and the keyboard does not resend it.
    • Keyboard to pointer: a keyboard overrun (0x00) releases a held TrackPoint button mid-drag.
    • Why it is wrong: every PS/2 packet restates its buttons whole, so a discarded byte owes the owner nothing. The write-off owes a zero-buttons Pointer record for source 0, not a key release.
    • The fix: scope the loss per device. LOST releases the keys alone and is sent only for the keyboard's overrun. The pointer's discard sends nothing, and its write-off sends a zero-buttons report.
    • The test: three_failed_reenables_write_the_pointer_off_with_0xa7 asserts the current [Record::Lost], so it must change. Add a stream.rs test that a discarded aux byte leaves a held key held. This changes the accepted design's LOST, so put it to the orchestrator before building.
  3. kernel/src/mouse.rs:36 and :94-98: deleting test-input-merge cut the only test of PointerSource allocation, and that allocation stays in the shipping kernel. The test covered two pointers claiming distinct sources, unbind queuing the release, and a replug taking the freed source back.

    • The body's own "unsure" bullet says nothing tests it now. No cheaper tier holds it, and no stage names the cut, so the cut breaks Guest tests.
    • Mutation: delete IN_USE[source.0 as usize / 64].fetch_and(!(1u64 << (source.0 % 64)), Ordering::Relaxed); from unbind. Nothing goes red, and every replug leaks one of 256 sources.
    • Required: a test that the mutation turns red, named in the body. A host test works if the allocation moves into a pure crate; otherwise keep the actuator row, reduced to allocation.
  4. kernel/src/isa.rs:158-162: deleting isa_claim_refused left the KernelDriven refusal with no test, and the refusal is still reachable. Any port this kernel declares inside the row refuses the claim. For example, a FADT RESET_REG at port 0x64 is declared by arch/x86_64/power.rs:94.

    • The body says isa_ports_are_the_binders_alone and isa_lines_reach_their_holder hold the claim's behaviour, but both hold only the grant.
    • Mutation: delete the if let Some((run, holder)) = … pio::holder … block from claim_row. Nothing goes red.
    • Required: a test that the mutation turns red, at the cheapest tier that reaches it, named in the body, or the arm deleted with the reason it cannot be reached.
  5. system/ps2server/src/probe.rs:63 and lib/input/src/lib.rs:23: two things ship for tests alone.

    • Probed::Armed { keyboard, .. } is read only by probe.rs's tests, and main.rs:52 discards it. The tests already check fake.cb and the armed line.
    • pub use merge::rel_scale_for is exported only for lib/input/tests/input.rs. Pointer calls it inside the crate.
    • Drop both, and move the scale test into merge.rs.

NOTE

  1. tests/toyos.rs: the branch does not merge cleanly. git merge-tree origin/main a42489c7c conflicts with The kernel loads Intel's CPU microcode early on every CPU, and an AMD CPU says ToyOS has no loader for it #886 at bc8429e71. The gates are owed again at the merged head.
  2. The design's control "the whole change reverted: ps2server_arms red" was not run on metal. c01 ran only console_image_boots. Run it with the next metal set, or say in the body why the judge's must_say makes it moot.
  3. kernel/src/mouse.rs:80: point 5's five-button mask has no test. Dropping & BUTTONS passes everything. Hold the mask in toyos-usbhid's decode, where host tests reach it.
  4. system/ps2server/src/hw.rs:68-89: rule 5, "looks once more after its deadline", is held by no test. The fake has its own wait, so moving past below record() passes everything.
  5. system/ps2server/src/probe.rs:176-192: on a wedged controller the quiet stage behaves differently from what it says.
    • The 0xAD write reads for 250 ms, about 35,000 reads, and ends "never took 0xAD". So the 32-read bound and its line are never reached there.
    • Every byte it read goes into drained and is never said.
  6. system/console/src/main.rs:345-382 and system/compositor/src/session.rs:788-851 are one accept, pump, decode and end loop, written twice. One helper would delete one copy.
  7. kernel/usbhid/src/keyboard.rs:21 and :27 still name Linux (usbkbd) in a file this diff edits. The lines are older than this branch, and the body defers them to S1 of the xHCI move into usbd: a FAT entry that grows its file is written a cache flush after its chain and data, held by a module test that stops the disk inside a flush epoch #901. Make sure S1 of the xHCI move into usbd: a FAT entry that grows its file is written a cache flush after its chain and data, held by a module test that stops the disk inside a flush epoch #901 does own them.
  8. Prose: at this head the body still reads "staged, 2" for the metal rows and "the orchestrator's to run" for c02. The orchestrator's comment shows both were run.

SEND BACK

@Japabu

Japabu commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator Author

Orchestrator ruling on build review round 1, BLOCKER 2 (this changes the accepted design's LOST).

Accepted as the review states it. Loss is scoped to the device that lost something:

  • LOST releases keys alone. ps2server sends it only on the keyboard's overrun (0x00).
  • A pointer byte the framer discards sends nothing, because the next packet restates the buttons whole.
  • The pointer's write-off sends a zero-buttons Pointer record for its source and no key release.

The owners of input (compositor and console) end only key state on LOST. Pointer state ends on the zero-buttons record or on gone.

Japabu and others added 6 commits October 13, 2026 09:14
tests/toyos.rs: #886's `microcode` row and this branch's `ps2server_arms`
were each added after `metal_sim_scanout_wc`; both are kept, each on
METALCASE.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
LOST was sent for the keyboard's overrun, for every aux byte the framer
discarded and at the pointer's write-off, and the compositor ended both
the source's keys and its pointer's buttons on it. So a resync of the
TrackPoint released a held Ctrl, and a keyboard overrun released a held
button mid-drag.

Every PS/2 packet states its buttons whole, so a discarded aux byte owes
the owner nothing: it is counted and sends nothing. The write-off owes a
report with no buttons for source 0, and no key release. LOST is the
keyboard's overrun alone, and the owner ends key state on it; pointer
state ends on a zero-buttons report or on the connection's end.

The orchestrator's ruling on build review round 1, BLOCKER 2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
`Probed::Armed`'s `keyboard` was read only by the probe's tests, which
now hold the armed line and the command byte instead; `rel_scale_for`
was exported only for `toyos-input`'s integration test. It is private,
its aspect test moves into `merge.rs`, and the motion test states its
expected scale as the numbers 1920x1080 gives.

Build review round 1, BLOCKER 5.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
…the deadline's look is tested

The quiet stage wrote 0xAD first, and a wedged output buffer kept that
write reading for its whole 250 ms, about 35,000 reads, ending "never
took 0xAD" with every byte it read unsaid. It now drains before its
first write as after, so a buffer still full after 32 reads is said
with what they read, and no write is made into it (C 14). A write the
controller never takes says what it drained first.

Hw's wait reads the clock before it looks for a record, so a record
landing as the deadline passes is still found. That loop is now
`Records::first`, and a host test preempts it between every two steps:
read the clock after the look and it reds.

Build review round 1, NOTEs 4 and 5.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
…d where host tests reach them

Deleting `test-input-merge` cut the only test of `PointerSource`'s
allocation, which stays in the shipping kernel: a `unbind` that gave no
number back would leak one of 256 on every replug, and nothing went red.
The allocation is `kernel::pointers::Sources` in the pure library now,
host-tested: two claims take distinct numbers, a number given back is
the next claim's, all 256 held refuse a claim, and a number given back
twice panics. `mouse.rs` keeps the type and calls it.

The mask to boot-mouse order's five buttons moves from `mouse::report`
into `toyos-usbhid`'s decode, reading `toyos_input::wire::BUTTONS`, the
one declaration the owner's decode refuses past; its test is there.

Build review round 1, BLOCKER 3 and NOTE 3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
… a test again

Deleting `isa_claim_refused` left `claim_row`'s `KernelDriven` arm with
no test, and the arm is reachable: a FADT reset register at 0x64 is
declared to the kernel (`power::init_reset`), and then the i8042's row
must not be handed out. The `isa-port-declared` actuator declares 0x64
to the kernel at boot, and the `isa_claim_refused_on_a_kernel_port` T14
row runs `test_rs_isa_claim_refused` under it: the claim answers
PermissionDenied, and the kernel names the port and its holder.

Build review round 1, BLOCKER 4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant