灵符lingfu

ling-lang

↓ 0 downloads

Ling - The Omniglot Systems Language

Keywords systems, polyglot, ai, crypto, game
lingfu install ling-lang
LicenseApache-2.0 OR MIT
AuthorsLing Lin, Sanny Lin
READMECode / FilesVersionsDependency GraphDependentsSecurityBuildsSettings

Code / Files (995)

crates/ling-kernel/src/arch/x86_64/idt.rs
//! Interrupt Descriptor Table: all 32 CPU exception vectors with a readable
//! fault dump (vector, name, error code, RIP/CS/RFLAGS/RSP/SS, and CR2 for
//! page faults) instead of the silent triple-fault this kernel had before —
//! and the three hardware IRQs currently in use (PIT heartbeat, PS/2
//! keyboard, PS/2 mouse), remapped by `pic.rs` onto vectors 32+.
//!
//! Requires `#![feature(abi_x86_interrupt)]` at the crate root: this is the
//! only correct way to write an ISR in Rust without hand-writing the
//! prologue/epilogue in `global_asm!` — the compiler emits the right
//! register save/restore and `iretq` itself, using the same hardware-pushed
//! frame layout `InterruptFrame` mirrors below.
use super::{cpu, gdt, io, pic};
use core::mem::size_of;

#[repr(C)]
pub struct InterruptFrame {
    pub rip: u64,
    pub cs: u64,
    pub rflags: u64,
    pub rsp: u64,
    pub ss: u64,
}

#[repr(C, packed)]
struct IdtEntry {
    offset_low: u16,
    selector: u16,
    ist: u8,
    type_attr: u8,
    offset_mid: u16,
    offset_high: u32,
    reserved: u32,
}

const IDT_ENTRIES: usize = 256;
const NULL_ENTRY: IdtEntry = IdtEntry {
    offset_low: 0,
    selector: 0,
    ist: 0,
    type_attr: 0,
    offset_mid: 0,
    offset_high: 0,
    reserved: 0,
};
static mut IDT: [IdtEntry; IDT_ENTRIES] = [NULL_ENTRY; IDT_ENTRIES];

const GATE_PRESENT_INTERRUPT: u8 = 0x8E;

fn set_gate(vector: u8, handler: u64, ist: u8) {
    unsafe {
        IDT[vector as usize] = IdtEntry {
            offset_low: (handler & 0xFFFF) as u16,
            selector: gdt::KERNEL_CODE_SEL,
            ist,
            type_attr: GATE_PRESENT_INTERRUPT,
            offset_mid: ((handler >> 16) & 0xFFFF) as u16,
            offset_high: ((handler >> 32) & 0xFFFF_FFFF) as u32,
            reserved: 0,
        };
    }
}

// ─── Fault dump ──────────────────────────────────────────────────────────

fn print_hex64(label: &str, val: u64) {
    crate::console_write(label.as_bytes());
    crate::console_write(b"0x");
    let mut buf = [0u8; 16];
    for i in 0..16 {
        let nibble = (val >> ((15 - i) * 4)) & 0xF;
        buf[i] = if nibble < 10 {
            b'0' + nibble as u8
        } else {
            b'a' + (nibble - 10) as u8
        };
    }
    crate::console_write(&buf);
    crate::console_write(b"\n");
}

fn fault_dump(vector: u8, name: &str, frame: &InterruptFrame, error_code: Option<u64>) {
    crate::console_write(b"\n!!! CPU EXCEPTION: ");
    crate::console_write(name.as_bytes());
    crate::console_write(b" (vector ");
    print_hex64("", vector as u64);
    if let Some(code) = error_code {
        print_hex64("  error_code=", code);
    }
    print_hex64("  rip=", frame.rip);
    print_hex64("  cs=", frame.cs);
    print_hex64("  rflags=", frame.rflags);
    print_hex64("  rsp=", frame.rsp);
    print_hex64("  ss=", frame.ss);
    if vector == 14 {
        print_hex64("  cr2=", unsafe { cpu::read_cr2() });
    }
    print_hex64("  cr3=", unsafe { cpu::read_cr3() });
}

fn fault_halt(vector: u8, name: &str, frame: &InterruptFrame, error_code: Option<u64>) -> ! {
    fault_dump(vector, name, frame, error_code);
    crate::console_write(b"!!! kernel halted\n");
    loop {
        unsafe { cpu::halt() };
    }
}

macro_rules! exception_fatal_no_err {
    ($name:ident, $vec:expr, $desc:expr) => {
        extern "x86-interrupt" fn $name(frame: InterruptFrame) {
            fault_halt($vec, $desc, &frame, None);
        }
    };
}

macro_rules! exception_fatal_with_err {
    ($name:ident, $vec:expr, $desc:expr) => {
        extern "x86-interrupt" fn $name(frame: InterruptFrame, error_code: u64) {
            fault_halt($vec, $desc, &frame, Some(error_code));
        }
    };
}

