summaryrefslogtreecommitdiffhomepage
path: root/src/loader/interrupt.c
blob: d7508329d6346e8694f77369cf806277a891a563 (plain) (blame)
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
// MIT License, Copyright (c) 2021 Marvin Borner

#include <interrupt.h>
#include <log.h>
#include <panic.h>
#include <pic.h>

/**
 * IDT
 */

extern u32 interrupt_table[];
static struct idt_entry idt_entries[256] = { 0 };
static struct idt_ptr idt = { .size = sizeof(idt_entries) - 1, .base = idt_entries };

void idt_install(void)
{
	// Initialize IDT using handler offset, segment and type

	for (u8 i = 0; i < 3; i++)
		idt_entries[i] = IDT_ENTRY(interrupt_table[i], 0x18, INTERRUPT_GATE);

	idt_entries[3] = IDT_ENTRY(interrupt_table[3], 0x18, INTERRUPT_TRAP);
	idt_entries[4] = IDT_ENTRY(interrupt_table[4], 0x18, INTERRUPT_TRAP);

	for (u8 i = 5; i < 48; i++)
		idt_entries[i] = IDT_ENTRY(interrupt_table[i], 0x18, INTERRUPT_GATE);

	// Load table
	__asm__ volatile("lidt %0" : : "m"(idt) : "memory");
}

/**
 * Exception (trap) handling
 */

static const char *interrupt_trap_names[32] = {
	"Division By Zero",
	"Debug",
	"Non Maskable Interrupt",
	"Breakpoint",
	"Into Detected Overflow",
	"Out of Bounds",
	"Invalid Opcode",
	"No Coprocessor",

	"Double Fault",
	"Coprocessor Segment Overrun",
	"Bad TSS",
	"Segment Not Present",
	"Stack Fault",
	"General Protection Fault",
	"Page Fault",
	"Unknown Interrupt",

	"Coprocessor Fault",
	"Alignment Check",
	"Machine Check",
	"Reserved",
	"Reserved",
	"Reserved",
	"Reserved",
	"Reserved",

	"Reserved",
	"Reserved",
	"Reserved",
	"Reserved",
	"Reserved",
	"Reserved",
	"Reserved",
	"Reserved",
};

static void interrupt_trap_handler(struct interrupt_frame *frame)
{
	static u8 faulting = 0;
	faulting++;

	if (faulting == 2) {
		// Fall back to serial driver
		serial_print("Double fault, halting immediatly\n");
		while (1)
			__asm__ volatile("cli\nhlt");
	}

	log("%s Exception (code %x) at 0x%x!\n", interrupt_trap_names[frame->interrupt_no],
	    frame->err_code, frame->eip);

	while (1)
		__asm__ volatile("cli\nhlt");
}

/**
 * Event handling
 */

static void (*interrupt_event_handlers[16])(void) = { 0 };

void interrupt_event_handler_add(u32 interrupt_no, void (*handler)(void))
{
	assert(interrupt_no < COUNT(interrupt_event_handlers));
	interrupt_event_handlers[interrupt_no] = handler;
}

static u32 interrupt_event_handler(struct interrupt_frame *frame)
{
	u32 interrupt_no = frame->interrupt_no - 32;
	assert(interrupt_no < COUNT(interrupt_event_handlers));
	void (*handler)(void) = interrupt_event_handlers[interrupt_no];
	if (handler)
		handler();

	return (u32)frame;
}

/**
 * Universal handler
 */

u32 interrupt_handler(u32 esp);
u32 interrupt_handler(u32 esp)
{
	struct interrupt_frame *frame = (struct interrupt_frame *)esp;
	if (frame->interrupt_no < 32)
		interrupt_trap_handler(frame);
	else if (frame->interrupt_no < 48)
		esp = interrupt_event_handler(frame);
	else
		panic("Unknown interrupt\n");

	pic_ack(frame->interrupt_no);
	return esp;
}