Newer
Older
//! Implementation of CPU interruptions
use core::ops::IndexMut;
use core::sync::atomic::Ordering;
use spin::Lazy;
use x86_64::instructions::port::Port;
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
use super::cmos::CMOS;
use super::pics::{PIC1_OFFSET, PIC2_OFFSET, PICS};
use super::pit::{self, LAST_RTC_UPDATE, TICKS};
use crate::kernel::interrupt::keyboard::KeyQueue;
/// Index of the interrupt numbers (IRQ)
#[repr(u8)]
#[derive(Debug, Clone, Copy)]
pub(super) enum InterruptIndex {
/// Timer interrupt
Timer = PIC1_OFFSET,
/// Keyboards interrupt
Keyboard,
/// Real-time controller interrupt
Rtc = PIC2_OFFSET,
/// Standard ATA primary interrupt (0xE above base offset)
AtaPrimary = PIC1_OFFSET + 0x0E,
/// Standard ATA primary interrupt (0xF above base offset)
AtaSecondary = PIC1_OFFSET + 0x0F,
}
impl From<InterruptIndex> for u8 {
fn from(value: InterruptIndex) -> Self {
value as Self
}
}
impl From<InterruptIndex> for usize {
fn from(value: InterruptIndex) -> Self {
u8::from(value).into()
}
}
/// Global Interruption Descriptor Table (IDT)
pub(super) static IDT: Lazy<InterruptDescriptorTable> = Lazy::new(|| {
let mut idt = InterruptDescriptorTable::new();
idt.breakpoint.set_handler_fn(breakpoint_handler);
idt.double_fault.set_handler_fn(double_fault_handler);
set_handler_fn(&mut idt, InterruptIndex::Timer, timer_interrupt_handler);
set_handler_fn(&mut idt, InterruptIndex::Keyboard, keyboard_interrupt_handler);
set_handler_fn(&mut idt, InterruptIndex::Rtc, rtc_interrupt_handler);
set_handler_fn(&mut idt, InterruptIndex::AtaPrimary, primary_ata_handler);
set_handler_fn(&mut idt, InterruptIndex::AtaSecondary, secondary_ata_handler);
idt
});
/// Set a given `handler` for the given `idt` at a given `pin`
fn set_handler_fn(
idt: &mut InterruptDescriptorTable,
pin: InterruptIndex,
handler: extern "x86-interrupt" fn(InterruptStackFrame),
) {
idt.index_mut(pin.into()).set_handler_fn(handler);
let index: u8 = Into::<u8>::into(pin) - PIC1_OFFSET;
let (interrupt_line, port_number) = if index < 8 { (index, 0x21) } else { (index - 8, 0xA1) };
let mut port = Port::<u8>::new(port_number);
// SAFETY: no memory violation is made here
let byte = unsafe { port.read() };
// SAFETY: and neither here
unsafe {
port.write(byte & !(1 << interrupt_line));
};
}
/// Custom interruption handler to print useful informations on the error
extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) {
println!("Exception: breakpoint\n{:#?}", stack_frame);
}
#[cfg(test)]
const extern "x86-interrupt" fn breakpoint_handler(_stack_frame: InterruptStackFrame) {}
/// Same purpose as [`breakpoint_handler`] but for double faults
extern "x86-interrupt" fn double_fault_handler(stack_frame: InterruptStackFrame, error_code: u64) -> ! {
panic!("Double fault code {}\n{:#?}", error_code, stack_frame);
}
/// Same purpose as [`breakpoint_handler`] but for time interruptions
extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFrame) {
TICKS.fetch_add(1, Ordering::Relaxed);
// SAFETY: Timer index is the same everywhere it should
unsafe {
PICS.lock().notify_end_of_interrupt(InterruptIndex::Timer.into());
};
}
/// Same purpose as [`breakpoint_handler`] but for real-time controller interruptions
extern "x86-interrupt" fn rtc_interrupt_handler(_stack_frame: InterruptStackFrame) {
LAST_RTC_UPDATE.store(pit::ticks(), Ordering::Relaxed);
CMOS.notify_end_of_interrupt();
// SAFETY: RTC index is the same everywhere it should
unsafe {
PICS.lock().notify_end_of_interrupt(InterruptIndex::Rtc.into());
};
}
/// Same purpose as [`breakpoint_handler`] but for real-time controller interruptions
extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStackFrame) {
// SAFETY: Keyboard index is the same everywhere it should
PICS.lock().notify_end_of_interrupt(InterruptIndex::Keyboard.into());
/// Primary ATA interrupt handler
extern "x86-interrupt" fn primary_ata_handler(_stack_frame: InterruptStackFrame) {
// SAFETY: ATA primary index is the standard one
unsafe {
PICS.lock().notify_end_of_interrupt(InterruptIndex::AtaPrimary.into());
};
}
/// Secondary ATA interrupt handler
extern "x86-interrupt" fn secondary_ata_handler(_stack_frame: InterruptStackFrame) {
// SAFETY: ATA secondary index is the standard one
unsafe {
PICS.lock().notify_end_of_interrupt(InterruptIndex::AtaSecondary.into());
};
}