summaryrefslogtreecommitdiffstats
path: root/rsnix/src/ps2.rs
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-08-17 14:53:52 +0200
committersalmonik <salmonik@tuta.io>2026-08-17 14:53:52 +0200
commitbe868a43185826b02bf6d091019bb779678525bb (patch)
tree586988c15f1d700b1621c7f0208584b30878a56c /rsnix/src/ps2.rs
parentv0.5.10: change em-dashes to dashes (diff)
downloadrs-dos-be868a43185826b02bf6d091019bb779678525bb.tar.gz
rs-dos-be868a43185826b02bf6d091019bb779678525bb.zip
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
Diffstat (limited to 'rsnix/src/ps2.rs')
-rw-r--r--rsnix/src/ps2.rs78
1 files changed, 59 insertions, 19 deletions
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() {
169 169
170 // Enable the keyboard interrupt on the PS/2 controller. 170 // Enable the keyboard interrupt on the PS/2 controller.
171 // Read Configuration Byte (command 0x20), set bit 0 (IRQ1 enable), 171 // Read Configuration Byte (command 0x20), set bit 0 (IRQ1 enable),
172 // write back (command 0x60). 172 // write back (command 0x60). Every wait is bounded; a wedged
173 // controller bails out instead of hanging the boot.
173 unsafe { 174 unsafe {
174 // Tell the keyboard controller we want to read the config byte. 175 // Tell the keyboard controller we want to read the config byte.
175 // Need to wait for the controller to be ready first. 176 // Need to wait for the controller to be ready first.
176 wait_write(); 177 if !wait_write() {
178 return;
179 }
177 core::arch::asm!("out dx, al", in("dx") 0x64u16, in("al") 0x20u8, 180 core::arch::asm!("out dx, al", in("dx") 0x64u16, in("al") 0x20u8,
178 options(nostack, preserves_flags)); 181 options(nostack, preserves_flags));
179 wait_read(); 182 if !wait_read() {
183 return;
184 }
180 let mut cfg: u8; 185 let mut cfg: u8;
181 core::arch::asm!("in al, dx", out("al") cfg, in("dx") 0x60u16, 186 core::arch::asm!("in al, dx", out("al") cfg, in("dx") 0x60u16,
182 options(nostack, preserves_flags)); 187 options(nostack, preserves_flags));
@@ -184,48 +189,60 @@ pub fn init() {
184 cfg |= 0x01; // enable keyboard interrupt 189 cfg |= 0x01; // enable keyboard interrupt
185 190
186 // Write config byte back 191 // Write config byte back
187 wait_write(); 192 if !wait_write() {
193 return;
194 }
188 core::arch::asm!("out dx, al", in("dx") 0x64u16, in("al") 0x60u8, 195 core::arch::asm!("out dx, al", in("dx") 0x64u16, in("al") 0x60u8,
189 options(nostack, preserves_flags)); 196 options(nostack, preserves_flags));
190 wait_write(); 197 if !wait_write() {
198 return;
199 }
191 core::arch::asm!("out dx, al", in("dx") 0x60u16, in("al") cfg, 200 core::arch::asm!("out dx, al", in("dx") 0x60u16, in("al") cfg,
192 options(nostack, preserves_flags)); 201 options(nostack, preserves_flags));
193 } 202 }
194} 203}
195 204
196/// Wait until the PS/2 controller is ready to receive a command. 205/// Wait until the PS/2 controller is ready to receive a command.
197fn wait_write() { 206/// Bounded: a wedged or missing 8042 must not hang the boot (the classic
207/// real-hardware freeze — the controller reports busy forever on some
208/// machines). Returns false on timeout.
209fn wait_write() -> bool {
198 unsafe { 210 unsafe {
199 loop { 211 for _ in 0..1_000_000 {
200 let status: u8; 212 let status: u8;
201 core::arch::asm!("in al, dx", out("al") status, in("dx") 0x64u16, 213 core::arch::asm!("in al, dx", out("al") status, in("dx") 0x64u16,
202 options(nostack, preserves_flags)); 214 options(nostack, preserves_flags));
203 if status & 0x02 == 0 { 215 if status & 0x02 == 0 {
204 // Input buffer empty — controller ready to receive 216 // Input buffer empty — controller ready to receive
205 break; 217 return true;
206 } 218 }
207 } 219 }
208 } 220 }
221 false
209} 222}
210 223
211/// Wait until the PS/2 controller has data available to read. 224/// Wait until the PS/2 controller has data available to read.
212fn wait_read() { 225/// Bounded like `wait_write`.
226fn wait_read() -> bool {
213 unsafe { 227 unsafe {
214 loop { 228 for _ in 0..1_000_000 {
215 let status: u8; 229 let status: u8;
216 core::arch::asm!("in al, dx", out("al") status, in("dx") 0x64u16, 230 core::arch::asm!("in al, dx", out("al") status, in("dx") 0x64u16,
217 options(nostack, preserves_flags)); 231 options(nostack, preserves_flags));
218 if status & 0x01 != 0 { 232 if status & 0x01 != 0 {
219 // Output buffer full — data ready to read 233 // Output buffer full — data ready to read
220 break; 234 return true;
221 } 235 }
222 } 236 }
223 } 237 }
238 false
224} 239}
225 240
241/// Drain any pending bytes. Bounded so a stuck output-full flag (or a
242/// controller that keeps raising it with no data) cannot hang the boot.
226fn drain_input() { 243fn drain_input() {
227 unsafe { 244 unsafe {
228 loop { 245 for _ in 0..32 {
229 let status: u8; 246 let status: u8;
230 core::arch::asm!("in al, dx", out("al") status, in("dx") STATUS_PORT, 247 core::arch::asm!("in al, dx", out("al") status, in("dx") STATUS_PORT,
231 options(nostack, preserves_flags)); 248 options(nostack, preserves_flags));
@@ -268,6 +285,15 @@ fn kbd() -> &'static mut KbdState {
268 unsafe { &mut *core::ptr::addr_of_mut!(KBD) } 285 unsafe { &mut *core::ptr::addr_of_mut!(KBD) }
269} 286}
270 287
288/// Feed one scancode-set-1 byte into the shared decoder state machine.
289/// Used by the PS/2 IRQ path (raw bytes) AND the USB HID keyboard driver
290/// (which translates HID usage codes to scancode-set-1 make/break bytes so
291/// the same shift/ctrl/caps decoding applies). Returns a KeyEvent on a
292/// visible press; releases and modifier-only changes return None.
293pub(crate) fn feed_scancode(byte: u8) -> Option<KeyEvent> {
294 process_byte(byte)
295}
296
271fn process_byte(byte: u8) -> Option<KeyEvent> { 297fn process_byte(byte: u8) -> Option<KeyEvent> {
272 let state = kbd(); 298 let state = kbd();
273 299
@@ -466,25 +492,35 @@ pub fn mouse_init() -> bool {
466 drain_input(); 492 drain_input();
467 unsafe { 493 unsafe {
468 // 1. Enable the auxiliary port itself. 494 // 1. Enable the auxiliary port itself.
469 wait_write(); 495 if !wait_write() {
496 return false;
497 }
470 core::arch::asm!("out dx, al", in("dx") STATUS_PORT, in("al") CMD_AUX_ENABLE, 498 core::arch::asm!("out dx, al", in("dx") STATUS_PORT, in("al") CMD_AUX_ENABLE,
471 options(nostack, preserves_flags)); 499 options(nostack, preserves_flags));
472 500
473 // 2. Configuration Byte: enable IRQ12 + the aux port clock. Read, 501 // 2. Configuration Byte: enable IRQ12 + the aux port clock. Read,
474 // OR the two bits, write back (preserves the keyboard IRQ bit 502 // OR the two bits, write back (preserves the keyboard IRQ bit
475 // that ps2::init set). 503 // that ps2::init set).
476 wait_write(); 504 if !wait_write() {
505 return false;
506 }
477 core::arch::asm!("out dx, al", in("dx") STATUS_PORT, in("al") CMD_READ_CFG, 507 core::arch::asm!("out dx, al", in("dx") STATUS_PORT, in("al") CMD_READ_CFG,
478 options(nostack, preserves_flags)); 508 options(nostack, preserves_flags));
479 wait_read(); 509 if !wait_read() {
510 return false;
511 }
480 let mut cfg: u8; 512 let mut cfg: u8;
481 core::arch::asm!("in al, dx", out("al") cfg, in("dx") DATA_PORT, 513 core::arch::asm!("in al, dx", out("al") cfg, in("dx") DATA_PORT,
482 options(nostack, preserves_flags)); 514 options(nostack, preserves_flags));
483 cfg |= CFG_AUX_IRQ | CFG_AUX_CLOCK; 515 cfg |= CFG_AUX_IRQ | CFG_AUX_CLOCK;
484 wait_write(); 516 if !wait_write() {
517 return false;
518 }
485 core::arch::asm!("out dx, al", in("dx") STATUS_PORT, in("al") CMD_WRITE_CFG, 519 core::arch::asm!("out dx, al", in("dx") STATUS_PORT, in("al") CMD_WRITE_CFG,
486 options(nostack, preserves_flags)); 520 options(nostack, preserves_flags));
487 wait_write(); 521 if !wait_write() {
522 return false;
523 }
488 core::arch::asm!("out dx, al", in("dx") DATA_PORT, in("al") cfg, 524 core::arch::asm!("out dx, al", in("dx") DATA_PORT, in("al") cfg,
489 options(nostack, preserves_flags)); 525 options(nostack, preserves_flags));
490 526
@@ -504,10 +540,14 @@ pub fn mouse_init() -> bool {
504/// The wait is bounded so a missing device cannot hang the boot. 540/// The wait is bounded so a missing device cannot hang the boot.
505fn aux_command(cmd: u8) -> bool { 541fn aux_command(cmd: u8) -> bool {
506 unsafe { 542 unsafe {
507 wait_write(); 543 if !wait_write() {
544 return false;
545 }
508 core::arch::asm!("out dx, al", in("dx") STATUS_PORT, in("al") CMD_SEND_TO_AUX, 546 core::arch::asm!("out dx, al", in("dx") STATUS_PORT, in("al") CMD_SEND_TO_AUX,
509 options(nostack, preserves_flags)); 547 options(nostack, preserves_flags));
510 wait_write(); 548 if !wait_write() {
549 return false;
550 }
511 core::arch::asm!("out dx, al", in("dx") DATA_PORT, in("al") cmd, 551 core::arch::asm!("out dx, al", in("dx") DATA_PORT, in("al") cmd,
512 options(nostack, preserves_flags)); 552 options(nostack, preserves_flags));
513 for _ in 0..100_000 { 553 for _ in 0..100_000 {