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