mirror of https://github.com/vinc/moros.git
230 lines
8.0 KiB
Rust
230 lines
8.0 KiB
Rust
use crate::sys;
|
|
use crate::sys::process::Registers;
|
|
|
|
use core::arch::asm;
|
|
use lazy_static::lazy_static;
|
|
use spin::Mutex;
|
|
use x86_64::instructions::interrupts;
|
|
use x86_64::instructions::port::Port;
|
|
use x86_64::registers::control::Cr2;
|
|
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, InterruptStackFrameValue, PageFaultErrorCode};
|
|
|
|
const PIC1: u16 = 0x21;
|
|
const PIC2: u16 = 0xA1;
|
|
|
|
pub fn init() {
|
|
IDT.load();
|
|
}
|
|
|
|
// Translate IRQ into system interrupt
|
|
fn interrupt_index(irq: u8) -> u8 {
|
|
sys::pic::PIC_1_OFFSET + irq
|
|
}
|
|
|
|
fn default_irq_handler() {}
|
|
|
|
lazy_static! {
|
|
pub static ref IRQ_HANDLERS: Mutex<[fn(); 16]> = Mutex::new([default_irq_handler; 16]);
|
|
|
|
static ref IDT: InterruptDescriptorTable = {
|
|
let mut idt = InterruptDescriptorTable::new();
|
|
idt.breakpoint.set_handler_fn(breakpoint_handler);
|
|
idt.stack_segment_fault.set_handler_fn(stack_segment_fault_handler);
|
|
idt.segment_not_present.set_handler_fn(segment_not_present_handler);
|
|
unsafe {
|
|
idt.double_fault.
|
|
set_handler_fn(double_fault_handler).
|
|
set_stack_index(sys::gdt::DOUBLE_FAULT_IST_INDEX);
|
|
idt.page_fault.
|
|
set_handler_fn(page_fault_handler).
|
|
set_stack_index(sys::gdt::PAGE_FAULT_IST_INDEX);
|
|
idt.general_protection_fault.
|
|
set_handler_fn(general_protection_fault_handler).
|
|
set_stack_index(sys::gdt::GENERAL_PROTECTION_FAULT_IST_INDEX);
|
|
idt[0x80].
|
|
set_handler_fn(core::mem::transmute(wrapped_syscall_handler as *mut fn())).
|
|
//set_stack_index(sys::gdt::GENERAL_PROTECTION_FAULT_IST_INDEX).
|
|
set_privilege_level(x86_64::PrivilegeLevel::Ring3);
|
|
}
|
|
idt[interrupt_index(0) as usize].set_handler_fn(irq0_handler);
|
|
idt[interrupt_index(1) as usize].set_handler_fn(irq1_handler);
|
|
idt[interrupt_index(2) as usize].set_handler_fn(irq2_handler);
|
|
idt[interrupt_index(3) as usize].set_handler_fn(irq3_handler);
|
|
idt[interrupt_index(4) as usize].set_handler_fn(irq4_handler);
|
|
idt[interrupt_index(5) as usize].set_handler_fn(irq5_handler);
|
|
idt[interrupt_index(6) as usize].set_handler_fn(irq6_handler);
|
|
idt[interrupt_index(7) as usize].set_handler_fn(irq7_handler);
|
|
idt[interrupt_index(8) as usize].set_handler_fn(irq8_handler);
|
|
idt[interrupt_index(9) as usize].set_handler_fn(irq9_handler);
|
|
idt[interrupt_index(10) as usize].set_handler_fn(irq10_handler);
|
|
idt[interrupt_index(11) as usize].set_handler_fn(irq11_handler);
|
|
idt[interrupt_index(12) as usize].set_handler_fn(irq12_handler);
|
|
idt[interrupt_index(13) as usize].set_handler_fn(irq13_handler);
|
|
idt[interrupt_index(14) as usize].set_handler_fn(irq14_handler);
|
|
idt[interrupt_index(15) as usize].set_handler_fn(irq15_handler);
|
|
idt
|
|
};
|
|
}
|
|
|
|
macro_rules! irq_handler {
|
|
($handler:ident, $irq:expr) => {
|
|
pub extern "x86-interrupt" fn $handler(_stack_frame: InterruptStackFrame) {
|
|
let handlers = IRQ_HANDLERS.lock();
|
|
handlers[$irq]();
|
|
unsafe { sys::pic::PICS.lock().notify_end_of_interrupt(interrupt_index($irq)); }
|
|
}
|
|
};
|
|
}
|
|
|
|
irq_handler!(irq0_handler, 0);
|
|
irq_handler!(irq1_handler, 1);
|
|
irq_handler!(irq2_handler, 2);
|
|
irq_handler!(irq3_handler, 3);
|
|
irq_handler!(irq4_handler, 4);
|
|
irq_handler!(irq5_handler, 5);
|
|
irq_handler!(irq6_handler, 6);
|
|
irq_handler!(irq7_handler, 7);
|
|
irq_handler!(irq8_handler, 8);
|
|
irq_handler!(irq9_handler, 9);
|
|
irq_handler!(irq10_handler, 10);
|
|
irq_handler!(irq11_handler, 11);
|
|
irq_handler!(irq12_handler, 12);
|
|
irq_handler!(irq13_handler, 13);
|
|
irq_handler!(irq14_handler, 14);
|
|
irq_handler!(irq15_handler, 15);
|
|
|
|
extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) {
|
|
debug!("EXCEPTION: BREAKPOINT");
|
|
debug!("Stack Frame: {:#?}", stack_frame);
|
|
panic!();
|
|
}
|
|
|
|
extern "x86-interrupt" fn double_fault_handler(stack_frame: InterruptStackFrame, error_code: u64) -> ! {
|
|
debug!("EXCEPTION: DOUBLE FAULT");
|
|
debug!("Stack Frame: {:#?}", stack_frame);
|
|
debug!("Error: {:?}", error_code);
|
|
panic!();
|
|
}
|
|
|
|
extern "x86-interrupt" fn page_fault_handler(_stack_frame: InterruptStackFrame, _error_code: PageFaultErrorCode) {
|
|
let addr = Cr2::read().as_u64();
|
|
sys::allocator::alloc_pages(addr, 1);
|
|
}
|
|
|
|
extern "x86-interrupt" fn general_protection_fault_handler(stack_frame: InterruptStackFrame, error_code: u64) {
|
|
debug!("EXCEPTION: GENERAL PROTECTION FAULT");
|
|
debug!("Stack Frame: {:#?}", stack_frame);
|
|
debug!("Error: {:?}", error_code);
|
|
panic!();
|
|
}
|
|
|
|
extern "x86-interrupt" fn stack_segment_fault_handler(stack_frame: InterruptStackFrame, error_code: u64) {
|
|
debug!("EXCEPTION: STACK SEGMENT FAULT");
|
|
debug!("Stack Frame: {:#?}", stack_frame);
|
|
debug!("Error: {:?}", error_code);
|
|
panic!();
|
|
}
|
|
|
|
extern "x86-interrupt" fn segment_not_present_handler(stack_frame: InterruptStackFrame, error_code: u64) {
|
|
debug!("EXCEPTION: SEGMENT NOT PRESENT");
|
|
debug!("Stack Frame: {:#?}", stack_frame);
|
|
debug!("Error: {:?}", error_code);
|
|
panic!();
|
|
}
|
|
|
|
// Naked function wrapper saving all scratch registers to the stack
|
|
// See: https://os.phil-opp.com/returning-from-exceptions/#a-naked-wrapper-function
|
|
macro_rules! wrap {
|
|
($fn: ident => $w:ident) => {
|
|
#[naked]
|
|
pub unsafe extern "sysv64" fn $w() {
|
|
asm!(
|
|
"push rax",
|
|
"push rcx",
|
|
"push rdx",
|
|
"push rsi",
|
|
"push rdi",
|
|
"push r8",
|
|
"push r9",
|
|
"push r10",
|
|
"push r11",
|
|
"mov rsi, rsp", // Arg #2: register list
|
|
"mov rdi, rsp", // Arg #1: interupt frame
|
|
"add rdi, 9 * 8",
|
|
"call {}",
|
|
"pop r11",
|
|
"pop r10",
|
|
"pop r9",
|
|
"pop r8",
|
|
"pop rdi",
|
|
"pop rsi",
|
|
"pop rdx",
|
|
"pop rcx",
|
|
"pop rax",
|
|
"iretq",
|
|
sym $fn,
|
|
options(noreturn)
|
|
);
|
|
}
|
|
};
|
|
}
|
|
|
|
wrap!(syscall_handler => wrapped_syscall_handler);
|
|
|
|
// NOTE: We can't use "x86-interrupt" for syscall_handler because we need to
|
|
// return a result in the RAX register and it will be overwritten when the
|
|
// context of the caller is restored.
|
|
extern "sysv64" fn syscall_handler(stack_frame: &mut InterruptStackFrame, regs: &mut Registers) {
|
|
// The registers order follow the System V ABI convention
|
|
let n = regs.rax;
|
|
let arg1 = regs.rdi;
|
|
let arg2 = regs.rsi;
|
|
let arg3 = regs.rdx;
|
|
let arg4 = regs.r8;
|
|
|
|
if n == sys::syscall::number::SPAWN { // Backup CPU context
|
|
sys::process::set_stack_frame(**stack_frame);
|
|
sys::process::set_registers(*regs);
|
|
}
|
|
|
|
let res = sys::syscall::dispatcher(n, arg1, arg2, arg3, arg4);
|
|
|
|
if n == sys::syscall::number::EXIT { // Restore CPU context
|
|
let sf = sys::process::stack_frame();
|
|
unsafe {
|
|
//stack_frame.as_mut().write(sf);
|
|
core::ptr::write_volatile(stack_frame.as_mut().extract_inner() as *mut InterruptStackFrameValue, sf); // FIXME
|
|
core::ptr::write_volatile(regs, sys::process::registers());
|
|
}
|
|
}
|
|
|
|
regs.rax = res;
|
|
|
|
unsafe { sys::pic::PICS.lock().notify_end_of_interrupt(0x80) };
|
|
}
|
|
|
|
pub fn set_irq_handler(irq: u8, handler: fn()) {
|
|
interrupts::without_interrupts(|| {
|
|
let mut handlers = IRQ_HANDLERS.lock();
|
|
handlers[irq as usize] = handler;
|
|
|
|
clear_irq_mask(irq);
|
|
});
|
|
}
|
|
|
|
pub fn set_irq_mask(irq: u8) {
|
|
let mut port: Port<u8> = Port::new(if irq < 8 { PIC1 } else { PIC2 });
|
|
unsafe {
|
|
let value = port.read() | (1 << (if irq < 8 { irq } else { irq - 8 }));
|
|
port.write(value);
|
|
}
|
|
}
|
|
|
|
pub fn clear_irq_mask(irq: u8) {
|
|
let mut port: Port<u8> = Port::new(if irq < 8 { PIC1 } else { PIC2 });
|
|
unsafe {
|
|
let value = port.read() & !(1 << if irq < 8 { irq } else { irq - 8 });
|
|
port.write(value);
|
|
}
|
|
}
|