* Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include "interrupts.h"
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <KernelExport.h>
#include <boot/platform.h>
#include <boot/platform/generic/text_console.h>
#include <arch_cpu.h>
#include <arch/x86/descriptors.h>
#include "debug.h"
#include "keyboard.h"
#ifdef TRACE_INTERRUPTS
# define TRACE(x...) dprintf(x)
#else
# define TRACE(x...) ;
#endif
#define INTERRUPT_FUNCTION(vector) \
extern "C" void interrupt_function##vector();
INTERRUPT_FUNCTIONS()
#undef INTERRUPT_FUNCTION
#define INTERRUPT_FUNCTION(vector) \
extern "C" void exception_interrupt_function##vector();
INTERRUPT_FUNCTIONS()
#undef INTERRUPT_FUNCTION
static const char *kInterruptNames[DEBUG_IDT_SLOT_COUNT] = {
"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",
};
struct interrupt_frame {
uint32 vector;
uint32 edi;
uint32 esi;
uint32 ebp;
uint32 esp;
uint32 ebx;
uint32 edx;
uint32 ecx;
uint32 eax;
uint32 error_code;
uint32 eip;
uint32 cs;
uint32 eflags;
};
static interrupt_descriptor sDebugIDT[DEBUG_IDT_SLOT_COUNT];
static gdt_idt_descr sBIOSIDTDescriptor;
static gdt_idt_descr sDebugIDTDescriptor = { sizeof(sDebugIDT) - 1, sDebugIDT };
static void
set_interrupt_gate(int n, void (*function)())
{
if (n >= DEBUG_IDT_SLOT_COUNT)
return;
sDebugIDT[n].a
= (KERNEL_CODE_SELECTOR << 16) | (0x0000ffff & (addr_t)function);
sDebugIDT[n].b = (0xffff0000 & (addr_t)function) | 0x8e00;
}
static void
set_idt(const gdt_idt_descr& idtDescriptor)
{
asm("lidt %0;" : : "m" (idtDescriptor));
}
extern "C" void
handle_exception_exception()
{
while (true);
}
extern "C" void
handle_exception(interrupt_frame frame)
{
#define INTERRUPT_FUNCTION(vector) \
set_interrupt_gate(vector, &exception_interrupt_function##vector);
INTERRUPT_FUNCTIONS()
#undef INTERRUPT_FUNCTION
if (stdout != NULL) {
console_set_color(RED, BLACK);
console_clear_screen();
console_set_cursor(0, 0);
console_hide_cursor();
}
if (frame.vector < DEBUG_IDT_SLOT_COUNT)
kprintf("%s", kInterruptNames[frame.vector]);
else
kprintf("Unknown exception %" B_PRIu32, frame.vector);
if (frame.vector == 14) {
uint32 cr2;
asm("movl %%cr2, %0" : "=r"(cr2));
kprintf(": %s fault at address: %#" B_PRIx32,
(frame.error_code & 0x2) != 0 ? "write" : "read", cr2);
}
kprintf("\n");
kprintf("Welcome to Boot Loader Death Land!\n");
kprintf("\n");
#define REG(name) " " #name " %-#10" B_PRIx32
kprintf(REG(eax) " " REG(ebx) " " REG(ecx) " " REG(edx) "\n",
frame.eax, frame.ebx, frame.ecx, frame.edx);
kprintf(REG(esi) " " REG(edi) " " REG(ebp) " " REG(esp) "\n",
frame.esi, frame.edi, frame.ebp, frame.esp);
kprintf(REG(eip) REG(eflags) "\n", frame.eip, frame.eflags);
#undef REG
kprintf("\n frame return address\n");
struct x86_stack_frame {
x86_stack_frame* next;
void* return_address;
};
x86_stack_frame* stackFrame = (x86_stack_frame*)frame.ebp;
void* instructionPointer = (void*)frame.eip;
for (int32 i = 0; i < 10 && stackFrame != NULL; i++) {
kprintf(" %p %p\n", stackFrame, instructionPointer);
instructionPointer = stackFrame->return_address;
stackFrame = stackFrame->next;
}
if (stdout != NULL) {
kprintf("\nPress a key to reboot.\n");
clear_key_buffer();
wait_for_key();
platform_exit();
}
while (true);
}
void
interrupts_init()
{
asm("sidt %0;" : : "m" (sBIOSIDTDescriptor));
TRACE("IDT: base: %p, limit: %u\n", sBIOSIDTDescriptor.base,
sBIOSIDTDescriptor.limit);
#define INTERRUPT_FUNCTION(vector) \
set_interrupt_gate(vector, &interrupt_function##vector);
INTERRUPT_FUNCTIONS()
#undef INTERRUPT_FUNCTION
set_debug_idt();
}
void
restore_bios_idt()
{
set_idt(sBIOSIDTDescriptor);
}
void
set_debug_idt()
{
set_idt(sDebugIDTDescriptor);
}