#include <sys/time.h> /* <sys/kernel.h> is broken wrt struct timeval */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/user.h>
#include <sys/vmmac.h>
#include <sys/map.h>
#include <sys/signal.h>
#include <machine/trap.h>
#include <machine/mmu.h>
#include <machine/pte.h>
#include <machine/enable.h>
#include <machine/reg.h>

#include <rciext/sparc_crash.h>

#include <vm/hat.h>
#include <vm/as.h>
#include <vm/seg.h>
#include <vm/page.h>

#include <sun/fault.h>
#include <rciext/rci.h>
#include <rciext/procx.h>

#define	TRAP_TYPES	(sizeof trap_type / sizeof trap_type[0])

static char *trap_type[] = {
	"Zero",
	"Text fault",
	"Illegal instruction",
	"Privileged instruction",
	"Floating point unit disabled",
	"Window overflow",
	"Window underflow",
	"Memory address alignment",
	"Floating point exception",
	"Data fault",
	"Tag overflow",
	"Trap 0x0B",
	"Trap 0x0C",
	"Trap 0x0D",
	"Trap 0x0E",
	"Trap 0x0F",
	"Spurious interrupt",
	"Interrupt level 1",
	"Interrupt level 2",
	"Interrupt level 3",
	"Interrupt level 4",
	"Interrupt level 5",
	"Interrupt level 6",
	"Interrupt level 7",
	"Interrupt level 8",
	"Interrupt level 9",
	"Interrupt level A",
	"Interrupt level B",
	"Interrupt level C",
	"Interrupt level D",
	"Interrupt level E",
	"Interrupt level F",
	"AST",
};

int kernel_user_traps = 0;
label_t kernel_user_catch;
struct proc *kernel_user_proc;
int (*kernel_user_fxn)() = 0;

trap(type,rp,addr,be,rw,dummy)
register unsigned type;
register struct regs *rp;
register unsigned int addr;
register u_char be;
register enum seg_rw rw;
register int dummy;		/* in case Sun added another argument */
{
	register int i;
	register struct proc *p;
	register unsigned inst;
	struct timeval syst;

	if (!kernel_user_traps)
	 { return (LMTt (type,rp,addr,be,rw,dummy));
	 }
	if (kernel_user_traps++ > 5)
	 { panic ("too many kernel user traps without reset");
	 }
	i = 0;
	p = u.u_procp;
	/* take any pending floating point exceptions now */
	syncfpu(rp);
	switch (type)
	 { default:
	   die:
	      splhigh();
	      printf("RCI: BAD TRAP\n");
	      if (type < TRAP_TYPES)
	       { panic(trap_type[type]);
	       }
	      panic("trap");
	      /*NOTREACHED*/
	      break;
	   case T_DATA_FAULT:	/* user data access fault */
	   case T_TEXT_FAULT:	/* user text access fault */
	      if (nofault) /* allow badaddr() inside kernel-user traps */
	       { label_t *ftmp;
		 ftmp = nofault;
		 nofault = 0;
		 longjmp(ftmp);
	       }
	      printf("kernel-user text/data fault, address = %x\n",(int)addr);
	      i = SIGSEGV;
	      break;
	   case T_ALIGNMENT:	/* user alignment error */
	      i = SIGBUS;
	      break;
	   case T_PRIV_INSTR:	/* privileged instruction fault */
	   case T_UNIMP_INSTR:	/* illegal instruction fault */
	   case T_BREAKPOINT:	/* breakpoint trap (t 1) */
	      i = SIGILL;
	      break;
	   case T_FP_EXCEPTION:	/* FPU arithmetic exception */
	      printf("kut FP_X\n");
	   case T_DIV0:		/* integer divide by zero */
	      i = SIGFPE;
	      break;
	   case T_TAG_OVERFLOW:	/* tag overflow (taddcctv, tsubcctv) */
	      i = SIGEMT;
	      break;
	   case T_WIN_OVERFLOW:	/* finish user window overflow */
	      panic("T_WIN_OVERFLOW kernel_user trap");
	      /* NOTREACHED */
	      /* may need to clone T_WIN_OVERFLOW+USER here */
	      break;
	   case T_AST:		/* profiling or resched psuedo trap */
	      panic("T_AST kernel_user trap");
	      /* NOTREACHED */
	      /* may need to clone T_AST+USER here */
	      break;
	 }
	if (kernel_user_proc)
	 { psignal(kernel_user_proc,i);
	   printf("KERNEL_USER trap handler, type %d\n", type);
	   printf("psr = 0x%x, pc = 0x%x, npc = 0x%x\n",
		   rp->r_psr,rp->r_pc,rp->r_npc);
	 }
	else if (kernel_user_fxn)
	 { (*kernel_user_fxn)(type, rp, addr, be);
	 }
	else
	 { panic("don't know what to do with kernel_user trap");
	 }
	printf ("rci trap ... longjumping\n");
	longjmp(&kernel_user_catch);
}

/*
 * Called from the trap handler when a system call occurs
 */
syscall(rp)
register struct regs *rp;
{
	register unsigned code;
	register struct sysent *callp;
	register struct proc *p;
	register syst_flag;
	struct timeval syst;

	if (!kernel_user_traps)
	 { return (LMTcall(rp));
	 }
	code = rp->r_g1;
	syncfpu(rp);
	if (kernel_user_proc)
	 { psignal(kernel_user_proc,SIGILL);
	   printf("KERNEL_USER trap handler: syscall %d\n",code);
	 }
	else if (kernel_user_fxn)
	 { (*kernel_user_fxn)(T_SYSCALL,rp,0,0);
	 }
	else
	 { panic("don't know what to do with kernel_user trap");
	 }
	longjmp(&kernel_user_catch);
}
