xref: /haiku/src/system/kernel/arch/x86/arch_int.cpp (revision 90ae2e54f6ccaca73c011a2aa4cdd660417108ad)
1 /*
2  * Copyright 2008-2011, Michael Lotz, mmlr@mlotz.ch.
3  * Copyright 2010, Clemens Zeidler, haiku@clemens-zeidler.de.
4  * Copyright 2009-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
5  * Copyright 2002-2010, Axel Dörfler, axeld@pinc-software.de.
6  * Distributed under the terms of the MIT License.
7  *
8  * Copyright 2001, Travis Geiselbrecht. All rights reserved.
9  * Distributed under the terms of the NewOS License.
10  */
11 
12 
13 #include <cpu.h>
14 #include <int.h>
15 #include <kscheduler.h>
16 #include <team.h>
17 #include <thread.h>
18 #include <util/AutoLock.h>
19 #include <vm/vm.h>
20 #include <vm/vm_priv.h>
21 
22 #include <arch/cpu.h>
23 #include <arch/int.h>
24 
25 #include <arch/x86/apic.h>
26 #include <arch/x86/descriptors.h>
27 #include <arch/x86/msi.h>
28 
29 #include <stdio.h>
30 
31 // interrupt controllers
32 #include <arch/x86/ioapic.h>
33 #include <arch/x86/pic.h>
34 
35 
36 //#define TRACE_ARCH_INT
37 #ifdef TRACE_ARCH_INT
38 #	define TRACE(x) dprintf x
39 #else
40 #	define TRACE(x) ;
41 #endif
42 
43 
44 static const char *kInterruptNames[] = {
45 	/*  0 */ "Divide Error Exception",
46 	/*  1 */ "Debug Exception",
47 	/*  2 */ "NMI Interrupt",
48 	/*  3 */ "Breakpoint Exception",
49 	/*  4 */ "Overflow Exception",
50 	/*  5 */ "BOUND Range Exceeded Exception",
51 	/*  6 */ "Invalid Opcode Exception",
52 	/*  7 */ "Device Not Available Exception",
53 	/*  8 */ "Double Fault Exception",
54 	/*  9 */ "Coprocessor Segment Overrun",
55 	/* 10 */ "Invalid TSS Exception",
56 	/* 11 */ "Segment Not Present",
57 	/* 12 */ "Stack Fault Exception",
58 	/* 13 */ "General Protection Exception",
59 	/* 14 */ "Page-Fault Exception",
60 	/* 15 */ "-",
61 	/* 16 */ "x87 FPU Floating-Point Error",
62 	/* 17 */ "Alignment Check Exception",
63 	/* 18 */ "Machine-Check Exception",
64 	/* 19 */ "SIMD Floating-Point Exception",
65 };
66 static const int kInterruptNameCount = 20;
67 
68 static const interrupt_controller* sCurrentPIC = NULL;
69 
70 
71 static const char*
72 exception_name(int number, char* buffer, int32 bufferSize)
73 {
74 	if (number >= 0 && number < kInterruptNameCount)
75 		return kInterruptNames[number];
76 
77 	snprintf(buffer, bufferSize, "exception %d", number);
78 	return buffer;
79 }
80 
81 
82 void
83 x86_invalid_exception(iframe* frame)
84 {
85 	Thread* thread = thread_get_current_thread();
86 	char name[32];
87 	panic("unhandled trap 0x%lx (%s) at ip 0x%lx, thread %" B_PRId32 "!\n",
88 		frame->vector, exception_name(frame->vector, name, sizeof(name)),
89 		frame->ip, thread ? thread->id : -1);
90 }
91 
92 
93 void
94 x86_fatal_exception(iframe* frame)
95 {
96 	char name[32];
97 	panic("Fatal exception \"%s\" occurred! Error code: 0x%lx\n",
98 		exception_name(frame->vector, name, sizeof(name)), frame->error_code);
99 }
100 
101 
102 void
103 x86_unexpected_exception(iframe* frame)
104 {
105 	debug_exception_type type;
106 	uint32 signalNumber;
107 	int32 signalCode;
108 	addr_t signalAddress = 0;
109 	int32 signalError = B_ERROR;
110 
111 	switch (frame->vector) {
112 		case 0:		// Divide Error Exception (#DE)
113 			type = B_DIVIDE_ERROR;
114 			signalNumber = SIGFPE;
115 			signalCode = FPE_INTDIV;
116 			signalAddress = frame->ip;
117 			break;
118 
119 		case 4:		// Overflow Exception (#OF)
120 			type = B_OVERFLOW_EXCEPTION;
121 			signalNumber = SIGFPE;
122 			signalCode = FPE_INTOVF;
123 			signalAddress = frame->ip;
124 			break;
125 
126 		case 5:		// BOUND Range Exceeded Exception (#BR)
127 			type = B_BOUNDS_CHECK_EXCEPTION;
128 			signalNumber = SIGTRAP;
129 			signalCode = SI_USER;
130 			break;
131 
132 		case 6:		// Invalid Opcode Exception (#UD)
133 			type = B_INVALID_OPCODE_EXCEPTION;
134 			signalNumber = SIGILL;
135 			signalCode = ILL_ILLOPC;
136 			signalAddress = frame->ip;
137 			break;
138 
139 		case 13: 	// General Protection Exception (#GP)
140 			type = B_GENERAL_PROTECTION_FAULT;
141 			signalNumber = SIGILL;
142 			signalCode = ILL_PRVOPC;	// or ILL_PRVREG
143 			signalAddress = frame->ip;
144 			break;
145 
146 		case 16: 	// x87 FPU Floating-Point Error (#MF)
147 			type = B_FLOATING_POINT_EXCEPTION;
148 			signalNumber = SIGFPE;
149 			signalCode = FPE_FLTDIV;
150 				// TODO: Determine the correct cause via the FPU status
151 				// register!
152 			signalAddress = frame->ip;
153 			break;
154 
155 		case 17: 	// Alignment Check Exception (#AC)
156 			type = B_ALIGNMENT_EXCEPTION;
157 			signalNumber = SIGBUS;
158 			signalCode = BUS_ADRALN;
159 			// TODO: Also get the address (from where?). Since we don't enable
160 			// alignment checking this exception should never happen, though.
161 			signalError = EFAULT;
162 			break;
163 
164 		case 19: 	// SIMD Floating-Point Exception (#XF)
165 			type = B_FLOATING_POINT_EXCEPTION;
166 			signalNumber = SIGFPE;
167 			signalCode = FPE_FLTDIV;
168 				// TODO: Determine the correct cause via the MXCSR register!
169 			signalAddress = frame->ip;
170 			break;
171 
172 		default:
173 			x86_invalid_exception(frame);
174 			return;
175 	}
176 
177 	if (IFRAME_IS_USER(frame)) {
178 		struct sigaction action;
179 		Thread* thread = thread_get_current_thread();
180 
181 		enable_interrupts();
182 
183 		// If the thread has a signal handler for the signal, we simply send it
184 		// the signal. Otherwise we notify the user debugger first.
185 		if ((sigaction(signalNumber, NULL, &action) == 0
186 				&& action.sa_handler != SIG_DFL
187 				&& action.sa_handler != SIG_IGN)
188 			|| user_debug_exception_occurred(type, signalNumber)) {
189 			Signal signal(signalNumber, signalCode, signalError,
190 				thread->team->id);
191 			signal.SetAddress((void*)signalAddress);
192 			send_signal_to_thread(thread, signal, 0);
193 		}
194 	} else {
195 		char name[32];
196 		panic("Unexpected exception \"%s\" occurred in kernel mode! "
197 			"Error code: 0x%lx\n",
198 			exception_name(frame->vector, name, sizeof(name)),
199 			frame->error_code);
200 	}
201 }
202 
203 
204 void
205 x86_hardware_interrupt(struct iframe* frame)
206 {
207 	int32 vector = frame->vector - ARCH_INTERRUPT_BASE;
208 	bool levelTriggered = false;
209 	Thread* thread = thread_get_current_thread();
210 
211 	if (sCurrentPIC->is_spurious_interrupt(vector)) {
212 		TRACE(("got spurious interrupt at vector %ld\n", vector));
213 		return;
214 	}
215 
216 	levelTriggered = sCurrentPIC->is_level_triggered_interrupt(vector);
217 
218 	if (!levelTriggered) {
219 		// if it's not handled by the current pic then it's an apic generated
220 		// interrupt like local interrupts, msi or ipi.
221 		if (!sCurrentPIC->end_of_interrupt(vector))
222 			apic_end_of_interrupt();
223 	}
224 
225 	int_io_interrupt_handler(vector, levelTriggered);
226 
227 	if (levelTriggered) {
228 		if (!sCurrentPIC->end_of_interrupt(vector))
229 			apic_end_of_interrupt();
230 	}
231 
232 	cpu_status state = disable_interrupts();
233 	if (thread->cpu->invoke_scheduler) {
234 		SpinLocker schedulerLocker(gSchedulerLock);
235 		scheduler_reschedule();
236 		schedulerLocker.Unlock();
237 		restore_interrupts(state);
238 	} else if (thread->post_interrupt_callback != NULL) {
239 		void (*callback)(void*) = thread->post_interrupt_callback;
240 		void* data = thread->post_interrupt_data;
241 
242 		thread->post_interrupt_callback = NULL;
243 		thread->post_interrupt_data = NULL;
244 
245 		restore_interrupts(state);
246 
247 		callback(data);
248 	}
249 }
250 
251 
252 void
253 x86_page_fault_exception(struct iframe* frame)
254 {
255 	Thread* thread = thread_get_current_thread();
256 	addr_t cr2 = x86_read_cr2();
257 	addr_t newip;
258 
259 	if (debug_debugger_running()) {
260 		// If this CPU or this thread has a fault handler, we're allowed to be
261 		// here.
262 		if (thread != NULL) {
263 			cpu_ent* cpu = &gCPU[smp_get_current_cpu()];
264 			if (cpu->fault_handler != 0) {
265 				debug_set_page_fault_info(cr2, frame->ip,
266 					(frame->error_code & 0x2) != 0
267 						? DEBUG_PAGE_FAULT_WRITE : 0);
268 				frame->ip = cpu->fault_handler;
269 				frame->bp = cpu->fault_handler_stack_pointer;
270 				return;
271 			}
272 
273 			if (thread->fault_handler != 0) {
274 				kprintf("ERROR: thread::fault_handler used in kernel "
275 					"debugger!\n");
276 				debug_set_page_fault_info(cr2, frame->ip,
277 					(frame->error_code & 0x2) != 0
278 						? DEBUG_PAGE_FAULT_WRITE : 0);
279 				frame->ip = thread->fault_handler;
280 				return;
281 			}
282 		}
283 
284 		// otherwise, not really
285 		panic("page fault in debugger without fault handler! Touching "
286 			"address %p from ip %p\n", (void*)cr2, (void*)frame->ip);
287 		return;
288 	} else if ((frame->flags & 0x200) == 0) {
289 		// interrupts disabled
290 
291 		// If a page fault handler is installed, we're allowed to be here.
292 		// TODO: Now we are generally allowing user_memcpy() with interrupts
293 		// disabled, which in most cases is a bug. We should add some thread
294 		// flag allowing to explicitly indicate that this handling is desired.
295 		if (thread && thread->fault_handler != 0) {
296 			if (frame->ip != thread->fault_handler) {
297 				frame->ip = thread->fault_handler;
298 				return;
299 			}
300 
301 			// The fault happened at the fault handler address. This is a
302 			// certain infinite loop.
303 			panic("page fault, interrupts disabled, fault handler loop. "
304 				"Touching address %p from ip %p\n", (void*)cr2,
305 				(void*)frame->ip);
306 		}
307 
308 		// If we are not running the kernel startup the page fault was not
309 		// allowed to happen and we must panic.
310 		panic("page fault, but interrupts were disabled. Touching address "
311 			"%p from ip %p\n", (void*)cr2, (void*)frame->ip);
312 		return;
313 	} else if (thread != NULL && thread->page_faults_allowed < 1) {
314 		panic("page fault not allowed at this place. Touching address "
315 			"%p from ip %p\n", (void*)cr2, (void*)frame->ip);
316 		return;
317 	}
318 
319 	enable_interrupts();
320 
321 	vm_page_fault(cr2, frame->ip,
322 		(frame->error_code & 0x2) != 0,	// write access
323 		(frame->error_code & 0x4) != 0,	// userland
324 		&newip);
325 	if (newip != 0) {
326 		// the page fault handler wants us to modify the iframe to set the
327 		// IP the cpu will return to this ip
328 		frame->ip = newip;
329 	}
330 }
331 
332 
333 // #pragma mark -
334 
335 
336 void
337 arch_int_enable_io_interrupt(int irq)
338 {
339 	sCurrentPIC->enable_io_interrupt(irq);
340 }
341 
342 
343 void
344 arch_int_disable_io_interrupt(int irq)
345 {
346 	sCurrentPIC->disable_io_interrupt(irq);
347 }
348 
349 
350 void
351 arch_int_configure_io_interrupt(int irq, uint32 config)
352 {
353 	sCurrentPIC->configure_io_interrupt(irq, config);
354 }
355 
356 
357 #undef arch_int_enable_interrupts
358 #undef arch_int_disable_interrupts
359 #undef arch_int_restore_interrupts
360 #undef arch_int_are_interrupts_enabled
361 
362 
363 void
364 arch_int_enable_interrupts(void)
365 {
366 	arch_int_enable_interrupts_inline();
367 }
368 
369 
370 int
371 arch_int_disable_interrupts(void)
372 {
373 	return arch_int_disable_interrupts_inline();
374 }
375 
376 
377 void
378 arch_int_restore_interrupts(int oldState)
379 {
380 	arch_int_restore_interrupts_inline(oldState);
381 }
382 
383 
384 bool
385 arch_int_are_interrupts_enabled(void)
386 {
387 	return arch_int_are_interrupts_enabled_inline();
388 }
389 
390 
391 status_t
392 arch_int_init(kernel_args* args)
393 {
394 	// setup the standard programmable interrupt controller
395 	pic_init();
396 	return B_OK;
397 }
398 
399 
400 status_t
401 arch_int_init_post_vm(kernel_args* args)
402 {
403 	// Always init the local apic as it can be used for timers even if we
404 	// don't end up using the io apic
405 	apic_init(args);
406 	return B_OK;
407 }
408 
409 
410 status_t
411 arch_int_init_io(kernel_args* args)
412 {
413 	msi_init();
414 	ioapic_init(args);
415 	return B_OK;
416 }
417 
418 
419 status_t
420 arch_int_init_post_device_manager(kernel_args* args)
421 {
422 	return B_OK;
423 }
424 
425 
426 void
427 arch_int_set_interrupt_controller(const interrupt_controller& controller)
428 {
429 	sCurrentPIC = &controller;
430 }
431