/*
 * Copyright (c) 1982, 1986 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 *
 *	@(#)trap.c	7.1 (Berkeley) 6/5/86
 */

#include "psl.h"
#include "reg.h"
#include "pte.h"

#include "param.h"
#include "systm.h"
#include "dir.h"
#include "user.h"
#include "assym.s"
#include "proc.h"
#include "seg.h"
#include "trap.h"
#include "acct.h"
#include "kernel.h"
#ifdef SYSCALLTRACE
#include "../sys/syscalls.c"
#endif

#include "mtpr.h"

#define	USER	040		/* user-mode flag added to type */
#ifdef KERNEL_USER_TRAPS
#define KERNEL_USER 0100	/* kernel executing with traps kicked back */
int kernel_user_traps = 0;
label_t kernel_user_catch;
struct proc *kernel_user_proc;
int (*kernel_user_fxn)() = 0;
int kut_regbank[17]; /* r0-r11, ap, fp, sp, pc, psl */
#endif

struct	sysent	sysent[];
int	nsysent;

char	*trap_type[] = {
	"Reserved addressing mode",
	"Privileged instruction",
	"Reserved operand",
	"Breakpoint",
	"Xfc trap",
	"Syscall trap",
	"Arithmetic fault",
	"Ast trap",
	"Segmentation fault",
	"Protection fault",
	"Trace trap",
	"Compatibility mode trap",
	"Page fault",
	"Page table fault",
};
#define	TRAP_TYPES	(sizeof trap_type / sizeof trap_type[0])

/*
 * Called from the trap handler when a processor trap occurs.
 */
