Skip to content

feat: xHCI NKRO keyboard fix, CC=12 resolution, verbose logging cleanup#240

Merged
ryanbreen merged 3 commits intomainfrom
feat/xhci-next
Mar 1, 2026
Merged

feat: xHCI NKRO keyboard fix, CC=12 resolution, verbose logging cleanup#240
ryanbreen merged 3 commits intomainfrom
feat/xhci-next

Conversation

@ryanbreen
Copy link
Owner

Summary

  • Fix NKRO keyboard parsing: Parallels virtual keyboard sends NKRO reports without a reserved byte ([report_id, modifier, key1, ...]), but code expected boot keyboard format ([modifier, reserved=0, key1, ...]). Every keycode was landing in the ignored reserved byte position. Fix: insert a zero reserved byte when reformatting NKRO reports for both MSI and poll paths.
  • Increase xHCI poll rate: From 10 Hz (100ms latency) to 50 Hz (20ms latency) for responsive keyboard input.
  • CC=12 resolution: Skip port reset when Port Enabled/Disabled (PED=1), UEFI DisconnectController workarounds, rate-limiting to prevent reset storms.
  • Verbose logging cleanup: Remove ~900 lines of CC=12 investigation serial output from xhci.rs, strip diagnostic logging from gpu_pci.rs and mod.rs, re-enable VirtIO GPU PCI init.
  • Tracing infrastructure: ARM64 tracing init, fork+exit instrumentation, process trace providers.
  • Userspace: Add btrace diagnostic tool, clean up init periodic btrace spawning.
  • Linux xHCI probe module: Reference kernel module and trace comparison tooling for xHCI debugging.

Test plan

  • ARM64 kernel builds with zero warnings
  • Verified all 26 letter keys produce correct USB HID usage codes via raw_serial_char diagnostics
  • Confirmed NKRO path (poll) handles keyboard events with reformatted boot keyboard reports
  • Boot test passes on Parallels Desktop VM

🤖 Generated with Claude Code

ryanbreen and others added 3 commits February 28, 2026 06:52
…workarounds

Snapshot of CC=12 debugging work. Key changes:

Loader (parallels-loader):
- Add UEFI DisconnectController for xHCI device at PCI 00:03.0
  to prevent UEFI cleanup from resetting the controller during EBS
- Add post-EBS PCI BAR re-enable safety fallback
- Minimal PciIoProtocol binding via #[unsafe_protocol] for GetLocation
- HardwareConfig gains xhci_hcrst_done sentinel + xhci_bar_phys fields

Kernel:
- xHCI driver: pre-HCRST delay for Parallels hypervisor settling
- xHCI driver: ARM64 delay_ms() using CNTPCT_EL0/CNTFRQ_EL0
- xHCI driver: extensive DMA/context/doorbell diagnostics
- PCI: MSI capability parsing, enhanced BAR handling
- platform_config: atomic xhci_hcrst_done_raw() accessor
- procfs: /proc/xhci status endpoint
- btrace: periodic boot tracing utility

Boot infrastructure:
- boot.S: HHDM-aware page table setup
- Heap: enlarged to 16MB for xHCI DMA buffers

Findings: DisconnectController prevents UEFI's cleanup HCRST, but
phymemrange_disable still fires during EBS. Delay approach (3s, 10s)
does not produce ep create events in host log. linux-probe comparison
shows its post-EBS HCRST at +29.5s DOES get ep creates — difference
is NOT timing. Root cause remains under investigation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Root cause of CC=12 (Endpoint Not Enabled): explicit port reset on
SuperSpeed ports that were already enabled (PED=1) after HCRST confused
the Parallels hypervisor's USB device model. The hypervisor never
processed SetConfiguration or created endpoint state, so all interrupt
transfers immediately failed with CC=12.

Key fixes:
- Skip port reset when PED=1, matching Linux kernel module behavior.
  After HCRST, SuperSpeed ports auto-enable; AddressDevice internally
  triggers the device reset the hypervisor needs.
- Fix Status Stage TRB direction per xHCI spec 4.11.2.2: IN for
  no-data/OUT-data transfers, OUT for IN-data transfers.
- Rate-limit poll_hid_events to 10 Hz (every 20th timer tick). Full
  200 Hz MMIO reads from timer interrupt context starved the Parallels
  hypervisor, locking up the system at fork+exit.

Also includes:
- Linux kernel module (linux_xhci_module/) used as reference for
  byte-for-byte comparison of xHCI TRB encoding and enumeration flow.
- DisconnectController disabled in loader (matches linux-probe).
- DMA buffers moved from .dma (NC) to .bss (WB-cacheable).
- Event ring expanded to 256 entries (matching Linux).
- PCI config space dump utility for debugging.
- DSB barriers tightened: dsb st for clean, dsb ld for invalidate.

Result: first working USB HID input on Parallels — keyboard keystrokes
and mouse events delivered through the full xHCI pipeline.

Co-Authored-By: Ryan Breen <rbreen@gmail.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Parallels virtual keyboard sends NKRO reports with layout
[report_id=1, modifier, key1, key2, ..., key7] — no reserved byte.
The code was passing this directly to process_keyboard_report() which
expects boot keyboard format [modifier, reserved=0, key1, ..., key6],
causing every keycode to land in the ignored "reserved" byte position.

Fix: construct a properly formatted boot keyboard report by inserting
a zero reserved byte before the keycodes in both the MSI handler and
poll code paths.

Also increase xHCI poll rate from 10 Hz (100ms latency) to 50 Hz
(20ms latency) for responsive keyboard input.

Cleanup: remove verbose CC=12 investigation logging from xhci.rs,
re-enable VirtIO GPU PCI init, strip diagnostic logging from gpu_pci.rs
and mod.rs, remove btrace periodic spawning from init.

Co-Authored-By: Ryan Breen <ryanbreen@gmail.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@ryanbreen ryanbreen merged commit d493911 into main Mar 1, 2026
1 of 4 checks passed
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