/*
 *  linux/kernel/traps.c
 *
 *  Copyright (C) 1993        Hamish Macdonald
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file README.legal in the main directory of this archive
 * for more details.
 */

/*
 * Sets up all exception vectors
 */

#include <asm/system.h>
#include <linux/traps.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/kernel.h>
#include <linux/mm.h>

/* assembler routines */
extern void system_call(void);
extern void buserr(void);
extern void trap(void);
extern void inthandler(void);

vector vectors[256] = {
    0, 0, buserr, trap, trap, trap, trap, trap,
    trap, trap, trap, trap, 0, trap, trap, trap,
    0, 0, 0, 0, 0, 0, 0, 0,
    inthandler, inthandler, inthandler, inthandler, /* interrupts */
    inthandler, inthandler, inthandler, inthandler, /* interrupts */
    system_call, trap, trap, trap, trap, trap, trap, trap,  /* TRAP #0-15 */
    trap, trap, trap, trap, trap, trap, trap, trap,
    trap, trap, trap, trap, trap, trap, trap, 0,
    trap, trap, trap, 0,
};

void vec_init (void)
{
    /* setup the exception vector table */
    __asm__ volatile ("movec %0,vbr" :: "r" (vectors) : "vbr" );
}

char *vec_names[] = {
    "RESET SP", "RESET PC", "BUS ERROR", "ADDRESS ERROR",
    "ILLEGAL INSTRUCTION", "ZERO DIVIDE", "CHK", "TRAPcc",
    "PRIVELIGE VIOLATION", "TRACE", "LINE 1010", "LINE 1111",
    "UNASSIGNED RESERVED 12", "COPROCESSOR PROTOCOL VIOLATION",
    "FORMAT ERROR", "UNINITIALIZED INTERRUPT",
    "UNASSIGNED RESERVED 16", "UNASSIGNED RESERVED 17",
    "UNASSIGNED RESERVED 18", "UNASSIGNED RESERVED 19",
    "UNASSIGNED RESERVED 20", "UNASSIGNED RESERVED 21",
    "UNASSIGNED RESERVED 22", "UNASSIGNED RESERVED 23",
    "SPURIOUS INTERRUPT", "LEVEL 1 INT", "LEVEL 2 INT", "LEVEL 3 INT",
    "LEVEL 4 INT", "LEVEL 5 INT", "LEVEL 6 INT", "LEVEL 7 INT",
    "SYSCALL", "TRAP #1", "TRAP #2", "TRAP #3",
    "TRAP #4", "TRAP #5", "TRAP #6", "TRAP #7",
    "TRAP #8", "TRAP #9", "TRAP #10", "TRAP #11",
    "TRAP #12", "TRAP #13", "TRAP #14", "TRAP #15"
    };

char *space_names[] = {
    "Space 0", "User Data", "User Program", "Space 3",
    "Space 4", "Super Data", "Super Program", "CPU"
    };



void buserr_c(struct frame *fp)
{
    unsigned long addr;
    unsigned short ssw = fp->un.fmtb.ssw;

#ifdef DEBUG
    printk ("*** Bus Error *** Format is %x\n", fp->format);

    printk ("SSW=%#06x  ", ssw);

    if (ssw & (FC | FB))
	printk ("Instruction fault at %#010x\n", fp->pc);
    if (ssw & DF)
	printk ("Data %s fault at %#010x in %s (pc=%#lx)\n",
		ssw & RW ? "read" : "write",
		fp->un.fmtb.daddr,
		space_names[ssw & DFC], fp->pc);
#endif

    if (fp->sr & PS_S) {
	/* kernel fault must be a data fault to user space */
	if (!(ssw & DF) || (ssw & DFC) != USER_DATA)
	    /* instruction fault! BAD */
	    panic ("BAD KERNEL BUSERR");
    } else {
	/* user fault */
	if (!(ssw & (FC | FB)) && !(ssw & DF))
	    /* not an instruction fault or data fault! BAD */
	    panic ("USER BUSERR w/o instruction or data fault");
    }

    /* get the fault address */
    if (fp->format == 0xA )
	if (ssw & FC)
	    addr = fp->pc + 2;
	else if (ssw & FB)
	    addr = fp->pc + 4;
	else /* data fault */
	    addr = fp->un.fmta.daddr;
    else
	if (ssw & FC)
	    addr = fp->un.fmtb.baddr - 2;
	else if (ssw & FB)
	    addr = fp->un.fmtb.baddr;
	else /* data fault */
	    addr = fp->un.fmtb.daddr;

    do_page_fault (fp, addr, !(ssw & RW));
}


