Files
volt-vmm/docs/i8042-implementation.md
Karl Clinger 40ed108dd5 Volt VMM (Neutron Stardust): source-available under AGPSL v5.0
KVM-based microVMM for the Volt platform:
- Sub-second VM boot times
- Minimal memory footprint
- Landlock LSM + seccomp security
- Virtio device support
- Custom kernel management

Copyright (c) Armored Gates LLC. All rights reserved.
Licensed under AGPSL v5.0
2026-03-21 01:04:35 -05:00

4.8 KiB

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.