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();
37INTERRUPT_FUNCTIONS()
38#undef INTERRUPT_FUNCTION
39
40#define INTERRUPT_FUNCTION(vector) \
41	extern "C" void exception_interrupt_function##vector();
42INTERRUPT_FUNCTIONS()
43#undef INTERRUPT_FUNCTION
44
45
46static 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
70struct 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
87static interrupt_descriptor sDebugIDT[DEBUG_IDT_SLOT_COUNT];
88
89static gdt_idt_descr sBIOSIDTDescriptor;
90static gdt_idt_descr sDebugIDTDescriptor = { sizeof(sDebugIDT) - 1, sDebugIDT };
91
92
93static void
94set_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
105static void
106set_idt(const gdt_idt_descr& idtDescriptor)
107{
108	asm("lidt	%0;" : : "m" (idtDescriptor));
109}
110
111
112extern "C" void
113handle_exception_exception()
114{
115	while (true);
116}
117
118
119extern "C" void
120handle_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
199void
200interrupts_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
219void
220restore_bios_idt()
221{
222	set_idt(sBIOSIDTDescriptor);
223}
224
225
226void
227set_debug_idt()
228{
229	set_idt(sDebugIDTDescriptor);
230}
231
232