NetBSD/sys/arch/amiga/amiga/trap.c
2019-11-21 19:23:58 +00:00

862 lines
21 KiB
C

/* $NetBSD: trap.c,v 1.138 2019/11/21 19:23:58 ad Exp $ */
/*
* Copyright (c) 1988 University of Utah.
* Copyright (c) 1982, 1986, 1990 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* the Systems Programming Group of the University of Utah Computer
* Science Department.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* from: Utah $Hdr: trap.c 1.32 91/04/06$
*
* @(#)trap.c 7.15 (Berkeley) 8/2/91
*/
#include "opt_ddb.h"
#include "opt_execfmt.h"
#include "opt_compat_sunos.h"
#include "opt_fpu_emulate.h"
#include "opt_m68k_arch.h"
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: trap.c,v 1.138 2019/11/21 19:23:58 ad Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/acct.h>
#include <sys/kernel.h>
#include <sys/signalvar.h>
#include <sys/resourcevar.h>
#include <sys/syslog.h>
#include <sys/syscall.h>
#include <sys/userret.h>
#include <sys/kauth.h>
#include <uvm/uvm_extern.h>
#include <machine/psl.h>
#include <machine/trap.h>
#include <machine/cpu.h>
#include <machine/pcb.h>
#include <machine/pte.h>
#include <m68k/fpe/fpu_emulate.h>
#include <m68k/cacheops.h>
#ifdef COMPAT_SUNOS
#include <compat/sunos/sunos_syscall.h>
extern struct emul emul_sunos;
#endif
/*
* XXX Hack until I can figure out what to do about this code's removal
* from m68k/include/frame.h
*/
/* 68040 fault frame */
#define SSW_CP 0x8000 /* Continuation - Floating-Point Post*/
#define SSW_CU 0x4000 /* Continuation - Unimpl. FP */
#define SSW_CT 0x2000 /* Continuation - Trace */
#define SSW_CM 0x1000 /* Continuation - MOVEM */
#define SSW_MA 0x0800 /* Misaligned access */
#define SSW_ATC 0x0400 /* ATC fault */
#define SSW_LK 0x0200 /* Locked transfer */
#define SSW_RW040 0x0100 /* Read/Write */
#define SSW_SZMASK 0x0060 /* Transfer size */
#define SSW_TTMASK 0x0018 /* Transfer type */
#define SSW_TMMASK 0x0007 /* Transfer modifier */
#define WBS_TMMASK 0x0007
#define WBS_TTMASK 0x0018
#define WBS_SZMASK 0x0060
#define WBS_VALID 0x0080
#define WBS_SIZE_BYTE 0x0020
#define WBS_SIZE_WORD 0x0040
#define WBS_SIZE_LONG 0x0000
#define WBS_SIZE_LINE 0x0060
#define WBS_TT_NORMAL 0x0000
#define WBS_TT_MOVE16 0x0008
#define WBS_TT_ALTFC 0x0010
#define WBS_TT_ACK 0x0018
#define WBS_TM_PUSH 0x0000
#define WBS_TM_UDATA 0x0001
#define WBS_TM_UCODE 0x0002
#define WBS_TM_MMUTD 0x0003
#define WBS_TM_MMUTC 0x0004
#define WBS_TM_SDATA 0x0005
#define WBS_TM_SCODE 0x0006
#define WBS_TM_RESV 0x0007
#define MMUSR_PA_MASK 0xfffff000
#define MMUSR_B 0x00000800
#define MMUSR_G 0x00000400
#define MMUSR_U1 0x00000200
#define MMUSR_U0 0x00000100
#define MMUSR_S 0x00000080
#define MMUSR_CM 0x00000060
#define MMUSR_M 0x00000010
#define MMUSR_0 0x00000008
#define MMUSR_W 0x00000004
#define MMUSR_T 0x00000002
#define MMUSR_R 0x00000001
#define FSLW_STRING "\020\1SEE\3BPE\4TTR\5WE\6RE\7TWE\010WP\011SP" \
"\012PF\013IL\014PTB\015PTA\016SBE\017PBE"
/*
* XXX End hack
*/
int astpending;
const char *trap_type[] = {
"Bus error",
"Address error",
"Illegal instruction",
"Zero divide",
"CHK instruction",
"TRAPV instruction",
"Privilege violation",
"Trace trap",
"MMU fault",
"SSIR trap",
"Format error",
"68881 exception",
"Coprocessor violation",
"Async system trap"
};
int trap_types = sizeof trap_type / sizeof trap_type[0];
/*
* Size of various exception stack frames (minus the standard 8 bytes)
*/
short exframesize[] = {
FMT0SIZE, /* type 0 - normal (68020/030/040/060) */
FMT1SIZE, /* type 1 - throwaway (68020/030/040) */
FMT2SIZE, /* type 2 - normal 6-word (68020/030/040/060) */
FMT3SIZE, /* type 3 - FP post-instruction (68040/060) */
FMT4SIZE, /* type 4 - access error/fp disabled (68060) */
-1, -1, /* type 5-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 */
};
#ifdef DEBUG
int mmudebug = 0;
#endif
extern struct pcb *curpcb;
int _write_back(u_int, u_int, u_int, u_int, struct vm_map *);
static void userret(struct lwp *, int, u_quad_t);
void panictrap(int, u_int, u_int, struct frame *);
void trapcpfault(struct lwp *, struct frame *, int);
void trapmmufault(int, u_int, u_int, struct frame *, struct lwp *,
u_quad_t);
void trap(struct frame *, int, u_int, u_int);
#ifdef DDB
#include <m68k/db_machdep.h>
int kdb_trap(int, db_regs_t *);
#endif
void _wb_fault(void);
static void
userret(struct lwp *l, int pc, u_quad_t oticks)
{
struct proc *p = l->l_proc;
/* Invoke MI userret code */
mi_userret(l);
/*
* If profiling, charge recent system time.
*/
if (p->p_stflag & PST_PROFIL) {
extern int psratio;
addupc_task(l, pc, (int)(p->p_sticks - oticks) * psratio);
}
}
/*
* Used by the common m68k syscall() and child_return() functions.
* XXX: Temporary until all m68k ports share common trap()/userret() code.
*/
void machine_userret(struct lwp *, struct frame *, u_quad_t);
void
machine_userret(struct lwp *l, struct frame *f, u_quad_t t)
{
userret(l, f->f_pc, t);
}
void
panictrap(int type, u_int code, u_int v, struct frame *fp)
{
static int panicking = 0;
if (panicking++ == 0) {
printf("trap type %d, code = %x, v = %x\n", type, code, v);
regdump((struct trapframe *)fp, 128);
}
type &= ~T_USER;
#ifdef DEBUG
DCIS(); /* XXX? push cache */
#endif
if ((u_int)type < trap_types)
panic(trap_type[type]);
panic("trap");
/*NOTREACHED*/
}
/*
* return to fault handler
*/
void
trapcpfault(struct lwp *l, struct frame *fp, int error)
{
struct pcb *pcb = lwp_getpcb(l);
/*
* We have arranged to catch this fault in one 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.
*/
fp->f_stackadj = exframesize[fp->f_format];
fp->f_format = fp->f_vector = 0;
fp->f_pc = (int)pcb->pcb_onfault;
fp->f_regs[D0] = error;
}
int donomore = 0;
void
trapmmufault(int type, u_int code, u_int v, struct frame *fp, struct lwp *l, u_quad_t sticks)
{
#if defined(DEBUG) && defined(M68060)
static u_int oldcode=0, oldv=0;
static struct proc *oldp=0;
#endif
extern struct vm_map *kernel_map;
struct proc *p = l->l_proc;
struct vmspace *vm = NULL;
struct vm_map *map;
struct pcb *pcb;
void *onfault;
vm_prot_t ftype;
vaddr_t va;
ksiginfo_t ksi;
int rv;
pcb = lwp_getpcb(l);
onfault = pcb->pcb_onfault;
KSI_INIT_TRAP(&ksi);
ksi.ksi_trap = type & ~T_USER;
/*
* 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.
*/
#ifdef DEBUG
/*
* Print out some data about the fault
*/
#ifdef DEBUG_PAGE0
if (v < PAGE_SIZE) /* XXX PAGE0 */
mmudebug |= 0x100; /* XXX PAGE0 */
#endif
if (mmudebug && mmutype == MMU_68040) {
#ifdef M68060
if (machineid & AMIGA_68060) {
if (--donomore == 0 || mmudebug & 1) {
char bits[64];
snprintb(bits, sizeof(bits), FSLW_STRING, code);
printf ("68060 access error: pc %x, code %s,"
" ea %x\n", fp->f_pc, bits, v);
}
if (p == oldp && v == oldv && code == oldcode)
panic("Identical fault backtoback!");
if (donomore == 0)
panic("Tired of faulting.");
oldp = p;
oldv = v;
oldcode = code;
} else
#endif
printf("68040 access error: pc %x, code %x,"
" ea %x, fa %x\n", fp->f_pc, code, fp->f_fmt7.f_ea, v);
if (curpcb)
printf(" curpcb %p\n", curpcb);
#ifdef DDB /* XXX PAGE0 */
if (v < PAGE_SIZE) /* XXX PAGE0 */
Debugger(); /* XXX PAGE0 */
#endif /* XXX PAGE0 */
}
#ifdef DEBUG_PAGE0
mmudebug &= ~0x100; /* XXX PAGE0 */
#endif
#endif
if (p)
vm = p->p_vmspace;
if (type == T_MMUFLT && (l == &lwp0 || onfault == 0 || (
#ifdef M68060
machineid & AMIGA_68060 ? code & FSLW_TM_SV :
#endif
mmutype == MMU_68040 ? (code & SSW_TMMASK) == FC_SUPERD :
(code & (SSW_DF|FC_SUPERD)) == (SSW_DF|FC_SUPERD))))
map = kernel_map;
else
map = &vm->vm_map;
if (
#ifdef M68060
machineid & AMIGA_68060 ? code & FSLW_RW_W :
#endif
mmutype == MMU_68040 ? (code & (SSW_LK|SSW_RW040)) != SSW_RW040 :
((code & SSW_DF) != 0 &&
((code & SSW_RW) == 0 || (code & SSW_RM) != 0)))
ftype = VM_PROT_WRITE;
else
ftype = VM_PROT_READ;
va = trunc_page((vaddr_t)v);
#ifdef DEBUG
if (map == kernel_map && va == 0) {
printf("trap: bad kernel access at %x pc %x\n", v, fp->f_pc);
panictrap(type, code, v, fp);
}
if (mmudebug)
printf("vm_fault(%p,%lx,%d)\n", map, va, ftype);
#endif
pcb->pcb_onfault = NULL;
rv = uvm_fault(map, va, ftype);
pcb->pcb_onfault = onfault;
#ifdef DEBUG
if (mmudebug)
printf("vmfault %s %lx returned %d\n",
map == kernel_map ? "kernel" : "user", va, rv);
#endif
#ifdef M68060
if ((machineid & AMIGA_68060) == 0 && mmutype == MMU_68040) {
#else
if (mmutype == MMU_68040) {
#endif
if (rv != 0) {
goto nogo;
}
/*
* The 68040 doesn't re-run instructions that cause
* write page faults (unless due to a move16 isntruction).
* So once the page is repaired, we have to write the
* value of WB2D out to memory ourselves. Because
* the writeback could possibly span two pages in
* memory, so we need to check both "ends" of the
* address to see if they are in the same page or not.
* If not, then we need to make sure the second page
* is valid, and bring it into memory if it's not.
*
* This whole process needs to be repeated for WB3 as well.
* <sigh>
*/
/* Check WB1 */
if (fp->f_fmt7.f_wb1s & WBS_VALID) {
printf ("trap: wb1 was valid, not handled yet\n");
panictrap(type, code, v, fp);
}
/*
* Check WB2
* skip if it's for a move16 instruction
*/
if(fp->f_fmt7.f_wb2s & WBS_VALID &&
((fp->f_fmt7.f_wb2s & WBS_TTMASK)==WBS_TT_MOVE16) == 0) {
if (_write_back(2, fp->f_fmt7.f_wb2s,
fp->f_fmt7.f_wb2d, fp->f_fmt7.f_wb2a, map) != 0)
goto nogo;
if ((fp->f_fmt7.f_wb2s & WBS_TMMASK)
!= (code & SSW_TMMASK))
panictrap(type, code, v, fp);
}
/* Check WB3 */
if(fp->f_fmt7.f_wb3s & WBS_VALID) {
struct vm_map *wb3_map;
if ((fp->f_fmt7.f_wb3s & WBS_TMMASK) == WBS_TM_SDATA)
wb3_map = kernel_map;
else
wb3_map = &vm->vm_map;
if (_write_back(3, fp->f_fmt7.f_wb3s,
fp->f_fmt7.f_wb3d, fp->f_fmt7.f_wb3a, wb3_map) != 0)
goto nogo;
}
}
/*
* 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
* error.
*/
if (rv == 0) {
if (map != kernel_map && (void *)va >= vm->vm_maxsaddr)
uvm_grow(p, va);
if (type == T_MMUFLT)
return;
userret(l, fp->f_pc, sticks);
return;
}
nogo:
if (type == T_MMUFLT) {
if (onfault) {
trapcpfault(l, fp, rv);
return;
}
printf("uvm_fault(%p, 0x%lx, 0x%x) -> 0x%x\n",
map, va, ftype, rv);
printf(" type %x, code [mmu,,ssw]: %x\n",
type, code);
panictrap(type, code, v, fp);
}
ksi.ksi_addr = (void *)v;
switch (rv) {
case ENOMEM:
printf("UVM: pid %d (%s), uid %d killed: out of swap\n",
p->p_pid, p->p_comm,
l->l_cred ? kauth_cred_geteuid(l->l_cred) : -1);
ksi.ksi_signo = SIGKILL;
break;
case EINVAL:
ksi.ksi_signo = SIGBUS;
ksi.ksi_code = BUS_ADRERR;
break;
case EACCES:
ksi.ksi_signo = SIGSEGV;
ksi.ksi_code = SEGV_ACCERR;
break;
default:
ksi.ksi_signo = SIGSEGV;
ksi.ksi_code = SEGV_MAPERR;
break;
}
trapsignal(l, &ksi);
if ((type & T_USER) == 0)
return;
userret(l, fp->f_pc, sticks);
}
/*
* 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.
*/
/*ARGSUSED*/
void
trap(struct frame *fp, int type, u_int code, u_int v)
{
struct lwp *l;
struct proc *p;
struct pcb *pcb;
ksiginfo_t ksi;
u_quad_t sticks = 0;
l = curlwp;
p = l->l_proc;
pcb = lwp_getpcb(l);
curcpu()->ci_data.cpu_ntrap++;
KSI_INIT_TRAP(&ksi);
ksi.ksi_trap = type & ~T_USER;
if (USERMODE(fp->f_sr)) {
type |= T_USER;
sticks = p->p_sticks;
l->l_md.md_regs = fp->f_regs;
LWP_CACHE_CREDS(l, p);
}
#ifdef DDB
if (type == T_TRACE || type == T_BREAKPOINT) {
if (kdb_trap(type, (db_regs_t *)fp))
return;
}
#endif
#ifdef DEBUG
if (mmudebug & 2)
printf("%s: t %x c %x v %x adj %x sr %x pc %x fmt %x vc %x\n",
__func__, type, code, v, fp->f_stackadj, fp->f_sr,
fp->f_pc, fp->f_format, fp->f_vector);
#endif
switch (type) {
default:
panictrap(type, code, v, fp);
/*
* Kernel Bus error
*/
case T_BUSERR:
if (!pcb->pcb_onfault)
panictrap(type, code, v, fp);
trapcpfault(l, fp, EFAULT);
return;
/*
* User Bus/Addr error.
*/
case T_BUSERR|T_USER:
case T_ADDRERR|T_USER:
ksi.ksi_addr = (void *)v;
ksi.ksi_signo = SIGBUS;
ksi.ksi_code = (type == (T_BUSERR|T_USER)) ?
BUS_OBJERR : BUS_ADRERR;
break;
/*
* User illegal/privleged inst fault
*/
case T_ILLINST|T_USER:
case T_PRIVINST|T_USER:
ksi.ksi_addr = (void *)(int)fp->f_format;
/* XXX was ILL_PRIVIN_FAULT */
ksi.ksi_signo = SIGILL;
ksi.ksi_code = (type == (T_PRIVINST|T_USER)) ?
ILL_PRVOPC : ILL_ILLOPC;
break;
/*
* divde by zero, CHK/TRAPV inst
*/
case T_ZERODIV|T_USER:
ksi.ksi_code = FPE_FLTDIV;
case T_CHKINST|T_USER:
case T_TRAPVINST|T_USER:
ksi.ksi_addr = (void *)(int)fp->f_format;
ksi.ksi_signo = SIGFPE;
break;
case T_FPEMULI|T_USER:
case T_FPEMULD|T_USER:
#ifdef FPU_EMULATE
if (fpu_emulate(fp, &pcb->pcb_fpregs, &ksi) == 0)
; /* XXX - Deal with tracing? (fp->f_sr & PSL_T) */
#else
printf("pid %d killed: no floating point support\n", p->p_pid);
ksi.ksi_signo = SIGILL;
ksi.ksi_code = ILL_ILLOPC;
#endif
break;
#ifdef FPCOPROC
/*
* User coprocessor violation
*/
case T_COPERR|T_USER:
/* XXX What is a proper response here? */
ksi.ksi_signo = SIGFPE;
ksi.ksi_code = FPE_FLTINV;
break;
/*
* 6888x exceptions
*/
case T_FPERR|T_USER:
/*
* We pass along the 68881 status register which locore
* stashed in code for us.
*/
ksi.ksi_signo = SIGFPE;
ksi.ksi_code = fpsr2siginfocode(code);
break;
/*
* Kernel coprocessor violation
*/
case T_COPERR:
/*FALLTHROUGH*/
#endif
/*
* Kernel format error
*/
case T_FMTERR:
/*
* The user has most likely trashed the RTE or FP state info
* in the stack frame of a signal handler.
*/
type |= T_USER;
#ifdef DEBUG
printf("pid %d: kernel %s exception\n", p->p_pid,
type==T_COPERR ? "coprocessor" : "format");
#endif
mutex_enter(p->p_lock);
SIGACTION(p, SIGILL).sa_handler = SIG_DFL;
sigdelset(&p->p_sigctx.ps_sigignore, SIGILL);
sigdelset(&p->p_sigctx.ps_sigcatch, SIGILL);
sigdelset(&l->l_sigmask, SIGILL);
mutex_exit(p->p_lock);
ksi.ksi_signo = SIGILL;
ksi.ksi_addr = (void *)(int)fp->f_format;
/* XXX was ILL_RESAD_FAULT */
ksi.ksi_code = (type == T_COPERR) ?
ILL_COPROC : ILL_ILLOPC;
break;
/*
* Trace traps.
*
* M68k NetBSD uses trap #2,
* SUN 3.x uses trap #15,
* KGDB uses trap #15 (for kernel breakpoints; handled elsewhere).
*
* Amiga traps get mapped by locore.s into T_TRACE.
* SUN 3.x traps get passed through as T_TRAP15 and are not really
* supported yet.
*/
case T_TRACE:
case T_TRAP15:
fp->f_sr &= ~PSL_T;
ksi.ksi_signo = SIGTRAP;
break;
case T_TRACE|T_USER:
case T_TRAP15|T_USER:
#ifdef COMPAT_SUNOS
/*
* SunOS uses Trap #2 for a "CPU cache flush".
* Just flush the on-chip caches and return.
*/
if (p->p_emul == &emul_sunos) {
ICIA();
DCIU();
return;
}
#endif
fp->f_sr &= ~PSL_T;
ksi.ksi_signo = SIGTRAP;
break;
/*
* Kernel AST (should not happen)
*/
case T_ASTFLT:
panictrap(type, code, v, fp);
/*
* User AST
*/
case T_ASTFLT|T_USER:
astpending = 0;
spl0();
if (l->l_pflag & LP_OWEUPC) {
l->l_pflag &= ~LP_OWEUPC;
ADDUPROF(l);
}
userret(l, fp->f_pc, sticks);
return;
/*
* Kernel/User page fault
*/
case T_MMUFLT:
case T_MMUFLT|T_USER: /* page fault */
trapmmufault(type, code, v, fp, l, sticks);
return;
}
#ifdef DEBUG
if (ksi.ksi_signo != SIGTRAP)
printf("trapsignal(%d, %d, %d, %x, %x)\n", p->p_pid,
ksi.ksi_signo, ksi.ksi_code, v, fp->f_pc);
#endif
if (ksi.ksi_signo)
trapsignal(l, &ksi);
if ((type & T_USER) == 0)
return;
userret(l, fp->f_pc, sticks);
}
/*
* Process a pending write back
*/
int
_write_back (u_int wb, u_int wb_sts, u_int wb_data, u_int wb_addr, struct vm_map *wb_map)
/* wb: writeback type: 1, 2, or 3 */
/* wb_sts: writeback status information */
/* wb_data: data to writeback */
/* wb_addr: address to writeback to */
{
u_int wb_extra_page = 0;
u_int wb_rc, mmusr;
void *onfault;
#ifdef DEBUG
if (mmudebug)
printf("wb%d valid: %x %x %x\n",wb,wb_sts,wb_addr,wb_data);
#endif
/* See if we're going to span two pages (for word or long transfers) */
if((wb_sts & WBS_SZMASK) == WBS_SIZE_WORD)
if(trunc_page((vaddr_t)wb_addr) !=
trunc_page((vaddr_t)wb_addr+1))
wb_extra_page = 1;
if((wb_sts & WBS_SZMASK) == WBS_SIZE_LONG)
if(trunc_page((vaddr_t)wb_addr) !=
trunc_page((vaddr_t)wb_addr+3))
wb_extra_page = 3;
/*
* if it's writeback 3, we need to check the first page
*/
if (wb == 3) {
mmusr = probeva(wb_addr, wb_sts & WBS_TMMASK);
#ifdef DEBUG
if (mmudebug)
printf("wb3: probeva(%x,%x) = %x\n",
wb_addr + wb_extra_page, wb_sts & WBS_TMMASK, mmusr);
#endif
if((mmusr & (MMUSR_R | MMUSR_W)) != MMUSR_R) {
#ifdef DEBUG
if (mmudebug)
printf("wb3: need to bring in first page\n");
#endif
onfault = curpcb->pcb_onfault;
curpcb->pcb_onfault = NULL;
wb_rc = uvm_fault(wb_map,
trunc_page((vm_offset_t)wb_addr),
VM_PROT_READ | VM_PROT_WRITE);
curpcb->pcb_onfault = onfault;
if (wb_rc != 0)
return (wb_rc);
#ifdef DEBUG
if (mmudebug)
printf("wb3: first page brought in.\n");
#endif
}
}
/*
* now check to see if a second page is required
*/
if(wb_extra_page) {
mmusr = probeva(wb_addr+wb_extra_page, wb_sts & WBS_TMMASK);
#ifdef DEBUG
if (mmudebug)
printf("wb%d: probeva %x %x = %x\n",
wb, wb_addr + wb_extra_page,
wb_sts & WBS_TMMASK,mmusr);
#endif
if((mmusr & (MMUSR_R | MMUSR_W)) != MMUSR_R) {
#ifdef DEBUG
if (mmudebug)
printf("wb%d: page boundary crossed."
" Bringing in extra page.\n",wb);
#endif
onfault = curpcb->pcb_onfault;
curpcb->pcb_onfault = NULL;
wb_rc = uvm_fault(wb_map,
trunc_page((vm_offset_t)wb_addr + wb_extra_page),
VM_PROT_READ | VM_PROT_WRITE);
curpcb->pcb_onfault = onfault;
if (wb_rc != 0)
return (wb_rc);
}
#ifdef DEBUG
if (mmudebug)
printf("wb%d: extra page brought in okay.\n", wb);
#endif
}
/* Actually do the write now */
if ((wb_sts & WBS_TMMASK) == FC_USERD &&
!curpcb->pcb_onfault) {
curpcb->pcb_onfault = (void *) _wb_fault;
}
switch(wb_sts & WBS_SZMASK) {
case WBS_SIZE_BYTE :
__asm volatile ("movec %0,%%dfc ; movesb %1,%2@":: "d" (wb_sts & WBS_TMMASK),
"d" (wb_data),
"a" (wb_addr));
break;
case WBS_SIZE_WORD :
__asm volatile ("movec %0,%%dfc ; movesw %1,%2@":: "d" (wb_sts & WBS_TMMASK),
"d" (wb_data),
"a" (wb_addr));
break;
case WBS_SIZE_LONG :
__asm volatile ("movec %0,%%dfc ; movesl %1,%2@":: "d" (wb_sts & WBS_TMMASK),
"d" (wb_data),
"a" (wb_addr));
break;
}
if (curpcb->pcb_onfault == (void *) _wb_fault)
curpcb->pcb_onfault = NULL;
if ((wb_sts & WBS_TMMASK) != FC_USERD)
__asm volatile ("movec %0,%%dfc\n" : : "d" (FC_USERD));
return 0;
}
/*
* fault handler for write back
*/
void
_wb_fault(void)
{
#ifdef DEBUG
printf ("trap: writeback fault\n");
#endif
return;
}