1 /*
2 * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7 #include "interrupts.h"
8
9 #include <stdio.h>
10 #include <string.h>
11
12 #include <algorithm>
13
14 #include <KernelExport.h>
15
16 #include <boot/platform.h>
17 #include <boot/platform/generic/text_console.h>
18
19 #include <arch_cpu.h>
20 #include <arch/x86/descriptors.h>
21
22 #include "debug.h"
23 #include "keyboard.h"
24
25
26 //#define TRACE_INTERRUPTS
27 #ifdef TRACE_INTERRUPTS
28 # define TRACE(x...) dprintf(x)
29 #else
30 # define TRACE(x...) ;
31 #endif
32
33
34 // interrupt function prototypes
35 #define INTERRUPT_FUNCTION(vector) \
36 extern "C" void interrupt_function##vector();
37 INTERRUPT_FUNCTIONS()
38 #undef INTERRUPT_FUNCTION
39
40 #define INTERRUPT_FUNCTION(vector) \
41 extern "C" void exception_interrupt_function##vector();
42 INTERRUPT_FUNCTIONS()
43 #undef INTERRUPT_FUNCTION
44
45
46 static const char *kInterruptNames[DEBUG_IDT_SLOT_COUNT] = {
47 /* 0 */ "Divide Error Exception",
48 /* 1 */ "Debug Exception",
49 /* 2 */ "NMI Interrupt",
50 /* 3 */ "Breakpoint Exception",
51 /* 4 */ "Overflow Exception",
52 /* 5 */ "BOUND Range Exceeded Exception",
53 /* 6 */ "Invalid Opcode Exception",
54 /* 7 */ "Device Not Available Exception",
55 /* 8 */ "Double Fault Exception",
56 /* 9 */ "Coprocessor Segment Overrun",
57 /* 10 */ "Invalid TSS Exception",
58 /* 11 */ "Segment Not Present",
59 /* 12 */ "Stack Fault Exception",
60 /* 13 */ "General Protection Exception",
61 /* 14 */ "Page-Fault Exception",
62 /* 15 */ "-",
63 /* 16 */ "x87 FPU Floating-Point Error",
64 /* 17 */ "Alignment Check Exception",
65 /* 18 */ "Machine-Check Exception",
66 /* 19 */ "SIMD Floating-Point Exception",
67 };
68
69
70 struct interrupt_frame {
71 uint32 vector;
72 uint32 edi;
73 uint32 esi;
74 uint32 ebp;
75 uint32 esp;
76 uint32 ebx;
77 uint32 edx;
78 uint32 ecx;
79 uint32 eax;
80 uint32 error_code;
81 uint32 eip;
82 uint32 cs;
83 uint32 eflags;
84 };
85
86
87 static interrupt_descriptor sDebugIDT[DEBUG_IDT_SLOT_COUNT];
88
89 static gdt_idt_descr sBIOSIDTDescriptor;
90 static gdt_idt_descr sDebugIDTDescriptor = { sizeof(sDebugIDT) - 1, sDebugIDT };
91
92
93 static void
set_interrupt_gate(int n,void (* function)())94 set_interrupt_gate(int n, void (*function)())
95 {
96 if (n >= DEBUG_IDT_SLOT_COUNT)
97 return;
98
99 sDebugIDT[n].a
100 = (KERNEL_CODE_SELECTOR << 16) | (0x0000ffff & (addr_t)function);
101 sDebugIDT[n].b = (0xffff0000 & (addr_t)function) | 0x8e00;
102 }
103
104
105 static void
set_idt(const gdt_idt_descr & idtDescriptor)106 set_idt(const gdt_idt_descr& idtDescriptor)
107 {
108 asm("lidt %0;" : : "m" (idtDescriptor));
109 }
110
111
112 extern "C" void
handle_exception_exception()113 handle_exception_exception()
114 {
115 while (true);
116 }
117
118
119 extern "C" void
handle_exception(interrupt_frame frame)120 handle_exception(interrupt_frame frame)
121 {
122 // Before doing anything else, set the interrupt gates so that
123 // handle_exception_exception() is called. This way we may avoid
124 // triple-faulting, if e.g. the stack is messed up and the user has at
125 // least the already printed output.
126 #define INTERRUPT_FUNCTION(vector) \
127 set_interrupt_gate(vector, &exception_interrupt_function##vector);
128 INTERRUPT_FUNCTIONS()
129 #undef INTERRUPT_FUNCTION
130
131 // If the console is already/still initialized, clear the screen and prepare
132 // for output.
133 if (stdout != NULL) {
134 console_set_color(RED, BLACK);
135 console_clear_screen();
136 console_set_cursor(0, 0);
137 console_hide_cursor();
138 }
139
140 // print exception name
141 if (frame.vector < DEBUG_IDT_SLOT_COUNT)
142 kprintf("%s", kInterruptNames[frame.vector]);
143 else
144 kprintf("Unknown exception %" B_PRIu32, frame.vector);
145
146 // additional info for page fault
147 if (frame.vector == 14) {
148 uint32 cr2;
149 asm("movl %%cr2, %0" : "=r"(cr2));
150 kprintf(": %s fault at address: %#" B_PRIx32,
151 (frame.error_code & 0x2) != 0 ? "write" : "read", cr2);
152 }
153
154 kprintf("\n");
155
156 // print greeting and registers
157 kprintf("Welcome to Boot Loader Death Land!\n");
158 kprintf("\n");
159
160 #define REG(name) " " #name " %-#10" B_PRIx32
161
162 kprintf(REG(eax) " " REG(ebx) " " REG(ecx) " " REG(edx) "\n",
163 frame.eax, frame.ebx, frame.ecx, frame.edx);
164 kprintf(REG(esi) " " REG(edi) " " REG(ebp) " " REG(esp) "\n",
165 frame.esi, frame.edi, frame.ebp, frame.esp);
166 kprintf(REG(eip) REG(eflags) "\n", frame.eip, frame.eflags);
167
168 #undef REG
169
170 // print stack trace (max 10 frames)
171 kprintf("\n frame return address\n");
172
173 struct x86_stack_frame {
174 x86_stack_frame* next;
175 void* return_address;
176 };
177
178 x86_stack_frame* stackFrame = (x86_stack_frame*)frame.ebp;
179 void* instructionPointer = (void*)frame.eip;
180
181 for (int32 i = 0; i < 10 && stackFrame != NULL; i++) {
182 kprintf(" %p %p\n", stackFrame, instructionPointer);
183
184 instructionPointer = stackFrame->return_address;
185 stackFrame = stackFrame->next;
186 }
187
188 if (stdout != NULL) {
189 kprintf("\nPress a key to reboot.\n");
190 clear_key_buffer();
191 wait_for_key();
192 platform_exit();
193 }
194
195 while (true);
196 }
197
198
199 void
interrupts_init()200 interrupts_init()
201 {
202 // get the IDT
203 asm("sidt %0;" : : "m" (sBIOSIDTDescriptor));
204
205 TRACE("IDT: base: %p, limit: %u\n", sBIOSIDTDescriptor.base,
206 sBIOSIDTDescriptor.limit);
207
208 // set interrupt gates
209 #define INTERRUPT_FUNCTION(vector) \
210 set_interrupt_gate(vector, &interrupt_function##vector);
211 INTERRUPT_FUNCTIONS()
212 #undef INTERRUPT_FUNCTION
213
214 // set the debug IDT
215 set_debug_idt();
216 }
217
218
219 void
restore_bios_idt()220 restore_bios_idt()
221 {
222 set_idt(sBIOSIDTDescriptor);
223 }
224
225
226 void
set_debug_idt()227 set_debug_idt()
228 {
229 set_idt(sDebugIDTDescriptor);
230 }
231
232