rstubs/interrupts/
idt.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
use core::arch::naked_asm;

use bitfield_struct::bitfield;
use pc_keyboard::DecodedKey;

use super::super::gdt::GDT;
use super::epilogue::Epilogue;
use super::guard::GUARD;
use super::Vector;
use crate::arch::int::idt::*;
use crate::arch::regs::Cr2;
use crate::arch::{cpu, int};
use crate::device::KEYBOARD;
use crate::util::Once;

/// The global interrupt descriptor table configures interrupt service routines
/// for interrupt vectors.
static IDT: Once<InterruptDescriptorTable> = Once::new();

/// Load the interrupt descriptor table on this core.
pub fn load() {
    IDT.get_or_init(|| {
        // Initialize the global interrupt descriptor table only once!
        let mut idt = InterruptDescriptorTable::new();
        idt.double_fault.set_handler_fn(double_fault);
        idt.general_protection_fault
            .set_handler_fn(general_protection_fault);
        idt.page_fault.set_handler_fn(page_fault);
        idt.invalid_tss.set_handler_fn(invalid_tss);

        idt[Vector::Timer as _].set_handler_fn(timer);
        idt[Vector::Keyboard as _].set_handler_fn(keyboard);
        idt[Vector::WakeUp as _].set_handler_fn(wake_up);
        idt[Vector::Assassin as _].set_handler_fn(assassin);
        idt[Vector::Panic as _].set_handler_fn(panic);

        idt
    })
    .activate();
}

/// A double fault is a special exception that occurs when the CPU fails to
/// invoke an exception handler.
/// By handling this we prevent a system reset.
extern "x86-interrupt" fn double_fault(stack: InterruptStack, error: u32) -> ! {
    panic!("DOUBLE FAULT {error:x}\n{:#x?}", stack);
}

/// Invalid task state segment register
extern "x86-interrupt" fn invalid_tss(stack: InterruptStack, error: u32) {
    panic!("TSS {error:x}\n{:#x?}", stack);
}

#[bitfield(u32)]
struct SelectorError {
    external: bool,
    #[bits(2)]
    table: u8,
    #[bits(13)]
    index: usize,
    #[bits(16)]
    __: (),
}

/// Invalid memory accesses with segmentation
extern "x86-interrupt" fn general_protection_fault(stack: InterruptStack, error: u32) {
    unsafe {
        serial!("{:?}", GUARD.read().scheduler.active());
        serial!("GDT: {:x?}", GDT.0.map(u64::from));
    }
    panic!(
        "GENERAL PROTECTION FAULT {:?}\n{stack:#x?}",
        SelectorError(error),
    );
}

/// Invalid memory accesses with paging
extern "x86-interrupt" fn page_fault(stack: InterruptStack, error: PageFaultError) {
    // The accessed virtual address is stored in the CR2 register
    let vaddr = Cr2::read().0 as *mut u8;
    panic!("PAGE FAULT {error:?} at {vaddr:?} @ {stack:#x?}");
}

/// Handle the custom panic interrupt
extern "x86-interrupt" fn panic(stack: InterruptStack) {
    panic!("PANIC\n{:#?}", stack);
}

/// Handle the custom timer interrupt
extern "x86-interrupt" fn timer(_stack: InterruptStack) {
    super::eoi(Vector::Timer as _);

    GUARD.relay(Epilogue::Timer);
}

/// Handle the custom keyboard interrupt
extern "x86-interrupt" fn keyboard(_stack: InterruptStack) {
    let event = unsafe { KEYBOARD.event() };
    super::eoi(Vector::Keyboard as _);

    if let Some(DecodedKey::Unicode(key)) = event {
        GUARD.relay(Epilogue::Keyboard { key });
    }
}

/// Handle the custom assassin interrupt
extern "x86-interrupt" fn assassin(_stack: InterruptStack) {
    super::eoi(Vector::Assassin as _);
    debug!("{}: assassin", cpu::id());
    GUARD.relay(Epilogue::Assassin);
}

/// Handle the custom wake up interrupt
extern "x86-interrupt" fn wake_up(_stack: InterruptStack) {
    super::eoi(Vector::WakeUp as _);
    int::enable(true);
}

/// Additional registers saved on the stack for syscalls.
#[derive(Debug)]
pub struct SyscallStack {
    // <- TODO: BST B1 - Additional registers
    pub stack: InterruptStack,
}

/// Trampoline function that captures additional registers for syscalls.
#[naked]
#[no_mangle]
pub unsafe extern "x86-interrupt" fn syscall_trampoline(stack: InterruptStack) {
    // TODO: BST B1 - Save and restore registers
    naked_asm!(
        // save registers
        "push esp",
        "call {syscall}",
        "add esp, 4",
        // restore registers
        "iretd",
        syscall = sym syscall
    )
}

#[no_mangle]
pub extern "C" fn syscall(_context: &mut SyscallStack) {
    todo!("BST B1 - Implement syscalls")
}