void trap_c(struct frame *fp)
{
    unsigned long isp;

    /* fetch interrupt stack pointer */
    __asm__ __volatile__ ("movec isp,%0" : "=g" (isp));

    if (fp->vector < 48*4)
	printk ("*** %s ***   FORMAT=%X\n", vec_names[fp->vector >> 2],
		fp->format);
    else
	printk ("*** Exception %d ***   FORMAT=%X\n", fp->vector >> 2,
		fp->format);

    printk ("PC=%#010x   SR=%#06x       SP=%#010x  ISP=%#010x\n",
	    fp->pc, fp->sr, fp, isp);
    printk ("D0=%#010x   D1=%#010x   D2=%#010x   D3=%#010x\n",
	    fp->d0, fp->regs[0], fp->regs[1], fp->regs[2]);
    printk ("D4=%#010x   D5=%#010x   D6=%#010x   D7=%#010x\n",
	    fp->regs[3], fp->regs[4], fp->regs[5], fp->regs[6]);
    printk ("A0=%#010x   A1=%#010x   A2=%#010x   A3=%#010x\n",
	    fp->regs[7], fp->regs[8], fp->regs[9], fp->regs[10]);
    printk ("A4=%#010x   A5=%#010x   A6=%#010x  USP=%#010x\n",
	    fp->regs[11], fp->regs[12], fp->regs[13], fp->usp);

    if ((fp->vector >> 2) == VEC_ADDRERR) {
	unsigned short ssw = fp->un.fmtb.ssw;

	printk ("SSW=%#06x  ", ssw);

	if (ssw & RC)
	    printk ("Pipe stage C instruction fault at %#010x\n",
		    fp->format == 0xA ? fp->pc + 2 : fp->un.fmtb.baddr - 2);
	if (ssw & RB)
	    printk ("Pipe stage B instruction fault at %#010x\n",
		    fp->format == 0xA ? fp->pc + 4 : fp->un.fmtb.baddr);
	if (ssw & DF)
	    printk ("Data %s fault at %#010x in %s (pc=%#lx)\n",
		    ssw & RW ? "read" : "write",
		    fp->un.fmtb.daddr,
		    space_names[ssw & DFC], fp->pc);

	if (fp->sr & PS_S) {
	    /* kernel fault must be a data fault to user space */
	    if (!(ssw & DF) || (ssw & DFC) != USER_DATA)
		/* instruction fault! BAD */
		panic ("BAD KERNEL ADDRESS ERROR");
	} else {
	    /* user fault */
	    if (!(ssw & (RC | RB)) && !(ssw & DF))
		/* not an instruction fault or data fault! BAD */
		panic ("USER ADDRERR w/o instruction or data fault");
	}
    }

    /* make screen red */
    for(;;)
	*(unsigned short *)0xdff180 = 0xf00;
}

void die_if_kernel (char *str, struct frame *fp, int nr)
{
    if (!(fp->sr & PS_S))
	return;

    printk("%s: %04x\n",str,nr&0xffff);
    printk("PC:    %p\nSR: %p\n", fp->pc,fp->sr);
    printk("Pid: %d\n",current->pid);
    do_exit(SIGSEGV);
}
