os/src/interrupts.rs

182 lines
5.2 KiB
Rust

use core::{intrinsics::transmute, arch::asm};
use crate::{lazy_static, print, println, proc::{self, Registers}};
use pc_keyboard::{layouts, DecodedKey, Keyboard, ScancodeSet1};
use pic8259::ChainedPics;
use spin::Mutex;
use x86_64::{
instructions::{interrupts, port::Port},
structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode},
};
pub const DOUBLE_FAULT_STACK_IDX: u16 = 0;
pub const TIMER_STACK_IDX: u16 = 1;
#[repr(u8)]
enum Interrupt {
Timer = PIC_1_OFFSET,
Keyboard,
Syscall = 0x80
}
lazy_static! {
static ref IDT: InterruptDescriptorTable = {
let mut idt = InterruptDescriptorTable::new();
unsafe {
idt.double_fault
.set_handler_fn(double_fault)
.set_stack_index(DOUBLE_FAULT_STACK_IDX)
};
idt.breakpoint.set_handler_fn(breakpoint);
idt.page_fault.set_handler_fn(page_fault);
idt.invalid_opcode.set_handler_fn(invalid_opcode);
idt.segment_not_present.set_handler_fn(segment_not_present);
idt.stack_segment_fault.set_handler_fn(stack_segment_fault);
idt.general_protection_fault.set_handler_fn(general_protection_fault);
unsafe {
idt[Interrupt::Timer as usize]
.set_handler_fn(transmute(timer_naked as *const ()))
.set_stack_index(TIMER_STACK_IDX)
.disable_interrupts(true) // this is the default but i want to make EXTRA SURE there will be no stupid edge case
};
idt[Interrupt::Keyboard as usize].set_handler_fn(keyboard);
idt[Interrupt::Syscall as usize].set_handler_fn(syscall);
idt
};
}
extern "x86-interrupt" fn breakpoint(stack: InterruptStackFrame) {
println!("breakpoint: {:#?}", stack);
}
extern "x86-interrupt" fn page_fault(stack: InterruptStackFrame, error: PageFaultErrorCode) {
panic!("page fault: {:?}\n{:#?}", error, stack)
}
extern "x86-interrupt" fn invalid_opcode(stack: InterruptStackFrame) {
panic!("invalid opcode: {:#?}", stack)
}
extern "x86-interrupt" fn segment_not_present(stack: InterruptStackFrame, error: u64) {
panic!("segment not present: {} {:#?}", error, stack)
}
extern "x86-interrupt" fn stack_segment_fault(stack: InterruptStackFrame, error: u64) {
panic!("stack segment fault: {} {:#?}", error, stack)
}
extern "x86-interrupt" fn general_protection_fault(stack: InterruptStackFrame, error: u64) {
panic!("general protection fault: {} {:#?}", error, stack)
}
extern "x86-interrupt" fn double_fault(stack: InterruptStackFrame, error: u64) -> ! {
panic!("aaaaaaaaaaaaa (error code: {}) {:#?}", error, stack);
}
extern "x86-interrupt" fn syscall(_: InterruptStackFrame) {
}
// dont even breathe on this code
#[naked]
unsafe extern "x86-interrupt" fn timer_naked() {
asm! {
// push the `Registers` struct
"push r15",
"push r14",
"push r13",
"push r12",
"push r11",
"push r10",
"push r9",
"push r8",
"push rbp",
"push rdi",
"push rsi",
"push rdx",
"push rcx",
"push rbx",
"push rax",
// call the `extern "C"` version
"call {}",
// pop the `Registers` struct off the stack and back into the registers
"pop rax",
"pop rbx",
"pop rcx",
"pop rdx",
"pop rsi",
"pop rdi",
"pop rbp",
"pop r8",
"pop r9",
"pop r10",
"pop r11",
"pop r12",
"pop r13",
"pop r14",
"pop r15",
"iretq",
sym timer_c,
options(noreturn),
}
}
const FREQUENCY_DIVIDER: u8 = 15;
static mut TIMER_COUNT: u8 = 0;
extern "C" fn timer_c(mut registers: Registers, mut stack: InterruptStackFrame) {
unsafe {
TIMER_COUNT = (TIMER_COUNT + 1) % FREQUENCY_DIVIDER;
if TIMER_COUNT == 0 {
proc::timer(stack.as_mut().extract_inner(), &mut registers);
}
}
unsafe {
PICS.lock().notify_end_of_interrupt(Interrupt::Timer as u8);
}
}
extern "x86-interrupt" fn keyboard(_: InterruptStackFrame) {
lazy_static! {
static ref KEYBOARD: Mutex<Keyboard<layouts::Us104Key, ScancodeSet1>> = {
Mutex::new(Keyboard::new(
layouts::Us104Key,
ScancodeSet1,
pc_keyboard::HandleControl::Ignore,
))
};
}
let mut port = Port::new(0x60);
let scancode: u8 = unsafe { port.read() };
let mut keyboard = KEYBOARD.lock();
if let Ok(Some(event)) = keyboard.add_byte(scancode) {
if let Some(key) = keyboard.process_keyevent(event) {
match key {
DecodedKey::RawKey(key_code) => print!("{:?}", key_code),
DecodedKey::Unicode(char) => print!("{}", char),
}
}
}
unsafe {
PICS.lock().notify_end_of_interrupt(Interrupt::Keyboard as u8);
}
}
pub fn init_interrupts() {
IDT.load();
unsafe { PICS.lock().initialize() }
interrupts::enable()
}
pub const PIC_1_OFFSET: u8 = 32;
pub const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8;
pub static PICS: Mutex<ChainedPics> = Mutex::new(unsafe { ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET) });