/*
 * Basic C-level trap handling, mostly panicing and dispatching system calls.
 */

/*
 * Impossible to avoid some minor lint complaints about unknown structs
 * without including every .h in sight.  This is a fairly minimal set.
 */
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/dir.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/reg.h>
#include <sys/seg.h>

static char Sccsid[] = "@(#)trap.c	1.7 of 14 March 85";

/*
 * Hardware bits and suchlike.
 */
#define	EBIT	1		/* user error bit in PS: C-bit */
#define FPINST	0170000		/* floating-point opcode prefix */
#define	SYS	0104400		/* sys (trap) instruction */
#define	USER	020		/* user-mode flag added to dev */
#define	CACHE	((physadr)0177744)	/* 11/44 cache registers */
#define	CPUERR	((physadr)0177766)	/* cpu error register */

/*
 * Offsets of the user's registers relative to
 * the saved r0. See reg.h
 */
char	regloc[9] =
{
	R0, R1, R2, R3, R4, R5, R6, R7, RPS
};

/*
 * Called from mch.s when a processor trap occurs.
 * The arguments are the words saved on the system stack
 * by the hardware and software during the trap processing.
 * Their order is dictated by the hardware and the details
 * of C's calling sequence. They are peculiar in that
 * this call is not 'by value' and changed user registers
 * get copied back on return.
 * dev is the kind of trap that occurred.
 */
