2506 lines
61 KiB
C
2506 lines
61 KiB
C
#ifndef lint
|
|
static char sccsid[] = "@(#)trap.c 1.1 94/10/31";
|
|
#endif
|
|
|
|
/*
|
|
* Copyright (c) 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 <vm/as.h>
|
|
#include <vm/seg.h>
|
|
|
|
#include <sys/reboot.h>
|
|
#include <debug/debug.h>
|
|
|
|
#include <os/atom.h>
|
|
|
|
#ifdef SUNDBE
|
|
#include <sys/syscall.h>
|
|
#endif /* SUNDBE */
|
|
|
|
#if defined(SUN4M_35)
|
|
extern int small_4m, no_vme;
|
|
#endif
|
|
|
|
#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",
|
|
#ifdef SPARC_V8_TRAPS
|
|
"Instruction access exception",
|
|
#else
|
|
"Text fault",
|
|
#endif
|
|
"Illegal instruction",
|
|
"Privileged instruction",
|
|
"Floating point unit disabled",
|
|
"Window overflow",
|
|
"Window underflow",
|
|
"Memory address alignment",
|
|
"Floating point exception",
|
|
#ifdef SPARC_V8_TRAPS
|
|
"Data access exception",
|
|
#else
|
|
"Data fault",
|
|
#endif
|
|
"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",
|
|
#ifdef SPARC_V8_TRAPS
|
|
"Interrupt level F",
|
|
"Trap 0x20",
|
|
"Instruction access error",
|
|
"Trap 0x22",
|
|
"Trap 0x23",
|
|
"Coprocessor unit disabled",
|
|
"Unimplemented flush",
|
|
"Trap 0x26",
|
|
"Trap 0x27",
|
|
"Coprocessor exception",
|
|
"Data access error",
|
|
"Divide by zero",
|
|
"Data store error",
|
|
#else
|
|
"AST",
|
|
#endif
|
|
|
|
};
|
|
|
|
#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;
|
|
|
|
#ifndef OPENPROMS
|
|
#define prom_write ???
|
|
#else OPENPROMS
|
|
#ifndef MULTIPROCESSOR
|
|
extern void prom_write();
|
|
#else MULTIPROCESSOR
|
|
#define prom_write mpprom_write
|
|
#endif MULTIPROCESSOR
|
|
#endif OPENPROMS
|
|
|
|
#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)
|
|
|
|
#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 = FT_INVALID_ADDR << FS_FTSHIFT; /* used by fp_traps to setup
|
|
* a datafault trap
|
|
*/
|
|
#define TRAPTRACE
|
|
#ifdef TRAPTRACE
|
|
int traptrace = 0;
|
|
#endif TRAPTRACE
|
|
|
|
void
|
|
badtrap(type, rp, addr, mmu_fsr, rw)
|
|
register unsigned type;
|
|
register struct regs *rp;
|
|
register addr_t addr;
|
|
register u_int mmu_fsr;
|
|
register enum seg_rw rw;
|
|
{
|
|
extern int cpuid;
|
|
|
|
trigger_logan();
|
|
(void) spl7();
|
|
printf("BAD TRAP: cpu=%d type=%x rp=%x addr=%x mmu_fsr=%x rw=%x\n",
|
|
cpuid, type, rp, addr, mmu_fsr, rw);
|
|
mmu_print_sfsr(mmu_fsr);
|
|
#if defined(MULTIPROCESSOR) && defined(PROM_PARANOIA)
|
|
mpprom_eprintf("PROM_PARANOIA: inside badtrap()\n");
|
|
#endif
|
|
printf("regs at %x:\n", rp);
|
|
printf("\tpsr=%x pc=%x npc=%x\n",
|
|
rp->r_psr, rp->r_pc, rp->r_npc);
|
|
printf("\ty: %x g1: %x g2: %x g3: %x\n",
|
|
rp->r_y, rp->r_g1, rp->r_g2, rp->r_g3);
|
|
printf("\tg4: %x g5: %x g6: %x g7: %x\n",
|
|
rp->r_g4, rp->r_g5, rp->r_g6, rp->r_g7);
|
|
printf("\to0: %x o1: %x o2: %x o3: %x\n",
|
|
rp->r_o0, rp->r_o1, rp->r_o2, rp->r_o3);
|
|
printf("\to4: %x o5: %x sp: %x ra: %x\n",
|
|
rp->r_o4, rp->r_o5, rp->r_o6, rp->r_o7);
|
|
showregs(type, rp, addr, mmu_fsr, rw);
|
|
traceback((caddr_t)rp->r_sp);
|
|
if (type < TRAP_TYPES)
|
|
panic(trap_type[type]);
|
|
panic("trap");
|
|
}
|
|
|
|
enum fault_type
|
|
get_faulttype(type, rp, addr, mmu_fsr, rw)
|
|
register unsigned type;
|
|
register struct regs *rp;
|
|
register addr_t addr;
|
|
register u_int mmu_fsr;
|
|
register enum seg_rw rw;
|
|
{
|
|
enum fault_type fault_type;
|
|
|
|
#ifdef TRAPTRACE
|
|
int pid;
|
|
|
|
if (u.u_procp)
|
|
pid = u.u_procp->p_pid;
|
|
else
|
|
pid = -1;
|
|
#endif TRAPTRACE
|
|
|
|
switch (X_FAULT_TYPE(mmu_fsr)) {
|
|
/*
|
|
* FT_NONE can't happen, locore stops us from getting here
|
|
* if sfsr has not ft_type
|
|
*/
|
|
|
|
case FT_TRANS_ERROR:
|
|
case FT_ACC_BUSERR:
|
|
case FT_INTERNAL:
|
|
default:
|
|
printf("Unexpected trap %d fault %x\n", type,
|
|
X_FAULT_TYPE(mmu_fsr));
|
|
badtrap(type, rp, addr, mmu_fsr, rw);
|
|
/* NO RETURN */
|
|
|
|
case FT_INVALID_ADDR:
|
|
#ifdef TRAPTRACE
|
|
if (traptrace) {
|
|
printf("%d: data fault at 0x%x: ", pid, addr);
|
|
printf("address not mapped\n");
|
|
}
|
|
#endif
|
|
if (tdebug)
|
|
printf("FT_INV\n");
|
|
fault_type = F_INVAL;
|
|
break;
|
|
|
|
case FT_PROT_ERROR:
|
|
case FT_PRIV_ERROR:
|
|
#ifdef TRAPTRACE
|
|
if (traptrace) {
|
|
printf("%d: data fault at 0x%x: ", pid, addr);
|
|
printf("protection violation\n");
|
|
}
|
|
#endif
|
|
if (tdebug)
|
|
printf("FT_PROT\n");
|
|
fault_type = F_PROT;
|
|
break;
|
|
}
|
|
return (fault_type);
|
|
|
|
}
|
|
|
|
void
|
|
check_fsr(type, rp, addr, mmu_fsr, rw)
|
|
register unsigned type;
|
|
register struct regs *rp;
|
|
register addr_t addr;
|
|
register u_int mmu_fsr;
|
|
register enum seg_rw rw;
|
|
{
|
|
extern int vac_parity_chk_dis();
|
|
|
|
if ((mmu_fsr & MMU_SFSR_FATAL) || (mmu_fsr & MMU_SFSR_UD)) {
|
|
|
|
/* bad bits in the SFSR */
|
|
if (lodebug)
|
|
showregs(type, rp, addr, mmu_fsr, rw);
|
|
/*
|
|
* If this trap was caused by a MBUS Uncorrectable
|
|
* error, it's an ECC uncorrectable error on read.
|
|
* This requires to print out the detailed info (
|
|
* including the SIMM number) before calling panic
|
|
*/
|
|
if ((mmu_fsr & MMU_SFSR_UC) ||
|
|
(mmu_fsr & MMU_SFSR_UD)) {
|
|
struct pte pte;
|
|
addr_t paddr;
|
|
int parity_err;
|
|
|
|
(void) spl7();
|
|
parity_err = vac_parity_chk_dis(mmu_fsr, 0);
|
|
if (parity_err)
|
|
printf("module parity error:\n");
|
|
else
|
|
printf("fatal system fault:\n");
|
|
mmu_getpte(addr, &pte);
|
|
if (pte_valid(&pte)) {
|
|
paddr = (caddr_t) (pte.PhysicalPageNumber <<
|
|
MMU_PAGESHIFT)
|
|
+ ((int) addr & MMU_PAGEOFFSET);
|
|
log_mem_err(mmu_fsr, (u_int) 0,
|
|
(u_int) paddr, (u_int) 1);
|
|
} else
|
|
printf("addr %x is not valid\n", addr);
|
|
printf("Control Registers:\n");
|
|
printf("\tsfsr = 0x%x, %s fault ", mmu_fsr,
|
|
(rw == S_WRITE? "write": "read"));
|
|
printf("at vaddr 0x%x\n", addr);
|
|
if (lodebug)
|
|
mmu_print_sfsr(mmu_fsr);
|
|
panic("memory error");
|
|
/* NOTREACHED */
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef VME
|
|
/*
|
|
* If "vme_int_ack_warn" is set, we will print
|
|
* a warning message whenever we drop a VME INT-ACK.
|
|
*/
|
|
int vme_int_ack_warn = 0;
|
|
/*
|
|
* "vme_int_ack_drops" is a quick count of how many
|
|
* times we have dropped a VME INT-ACK cycle. It should
|
|
* stay at zero.
|
|
*/
|
|
int vme_int_ack_drops = 0;
|
|
#endif VME
|
|
/*
|
|
* Called from the trap handler when a processor trap occurs.
|
|
* Addr, mmu_fsr and rw only are passed for text and data faults.
|
|
*/
|
|
|
|
|
|
/*VARARGS2*/
|
|
void
|
|
trap(type, rp, addr, mmu_fsr, rw)
|
|
register unsigned type;
|
|
register struct regs *rp;
|
|
addr_t addr;
|
|
register u_int mmu_fsr;
|
|
register enum seg_rw rw;
|
|
{
|
|
register int i = 0;
|
|
register struct proc *p;
|
|
struct timeval syst;
|
|
int lofault;
|
|
faultcode_t pagefault(), res;
|
|
u_int inst;
|
|
enum fault_type fault_type;
|
|
int mask;
|
|
#ifdef TRAPTRACE
|
|
int pid;
|
|
#endif TRAPTRACE
|
|
extern void module_wkaround();
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
klock_enter();
|
|
#endif MULTIPROCESSOR
|
|
|
|
p = u.u_procp;
|
|
#ifdef TRAPTRACE
|
|
if (p) pid = p->p_pid;
|
|
else pid = -1;
|
|
#endif TRAPTRACE
|
|
cnt.v_trap++;
|
|
syst = u.u_ru.ru_stime;
|
|
if (tdebug)
|
|
showregs(type, rp, addr, mmu_fsr, rw);
|
|
|
|
if (USERMODE(rp->r_psr)) {
|
|
if (tdebug)
|
|
printf("Setting user bit\n");
|
|
type |= USER;
|
|
u.u_ar0 = (int *)rp;
|
|
}
|
|
|
|
|
|
/*
|
|
* take any pending floating point exceptions now
|
|
*/
|
|
syncfpu(rp);
|
|
|
|
|
|
switch (type) {
|
|
|
|
case T_ALIGNMENT: /* supv alignment error */
|
|
/* printf("kernel alignment fault\n"); */
|
|
goto die;
|
|
|
|
case T_DATA_STORE: /* store buffer exception */
|
|
case T_DATA_STORE + USER: /* store buffer exception */
|
|
|
|
#if defined(SUN4M_35)
|
|
if (!small_4m)
|
|
(void) check_fsr(type, rp, addr, mmu_fsr, rw);
|
|
#else
|
|
(void) check_fsr(type, rp, addr, mmu_fsr, rw);
|
|
#endif
|
|
/*
|
|
* FIXME: currently we panic the system when store
|
|
* buffer exception occurs. We should try to recover
|
|
* from it.
|
|
*/
|
|
if (tdebug) {
|
|
printf("T_DATA_STORE mmu_fsr %x ", mmu_fsr);
|
|
showregs(type, rp, addr, mmu_fsr, rw);
|
|
}
|
|
goto die;
|
|
/* NOTREACHED */
|
|
default:
|
|
/*
|
|
* Check for user software trap.
|
|
*/
|
|
if (type & USER) {
|
|
#ifdef TRAPTRACE
|
|
if (traptrace) {
|
|
printf("%d: bad user software trap 0x%x\n",
|
|
pid, type);
|
|
}
|
|
#endif
|
|
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:
|
|
badtrap(type, rp, addr, mmu_fsr, rw);
|
|
/* NOTREACHED */
|
|
|
|
|
|
#ifdef SPARC_V8_TRAPS
|
|
case T_TEXT_FAULT: /* system text access exception */
|
|
case T_TEXT_ERROR: /* system text access error */
|
|
#else
|
|
case T_TEXT_FAULT: /* system text access fault */
|
|
#endif
|
|
|
|
#if defined(SUN4M_35)
|
|
if (!small_4m)
|
|
(void) check_fsr(type, rp, addr, mmu_fsr, rw);
|
|
#else
|
|
(void) check_fsr(type, rp, addr, mmu_fsr, rw);
|
|
#endif
|
|
|
|
/*
|
|
* 605 overwrite bit is not checked here.
|
|
* whether set or not kernel is going to die any way
|
|
*
|
|
*/
|
|
/* do the lda of sup/user inst space for completeness */
|
|
#ifdef TRAPTRACE
|
|
if (traptrace) {
|
|
printf("%d: text fault at 0x%x\n", pid, addr);
|
|
}
|
|
#endif
|
|
|
|
#if defined (SUN4M_35)
|
|
if (cpu == CPU_SUN4M_35 && X_EBE(mmu_fsr)) {
|
|
if (ebe_handler(MERR_SYNC, mmu_fsr,
|
|
addr, type, rp) != -1)
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (lodebug)
|
|
showregs(type, rp, addr, mmu_fsr, rw);
|
|
goto die;
|
|
|
|
#ifdef SPARC_V8_TRAPS
|
|
case T_DATA_FAULT: /* system data access exception */
|
|
case T_DATA_ERROR: /* system data access error */
|
|
#else
|
|
case T_DATA_FAULT: /* system data access fault */
|
|
#endif
|
|
/* may have been expected by C (e.g., bus probe) */
|
|
/*
|
|
* 605 overwrite bit story
|
|
* if set says the second fault could have been a
|
|
* a translation error on an instruction fault only.
|
|
* In any case translation errors are panics and hence
|
|
* we print info and giveup
|
|
*/
|
|
|
|
if (tdebug)
|
|
printf("got to DATAFAULT\n");
|
|
if (nofault) {
|
|
label_t *ftmp;
|
|
|
|
ftmp = nofault;
|
|
nofault = 0;
|
|
longjmp(ftmp);
|
|
}
|
|
#if defined (SUN4M_35)
|
|
if (cpu == CPU_SUN4M_35 && X_EBE(mmu_fsr)) {
|
|
if (ebe_handler(MERR_SYNC, mmu_fsr,
|
|
addr, type, rp) != -1)
|
|
return;
|
|
}
|
|
#endif
|
|
#ifdef VME
|
|
/*
|
|
* If this trap was caused by a VME device not
|
|
* completing an interrupt acknowledge cycle,
|
|
* print a warning message and continue.
|
|
* NOTE: leaves the destination register of the load
|
|
* entirely unmodified ...
|
|
*/
|
|
#if defined(SUN4M_35)
|
|
if ((no_vme == 0) && (type == T_DATA_FAULT) &&
|
|
(mmu_fsr & MMU_SFSR_FATAL) &&
|
|
#else
|
|
if ((type == T_DATA_FAULT) && (mmu_fsr & MMU_SFSR_FATAL) &&
|
|
#endif
|
|
((int)addr & ~0xF) == V_VMEBUS_VEC) {
|
|
vme_int_ack_drops ++;
|
|
if (vme_int_ack_warn) {
|
|
printf("Warning: VME dropped an INT-ACK cycle\n");
|
|
mmu_print_sfsr(mmu_fsr);
|
|
}
|
|
rp->r_pc = rp->r_npc;
|
|
rp->r_npc = rp->r_npc + 4;
|
|
return;
|
|
}
|
|
#endif VME
|
|
/* do module specific workarounds if any */
|
|
module_wkaround(&addr, rp, rw, mmu_fsr);
|
|
|
|
#if defined(SUN4M_35)
|
|
if (!small_4m)
|
|
(void) check_fsr(type, rp, addr, mmu_fsr, rw);
|
|
#else
|
|
(void) check_fsr(type, rp, addr, mmu_fsr, rw);
|
|
#endif
|
|
|
|
/* get_faulttype can panic if bad trap */
|
|
fault_type = get_faulttype(type, rp, addr, mmu_fsr, rw);
|
|
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
lofault = u.u_lofault;
|
|
u.u_lofault = 0;
|
|
|
|
if (addr < (addr_t)KERNELBASE + sizeof (int)) {
|
|
if (lofault == 0) {
|
|
goto die;
|
|
}
|
|
res = pagefault(addr, rw, 0, (u_int) fault_type);
|
|
if (res != 0 && grow((int)addr))
|
|
res = 0;
|
|
} else {
|
|
res = pagefault(addr, rw, 1, (u_int) fault_type);
|
|
}
|
|
/*
|
|
* 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;
|
|
|
|
if (lofault == 0)
|
|
goto die;
|
|
|
|
/*
|
|
* Cannot resolve fault. Return to lofault.
|
|
*/
|
|
if (lodebug) {
|
|
showregs(type, rp, addr, mmu_fsr, 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 = lofault;
|
|
rp->r_npc = lofault + 4;
|
|
return;
|
|
|
|
#ifdef SPARC_V8_TRAPS
|
|
case T_DATA_FAULT + USER: /* user data access exception */
|
|
case T_DATA_ERROR + USER: /* user data access error */
|
|
#else
|
|
case T_DATA_FAULT + USER: /* user data access fault */
|
|
#endif
|
|
/* module specific workarounds if any */
|
|
/* we know the vikings need workaround in the case of
|
|
* data faults. See vik_module_wkaround() for deatails
|
|
*/
|
|
module_wkaround(&addr, rp, rw, mmu_fsr);
|
|
/* FALL THROUGH */
|
|
#ifdef SPARC_V8_TRAPS
|
|
case T_TEXT_FAULT + USER: /* user text access exception */
|
|
case T_TEXT_ERROR + USER: /* user text access error */
|
|
#else
|
|
case T_TEXT_FAULT + USER: /* user text access fault */
|
|
#endif
|
|
|
|
#if defined (SUN4M_35)
|
|
/* possible parity error? */
|
|
if (cpu == CPU_SUN4M_35 && X_EBE(mmu_fsr))
|
|
if (ebe_handler(MERR_SYNC, mmu_fsr,
|
|
addr, type, rp) != -1)
|
|
return;
|
|
#endif
|
|
/*
|
|
* 605 fault overwrite story.
|
|
* Instruction-instruction: Resolve at PC and let the
|
|
* other one happen again. The second fault could be
|
|
* for a prefetch of instructions. Hence we handle the
|
|
* fault pc only.
|
|
* instruction-translation: Translation is treated as
|
|
* bad thing by the kernel. panic
|
|
* translation-otherfault: bad panic
|
|
* data - data: CANNOT happen on 601 IU
|
|
* data - translation: panic because of trans error
|
|
*/
|
|
|
|
|
|
if ((mmu_fsr&MMU_SFSR_OW) &&
|
|
(X_FAULT_TYPE(mmu_fsr) == FT_TRANS_ERROR)) {
|
|
printf("cpu %d: Multiple faults occurred\n", cpuid);
|
|
printf("\t first fault %s at pc %x \n", cpuid,
|
|
(type == (T_DATA_FAULT+USER)?
|
|
"User Data ":"User text "));
|
|
printf("\t second fault: translation error ");
|
|
printf("at addr %x\n", addr);
|
|
}
|
|
|
|
if (tdebug) {
|
|
printf("T_DATA_FAULT | USER mmu_fsr %x ", mmu_fsr);
|
|
printf("X_FAULT_TYPE %x\n",
|
|
(u_int)X_FAULT_TYPE(mmu_fsr));
|
|
}
|
|
|
|
if ((mmu_fsr & (MMU_SFSR_TO | MMU_SFSR_BE)) &&
|
|
!(mmu_fsr & MMU_SFSR_UC)) {
|
|
res = FC_HWERR;
|
|
goto badbus;
|
|
}
|
|
|
|
#if defined(SUN4M_35)
|
|
if (!small_4m)
|
|
(void) check_fsr(type, rp, addr, mmu_fsr, rw);
|
|
#else
|
|
(void) check_fsr(type, rp, addr, mmu_fsr, rw);
|
|
#endif
|
|
|
|
/* get_faulttype can panic if bad trap */
|
|
fault_type = get_faulttype(type, rp, addr, mmu_fsr, rw);
|
|
|
|
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 mmu_fsr=0x%x\n",
|
|
fault_str, addr, mmu_fsr);
|
|
}
|
|
if (tdebug) {
|
|
printf("Calling pagefault: ");
|
|
printf("addr %x rw %x fault_type %x\n",
|
|
addr, rw, fault_type);
|
|
}
|
|
|
|
|
|
res = pagefault(addr, rw, 0, (u_int) fault_type);
|
|
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, mmu_fsr, 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.
|
|
*/
|
|
badbus:
|
|
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 */
|
|
#ifdef TRAPTRACE
|
|
if (traptrace) {
|
|
printf("%d: alignment error at 0x%x\n",
|
|
pid, addr);
|
|
}
|
|
#endif
|
|
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 */
|
|
#ifdef TRAPTRACE
|
|
if (traptrace) {
|
|
printf("%d: privileged instruction at 0x%x\n",
|
|
pid, rp->r_pc);
|
|
}
|
|
#endif
|
|
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 */
|
|
#ifdef TRAPTRACE
|
|
if (traptrace) {
|
|
printf("%d: unimplemented instruction at 0x%x\n",
|
|
pid, rp->r_pc);
|
|
}
|
|
#endif
|
|
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 */
|
|
#ifdef TRAPTRACE
|
|
if (traptrace) {
|
|
printf("%d: divide by zero at 0x%x\n",
|
|
pid, rp->r_pc);
|
|
}
|
|
#endif
|
|
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 */
|
|
#ifdef TRAPTRACE
|
|
if (traptrace) {
|
|
printf("%d: integer overflow at 0x%x\n",
|
|
pid, rp->r_pc);
|
|
}
|
|
#endif
|
|
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:
|
|
#ifdef TRAPTRACE
|
|
if (traptrace) {
|
|
printf("%d: flotating point exception at 0x%x\n",
|
|
pid, rp->r_pc);
|
|
}
|
|
#endif
|
|
if (tudebug && tudebugfpe)
|
|
showregs(type, rp, addr, 0, S_OTHER);
|
|
u.u_code = mmu_fsr; /* this is actually fp trap code
|
|
* from fp_traps()
|
|
*/
|
|
u.u_addr = (char *)addr;
|
|
i = SIGFPE;
|
|
break;
|
|
|
|
case T_BREAKPOINT + USER: /* breakpoint trap (t 1) */
|
|
#ifdef TRAPTRACE
|
|
if (traptrace) {
|
|
printf("%d: breakpoint at 0x%x\n",
|
|
pid, rp->r_pc);
|
|
}
|
|
#endif
|
|
if (tudebug && tudebugbpt)
|
|
showregs(type, rp, (addr_t)0, 0, S_OTHER);
|
|
i = SIGTRAP;
|
|
break;
|
|
|
|
case T_TAG_OVERFLOW + USER: /* tag overflow (taddcctv, tsubcctv) */
|
|
#ifdef TRAPTRACE
|
|
if (traptrace) {
|
|
printf("%d: tag overflow at 0x%x\n",
|
|
pid, rp->r_pc);
|
|
}
|
|
#endif
|
|
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];
|
|
|
|
/*
|
|
* XXX - do we still need PARTIAL_ALIGN?
|
|
*/
|
|
#ifdef PARTIAL_ALIGN
|
|
if ((partial_align? ((int)sp & 0x3): ((int)sp & 0x7))) {
|
|
#else
|
|
if (((int)sp & (STACK_ALIGN-1))) {
|
|
#endif PARTIAL_ALIGN
|
|
u.u_addr = (char *)rp->r_pc;
|
|
u.u_code = ILL_STACK;
|
|
i = SIGILL;
|
|
break;
|
|
} else {
|
|
#ifdef SAS
|
|
if (tudebug) {
|
|
printf("trap: copyout(%x, %x, %x) ",
|
|
(caddr_t)&u.u_pcb.pcb_wbuf[j],
|
|
sp, sizeof (struct rwindow));
|
|
printf("j %x\n", j);
|
|
}
|
|
#endif SAS
|
|
inst = copyout((caddr_t)&u.u_pcb.pcb_wbuf[j],
|
|
sp, sizeof (struct rwindow));
|
|
if (inst != 0) {
|
|
#ifdef SAS
|
|
printf("%d: ", u.u_procp->p_pid);
|
|
printf("bad copyout ret %x ",
|
|
inst);
|
|
printf("pc %x sp %x ",
|
|
rp->r_pc, sp);
|
|
printf("wbuf %x j %x\n",
|
|
u.u_pcb.pcb_wbcnt, j);
|
|
#endif SAS
|
|
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();
|
|
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();
|
|
p->p_pri = p->p_usrpri;
|
|
setrq(p);
|
|
u.u_ru.ru_nivcsw++;
|
|
swtch();
|
|
(void) spl0();
|
|
} else{
|
|
p->p_pri = p->p_usrpri;
|
|
}
|
|
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 SYSCALL_TRACE_STRINGS
|
|
int syscall_strs[] = { /* which args to print as ascii */
|
|
0, /* 0 = indir */
|
|
0, /* 1 = exit */
|
|
0, /* 2 = fork */
|
|
0, /* 3 = read */
|
|
0, /* 4 = write */
|
|
01, /* 5 = open */
|
|
0, /* 6 = close */
|
|
0, /* 7 = wait4 */
|
|
01, /* 8 = creat */
|
|
03, /* 9 = link */
|
|
01, /* 10 = unlink */
|
|
01, /* 11 = execv */
|
|
01, /* 12 = chdir */
|
|
0, /* 13 = old time */
|
|
01, /* 14 = mknod */
|
|
01, /* 15 = chmod */
|
|
01, /* 16 = chown; now 3 args */
|
|
0, /* 17 = brk */
|
|
01, /* 18 = old stat */
|
|
0, /* 19 = lseek */
|
|
0, /* 20 = getpid */
|
|
0, /* 21 = old mount */
|
|
#ifdef UFS
|
|
01, /* 22 = old umount */
|
|
#else
|
|
0, /* 22 = old umount */
|
|
#endif
|
|
0, /* 23 = old setuid */
|
|
0, /* 24 = getuid */
|
|
0, /* 25 = old stime */
|
|
0, /* 26 = ptrace */
|
|
0, /* 27 = old alarm */
|
|
0, /* 28 = old fstat */
|
|
0, /* 29 = opause */
|
|
01, /* 30 = old utime */
|
|
0, /* 31 = was stty */
|
|
0, /* 32 = was gtty */
|
|
01, /* 33 = access */
|
|
0, /* 34 = old nice */
|
|
0, /* 35 = old ftime */
|
|
0, /* 36 = sync */
|
|
0, /* 37 = kill */
|
|
01, /* 38 = stat */
|
|
0, /* 39 = old setpgrp */
|
|
01, /* 40 = lstat */
|
|
0, /* 41 = dup */
|
|
0, /* 42 = pipe */
|
|
0, /* 43 = old times */
|
|
0, /* 44 = profil */
|
|
0, /* 45 = nosys */
|
|
0, /* 46 = old setgid */
|
|
0, /* 47 = getgid */
|
|
0, /* 48 = old sig */
|
|
0, /* 49 = reserved for USG */
|
|
0, /* 50 = reserved for USG */
|
|
#ifdef SYSACCT
|
|
0, /* 51 = turn acct off/on */
|
|
#else
|
|
0, /* 51 = not configured */
|
|
#endif SYSACCT
|
|
0, /* 52 = old set phys addr */
|
|
0, /* 53 = memory control */
|
|
0, /* 54 = ioctl */
|
|
02, /* 55 = reboot */
|
|
#ifdef sparc
|
|
0, /* 56 = wait3 */
|
|
#else
|
|
0, /* 56 = old mpxchan */
|
|
#endif sparc
|
|
03, /* 57 = symlink */
|
|
01, /* 58 = readlink */
|
|
01, /* 59 = execve */
|
|
0, /* 60 = umask */
|
|
01, /* 61 = chroot */
|
|
0, /* 62 = fstat */
|
|
0, /* 63 = used internally */
|
|
0, /* 64 = getpagesize */
|
|
0, /* 65 = old msync */
|
|
0, /* 66 = vfork */
|
|
0, /* 67 = old vread */
|
|
0, /* 68 = old vwrite */
|
|
0, /* 69 = sbrk */
|
|
0, /* 70 = sstk */
|
|
0, /* 71 = mmap */
|
|
0, /* 72 = old vadvise */
|
|
0, /* 73 = munmap */
|
|
0, /* 74 = mprotect */
|
|
0, /* 75 = old madvise */
|
|
0, /* 76 = vhangup */
|
|
0, /* 77 = old vlimit */
|
|
0, /* 78 = mincore */
|
|
0, /* 79 = getgroups */
|
|
0, /* 80 = setgroups */
|
|
0, /* 81 = getpgrp */
|
|
0, /* 82 = setpgrp */
|
|
0, /* 83 = setitimer */
|
|
0, /* 84 = old wait & wait3 */
|
|
01, /* 85 = swapon */
|
|
0, /* 86 = getitimer */
|
|
0, /* 87 = gethostname */
|
|
01, /* 88 = sethostname */
|
|
0, /* 89 = getdtablesize */
|
|
0, /* 90 = dup2 */
|
|
0, /* 91 = getdopt */
|
|
0, /* 92 = fcntl */
|
|
0, /* 93 = select */
|
|
0, /* 94 = setdopt */
|
|
0, /* 95 = fsync */
|
|
0, /* 96 = setpriority */
|
|
0, /* 97 = socket */
|
|
0, /* 98 = connect */
|
|
0, /* 99 = accept */
|
|
0, /* 100 = getpriority */
|
|
0, /* 101 = send */
|
|
0, /* 102 = recv */
|
|
0, /* 103 = old socketaddr */
|
|
0, /* 104 = bind */
|
|
0, /* 105 = setsockopt */
|
|
0, /* 106 = listen */
|
|
0, /* 107 = old vtimes */
|
|
0, /* 108 = sigvec */
|
|
0, /* 109 = sigblock */
|
|
0, /* 110 = sigsetmask */
|
|
0, /* 111 = sigpause */
|
|
0, /* 112 = sigstack */
|
|
0, /* 113 = recvmsg */
|
|
0, /* 114 = sendmsg */
|
|
#ifdef TRACE
|
|
0, /* 115 = vtrace */
|
|
#else TRACE
|
|
0, /* 115 = nosys */
|
|
#endif TRACE
|
|
0, /* 116 = gettimeofday */
|
|
0, /* 117 = getrusage */
|
|
0, /* 118 = getsockopt */
|
|
#ifdef vax
|
|
0, /* 119 = resuba */
|
|
#else
|
|
0, /* 119 = nosys */
|
|
#endif
|
|
0, /* 120 = readv */
|
|
0, /* 121 = writev */
|
|
0, /* 122 = settimeofday */
|
|
0, /* 123 = fchown */
|
|
0, /* 124 = fchmod */
|
|
0, /* 125 = recvfrom */
|
|
0, /* 126 = setreuid */
|
|
0, /* 127 = setregid */
|
|
03, /* 128 = rename */
|
|
01, /* 129 = truncate */
|
|
0, /* 130 = ftruncate */
|
|
0, /* 131 = flock */
|
|
0, /* 132 = nosys */
|
|
0, /* 133 = sendto */
|
|
0, /* 134 = shutdown */
|
|
0, /* 135 = socketpair */
|
|
01, /* 136 = mkdir */
|
|
01, /* 137 = rmdir */
|
|
01, /* 138 = utimes */
|
|
0, /* 139 = signalcleanup */
|
|
0, /* 140 = adjtime */
|
|
0, /* 141 = getpeername */
|
|
0, /* 142 = gethostid */
|
|
0, /* 143 = old sethostid */
|
|
0, /* 144 = getrlimit */
|
|
0, /* 145 = setrlimit */
|
|
0, /* 146 = killpg */
|
|
0, /* 147 = nosys */
|
|
0, /* XXX */ /* 148 = old quota */
|
|
0, /* XXX */ /* 149 = old qquota */
|
|
0, /* 150 = getsockname */
|
|
0, /* 151 = getmsg */
|
|
0, /* 152 = putmsg */
|
|
0, /* 153 = poll */
|
|
#ifdef NFSSERVER
|
|
0, /* 154 = old nfs_mount */
|
|
0, /* 155 = nfs_svc */
|
|
#else
|
|
0, /* 154 = nosys */
|
|
0, /* 155 = errsys */
|
|
#endif
|
|
0, /* 156 = getdirentries */
|
|
01, /* 157 = statfs */
|
|
0, /* 158 = fstatfs */
|
|
01, /* 159 = unmount */
|
|
#ifdef NFSCLIENT
|
|
0, /* 160 = async_daemon */
|
|
#else
|
|
0, /* 160 = errsys */
|
|
#endif
|
|
#ifdef NFSSERVER
|
|
0, /* 161 = get file handle */
|
|
#else
|
|
0, /* 161 = nosys */
|
|
#endif
|
|
0, /* 162 = getdomainname */
|
|
01, /* 163 = setdomainname */
|
|
#ifdef RT_SCHEDULE
|
|
0, /* 164 = rtschedule */
|
|
#else
|
|
0, /* 164 = not configured */
|
|
#endif RT_SCHEDULE
|
|
#ifdef QUOTA
|
|
0, /* 165 = quotactl */
|
|
#else
|
|
0, /* 165 = not configured */
|
|
#endif QUOTA
|
|
#ifdef NFSSERVER
|
|
0, /* 166 = exportfs */
|
|
#else
|
|
0, /* 166 = not configured */
|
|
#endif
|
|
03, /* 167 = mount */
|
|
0, /* 168 = ustat */
|
|
#ifdef IPCSEMAPHORE
|
|
0, /* 169 = semsys */
|
|
#else
|
|
0, /* 169 = not configured */
|
|
#endif
|
|
#ifdef IPCMESSAGE
|
|
0, /* 170 = msgsys */
|
|
#else
|
|
0, /* 170 = not configured */
|
|
#endif
|
|
#ifdef IPCSHMEM
|
|
0, /* 171 = shmsys */
|
|
#else
|
|
0, /* 171 = not configured */
|
|
#endif
|
|
#ifdef SYSAUDIT
|
|
0, /* 172 = auditsys (audit control) */
|
|
#else
|
|
0, /* 172 = not configured */
|
|
#endif SYSAUDIT
|
|
#ifdef RFS
|
|
0, /* 173 = RFS calls */
|
|
#else
|
|
0, /* 173 = not configured */
|
|
#endif
|
|
0, /* 174 = getdents */
|
|
0, /* 175 = setsid & s5 setpgrp() */
|
|
0, /* 176 = fchdir */
|
|
0, /* 177 = fchroot */
|
|
#ifdef VPIX
|
|
0, /* 178 = VP/ix system calls */
|
|
#else
|
|
0, /* 178 = not configured */
|
|
#endif
|
|
#ifdef ASYNCHIO
|
|
0, /* 179 = aioread */
|
|
0, /* 180 = aiowrite */
|
|
0, /* 181 = aiowait */
|
|
0, /* 182 = aiocancel */
|
|
#else
|
|
0, /* 179 not configured */
|
|
0, /* 180 not configured */
|
|
0, /* 181 not configured */
|
|
0, /* 182 not configured */
|
|
#endif ASYNCHIO
|
|
0, /* 183 = sigpending */
|
|
0, /* 184 = AVAILABLE */
|
|
0, /* 185 = setpgid */
|
|
01, /* 186 = pathconf */
|
|
0, /* 187 = fpathconf */
|
|
0, /* 188 = sysconf */
|
|
01, /* 189 = uname */
|
|
#ifdef VDDRV
|
|
0, /* 190 = reserved - Loadable syscalls */
|
|
0, /* 191 = reserved - Loadable syscalls */
|
|
0, /* 192 = reserved - Loadable syscalls */
|
|
0, /* 193 = reserved - Loadable syscalls */
|
|
0, /* 194 = reserved - Loadable syscalls */
|
|
0, /* 195 = reserved - Loadable syscalls */
|
|
0, /* 196 = reserved - Loadable syscalls */
|
|
0, /* 197 = reserved - Loadable syscalls */
|
|
#endif
|
|
};
|
|
#endif SYSCALL_TRACE_STRINGS
|
|
|
|
#ifdef SYSCALLTRACE
|
|
#ifdef MULTIPROCESSOR
|
|
int syscalltracelockok = 0;
|
|
atom_t syscalltracelock;
|
|
#endif
|
|
char syscalltracebuf[4096];
|
|
static char *
|
|
eostr(bp)
|
|
char *bp;
|
|
{
|
|
while (*bp) ++bp;
|
|
return (bp);
|
|
}
|
|
|
|
static void
|
|
prsct(code, na, fmt, arg)
|
|
unsigned code;
|
|
char *fmt;
|
|
{
|
|
extern char *sprintf();
|
|
register int i;
|
|
char *cp;
|
|
char *bp = syscalltracebuf;
|
|
|
|
*bp = 0;
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
/*
|
|
* Yes, this could be a race, but what are the
|
|
* chances of both processes coming through here
|
|
* at the same time before we initialize
|
|
* this lock? Zero.
|
|
*/
|
|
if (!syscalltracelockok) {
|
|
atom_init(syscalltracelock);
|
|
syscalltracelockok = 1;
|
|
}
|
|
atom_wcs(syscalltracelock);
|
|
(void) sprintf(bp = eostr(bp), "%d/", cpuid);
|
|
#endif
|
|
(void) sprintf(bp = eostr(bp), "%d: ", u.u_procp->p_pid);
|
|
(void) sprintf(bp = eostr(bp), "sys 0x%x", code);
|
|
if (code < nsysent)
|
|
(void) sprintf(bp = eostr(bp), " %s",
|
|
syscallnames[code]);
|
|
else
|
|
(void) sprintf(bp = eostr(bp), " ???");
|
|
cp = "(";
|
|
for (i = 0; i < na; i++) {
|
|
#ifdef SYSCALL_TRACE_STRINGS
|
|
if ((code < nsysent) &&
|
|
(syscall_strs[code] & (1<<i))) {
|
|
register int l, c, a;
|
|
(void) sprintf(bp = eostr(bp), "%s", cp);
|
|
a = u.u_ap[i];
|
|
if (!a)
|
|
(void) sprintf(bp = eostr(bp), "NULL");
|
|
else if (fubyte((caddr_t) a) < 0)
|
|
(void) sprintf(bp = eostr(bp), "%x (?)",
|
|
a);
|
|
else {
|
|
(void) sprintf(bp = eostr(bp), "\"");
|
|
for (l = 0; l < 80; ++l) {
|
|
c = fubyte((caddr_t) a);
|
|
if (c <= 0) break;
|
|
(void) sprintf(bp = eostr(bp),
|
|
"%c", c);
|
|
a++;
|
|
}
|
|
if (c < 0) sprintf(bp = eostr(bp),
|
|
"\" (?)");
|
|
else if (c) sprintf(bp = eostr(bp),
|
|
"\"...");
|
|
else sprintf(bp = eostr(bp), "\"");
|
|
}
|
|
} else
|
|
#endif SYSCALL_TRACE_STRINGS
|
|
(void) sprintf(bp = eostr(bp), "%s%x", cp,
|
|
u.u_ap[i]);
|
|
cp = ", ";
|
|
}
|
|
if (i)
|
|
(void) sprintf(bp = eostr(bp), ")");
|
|
(void) sprintf(bp = eostr(bp), fmt, arg);
|
|
bp = eostr(bp);
|
|
*bp++ = '\n';
|
|
prom_writestr(syscalltracebuf, bp - syscalltracebuf);
|
|
#ifdef MULTIPROCESSOR
|
|
atom_clr(syscalltracelock);
|
|
#endif
|
|
}
|
|
#endif SYSCALLTRACE
|
|
|
|
|
|
#ifdef SUNDBE
|
|
long klkbusydokv = 0;
|
|
long syscalldokv = 0;
|
|
extern int klock;
|
|
|
|
int (*syscall_preamble(rp))()
|
|
register struct regs *rp;
|
|
{
|
|
register struct user *up;
|
|
register struct proc *procp;
|
|
register unsigned code;
|
|
register struct sysent *callp;
|
|
register struct proc *p;
|
|
|
|
up = &u;
|
|
procp = up->u_procp;
|
|
code = rp->r_g1;
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
if (klock)
|
|
klkbusydokv++;
|
|
klock_enter();
|
|
#endif
|
|
syscalldokv++;
|
|
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);
|
|
}
|
|
|
|
#ifdef just_for_reference
|
|
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);
|
|
}
|
|
#endif
|
|
|
|
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 */
|
|
/*
|
|
* 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;
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
klock_enter();
|
|
#endif MULTIPROCESSOR
|
|
|
|
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 & 1)
|
|
prsct(code, callp->sy_narg,
|
|
" ...", 0);
|
|
#endif SYSCALLTRACE
|
|
/*
|
|
* 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 & 2)
|
|
prsct(code, callp->sy_narg,
|
|
" error %d", u.u_error);
|
|
#endif
|
|
rp->r_o0 = u.u_error;
|
|
rp->r_psr |= PSR_C; /* set carry bit */
|
|
} else {
|
|
#ifdef SYSCALLTRACE
|
|
if (syscalltrace & 4)
|
|
prsct(code, callp->sy_narg,
|
|
" returns 0x%x", u.u_r.r_val1);
|
|
#endif
|
|
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();
|
|
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();
|
|
p->p_pri = p->p_usrpri;
|
|
setrq(p);
|
|
u.u_ru.ru_nivcsw++;
|
|
swtch();
|
|
(void) spl0();
|
|
} else {
|
|
p->p_pri = p->p_usrpri;
|
|
}
|
|
|
|
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 */
|
|
|
|
/*
|
|
* 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];
|
|
|
|
u.u_error = 0;
|
|
|
|
|
|
/*
|
|
* 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 & 8)
|
|
prsct(code, callp->sy_narg, " ...", 0);
|
|
#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];
|
|
(*calls[uap->id])();
|
|
return;
|
|
}
|
|
#endif /* SUNDBE */
|
|
|
|
(*(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;
|
|
|
|
(void) setjmp(&l);
|
|
traceback((caddr_t)l.val[1]);
|
|
}
|
|
|
|
#ifdef TRAPWINDOW
|
|
long trap_window[25];
|
|
#endif TRAPWINDOW
|
|
|
|
/*
|
|
* Print out debugging info.
|
|
*/
|
|
showregs(type, rp, addr, mmu_fsr, rw)
|
|
register unsigned type;
|
|
register struct regs *rp;
|
|
addr_t addr;
|
|
u_int mmu_fsr;
|
|
enum seg_rw rw;
|
|
{
|
|
int s;
|
|
extern u_int mmu_getctx();
|
|
|
|
trigger_logan();
|
|
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) {
|
|
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, *(u_int *)&pte);
|
|
mmu_print_sfsr(mmu_fsr);
|
|
} else if (addr) {
|
|
printf("addr=0x%x\n", addr);
|
|
}
|
|
printf("rp=0x%x, pc=0x%x, sp=0x%x, psr=0x%x, context=0x%x\n",
|
|
rp, rp->r_pc, rp->r_sp, rp->r_psr, mmu_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
|
|
#ifdef PROM_PARANOIA
|
|
mpprom_eprintf("PROM_PARANOIA: inside showregs()\n");
|
|
#endif PROM_PARANOIA
|
|
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();
|
|
#ifdef MULTIPROCESSOR
|
|
klock_enter();
|
|
#endif
|
|
setrq(p);
|
|
u.u_ru.ru_nivcsw++;
|
|
swtch();
|
|
(void) spl0();
|
|
}
|
|
|
|
#if 0
|
|
/* 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;
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
klock_enter();
|
|
#endif
|
|
/*
|
|
* 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((caddr_t)&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);
|
|
if (reg < 16)
|
|
rgs[reg] = data;
|
|
else {
|
|
if (suword((caddr_t)&rw[reg - 16], (int)data) != 0) {
|
|
return (-1);
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
#ifndef lint
|
|
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]);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* 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) != 0)
|
|
return (SIMU_FAULT);
|
|
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) != 0)
|
|
return (SIMU_FAULT);
|
|
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) != 0)
|
|
return (SIMU_FAULT);
|
|
data.i[0] = val;
|
|
if (sz == 8) {
|
|
if (getreg(rgs, rw, rd+1, &val) != 0)
|
|
return (SIMU_FAULT);
|
|
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)
|
|
return (SIMU_FAULT);
|
|
} else {
|
|
if (copyout((caddr_t)&data.i[0], (caddr_t)addr,
|
|
(u_int)sz) == -1)
|
|
return (SIMU_FAULT);
|
|
}
|
|
} else { /* load */
|
|
if (sz == 2) {
|
|
if (copyin((caddr_t)addr, (caddr_t)&data.s[1],
|
|
(u_int)sz) == -1)
|
|
return (SIMU_FAULT);
|
|
/* 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)
|
|
return (SIMU_FAULT);
|
|
|
|
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)
|
|
return (SIMU_FAULT);
|
|
if (sz == 8)
|
|
if (putreg(data.i[1], rgs, rw, rd+1) == -1)
|
|
return (SIMU_FAULT);
|
|
}
|
|
}
|
|
return (SIMU_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* 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 iflush instruction
|
|
* the iflush support is only for th ROSS CPU. Instead of doing a
|
|
* NOP, the ROSS does a illegal instruction trap. Just ignore
|
|
* it here and return
|
|
* The sun4s/sun4cs do a nop
|
|
* Vikings implement the iflush instruction
|
|
*/
|
|
if ((inst & 0xc1f80000) == 0x81d80000) {
|
|
return (SIMU_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* 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]; /* put high mul in y */
|
|
return (SIMU_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* Otherwise, we can't simulate instruction, its illegal.
|
|
*/
|
|
return (SIMU_ILLEGAL);
|
|
}
|
|
|
|
/*
|
|
* fix_addr(): used by viking bug workaround. It is here because of getreg
|
|
* Picks apart the instruction and computes the effective address used by
|
|
* the instruction.
|
|
* Remember fix_addr() can be called User and Kernel PCs as well.
|
|
* Need to do special case for kernel registers
|
|
*/
|
|
|
|
addr_t
|
|
fix_addr (rp, mfsr)
|
|
struct regs *rp;
|
|
register u_int mfsr;
|
|
{
|
|
|
|
register u_int inst;
|
|
u_int rs1, rs2;
|
|
register u_int *rgs; /* pointer to struct regs */
|
|
register u_int *rw; /* pointer to frame */
|
|
register u_int err, addr;
|
|
int kpc = 0; /* Assume User PC */
|
|
|
|
|
|
/*
|
|
* If the failing PC is in the kernel, can't use fuword() to access
|
|
* registers and instruction. So figure out whether the PC is in
|
|
* the user or kernel area.
|
|
*/
|
|
if ((u_int)rp->r_pc > (u_int)KERNELBASE)
|
|
kpc = 1;
|
|
|
|
/* Our case, fix it */
|
|
if (kpc) {
|
|
inst = *(u_int *)rp->r_pc;
|
|
flush_windows();
|
|
}
|
|
else {
|
|
if ((inst = fuword((caddr_t)rp->r_pc)) == -1)
|
|
return ((addr_t)-1);
|
|
flush_user_windows_to_stack();
|
|
}
|
|
|
|
/*
|
|
* implement workaround for memory operations (format == 3).
|
|
* The workaround does not apply to coprocessor instructions
|
|
* (bits 5:4 == 3) because Viking does not support them.
|
|
*/
|
|
if (((inst >> 30) != 0x3) || ((inst >> 23 & 0x3) == 3))
|
|
return ((addr_t)-1);
|
|
|
|
rgs = (u_int *)&rp->r_y; /* globals and outs */
|
|
rw = (u_int *)rp->r_sp; /* ins and locals */
|
|
|
|
|
|
/* generate first operand rs1 */
|
|
if (getkureg(rgs, rw, (inst >> 14) & 0x1f, &rs1, kpc) != 0)
|
|
return ((addr_t)-1);
|
|
/* 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;
|
|
addr = rs1 + imm;
|
|
} else {
|
|
if (getkureg(rgs, rw, inst & 0x1f, &rs2, kpc) != 0)
|
|
return ((addr_t)-1);
|
|
addr = rs1 + rs2;
|
|
}
|
|
return((addr_t)addr);
|
|
}
|
|
|
|
#ifdef SUNDBE
|
|
/*
|
|
* Called from the trap handler when a system call occurs
|
|
* before syscall() is called. We are still outside of the kernel
|
|
* lock. If syscallnolock returns nonzero, the trap handler returns
|
|
* to user. Otherwise, syscall() is called as usual.
|
|
*
|
|
* The intention is to do some system calls without having to acquire
|
|
* the kernel lock.
|
|
*
|
|
* Note - We only use it to accelerate the Sybase aiowait() calls.
|
|
*/
|
|
int
|
|
syscallnolock(rp)
|
|
register struct regs *rp;
|
|
{
|
|
register unsigned code;
|
|
|
|
code = rp->r_g1;
|
|
|
|
if (code != SYS_aiowait || dbe_probeawait((struct timeval *)rp->r_o0))
|
|
return 0; /* do a normal system call */
|
|
|
|
rp->r_psr &= ~PSR_C; /* reset carry bit */
|
|
rp->r_o0 = 0;
|
|
rp->r_pc = rp->r_npc;
|
|
rp->r_npc += 4;
|
|
return 1; /* return to user */
|
|
}
|
|
#endif /* SUNDBE */
|
|
|
|
/*
|
|
* register read support for fix_addr.
|
|
* Reads kernel or user registers based on arg: kernelargs.
|
|
*/
|
|
|
|
static int
|
|
getkureg(rgs, rw, reg, val, kernelregs)
|
|
u_int *rgs, *rw, reg;
|
|
u_int *val;
|
|
int kernelregs;
|
|
{
|
|
if (reg == 0)
|
|
*val = 0;
|
|
else if (reg < 16)
|
|
*val = rgs[reg];
|
|
else {
|
|
if (kernelregs)
|
|
*val = rw[reg - 16];
|
|
else {
|
|
if ((*val = fuword((caddr_t)&rw[reg - 16])) == -1) {
|
|
if (fubyte((caddr_t)&rw[reg - 16]) == -1)
|
|
return (-1);
|
|
}
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|