Files
Arquivotheca.SunOS-4.1.4/sys/sun4c/trap.c
seta75D ff309bfe1c Init
2021-10-11 18:37:13 -03:00

1868 lines
44 KiB
C

#ident "@(#)trap.c 1.1 94/10/31 SMI"
/*
* Copyright (c) 1989, 1990 by Sun Microsystems, Inc.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/vm.h>
#include <sys/kernel.h>
#include <sys/msgbuf.h>
#include <sys/trace.h>
#include <sun/fault.h>
#include <machine/frame.h>
#include <machine/asm_linkage.h>
#include <machine/buserr.h>
#include <machine/mmu.h>
#include <machine/pcb.h>
#include <machine/cpu.h>
#include <machine/psl.h>
#include <machine/pte.h>
#include <machine/reg.h>
#include <machine/trap.h>
#include <machine/vm_hat.h>
#include <machine/seg_kmem.h>
#include <machine/memerr.h>
#include <vm/as.h>
#include <vm/seg.h>
#include <sys/reboot.h>
#include <debug/debug.h>
#ifdef SUNDBE
#include <sys/syscall.h>
#endif /* INLINE_SEMSYS */
#define USER 0x10000 /* user-mode flag added to type */
#ifdef SYSCALLTRACE
extern char* syscallnames[];
#endif /* SYSCALLTRACE */
extern struct sysent sysent[];
extern int nsysent;
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",
};
#define TRAP_TYPES (sizeof (trap_type) / sizeof (trap_type[0]))
int tudebug = 0;
int tudebugbpt = 0;
int tudebugfpe = 0;
int aligndebug = 0;
int alignfaults = 0;
#if defined(TRAPDEBUG) || defined(lint)
int tdebug = 0;
int lodebug = 0;
int faultdebug = 0;
#else
#define tdebug 0
#define lodebug 0
#define faultdebug 0
#endif defined(TRAPDEBUG) || defined(lint)
#ifdef SUN4_110
int fault_type;
#endif
#define SIMU_SUCCESS 1 /* simulation worked */
#define SIMU_ILLEGAL 0 /* tried to simulate an illegal instruction */
#define SIMU_FAULT -1 /* simulation generated an illegal access */
#define SIMU_DZERO -2 /* divided by zero detected */
u_int beval = 0; /* used by fp_traps() to setup a data pg fault*/
/*
* Called from the trap handler when a processor trap occurs.
* Addr, be and rw only are passed for text and data faults.
*/
/*VARARGS2*/
void
trap(type, rp, addr, be, rw)
register unsigned type;
register struct regs *rp;
register addr_t addr;
register u_int be;
register enum seg_rw rw;
{
register int i = 0;
register struct proc *p;
struct timeval syst;
int lofault;
faultcode_t pagefault(), res;
int mask;
p = u.u_procp;
cnt.v_trap++;
syst = u.u_ru.ru_stime;
if (tdebug)
showregs(type, rp, addr, be, rw);
if (USERMODE(rp->r_psr)) {
type |= USER;
u.u_ar0 = (int *)rp;
}
/*
* take any pending floating point exceptions now
*/
syncfpu(rp);
switch (type) {
default:
/*
* Check for user software trap.
*/
if (type & USER) {
if (tudebug)
showregs(type, rp, (addr_t)0, 0, S_OTHER);
if (((type & ~USER) >= T_SOFTWARE_TRAP) ||
((type & ~USER) & CP_BIT)) {
u.u_addr = (char *)rp->r_pc;
u.u_code = type &~ USER;
i = SIGILL;
break;
}
}
die:
(void) spl7();
printf("BAD TRAP\n");
showregs(type, rp, addr, be, rw);
traceback((caddr_t)rp->r_sp);
if (type < TRAP_TYPES)
panic(trap_type[type]);
panic("trap");
/* NOTREACHED */
case T_TEXT_FAULT: /* system text access fault */
if (be & SE_MEMERR) {
if (lodebug)
showregs(type, rp, addr, be, rw);
memerr(MERR_SYNC, be, addr, type, rp);
/* memerr returns if recoverable, panics if not */
return;
}
goto die;
case T_DATA_FAULT: /* system data access fault */
/* may have been expected by C (e.g., bus probe) */
if (nofault) {
label_t *ftmp;
ftmp = nofault;
nofault = 0;
longjmp(ftmp);
}
if (be & SE_MEMERR) {
if (lodebug)
showregs(type, rp, addr, be, rw);
memerr(MERR_SYNC, be, addr, type, rp);
/* memerr returns if recoverable, panics if not */
return;
}
#ifndef lint
if (be & SE_SIZERR) {
if (lodebug)
showregs(type, rp, addr, be, rw);
}
/* may be fault caused by timeout on read of VME vector */
if (be & SE_TIMEOUT) {
/*
* XXX FIX ME FIX ME- need to put in an SBUS
* spurious interrupt routine
*/
#ifndef sun4c
extern int vme_read_vector(), spurious();
if (lodebug)
showregs(type, rp, addr, be, rw);
if ((addr_t) rp->r_pc == (addr_t) vme_read_vector) {
rp->r_pc = (int) spurious;
rp->r_npc = (int) spurious + 4;
return;
}
#else
/* Ignore timeout if lofault set */
if (u.u_lofault) {
if (lodebug) {
showregs(type, rp, addr, be, rw);
traceback((caddr_t)rp->r_sp);
}
rp->r_g1 = EFAULT;
rp->r_pc = u.u_lofault;
rp->r_npc = u.u_lofault + 4;
return;
}
printf("KERNEL DATA ACCESS TIMEOUT\n");
showregs(type, rp, addr, be, rw);
traceback((caddr_t)rp->r_sp);
panic("system bus error timeout\n");
/*NOTREACHED*/
#endif
}
#endif !lint
/*
* There is a bug in Campus and Calvin, where a
* protection error on a LDSTUB (and perhaps SWAP?)
* will not set the RW bit in the SER.
* bugid 1040150 (hw), 1040151 (this workaround)
*/
if (be & SE_PROTERR && rw == S_READ && is_atomic(rp))
rw = S_WRITE;
/*
* See if we can handle as pagefault. Save lofault
* across this. Here we assume that an address
* less than KERNELBASE + sizeof (int) is a user fault.
* We can do this as copy.s routines verify that the
* starting address is less than KERNELBASE before
* starting and because we know that we always have
* KERNELBASE mapped as invalid to serve as a "barrier".
* Because of SF9010 bug (see below), we must validate
* the bus error register when more than one bit is on.
*/
if ((be & (be - 1)) != 0) { /* more than one bit set? */
struct pte pte;
mmu_getpte(addr, &pte);
if (pte_valid(&pte)) {
if (be & SE_PROTERR &&
rw == S_WRITE && pte_ronly(&pte))
be = SE_PROTERR;
else
be &= ~SE_PROTERR;
} else {
be = SE_INVALID;
}
} else if ((be & ~(SE_RW|SE_INVALID|SE_PROTERR)) != 0) {
/*
* In this case, we might have a SBus Error.
* In any case, this is a gross hardware
* error that we should trap. In any case,
* we need to skip over the crap below which
* is trying to resolve page faults, etc..
*/
lofault = u.u_lofault;
res = FC_HWERR;
goto skip;
}
lofault = u.u_lofault;
u.u_lofault = 0;
if (addr < (addr_t)KERNELBASE + sizeof (int)) {
if (lofault == 0) {
goto die;
}
res = pagefault(addr,
(be & SE_PROTERR)? F_PROT: F_INVAL, rw, 0);
if (res != 0 && grow((int)addr))
res = 0;
} else {
res = pagefault(addr,
(be & SE_PROTERR)? F_PROT: F_INVAL, rw, 1);
}
/*
* Restore lofault. If we resolved the fault, exit.
* If we didn't and lofault wasn't set, die.
*/
u.u_lofault = lofault;
if (res == 0)
return;
#ifndef XXXX /* Bogus kernel page faults (P1_5 only?) */
/*
* If the pme says this is a good page, print bogosity
* message and ignore it.
*/
if (good_addr(addr)) {
struct pte pte;
mmu_getpte(addr, &pte);
if (pte_valid(&pte) && (rw != S_WRITE ||
!pte_ronly(&pte))) {
printf("BOGUS page fault on valid page: ");
showregs(type, rp, addr, be, rw);
return;
}
}
#endif XXXX
skip:
if (lofault == 0)
goto die;
/*
* Cannot resolve fault. Return to lofault.
*/
if (lodebug) {
showregs(type, rp, addr, be, rw);
traceback((caddr_t)rp->r_sp);
}
if (FC_CODE(res) == FC_OBJERR)
res = FC_ERRNO(res);
else
res = EFAULT;
rp->r_g1 = res;
rp->r_pc = u.u_lofault;
rp->r_npc = u.u_lofault + 4;
return;
case T_DATA_FAULT + USER: /* user data access fault */
case T_TEXT_FAULT + USER: /* user text access fault */
/*
* The SPARC processor prefetches instructions.
* The bus error register may also reflect faults
* that occur during prefetch in addition to the one
* that caused the current fault. For example:
* st [proterr] ! end of page
* ... ! invalid page
* will cause both SE_INVALID and SE_PROTERR.
#ifdef notdef
* If any of the unusual bus error register bits
* (SE_TIMEOUT, SE_SBBERR, SE_SIZERR)
* are on, we kill the user even though these errors
* could have occurred during some previous (possibly
* annulled) fetch. We do this because it is simple
* and the user had to have done something weird in
* order to get this. The downside of this is that
* the current pc is not necessarily the instruction
* that caused the bad fault.
#endif notdef
* The gate array version of sparc (SF9010) has a bug
* which works as follows: When the chip does a prefetch
* to an invalid page the board loads garbage into the
* chip. If this garbage looks like a branch instruction
* a prefetch will be generated to some random address
* even though the branch is annulled. This can cause
* bits in the bus error register to be set. In this
* case we have to validate the bus error register bits.
* We only handle true SE_INVALID and SE_PROTERR faults.
* SE_MEMERR is given to memerr(), which will handle
* recoverable parity errors.
* All others cause the user to die.
*/
if (be & SE_MEMERR) {
memerr(MERR_SYNC, be, addr, type & ~USER, rp);
/* memerr returns if recoverable, panics if not */
goto out;
}
/*
* There is a bug in Campus and Calvin, where a
* protection error on a LDSTUB (and perhaps SWAP?)
* will not set the RW bit in the SER.
* bugid 1040150 (hw), 1040151 (this workaround)
*/
if (be & SE_PROTERR && rw == S_READ && is_atomic(rp))
rw = S_WRITE;
if ((be & (be - 1)) != 0) { /* more than one bit set? */
struct pte pte;
mmu_getpte(addr, &pte);
if (pte_valid(&pte)) {
if (be & SE_PROTERR && (pte_konly(&pte) ||
(rw == S_WRITE && pte_ronly(&pte))))
be = SE_PROTERR;
else
be &= ~(SE_PROTERR|SE_INVALID);
} else {
be = SE_INVALID;
}
}
if ((be & ~(SE_RW|SE_INVALID|SE_PROTERR)) != 0) {
/*
* There was an error in the buserr register besides
* invalid and protection - we cannot handle it.
*/
res = FC_HWERR;
} else {
#ifdef notdef
/*
* If SE_INVALID and SE_PROTERR were both on
* figure out which one it really was.
*/
if (be == (SE_INVALID|SE_PROTERR)) {
struct pte pte;
mmu_getpte(addr, &pte);
if (pte_valid(&pte))
be = SE_PROTERR;
else
be = SE_INVALID;
}
#endif notdef
if (faultdebug) {
char *fault_str;
switch (rw) {
case S_READ:
fault_str = "read";
break;
case S_WRITE:
fault_str = "write";
break;
case S_EXEC:
fault_str = "exec";
break;
default:
fault_str = "";
break;
}
printf("user %s fault: addr=0x%x be=0x%x\n",
fault_str, addr, be);
}
#ifdef SUN4_110
if (rw == S_EXEC)
fault_type = T_TEXT_FAULT;
else
fault_type = T_DATA_FAULT;
#endif
res = pagefault(addr,
(be & SE_PROTERR)? F_PROT: F_INVAL, rw, 0);
if (res == 0)
return;
if (type == T_DATA_FAULT + USER) {
/*
* Grow the stack automatically.
*/
if (grow((int)addr))
goto out;
}
}
if (tudebug)
showregs(type, rp, addr, be, rw);
/*
* In the case where both pagefault and grow fail,
* set the code to the value provided by pagefault.
* We map errors relating to the underlying object
* to SIGBUS, others to SIGSEGV.
*/
u.u_addr = (char *)addr;
u.u_code = res;
switch (FC_CODE(res)) {
case FC_HWERR:
case FC_OBJERR:
i = SIGBUS;
break;
default:
i = SIGSEGV;
}
break;
case T_ALIGNMENT + USER: /* user alignment error */
if (tudebug)
showregs(type, rp, (addr_t)0, 0, S_OTHER);
/*
* if the user has to do unaligned references
* the ugly stuff gets done here
*/
alignfaults++;
if (u.u_pcb.pcb_flags & FIX_ALIGNMENT) {
if (do_unaligned(rp)) {
rp->r_pc = rp->r_npc;
rp->r_npc += 4;
return;
}
}
u.u_addr = (char *)rp->r_pc; /* not quite right */
u.u_code = FC_ALIGN;
i = SIGBUS;
break;
case T_PRIV_INSTR + USER: /* privileged instruction fault */
if (tudebug)
showregs(type, rp, (addr_t)0, 0, S_OTHER);
u.u_addr = (char *)rp->r_pc;
u.u_code = ILL_PRIVINSTR_FAULT;
i = SIGILL;
break;
case T_UNIMP_INSTR + USER: /* illegal instruction fault */
if (tudebug)
showregs(type, rp, (addr_t)0, 0, S_OTHER);
switch (simulate_unimp(rp)) {
case SIMU_SUCCESS:
/* skip the successfully simulated instruction */
rp->r_pc = rp->r_npc;
rp->r_npc += 4;
return;
case SIMU_FAULT:
/*
* tried to reference an illegal address
* u.u_{addr, code} set in simulation called above
*/
i = SIGSEGV;
break;
case SIMU_DZERO:
/* a simulated division had a divisor of zero */
u.u_addr = (char *)rp->r_pc;
u.u_code = (int)FPE_INTDIV_TRAP;
i = SIGFPE;
break;
case SIMU_ILLEGAL:
default:
/* no such instruction */
u.u_addr = (char *)rp->r_pc;
u.u_code = ILL_ILLINSTR_FAULT;
i = SIGILL;
break;
}
break;
case T_DIV0 + USER: /* integer divide by zero */
if (tudebug && tudebugfpe)
showregs(type, rp, (addr_t)0, 0, S_OTHER);
rp->r_pc = rp->r_npc; /* skip trap instruction */
rp->r_npc += 4;
u.u_addr = (char *)rp->r_pc;
u.u_code = (int)FPE_INTDIV_TRAP;
i = SIGFPE;
break;
case T_INT_OVERFLOW + USER: /* integer overflow */
if (tudebug && tudebugfpe)
showregs(type, rp, (addr_t)0, 0, S_OTHER);
rp->r_pc = rp->r_npc; /* skip trap instruction */
rp->r_npc += 4;
u.u_addr = (char *)rp->r_pc;
u.u_code = (int)FPE_INTOVF_TRAP;
i = SIGFPE;
break;
case T_FP_EXCEPTION + USER: /* FPU arithmetic exception */
case T_FP_EXCEPTION:
if (tudebug && tudebugfpe)
showregs(type, rp, addr, 0, S_OTHER);
u.u_code = be;
u.u_addr = (char *)addr;
i = SIGFPE;
break;
case T_BREAKPOINT + USER: /* breakpoint trap (t 1) */
if (tudebug && tudebugbpt)
showregs(type, rp, (addr_t)0, 0, S_OTHER);
i = SIGTRAP;
break;
case T_TAG_OVERFLOW + USER: /* tag overflow (taddcctv, tsubcctv) */
if (tudebug)
showregs(type, rp, (addr_t)0, 0, S_OTHER);
u.u_addr = (char *)rp->r_pc;
u.u_code = EMT_TAG;
i = SIGEMT;
break;
case T_WIN_OVERFLOW + USER: /* finish user window overflow */
/*
* This trap is entered from sys_rtt in locore.s when, upon
* return to user is is found that there are user windows in
* u.u_wbuf. This happens because they could not be saved on
* the user stack, either because it wasn't resident or because
* it was misaligned. We flush the user's windows to make
* sure pcb_wbcnt does not change while we are doing this.
*/
{
register int j;
flush_user_windows();
for (j = 0; j < u.u_pcb.pcb_wbcnt; j++) {
register char *sp;
sp = u.u_pcb.pcb_spbuf[j];
if (((int)sp & (STACK_ALIGN-1)) ||
copyout((caddr_t)&u.u_pcb.pcb_wbuf[j], sp,
sizeof (struct rwindow)) != 0) {
u.u_addr = (char *)rp->r_pc;
u.u_code = ILL_STACK;
i = SIGILL;
break;
}
}
if (i == 0) {
u.u_pcb.pcb_wbcnt = 0;
return;
}
if (tudebug)
showregs(type, rp, (addr_t)0, 0, S_OTHER);
}
break;
case T_AST + USER: /* profiling or resched psuedo trap */
astoff();
if ((p->p_flag & SOWEUPC) && u.u_prof.pr_scale) {
addupc(rp->r_pc, &u.u_prof, 1);
p->p_flag &= ~SOWEUPC;
}
goto out;
}
/*
* Before trying to send a signal to the process, make
* sure it's not masked, otherwise we'll loop taking
* synchronous traps until someone notices and kills the
* process.
*/
mask = sigmask(i);
if ((mask & p->p_sigmask) && (i != SIGFPE)) {
u.u_signal[i] = SIG_DFL;
p->p_sigignore &= ~mask;
p->p_sigcatch &= ~mask;
p->p_sigmask &= ~mask;
}
psignal(p, i);
out:
if (((p->p_flag & STRC) == 0 && p->p_cursig) || ISSIG(p, 0))
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) spl6();
setrq(p);
u.u_ru.ru_nivcsw++;
swtch();
(void) spl0();
}
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(rp->r_pc, &u.u_prof, ticks);
}
curpri = p->p_pri;
}
#ifdef SYSCALLTRACE
int syscalltrace = 0;
#endif
#ifdef SUNDBE
/*
* We do a part of the system call dispatcher in locore.s. We want to
* eliminate usage of setjmp in C functions (setjmp turns off the
* optimization of the function) and reduce the level of function
* call nesting in case the system call will sleep. Reducing the
* level of nesting reduces the number of SPARC reg. windows that
* will be flushed to the stack in context switch.
*
* We justify the increased complexity of the code by significantly
* reducing the overhead of all system calls.
*/
#ifdef SYSCALLOPTIM_DEBUG
/*
* Turn on SYSCALLOPTIM_DEBUG for debugging.
*/
syscall_debug(rp)
register struct regs *rp;
{
int (*syscall_preamble())();
void syscall_postamble();
void syscall_userprof();
struct timeval syst;
int syst_flag;
int pid;
int (*f)();
if (u.u_prof.pr_scale) {
syst = u.u_ru.ru_stime;
syst_flag = 1;
pid = u.u_procp->p_pid;
} else
syst_flag = 0;
f = syscall_preamble(rp);
if (f == NULL)
goto bad;
if (setjmp(&u.u_qsave)) {
if (u.u_error == 0 && u.u_eosys == NORMALRETURN)
u.u_error = EINTR;
} else {
(void) (*f)(u.u_ap);
}
bad:
syscall_postamble(rp);
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) spl6();
setrq(u.u_procp);
u.u_ru.ru_nivcsw++;
swtch();
(void) spl0();
}
curpri = u.u_procp->p_pri;
if (syst_flag)
syscall_userprof(rp, pid, syst.tv_sec, syst.tv_usec);
}
#endif /* SYSCALLOPTIM_DEBUG */
int (*syscall_preamble(rp))()
register struct regs *rp;
{
register struct user *up; /* hand optimization of global variable access */
register struct proc *procp; /* hand optimization of global variable access */
register unsigned code;
register struct sysent *callp;
register struct proc *p;
up = &u;
procp = up->u_procp;
code = rp->r_g1;
cnt.v_syscall++;
/*
* take any pending floating point exceptions now
*/
syncfpu(rp);
up->u_ar0 = (int *)rp;
if (code >= nsysent)
callp = &sysent[63];
else
callp = &sysent[code];
up->u_error = 0;
if ((callp->sy_narg > 6) || (procp->p_flag & STRCSYS)) {
/*
* Copy registers and stack arguments into u.u_arg array.
*/
up->u_arg[0] = rp->r_o0; up->u_arg[1] = rp->r_o1;
up->u_arg[2] = rp->r_o2; up->u_arg[3] = rp->r_o3;
up->u_arg[4] = rp->r_o4; up->u_arg[5] = rp->r_o5;
if (copyin((addr_t)rp->r_sp + MINFRAME, (addr_t)&up->u_arg[6],
(u_int)((callp->sy_narg - 6) * sizeof (int)))) {
up->u_eosys = NORMALRETURN;
up->u_error = EFAULT;
goto bad;
}
up->u_ap = up->u_arg;
} else {
/*
* Use the registers as arguments.
*/
up->u_ap = &rp->r_o0;
}
up->u_r.r_val1 = 0;
up->u_r.r_val2 = rp->r_o1;
up->u_eosys = NORMALRETURN;
trace6(TR_SYSCALL, code, u.u_arg[0], u.u_arg[1], u.u_arg[2],
u.u_arg[3], u.u_arg[4]);
#ifdef SYSCALLTRACE
if (syscalltrace) {
register int i;
char *cp;
printf("%d: ", u.u_procp->p_pid);
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_ap[i]);
cp = ", ";
}
if (i)
printf(")");
printf("\n");
}
#endif
/*
* If tracing sys calls signal a trap.
* System call code is slipped into u_arg[7].
* No system call has 8 args (at the present time!).
*/
if ((procp->p_flag & STRCSYS) && code != 0) {
int s, save_sigmask;
procp->p_flag &= ~STRCSYS;
up->u_arg[7] = code;
psignal(procp, SIGTRAP);
s = splhigh();
save_sigmask = procp->p_sigmask;
procp->p_sigmask = ~sigmask(SIGTRAP);
(void) issig(0);
procp->p_sigmask = save_sigmask;
(void) splx(s);
}
/*
if (setjmp(&u.u_qsave)) {
if (up->u_error == 0 && up->u_eosys == NORMALRETURN)
up->u_error = EINTR;
} else {
(*(callp->sy_call))(up->u_ap);
}
*/
return (callp->sy_call);
bad:
return (NULL);
}
void
syscall_postamble(rp)
register struct regs *rp;
{
register struct user *up;
register struct proc *procp;
register unsigned code;
register struct sysent *callp;
up = &u;
procp = up->u_procp;
if (up->u_eosys == NORMALRETURN) {
if (up->u_error) {
#ifdef SYSCALLTRACE
if (syscalltrace)
printf("syscall: error=%d\n", u.u_error);
#endif
rp->r_o0 = up->u_error;
rp->r_psr |= PSR_C; /* set carry bit */
} else {
rp->r_psr &= ~PSR_C; /* reset carry bit */
rp->r_o0 = up->u_r.r_val1;
rp->r_o1 = up->u_r.r_val2;
}
/*
* The default action is to redo the trap instruction.
* We increment the pc and npc past it for NORMALRETURN.
* JUSTRETURN has set up a new pc and npc already.
* RESTARTSYS automatically restarts by leaving pc and npc
* alone.
*/
rp->r_pc = rp->r_npc;
rp->r_npc += 4;
}
/* If tracing sys calls signal a trap */
if (procp->p_flag & STRCSYS) {
procp->p_flag &= ~STRCSYS;
up->u_arg[7] = 0;
psignal(procp, SIGTRAP);
}
if (((procp->p_flag & STRC) == 0 && procp->p_cursig) || ISSIG(procp, 0))
psig();
procp->p_pri = procp->p_usrpri;
}
void
syscall_userprof(rp, pid, sec, usec)
register struct regs *rp;
int pid;
long sec;
long usec;
{
register int ticks;
struct timeval *tv = &u.u_ru.ru_stime;
/*
* If pid != pp->p_pid, then we are the child returning from
* a "fork" or "vfork" system call. In this case, reset
* "syst", since our time was reset in fork.
*/
if (pid != u.u_procp->p_pid) {
sec = usec = 0;
}
ticks = ((tv->tv_sec - sec) * 1000 +
(tv->tv_usec - usec) / 1000) / (tick / 1000);
if (ticks)
addupc(rp->r_pc, &u.u_prof, ticks);
}
#else /* SUNDBE SYSCALLOPTIM */
/*
* Called from the trap handler when a system call occurs
*/
void
syscall(rp)
register struct regs *rp;
{
register unsigned code;
register struct sysent *callp;
register struct proc *p;
register syst_flag;
struct timeval syst;
register short pid;
code = rp->r_g1;
cnt.v_syscall++;
if (u.u_prof.pr_scale) {
syst = u.u_ru.ru_stime;
syst_flag = 1;
pid = u.u_procp->p_pid;
} else
syst_flag = 0;
#ifdef notdef
if (!USERMODE(rp->r_psr))
panic("syscall");
#endif
/*
* take any pending floating point exceptions now
*/
syncfpu(rp);
u.u_ar0 = (int *)rp;
if (code >= nsysent)
callp = &sysent[63];
else
callp = &sysent[code];
u.u_error = 0;
if ((callp->sy_narg > 6) || (u.u_procp->p_flag & STRCSYS)) {
/*
* Copy registers and stack arguments into u.u_arg array.
*/
u.u_arg[0] = rp->r_o0; u.u_arg[1] = rp->r_o1;
u.u_arg[2] = rp->r_o2; u.u_arg[3] = rp->r_o3;
u.u_arg[4] = rp->r_o4; u.u_arg[5] = rp->r_o5;
if (copyin((addr_t)rp->r_sp + MINFRAME, (addr_t)&u.u_arg[6],
(u_int)((callp->sy_narg - 6) * sizeof (int)))) {
u.u_error = EFAULT;
goto bad;
}
u.u_ap = u.u_arg;
} else {
/*
* Use the registers as arguments.
*/
u.u_ap = &rp->r_o0;
}
u.u_r.r_val1 = 0;
u.u_r.r_val2 = rp->r_o1;
if (setjmp(&u.u_qsave)) {
if (u.u_error == 0 && u.u_eosys == NORMALRETURN)
u.u_error = EINTR;
} else {
u.u_eosys = NORMALRETURN;
trace6(TR_SYSCALL, code, u.u_arg[0], u.u_arg[1], u.u_arg[2],
u.u_arg[3], u.u_arg[4]);
#ifdef SYSCALLTRACE
if (syscalltrace) {
register int i;
char *cp;
printf("%d: ", u.u_procp->p_pid);
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_ap[i]);
cp = ", ";
}
if (i)
printf(")");
printf("\n");
}
#endif
/*
* If tracing sys calls signal a trap.
* System call code is slipped into u_arg[7].
* No system call has 8 args (at the present time!).
*/
if ((u.u_procp->p_flag & STRCSYS) && code != 0) {
int s, save_sigmask;
u.u_procp->p_flag &= ~STRCSYS;
u.u_arg[7] = code;
psignal(u.u_procp, SIGTRAP);
s = splhigh();
save_sigmask = u.u_procp->p_sigmask;
u.u_procp->p_sigmask = ~sigmask(SIGTRAP);
(void) issig(0);
u.u_procp->p_sigmask = save_sigmask;
(void) splx(s);
}
(*(callp->sy_call))(u.u_ap);
}
if (u.u_eosys == NORMALRETURN) {
if (u.u_error) {
bad:
#ifdef SYSCALLTRACE
if (syscalltrace)
printf("syscall: error=%d\n", u.u_error);
#endif
rp->r_o0 = u.u_error;
rp->r_psr |= PSR_C; /* set carry bit */
} else {
rp->r_psr &= ~PSR_C; /* reset carry bit */
rp->r_o0 = u.u_r.r_val1;
rp->r_o1 = u.u_r.r_val2;
}
/*
* The default action is to redo the trap instruction.
* We increment the pc and npc past it for NORMALRETURN.
* JUSTRETURN has set up a new pc and npc already.
* RESTARTSYS automatically restarts by leaving pc and npc
* alone.
*/
rp->r_pc = rp->r_npc;
rp->r_npc += 4;
}
/* If tracing sys calls signal a trap */
if (u.u_procp->p_flag & STRCSYS) {
u.u_procp->p_flag &= ~STRCSYS;
u.u_arg[7] = 0;
psignal(u.u_procp, SIGTRAP);
}
p = u.u_procp;
if (((p->p_flag & STRC) == 0 && p->p_cursig) || ISSIG(p, 0))
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) spl6();
setrq(p);
u.u_ru.ru_nivcsw++;
swtch();
(void) spl0();
}
if (syst_flag && u.u_prof.pr_scale) {
register int ticks;
struct timeval *tv = &u.u_ru.ru_stime;
/*
* If pid != pp->p_pid, then we are the child returning from
* a "fork" or "vfork" system call. In this case, reset
* "syst", since our time was reset in fork.
*/
if (pid != p->p_pid)
timerclear(&syst);
ticks = ((tv->tv_sec - syst.tv_sec) * 1000 +
(tv->tv_usec - syst.tv_usec) / 1000) / (tick / 1000);
if (ticks)
addupc(rp->r_pc, &u.u_prof, ticks);
}
curpri = p->p_pri;
}
#endif /* SUNDBE SYSCALLOPTIM */
/*
* Indirect system call.
* Used to be handled above, in syscall, but then everyone
* was paying a performance penalty for this rarely-used
* (and questionable) feature.
*/
indir()
{
register unsigned code;
register int *sp, *dp;
register struct sysent *callp;
register int a;
code = (unsigned) u.u_ap[0];
if (code == 0 || code >= nsysent)
callp = &sysent[63];
else
callp = &sysent[code];
/*
* Shift the valid args down 1 and copy into u.u_arg array.
*/
a = MIN(callp->sy_narg, 5);
sp = &u.u_ap[1];
dp = &u.u_arg[0];
while (sp <= (int *)&u.u_ap[a]) {
*dp++ = *sp++;
}
/*
* Copy in any new args.
*/
if (callp->sy_narg > 5) {
if (copyin((addr_t)u.u_ar0[SP] + MINFRAME,
(addr_t)&u.u_arg[5],
(u_int)((callp->sy_narg - 5) * sizeof (int)))) {
u.u_error = EFAULT;
return;
}
}
u.u_ap = u.u_arg;
#ifdef SYSCALLTRACE
if (syscalltrace) {
register int i;
char *cp;
printf("%d: ", u.u_procp->p_pid);
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_ap[i]);
cp = ", ";
}
if (i)
printf(")");
printf("\n");
}
#endif
if (u.u_procp->p_flag & STRCSYS) {
int s, save_sigmask;
u.u_procp->p_flag &= ~STRCSYS;
u.u_arg[7] = code;
psignal(u.u_procp, SIGTRAP);
s = splhigh();
save_sigmask = u.u_procp->p_sigmask;
u.u_procp->p_sigmask = ~sigmask(SIGTRAP);
(void) issig(0);
u.u_procp->p_sigmask = save_sigmask;
(void) splx(s);
}
#ifdef SUNDBE
/*
* The below is inline semsys() from os/ipc_sem.c
*/
if (code == SYS_semsys) {
int semctl(), semget(), semop();
static int (*calls[])() = {semctl, semget, semop};
register struct a {
uint id; /* function code id */
} *uap = (struct a *)u.u_ap;
if (uap->id > 2) {
u.u_error = EINVAL;
return;
}
u.u_ap = &u.u_arg[1];
#ifdef TAILCALLOPTIM
tailcall0(*calls[uap->id]);
/* tailcall0 never return */
#else /* TAILCALLOPTIM */
(*calls[uap->id])();
return;
#endif /* TAILCALLOPTIM */
}
#endif /* SUNDBE INLINE_SEMSYS */
(*(callp->sy_call))(u.u_ap);
}
/*
* 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);
}
/*
* Print out a traceback for kernel traps
*/
traceback(sp)
caddr_t sp;
{
register u_int tospage;
register struct frame *fp;
static int done = 0;
if (panicstr && done++ > 0)
return;
if ((int)sp & (STACK_ALIGN-1)) {
printf("traceback: misaligned sp = %x\n", sp);
return;
}
flush_windows();
tospage = (u_int)btoc(sp);
fp = (struct frame *)sp;
printf("Begin traceback... sp = %x\n", sp);
while (btoc((u_int)fp) == tospage) {
if (fp == fp->fr_savfp) {
printf("FP loop at %x", fp);
break;
}
printf("Called from %x, fp=%x, args=%x %x %x %x %x %x\n",
fp->fr_savpc, fp->fr_savfp,
fp->fr_arg[0], fp->fr_arg[1], fp->fr_arg[2],
fp->fr_arg[3], fp->fr_arg[4], fp->fr_arg[5]);
#ifdef notdef
printf("\tl0-l7: %x, %x, %x, %x, %x, %x, %x, %x\n",
fp->fr_local[0], fp->fr_local[1],
fp->fr_local[2], fp->fr_local[3],
fp->fr_local[4], fp->fr_local[5],
fp->fr_local[6], fp->fr_local[7]);
#endif
fp = fp->fr_savfp;
if (fp == 0)
break;
}
printf("End traceback...\n");
#ifndef SAS
#ifdef VAC
vac_flush((caddr_t)&msgbuf, sizeof (msgbuf)); /* push msgbuf to mem */
#endif VAC
DELAY(2000000);
#endif SAS
}
/*
* General system stack backtrace
*/
tracedump()
{
label_t l;
#ifdef lint
int x;
x = setjmp(&l);
x = x;
#else
setjmp(&l);
#endif
traceback((caddr_t)l.val[1]);
}
#ifdef TRAPWINDOW
long trap_window[25];
#endif TRAPWINDOW
/*
* Print out debugging info.
*/
showregs(type, rp, addr, be, rw)
register unsigned type;
register struct regs *rp;
addr_t addr;
u_int be;
enum seg_rw rw;
{
int s;
extern u_int map_getctx();
s = spl7();
type &= ~USER;
if (masterprocp == NULL || peekc(u.u_comm) == -1)
printf("(unknown): ");
else
printf("pid %d, `%s': ", masterprocp->p_pid, u.u_comm);
if (type < TRAP_TYPES)
printf("%s\n", trap_type[type]);
else switch (type) {
case T_SYSCALL:
printf("syscall trap:\n");
break;
case T_BREAKPOINT:
printf("breakpoint trap:\n");
break;
case T_DIV0:
printf("zero divide trap:\n");
break;
case T_FLUSH_WINDOWS:
printf("flush windows trap:\n");
break;
case T_SPURIOUS:
printf("spurious interrupt:\n");
break;
case T_AST:
printf("AST\n");
break;
default:
if (type >= T_SOFTWARE_TRAP && type <= T_ESOFTWARE_TRAP)
printf("software trap 0x%x\n", type - T_SOFTWARE_TRAP);
else
printf("bad trap = %d\n", type);
break;
}
if (type == T_DATA_FAULT || type == T_TEXT_FAULT) {
if (good_addr(addr)) {
struct pte pte;
mmu_getpte(addr, &pte);
printf("%s %s fault at addr=0x%x, pme=0x%x\n",
(USERMODE(rp->r_psr)? "user": "kernel"),
(rw == S_WRITE? "write": "read"),
addr, *(int *)&pte);
} else {
printf("bad %s %s fault at addr=0x%x\n",
(USERMODE(rp->r_psr)? "user": "kernel"),
(rw == S_WRITE? "write": "read"),
addr);
}
printf("Sync Error Reg %b\n", be, SYNCERR_BITS);
} else if (addr) {
printf("addr=0x%x\n", addr);
}
printf("pc=0x%x, sp=0x%x, psr=0x%x, context=0x%x\n",
rp->r_pc, rp->r_sp, rp->r_psr, map_getctx());
if (USERMODE(rp->r_psr)) {
printf("o0-o7: %x, %x, %x, %x, %x, %x, %x, %x\n",
rp->r_o0, rp->r_o1, rp->r_o2, rp->r_o3,
rp->r_o4, rp->r_o5, rp->r_o6, rp->r_o7);
}
printf("g1-g7: %x, %x, %x, %x, %x, %x, %x\n",
rp->r_g1, rp->r_g2, rp->r_g3,
rp->r_g4, rp->r_g5, rp->r_g6, rp->r_g7);
#ifdef TRAPWINDOW
printf("trap_window: wim=%x\n", trap_window[24]);
printf("o0-o7: %x, %x, %x, %x, %x, %x, %x, %x\n",
trap_window[0], trap_window[1], trap_window[2], trap_window[3],
trap_window[4], trap_window[5], trap_window[6], trap_window[7]);
printf("l0-l7: %x, %x, %x, %x, %x, %x, %x, %x\n",
trap_window[8], trap_window[9], trap_window[10], trap_window[11],
trap_window[12], trap_window[13], trap_window[14], trap_window[15]);
printf("i0-i7: %x, %x, %x, %x, %x, %x, %x, %x\n",
trap_window[16], trap_window[17], trap_window[18], trap_window[19],
trap_window[20], trap_window[21], trap_window[22], trap_window[23]);
#endif TRAPWINDOW
vac_flush((caddr_t)&msgbuf, sizeof (msgbuf)); /* push msgbuf to mem */
#ifndef SAS
if (tudebug > 1 && (boothowto & RB_DEBUG)) {
CALL_DEBUG();
}
#endif SAS
(void) splx(s);
}
/*ARGSUSED*/
sys_rttchk(p, l7, sp)
struct proc *p;
caddr_t l7, sp;
{
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) spl6();
setrq(p);
u.u_ru.ru_nivcsw++;
swtch();
(void) spl0();
}
#ifdef notdef
/* XXX - need to do something about this */
if (syst_flag && u.u_prof.pr_scale) {
register int ticks;
struct timeval *tv = &u.u_ru.ru_stime;
/*
* If pid != pp->p_pid, then we are the child returning from
* a "fork" or "vfork" system call. In this case, reset
* "syst", since our time was reset in fork.
*/
if (pid != p->p_pid)
timerclear(&syst);
ticks = ((tv->tv_sec - syst.tv_sec) * 1000 +
(tv->tv_usec - syst.tv_usec) / 1000) / (tick / 1000);
if (ticks)
addupc(rp->r_pc, &u.u_prof, ticks);
}
#endif
curpri = p->p_pri;
}
/*
* unimplemented and unaligned instruction simulation support
*/
static int
getreg(rgs, rw, reg, val)
u_int *rgs, *rw, reg;
u_int *val;
{
if (reg == 0)
*val = 0;
else if (reg < 16)
*val = rgs[reg];
else {
if ((*val = fuword((int *)&rw[reg - 16])) == -1) {
if (fubyte((caddr_t)&rw[reg - 16]) == -1) {
return (-1);
}
}
}
return (0);
}
static int
putreg(data, rgs, rw, reg)
u_int data, *rgs, *rw, reg;
{
if (reg == 0)
return (0); /* can't write g0 */
if (reg < 16)
rgs[reg] = data;
else {
if (suword((caddr_t)&rw[reg - 16], (int)data) != 0)
return (-1);
}
return (0);
}
u_int dev_null = 0; /* a place to store junk */
/*
* calculate the address of a register that will eventually get
* restored for a user so it can be changed.
*/
u_int *
getregaddr(rgs, rw, reg)
u_int rgs[], rw[];
u_int reg;
{
if (reg == 0)
return (&dev_null);
if (reg < 16)
return (&rgs[reg]);
else
return (&rw[reg - 16]);
}
/*
* For the sake of those who must be compatible with unaligned
* architectures, users can link their programs to use a
* corrective trap handler that will fix unaligned references
* a special trap #6 (T_FIX_ALIGN) enables this 'feature'.
*/
char *sizestr[] = {"word", "byte", "halfword", "double"};
do_unaligned(rp)
register struct regs *rp;
{
register u_int inst;
register u_int rd, rs1, rs2;
register u_int *rgs;
register u_int *rw;
register u_int addr;
register int sz;
register int floatflg;
register int immflg;
u_int val;
extern void _fp_read_pfreg();
extern void _fp_write_pfreg();
union ud {
double d;
u_int i[2];
u_short s[4];
u_char c[8];
} data;
inst = fuword((caddr_t)rp->r_pc); /* get the instruction */
rd = (inst >> 25) & 0x1f;
rs1 = (inst >> 14) & 0x1f;
rs2 = inst & 0x1f;
floatflg = (inst >> 24) & 1;
immflg = (inst >> 13) & 1;
switch ((inst >> 19) & 3) { /* map size bits to a number */
case 0: sz = 4; break;
case 1: printf("alignment botch\n"); return (0);
case 2: sz = 2; break;
case 3: sz = 8; break;
}
if (aligndebug) {
printf("unaligned access at 0x%x, instruction: 0x%x\n",
rp->r_pc, inst);
printf("type %s %s %s\n",
(((inst >> 21) & 1) ? "st" : "ld"),
(((inst >> 22) & 1) ? "signed" : "unsigned"),
sizestr[((inst >> 19) & 3)]);
printf("rd = %d, rs1 = %d, rs2 = %d, imm13 = 0x%x\n",
rd, rs1, rs2, (inst & 0x1fff));
}
/* if not load or store, or to alternate space do nothing */
if (((inst >> 30) != 3) ||
(immflg == 0 && ((inst >> 5) & 0xff)))
return (0);
flush_user_windows_to_stack(); /* flush windows into memory */
rgs = (u_int *)&rp->r_y; /* globals and outs */
rw = (u_int *)rp->r_sp; /* ins and locals */
/* calculate address, get first register */
if (getreg(rgs, rw, rs1, &val) == -1)
goto badret;
addr = val;
/* check immediate bit and use immediate field or reg (rs2) */
if (immflg) {
register int imm;
imm = inst & 0x1fff; /* mask out immediate field */
imm <<= 19; /* sign extend it */
imm >>= 19;
addr += imm; /* compute address */
} else {
if (getreg(rgs, rw, rs2, &val) == -1)
goto badret;
addr += val;
}
if (aligndebug)
printf("addr = 0x%x\n", addr);
/* a single bit differentiates ld and st */
if ((inst >> 21) & 1) { /* store */
if (floatflg) { /* if fp read fpu reg */
_fp_read_pfreg((u_int *)&data.i[0], rd);
if (sz == 8)
_fp_read_pfreg((u_int *)&data.i[1], rd+1);
} else {
if (getreg(rgs, rw, rd, &val) == -1)
goto badret;
data.i[0] = val;
if (sz == 8) {
if (getreg(rgs, rw, rd+1, &val) == -1)
goto badret;
data.i[1] = val;
}
}
if (aligndebug) {
printf("data %x %x %x %x %x %x %x %x\n",
data.c[0], data.c[1], data.c[2], data.c[3],
data.c[4], data.c[5], data.c[6], data.c[7]);
}
if (sz == 2) {
if (copyout((caddr_t)&data.s[1], (caddr_t)addr,
(u_int)sz) == -1)
goto badret;
} else {
if (copyout((caddr_t)&data.i[0], (caddr_t)addr,
(u_int)sz) == -1)
goto badret;
}
} else { /* load */
if (sz == 2) {
if (copyin((caddr_t)addr, (caddr_t)&data.s[1],
(u_int)sz) == -1)
goto badret;
/* if signed and the sign bit is set extend it */
if (((inst >> 22) & 1) && ((data.s[1] >> 15) & 1))
data.s[0] = -1; /* extend sign bit */
else
data.s[0] = 0; /* clear upper 16 bits */
} else
if (copyin((caddr_t)addr, (caddr_t)&data.i[0],
(u_int)sz) == -1)
goto badret;
if (aligndebug) {
printf("data %x %x %x %x %x %x %x %x\n",
data.c[0], data.c[1], data.c[2], data.c[3],
data.c[4], data.c[5], data.c[6], data.c[7]);
}
if (floatflg) { /* if fp, write fpu reg */
_fp_write_pfreg((u_int *)&data.i[0], rd);
if (sz == 8)
_fp_write_pfreg((u_int *)&data.i[1], rd+1);
} else {
if (putreg(data.i[0], rgs, rw, rd) == -1)
goto badret;
if (sz == 8)
if (putreg(data.i[1], rgs, rw, rd+1) == -1)
goto badret;
}
}
return (SIMU_SUCCESS);
badret:
return (SIMU_FAULT);
}
/*
* simulate the swap instruction as best as possible
*/
do_swap(rp, inst)
register struct regs *rp;
register u_int inst;
{
register u_int rd, rs1, rs2, addr;
register u_int *rgs, *rw;
register int immflg, s;
u_int val, newval;
rd = (inst >> 25) & 0x1f;
rs1 = (inst >> 14) & 0x1f;
rs2 = inst & 0x1f;
immflg = (inst >> 13) & 1;
/*
* It assumed that the register windows have been flushed
* to the stack. Getreg, putreg, suword, and fuword all
* depend upon this.
*/
rgs = (u_int *)&rp->r_y; /* globals and outs */
rw = (u_int *)rp->r_sp; /* ins and locals */
/* calculate address, get first register */
if (getreg(rgs, rw, rs1, &val) == -1)
goto badret;
addr = val;
/* check immediate bit and use immediate field or reg (rs2) */
if (immflg) {
s = inst & 0x1fff; /* mask out immediate field */
s <<= 19; /* sign extend it */
s >>= 19;
addr += s; /* compute address */
} else {
if (getreg(rgs, rw, rs2, &val) == -1)
goto badret;
addr += val;
}
/* raise priority (atomic swap operation) */
s = spl6();
/* read memory at source address */
if ((val = fuword((caddr_t)addr)) == -1) {
if ((val = fubyte((caddr_t)addr)) == -1)
goto badret;
else
val = -1;
}
newval = val;
/* write src address with value from destination reg */
if (getreg(rgs, rw, rd, &val) == -1)
goto badret;
if (suword((caddr_t)addr, (int) val) == -1)
goto badret;
/* update destination reg with value from memory */
if (putreg(newval, rgs, rw, rd) == -1) {
addr = (int)getregaddr(rgs, rw, rd);
goto badret;
}
/* restore priority and return success or failure */
(void) splx(s);
return (SIMU_SUCCESS);
badret:
u.u_addr = (char *)addr;
u.u_code = FC_NOMAP;
(void) splx(s);
return (SIMU_FAULT);
}
/*
* simulate unimplemented instructions (swap, mul, div)
*/
simulate_unimp(rp)
struct regs *rp;
{
register u_int inst, rv;
u_int val, wasmul = 1;
if ((inst = fuword((caddr_t)rp->r_pc)) == -1){
/*
* -1 is an illegal instruction
* or a error in fuword, give up now
*/
return (SIMU_ILLEGAL);
}
/*
* simulation depends on register windows being stack resident
*/
flush_user_windows_to_stack();
/*
* check for the unimplemented swap instruction
* note: swapa is not used anywhere and is not currently supported
*/
if ((inst & 0xc1f80000) == 0xc0780000) {
return (do_swap(rp, inst));
}
/*
* for mul/div instruction switch on op3 field of instruction
* if the two bit op field is 0x2
*/
if ((inst >> 30) == 0x2) {
register u_int rs1, rs2, rd;
register u_int *rgs; /* pointer to struct regs */
register u_int *rw; /* pointer to frame */
u_int dest[2]; /* destination for simulator */
rd = (inst >> 25) & 0x1f;
rgs = (u_int *)&rp->r_y; /* globals and outs */
rw = (u_int *)rp->r_sp; /* ins and locals */
/* generate first operand rs1 */
if (getreg(rgs, rw, (inst >> 14) & 0x1f, &val) != 0)
return (SIMU_FAULT);
rs1 = val;
/* check immediate bit and use immediate field or reg (rs2) */
if ((inst >> 13) & 1) {
register int imm;
imm = inst & 0x1fff; /* mask out immediate field */
imm <<= 19; /* sign extend it */
imm >>= 19;
rs2 = imm; /* compute address */
} else {
if (getreg(rgs, rw, inst & 0x1f, &val) != 0)
return (SIMU_FAULT);
rs2 = val;
}
switch ((inst & 0x01f80000) >> 19) {
case 0xa:
rv = _ip_umul(rs1, rs2, dest, &rp->r_y, &rp->r_psr);
break;
case 0xb:
rv = _ip_mul(rs1, rs2, dest, &rp->r_y, &rp->r_psr);
break;
case 0xe:
rv = _ip_udiv(rs1, rs2, dest, &rp->r_y, &rp->r_psr);
wasmul = 0;
break;
case 0xf:
rv = _ip_div(rs1, rs2, dest, &rp->r_y, &rp->r_psr);
wasmul = 0;
break;
case 0x1a:
rv = _ip_umulcc(rs1, rs2, dest, &rp->r_y, &rp->r_psr);
break;
case 0x1b:
rv = _ip_mulcc(rs1, rs2, dest, &rp->r_y, &rp->r_psr);
break;
case 0x1e:
rv = _ip_udivcc(rs1, rs2, dest, &rp->r_y, &rp->r_psr);
wasmul = 0;
break;
case 0x1f:
rv = _ip_divcc(rs1, rs2, dest, &rp->r_y, &rp->r_psr);
wasmul = 0;
break;
default:
return (SIMU_ILLEGAL);
}
if (rv != SIMU_SUCCESS) {
return (rv);
}
if (putreg(dest[0], rgs, rw, rd) != 0) {
return (SIMU_FAULT);
}
if (wasmul)
rp->r_y = dest[1];
return (SIMU_SUCCESS);
}
/*
* Otherwise, we can't simulate instruction, its illegal.
*/
return (SIMU_ILLEGAL);
}
/*
* Check for an atomic (LDST, SWAP) instruction
* returns 1 if yes, 0 if no.
*/
int
is_atomic(rp)
struct regs *rp;
{
u_int inst;
inst = USERMODE(rp->r_ps) ? fuword((caddr_t)rp->r_pc) :
*(u_int *)rp->r_pc;
/* True for LDSTUB(A) and SWAP(A) */
return ((inst & 0xc1680000) == 0xc0680000);
}