/* ARGSUSED */
trap(dev, sp, r1, nps, r0, pc, ps)
dev_t dev;
int sp, r1, nps, r0;
int *pc;
int ps;
{
	register int i;			/* Signal #; also misc temporary. */
	register int *addr;		/* Address of call and arguments. */
	register struct sysent *callp;	/* sysent struct for this call. */
	int oldfpsaved;			/* For saving+restoring u.u_fpsaved. */
	int *oldar0;			/* For saving+restoring u.u_ar0. */
	int (*fetch)();			/* Arg fetcher: fuword or fuiword. */
	time_t syst;			/* Initial u.u_stime, for profiling. */

	syst = u.u_stime;

	/*
	 * Save the elements of the user structure which are about to be
	 * modified.  This is vital because things like imprecise floating-
	 * point exceptions can cause recursive calls of trap().
	 */
	oldfpsaved = u.u_fpsaved;
	oldar0 = u.u_ar0;

	/*
	 * We don't actually save the floating-point registers here
	 * because we may not have to (if we return without having to
	 * yield the CPU), but we note that they need to be saved.
	 */
	u.u_fpsaved = 0;

	if ((ps&UMODE) == UMODE)
		dev |= USER;
	u.u_ar0 = &r0;			/* So other code can find registers. */

	switch (minor(dev)) {

	/*
	 * Trap not expected.
	 * Usually a kernel mode bus error.
	 * The numbers printed are used to
	 * find the hardware PS/PC as follows.
	 * (all numbers in octal 18 bits)
	 *	address_of_saved_ps =
	 *		(ka6*0100) + aps - 0140000;
	 *	address_of_saved_pc =
	 *		address_of_saved_ps - 2;
	 */
	default:
		printf("ka6 = 0%o\n", ka6->r[0]);
		printf("aps = 0%o\n", &ps);
		printf("pc = 0%o ps = 0%o\n", pc, ps);
		if (cputype == 70)
			printf("CPU err = 0%o\n", CPUERR->r[0]);
		printf("trap type 0%o\n", dev);
		panic("trap");
		/* NOTREACHED */

	case 0+USER: /* bus error */
		i = SIGBUS;
		break;

	/*
	 * Illegal instructions that look like floating-point are tried out
	 * on the floating-point simulator first.
	 */
	case 1+USER: /* illegal instruction */
		if ((fuiword((caddr_t)(pc-1))&FPINST) == FPINST) {
			/*
			 * Fpsim is in assembler, hence it is passed various
			 * things to avoid having to hard-wire them in the
			 * awful assembler code.
			 */
			if (fpsim(u.u_ar0, regloc, &u.u_fps.u_fpsr, u.u_fps.u_fpregs) == 0)
				goto out;
			i = SIGFPT;	/* Fpsim didn't like it. */
		} else
			i = SIGINS;
		break;

	case 2+USER: /* bpt or trace */
		i = SIGTRC;
		ps &= ~TBIT;		/* Turn single-stepping off if on. */
		break;

	case 3+USER: /* iot */
		i = SIGIOT;
		break;

	case 5+USER: /* emt */
		i = SIGEMT;
		break;

	case 6+USER: /* sys call */
		u.u_error = 0;
		ps &= ~EBIT;
		/*
		 * First, which call is it, and which fetch routine
		 * should be used?
		 */
		addr = pc;
		callp = &sysent[fuiword((caddr_t)(addr-1))&077];
		if (callp == sysent) { /* indirect */
			addr = (int *)fuiword((caddr_t)(addr));
			pc++;
			i = fuword((caddr_t)addr);
			addr++;
			if ((i & ~077) != SYS)
				i = 077;	/* illegal */
			callp = &sysent[i&077];
			fetch = fuword;
		} else {
			pc += callp->sy_narg - callp->sy_nrarg;
			fetch = fuiword;
		}
		/*
		 * Second, fetch the arguments.
		 */
		for (i=0; i<callp->sy_nrarg; i++)
			u.u_arg[i] = u.u_ar0[regloc[i]];
		for (; i<callp->sy_narg; i++)
			u.u_arg[i] = (*fetch)((caddr_t)addr++);
		/*
		 * Third, set up some standardized pointers.
		 */
		u.u_dirp = (caddr_t)u.u_arg[0];
		u.u_r.r_val1 = u.u_ar0[R0];
		u.u_r.r_val2 = u.u_ar0[R1];
		u.u_ap = u.u_arg;
		/*
		 * Fourth, prepare for abnormal returns.
		 */
		if (save(u.u_qsav)) {
			/* Abnormal return, user interrupt if nothing else. */
			if (u.u_error == 0)
				u.u_error = EINTR;
		} else {
			/*
			 * Fifth, do the call.
			 */
			(*callp->sy_call)();
		}
		/*
		 * Sixth, set up returned value(s).
		 */
		if (u.u_error) {
			ps |= EBIT;
			u.u_ar0[R0] = u.u_error;
		} else {
			u.u_ar0[R0] = u.u_r.r_val1;
			u.u_ar0[R1] = u.u_r.r_val2;
		}
		goto out;

	/*
	 * Since the floating exception is an
	 * imprecise trap, a user generated
	 * trap may actually come from kernel
	 * mode. In this case, a signal is sent
	 * to the current process to be picked
	 * up later, so as not to interfere with
	 * current activities.
	 */
	case 8: /* floating exception */
		stst(&u.u_fper);	/* save error code */
		psignal(u.u_procp, SIGFPT);
		u.u_fpsaved = oldfpsaved;
		u.u_ar0 = oldar0;
		return;

	case 8+USER:
		i = SIGFPT;
		stst(&u.u_fper);	/* save error code */
		break;

	/*
	 * If the user SP is below the stack segment,
	 * grow the stack automatically.
	 * This relies on the ability of the hardware
	 * to restart a half executed instruction.
	 * On the 11/40 this is not the case and
	 * the routine backup in mch.s may fail.
	 * The classic example is on the instruction
	 *	cmp	-(sp),-(sp)
	 */
	case 9+USER: /* segmentation exception */
	{
	int	osp;

		osp = sp;
		/*
		 * If restartable, and stack was indeed overflowed
		 * but could be grown further, then all ok.
		 */
		if (backup(u.u_ar0) == 0)
			if (grow((unsigned)osp))
				goto out;
		i = SIGSEG;		/* One or more conditions not met. */
		break;
	}

	/*
	 * The code here is a half-hearted
	 * attempt to do something with all
	 * of the 11/44 cache registers.
	 * In fact, there is little that
	 * can be done.
	 */
	case 10:
	case 10+USER:
		printf("cache ");
		if (cputype == 70) {
			for (i=0; i<5; i++)
				printf("0%o ", CACHE->r[i]);
			printf("\n");
			CACHE->r[0] = -1;	/* clear error bits */
			if (dev & USER) {
				i = SIGBUS;
				break;
			}
		}
		panic("cache");
		/* NOTREACHED */

	/*
	 * Allow process switch (see mch.s/call() for invocation).
	 */
	case USER+12:
		goto out;

	/*
	 * Locations 0-2 specify this style trap, since
	 * DEC hardware often generates spurious
	 * traps through location 0.  This is a
	 * symptom of hardware problems and may
	 * represent a real interrupt that got
	 * sent to the wrong place.  Watch out
	 * for hangs on disk completion if this message appears.
	 */
	case 15:
	case 15+USER:
		printf("Random interrupt ignored\n");
		u.u_fpsaved = oldfpsaved;
		u.u_ar0 = oldar0;
		return;
	}

	/*
	 * The switch has spoken.  Send whatever signal is appropriate.
	 */
	psignal(u.u_procp, i);

out:
	/*
	 * Clean up and leave.
	 */

	if (ISSIG())			/* Have we been signalled? */
		psig();

	curpri = setpri(u.u_procp);	/* Recompute priority. */
	if (runrun)			/* Should we yield cpu? */
		qswtch();		/* Returns when rescheduled. */

	if (u.u_prof.pr_scale)		/* If profiling, add call's time. */
		addupc((caddr_t)pc, &u.u_prof, (int)(u.u_stime-syst));

	if (u.u_fpsaved)		/* Restore fp regs if got saved. */
		restfp(&u.u_fps);

	u.u_fpsaved = oldfpsaved;
	u.u_ar0 = oldar0;
}

/*
 * Nonexistent system call -- set fatal error code.
 */
nosys()
{
	u.u_error = EINVAL;
}

/*
 * Ignored system call
 */
nullsys()
{
}
