# i8042 PS/2 Controller Implementation ## Summary Completed the i8042 PS/2 keyboard controller emulation to handle the full Linux kernel probe sequence. Previously, the controller only handled self-test (0xAA) and interface test (0xAB), but was missing the command byte (CTR) read/write support, causing the kernel to fail with "Can't read CTR while initializing i8042" and adding ~500ms+ of timeout penalty during boot. ## Problem The Linux kernel's i8042 driver probe sequence requires: 1. **Self-test** (0xAA → 0x55) ✅ was working 2. **Read CTR** (0x20 → command byte on port 0x60) ❌ was missing 3. **Write CTR** (0x60, then data byte to port 0x60) ❌ was missing 4. **Interface test** (0xAB → 0x00) ✅ was working 5. **Enable/disable keyboard** (0xAD/0xAE) ❌ was missing Additionally, the code had compilation errors — `I8042State` in `vcpu.rs` referenced `self.cmd_byte` and `self.expecting_data` fields that didn't exist in the struct definition. The data port (0x60) write handler also didn't forward writes to the i8042 state machine. ## Changes Made ### `vmm/src/kvm/vcpu.rs` — Active I8042State (used in vCPU run loop) Added missing fields to `I8042State`: - `cmd_byte: u8` — Controller Configuration Register, default `0x47` (keyboard IRQ enabled, system flag, keyboard enabled, translation) - `expecting_data: bool` — tracks when next port 0x60 write is a command data byte - `pending_cmd: u8` — which command is waiting for data Added `write_data()` method for port 0x60 writes: - Handles 0x60 (write command byte) data phase - Handles 0xD4 (write to aux device) data phase Enhanced `write_command()`: - 0x20: Read command byte → queues `cmd_byte` to output buffer - 0x60: Write command byte → sets `expecting_data`, `pending_cmd` - 0xA7/0xA8: Disable/enable aux port (updates CTR bit 5) - 0xA9: Aux interface test → queues 0x00 - 0xAA: Self-test → queues 0x55, resets CTR to default - 0xAD/0xAE: Disable/enable keyboard (updates CTR bit 4) - 0xD4: Write to aux → sets `expecting_data`, `pending_cmd` Fixed port 0x60 IoOut handler to call `i8042.write_data(data[0])` instead of ignoring all data port writes. ### `vmm/src/devices/i8042.rs` — Library I8042 (updated for parity) Rewrote to match the same logic as the vcpu.rs inline version, with full test coverage including the complete Linux probe sequence test. ## Boot Timing Results (5 iterations) Kernel: vmlinux (4.14.174), Memory: 128M, Command line includes `i8042.noaux` | Run | i8042 Init (kernel time) | KBD Port Ready | Reboot Trigger | |-----|--------------------------|----------------|----------------| | 1 | 0.288149s | 0.288716s | 1.118453s | | 2 | 0.287622s | 0.288232s | 1.116971s | | 3 | 0.292594s | 0.293164s | 1.123013s | | 4 | 0.288518s | 0.289095s | 1.118687s | | 5 | 0.288203s | 0.288780s | 1.119400s | **Average i8042 init time: 0.289s** (kernel timestamp) **i8042 init duration: <1ms** (from "Keylock active" to "KBD port" message) ### Before Fix The kernel would output: ``` i8042: Can't read CTR while initializing i8042 ``` and the i8042 probe would either timeout (~500ms-1000ms penalty) or fail entirely, depending on kernel configuration. The `i8042.noaux` kernel parameter mitigates some of the timeout but the CTR read failure still caused delays. ### After Fix The kernel successfully probes the i8042: ``` [ 0.288149] i8042: Warning: Keylock active [ 0.288716] serio: i8042 KBD port at 0x60,0x64 irq 1 ``` The "Warning: Keylock active" message is normal — it's because our default CTR value (0x47) has bit 2 (system flag) set, which the kernel interprets as the keylock being active. This is harmless. ## Status Register (OBF) Behavior The status register (port 0x64 read) correctly reflects the Output Buffer Full (OBF) bit: - **OBF set (bit 0 = 1)**: When the output queue has data pending for the guest to read from port 0x60 (after self-test, read CTR, interface test, etc.) - **OBF clear (bit 0 = 0)**: When the output queue is empty (after the guest reads all pending data from port 0x60) This is critical because the Linux kernel polls the status register to know when response data is available. Without correct OBF tracking, the kernel's `i8042_wait_read()` times out. ## Architecture Note There are two i8042 implementations in the codebase: 1. **`vmm/src/kvm/vcpu.rs`** — Inline `I8042State` struct used in the actual vCPU run loop. This is the active implementation. 2. **`vmm/src/devices/i8042.rs`** — Library `I8042` struct with full test suite. This is exported but currently unused in the hot path. Both are kept in sync. A future refactor could consolidate them by having the vCPU run loop use the `devices::I8042` implementation directly.