/*ARGSUSED*/
trap(sp,type,code,pc,psl)
int sp;
int type;
unsigned int code;
int pc;
int psl;
{
 register int *locr0;
 register int i;
 register struct proc *p;
 struct timeval syst;

 locr0 = ((int *)&psl) - PS;
 syst = u.u_ru.ru_stime;
 if (USERMODE(locr0[PS]))
  { type |= USER;
    u.u_ar0 = locr0;
  }
#ifdef KERNEL_USER_TRAPS
 else if (kernel_user_traps)
  { type |= KERNEL_USER;
  }
#endif
 switch (type)
  { default:
       printf("trap type %d, code = %x, pc = %x\n",type,code,pc);
       type &= ~(USER
#ifdef KERNEL_USER_TRAPS
		      | KERNEL_USER
#endif
				   );
       if ((unsigned int)type < TRAP_TYPES)
	{ panic(trap_type[type]);
	}
       panic("trap");
       break;
#ifdef KERNEL_USER_TRAPS
    case T_PROTFLT|KERNEL_USER:
       i = SIGBUS;
       break;
    case T_PRIVINFLT|KERNEL_USER:
    case T_RESADFLT|KERNEL_USER:
    case T_RESOPFLT|KERNEL_USER:
    case T_COMPATFLT|KERNEL_USER:
       i = SIGILL;
       break;
    case T_ARITHTRAP|KERNEL_USER:
       i = SIGFPE;
       break;
    case T_PAGEFLT|KERNEL_USER:
    case T_SEGFLT|KERNEL_USER:
       i = SIGSEGV;
       break;
#endif
    case T_PROTFLT+USER:	/* protection fault */
       i = SIGBUS;
       break;
    case T_PRIVINFLT+USER:	/* privileged instruction fault */
    case T_RESADFLT+USER:	/* reserved addressing fault */
    case T_RESOPFLT+USER:	/* resereved operand fault */
       u.u_code = type &~ USER;
       i = SIGILL;
       break;
    case T_ASTFLT+USER:
       astoff();
       if ((u.u_procp->p_flag & SOWEUPC) && u.u_prof.pr_scale)
	{ addupc(pc,&u.u_prof,1);
	  u.u_procp->p_flag &= ~SOWEUPC;
	}
       goto out;
       break;
    case T_ARITHTRAP+USER:
       u.u_code = code;
       i = SIGFPE;
       break;
    case T_SEGFLT+USER:
       /*
	* If the user SP is above the stack segment,
	*  grow the stack automatically.
	*/
       if (grow((unsigned)locr0[SP]) || grow(code))
	{ goto out;
	}
       i = SIGSEGV;
       break;
    case T_TABLEFLT:	/* allow page table faults in kernel mode */
    case T_TABLEFLT+USER:   /* page table fault */
       panic("ptable fault");
       break;
    case T_PAGEFLT:		/* allow page faults in kernel mode */
    case T_PAGEFLT+USER:	/* page fault */
       i = u.u_error;
       pagein(code,0);
       u.u_error = i;
       if (type == T_PAGEFLT)
	{ return;
	}
       goto out;
       break;
    case T_BPTFLT+USER:	/* bpt instruction fault */
    case T_TRCTRAP+USER:	/* trace trap */
       locr0[PS] &= ~PSL_T;
       i = SIGTRAP;
       break;
    case T_XFCFLT+USER:	/* xfc instruction fault */
       i = SIGEMT;
       break;
    case T_COMPATFLT+USER:	/* compatibility mode fault */
       u.u_acflag |= ACOMPAT;
       u.u_code = code;
       i = SIGILL;
       break;
  }
#ifdef KERNEL_USER_TRAPS
 if (type & KERNEL_USER)
  { if (mfpr(IPL) != 31)
     { panic("KERNEL_USER trap handling when not at splhigh()");
     }
    type &= ~KERNEL_USER;
    if (kernel_user_proc)
     { psignal(kernel_user_proc,i);
       printf("KERNEL_USER trap handler:\n");
       if ((unsigned)type < TRAP_TYPES)
	{ printf("  %s\n",trap_type[type]);
	}
       else
	{ printf("  unknown type %d\n",type);
	}
       if (type == T_ARITHTRAP)
	{ switch (code)
	   { default:
		printf("  unknown code %d\n",code);
		break;
	     case 1:
		printf("  integer overflow\n");
		break;
	     case 2:
		printf("  integer divide by zero\n");
		break;
	     case 3:
		printf("  floating overflow\n");
		break;
	     case 4:
		printf("  floating/decimal divide by zero\n");
		break;
	     case 5:
		printf("  floating underflow\n");
		break;
	     case 6:
		printf("  decimal overflow\n");
		break;
	     case 7:
		printf("  subscript range\n");
		break;
	     case 8:
		printf("  floating overflow fault\n");
		break;
	     case 9:
		printf("  floating divide by zero fault\n");
		break;
	     case 10:
		printf("  floating underflow fault\n");
		break;
	    }
	}
       switch (type)
	{ default:
	     printf("  pc from trap: 0x%x\n",pc);
	     break;
	  case T_ARITHTRAP:
	     switch (code)
	      { default:
		   printf("  pc from trap: 0x%x\n",pc);
		   break;
		case 1:
		case 2:
		case 3:
		case 4:
		case 5:
		case 6:
		case 7:
		   printf("  pc of instruction after the culprit: 0x%x\n",pc);
		   break;
		case 8:
		case 9:
		case 10:
		   printf("  pc of guilty instruction: 0x%x\n",pc);
		   break;
	      }
	     break;
	  case T_PROTFLT:
	  case T_PAGEFLT:
	  case T_SEGFLT:
	     printf("  faulting address: 0x%x\n",code);
	     printf("  pc of guilty instruction: 0x%x\n",pc);
	     break;
	}
     }
    else if (kernel_user_fxn)
     { (*kernel_user_fxn)(type,code,pc,psl,locr0);
     }
    longjmp(&kernel_user_catch);
  }
#endif
 psignal(u.u_procp,i);
out:
 p = u.u_procp;
 if (p->p_cursig || ISSIG(p))
  { psig();
  }
 p->p_pri = p->p_usrpri;
	if (runrun) {
		/*
		 * Since we are u.u_procp, clock will normally just change
		 * our priority without moving us from one queue to another
		 * (since the running process is not on a queue.)
		 * If that happened after we setrq ourselves but before we
		 * swtch()'ed, we might not be on the queue indicated by
		 * our priority.
		 */
		(void) splclock();
		setrq(p);
		u.u_ru.ru_nivcsw++;
		swtch();
	}
	if (u.u_prof.pr_scale) {
		int ticks;
		struct timeval *tv = &u.u_ru.ru_stime;

		ticks = ((tv->tv_sec - syst.tv_sec) * 1000 +
			(tv->tv_usec - syst.tv_usec) / 1000) / (tick / 1000);
		if (ticks)
			addupc(locr0[PC], &u.u_prof, ticks);
	}
	curpri = p->p_pri;
}

#ifdef KERNEL_USER_TRAPS

static int (*kut_fxn)();
static long int kut_args[4];
static struct proc *kut_proc;
static int kut_mutex = 0;
static int kut_rv;

kut_soft15()
{
 int s;

 s = splhigh();
 if (u.u_procp != kut_proc) panic("kut_soft15: wrong process\n");
 kernel_user_proc = u.u_procp;
 kernel_user_traps = 1;
 if (setjmp(&kernel_user_catch) == 0)
  { kut_rv = (*kut_fxn)(kut_args[0],kut_args[1],kut_args[2],kut_args[3]);
  }
 kut_fxn = 0;
 kernel_user_traps = 0;
 splx(s);
}

kut_callit(fxn,arg1,arg2,arg3,arg4)
int (*fxn)();
long int arg1;
long int arg2;
long int arg3;
long int arg4;
{
 int s;

 s = splhigh();
 if (s > 14)
  { printf("kut_callit: ipl>14\n");
    psignal(u.u_procp,SIGIOT);
    splx(s);
    return;
  }
 while (kut_mutex & 1)
  { kut_mutex |= 2;
    sleep((caddr_t)&kut_mutex,PZERO+1);
  }
 kut_mutex = 1;
 splx(s);
 kut_fxn = fxn;
 kut_args[0] = arg1;
 kut_args[1] = arg2;
 kut_args[2] = arg3;
 kut_args[3] = arg4;
 kut_proc = u.u_procp;
 kut_rv = -1; /* set it so return value is deterministic.  Why -1?  Why not? */
 mtpr(SIRR,15);
 if (kut_fxn)
  { panic("kut_fxn set after mtpr SIRR in kut_callit");
  }
 s = splhigh();
 kut_mutex &=~ 1;
 if (kut_mutex & 2)
  { kut_mutex &=~ 2;
    wakeup((caddr_t)&kut_mutex);
  }
 splx(s);
 return(kut_rv);
}

