Skip to content
Snippets Groups Projects
handler.rs 4.82 KiB
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
#[cfg(not(test))]
extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) {
pigeonmoelleux's avatar
pigeonmoelleux committed
    use crate::println;

    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) {
pigeonmoelleux's avatar
pigeonmoelleux committed
    KeyQueue::handle_interrupt();
    // SAFETY: Keyboard index is the same everywhere it should
    unsafe {
pigeonmoelleux's avatar
pigeonmoelleux committed
        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());
    };
}

#[cfg(test)]
mod test {
    use x86_64::instructions::interrupts;

    #[test_case]
    fn interruption() {
        interrupts::int3();
    }
}