- Implement Limine v8 boot protocol requests in dedicated PT_LOAD RW segment - Configure IOPL=3 and SSE in arch setup for Ring 3 drivers and libc - Unify scheduler context switching via switch_to and preempt_tick - Support dual-source keyboard/serial input in sys_key_read - Add automated 'make image' and 'make run' targets for instant QEMU launch - Complete interactive UNIX-like shell (osh) with built-in commands & ramdisk
218 lines
7.6 KiB
Rust
218 lines
7.6 KiB
Rust
//! Interrupt Descriptor Table (IDT) and Exception Handling
|
|
|
|
use crate::kprintln;
|
|
use crate::sched::SCHEDULER;
|
|
use crate::drivers::timer::send_eoi;
|
|
use core::mem::size_of;
|
|
|
|
#[repr(C, packed)]
|
|
#[derive(Clone, Copy)]
|
|
pub struct IdtEntry {
|
|
offset_low: u16,
|
|
selector: u16,
|
|
ist: u8,
|
|
type_attr: u8,
|
|
offset_mid: u16,
|
|
offset_high: u32,
|
|
_reserved: u32,
|
|
}
|
|
|
|
impl IdtEntry {
|
|
pub const fn missing() -> Self {
|
|
Self {
|
|
offset_low: 0,
|
|
selector: 0,
|
|
ist: 0,
|
|
type_attr: 0,
|
|
offset_mid: 0,
|
|
offset_high: 0,
|
|
_reserved: 0,
|
|
}
|
|
}
|
|
|
|
pub fn set_handler(&mut self, handler: u64, ist: u8, dpl: u8) {
|
|
self.offset_low = (handler & 0xffff) as u16;
|
|
self.selector = 0x08; // Kernel code segment
|
|
self.ist = ist & 0x7;
|
|
self.type_attr = 0x80 | ((dpl & 0x3) << 5) | 0x0E; // 64-bit Interrupt Gate Present
|
|
self.offset_mid = ((handler >> 16) & 0xffff) as u16;
|
|
self.offset_high = ((handler >> 32) & 0xffffffff) as u32;
|
|
self._reserved = 0;
|
|
}
|
|
}
|
|
|
|
#[repr(C, packed)]
|
|
struct IdtDescriptor {
|
|
limit: u16,
|
|
base: u64,
|
|
}
|
|
|
|
static mut IDT: [IdtEntry; 256] = [IdtEntry::missing(); 256];
|
|
|
|
extern "C" {
|
|
fn default_isr_stub();
|
|
fn timer_isr_entry();
|
|
fn keyboard_isr_entry();
|
|
}
|
|
|
|
// Global assembly ISR stub with vector ID pushed as separate stubs (0-31)
|
|
core::arch::global_asm!(
|
|
// With error code: CPU already pushed error_code, we push vector on top
|
|
".macro ISR_ERR_CODE num",
|
|
"isr_stub_\\num:",
|
|
"push \\num", // push vector number (error_code already on stack below)
|
|
"jmp default_isr_common",
|
|
".endm",
|
|
// Without error code: push dummy 0, then vector
|
|
".macro ISR_NO_ERR num",
|
|
"isr_stub_\\num:",
|
|
"push 0", // dummy error_code
|
|
"push \\num", // push vector number
|
|
"jmp default_isr_common",
|
|
".endm",
|
|
|
|
"ISR_NO_ERR 0", "ISR_NO_ERR 1", "ISR_NO_ERR 2", "ISR_NO_ERR 3",
|
|
"ISR_NO_ERR 4", "ISR_NO_ERR 5", "ISR_NO_ERR 6", "ISR_NO_ERR 7",
|
|
"ISR_ERR_CODE 8","ISR_NO_ERR 9", "ISR_ERR_CODE 10","ISR_ERR_CODE 11",
|
|
"ISR_ERR_CODE 12","ISR_ERR_CODE 13","ISR_ERR_CODE 14","ISR_NO_ERR 15",
|
|
"ISR_NO_ERR 16","ISR_ERR_CODE 17","ISR_NO_ERR 18","ISR_NO_ERR 19",
|
|
"ISR_NO_ERR 20","ISR_ERR_CODE 21","ISR_NO_ERR 22","ISR_NO_ERR 23",
|
|
"ISR_NO_ERR 24","ISR_NO_ERR 25","ISR_NO_ERR 26","ISR_NO_ERR 27",
|
|
"ISR_NO_ERR 28","ISR_NO_ERR 29","ISR_ERR_CODE 30","ISR_NO_ERR 31",
|
|
|
|
".global default_isr_stub",
|
|
"default_isr_stub:",
|
|
"push 0", // dummy error code
|
|
"push 255", // dummy vector
|
|
"jmp default_isr_common",
|
|
|
|
".global default_isr_common",
|
|
"default_isr_common:",
|
|
// Stack at this point (top to bottom):
|
|
// RSP+0: vector
|
|
// RSP+8: error_code
|
|
// RSP+16: RIP (pushed by CPU)
|
|
// RSP+24: CS
|
|
// RSP+32: RFLAGS
|
|
// RSP+40: RSP (if ring change)
|
|
// RSP+48: SS (if ring change)
|
|
"push rax", "push rcx", "push rdx",
|
|
"push rsi", "push rdi",
|
|
"push r8", "push r9", "push r10", "push r11",
|
|
"mov rdi, rsp",
|
|
"call raw_exception_handler",
|
|
"pop r11", "pop r10", "pop r9", "pop r8",
|
|
"pop rdi", "pop rsi",
|
|
"pop rdx", "pop rcx", "pop rax",
|
|
// skip vector + error_code from stack
|
|
"add rsp, 16",
|
|
"iretq"
|
|
);
|
|
|
|
|
|
pub unsafe fn init() {
|
|
let idt_ptr = core::ptr::addr_of_mut!(IDT) as *mut IdtEntry;
|
|
|
|
// Install per-vector stubs for exceptions 0-31
|
|
extern "C" {
|
|
fn isr_stub_0(); fn isr_stub_1(); fn isr_stub_2(); fn isr_stub_3();
|
|
fn isr_stub_4(); fn isr_stub_5(); fn isr_stub_6(); fn isr_stub_7();
|
|
fn isr_stub_8(); fn isr_stub_9(); fn isr_stub_10(); fn isr_stub_11();
|
|
fn isr_stub_12(); fn isr_stub_13(); fn isr_stub_14(); fn isr_stub_15();
|
|
fn isr_stub_16(); fn isr_stub_17(); fn isr_stub_18(); fn isr_stub_19();
|
|
fn isr_stub_20(); fn isr_stub_21(); fn isr_stub_22(); fn isr_stub_23();
|
|
fn isr_stub_24(); fn isr_stub_25(); fn isr_stub_26(); fn isr_stub_27();
|
|
fn isr_stub_28(); fn isr_stub_29(); fn isr_stub_30(); fn isr_stub_31();
|
|
}
|
|
|
|
let exception_stubs: [u64; 32] = [
|
|
isr_stub_0 as u64, isr_stub_1 as u64, isr_stub_2 as u64, isr_stub_3 as u64,
|
|
isr_stub_4 as u64, isr_stub_5 as u64, isr_stub_6 as u64, isr_stub_7 as u64,
|
|
isr_stub_8 as u64, isr_stub_9 as u64, isr_stub_10 as u64, isr_stub_11 as u64,
|
|
isr_stub_12 as u64, isr_stub_13 as u64, isr_stub_14 as u64, isr_stub_15 as u64,
|
|
isr_stub_16 as u64, isr_stub_17 as u64, isr_stub_18 as u64, isr_stub_19 as u64,
|
|
isr_stub_20 as u64, isr_stub_21 as u64, isr_stub_22 as u64, isr_stub_23 as u64,
|
|
isr_stub_24 as u64, isr_stub_25 as u64, isr_stub_26 as u64, isr_stub_27 as u64,
|
|
isr_stub_28 as u64, isr_stub_29 as u64, isr_stub_30 as u64, isr_stub_31 as u64,
|
|
];
|
|
for (i, &stub_addr) in exception_stubs.iter().enumerate() {
|
|
(*idt_ptr.add(i)).set_handler(stub_addr, 0, 0);
|
|
}
|
|
|
|
// Fill 32..255 with default stub
|
|
let stub_addr = default_isr_stub as *const () as usize as u64;
|
|
for i in 32..256usize {
|
|
(*idt_ptr.add(i)).set_handler(stub_addr, 0, 0);
|
|
}
|
|
|
|
// Set Timer Interrupt (Vector 32 / 0x20)
|
|
let timer_addr = timer_isr_entry as *const () as usize as u64;
|
|
(*idt_ptr.add(32)).set_handler(timer_addr, 0, 0);
|
|
|
|
// Set Keyboard Interrupt (Vector 33 / 0x21)
|
|
let kbd_addr = keyboard_isr_entry as *const () as usize as u64;
|
|
(*idt_ptr.add(33)).set_handler(kbd_addr, 0, 0);
|
|
|
|
let descriptor = IdtDescriptor {
|
|
limit: (size_of::<[IdtEntry; 256]>() - 1) as u16,
|
|
base: idt_ptr as u64,
|
|
};
|
|
|
|
core::arch::asm!("lidt [{0}]", in(reg) &descriptor, options(preserves_flags));
|
|
}
|
|
|
|
#[repr(C)]
|
|
pub struct ExceptionFrame {
|
|
// Stack layout when raw_exception_handler is called (low addr = top of stack):
|
|
// Pushed by default_isr_common: r11,r10,r9,r8,rdi,rsi,rdx,rcx,rax (in that push order)
|
|
pub r11: u64, pub r10: u64, pub r9: u64, pub r8: u64,
|
|
pub rdi: u64, pub rsi: u64, pub rdx: u64, pub rcx: u64, pub rax: u64,
|
|
// Pushed by per-vector stub (in push order: vector first on top)
|
|
pub vector: u64,
|
|
pub error_code: u64,
|
|
// Pushed by CPU hardware:
|
|
pub rip: u64,
|
|
pub cs: u64,
|
|
pub rflags: u64,
|
|
pub user_rsp: u64, // only present on ring transition
|
|
pub ss: u64,
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern "C" fn raw_exception_handler(frame: *const ExceptionFrame) {
|
|
unsafe {
|
|
let v = (*frame).vector;
|
|
let ec = (*frame).error_code;
|
|
let rip = (*frame).rip;
|
|
let cs = (*frame).cs;
|
|
let rflags = (*frame).rflags;
|
|
let names = [
|
|
"#DE", "#DB", "NMI", "#BP", "#OF", "#BR", "#UD", "#NM",
|
|
"#DF", "CSO", "#TS", "#NP", "#SS", "#GP", "#PF", "RES",
|
|
"#MF", "#AC", "#MC", "#XM", "#VE", "#CP", "??" , "??" ,
|
|
"??" , "??" , "??" , "??" , "??" , "??" , "#SX", "??" ,
|
|
];
|
|
let name = if v < 32 { names[v as usize] } else { "IRQ" };
|
|
kprintln!("\n[EXCEPTION] Vec={} ({}) ErrCode={:#x} RIP={:#018x} CS={:#x} RFLAGS={:#x}",
|
|
v, name, ec, rip, cs, rflags);
|
|
if v == 14 {
|
|
let cr2: u64;
|
|
core::arch::asm!("mov {}, cr2", out(reg) cr2, options(nostack, preserves_flags));
|
|
kprintln!(" #PF CR2 (faulting address): {:#018x}", cr2);
|
|
}
|
|
if v < 32 {
|
|
kprintln!(" [HALT] Unrecoverable exception - CPU halted.");
|
|
loop { core::arch::asm!("cli; hlt", options(nostack, preserves_flags)); }
|
|
}
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern "C" fn timer_interrupt_handler() {
|
|
unsafe {
|
|
send_eoi();
|
|
let sched = &mut *core::ptr::addr_of_mut!(SCHEDULER);
|
|
sched.preempt_tick();
|
|
}
|
|
}
|