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.
8 * Copyright 2001, Travis Geiselbrecht. All rights reserved.
9 * Distributed under the terms of the NewOS License.
15 #include <kscheduler.h>
18 #include <util/AutoLock.h>
20 #include <vm/vm_priv.h>
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>
32 // interrupt controllers
33 #include <arch/x86/ioapic.h>
34 #include <arch/x86/pic.h>
37 //#define TRACE_ARCH_INT
39 # define TRACE(x) dprintf x
45 static irq_source sVectorSources
[NUM_IO_VECTORS
];
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",
64 /* 16 */ "x87 FPU Floating-Point Error",
65 /* 17 */ "Alignment Check Exception",
66 /* 18 */ "Machine-Check Exception",
67 /* 19 */ "SIMD Floating-Point Exception",
69 static const int kInterruptNameCount
= 20;
71 static const interrupt_controller
* sCurrentPIC
= NULL
;
75 exception_name(int number
, char* buffer
, int32 bufferSize
)
77 if (number
>= 0 && number
< kInterruptNameCount
)
78 return kInterruptNames
[number
];
80 snprintf(buffer
, bufferSize
, "exception %d", number
);
86 x86_invalid_exception(iframe
* frame
)
88 Thread
* thread
= thread_get_current_thread();
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);
97 x86_fatal_exception(iframe
* frame
)
100 panic("Fatal exception \"%s\" occurred! Error code: 0x%lx\n",
101 exception_name(frame
->vector
, name
, sizeof(name
)), frame
->error_code
);
106 x86_unexpected_exception(iframe
* frame
)
108 debug_exception_type type
;
111 addr_t signalAddress
= 0;
112 int32 signalError
= B_ERROR
;
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
;
122 case 4: // Overflow Exception (#OF)
123 type
= B_OVERFLOW_EXCEPTION
;
124 signalNumber
= SIGFPE
;
125 signalCode
= FPE_INTOVF
;
126 signalAddress
= frame
->ip
;
129 case 5: // BOUND Range Exceeded Exception (#BR)
130 type
= B_BOUNDS_CHECK_EXCEPTION
;
131 signalNumber
= SIGTRAP
;
132 signalCode
= SI_USER
;
135 case 6: // Invalid Opcode Exception (#UD)
136 type
= B_INVALID_OPCODE_EXCEPTION
;
137 signalNumber
= SIGILL
;
138 signalCode
= ILL_ILLOPC
;
139 signalAddress
= frame
->ip
;
142 case 12: // Stack Fault (#SS)
143 type
= B_STACK_FAULT
;
144 signalNumber
= SIGBUS
;
145 signalCode
= BUS_ADRERR
;
146 signalAddress
= frame
->ip
;
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
;
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
162 signalAddress
= frame
->ip
;
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
;
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
;
183 x86_invalid_exception(frame
);
187 if (IFRAME_IS_USER(frame
)) {
188 struct sigaction action
;
189 Thread
* thread
= thread_get_current_thread();
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
,
201 signal
.SetAddress((void*)signalAddress
);
202 send_signal_to_thread(thread
, signal
, 0);
206 panic("Unexpected exception \"%s\" occurred in kernel mode! "
207 "Error code: 0x%lx\n",
208 exception_name(frame
->vector
, name
, sizeof(name
)),
215 x86_hardware_interrupt(struct iframe
* frame
)
217 int32 vector
= frame
->vector
- ARCH_INTERRUPT_BASE
;
218 bool levelTriggered
= false;
219 Thread
* thread
= thread_get_current_thread();
221 if (sCurrentPIC
->is_spurious_interrupt(vector
)) {
222 TRACE(("got spurious interrupt at vector %ld\n", vector
));
226 levelTriggered
= sCurrentPIC
->is_level_triggered_interrupt(vector
);
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();
235 int_io_interrupt_handler(vector
, levelTriggered
);
237 if (levelTriggered
) {
238 if (!sCurrentPIC
->end_of_interrupt(vector
))
239 apic_end_of_interrupt();
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
;
252 thread
->post_interrupt_callback
= NULL
;
253 thread
->post_interrupt_data
= NULL
;
255 restore_interrupts(state
);
263 x86_page_fault_exception(struct iframe
* frame
)
265 Thread
* thread
= thread_get_current_thread();
266 addr_t cr2
= x86_read_cr2();
269 if (debug_debugger_running()) {
270 // If this CPU or this thread has a fault handler, we're allowed to be
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
;
283 if (thread
->fault_handler
!= 0) {
284 kprintf("ERROR: thread::fault_handler used in kernel "
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
);
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
);
298 } else if ((frame
->flags
& 0x200) == 0) {
299 // interrupts disabled
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) {
307 = reinterpret_cast<uintptr_t>(thread
->fault_handler
);
308 if (frame
->ip
!= handler
) {
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
,
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
);
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
);
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
339 // the page fault handler wants us to modify the iframe to set the
340 // IP the cpu will return to this ip
347 x86_set_irq_source(int irq
, irq_source source
)
349 sVectorSources
[irq
] = source
;
357 arch_int_enable_io_interrupt(int irq
)
359 sCurrentPIC
->enable_io_interrupt(irq
);
364 arch_int_disable_io_interrupt(int irq
)
366 sCurrentPIC
->disable_io_interrupt(irq
);
371 arch_int_configure_io_interrupt(int irq
, uint32 config
)
373 sCurrentPIC
->configure_io_interrupt(irq
, config
);
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
384 arch_int_enable_interrupts(void)
386 arch_int_enable_interrupts_inline();
391 arch_int_disable_interrupts(void)
393 return arch_int_disable_interrupts_inline();
398 arch_int_restore_interrupts(int oldState
)
400 arch_int_restore_interrupts_inline(oldState
);
405 arch_int_are_interrupts_enabled(void)
407 return arch_int_are_interrupts_enabled_inline();
412 arch_int_assign_to_cpu(int32 irq
, int32 cpu
)
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
);
421 msi_assign_interrupt_to_cpu(irq
, cpu
);
431 arch_int_init(kernel_args
* args
)
433 // setup the standard programmable interrupt controller
440 arch_int_init_post_vm(kernel_args
* args
)
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
450 arch_int_init_io(kernel_args
* args
)
459 arch_int_init_post_device_manager(kernel_args
* args
)
466 arch_int_set_interrupt_controller(const interrupt_controller
& controller
)
468 sCurrentPIC
= &controller
;