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
use core::arch::global_asm;
use core::cell::LazyCell;
use core::usize;

use bitfield_struct::bitfield;

use crate::arch::gdt::Ring;
use crate::arch::int::idt::*;
use crate::arch::{cpu, int};

use crate::arch::regs::Cr2;
use crate::device::KEYBOARD;

use super::super::gdt::GDT;
use super::epilog::Epilog;
use super::guard::GUARD;
use super::Vector;

/// The global interrupt descriptor table configures interrupt service routines
/// for interrupt vectors.
static mut IDT: LazyCell<InterruptDescriptorTable> = LazyCell::new(|| {
    // Initialize the global interrupt descriptor table.
    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);

    let opt = idt[Vector::Syscall as _].set_handler_fn(syscall_trampoline);
    opt.set_privilege_level(Ring::User);

    idt
});

/// Load the interrupt descriptor table on this core.
pub fn load() {
    unsafe { 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);
}

/// Invalid memory accesses with segmentation
extern "x86-interrupt" fn general_protection_fault(stack: InterruptStack, error: u32) {
    #[bitfield(u32)]
    struct SelectorError {
        external: bool,
        #[bits(2)]
        table: u8,
        #[bits(13)]
        index: usize,
        #[bits(16)]
        __: (),
    }

    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 *const u8;
    panic!("PAGE FAULT {error:?} to {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(Epilog::Timer);
}

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

    if let Some(key) = hit {
        GUARD.relay(Epilog::Keyboard { key });
    }
}

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

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

int_wrapper!(syscall, syscall_trampoline);

#[no_mangle]
pub extern "C" fn syscall(_context: &mut InterruptContext) {
    todo!("A1: Syscalls")
}