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/vt.rs | 51 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) (limited to 'rsnix/src/vt.rs') diff --git a/rsnix/src/vt.rs b/rsnix/src/vt.rs index 4ffb44d..9444063 100644 --- a/rsnix/src/vt.rs +++ b/rsnix/src/vt.rs @@ -121,9 +121,11 @@ pub fn init() { COLS = cols; CELLS = Box::into_raw(Box::new(vec![BLANK; rows * cols])); RENDERED = Box::into_raw(Box::new(vec![BLANK; rows * cols])); - CURSOR_R = 0; + // Home below row 0: row 0 is the boot-marker status line, so the + // terminal (shell + klog screen echo) must never write to it. + CURSOR_R = 1; CURSOR_C = 0; - LAST_CURSOR = (0, 0); + LAST_CURSOR = (1, 0); CURSOR_VISIBLE = true; SGR_FG = 7; SGR_BG = 0; @@ -133,6 +135,13 @@ pub fn init() { } } +/// True once the terminal is initialised (grids live). The klog screen +/// echo checks this so serial lines printed before vt::init (very early +/// boot) are skipped instead of hitting a null grid. +pub fn ready() -> bool { + unsafe { ROWS != 0 && !CELLS.is_null() } +} + /// (rows, cols) — matches the editor's expected (rows, cols) ordering. pub fn size() -> (usize, usize) { unsafe { (ROWS, COLS) } @@ -667,10 +676,41 @@ pub fn sync_scroll() { // public writing API // --------------------------------------------------------------------------- +/// Disable interrupts unless they are already off (nested-safe). Returns +/// whether they were on, so `irq_restore` re-enables exactly when needed. +/// The terminal write path uses this so a klog screen-echo (which can run +/// in ISR context) can never tear a shell write, and a task-context write +/// can never be split by a tick. +fn irq_off() -> bool { + let rflags: u64; + unsafe { + core::arch::asm!( + "pushfq; pop {}", + out(reg) rflags, + options(nostack, preserves_flags) + ); + if rflags & (1 << 9) != 0 { + core::arch::asm!("cli", options(nostack, preserves_flags)); + true + } else { + false + } + } +} + +fn irq_restore(was_on: bool) { + if was_on { + unsafe { + core::arch::asm!("sti", options(nostack, preserves_flags)); + } + } +} + /// Write raw bytes (ANSI-capable). Used by full-screen apps. UTF-8 /// sequences are decoded so Unicode glyphs (box-drawing, arrows) render; /// undecodable bytes become '?'. pub fn write_bytes(b: &[u8]) { + let on = irq_off(); let mut i = 0; while i < b.len() { let byte = b[i]; @@ -701,20 +741,27 @@ pub fn write_bytes(b: &[u8]) { } } flush(); + irq_restore(on); } /// Write a UTF-8 string (plain text; Unicode glyphs render, others '?'). +/// Atomic with respect to interrupts: a klog screen-echo in ISR context +/// cannot interleave with a shell write, and a tick cannot split a write. pub fn write_str(s: &str) { + let on = irq_off(); for c in s.chars() { write_char(c); } flush(); + irq_restore(on); } /// Write a single ASCII byte / char. pub fn put_char(b: u8) { + let on = irq_off(); put_byte(b); flush(); + irq_restore(on); } /// Clear the whole screen, reset colours, home the cursor. -- cgit v1.2.3