#endif

#ifdef SYSCALLTRACE
int syscalltrace = 0;
#endif
/*
 * Called from the trap handler when a system call occurs
 */
/*ARGSUSED*/
syscall(sp, type, code, pc, psl)
	unsigned code;
{
	register int *locr0 = ((int *)&psl)-PS;
	register caddr_t params;		/* known to be r10 below */
	register int i;				/* known to be r9 below */
	register struct sysent *callp;
	register struct proc *p;
	int opc;
	struct timeval syst;

	syst = u.u_ru.ru_stime;
	if (!USERMODE(locr0[PS]))
		panic("syscall");
	u.u_ar0 = locr0;
	if (code == 139) {			/* XXX 4.2 COMPATIBILITY */
		osigcleanup();			/* XXX 4.2 COMPATIBILITY */
		goto done;			/* XXX 4.2 COMPATIBILITY */
	}					/* XXX 4.2 COMPATIBILITY */
	params = (caddr_t)locr0[AP] + NBPW;
	u.u_error = 0;
	opc = pc - 2;
	if (code > 63)
		opc -= 2;
	if (code >= nsysent)
		callp = &sysent[0];		/* indir (illegal) */
	else {
		callp = &sysent[code];
		if (callp == sysent) {		/* indir */
			i = fuword(params);
			params += NBPW;
			if ((unsigned)i >= nsysent)
				callp = &sysent[0];
			else
				callp = &sysent[i];
		}
	}
	if ((i = callp->sy_narg * sizeof (int)) &&
	    (u.u_error = copyin(params, (caddr_t)u.u_arg, (u_int)i)) != 0) {
		locr0[R0] = u.u_error;
		locr0[PS] |= PSL_C;	/* carry bit */
		goto done;
	}
	u.u_r.r_val1 = 0;
	u.u_r.r_val2 = locr0[R1];
	if (setjmp(&u.u_qsave)) {
		if (u.u_error == 0 && u.u_eosys != RESTARTSYS)
			u.u_error = EINTR;
	} else {
		u.u_eosys = NORMALRETURN;
#ifdef SYSCALLTRACE
		if (syscalltrace) {
			register int i;
			char *cp;

			if (code >= nsysent)
				printf("0x%x", code);
			else
				printf("%s", syscallnames[code]);
			cp = "(";
			for (i= 0; i < callp->sy_narg; i++) {
				printf("%s%x", cp, u.u_arg[i]);
				cp = ", ";
			}
			if (i)
				putchar(')', 0);
			putchar('\n', 0);
		}
#endif
		(*(callp->sy_call))();
	}
	if (u.u_eosys == NORMALRETURN) {
		if (u.u_error) {
			locr0[R0] = u.u_error;
			locr0[PS] |= PSL_C;	/* carry bit */
		} else {
			locr0[R0] = u.u_r.r_val1;
			locr0[R1] = u.u_r.r_val2;
			locr0[PS] &= ~PSL_C;
		}
	} else if (u.u_eosys == RESTARTSYS)
		pc = opc;
	/* else if (u.u_eosys == JUSTRETURN) */
		/* nothing to do */
done:
	p = u.u_procp;
	if (p->p_cursig || ISSIG(p))
		psig();
	p->p_pri = p->p_usrpri;
	if (runrun) {
		/*
		 * Since we are u.u_procp, clock will normally just change
		 * our priority without moving us from one queue to another
		 * (since the running process is not on a queue.)
		 * If that happened after we setrq ourselves but before we
		 * swtch()'ed, we might not be on the queue indicated by
		 * our priority.
		 */
		(void) splclock();
		setrq(p);
		u.u_ru.ru_nivcsw++;
		swtch();
	}
	if (u.u_prof.pr_scale) {
		int ticks;
		struct timeval *tv = &u.u_ru.ru_stime;

		ticks = ((tv->tv_sec - syst.tv_sec) * 1000 +
			(tv->tv_usec - syst.tv_usec) / 1000) / (tick / 1000);
		if (ticks)
			addupc(locr0[PC], &u.u_prof, ticks);
	}
	curpri = p->p_pri;
}

/*
 * nonexistent system call-- signal process (may want to handle it)
 * flag error if process won't see signal immediately
 * Q: should we do that all the time ??
 */
nosys()
{
	if (u.u_signal[SIGSYS] == SIG_IGN || u.u_signal[SIGSYS] == SIG_HOLD)
		u.u_error = EINVAL;
	psignal(u.u_procp, SIGSYS);
}
