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
#![allow(unused)]

use core::arch::global_asm;

use bitfield_struct::bitfield;

use super::super::gdt::GDT;
use super::Vector;
use crate::arch::int::idt::*;
use crate::arch::regs::Cr2;
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::Panic as usize].set_handler_fn(panic);
        // TODO: BSB A2 - Add the other handlers

        // TODO: BST B1 - Add the syscall handler to the IDT

        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!("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) {
    todo!("BSB A5")
}

/// Handle the custom keyboard interrupt
extern "x86-interrupt" fn keyboard(_stack: InterruptStack) {
    todo!("BSB A2")
}

/// Handle the custom assassin interrupt
extern "x86-interrupt" fn assassin(_stack: InterruptStack) {
    todo!("BSB A5")
}

/// Handle the custom wake up interrupt
extern "x86-interrupt" fn wake_up(_stack: InterruptStack) {
    todo!("BSB A6")
}

/// Context for a syscall, containing all relevant registers.
#[derive(Debug)]
pub struct InterruptContext {
    pub eax: usize,
    pub ecx: usize,
    pub edx: usize,
    pub ebx: usize,
    pub edi: usize,
    pub stack: InterruptStack,
}

extern "x86-interrupt" {
    /// Wrapper that saves and provides access to additional registers.
    pub fn syscall_trampoline(stack: InterruptStack);
}
global_asm!(
    ".align 4
    .globl {trampoline}
    {trampoline}:
        call {handler}
        iretd", // TODO: BST B1 - Save and restore registers
    trampoline = sym syscall_trampoline,
    handler = sym syscall,
);

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