extern "x86-interrupt" fn isr_breakpoint(frame: InterruptFrame) {
    // Not fatal: a debugger hitting `int3` expects execution to resume, and
    // there is no reason a breakpoint should take the kernel down. RIP
    // already points past the `int3` opcode (a trap, not a fault), so a
    // plain `iretq` continues correctly.
    fault_dump(3, "Breakpoint", &frame, None);
}

exception_fatal_no_err!(isr_divide_error, 0, "Divide Error");
exception_fatal_no_err!(isr_debug, 1, "Debug");
exception_fatal_no_err!(isr_nmi, 2, "Non-Maskable Interrupt");
exception_fatal_no_err!(isr_overflow, 4, "Overflow");
exception_fatal_no_err!(isr_bound_range, 5, "Bound Range Exceeded");
exception_fatal_no_err!(isr_invalid_opcode, 6, "Invalid Opcode");
exception_fatal_no_err!(isr_device_not_available, 7, "Device Not Available");
exception_fatal_with_err!(isr_double_fault, 8, "Double Fault");
exception_fatal_no_err!(isr_coprocessor_overrun, 9, "Coprocessor Segment Overrun");
exception_fatal_with_err!(isr_invalid_tss, 10, "Invalid TSS");
exception_fatal_with_err!(isr_segment_not_present, 11, "Segment Not Present");
exception_fatal_with_err!(isr_stack_segment_fault, 12, "Stack-Segment Fault");
exception_fatal_with_err!(isr_general_protection, 13, "General Protection Fault");
exception_fatal_with_err!(isr_page_fault, 14, "Page Fault");
exception_fatal_no_err!(isr_reserved_15, 15, "Reserved");
exception_fatal_no_err!(isr_x87_fp, 16, "x87 Floating-Point Exception");
exception_fatal_with_err!(isr_alignment_check, 17, "Alignment Check");
exception_fatal_no_err!(isr_machine_check, 18, "Machine Check");
exception_fatal_no_err!(isr_simd_fp, 19, "SIMD Floating-Point Exception");
exception_fatal_no_err!(isr_virtualization, 20, "Virtualization Exception");
exception_fatal_with_err!(isr_control_protection, 21, "Control Protection Exception");
exception_fatal_no_err!(isr_reserved_22, 22, "Reserved");
exception_fatal_no_err!(isr_reserved_23, 23, "Reserved");
exception_fatal_no_err!(isr_reserved_24, 24, "Reserved");
exception_fatal_no_err!(isr_reserved_25, 25, "Reserved");
exception_fatal_no_err!(isr_reserved_26, 26, "Reserved");
exception_fatal_no_err!(isr_reserved_27, 27, "Reserved");
exception_fatal_no_err!(
    isr_hypervisor_injection,
    28,
    "Hypervisor Injection Exception"
);
exception_fatal_with_err!(isr_vmm_communication, 29, "VMM Communication Exception");
exception_fatal_with_err!(isr_security, 30, "Security Exception");
exception_fatal_no_err!(isr_reserved_31, 31, "Reserved");

// ─── Hardware IRQs (vectors 32+, remapped by pic.rs) ────────────────────

// Vector 32 (the PIT heartbeat) is *not* wired here: it needs full
// general-purpose-register save/restore to drive `proc::uproc`'s
// preemptive scheduler, which the `extern "x86-interrupt"` ABI's
// compiler-managed prologue never exposes to Rust. See `trap.rs` for that
// handler (`timer_trap_entry`) and `ticks()` (moved there too, alongside
// the state it now also drives scheduling decisions from) — `init` below
// still installs it, just onto `gdt::TIMER_IST` instead of the shared
// no-IST gates every other vector uses.

/// Drain every byte the 8042 currently holds, routing each by the status
/// register's aux-origin bit (bit 5: set = mouse, clear = keyboard). Both
/// the IRQ1 and IRQ12 handlers call this instead of reading exactly one
/// byte, for two load-bearing reasons found the hard way (QEMU headless
/// `mouse_move` test, `wm.rs`'s serial diagnostics):
///
/// 1. The 8259 PIC is edge-triggered and the 8042 keeps its interrupt line
///    HIGH while more bytes are queued -- a one-byte-per-interrupt handler
///    reads byte 1, the line never falls, no new edge ever arrives, and the
///    remaining bytes wedge there forever (observed: status 0x3D -- OBF set,
///    aux origin, byte count frozen at 1).
/// 2. With both devices live, "which IRQ fired" does not reliably identify
///    the byte's origin -- the status bit does. Routing by IRQ number sent
///    mouse packets into the keyboard decoder on real interleavings.
fn drain_8042() {
    loop {
        let status = unsafe { io::inb(0x64) };
        if status & 0x01 == 0 {
            break;
        }
        let byte = unsafe { io::inb(0x60) };
        if status & 0x20 != 0 {
            crate::drivers::mouse::irq_byte(byte);
        } else {
            crate::drivers::keyboard::irq_scancode(byte);
        }
    }
}

