* Copyright (c) 1988 University of Utah.
* Copyright (c) 1982, 1986, 1990 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* the Systems Programming Group of the University of Utah Computer
* %sccs.include.redist.c%
* from: Utah $Hdr: trap.c 1.35 91/12/26$
* @(#)trap.c 7.25 (Berkeley) %G%
#include <sys/signalvar.h>
#include <sys/resourcevar.h>
#include <machine/trap.h>
#include <machine/mtpr.h>
#include <hp/hpux/hpux.h>
#define TRAP_TYPES (sizeof trap_type / sizeof trap_type[0])
* Size of various exception stack frames (minus the standard 8 bytes)
FMT0SIZE
, /* type 0 - normal (68020/030/040) */
FMT1SIZE
, /* type 1 - throwaway (68020/030/040) */
FMT2SIZE
, /* type 2 - normal 6-word (68020/030/040) */
FMT3SIZE
, /* type 3 - FP post-instruction (68040) */
-1, -1, -1, /* type 4-6 - undefined */
FMT7SIZE
, /* type 7 - access error (68040) */
58, /* type 8 - bus fault (68010) */
FMT9SIZE
, /* type 9 - coprocessor mid-instruction (68020/030) */
FMTASIZE
, /* type A - short bus fault (68020/030) */
FMTBSIZE
, /* type B - long bus fault (68020/030) */
-1, -1, -1, -1 /* type C-F - undefined */
#define KDFAULT(c) (mmutype == MMU_68040 ? \
((c) & SSW4_TMMASK) == SSW4_TMKD : \
((c) & (SSW_DF|FC_SUPERD)) == (SSW_DF|FC_SUPERD))
#define WRFAULT(c) (mmutype == MMU_68040 ? \
((c) & (SSW_DF|SSW_RW)) == SSW_DF)
#define KDFAULT(c) (((c) & (SSW_DF|SSW_FCMASK)) == (SSW_DF|FC_SUPERD))
#define WRFAULT(c) (((c) & (SSW_DF|SSW_RW)) == SSW_DF)
#define MDB_ISPID(p) (p) == mmupid
* trap and syscall both need the following work done before returning
userret(p
, fp
, oticks
, faultaddr
, fromtrap
)
register struct frame
*fp
;
/* take pending signals */
while ((sig
= CURSIG(p
)) != 0)
* Since we are curproc, 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
p
->p_stats
->p_ru
.ru_nivcsw
++;
while ((sig
= CURSIG(p
)) != 0)
* If profiling, charge system time to the trapped pc.
if (p
->p_flag
& SPROFIL
) {
(int)(p
->p_sticks
- oticks
) * psratio
);
* Deal with user mode writebacks (from trap, or from sigreturn).
* If any writeback fails, go back and attempt signal delivery.
* unless we have already been here and attempted the writeback
* (e.g. bad address with user ignoring SIGSEGV). In that case
* we just return to the user without sucessfully completing
* the writebacks. Maybe we should just drop the sucker?
if (mmutype
== MMU_68040
&& fp
->f_format
== FMT7
) {
if (mmudebug
& MDB_WBFAILED
)
"pid %d(%s): writeback aborted, pc=%x, fa=%x\n" :
"pid %d(%s): writeback aborted in sigreturn, pc=%x\n",
p
->p_pid
, p
->p_comm
, fp
->f_pc
, faultaddr
);
} else if (sig
= writeback(fp
, fromtrap
)) {
trapsignal(p
, sig
, faultaddr
);
* Trap is called from locore to handle most types of processor traps,
* including events such as simulated software interrupts/AST's.
* System calls are broken out for efficiency.
trap(type
, code
, v
, frame
)
if (USERMODE(frame
.f_sr
)) {
p
->p_md
.md_regs
= frame
.f_regs
;
printf("trap type %d, code = %x, v = %x\n", type
, code
, v
);
regdump(frame
.f_regs
, 128);
if ((unsigned)type
< TRAP_TYPES
)
case T_BUSERR
: /* kernel bus error */
if (!p
->p_addr
->u_pcb
.pcb_onfault
)
* If we have arranged to catch this fault in any of the
* copy to/from user space routines, set PC to return to
* indicated location and set flag informing buserror code
* that it may need to clean up stack frame.
frame
.f_stackadj
= exframesize
[frame
.f_format
];
frame
.f_format
= frame
.f_vector
= 0;
frame
.f_pc
= (int) p
->p_addr
->u_pcb
.pcb_onfault
;
case T_BUSERR
|T_USER
: /* bus error */
case T_ADDRERR
|T_USER
: /* address error */
case T_COPERR
: /* kernel coprocessor violation */
case T_FMTERR
|T_USER
: /* do all RTE errors come in as T_USER? */
case T_FMTERR
: /* ...just in case... */
* The user has most likely trashed the RTE or FP state info
* in the stack frame of a signal handler.
printf("pid %d: kernel %s exception\n", p
->p_pid
,
type
==T_COPERR
? "coprocessor" : "format");
p
->p_sigacts
->ps_sigact
[SIGILL
] = SIG_DFL
;
ucode
= frame
.f_format
; /* XXX was ILL_RESAD_FAULT */
case T_COPERR
|T_USER
: /* user coprocessor violation */
/* What is a proper response here? */
case T_FPERR
|T_USER
: /* 68881 exceptions */
* We pass along the 68881 status register which locore stashed
* in code for us. Note that there is a possibility that the
* bit pattern of this register will conflict with one of the
* FPE_* codes defined in signal.h. Fortunately for us, the
* only such codes we use are all in the range 1-7 and the low
* 3 bits of the status register are defined as 0 so there is
case T_FPEMULI
|T_USER
: /* unimplemented FP instuction */
case T_FPEMULD
|T_USER
: /* unimplemented FP data type */
printf("pid %d(%s): unimplemented FP %s at %x (EA %x)\n",
frame
.f_format
== 2 ? "instruction" : "data type",
frame
.f_pc
, frame
.f_fmt2
.f_iaddr
);
/* XXX need to FRESTORE */
case T_ILLINST
|T_USER
: /* illegal instruction fault */
ucode
= HPUX_ILL_ILLINST_TRAP
;
case T_PRIVINST
|T_USER
: /* privileged instruction fault */
ucode
= HPUX_ILL_PRIV_TRAP
;
ucode
= frame
.f_format
; /* XXX was ILL_PRIVIN_FAULT */
case T_ZERODIV
|T_USER
: /* Divide by zero */
ucode
= HPUX_FPE_INTDIV_TRAP
;
ucode
= frame
.f_format
; /* XXX was FPE_INTDIV_TRAP */
case T_CHKINST
|T_USER
: /* CHK instruction trap */
/* handled differently under hp-ux */
ucode
= HPUX_ILL_CHK_TRAP
;
ucode
= frame
.f_format
; /* XXX was FPE_SUBRNG_TRAP */
case T_TRAPVINST
|T_USER
: /* TRAPV instruction trap */
/* handled differently under hp-ux */
ucode
= HPUX_ILL_TRAPV_TRAP
;
ucode
= frame
.f_format
; /* XXX was FPE_INTOVF_TRAP */
* XXX: Trace traps are a nightmare.
* HP-UX uses trap #1 for breakpoints,
* KGDB uses trap #15 (for kernel breakpoints; handled elsewhere).
* HPBSD and HP-UX traps both get mapped by locore.s into T_TRACE.
* SUN 3.x traps get passed through as T_TRAP15 and are not really
case T_TRACE
: /* kernel trace trap */
case T_TRAP15
: /* SUN trace trap */
case T_TRACE
|T_USER
: /* user trace trap */
case T_TRAP15
|T_USER
: /* SUN user trace trap */
case T_ASTFLT
: /* system async trap, cannot happen */
case T_ASTFLT
|T_USER
: /* user async trap */
* We check for software interrupts first. This is because
* they are at a higher level than ASTs, and on a VAX would
* interrupt the AST. We assume that if we are processing
* an AST that we must be at IPL0 so we don't bother to
* check. Note that we ensure that we are at least at SIR
* IPL while processing the SIR.
case T_SSIR
: /* software interrupt */
* If this was not an AST trap, we are all done.
if (type
!= (T_ASTFLT
|T_USER
)) {
if (p
->p_flag
& SOWEUPC
) {
case T_MMUFLT
: /* kernel mode page fault */
* If we were doing profiling ticks or other user mode
* stuff from interrupt code, Just Say No.
if (p
->p_addr
->u_pcb
.pcb_onfault
== fswintr
)
case T_MMUFLT
|T_USER
: /* page fault */
register struct vmspace
*vm
= p
->p_vmspace
;
extern vm_map_t kernel_map
;
if ((mmudebug
& MDB_WBFOLLOW
) || MDB_ISPID(p
->p_pid
))
printf("trap: T_MMUFLT pid=%d, code=%x, v=%x, pc=%x, sr=%x\n",
p
->p_pid
, code
, v
, frame
.f_pc
, frame
.f_sr
);
* It is only a kernel address space fault iff:
* 1. (type & T_USER) == 0 and
* 2. pcb_onfault not set or
* 3. pcb_onfault set but supervisor space data fault
* The last can occur during an exec() copyin where the
* argument space is lazy-allocated.
(!p
->p_addr
->u_pcb
.pcb_onfault
|| KDFAULT(code
)))
ftype
= VM_PROT_READ
| VM_PROT_WRITE
;
va
= trunc_page((vm_offset_t
)v
);
if (map
== kernel_map
&& va
== 0) {
printf("trap: bad kernel access at %x\n", v
);
rv
= vm_fault(map
, va
, ftype
, FALSE
);
if (rv
&& MDB_ISPID(p
->p_pid
))
printf("vm_fault(%x, %x, %x, 0) -> %x\n",
* If this was a stack access we keep track of the maximum
* accessed stack size. Also, if vm_fault gets a protection
* failure it is due to accessing the stack region outside
* the current limit and we need to reflect that as an access
if ((caddr_t
)va
>= vm
->vm_maxsaddr
&& map
!= kernel_map
) {
if (rv
== KERN_SUCCESS
) {
nss
= clrnd(btoc(USRSTACK
-(unsigned)va
));
} else if (rv
== KERN_PROTECTION_FAILURE
)
rv
= KERN_INVALID_ADDRESS
;
if (rv
== KERN_SUCCESS
) {
if (mmutype
== MMU_68040
)
(void) writeback(&frame
, 1);
if (p
->p_addr
->u_pcb
.pcb_onfault
)
printf("vm_fault(%x, %x, %x, 0) -> %x\n",
printf(" type %x, code [mmu,,ssw]: %x\n",
i
= (rv
== KERN_PROTECTION_FAILURE
) ? SIGBUS
: SIGSEGV
;
if ((type
& T_USER
) == 0)
userret(p
, &frame
, sticks
, v
, 1);
char *f7sz
[] = { "longword", "byte", "word", "line" };
char *f7tt
[] = { "normal", "MOVE16", "AFC", "ACK" };
char *f7tm
[] = { "d-push", "u-data", "u-code", "M-data",
"M-code", "k-data", "k-code", "RES" };
"WARNING: pid %d(%s) writeback [%s] failed, pc=%x fa=%x wba=%x wbd=%x\n";
writeback(fp
, docachepush
)
register struct fmt7
*f
= &fp
->f_fmt7
;
register struct proc
*p
= curproc
;
caddr_t oonfault
= p
->p_addr
->u_pcb
.pcb_onfault
;
if ((mmudebug
& MDB_WBFOLLOW
) || MDB_ISPID(p
->p_pid
)) {
printf(" pid=%d, fa=%x,", p
->p_pid
, f
->f_fa
);
* Deal with special cases first.
if ((f
->f_ssw
& SSW4_TMMASK
) == SSW4_TMDCP
) {
* Line-align the address and write out the push data to
* the indicated physical address.
if ((mmudebug
& MDB_WBFOLLOW
) || MDB_ISPID(p
->p_pid
)) {
printf(" pushing %s to PA %x, data %x",
f7sz
[(f
->f_ssw
& SSW4_SZMASK
) >> 5],
if ((f
->f_ssw
& SSW4_SZMASK
) == SSW4_SZLN
)
f
->f_pd1
, f
->f_pd2
, f
->f_pd3
);
if (f
->f_wb1s
& SSW4_WBSV
)
panic("writeback: cache push with WB1S valid");
* XXX there are security problems if we attempt to do a
* cache push after a signal handler has been called.
pmap_enter(kernel_pmap
, (vm_offset_t
)vmmap
,
trunc_page(f
->f_fa
), VM_PROT_WRITE
, TRUE
);
fa
= (u_int
)&vmmap
[(f
->f_fa
& PGOFSET
) & ~0xF];
bcopy((caddr_t
)&f
->f_pd0
, (caddr_t
)fa
, 16);
DCFL(pmap_extract(kernel_pmap
, (vm_offset_t
)fa
));
pmap_remove(kernel_pmap
, (vm_offset_t
)vmmap
,
(vm_offset_t
)&vmmap
[NBPG
]);
printf("WARNING: pid %d(%s) uid %d: CPUSH not done\n",
p
->p_pid
, p
->p_comm
, p
->p_ucred
->cr_uid
);
} else if ((f
->f_ssw
& (SSW4_RW
|SSW4_TTMASK
)) == SSW4_TTM16
) {
* Line-align the address and write out the push data to
* the indicated virtual address.
if ((mmudebug
& MDB_WBFOLLOW
) || MDB_ISPID(p
->p_pid
))
printf(" MOVE16 to VA %x(%x), data %x/%x/%x/%x\n",
f
->f_fa
, f
->f_fa
& ~0xF, f
->f_pd0
, f
->f_pd1
,
if (f
->f_wb1s
& SSW4_WBSV
)
panic("writeback: MOVE16 with WB1S valid");
bcopy((caddr_t
)&f
->f_pd0
, (caddr_t
)(f
->f_fa
& ~0xF), 16);
err
= suline((caddr_t
)(f
->f_fa
& ~0xF), (caddr_t
)&f
->f_pd0
);
if (mmudebug
& MDB_WBFAILED
)
printf(wberrstr
, p
->p_pid
, p
->p_comm
,
"MOVE16", fp
->f_pc
, f
->f_fa
,
f
->f_fa
& ~0xF, f
->f_pd0
);
} else if (f
->f_wb1s
& SSW4_WBSV
) {
* Position the "memory-aligned" data and write it out.
register u_int wb1d
= f
->f_wb1d
;
if ((mmudebug
& MDB_WBFOLLOW
) || MDB_ISPID(p
->p_pid
))
dumpwb(1, f
->f_wb1s
, f
->f_wb1a
, f
->f_wb1d
);
wbstats
.wbsize
[(f
->f_wb2s
&SSW4_SZMASK
)>>5]++;
off
= (f
->f_wb1a
& 3) * 8;
switch (f
->f_wb1s
& SSW4_SZMASK
) {
wb1d
= (wb1d
>> (32 - off
)) | (wb1d
<< off
);
*(long *)f
->f_wb1a
= wb1d
;
err
= suword((caddr_t
)f
->f_wb1a
, wb1d
);
*(char *)f
->f_wb1a
= wb1d
;
err
= subyte((caddr_t
)f
->f_wb1a
, wb1d
);
wb1d
= (wb1d
>> (32 - off
)) | (wb1d
<< off
);
*(short *)f
->f_wb1a
= wb1d
;
err
= susword((caddr_t
)f
->f_wb1a
, wb1d
);
if (mmudebug
& MDB_WBFAILED
)
printf(wberrstr
, p
->p_pid
, p
->p_comm
,
* Deal with the "normal" writebacks.
* XXX writeback2 is known to reflect a LINE size writeback after
* a MOVE16 was already dealt with above. Ignore it.
if (err
== 0 && (f
->f_wb2s
& SSW4_WBSV
) &&
(f
->f_wb2s
& SSW4_SZMASK
) != SSW4_SZLN
) {
if ((mmudebug
& MDB_WBFOLLOW
) || MDB_ISPID(p
->p_pid
))
dumpwb(2, f
->f_wb2s
, f
->f_wb2a
, f
->f_wb2d
);
wbstats
.wbsize
[(f
->f_wb2s
&SSW4_SZMASK
)>>5]++;
switch (f
->f_wb2s
& SSW4_SZMASK
) {
*(long *)f
->f_wb2a
= f
->f_wb2d
;
err
= suword((caddr_t
)f
->f_wb2a
, f
->f_wb2d
);
*(char *)f
->f_wb2a
= f
->f_wb2d
;
err
= subyte((caddr_t
)f
->f_wb2a
, f
->f_wb2d
);
*(short *)f
->f_wb2a
= f
->f_wb2d
;
err
= susword((caddr_t
)f
->f_wb2a
, f
->f_wb2d
);
if (mmudebug
& MDB_WBFAILED
) {
printf(wberrstr
, p
->p_pid
, p
->p_comm
,
dumpwb(2, f
->f_wb2s
, f
->f_wb2a
, f
->f_wb2d
);
if (err
== 0 && (f
->f_wb3s
& SSW4_WBSV
)) {
if ((mmudebug
& MDB_WBFOLLOW
) || MDB_ISPID(p
->p_pid
))
dumpwb(3, f
->f_wb3s
, f
->f_wb3a
, f
->f_wb3d
);
wbstats
.wbsize
[(f
->f_wb3s
&SSW4_SZMASK
)>>5]++;
switch (f
->f_wb3s
& SSW4_SZMASK
) {
*(long *)f
->f_wb3a
= f
->f_wb3d
;
err
= suword((caddr_t
)f
->f_wb3a
, f
->f_wb3d
);
*(char *)f
->f_wb3a
= f
->f_wb3d
;
err
= subyte((caddr_t
)f
->f_wb3a
, f
->f_wb3d
);
*(short *)f
->f_wb3a
= f
->f_wb3d
;
err
= susword((caddr_t
)f
->f_wb3a
, f
->f_wb3d
);
panic("writeback: wb3s indicates LINE write");
if (mmudebug
& MDB_WBFAILED
)
printf(wberrstr
, p
->p_pid
, p
->p_comm
,
p
->p_addr
->u_pcb
.pcb_onfault
= oonfault
;
* Determine the cause of the failure if any translating to
* a signal. If the corresponding VA is valid and RO it is
* a protection fault (SIGBUS) otherwise consider it an
* illegal reference (SIGSEGV).
if (vm_map_check_protection(&p
->p_vmspace
->vm_map
,
trunc_page(fa
), round_page(fa
),
!vm_map_check_protection(&p
->p_vmspace
->vm_map
,
trunc_page(fa
), round_page(fa
),
printf(" SSW: %x: ", ssw
);
printf(" SZ=%s, TT=%s, TM=%s\n",
f7sz
[(ssw
& SSW4_SZMASK
) >> 5],
f7tt
[(ssw
& SSW4_TTMASK
) >> 3],
f7tm
[ssw
& SSW4_TMMASK
]);
register struct proc
*p
= curproc
;
printf(" writeback #%d: VA %x, data %x, SZ=%s, TT=%s, TM=%s\n",
num
, a
, d
, f7sz
[(s
& SSW4_SZMASK
) >> 5],
f7tt
[(s
& SSW4_TTMASK
) >> 3], f7tm
[s
& SSW4_TMMASK
]);
pa
= pmap_extract(&p
->p_vmspace
->vm_pmap
, (vm_offset_t
)a
);
printf("<invalid address>");
printf("%x, current value %x", pa
, fuword((caddr_t
)a
));
printf("FP exception: pid %d(%s): no busy frame, ft=%x pc=%x vec=%x\n",
curproc
->p_pid
, curproc
->p_comm
,
frame
.fpsaveframe
, frame
.pc
, frame
.vector
);
panic("bad FP exception");
register struct sysent
*callp
;
int error
, opc
, numsys
, s
;
extern struct sysent hpuxsysent
[];
extern int hpuxnsysent
, notimp();
if (!USERMODE(frame
.f_sr
))
p
->p_md
.md_regs
= frame
.f_regs
;
callp
= hpuxsysent
, numsys
= hpuxnsysent
;
callp
= sysent
, numsys
= nsysent
;
params
= (caddr_t
)frame
.f_regs
[SP
] + sizeof(int);
* Code is first argument, followed by actual args.
* Like indir, but code is a quad, so as to maintain
* quad alignment for the rest of the arguments.
code
= fuword(params
+ _QUAD_LOWWORD
* sizeof(int));
params
+= sizeof(quad_t
);
/* nothing to do by default */
callp
+= SYS_indir
; /* => nosys */
argsize
= callp
->sy_narg
* sizeof(int);
if (argsize
&& (error
= copyin(params
, (caddr_t
)&args
, argsize
))) {
if (KTRPOINT(p
, KTR_SYSCALL
))
ktrsyscall(p
->p_tracep
, code
, callp
->sy_narg
, args
.i
);
if (KTRPOINT(p
, KTR_SYSCALL
))
ktrsyscall(p
->p_tracep
, code
, callp
->sy_narg
, args
.i
);
rval
[1] = frame
.f_regs
[D1
];
if (callp
->sy_call
== notimp
)
error
= notimp(p
, args
.i
, rval
, code
, callp
->sy_narg
);
error
= (*callp
->sy_call
)(p
, &args
, rval
);
* Reinitialize proc pointer `p' as it may be different
* if this is a child returning from fork syscall.
frame
.f_regs
[D0
] = rval
[0];
frame
.f_regs
[D1
] = rval
[1];
break; /* nothing to do */
error
= bsdtohpuxerrno(error
);
frame
.f_regs
[D0
] = error
;
userret(p
, &frame
, sticks
, (u_int
)0, 0);
if (KTRPOINT(p
, KTR_SYSRET
))
ktrsysret(p
->p_tracep
, code
, error
, rval
[0]);