xref: /haiku/src/system/boot/platform/bios_ia32/interrupts.cpp (revision 922e7ba1f3228e6f28db69b0ded8f86eb32dea17)
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 <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 struct interrupt_descriptor {
87 	uint32 a;
88 	uint32 b;
89 };
90 
91 
92 static interrupt_descriptor sDebugIDT[DEBUG_IDT_SLOT_COUNT];
93 
94 static gdt_idt_descr sBIOSIDTDescriptor;
95 static gdt_idt_descr sDebugIDTDescriptor = { sizeof(sDebugIDT) - 1, sDebugIDT };
96 
97 
98 static void
99 set_interrupt_gate(int n, void (*function)())
100 {
101 	if (n >= DEBUG_IDT_SLOT_COUNT)
102 		return;
103 
104 	sDebugIDT[n].a = (KERNEL_CODE_SEG << 16) | (0x0000ffff & (addr_t)function);
105 	sDebugIDT[n].b = (0xffff0000 & (addr_t)function) | 0x8e00;
106 }
107 
108 
109 static void
110 set_idt(const gdt_idt_descr& idtDescriptor)
111 {
112 	asm("lidt	%0;" : : "m" (idtDescriptor));
113 }
114 
115 
116 extern "C" void
117 handle_exception_exception()
118 {
119 	while (true);
120 }
121 
122 
123 extern "C" void
124 handle_exception(interrupt_frame frame)
125 {
126 	// Before doing anything else, set the interrupt gates so that
127 	// handle_exception_exception() is called. This way we may avoid
128 	// triple-faulting, if e.g. the stack is messed up and the user has at
129 	// least the already printed output.
130 	#define INTERRUPT_FUNCTION(vector) \
131 		set_interrupt_gate(vector, &exception_interrupt_function##vector);
132 	INTERRUPT_FUNCTIONS()
133 	#undef INTERRUPT_FUNCTION
134 
135 	// If the console is already/still initialized, clear the screen and prepare
136 	// for output.
137 	if (stdout != NULL) {
138 		console_set_color(RED, BLACK);
139 		console_clear_screen();
140 		console_set_cursor(0, 0);
141 		console_hide_cursor();
142 	}
143 
144 	// print exception name
145 	if (frame.vector < DEBUG_IDT_SLOT_COUNT)
146 		kprintf("%s", kInterruptNames[frame.vector]);
147 	else
148 		kprintf("Unknown exception %" B_PRIu32, frame.vector);
149 
150 	// additional info for page fault
151 	if (frame.vector == 14) {
152 		uint32 cr2;
153 		asm("movl %%cr2, %0" : "=r"(cr2));
154 		kprintf(": %s fault at address: %#" B_PRIx32,
155 			(frame.error_code & 0x2) != 0 ? "write" : "read", cr2);
156 	}
157 
158 	kprintf("\n");
159 
160 	// print greeting and registers
161 	kprintf("Welcome to Boot Loader Death Land!\n");
162 	kprintf("\n");
163 
164 	#define REG(name)	" " #name " %-#10" B_PRIx32
165 
166 	kprintf(REG(eax) "   " REG(ebx) "    " REG(ecx) " " REG(edx) "\n",
167 		frame.eax, frame.ebx, frame.ecx, frame.edx);
168 	kprintf(REG(esi) "   " REG(edi) "    " REG(ebp) " " REG(esp) "\n",
169 		frame.esi, frame.edi, frame.ebp, frame.esp);
170 	kprintf(REG(eip) REG(eflags) "\n", frame.eip, frame.eflags);
171 
172 	#undef REG
173 
174 	// print stack trace (max 10 frames)
175 	kprintf("\n       frame    return address\n");
176 
177 	struct x86_stack_frame {
178 		x86_stack_frame*	next;
179 		void*				return_address;
180 	};
181 
182 	x86_stack_frame* stackFrame = (x86_stack_frame*)frame.ebp;
183 	void* instructionPointer = (void*)frame.eip;
184 
185 	for (int32 i = 0; i < 10 && stackFrame != NULL; i++) {
186 		kprintf("  %p        %p\n", stackFrame, instructionPointer);
187 
188 		instructionPointer = stackFrame->return_address;
189 		stackFrame = stackFrame->next;
190 	}
191 
192 	if (stdout != NULL) {
193 		kprintf("\nPress a key to reboot.\n");
194 		clear_key_buffer();
195 		wait_for_key();
196 		platform_exit();
197 	}
198 
199 	while (true);
200 }
201 
202 
203 void
204 interrupts_init()
205 {
206 	// get the IDT
207 	asm("sidt	%0;" : : "m" (sBIOSIDTDescriptor));
208 
209 	TRACE("IDT: base: %p, limit: %u\n", sBIOSIDTDescriptor.base,
210 		sBIOSIDTDescriptor.limit);
211 
212 	// set interrupt gates
213 	#define INTERRUPT_FUNCTION(vector) \
214 		set_interrupt_gate(vector, &interrupt_function##vector);
215 	INTERRUPT_FUNCTIONS()
216 	#undef INTERRUPT_FUNCTION
217 
218 	// set the debug IDT
219 	set_debug_idt();
220 }
221 
222 
223 void
224 restore_bios_idt()
225 {
226 	set_idt(sBIOSIDTDescriptor);
227 }
228 
229 
230 void
231 set_debug_idt()
232 {
233 	set_idt(sDebugIDTDescriptor);
234 }
235 
236 
237 void
238 interrupts_init_kernel_idt(void* idt, size_t idtSize)
239 {
240 	// clear it but copy the descriptors we've set up for the exceptions
241 	memset(idt, 0, idtSize);
242 	memcpy(idt, sDebugIDT, sizeof(sDebugIDT));
243 
244 	// load the idt
245 	gdt_idt_descr idtDescriptor;
246 	idtDescriptor.limit = idtSize - 1;
247 	idtDescriptor.base = idt;
248 	set_idt(idtDescriptor);
249 }
250