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