/// Polled variant of the same drain, for callers outside interrupt context
/// (the WM's once-per-frame `ling_kernel_mouse_poll`). Interrupts are
/// masked across the drain so an IRQ1/IRQ12 can't interleave and split a
/// 3-byte mouse packet between the two paths. This exists because QEMU's
/// 8042 was observed holding OBF high with aux data queued while never
/// raising IRQ12 at all (see `drain_8042`'s doc) -- with a per-frame poll,
/// input keeps flowing on any 8042, IRQ-generous or not.
pub(crate) fn poll_drain_8042() {
    unsafe {
        core::arch::asm!("cli");
    }
    drain_8042();
    unsafe {
        core::arch::asm!("sti");
    }
}

extern "x86-interrupt" fn isr_keyboard(_frame: InterruptFrame) {
    drain_8042();
    pic::eoi(1);
}

extern "x86-interrupt" fn isr_mouse(_frame: InterruptFrame) {
    drain_8042();
    pic::eoi(12);
}

extern "x86-interrupt" fn isr_spurious(_frame: InterruptFrame) {
    // No handler registered for this vector but the line still fired (e.g.
    // a legitimately spurious IRQ7/IRQ15, or a device never masked). EOI
    // unconditionally so the controller doesn't wedge; there is nothing
    // useful to dump since no specific device claims this vector.
}

pub fn init() {
    set_gate(0, isr_divide_error as *const () as u64, 0);
    set_gate(1, isr_debug as *const () as u64, 0);
    set_gate(2, isr_nmi as *const () as u64, gdt::DOUBLE_FAULT_IST);
    set_gate(3, isr_breakpoint as *const () as u64, 0);
    set_gate(4, isr_overflow as *const () as u64, 0);
    set_gate(5, isr_bound_range as *const () as u64, 0);
    set_gate(6, isr_invalid_opcode as *const () as u64, 0);
    set_gate(7, isr_device_not_available as *const () as u64, 0);
    set_gate(
        8,
        isr_double_fault as *const () as u64,
        gdt::DOUBLE_FAULT_IST,
    );
    set_gate(9, isr_coprocessor_overrun as *const () as u64, 0);
    set_gate(10, isr_invalid_tss as *const () as u64, 0);
    set_gate(11, isr_segment_not_present as *const () as u64, 0);
    set_gate(12, isr_stack_segment_fault as *const () as u64, 0);
    set_gate(13, isr_general_protection as *const () as u64, 0);
    set_gate(14, isr_page_fault as *const () as u64, 0);
    set_gate(15, isr_reserved_15 as *const () as u64, 0);
    set_gate(16, isr_x87_fp as *const () as u64, 0);
    set_gate(17, isr_alignment_check as *const () as u64, 0);
    set_gate(
        18,
        isr_machine_check as *const () as u64,
        gdt::DOUBLE_FAULT_IST,
    );
    set_gate(19, isr_simd_fp as *const () as u64, 0);
    set_gate(20, isr_virtualization as *const () as u64, 0);
    set_gate(21, isr_control_protection as *const () as u64, 0);
    set_gate(22, isr_reserved_22 as *const () as u64, 0);
    set_gate(23, isr_reserved_23 as *const () as u64, 0);
    set_gate(24, isr_reserved_24 as *const () as u64, 0);
    set_gate(25, isr_reserved_25 as *const () as u64, 0);
    set_gate(26, isr_reserved_26 as *const () as u64, 0);
    set_gate(27, isr_reserved_27 as *const () as u64, 0);
    set_gate(28, isr_hypervisor_injection as *const () as u64, 0);
    set_gate(29, isr_vmm_communication as *const () as u64, 0);
    set_gate(30, isr_security as *const () as u64, 0);
    set_gate(31, isr_reserved_31 as *const () as u64, 0);

    for v in 32..IDT_ENTRIES {
        set_gate(v as u8, isr_spurious as *const () as u64, 0);
    }
    set_gate(pic::IRQ0_VECTOR, super::trap::timer_trap_entry as *const () as u64, gdt::TIMER_IST);
    set_gate(pic::IRQ0_VECTOR + 1, isr_keyboard as *const () as u64, 0);
    set_gate(pic::IRQ0_VECTOR + 12, isr_mouse as *const () as u64, 0);

    #[repr(C, packed)]
    struct IdtPointer {
        limit: u16,
        base: u64,
    }
    unsafe {
        let pointer = IdtPointer {
            limit: (size_of::<[IdtEntry; IDT_ENTRIES]>() - 1) as u16,
            base: (&raw const IDT) as u64,
        };
        core::arch::asm!("lidt [{}]", in(reg) &pointer, options(readonly, nostack, preserves_flags));
    }
}

← Back to index