182 lines
5.2 KiB
Rust
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) }); |