2.11BSD/sys/pdp/trap.c
/*
* Copyright (c) 1986 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*
* @(#)trap.c 1.6 (2.11BSD) 1999/9/13
*/
#include "param.h"
#include "../machine/psl.h"
#include "../machine/reg.h"
#include "../machine/seg.h"
#include "../machine/trap.h"
#include "../machine/iopage.h"
#include "signalvar.h"
#include "systm.h"
#include "user.h"
#include "proc.h"
#include "vm.h"
extern int fpp, kdj11;
#ifdef INET
extern int netoff;
#endif
#ifdef DIAGNOSTIC
extern int hasmap;
static int savhasmap;
#endif
/*
* Offsets of the user's registers relative to
* the saved r0. See sys/reg.h.
*/
char regloc[] = {
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, ov, nps, r0, pc, ps)
dev_t dev;
caddr_t sp, pc;
int r1, ov, nps, r0, ps;
{
extern int _ovno;
static int once_thru = 0;
register int i;
register struct proc *p;
time_t syst;
mapinfo kernelmap;
#ifdef UCB_METER
cnt.v_trap++;
#endif
/*
* In order to stop the system from destroying
* kernel data space any further, allow only one
* fatal trap. After once_thru is set, any
* futher traps will be handled by looping in place.
*/
if (once_thru) {
(void) _splhigh();
for(;;);
}
if (USERMODE(ps))
dev |= USER;
else
#ifdef INET
if (SUPVMODE(ps))
dev |= SUPV;
else
#endif
savemap(kernelmap); /* guarantee normal kernel mapping */
syst = u.u_ru.ru_stime;
p = u.u_procp;
u.u_fpsaved = 0;
u.u_ar0 = &r0;
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:
once_thru = 1;
#ifdef DIAGNOSTIC
/*
* Clear hasmap if we attempt to sync the fs.
* Else it might fail if we faulted with a mapped
* buffer.
*/
savhasmap = hasmap;
hasmap = 0;
#endif
i = splhigh();
printf("ka6 %o aps %o\npc %o ps %o\nov %d\n", *ka6, &ps,
pc, ps, ov);
if ((cputype == 70) || (cputype == 44))
printf("cpuerr %o\n", *CPUERR);
printf("trap type %o\n", dev);
splx(i);
panic("trap");
/*NOTREACHED*/
case T_BUSFLT + USER:
i = SIGBUS;
break;
case T_INSTRAP + USER:
#if defined(FPSIM) || defined(GENERIC)
/*
* If no floating point hardware is present, see if the
* offending instruction was a floating point instruction ...
*/
if (fpp == 0) {
i = fptrap();
#ifdef UCB_METER
if (i != SIGILL) {
cnt.v_fpsim++;
}
#endif
if (i == 0)
goto out;
if (i == SIGTRAP)
ps &= ~PSL_T;
u.u_code = u.u_fperr.f_fec;
break;
}
#endif
/*
* If illegal instructions are not being caught and the
* offending instruction is a SETD, the trap is ignored.
* This is because C produces a SETD at the beginning of
* every program which will trap on CPUs without an FP-11.
*/
#define SETD 0170011 /* SETD instruction */
if(fuiword((caddr_t)(pc-2)) == SETD && u.u_signal[SIGILL] == 0)
goto out;
i = SIGILL;
u.u_code = ILL_RESAD_FAULT; /* it's simplest to lie */
break;
case T_BPTTRAP + USER:
i = SIGTRAP;
ps &= ~PSL_T;
break;
case T_IOTTRAP + USER:
i = SIGIOT;
break;
case T_EMTTRAP + USER:
i = SIGEMT;
break;
/*
* 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.
*/
#ifdef INET
case T_ARITHTRAP+SUPV:
#endif
case T_ARITHTRAP:
case T_ARITHTRAP + USER:
i = SIGFPE;
stst(&u.u_fperr); /* save error code and address */
u.u_code = u.u_fperr.f_fec;
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/mch.s may fail.
* The classic example is on the instruction
* cmp -(sp),-(sp)
*
* The KDJ-11 (11/53,73,83,84,93,94) handles the trap when doing
* a double word store differently than the other pdp-11s. When
* doing:
* setl
* movfi fr0,-(sp)
* and the stack segment becomes invalid part way thru then the
* trap is generated (as expected) BUT 'sp' IS NOT LEFT DECREMENTED!
* The 'grow' routine sees that SP is still within the (valid) stack
* segment and does not extend the stack, resulting in a 'segmentation
* violation' rather than a successfull floating to long store.
* The "fix" is to pretend that SP is 4 bytes lower than it really
* is (for KDJ-11 systems only) when calling 'grow'.
*/
case T_SEGFLT + USER:
{
caddr_t osp;
osp = sp;
if (kdj11)
osp -= 4;
if (backup(u.u_ar0) == 0)
if (!(u.u_sigstk.ss_flags & SA_ONSTACK) &&
grow((u_int)osp))
goto out;
i = SIGSEGV;
break;
}
/*
* The code here is a half-hearted attempt to do something with
* all of the PDP11 parity registers. In fact, there is little
* that can be done.
*/
case T_PARITYFLT:
case T_PARITYFLT + USER:
#ifdef INET
case T_PARITYFLT + SUPV:
#endif
printf("parity\n");
if ((cputype == 70) || (cputype == 44)) {
for(i = 0; i < 4; i++)
printf("%o ", MEMERRLO[i]);
printf("\n");
MEMERRLO[2] = MEMERRLO[2];
if (dev & USER) {
i = SIGBUS;
break;
}
}
panic("parity");
/*NOTREACHED*/
/*
* Allow process switch
*/
case T_SWITCHTRAP + USER:
goto out;
#ifdef INET
case T_BUSFLT+SUPV:
case T_INSTRAP+SUPV:
case T_BPTTRAP+SUPV:
case T_IOTTRAP+SUPV:
case T_EMTTRAP+SUPV:
case T_PIRQ+SUPV:
case T_SEGFLT+SUPV:
case T_SYSCALL+SUPV:
case T_SWITCHTRAP+SUPV:
case T_ZEROTRAP+SUPV:
case T_RANDOMTRAP+SUPV:
i = splhigh();
if (!netoff) {
netoff = 1;
savestate();
}
printf("Unexpected net trap (%o)\n", dev-SUPV);
printf("ka6 %o aps %o\n", *ka6, &ps);
printf("pc %o ps %o\n", pc, ps);
splx(i);
panic("net crashed");
/*NOTREACHED*/
#endif
/*
* Whenever possible, 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. Uninitialized
* interrupt vectors may be set to trap here also.
*/
case T_ZEROTRAP:
case T_ZEROTRAP + USER:
printf("Trap to 0: ");
/*FALL THROUGH*/
case T_RANDOMTRAP: /* generated by autoconfig */
case T_RANDOMTRAP + USER:
printf("Random intr ignored\n");
if (!(dev & USER))
restormap(kernelmap);
return;
}
psignal(p, i);
out:
while (i = CURSIG(p))
postsig(i);
curpri = setpri(p);
if (runrun) {
setrq(u.u_procp);
u.u_ru.ru_nivcsw++;
swtch();
}
if (u.u_prof.pr_scale)
addupc(pc, &u.u_prof, (int) (u.u_ru.ru_stime - syst));
if (u.u_fpsaved)
restfp(&u.u_fps);
}
/*
* Called from mch.s when a system call occurs. dev, ov and nps are
* unused, but are necessitated by the values of the R[0-7] ... constants
* in sys/reg.h (which, in turn, are defined by trap's stack frame).
*/
/*ARGSUSED*/
syscall(dev, sp, r1, ov, nps, r0, pc, ps)
dev_t dev;
caddr_t sp, pc;
int r1, ov, nps, r0, ps;
{
extern int nsysent;
register int code;
register struct sysent *callp;
time_t syst;
register caddr_t opc; /* original pc for restarting syscalls */
int i;
#ifdef UCB_METER
cnt.v_syscall++;
#endif
syst = u.u_ru.ru_stime;
u.u_fpsaved = 0;
u.u_ar0 = &r0;
u.u_error = 0;
opc = pc - 2; /* opc now points at syscall */
code = fuiword((caddr_t)opc) & 0377; /* bottom 8 bits are index */
if (code >= nsysent)
callp = &sysent[0]; /* indir (illegal) */
else
callp = &sysent[code];
if (callp->sy_narg)
copyin(sp+2, (caddr_t)u.u_arg, callp->sy_narg*NBPW);
u.u_r.r_val1 = 0;
u.u_r.r_val2 = r1;
if (setjmp(&u.u_qsave) == 0)
{
(*callp->sy_call)();
#ifdef DIAGNOSTIC
if (hasmap)
panic("hasmap");
#endif
}
switch (u.u_error)
{
case 0:
ps &= ~PSL_C;
r0 = u.u_r.r_val1;
r1 = u.u_r.r_val2;
break;
case ERESTART:
pc = opc;
break;
case EJUSTRETURN:
break;
default:
ps |= PSL_C;
r0 = u.u_error;
break;
}
while (i = CURSIG(u.u_procp))
postsig(i);
curpri = setpri(u.u_procp);
if (runrun) {
setrq(u.u_procp);
u.u_ru.ru_nivcsw++;
swtch();
}
if (u.u_prof.pr_scale)
addupc(pc, &u.u_prof, (int)(u.u_ru.ru_stime - syst));
if (u.u_fpsaved)
restfp(&u.u_fps);
}
/*
* 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);
}