summaryrefslogtreecommitdiffstats
path: root/rsnix/src/usb.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/usb.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 '')
-rw-r--r--rsnix/src/usb.rs147
1 files changed, 144 insertions, 3 deletions
diff --git a/rsnix/src/usb.rs b/rsnix/src/usb.rs
index 71df781..3ed3a0c 100644
--- a/rsnix/src/usb.rs
+++ b/rsnix/src/usb.rs
@@ -229,6 +229,15 @@ unsafe fn wr64(a: usize, v: u64) {
229fn op(off: usize) -> usize { 229fn op(off: usize) -> usize {
230 unsafe { OPB + off } 230 unsafe { OPB + off }
231} 231}
232
233/// 16-bit I/O read (UHCI port status/control registers live in I/O space).
234unsafe fn io_iow16(port: u16) -> u16 {
235 let v: u16;
236 unsafe {
237 core::arch::asm!("in ax, dx", out("ax") v, in("dx") port, options(nomem, nostack, preserves_flags));
238 }
239 v
240}
232fn db(slot: u32) -> usize { 241fn db(slot: u32) -> usize {
233 unsafe { DBB }.wrapping_add(slot as usize * 4) 242 unsafe { DBB }.wrapping_add(slot as usize * 4)
234} 243}
@@ -727,7 +736,13 @@ pub fn init() -> bool {
727 dev, 736 dev,
728 func, 737 func,
729 0x04, 738 0x04,
730 (cmd & 0xFFFF0000) | ((cmd & 0xFFFF) | 0x6), 739 // Memory space + bus master + INTx disable (bit 10). The
740 // driver is polled (event ring drained from the cursor task),
741 // so the controller must never assert INTx: an unhandled
742 // xHCI interrupt would hit the kernel's catch-all handler
743 // and (being level-triggered) re-fire forever on real
744 // hardware.
745 (cmd & 0xFFFF_0000) | ((cmd & 0xFFFF) | 0x6 | 0x400),
731 ); 746 );
732 let bar = crate::pci::bar64(b, dev, func, 0); 747 let bar = crate::pci::bar64(b, dev, func, 0);
733 let base = bar.unwrap() as usize; 748 let base = bar.unwrap() as usize;
@@ -745,6 +760,15 @@ pub fn init() -> bool {
745 MAXPORTS = (hcs1 >> 24) & 0xff; // MaxPorts is bits 31:24 (bits 23:16 are MaxIntrs) 760 MAXPORTS = (hcs1 >> 24) & 0xff; // MaxPorts is bits 31:24 (bits 23:16 are MaxIntrs)
746 let _ = hcc; 761 let _ = hcc;
747 CTRL_CS = if hcc & (1 << 2) != 0 { 64 } else { 32 }; 762 CTRL_CS = if hcc & (1 << 2) != 0 { 64 } else { 32 };
763
764 // Claim USB Legacy ownership from the BIOS before resetting the
765 // controller (same SMM-legacy rationale as the EHCI driver).
766 match crate::pci::claim_usb_legacy(b, dev, func, base, 0x04, true) {
767 0 => crate::serial_println!("usb: xHCI USB legacy: already released (no claim needed)"),
768 1 => crate::serial_println!("usb: xHCI USB legacy handed over by BIOS"),
769 2 => crate::serial_println!("usb: xHCI USB legacy claim timed out (BIOS sem stuck)"),
770 _ => crate::serial_println!("usb: xHCI USB legacy: no capability found"),
771 }
748 } 772 }
749 773
750 // Reset the controller (stop, then reset). Wait for reset completion 774 // Reset the controller (stop, then reset). Wait for reset completion
@@ -802,8 +826,10 @@ pub fn init() -> bool {
802 wr(rt(RT_ERSTSZ), 1); 826 wr(rt(RT_ERSTSZ), 1);
803 wr64(rt(RT_ERSTBA), ERST as u64); 827 wr64(rt(RT_ERSTBA), ERST as u64);
804 wr64(rt(RT_ERDP), EV.raw as u64); 828 wr64(rt(RT_ERDP), EV.raw as u64);
805 // Interrupter 0: clear pending, enable. 829 // Interrupter 0: keep the interrupt DISABLED (IE=0, clear any
806 wr(rt(RT_IMAN), rd(rt(RT_IMAN)) | 0x2); 830 // pending IP) — the driver is polled and the controller must not
831 // assert INTx (see the PCI command-register comment above).
832 wr(rt(RT_IMAN), (rd(rt(RT_IMAN)) & !0x2) | 0x1);
807 wr(rt(RT_IMOD), 0); 833 wr(rt(RT_IMOD), 0);
808 // Run. 834 // Run.
809 wr(op(OP_USBCMD), rd(op(OP_USBCMD)) | CMD_RUN); 835 wr(op(OP_USBCMD), rd(op(OP_USBCMD)) | CMD_RUN);
@@ -973,5 +999,120 @@ pub fn diagnose() -> alloc::string::String {
973 unsafe { QUEUED } 999 unsafe { QUEUED }
974 )); 1000 ));
975 } 1001 }
1002
1003 // Cross-controller attach scan: report which PCI USB controller(s)
1004 // currently have a device with bit0 (connect) set, across xHCI / EHCI /
1005 // UHCI / OHCI. Read-only diagnostic to locate the mouse on real hw.
1006 s.push_str("usbctl: scanning all USB controllers:\n");
1007 unsafe {
1008 for d in crate::pci::probe() {
1009 if d.class != 0x0C || d.subclass != 0x03 {
1010 continue;
1011 }
1012 let lbl = match d.prog_if {
1013 0x00 => "UHCI",
1014 0x10 => "OHCI",
1015 0x20 => "EHCI",
1016 0x30 => "xHCI",
1017 _ => "USB?",
1018 };
1019 let bar = crate::pci::bar64(d.bus, d.device, d.func, 0);
1020 let base = bar.unwrap_or(0);
1021 s.push_str(&alloc::format!(
1022 " {} {:02x}:{:02x}.{} bar={:#x}\n",
1023 lbl, d.bus, d.device, d.func, base
1024 ));
1025 if base == 0 || base == u64::MAX {
1026 // BAR not decoded / not enabled — nothing to probe.
1027 continue;
1028 }
1029 match d.prog_if {
1030 0x30 => {
1031 if base == 0 {
1032 continue;
1033 }
1034 let caplen = rd(base as usize);
1035 let hcs1 = rd(base as usize + 0x04);
1036 let np = ((hcs1 >> 24) & 0xff) as usize;
1037 let opb = base as usize + (caplen & 0xff) as usize;
1038 s.push_str(&alloc::format!(
1039 " xhci ports={} sc=", np
1040 ));
1041 for p in 0..np.min(16) {
1042 let v = rd(opb + 0x400 + p * 0x10);
1043 s.push_str(&alloc::format!(
1044 "{}{:#x}",
1045 if v & 1 != 0 { "*" } else { "" },
1046 v & 0x3ff
1047 ));
1048 if p + 1 < (np.min(16)) {
1049 s.push(' ');
1050 }
1051 }
1052 s.push('\n');
1053 }
1054 0x20 => {
1055 // EHCI: MMIO BAR. CAPLENGTH byte0, HcSParams (nports) @0x04.
1056 let cl = rd(base as usize) & 0xff;
1057 let sp = rd(base as usize + 0x04);
1058 let hc = rd(base as usize + 0x08);
1059 let np = (sp & 0xf) as usize;
1060 let opb = base as usize + cl as usize;
1061 let cmd = crate::pci::config_read(d.bus, d.device, d.func, 0x04);
1062 let eccp = crate::pci::config_read(d.bus, d.device, d.func, 0x34);
1063 s.push_str(&alloc::format!(
1064 " ehci nports={} hrs={} hcc={:#x} cmd={:#x} eccp={:#x}\n",
1065 np, sp, hc, cmd, (eccp >> 8) & 0xff
1066 ));
1067 let cfg = rd(opb + 0x40);
1068 s.push_str(&alloc::format!(
1069 " ehci cfgflag={:#x} ", cfg
1070 ));
1071 for p in 0..np.min(16) {
1072 let v = rd(opb + 0x44 + p * 0x04);
1073 s.push_str(&alloc::format!(
1074 "{}{:#x},",
1075 if v & 1 != 0 { "*" } else { "" },
1076 v & 0xffff
1077 ));
1078 }
1079 s.push('\n');
1080 // Legacy BIOS handoff status (USBLEGSUP at EECP).
1081 let eo = ((eccp >> 8) & 0xff) as usize;
1082 if eo != 0 && (eccp & 0xff) != 0 {
1083 let hs = crate::pci::config_read(
1084 d.bus,
1085 d.device,
1086 d.func,
1087 (eo & 0xfc) as u8,
1088 );
1089 s.push_str(&alloc::format!(
1090 " ehci legacy=usblegsup={:#x} os-owner={} bios-owned={}\n",
1091 hs,
1092 (hs & (1 << 24)) != 0,
1093 (hs & (1 << 16)) != 0 || (hs & 0x100) != 0
1094 ));
1095 }
1096 }
1097 0x00 => {
1098 // UHCI: I/O BAR (bar64 refuses I/O bars — read raw).
1099 let lo = crate::pci::config_read(d.bus, d.device, d.func, 0x10);
1100 let io = (lo & 0xFFF0) as u16;
1101 if io == 0 {
1102 continue;
1103 }
1104 for p in 0..2 {
1105 let pr = io_iow16(io + 0x10 + (p as u16) * 2);
1106 s.push_str(&alloc::format!(
1107 "{}{:x}:", if pr & 1 != 0 { "*" } else { "" },
1108 (pr & 0xff)
1109 ));
1110 }
1111 s.push('\n');
1112 }
1113 _ => {}
1114 }
1115 }
1116 }
976 s 1117 s
977} 1118}