* Copyright 2018, Jérôme Duval, jerome.duval@gmail.com.
* Copyright 2008-2011, Michael Lotz, mmlr@mlotz.ch.
* Copyright 2010, Clemens Zeidler, haiku@clemens-zeidler.de.
* Copyright 2009-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2002-2010, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*
* Copyright 2001, Travis Geiselbrecht. All rights reserved.
* Distributed under the terms of the NewOS License.
*/
#include <cpu.h>
#include <interrupts.h>
#include <kscheduler.h>
#include <team.h>
#include <thread.h>
#include <util/AutoLock.h>
#include <vm/vm.h>
#include <vm/vm_priv.h>
#include <arch/cpu.h>
#include <arch/int.h>
#include <arch/x86/apic.h>
#include <arch/x86/descriptors.h>
#include <arch/x86/msi.h>
#include <arch/x86/msi_priv.h>
#include <fenv.h>
#include <stdio.h>
#include <arch/x86/ioapic.h>
#include <arch/x86/pic.h>
#ifdef TRACE_ARCH_INT
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
static irq_source sVectorSources[NUM_IO_VECTORS];
static const char *kInterruptNames[] = {
"Divide Error Exception",
"Debug Exception",
"NMI Interrupt",
"Breakpoint Exception",
"Overflow Exception",
"BOUND Range Exceeded Exception",
"Invalid Opcode Exception",
"Device Not Available Exception",
"Double Fault Exception",
"Coprocessor Segment Overrun",
"Invalid TSS Exception",
"Segment Not Present",
"Stack Fault Exception",
"General Protection Exception",
"Page-Fault Exception",
"-",
"x87 FPU Floating-Point Error",
"Alignment Check Exception",
"Machine-Check Exception",
"SIMD Floating-Point Exception",
};
static const int kInterruptNameCount = 20;
static const interrupt_controller* sCurrentPIC = NULL;
static const char*
exception_name(int number, char* buffer, int32 bufferSize)
{
if (number >= 0 && number < kInterruptNameCount)
return kInterruptNames[number];
snprintf(buffer, bufferSize, "exception %d", number);
return buffer;
}
void
x86_invalid_exception(iframe* frame)
{
Thread* thread = thread_get_current_thread();
char name[32];
panic("unhandled trap 0x%lx (%s) at ip 0x%lx, thread %" B_PRId32 "!\n",
(long unsigned int)frame->vector,
exception_name(frame->vector, name, sizeof(name)),
(long unsigned int)frame->ip, thread ? thread->id : -1);
}
void
x86_fatal_exception(iframe* frame)
{
char name[32];
panic("Fatal exception \"%s\" occurred! Error code: 0x%lx\n",
exception_name(frame->vector, name, sizeof(name)),
(long unsigned int)frame->error_code);
}
void
x86_unexpected_exception(iframe* frame)
{
debug_exception_type type;
uint32 signalNumber;
int32 signalCode;
addr_t signalAddress = 0;
int32 signalError = B_ERROR;
switch (frame->vector) {
case 0:
type = B_DIVIDE_ERROR;
signalNumber = SIGFPE;
signalCode = FPE_INTDIV;
signalAddress = frame->ip;
break;
case 4:
type = B_OVERFLOW_EXCEPTION;
signalNumber = SIGFPE;
signalCode = FPE_INTOVF;
signalAddress = frame->ip;
break;
case 5:
type = B_BOUNDS_CHECK_EXCEPTION;
signalNumber = SIGTRAP;
signalCode = SI_USER;
break;
case 6:
type = B_INVALID_OPCODE_EXCEPTION;
signalNumber = SIGILL;
signalCode = ILL_ILLOPC;
signalAddress = frame->ip;
break;
case 12:
type = B_STACK_FAULT;
signalNumber = SIGBUS;
signalCode = BUS_ADRERR;
signalAddress = frame->ip;
break;
case 13:
type = B_GENERAL_PROTECTION_FAULT;
signalNumber = SIGILL;
signalCode = ILL_PRVOPC;
signalAddress = frame->ip;
break;
case 16:
case 19:
{
type = B_FLOATING_POINT_EXCEPTION;
signalNumber = SIGFPE;
signalCode = FPE_FLTINV;
signalAddress = frame->ip;
uint32 status = 0;
if (frame->vector == 19) {
__stmxcsr(&status);
} else {
uint16 fsw = 0;
__fnstsw(&fsw);
status = fsw;
}
if ((status & FE_INVALID) != 0)
signalCode = FPE_FLTINV;
else if ((status & FE_DENORMAL) != 0)
signalCode = FPE_FLTUND;
else if ((status & FE_DIVBYZERO) != 0)
signalCode = FPE_FLTDIV;
else if ((status & FE_OVERFLOW) != 0)
signalCode = FPE_FLTOVF;
else if ((status & FE_UNDERFLOW) != 0)
signalCode = FPE_FLTUND;
else if ((status & FE_INEXACT) != 0)
signalCode = FPE_FLTRES;
break;
}
case 17:
type = B_ALIGNMENT_EXCEPTION;
signalNumber = SIGBUS;
signalCode = BUS_ADRALN;
signalError = EFAULT;
break;
default:
x86_invalid_exception(frame);
return;
}
if (IFRAME_IS_USER(frame)) {
struct sigaction action;
Thread* thread = thread_get_current_thread();
enable_interrupts();
if ((sigaction(signalNumber, NULL, &action) == 0
&& action.sa_handler != SIG_DFL
&& action.sa_handler != SIG_IGN)
|| user_debug_exception_occurred(type, signalNumber)) {
Signal signal(signalNumber, signalCode, signalError,
thread->team->id);
signal.SetAddress((void*)signalAddress);
send_signal_to_thread(thread, signal, 0);
}
} else {
char name[32];
panic("Unexpected exception \"%s\" occurred in kernel mode! "
"Error code: 0x%lx\n",
exception_name(frame->vector, name, sizeof(name)),
(long unsigned int)(frame->error_code));
}
}
void
x86_hardware_interrupt(struct iframe* frame)
{
int32 vector = frame->vector - ARCH_INTERRUPT_BASE;
bool levelTriggered = false;
Thread* thread = thread_get_current_thread();
if (sCurrentPIC->is_spurious_interrupt(vector)) {
TRACE(("got spurious interrupt at vector %ld\n", vector));
return;
}
levelTriggered = sCurrentPIC->is_level_triggered_interrupt(vector);
if (!levelTriggered) {
if (!sCurrentPIC->end_of_interrupt(vector))
apic_end_of_interrupt();
}
io_interrupt_handler(vector, levelTriggered);
if (levelTriggered) {
if (!sCurrentPIC->end_of_interrupt(vector))
apic_end_of_interrupt();
}
cpu_status state = disable_interrupts();
if (thread->post_interrupt_callback != NULL) {
void (*callback)(void*) = thread->post_interrupt_callback;
void* data = thread->post_interrupt_data;
thread->post_interrupt_callback = NULL;
thread->post_interrupt_data = NULL;
restore_interrupts(state);
callback(data);
} else if (thread->cpu->invoke_scheduler) {
SpinLocker schedulerLocker(thread->scheduler_lock);
scheduler_reschedule(B_THREAD_READY);
schedulerLocker.Unlock();
restore_interrupts(state);
}
}
void
x86_page_fault_exception(struct iframe* frame)
{
Thread* thread = thread_get_current_thread();
addr_t cr2 = x86_read_cr2();
addr_t newip;
if (debug_debugger_running()) {
if (thread != NULL) {
cpu_ent* cpu = &gCPU[smp_get_current_cpu()];
if (cpu->fault_handler != 0) {
debug_set_page_fault_info(cr2, frame->ip,
(frame->error_code & PGFAULT_W) != 0
? DEBUG_PAGE_FAULT_WRITE : 0);
frame->ip = cpu->fault_handler;
frame->bp = cpu->fault_handler_stack_pointer;
return;
}
if (thread->fault_handler != 0) {
kprintf("ERROR: thread::fault_handler used in kernel "
"debugger!\n");
debug_set_page_fault_info(cr2, frame->ip,
(frame->error_code & PGFAULT_W) != 0
? DEBUG_PAGE_FAULT_WRITE : 0);
frame->ip = reinterpret_cast<uintptr_t>(thread->fault_handler);
return;
}
}
panic("page fault in debugger without fault handler! Touching "
"address %p from ip %p\n", (void*)cr2, (void*)frame->ip);
return;
} else if (!IFRAME_IS_USER(frame)
&& (frame->error_code & PGFAULT_I) != 0
&& (x86_read_cr4() & IA32_CR4_SMEP) != 0) {
panic("SMEP violation user-mapped address %p touched from kernel %p\n",
(void*)cr2, (void*)frame->ip);
} else if ((frame->flags & X86_EFLAGS_ALIGNMENT_CHECK) == 0
&& !IFRAME_IS_USER(frame)
&& (frame->error_code & PGFAULT_P) != 0
&& (x86_read_cr4() & IA32_CR4_SMAP) != 0) {
panic("SMAP violation user-mapped address %p touched from kernel %p\n",
(void*)cr2, (void*)frame->ip);
} else if ((frame->flags & X86_EFLAGS_INTERRUPT) == 0) {
if (thread != NULL && thread->fault_handler != 0) {
uintptr_t handler
= reinterpret_cast<uintptr_t>(thread->fault_handler);
if (frame->ip != handler) {
frame->ip = handler;
return;
}
panic("page fault, interrupts disabled, fault handler loop. "
"Touching address %p from ip %p\n", (void*)cr2,
(void*)frame->ip);
}
panic("page fault, but interrupts were disabled. Touching address "
"%p from ip %p\n", (void*)cr2, (void*)frame->ip);
return;
} else if (thread != NULL && thread->page_faults_allowed < 1) {
panic("page fault not allowed at this place. Touching address "
"%p from ip %p\n", (void*)cr2, (void*)frame->ip);
return;
}
enable_interrupts();
vm_page_fault(cr2, frame->ip,
(frame->error_code & PGFAULT_W) != 0,
(frame->error_code & PGFAULT_I) != 0,
IFRAME_IS_USER(frame),
&newip);
if (newip != 0) {
frame->ip = newip;
}
}
void
x86_set_irq_source(int32 irq, irq_source source)
{
sVectorSources[irq] = source;
}
void
arch_int_enable_io_interrupt(int32 irq)
{
sCurrentPIC->enable_io_interrupt(irq);
}
void
arch_int_disable_io_interrupt(int32 irq)
{
sCurrentPIC->disable_io_interrupt(irq);
}
void
arch_int_configure_io_interrupt(int32 irq, uint32 config)
{
sCurrentPIC->configure_io_interrupt(irq, config);
}
#undef arch_int_enable_interrupts
#undef arch_int_disable_interrupts
#undef arch_int_restore_interrupts
#undef arch_int_are_interrupts_enabled
void
arch_int_enable_interrupts(void)
{
arch_int_enable_interrupts_inline();
}
int
arch_int_disable_interrupts(void)
{
return arch_int_disable_interrupts_inline();
}
void
arch_int_restore_interrupts(int oldState)
{
arch_int_restore_interrupts_inline(oldState);
}
bool
arch_int_are_interrupts_enabled(void)
{
return arch_int_are_interrupts_enabled_inline();
}
int32
arch_int_assign_to_cpu(int32 irq, int32 cpu)
{
switch (sVectorSources[irq]) {
case IRQ_SOURCE_IOAPIC:
if (sCurrentPIC->assign_interrupt_to_cpu != NULL)
sCurrentPIC->assign_interrupt_to_cpu(irq, cpu);
break;
case IRQ_SOURCE_MSI:
msi_assign_interrupt_to_cpu(irq, cpu);
break;
default:
break;
}
return cpu;
}
status_t
arch_int_init(kernel_args* args)
{
pic_init();
return B_OK;
}
status_t
arch_int_init_post_vm(kernel_args* args)
{
apic_init(args);
return B_OK;
}
status_t
arch_int_init_io(kernel_args* args)
{
msi_init(args);
ioapic_preinit(args);
return B_OK;
}
status_t
arch_int_init_post_device_manager(kernel_args* args)
{
return B_OK;
}
void
arch_int_set_interrupt_controller(const interrupt_controller& controller)
{
sCurrentPIC = &controller;
}