/*-
* Copyright (c) 2005-2007, Kohsuke Ohtani
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* trap.c - called from the trap handler when a processor trap occurs.
*/
#include <kernel.h>
#include <exception.h>
#include <thread.h>
#include <task.h>
#include <cpu.h>
#include <locore.h>
#ifdef DEBUG
static void trap_dump(struct cpu_regs *);
static char *const trap_name[] = {
"Divide error", /* 0 */
"Debug trap", /* 1 */
"NMI", /* 2 */
"Breakpoint", /* 3 */
"Overflow", /* 4 */
"Bounds check", /* 5 */
"Invalid opecode", /* 6 */
"Device not available", /* 7 */
"Double fault", /* 8 */
"Coprocessor overrun", /* 9 */
"Invalid TSS", /* 10 */
"Segment not present", /* 11 */
"Stack bounds", /* 12 */
"General Protection", /* 13 */
"Page fault", /* 14 */
"Reserved", /* 15 */
"Coprocessor error", /* 16 */
"Alignment check", /* 17 */
"Cache flush denied" /* 18 */
};
#define MAXTRAP (sizeof(trap_name) / sizeof(void *) - 1)
#endif /* DEBUG */
/*
* Trap/exception mapping table.
* i386 trap code is translated to the architecture
* independent exception code.
*/
static const int exception_map[] = {
SIGFPE, /* 0: Divide error */
SIGTRAP, /* 1: Debug trap */
SIGILL, /* 2: NMI */
SIGTRAP, /* 3: Breakpoint */
SIGFPE, /* 4: Overflow */
SIGILL, /* 5: Bounds check */
SIGILL, /* 6: Invalid opecode */
SIGFPE, /* 7: Device not available */
SIGILL, /* 8: Double fault */
SIGFPE, /* 9: Coprocessor overrun */
SIGSEGV, /* 10: Invalid TSS */
SIGSEGV, /* 11: Segment not present */
SIGSEGV, /* 12: Stack bounds */
SIGILL, /* 13: General Protection fault */
SIGSEGV, /* 14: Page fault */
SIGILL, /* 15: Reserved */
SIGFPE, /* 16: Coprocessor error */
SIGILL, /* 17: Alignment check */
SIGILL, /* 18: Cache flush denied */
};
/*
* Trap handler
* Invoke the exception handler if it is needed.
*/
void
trap_handler(struct cpu_regs *regs)
{
u_long trap_no = regs->trap_no;
if (trap_no > 18)
panic("Unknown trap");
else if (trap_no == 2)
panic("NMI");
/*
* Check whether this trap is kernel page fault caused
* by known routine to access user space.
*/
if (trap_no == 14 && regs->cs == KERNEL_CS &&
(regs->eip == (u_long)known_fault1 ||
regs->eip == (u_long)known_fault2 ||
regs->eip == (u_long)known_fault3)) {
printk("\n*** Detect Fault! task:%s (id:%x) ***\n",
cur_task()->name ? cur_task()->name : "no name",
cur_task());
regs->eip = (u_long)umem_fault;
return;
}
#ifdef DEBUG
printk("============================\n");
printk("Trap %x: %s\n", trap_no, trap_name[trap_no]);
if (trap_no == 14)
printk(" Fault address=%x\n", get_cr2());
printk("============================\n");
trap_dump(regs);
if (regs->cs == KERNEL_CS) {
interrupt_mask(0);
sti();
for (;;) ;
}
#endif
if (regs->cs == KERNEL_CS)
panic("Kernel exception");
exception_mark(exception_map[trap_no]);
exception_deliver();
}
#ifdef DEBUG
static void
trap_dump(struct cpu_regs *r)
{
u_long ss, esp, *fp;
u_int i;
if (r->cs & 3) {
ss = r->ss;
esp = r->esp;
} else {
ss = r->ds;
esp = (u_long)r;
}
printk("Trap frame %x error %x\n", r, r->err_code);
printk(" eax %08x ebx %08x ecx %08x edx %08x esi %08x edi %08x\n",
r->eax, r->ebx, r->ecx, r->edx, r->esi, r->edi);
printk(" eip %08x esp %08x ebp %08x eflags %08x\n",
r->eip, esp, r->ebp, r->eflags);
printk(" cs %08x ss %08x ds %08x es %08x esp0 %08x\n",
r->cs, ss, r->ds, r->es, tss_get());
if (irq_level > 0)
printk(" >> trap in isr (irq_level=%d)\n", irq_level);
printk(" >> interrupt is %s\n",
(get_eflags() & EFL_IF) ? "enabled" : "disabled");
if (r->cs == KERNEL_CS)
printk(" >> Oops! it's kernel mode now!!!\n");
else
printk(" >> task:%s (id:%x)\n",
cur_task()->name ? cur_task()->name : "no name",
cur_task());
if (r->cs == KERNEL_CS) {
printk("Stack trace:\n");
fp = (u_long *)r->ebp;
for (i = 0; i < 8; i++) {
if (fp == 0)
break;
fp = (u_long *)(*fp); /* XXX: may cause fault */
if (!(*(fp + 1) && *fp))
break;
printk(" %08x\n", *(fp + 1));
}
}
}
#endif /* DEBUG */