From 6a5c32fa6ae8ec34e3e5b44d7e5a78ded8fe729b Mon Sep 17 00:00:00 2001 From: salmonik Date: Fri, 31 Jul 2026 02:58:31 +0200 Subject: restructure: rsnix kernel + shell crate + rsdos entry point MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Architecture: rs-dos/ (workspace root, was raisernics/) ├── rsnix/ — kernel lib crate (uefi, serial, gop, ps2, interrupts, memory) ├── shell/ — shell lib crate (prompt, commands, line input) ├── rsdos/ — binary crate (efi_main, init, panic handler) └── rsboot/ — future git submodule No functional changes — boots to shell prompt identically. Version auto-pulled from Cargo.toml via env!("CARGO_PKG_VERSION"). --- src/interrupts.rs | 401 ------------------------------------------------------ 1 file changed, 401 deletions(-) delete mode 100644 src/interrupts.rs (limited to 'src/interrupts.rs') diff --git a/src/interrupts.rs b/src/interrupts.rs deleted file mode 100644 index 90d5bbb..0000000 --- a/src/interrupts.rs +++ /dev/null @@ -1,401 +0,0 @@ -// --------------------------------------------------------------------------- -// Interrupt handling: IDT, PIC remap, ISR dispatch. -// --------------------------------------------------------------------------- -use core::arch::asm; -use core::sync::atomic::{AtomicU16, Ordering}; - -// --------------------------------------------------------------------------- -// IDT entry and table -// --------------------------------------------------------------------------- - -/// A single 64-bit IDT entry (16 bytes). -#[derive(Copy, Clone)] -#[repr(C, packed)] -struct IdtEntry { - offset_lo: u16, - selector: u16, - ist: u8, - flags: u8, - offset_mid: u16, - offset_hi: u32, - _reserved: u32, -} - -/// Fill one IDT entry at the given offset (in bytes). -unsafe fn idt_set_entry(idt: *mut u8, vec: u8, handler: u64, selector: u16, flags: u8) { - unsafe { - let off = (vec as usize) * 16; - let p = idt.add(off) as *mut IdtEntry; - p.write(IdtEntry { - offset_lo: handler as u16, - selector, - ist: 0, - flags: flags | 0x80, // bit 7 = present - offset_mid: (handler >> 16) as u16, - offset_hi: (handler >> 32) as u32, - _reserved: 0, - }); - } -} - -/// The one true IDT (live in .bss, zero-initialised). -static mut IDT: [u8; 256 * 16] = [0u8; 256 * 16]; -static mut IDTR: [u16; 5] = [0; 5]; // 2-byte limit + 4-byte base (48-bit pseudo-desc) - -// --------------------------------------------------------------------------- -// PIC (8259A) -// --------------------------------------------------------------------------- - -const PIC1_CMD: u16 = 0x20; -const PIC1_DATA: u16 = 0x21; -const PIC2_CMD: u16 = 0xA0; -const PIC2_DATA: u16 = 0xA1; - -const PIC_EOI: u8 = 0x20; - -const ICW1: u8 = 0x11; // edge, cascade, icw4 -const ICW4: u8 = 0x01; // x86 mode - -/// Remap PIC IRQs 0-15 to IDT vectors 32-47, then mask all. -unsafe fn remap_pic() { - unsafe { - asm!("out dx, al", in("dx") PIC1_CMD, in("al") ICW1, options(nostack, preserves_flags)); - asm!("out dx, al", in("dx") PIC2_CMD, in("al") ICW1, options(nostack, preserves_flags)); - asm!("out dx, al", in("dx") PIC1_DATA, in("al") 0x20u8, options(nostack, preserves_flags)); - asm!("out dx, al", in("dx") PIC2_DATA, in("al") 0x28u8, options(nostack, preserves_flags)); - asm!("out dx, al", in("dx") PIC1_DATA, in("al") 0x04u8, options(nostack, preserves_flags)); - asm!("out dx, al", in("dx") PIC2_DATA, in("al") 0x02u8, options(nostack, preserves_flags)); - asm!("out dx, al", in("dx") PIC1_DATA, in("al") ICW4, options(nostack, preserves_flags)); - asm!("out dx, al", in("dx") PIC2_DATA, in("al") ICW4, options(nostack, preserves_flags)); - asm!("out dx, al", in("dx") PIC1_DATA, in("al") 0xFFu8, options(nostack, preserves_flags)); - asm!("out dx, al", in("dx") PIC2_DATA, in("al") 0xFFu8, options(nostack, preserves_flags)); - } -} - -/// Send End-Of-Interrupt to the master PIC (and slave if needed). -fn eoi(irq: u8) { - unsafe { - // Slave IRQs are 8-15, mapped to vectors 40-47. - if irq >= 8 { - asm!("out dx, al", in("dx") PIC2_CMD, in("al") PIC_EOI, - options(nostack, preserves_flags)); - } - asm!("out dx, al", in("dx") PIC1_CMD, in("al") PIC_EOI, - options(nostack, preserves_flags)); - } -} - -// --------------------------------------------------------------------------- -// PIT timer (channel 0, ~100 Hz) -// --------------------------------------------------------------------------- - -const PIT_CMD: u16 = 0x43; -const PIT_CH0: u16 = 0x40; -const PIT_FREQ: u64 = 1_193_182; - -/// Set up PIT channel 0 in mode 2 (rate generator) at `hz` (1–1000ish). -fn pit_set_rate(hz: u64) { - let divisor = (PIT_FREQ / hz) as u16; - unsafe { - // Mode 2, channel 0, lobyte/hibyte, binary - asm!("out dx, al", in("dx") PIT_CMD, in("al") 0b00_11_010_0u8, - options(nostack, preserves_flags)); - asm!("out dx, al", in("dx") PIT_CH0, in("al") (divisor & 0xFF) as u8, - options(nostack, preserves_flags)); - asm!("out dx, al", in("dx") PIT_CH0, in("al") (divisor >> 8) as u8, - options(nostack, preserves_flags)); - } -} - -// --------------------------------------------------------------------------- -// Lock-free SPSC ring buffer for keyboard scancodes -// --------------------------------------------------------------------------- - -/// Single-producer (ISR) / single-consumer (main) lock-free ring buffer. -struct KeyBuf { - buf: [core::cell::UnsafeCell; 256], - head: AtomicU16, // next slot to read (written by consumer / main) - tail: AtomicU16, // next slot to write (written by producer / ISR) -} - -unsafe impl Sync for KeyBuf {} - -impl KeyBuf { - const fn new() -> Self { - // UnsafeCell requires const initialisation in a const ctx. - // Build an array the long way — one cell at a time. - const NEW_CELL: core::cell::UnsafeCell = core::cell::UnsafeCell::new(0); - // `[NEW_CELL; 256]` needs Clone, which UnsafeCell doesn't have. - // Use a union trick or just mark the whole buffer init with zeroes. - // SAFETY: we initialise via the const-fn path in init(). - Self { - buf: [ - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - ], - head: AtomicU16::new(0), - tail: AtomicU16::new(0), - } - } - - /// Push one byte — called from ISR (interrupts disabled, single-producer). - fn push(&self, byte: u8) { - let t = self.tail.load(Ordering::Relaxed); - let h = self.head.load(Ordering::Acquire); - let next = t.wrapping_add(1); - // Full if the distance between head and tail == capacity (256). - if next.wrapping_sub(h) as u16 > 255 { - return; // drop — ring is full - } - unsafe { - *self.buf[t as usize & 255].get() = byte; - } - // Ensure byte is globally visible before incrementing tail. - self.tail.store(next, Ordering::Release); - } - - /// Pop one byte — called from main context. Returns None when empty. - fn pop(&self) -> Option { - let h = self.head.load(Ordering::Relaxed); - let t = self.tail.load(Ordering::Acquire); - if h == t { - return None; - } - let byte = unsafe { *self.buf[h as usize & 255].get() }; - self.head.store(h.wrapping_add(1), Ordering::Release); - Some(byte) - } -} - -static KEYBUF: KeyBuf = KeyBuf::new(); - -// --------------------------------------------------------------------------- -// Interrupt stubs (assembly) and dispatch -// --------------------------------------------------------------------------- - -core::arch::global_asm!( - // ISR stubs — push vector number + error code, jump to common. - ".globl isr_stub_32", - ".p2align 4", - "isr_stub_32:", - " push 0", // error code (non-exception) - " push 32", // vector - " jmp isr_common", - "", - ".globl isr_stub_33", - ".p2align 4", - "isr_stub_33:", - " push 0", - " push 33", - " jmp isr_common", - "", - // Catch-all for unexpected interrupts. - ".globl isr_stub_spurious", - ".p2align 4", - "isr_stub_spurious:", - " push 0", - " push 0xFF", - " jmp isr_common", - "", - // Common handler — save all registers, call into Rust, restore, iretq. - "isr_common:", - " push rax", - " push rcx", - " push rdx", - " push rbx", - " push rbp", - " push rsi", - " push rdi", - " push r8", - " push r9", - " push r10", - " push r11", - " push r12", - " push r13", - " push r14", - " push r15", - "", - " mov rcx, rsp", // first arg (rcx = MS x64 ABI) - " call isr_dispatch", - "", - " pop r15", - " pop r14", - " pop r13", - " pop r12", - " pop r11", - " pop r10", - " pop r9", - " pop r8", - " pop rdi", - " pop rsi", - " pop rbp", - " pop rbx", - " pop rdx", - " pop rcx", - " pop rax", - " add rsp, 16", // pop vector + error code - " iretq", -); - -/// Saved register frame (as pushed by `isr_common`). -#[repr(C)] -struct IntFrame { - r15: u64, - r14: u64, - r13: u64, - r12: u64, - r11: u64, - r10: u64, - r9: u64, - r8: u64, - rdi: u64, - rsi: u64, - rbp: u64, - rbx: u64, - rdx: u64, - rcx: u64, - rax: u64, - vector: u64, - error: u64, - rip: u64, - cs: u64, - rflags: u64, -} - -/// Called from the asm common handler — dispatches to the right Rust handler. -#[no_mangle] -unsafe extern "C" fn isr_dispatch(frame: &IntFrame) { - unsafe { - match frame.vector { - 32 => eoi(0), - 33 => { - let byte: u8; - core::arch::asm!("in al, dx", out("al") byte, in("dx") 0x60u16, - options(nostack, preserves_flags)); - // Push scancode to ring buffer (called with interrupts disabled). - KEYBUF.push(byte); - eoi(1); - } - _ => {} - } - } -} - -/// Timer IRQ (PIT channel 0 ~ 100 Hz). -fn timer_irq() { - eoi(0); -} - -/// Keyboard IRQ — read scancode from port 0x60 and push to ring buffer. -fn keyboard_irq() { - let byte: u8; - unsafe { - asm!("in al, dx", out("al") byte, in("dx") 0x60, - options(nostack, preserves_flags)); - } - KEYBUF.push(byte); - eoi(1); -} - -// --------------------------------------------------------------------------- -// Public API -// --------------------------------------------------------------------------- - -/// Read one scancode from the interrupt-driven ring buffer (non-blocking). -pub fn read_scancode() -> Option { - KEYBUF.pop() -} - -/// Global tick counter (incremented by timer IRQ, ~100 Hz). -pub static TICKS: AtomicU16 = AtomicU16::new(0); - -/// Initialise the IDT, remap the PIC, set up the PIT, and enable interrupts. -pub fn init() { - unsafe { - // Read the current code segment selector from CS. - let cs: u64; - asm!("mov {0}, cs", out(reg) cs, options(nostack, preserves_flags)); - let cs = cs as u16; - - // Build the IDT. - let idt_ptr = IDT.as_mut_ptr(); - let spurious = isr_stub_spurious as *const () as u64; - - // Fill all 256 entries with the spurious handler. - for vec in 0..=255u8 { - idt_set_entry(idt_ptr, vec, spurious, cs, 0x0Eu8); - } - - // Override vectors for interrupt we actually handle. - idt_set_entry(idt_ptr, 32, isr_stub_32 as *const () as u64, cs, 0x0Eu8); - idt_set_entry(idt_ptr, 33, isr_stub_33 as *const () as u64, cs, 0x0Eu8); - - // Build the IDTR pseudo-descriptor. - IDTR = [ - (256u16 * 16 - 1) as u16, // limit = 256*16 - 1 - idt_ptr as u16, // base lo - (idt_ptr as u64 >> 16) as u16, // base mid - (idt_ptr as u64 >> 32) as u16, // base hi - (idt_ptr as u64 >> 48) as u16, // base top - ]; - - // Load IDT. - asm!("lidt [{}]", in(reg) &IDTR, options(nostack, preserves_flags)); - - // Remap PIC and mask everything. - remap_pic(); - - // Set up PIT timer at ~100 Hz. - pit_set_rate(100); - - // Unmask timer (IRQ0 → bit 0) and keyboard (IRQ1 → bit 1) on master. - let mut mask: u8; - asm!("in al, dx", out("al") mask, in("dx") PIC1_DATA, - options(nostack, preserves_flags)); - mask &= !0x03u8; - asm!("out dx, al", in("dx") PIC1_DATA, in("al") mask, - options(nostack, preserves_flags)); - - // Enable interrupts. - asm!("sti", options(nostack)); - } -} - -// --------------------------------------------------------------------------- -// Extern symbols from global_asm! -// --------------------------------------------------------------------------- -extern "C" { - fn isr_stub_32(); - fn isr_stub_33(); - fn isr_stub_spurious(); -} -- cgit v1.2.3