From be868a43185826b02bf6d091019bb779678525bb Mon Sep 17 00:00:00 2001 From: salmonik Date: Mon, 17 Aug 2026 14:53:52 +0200 Subject: USB2/1.1 (EHCI/UHCI) drivers + real-hardware bring-up hardening - Add polled EHCI (USB2) and UHCI (USB1.1) host-controller drivers with HID boot mouse support, wired into cursor polling and the 'usb' shell cmd - Add USB Legacy ownership handoff (claim_usb_legacy) plus config_write_byte - Harden boot: verify the PIT/timer ticks before shell entry, EOI stray interrupts in the LAPIC to stop interrupt storms, bound PS/2 controller waits, and INTx-disable AHCI (polled driver) - Add boot stage markers, klog screen echo, and QR lock; scheduler mark_shell_running; NO_USB stopgap to keep BIOS USB-Legacy input alive - alloc_pages_above for DMA buffers that must avoid low conventional memory - Expand DEBUG_NOTES with EHCI/QEMU bring-up findings --- rsnix/src/ps2.rs | 78 ++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 59 insertions(+), 19 deletions(-) (limited to 'rsnix/src/ps2.rs') diff --git a/rsnix/src/ps2.rs b/rsnix/src/ps2.rs index 5f4a804..965e4a0 100644 --- a/rsnix/src/ps2.rs +++ b/rsnix/src/ps2.rs @@ -169,14 +169,19 @@ pub fn init() { // Enable the keyboard interrupt on the PS/2 controller. // Read Configuration Byte (command 0x20), set bit 0 (IRQ1 enable), - // write back (command 0x60). + // write back (command 0x60). Every wait is bounded; a wedged + // controller bails out instead of hanging the boot. unsafe { // Tell the keyboard controller we want to read the config byte. // Need to wait for the controller to be ready first. - wait_write(); + if !wait_write() { + return; + } core::arch::asm!("out dx, al", in("dx") 0x64u16, in("al") 0x20u8, options(nostack, preserves_flags)); - wait_read(); + if !wait_read() { + return; + } let mut cfg: u8; core::arch::asm!("in al, dx", out("al") cfg, in("dx") 0x60u16, options(nostack, preserves_flags)); @@ -184,48 +189,60 @@ pub fn init() { cfg |= 0x01; // enable keyboard interrupt // Write config byte back - wait_write(); + if !wait_write() { + return; + } core::arch::asm!("out dx, al", in("dx") 0x64u16, in("al") 0x60u8, options(nostack, preserves_flags)); - wait_write(); + if !wait_write() { + return; + } core::arch::asm!("out dx, al", in("dx") 0x60u16, in("al") cfg, options(nostack, preserves_flags)); } } /// Wait until the PS/2 controller is ready to receive a command. -fn wait_write() { +/// Bounded: a wedged or missing 8042 must not hang the boot (the classic +/// real-hardware freeze — the controller reports busy forever on some +/// machines). Returns false on timeout. +fn wait_write() -> bool { unsafe { - loop { + for _ in 0..1_000_000 { let status: u8; core::arch::asm!("in al, dx", out("al") status, in("dx") 0x64u16, options(nostack, preserves_flags)); if status & 0x02 == 0 { // Input buffer empty — controller ready to receive - break; + return true; } } } + false } /// Wait until the PS/2 controller has data available to read. -fn wait_read() { +/// Bounded like `wait_write`. +fn wait_read() -> bool { unsafe { - loop { + for _ in 0..1_000_000 { let status: u8; core::arch::asm!("in al, dx", out("al") status, in("dx") 0x64u16, options(nostack, preserves_flags)); if status & 0x01 != 0 { // Output buffer full — data ready to read - break; + return true; } } } + false } +/// Drain any pending bytes. Bounded so a stuck output-full flag (or a +/// controller that keeps raising it with no data) cannot hang the boot. fn drain_input() { unsafe { - loop { + for _ in 0..32 { let status: u8; core::arch::asm!("in al, dx", out("al") status, in("dx") STATUS_PORT, options(nostack, preserves_flags)); @@ -268,6 +285,15 @@ fn kbd() -> &'static mut KbdState { unsafe { &mut *core::ptr::addr_of_mut!(KBD) } } +/// Feed one scancode-set-1 byte into the shared decoder state machine. +/// Used by the PS/2 IRQ path (raw bytes) AND the USB HID keyboard driver +/// (which translates HID usage codes to scancode-set-1 make/break bytes so +/// the same shift/ctrl/caps decoding applies). Returns a KeyEvent on a +/// visible press; releases and modifier-only changes return None. +pub(crate) fn feed_scancode(byte: u8) -> Option { + process_byte(byte) +} + fn process_byte(byte: u8) -> Option { let state = kbd(); @@ -466,25 +492,35 @@ pub fn mouse_init() -> bool { drain_input(); unsafe { // 1. Enable the auxiliary port itself. - wait_write(); + if !wait_write() { + return false; + } core::arch::asm!("out dx, al", in("dx") STATUS_PORT, in("al") CMD_AUX_ENABLE, options(nostack, preserves_flags)); // 2. Configuration Byte: enable IRQ12 + the aux port clock. Read, // OR the two bits, write back (preserves the keyboard IRQ bit // that ps2::init set). - wait_write(); + if !wait_write() { + return false; + } core::arch::asm!("out dx, al", in("dx") STATUS_PORT, in("al") CMD_READ_CFG, options(nostack, preserves_flags)); - wait_read(); + if !wait_read() { + return false; + } let mut cfg: u8; core::arch::asm!("in al, dx", out("al") cfg, in("dx") DATA_PORT, options(nostack, preserves_flags)); cfg |= CFG_AUX_IRQ | CFG_AUX_CLOCK; - wait_write(); + if !wait_write() { + return false; + } core::arch::asm!("out dx, al", in("dx") STATUS_PORT, in("al") CMD_WRITE_CFG, options(nostack, preserves_flags)); - wait_write(); + if !wait_write() { + return false; + } core::arch::asm!("out dx, al", in("dx") DATA_PORT, in("al") cfg, options(nostack, preserves_flags)); @@ -504,10 +540,14 @@ pub fn mouse_init() -> bool { /// The wait is bounded so a missing device cannot hang the boot. fn aux_command(cmd: u8) -> bool { unsafe { - wait_write(); + if !wait_write() { + return false; + } core::arch::asm!("out dx, al", in("dx") STATUS_PORT, in("al") CMD_SEND_TO_AUX, options(nostack, preserves_flags)); - wait_write(); + if !wait_write() { + return false; + } core::arch::asm!("out dx, al", in("dx") DATA_PORT, in("al") cmd, options(nostack, preserves_flags)); for _ in 0..100_000 { -- cgit v1.2.3