text: /*	$OpenBSD: trap.c,v 1.19 1998/09/30 12:40:41 pefo Exp $	*/
  /* tracked to 1.23 */
  /*-
   * Copyright (c) 1988 University of Utah.
   * Copyright (c) 1992, 1993
   *	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 and Ralph Campbell.
   *
   * 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.
   * 4. 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
   *
   *	from: @(#)trap.c	8.5 (Berkeley) 1/11/94
   *	JNPR: trap.c,v 1.13.2.2 2007/08/29 10:03:49 girish
   */
  #include <sys/cdefs.h>
  __FBSDID("$FreeBSD: release/9.1.0/sys/mips/mips/trap.c 229374 2012-01-03 09:42:08Z kib $");
  
  #include "opt_ddb.h"
  #include "opt_global.h"
  #include "opt_ktrace.h"
  
  #define	NO_REG_DEFS	1	/* Prevent asm.h from including regdef.h */
  #include <sys/param.h>
  #include <sys/systm.h>
  #include <sys/sysent.h>
  #include <sys/proc.h>
  #include <sys/kernel.h>
  #include <sys/signalvar.h>
  #include <sys/syscall.h>
  #include <sys/lock.h>
  #include <vm/vm.h>
  #include <vm/vm_extern.h>
  #include <vm/vm_kern.h>
  #include <vm/vm_page.h>
  #include <vm/vm_map.h>
  #include <vm/vm_param.h>
  #include <sys/vmmeter.h>
  #include <sys/ptrace.h>
  #include <sys/user.h>
  #include <sys/buf.h>
  #include <sys/vnode.h>
  #include <sys/pioctl.h>
  #include <sys/sysctl.h>
  #include <sys/syslog.h>
  #include <sys/bus.h>
  #ifdef KTRACE
  #include <sys/ktrace.h>
  #endif
  #include <net/netisr.h>
  
  #include <machine/trap.h>
  #include <machine/cpu.h>
  #include <machine/pte.h>
  #include <machine/pmap.h>
  #include <machine/md_var.h>
  #include <machine/mips_opcode.h>
  #include <machine/frame.h>
  #include <machine/regnum.h>
  #include <machine/asm.h>
  
  #ifdef DDB
  #include <machine/db_machdep.h>
  #include <ddb/db_sym.h>
  #include <ddb/ddb.h>
  #include <sys/kdb.h>
  #endif
  
  #ifdef TRAP_DEBUG
  int trap_debug = 0;
  SYSCTL_INT(_machdep, OID_AUTO, trap_debug, CTLFLAG_RW,
      &trap_debug, 0, "Debug information on all traps");
  #endif
  
  static void log_illegal_instruction(const char *, struct trapframe *);
  static void log_bad_page_fault(char *, struct trapframe *, int);
  static void log_frame_dump(struct trapframe *frame);
  static void get_mapping_info(vm_offset_t, pd_entry_t **, pt_entry_t **);
  
  #ifdef TRAP_DEBUG
  static void trap_frame_dump(struct trapframe *frame);
  #endif
  
  void (*machExceptionTable[]) (void)= {
  /*
   * The kernel exception handlers.
   */
  	MipsKernIntr,		/* external interrupt */
  	MipsKernGenException,	/* TLB modification */
  	MipsTLBInvalidException,/* TLB miss (load or instr. fetch) */
  	MipsTLBInvalidException,/* TLB miss (store) */
  	MipsKernGenException,	/* address error (load or I-fetch) */
  	MipsKernGenException,	/* address error (store) */
  	MipsKernGenException,	/* bus error (I-fetch) */
  	MipsKernGenException,	/* bus error (load or store) */
  	MipsKernGenException,	/* system call */
  	MipsKernGenException,	/* breakpoint */
  	MipsKernGenException,	/* reserved instruction */
  	MipsKernGenException,	/* coprocessor unusable */
  	MipsKernGenException,	/* arithmetic overflow */
  	MipsKernGenException,	/* trap exception */
  	MipsKernGenException,	/* virtual coherence exception inst */
  	MipsKernGenException,	/* floating point exception */
  	MipsKernGenException,	/* reserved */
  	MipsKernGenException,	/* reserved */
  	MipsKernGenException,	/* reserved */
  	MipsKernGenException,	/* reserved */
  	MipsKernGenException,	/* reserved */
  	MipsKernGenException,	/* reserved */
  	MipsKernGenException,	/* reserved */
  	MipsKernGenException,	/* watch exception */
  	MipsKernGenException,	/* reserved */
  	MipsKernGenException,	/* reserved */
  	MipsKernGenException,	/* reserved */
  	MipsKernGenException,	/* reserved */
  	MipsKernGenException,	/* reserved */
  	MipsKernGenException,	/* reserved */
  	MipsKernGenException,	/* reserved */
  	MipsKernGenException,	/* virtual coherence exception data */
  /*
   * The user exception handlers.
   */
  	MipsUserIntr,		/* 0 */
  	MipsUserGenException,	/* 1 */
  	MipsTLBInvalidException,/* 2 */
  	MipsTLBInvalidException,/* 3 */
  	MipsUserGenException,	/* 4 */
  	MipsUserGenException,	/* 5 */
  	MipsUserGenException,	/* 6 */
  	MipsUserGenException,	/* 7 */
  	MipsUserGenException,	/* 8 */
  	MipsUserGenException,	/* 9 */
  	MipsUserGenException,	/* 10 */
  	MipsUserGenException,	/* 11 */
  	MipsUserGenException,	/* 12 */
  	MipsUserGenException,	/* 13 */
  	MipsUserGenException,	/* 14 */
  	MipsUserGenException,	/* 15 */
  	MipsUserGenException,	/* 16 */
  	MipsUserGenException,	/* 17 */
  	MipsUserGenException,	/* 18 */
  	MipsUserGenException,	/* 19 */
  	MipsUserGenException,	/* 20 */
  	MipsUserGenException,	/* 21 */
  	MipsUserGenException,	/* 22 */
  	MipsUserGenException,	/* 23 */
  	MipsUserGenException,	/* 24 */
  	MipsUserGenException,	/* 25 */
  	MipsUserGenException,	/* 26 */
  	MipsUserGenException,	/* 27 */
  	MipsUserGenException,	/* 28 */
  	MipsUserGenException,	/* 29 */
  	MipsUserGenException,	/* 20 */
  	MipsUserGenException,	/* 31 */
  };
  
  char *trap_type[] = {
  	"external interrupt",
  	"TLB modification",
  	"TLB miss (load or instr. fetch)",
  	"TLB miss (store)",
  	"address error (load or I-fetch)",
  	"address error (store)",
  	"bus error (I-fetch)",
  	"bus error (load or store)",
  	"system call",
  	"breakpoint",
  	"reserved instruction",
  	"coprocessor unusable",
  	"arithmetic overflow",
  	"trap",
  	"virtual coherency instruction",
  	"floating point",
  	"reserved 16",
  	"reserved 17",
  	"reserved 18",
  	"reserved 19",
  	"reserved 20",
  	"reserved 21",
  	"reserved 22",
  	"watch",
  	"reserved 24",
  	"reserved 25",
  	"reserved 26",
  	"reserved 27",
  	"reserved 28",
  	"reserved 29",
  	"reserved 30",
  	"virtual coherency data",
  };
  
  #if !defined(SMP) && (defined(DDB) || defined(DEBUG))
  struct trapdebug trapdebug[TRAPSIZE], *trp = trapdebug;
  #endif
  
  #if defined(DDB) || defined(DEBUG)
  void stacktrace(struct trapframe *);
  void logstacktrace(struct trapframe *);
  #endif
  
  #define	KERNLAND(x)	((vm_offset_t)(x) >= VM_MIN_KERNEL_ADDRESS && (vm_offset_t)(x) < VM_MAX_KERNEL_ADDRESS)
  #define	DELAYBRANCH(x)	((int)(x) < 0)
  
  /*
   * MIPS load/store access type
   */
  enum {
  	MIPS_LHU_ACCESS = 1,
  	MIPS_LH_ACCESS,
  	MIPS_LWU_ACCESS,
  	MIPS_LW_ACCESS,
  	MIPS_LD_ACCESS,
  	MIPS_SH_ACCESS,
  	MIPS_SW_ACCESS,
  	MIPS_SD_ACCESS
  };
  
  char *access_name[] = {
  	"Load Halfword Unsigned",
  	"Load Halfword",
  	"Load Word Unsigned",
  	"Load Word",
  	"Load Doubleword",
  	"Store Halfword",
  	"Store Word",
  	"Store Doubleword"
  };
  
  
  static int allow_unaligned_acc = 1;
  
  SYSCTL_INT(_vm, OID_AUTO, allow_unaligned_acc, CTLFLAG_RW,
      &allow_unaligned_acc, 0, "Allow unaligned accesses");
  
  static int emulate_unaligned_access(struct trapframe *frame, int mode);
  
  extern void fswintrberr(void); /* XXX */
  
  int
  cpu_fetch_syscall_args(struct thread *td, struct syscall_args *sa)
  {
  	struct trapframe *locr0 = td->td_frame;
  	struct sysentvec *se;
  	int error, nsaved;
  
  	bzero(sa->args, sizeof(sa->args));
  
  	/* compute next PC after syscall instruction */
  	td->td_pcb->pcb_tpc = sa->trapframe->pc; /* Remember if restart */
  	if (DELAYBRANCH(sa->trapframe->cause))	 /* Check BD bit */
  		locr0->pc = MipsEmulateBranch(locr0, sa->trapframe->pc, 0, 0);
  	else
  		locr0->pc += sizeof(int);
  	sa->code = locr0->v0;
  
  	switch (sa->code) {
  #if defined(__mips_n32) || defined(__mips_n64)
  	case SYS___syscall:
  		/*
  		 * Quads fit in a single register in
  		 * new ABIs.
  		 *
  		 * XXX o64?
  		 */
  #endif
  	case SYS_syscall:
  		/*
  		 * Code is first argument, followed by
  		 * actual args.
  		 */
  		sa->code = locr0->a0;
  		sa->args[0] = locr0->a1;
  		sa->args[1] = locr0->a2;
  		sa->args[2] = locr0->a3;
  		nsaved = 3;
  #if defined(__mips_n32) || defined(__mips_n64)
  		sa->args[3] = locr0->t4;
  		sa->args[4] = locr0->t5;
  		sa->args[5] = locr0->t6;
  		sa->args[6] = locr0->t7;
  		nsaved += 4;
  #endif
  		break;
  
  #if defined(__mips_o32)
  	case SYS___syscall:
  		/*
  		 * Like syscall, but code is a quad, so as
  		 * to maintain quad alignment for the rest
  		 * of the arguments.
  		 */
  		if (_QUAD_LOWWORD == 0)
  			sa->code = locr0->a0;
  		else
  			sa->code = locr0->a1;
  		sa->args[0] = locr0->a2;
  		sa->args[1] = locr0->a3;
  		nsaved = 2;
  		break;
  #endif
  
  	default:
  		sa->args[0] = locr0->a0;
  		sa->args[1] = locr0->a1;
  		sa->args[2] = locr0->a2;
  		sa->args[3] = locr0->a3;
  		nsaved = 4;
  #if defined (__mips_n32) || defined(__mips_n64)
  		sa->args[4] = locr0->t4;
  		sa->args[5] = locr0->t5;
  		sa->args[6] = locr0->t6;
  		sa->args[7] = locr0->t7;
  		nsaved += 4;
  #endif
  		break;
  	}
  #ifdef TRAP_DEBUG
  	if (trap_debug)
  		printf("SYSCALL #%d pid:%u\n", code, p->p_pid);
  #endif
  
  	se = td->td_proc->p_sysent;
  	if (se->sv_mask)
  		sa->code &= se->sv_mask;
  
  	if (sa->code >= se->sv_size)
  		sa->callp = &se->sv_table[0];
  	else
  		sa->callp = &se->sv_table[sa->code];
  
  	sa->narg = sa->callp->sy_narg;
  
  	if (sa->narg > nsaved) {
  #if defined(__mips_n32) || defined(__mips_n64)
  		/*
  		 * XXX
  		 * Is this right for new ABIs?  I think the 4 there
  		 * should be 8, size there are 8 registers to skip,
  		 * not 4, but I'm not certain.
  		 */
  		printf("SYSCALL #%u pid:%u, nargs > nsaved.\n", sa->code,
  		    td->td_proc->p_pid);
  #endif
  		error = copyin((caddr_t)(intptr_t)(locr0->sp +
  		    4 * sizeof(register_t)), (caddr_t)&sa->args[nsaved],
  		   (u_int)(sa->narg - nsaved) * sizeof(register_t));
  		if (error != 0) {
  			locr0->v0 = error;
  			locr0->a3 = 1;
  		}
  	} else
  		error = 0;
  
  	if (error == 0) {
  		td->td_retval[0] = 0;
  		td->td_retval[1] = locr0->v1;
  	}
  
  	return (error);
  }
  
  #undef __FBSDID
  #define __FBSDID(x)
  #include "../../kern/subr_syscall.c"
  
  /*
   * Handle an exception.
   * Called from MipsKernGenException() or MipsUserGenException()
   * when a processor trap occurs.
   * In the case of a kernel trap, we return the pc where to resume if
   * p->p_addr->u_pcb.pcb_onfault is set, otherwise, return old pc.
   */
  register_t
  trap(struct trapframe *trapframe)
  {
  	int type, usermode;
  	int i = 0;
  	unsigned ucode = 0;
  	struct thread *td = curthread;
  	struct proc *p = curproc;
  	vm_prot_t ftype;
  	pmap_t pmap;
  	int access_type;
  	ksiginfo_t ksi;
  	char *msg = NULL;
  	intptr_t addr = 0;
  	register_t pc;
  
  	trapdebug_enter(trapframe, 0);
  	
  	type = (trapframe->cause & MIPS3_CR_EXC_CODE) >> MIPS_CR_EXC_CODE_SHIFT;
  	if (TRAPF_USERMODE(trapframe)) {
  		type |= T_USER;
  		usermode = 1;
  	} else {
  		usermode = 0;
  	}
  
  	/*
  	 * Enable hardware interrupts if they were on before the trap. If it
  	 * was off disable all so we don't accidently enable it when doing a
  	 * return to userland.
  	 */
  	if (trapframe->sr & MIPS_SR_INT_IE) {
  		set_intr_mask(trapframe->sr & MIPS_SR_INT_MASK);
  		intr_enable();
  	} else {
  		intr_disable();
  	}
  
  #ifdef TRAP_DEBUG
  	if (trap_debug) {
  		static vm_offset_t last_badvaddr = 0;
  		static vm_offset_t this_badvaddr = 0;
  		static int count = 0;
  		u_int32_t pid;
  
  		printf("trap type %x (%s - ", type,
  		    trap_type[type & (~T_USER)]);
  
  		if (type & T_USER)
  			printf("user mode)\n");
  		else
  			printf("kernel mode)\n");
  
  #ifdef SMP
  		printf("cpuid = %d\n", PCPU_GET(cpuid));
  #endif
  		pid = mips_rd_entryhi() & TLBHI_ASID_MASK;
  		printf("badaddr = %#jx, pc = %#jx, ra = %#jx, sp = %#jx, sr = %jx, pid = %d, ASID = %u\n",
  		    (intmax_t)trapframe->badvaddr, (intmax_t)trapframe->pc, (intmax_t)trapframe->ra,
  		    (intmax_t)trapframe->sp, (intmax_t)trapframe->sr,
  		    (curproc ? curproc->p_pid : -1), pid);
  
  		switch (type & ~T_USER) {
  		case T_TLB_MOD:
  		case T_TLB_LD_MISS:
  		case T_TLB_ST_MISS:
  		case T_ADDR_ERR_LD:
  		case T_ADDR_ERR_ST:
  			this_badvaddr = trapframe->badvaddr;
  			break;
  		case T_SYSCALL:
  			this_badvaddr = trapframe->ra;
  			break;
  		default:
  			this_badvaddr = trapframe->pc;
  			break;
  		}
  		if ((last_badvaddr == this_badvaddr) &&
  		    ((type & ~T_USER) != T_SYSCALL)) {
  			if (++count == 3) {
  				trap_frame_dump(trapframe);
  				panic("too many faults at %p\n", (void *)last_badvaddr);
  			}
  		} else {
  			last_badvaddr = this_badvaddr;
  			count = 0;
  		}
  	}
  #endif
  	switch (type) {
  	case T_MCHECK:
  #ifdef DDB
  		kdb_trap(type, 0, trapframe);
  #endif
  		panic("MCHECK\n");
  		break;
  	case T_TLB_MOD:
  		/* check for kernel address */
  		if (KERNLAND(trapframe->badvaddr)) {
  			if (pmap_emulate_modified(kernel_pmap, 
  			    trapframe->badvaddr) != 0) {
  				ftype = VM_PROT_WRITE;
  				goto kernel_fault;
  			}
  			return (trapframe->pc);
  		}
  		/* FALLTHROUGH */
  
  	case T_TLB_MOD + T_USER:
  		pmap = &p->p_vmspace->vm_pmap;
  		if (pmap_emulate_modified(pmap, trapframe->badvaddr) != 0) {
  			ftype = VM_PROT_WRITE;
  			goto dofault;
  		}
  		if (!usermode)
  			return (trapframe->pc);
  		goto out;
  
  	case T_TLB_LD_MISS:
  	case T_TLB_ST_MISS:
  		ftype = (type == T_TLB_ST_MISS) ? VM_PROT_WRITE : VM_PROT_READ;
  		/* check for kernel address */
  		if (KERNLAND(trapframe->badvaddr)) {
  			vm_offset_t va;
  			int rv;
  
  	kernel_fault:
  			va = trunc_page((vm_offset_t)trapframe->badvaddr);
  			rv = vm_fault(kernel_map, va, ftype, VM_FAULT_NORMAL);
  			if (rv == KERN_SUCCESS)
  				return (trapframe->pc);
  			if (td->td_pcb->pcb_onfault != NULL) {
  				pc = (register_t)(intptr_t)td->td_pcb->pcb_onfault;
  				td->td_pcb->pcb_onfault = NULL;
  				return (pc);
  			}
  			goto err;
  		}
  
                  /*
  		 * It is an error for the kernel to access user space except
  		 * through the copyin/copyout routines.
  		 */
  		if (td->td_pcb->pcb_onfault == NULL)
  			goto err;
  
  		/* check for fuswintr() or suswintr() getting a page fault */
  		/* XXX There must be a nicer way to do this.  */
  		if (td->td_pcb->pcb_onfault == fswintrberr) {
  			pc = (register_t)(intptr_t)td->td_pcb->pcb_onfault;
  			td->td_pcb->pcb_onfault = NULL;
  			return (pc);
  		}
  
  		goto dofault;
  
  	case T_TLB_LD_MISS + T_USER:
  		ftype = VM_PROT_READ;
  		goto dofault;
  
  	case T_TLB_ST_MISS + T_USER:
  		ftype = VM_PROT_WRITE;
  dofault:
  		{
  			vm_offset_t va;
  			struct vmspace *vm;
  			vm_map_t map;
  			int rv = 0;
  
  			vm = p->p_vmspace;
  			map = &vm->vm_map;
  			va = trunc_page((vm_offset_t)trapframe->badvaddr);
  			if (KERNLAND(trapframe->badvaddr)) {
  				/*
  				 * Don't allow user-mode faults in kernel
  				 * address space.
  				 */
  				goto nogo;
  			}
  
  			/*
  			 * Keep swapout from messing with us during this
  			 * critical time.
  			 */
  			PROC_LOCK(p);
  			++p->p_lock;
  			PROC_UNLOCK(p);
  
  			rv = vm_fault(map, va, ftype, VM_FAULT_NORMAL);
  
  			PROC_LOCK(p);
  			--p->p_lock;
  			PROC_UNLOCK(p);
  #ifdef VMFAULT_TRACE
  			printf("vm_fault(%p (pmap %p), %p (%p), %x, %d) -> %x at pc %p\n",
  			    map, &vm->vm_pmap, (void *)va, (void *)(intptr_t)trapframe->badvaddr,
  			    ftype, VM_FAULT_NORMAL, rv, (void *)(intptr_t)trapframe->pc);
  #endif
  
  			if (rv == KERN_SUCCESS) {
  				if (!usermode) {
  					return (trapframe->pc);
  				}
  				goto out;
  			}
  	nogo:
  			if (!usermode) {
  				if (td->td_pcb->pcb_onfault != NULL) {
  					pc = (register_t)(intptr_t)td->td_pcb->pcb_onfault;
  					td->td_pcb->pcb_onfault = NULL;
  					return (pc);
  				}
  				goto err;
  			}
  			ucode = ftype;
  			i = ((rv == KERN_PROTECTION_FAILURE) ? SIGBUS : SIGSEGV);
  			addr = trapframe->pc;
  
  			msg = "BAD_PAGE_FAULT";
  			log_bad_page_fault(msg, trapframe, type);
  
  			break;
  		}
  
  	case T_ADDR_ERR_LD + T_USER:	/* misaligned or kseg access */
  	case T_ADDR_ERR_ST + T_USER:	/* misaligned or kseg access */
  		if (trapframe->badvaddr < 0 ||
  		    trapframe->badvaddr >= VM_MAXUSER_ADDRESS) {
  			msg = "ADDRESS_SPACE_ERR";
  		} else if (allow_unaligned_acc) {
  			int mode;
  
  			if (type == (T_ADDR_ERR_LD + T_USER))
  				mode = VM_PROT_READ;
  			else
  				mode = VM_PROT_WRITE;
  
  			access_type = emulate_unaligned_access(trapframe, mode);
  			if (access_type != 0)
  				goto out;
  			msg = "ALIGNMENT_FIX_ERR";
  		} else {
  			msg = "ADDRESS_ERR";
  		}
  
  		/* FALL THROUGH */
  
  	case T_BUS_ERR_IFETCH + T_USER:	/* BERR asserted to cpu */
  	case T_BUS_ERR_LD_ST + T_USER:	/* BERR asserted to cpu */
  		ucode = 0;	/* XXX should be VM_PROT_something */
  		i = SIGBUS;
  		addr = trapframe->pc;
  		if (!msg)
  			msg = "BUS_ERR";
  		log_bad_page_fault(msg, trapframe, type);
  		break;
  
  	case T_SYSCALL + T_USER:
  		{
  			struct syscall_args sa;
  			int error;
  
  			sa.trapframe = trapframe;
  			error = syscallenter(td, &sa);
  
  #if !defined(SMP) && (defined(DDB) || defined(DEBUG))
  			if (trp == trapdebug)
  				trapdebug[TRAPSIZE - 1].code = sa.code;
  			else
  				trp[-1].code = sa.code;
  #endif
  			trapdebug_enter(td->td_frame, -sa.code);
  
  			/*
  			 * The sync'ing of I & D caches for SYS_ptrace() is
  			 * done by procfs_domem() through procfs_rwmem()
  			 * instead of being done here under a special check
  			 * for SYS_ptrace().
  			 */
  			syscallret(td, error, &sa);
  			return (trapframe->pc);
  		}
  
  #ifdef DDB
  	case T_BREAK:
  		kdb_trap(type, 0, trapframe);
  		return (trapframe->pc);
  #endif
  
  	case T_BREAK + T_USER:
  		{
  			intptr_t va;
  			uint32_t instr;
  
  			/* compute address of break instruction */
  			va = trapframe->pc;
  			if (DELAYBRANCH(trapframe->cause))
  				va += sizeof(int);
  
  			/* read break instruction */
  			instr = fuword32((caddr_t)va);
  #if 0
  			printf("trap: %s (%d) breakpoint %x at %x: (adr %x ins %x)\n",
  			    p->p_comm, p->p_pid, instr, trapframe->pc,
  			    p->p_md.md_ss_addr, p->p_md.md_ss_instr);	/* XXX */
  #endif
  			if (td->td_md.md_ss_addr != va ||
  			    instr != MIPS_BREAK_SSTEP) {
  				i = SIGTRAP;
  				addr = trapframe->pc;
  				break;
  			}
  			/*
  			 * The restoration of the original instruction and
  			 * the clearing of the berakpoint will be done later
  			 * by the call to ptrace_clear_single_step() in
  			 * issignal() when SIGTRAP is processed.
  			 */
  			addr = trapframe->pc;
  			i = SIGTRAP;
  			break;
  		}
  
  	case T_IWATCH + T_USER:
  	case T_DWATCH + T_USER:
  		{
  			intptr_t va;
  
  			/* compute address of trapped instruction */
  			va = trapframe->pc;
  			if (DELAYBRANCH(trapframe->cause))
  				va += sizeof(int);
  			printf("watch exception @ %p\n", (void *)va);
  			i = SIGTRAP;
  			addr = va;
  			break;
  		}
  
  	case T_TRAP + T_USER:
  		{
  			intptr_t va;
  			uint32_t instr;
  			struct trapframe *locr0 = td->td_frame;
  
  			/* compute address of trap instruction */
  			va = trapframe->pc;
  			if (DELAYBRANCH(trapframe->cause))
  				va += sizeof(int);
  			/* read break instruction */
  			instr = fuword32((caddr_t)va);
  
  			if (DELAYBRANCH(trapframe->cause)) {	/* Check BD bit */
  				locr0->pc = MipsEmulateBranch(locr0, trapframe->pc, 0,
  				    0);
  			} else {
  				locr0->pc += sizeof(int);
  			}
  			addr = va;
  			i = SIGEMT;	/* Stuff it with something for now */
  			break;
  		}
  
  	case T_RES_INST + T_USER:
  		log_illegal_instruction("RES_INST", trapframe);
  		i = SIGILL;
  		addr = trapframe->pc;
  		break;
  	case T_C2E:
  	case T_C2E + T_USER:
  		goto err;
  		break;
  	case T_COP_UNUSABLE:
  		goto err;
  		break;
  	case T_COP_UNUSABLE + T_USER:
  #if !defined(CPU_HAVEFPU)
  		/* FP (COP1) instruction */
  		if ((trapframe->cause & MIPS_CR_COP_ERR) == 0x10000000) {
  			log_illegal_instruction("COP1_UNUSABLE", trapframe);
  			i = SIGILL;
  			break;
  		}
  #endif
  		if ((trapframe->cause & MIPS_CR_COP_ERR) != 0x10000000) {
  			log_illegal_instruction("COPn_UNUSABLE", trapframe);
  			i = SIGILL;	/* only FPU instructions allowed */
  			break;
  		}
  		addr = trapframe->pc;
  		MipsSwitchFPState(PCPU_GET(fpcurthread), td->td_frame);
  		PCPU_SET(fpcurthread, td);
  		td->td_frame->sr |= MIPS_SR_COP_1_BIT;
  		td->td_md.md_flags |= MDTD_FPUSED;
  		goto out;
  
  	case T_FPE:
  #if !defined(SMP) && (defined(DDB) || defined(DEBUG))
  		trapDump("fpintr");
  #else
  		printf("FPU Trap: PC %#jx CR %x SR %x\n",
  		    (intmax_t)trapframe->pc, (unsigned)trapframe->cause, (unsigned)trapframe->sr);
  		goto err;
  #endif
  
  	case T_FPE + T_USER:
  		MipsFPTrap(trapframe->sr, trapframe->cause, trapframe->pc);
  		goto out;
  
  	case T_OVFLOW + T_USER:
  		i = SIGFPE;
  		addr = trapframe->pc;
  		break;
  
  	case T_ADDR_ERR_LD:	/* misaligned access */
  	case T_ADDR_ERR_ST:	/* misaligned access */
  #ifdef TRAP_DEBUG
  		if (trap_debug) {
  			printf("+++ ADDR_ERR: type = %d, badvaddr = %#jx\n", type,
  			    (intmax_t)trapframe->badvaddr);
  		}
  #endif
  		/* Only allow emulation on a user address */
  		if (allow_unaligned_acc &&
  		    ((vm_offset_t)trapframe->badvaddr < VM_MAXUSER_ADDRESS)) {
  			int mode;
  
  			if (type == T_ADDR_ERR_LD)
  				mode = VM_PROT_READ;
  			else
  				mode = VM_PROT_WRITE;
  
  			access_type = emulate_unaligned_access(trapframe, mode);
  			if (access_type != 0)
  				return (trapframe->pc);
  		}
  		/* FALLTHROUGH */
  
  	case T_BUS_ERR_LD_ST:	/* BERR asserted to cpu */
  		if (td->td_pcb->pcb_onfault != NULL) {
  			pc = (register_t)(intptr_t)td->td_pcb->pcb_onfault;
  			td->td_pcb->pcb_onfault = NULL;
  			return (pc);
  		}
  
  		/* FALLTHROUGH */
  
  	default:
  err:
  
  #if !defined(SMP) && defined(DEBUG)
  		stacktrace(!usermode ? trapframe : td->td_frame);
  		trapDump("trap");
  #endif
  #ifdef SMP
  		printf("cpu:%d-", PCPU_GET(cpuid));
  #endif
  		printf("Trap cause = %d (%s - ", type,
  		    trap_type[type & (~T_USER)]);
  
  		if (type & T_USER)
  			printf("user mode)\n");
  		else
  			printf("kernel mode)\n");
  
  #ifdef TRAP_DEBUG
  		if (trap_debug)
  			printf("badvaddr = %#jx, pc = %#jx, ra = %#jx, sr = %#jxx\n",
  			       (intmax_t)trapframe->badvaddr, (intmax_t)trapframe->pc, (intmax_t)trapframe->ra,
  			       (intmax_t)trapframe->sr);
  #endif
  
  #ifdef KDB
  		if (debugger_on_panic || kdb_active) {
  			kdb_trap(type, 0, trapframe);
  		}
  #endif
  		panic("trap");
  	}
  	td->td_frame->pc = trapframe->pc;
  	td->td_frame->cause = trapframe->cause;
  	td->td_frame->badvaddr = trapframe->badvaddr;
  	ksiginfo_init_trap(&ksi);
  	ksi.ksi_signo = i;
  	ksi.ksi_code = ucode;
  	ksi.ksi_addr = (void *)addr;
  	ksi.ksi_trapno = type;
  	trapsignal(td, &ksi);
  out:
  
  	/*
  	 * Note: we should only get here if returning to user mode.
  	 */
  	userret(td, trapframe);
  	mtx_assert(&Giant, MA_NOTOWNED);
  	return (trapframe->pc);
  }
  
  #if !defined(SMP) && (defined(DDB) || defined(DEBUG))
  void
  trapDump(char *msg)
  {
  	register_t s;
  	int i;
  
  	s = intr_disable();
  	printf("trapDump(%s)\n", msg);
  	for (i = 0; i < TRAPSIZE; i++) {
  		if (trp == trapdebug) {
  			trp = &trapdebug[TRAPSIZE - 1];
  		} else {
  			trp--;
  		}
  
  		if (trp->cause == 0)
  			break;
  
  		printf("%s: ADR %jx PC %jx CR %jx SR %jx\n",
  		    trap_type[(trp->cause & MIPS3_CR_EXC_CODE) >> 
  			MIPS_CR_EXC_CODE_SHIFT],
  		    (intmax_t)trp->vadr, (intmax_t)trp->pc,
  		    (intmax_t)trp->cause, (intmax_t)trp->status);
  
  		printf("   RA %jx SP %jx code %d\n", (intmax_t)trp->ra,
  		    (intmax_t)trp->sp, (int)trp->code);
  	}
  	intr_restore(s);
  }
  #endif
  
  
  /*
   * Return the resulting PC as if the branch was executed.
   */
  uintptr_t
  MipsEmulateBranch(struct trapframe *framePtr, uintptr_t instPC, int fpcCSR,
      uintptr_t instptr)
  {
  	InstFmt inst;
  	register_t *regsPtr = (register_t *) framePtr;
  	uintptr_t retAddr = 0;
  	int condition;
  
  #define	GetBranchDest(InstPtr, inst) \
  	(InstPtr + 4 + ((short)inst.IType.imm << 2))
  
  
  	if (instptr) {
  		if (instptr < MIPS_KSEG0_START)
  			inst.word = fuword32((void *)instptr);
  		else
  			inst = *(InstFmt *) instptr;
  	} else {
  		if ((vm_offset_t)instPC < MIPS_KSEG0_START)
  			inst.word = fuword32((void *)instPC);
  		else
  			inst = *(InstFmt *) instPC;
  	}
  
  	switch ((int)inst.JType.op) {
  	case OP_SPECIAL:
  		switch ((int)inst.RType.func) {
  		case OP_JR:
  		case OP_JALR:
  			retAddr = regsPtr[inst.RType.rs];
  			break;
  
  		default:
  			retAddr = instPC + 4;
  			break;
  		}
  		break;
  
  	case OP_BCOND:
  		switch ((int)inst.IType.rt) {
  		case OP_BLTZ:
  		case OP_BLTZL:
  		case OP_BLTZAL:
  		case OP_BLTZALL:
  			if ((int)(regsPtr[inst.RType.rs]) < 0)
  				retAddr = GetBranchDest(instPC, inst);
  			else
  				retAddr = instPC + 8;
  			break;
  
  		case OP_BGEZ:
  		case OP_BGEZL:
  		case OP_BGEZAL:
  		case OP_BGEZALL:
  			if ((int)(regsPtr[inst.RType.rs]) >= 0)
  				retAddr = GetBranchDest(instPC, inst);
  			else
  				retAddr = instPC + 8;
  			break;
  
  		case OP_TGEI:
  		case OP_TGEIU:
  		case OP_TLTI:
  		case OP_TLTIU:
  		case OP_TEQI:
  		case OP_TNEI:
  			retAddr = instPC + 4;	/* Like syscall... */
  			break;
  
  		default:
  			panic("MipsEmulateBranch: Bad branch cond");
  		}
  		break;
  
  	case OP_J:
  	case OP_JAL:
  		retAddr = (inst.JType.target << 2) |
  		    ((unsigned)(instPC + 4) & 0xF0000000);
  		break;
  
  	case OP_BEQ:
  	case OP_BEQL:
  		if (regsPtr[inst.RType.rs] == regsPtr[inst.RType.rt])
  			retAddr = GetBranchDest(instPC, inst);
  		else
  			retAddr = instPC + 8;
  		break;
  
  	case OP_BNE:
  	case OP_BNEL:
  		if (regsPtr[inst.RType.rs] != regsPtr[inst.RType.rt])
  			retAddr = GetBranchDest(instPC, inst);
  		else
  			retAddr = instPC + 8;
  		break;
  
  	case OP_BLEZ:
  	case OP_BLEZL:
  		if ((int)(regsPtr[inst.RType.rs]) <= 0)
  			retAddr = GetBranchDest(instPC, inst);
  		else
  			retAddr = instPC + 8;
  		break;
  
  	case OP_BGTZ:
  	case OP_BGTZL:
  		if ((int)(regsPtr[inst.RType.rs]) > 0)
  			retAddr = GetBranchDest(instPC, inst);
  		else
  			retAddr = instPC + 8;
  		break;
  
  	case OP_COP1:
  		switch (inst.RType.rs) {
  		case OP_BCx:
  		case OP_BCy:
  			if ((inst.RType.rt & COPz_BC_TF_MASK) == COPz_BC_TRUE)
  				condition = fpcCSR & MIPS_FPU_COND_BIT;
  			else
  				condition = !(fpcCSR & MIPS_FPU_COND_BIT);
  			if (condition)
  				retAddr = GetBranchDest(instPC, inst);
  			else
  				retAddr = instPC + 8;
  			break;
  
  		default:
  			retAddr = instPC + 4;
  		}
  		break;
  
  	default:
  		retAddr = instPC + 4;
  	}
  	return (retAddr);
  }
  
  
  #if defined(DDB) || defined(DEBUG)
  /*
   * Print a stack backtrace.
   */
  void
  stacktrace(struct trapframe *regs)
  {
  	stacktrace_subr(regs->pc, regs->sp, regs->ra, printf);
  }
  #endif
  
  static void
  log_frame_dump(struct trapframe *frame)
  {
  	log(LOG_ERR, "Trapframe Register Dump:\n");
  	log(LOG_ERR, "\tzero: %#jx\tat: %#jx\tv0: %#jx\tv1: %#jx\n",
  	    (intmax_t)0, (intmax_t)frame->ast, (intmax_t)frame->v0, (intmax_t)frame->v1);
  
  	log(LOG_ERR, "\ta0: %#jx\ta1: %#jx\ta2: %#jx\ta3: %#jx\n",
  	    (intmax_t)frame->a0, (intmax_t)frame->a1, (intmax_t)frame->a2, (intmax_t)frame->a3);
  
  	log(LOG_ERR, "\tt0: %#jx\tt1: %#jx\tt2: %#jx\tt3: %#jx\n",
  	    (intmax_t)frame->t0, (intmax_t)frame->t1, (intmax_t)frame->t2, (intmax_t)frame->t3);
  
  	log(LOG_ERR, "\tt4: %#jx\tt5: %#jx\tt6: %#jx\tt7: %#jx\n",
  	    (intmax_t)frame->t4, (intmax_t)frame->t5, (intmax_t)frame->t6, (intmax_t)frame->t7);
  
  	log(LOG_ERR, "\tt8: %#jx\tt9: %#jx\ts0: %#jx\ts1: %#jx\n",
  	    (intmax_t)frame->t8, (intmax_t)frame->t9, (intmax_t)frame->s0, (intmax_t)frame->s1);
  
  	log(LOG_ERR, "\ts2: %#jx\ts3: %#jx\ts4: %#jx\ts5: %#jx\n",
  	    (intmax_t)frame->s2, (intmax_t)frame->s3, (intmax_t)frame->s4, (intmax_t)frame->s5);
  
  	log(LOG_ERR, "\ts6: %#jx\ts7: %#jx\tk0: %#jx\tk1: %#jx\n",
  	    (intmax_t)frame->s6, (intmax_t)frame->s7, (intmax_t)frame->k0, (intmax_t)frame->k1);
  
  	log(LOG_ERR, "\tgp: %#jx\tsp: %#jx\ts8: %#jx\tra: %#jx\n",
  	    (intmax_t)frame->gp, (intmax_t)frame->sp, (intmax_t)frame->s8, (intmax_t)frame->ra);
  
  	log(LOG_ERR, "\tsr: %#jx\tmullo: %#jx\tmulhi: %#jx\tbadvaddr: %#jx\n",
  	    (intmax_t)frame->sr, (intmax_t)frame->mullo, (intmax_t)frame->mulhi, (intmax_t)frame->badvaddr);
  
  #ifdef IC_REG
  	log(LOG_ERR, "\tcause: %#jx\tpc: %#jx\tic: %#jx\n",
  	    (intmax_t)frame->cause, (intmax_t)frame->pc, (intmax_t)frame->ic);
  #else
  	log(LOG_ERR, "\tcause: %#jx\tpc: %#jx\n",
  	    (intmax_t)frame->cause, (intmax_t)frame->pc);
  #endif
  }
  
  #ifdef TRAP_DEBUG
  static void
  trap_frame_dump(struct trapframe *frame)
  {
  	printf("Trapframe Register Dump:\n");
  	printf("\tzero: %#jx\tat: %#jx\tv0: %#jx\tv1: %#jx\n",
  	    (intmax_t)0, (intmax_t)frame->ast, (intmax_t)frame->v0, (intmax_t)frame->v1);
  
  	printf("\ta0: %#jx\ta1: %#jx\ta2: %#jx\ta3: %#jx\n",
  	    (intmax_t)frame->a0, (intmax_t)frame->a1, (intmax_t)frame->a2, (intmax_t)frame->a3);
  
  	printf("\tt0: %#jx\tt1: %#jx\tt2: %#jx\tt3: %#jx\n",
  	    (intmax_t)frame->t0, (intmax_t)frame->t1, (intmax_t)frame->t2, (intmax_t)frame->t3);
  
  	printf("\tt4: %#jx\tt5: %#jx\tt6: %#jx\tt7: %#jx\n",
  	    (intmax_t)frame->t4, (intmax_t)frame->t5, (intmax_t)frame->t6, (intmax_t)frame->t7);
  
  	printf("\tt8: %#jx\tt9: %#jx\ts0: %#jx\ts1: %#jx\n",
  	    (intmax_t)frame->t8, (intmax_t)frame->t9, (intmax_t)frame->s0, (intmax_t)frame->s1);
  
  	printf("\ts2: %#jx\ts3: %#jx\ts4: %#jx\ts5: %#jx\n",
  	    (intmax_t)frame->s2, (intmax_t)frame->s3, (intmax_t)frame->s4, (intmax_t)frame->s5);
  
  	printf("\ts6: %#jx\ts7: %#jx\tk0: %#jx\tk1: %#jx\n",
  	    (intmax_t)frame->s6, (intmax_t)frame->s7, (intmax_t)frame->k0, (intmax_t)frame->k1);
  
  	printf("\tgp: %#jx\tsp: %#jx\ts8: %#jx\tra: %#jx\n",
  	    (intmax_t)frame->gp, (intmax_t)frame->sp, (intmax_t)frame->s8, (intmax_t)frame->ra);
  
  	printf("\tsr: %#jx\tmullo: %#jx\tmulhi: %#jx\tbadvaddr: %#jx\n",
  	    (intmax_t)frame->sr, (intmax_t)frame->mullo, (intmax_t)frame->mulhi, (intmax_t)frame->badvaddr);
  
  #ifdef IC_REG
  	printf("\tcause: %#jx\tpc: %#jx\tic: %#jx\n",
  	    (intmax_t)frame->cause, (intmax_t)frame->pc, (intmax_t)frame->ic);
  #else
  	printf("\tcause: %#jx\tpc: %#jx\n",
  	    (intmax_t)frame->cause, (intmax_t)frame->pc);
  #endif
  }
  
  #endif
  
  
  static void
  get_mapping_info(vm_offset_t va, pd_entry_t **pdepp, pt_entry_t **ptepp)
  {
  	pt_entry_t *ptep;
  	pd_entry_t *pdep;
  	struct proc *p = curproc;
  
  	pdep = (&(p->p_vmspace->vm_pmap.pm_segtab[(va >> SEGSHIFT) & (NPDEPG - 1)]));
  	if (*pdep)
  		ptep = pmap_pte(&p->p_vmspace->vm_pmap, va);
  	else
  		ptep = (pt_entry_t *)0;
  
  	*pdepp = pdep;
  	*ptepp = ptep;
  }
  
  static void
  log_illegal_instruction(const char *msg, struct trapframe *frame)
  {
  	pt_entry_t *ptep;
  	pd_entry_t *pdep;
  	unsigned int *addr;
  	struct proc *p = curproc;
  	register_t pc;
  
  #ifdef SMP
  	printf("cpuid = %d\n", PCPU_GET(cpuid));
  #endif
  	pc = frame->pc + (DELAYBRANCH(frame->cause) ? 4 : 0);
  	log(LOG_ERR, "%s: pid %d (%s), uid %d: pc %#jx ra %#jx\n",
  	    msg, p->p_pid, p->p_comm,
  	    p->p_ucred ? p->p_ucred->cr_uid : -1,
  	    (intmax_t)pc,
  	    (intmax_t)frame->ra);
  
  	/* log registers in trap frame */
  	log_frame_dump(frame);
  
  	get_mapping_info((vm_offset_t)pc, &pdep, &ptep);
  
  	/*
  	 * Dump a few words around faulting instruction, if the addres is
  	 * valid.
  	 */
  	if (!(pc & 3) &&
  	    useracc((caddr_t)(intptr_t)pc, sizeof(int) * 4, VM_PROT_READ)) {
  		/* dump page table entry for faulting instruction */
  		log(LOG_ERR, "Page table info for pc address %#jx: pde = %p, pte = %#jx\n",
  		    (intmax_t)pc, (void *)(intptr_t)*pdep, (uintmax_t)(ptep ? *ptep : 0));
  
  		addr = (unsigned int *)(intptr_t)pc;
  		log(LOG_ERR, "Dumping 4 words starting at pc address %p: \n",
  		    addr);
  		log(LOG_ERR, "%08x %08x %08x %08x\n",
  		    addr[0], addr[1], addr[2], addr[3]);
  	} else {
  		log(LOG_ERR, "pc address %#jx is inaccessible, pde = %p, pte = %#jx\n",
  		    (intmax_t)pc, (void *)(intptr_t)*pdep, (uintmax_t)(ptep ? *ptep : 0));
  	}
  }
  
  static void
  log_bad_page_fault(char *msg, struct trapframe *frame, int trap_type)
  {
  	pt_entry_t *ptep;
  	pd_entry_t *pdep;
  	unsigned int *addr;
  	struct proc *p = curproc;
  	char *read_or_write;
  	register_t pc;
  
  	trap_type &= ~T_USER;
  
  #ifdef SMP
  	printf("cpuid = %d\n", PCPU_GET(cpuid));
  #endif
  	switch (trap_type) {
  	case T_TLB_ST_MISS:
  	case T_ADDR_ERR_ST:
  		read_or_write = "write";
  		break;
  	case T_TLB_LD_MISS:
  	case T_ADDR_ERR_LD:
  	case T_BUS_ERR_IFETCH:
  		read_or_write = "read";
  		break;
  	default:
  		read_or_write = "";
  	}
  
  	pc = frame->pc + (DELAYBRANCH(frame->cause) ? 4 : 0);
  	log(LOG_ERR, "%s: pid %d (%s), uid %d: pc %#jx got a %s fault at %#jx\n",
  	    msg, p->p_pid, p->p_comm,
  	    p->p_ucred ? p->p_ucred->cr_uid : -1,
  	    (intmax_t)pc,
  	    read_or_write,
  	    (intmax_t)frame->badvaddr);
  
  	/* log registers in trap frame */
  	log_frame_dump(frame);
  
  	get_mapping_info((vm_offset_t)pc, &pdep, &ptep);
  
  	/*
  	 * Dump a few words around faulting instruction, if the addres is
  	 * valid.
  	 */
  	if (!(pc & 3) && (pc != frame->badvaddr) &&
  	    (trap_type != T_BUS_ERR_IFETCH) &&
  	    useracc((caddr_t)(intptr_t)pc, sizeof(int) * 4, VM_PROT_READ)) {
  		/* dump page table entry for faulting instruction */
  		log(LOG_ERR, "Page table info for pc address %#jx: pde = %p, pte = %#jx\n",
  		    (intmax_t)pc, (void *)(intptr_t)*pdep, (uintmax_t)(ptep ? *ptep : 0));
  
  		addr = (unsigned int *)(intptr_t)pc;
  		log(LOG_ERR, "Dumping 4 words starting at pc address %p: \n",
  		    addr);
  		log(LOG_ERR, "%08x %08x %08x %08x\n",
  		    addr[0], addr[1], addr[2], addr[3]);
  	} else {
  		log(LOG_ERR, "pc address %#jx is inaccessible, pde = %p, pte = %#jx\n",
  		    (intmax_t)pc, (void *)(intptr_t)*pdep, (uintmax_t)(ptep ? *ptep : 0));
  	}
  
  	get_mapping_info((vm_offset_t)frame->badvaddr, &pdep, &ptep);
  	log(LOG_ERR, "Page table info for bad address %#jx: pde = %p, pte = %#jx\n",
  	    (intmax_t)frame->badvaddr, (void *)(intptr_t)*pdep, (uintmax_t)(ptep ? *ptep : 0));
  }
  
  
  /*
   * Unaligned load/store emulation
   */
  static int
  mips_unaligned_load_store(struct trapframe *frame, int mode, register_t addr, register_t pc)
  {
  	register_t *reg = (register_t *) frame;
  	u_int32_t inst = *((u_int32_t *)(intptr_t)pc);
  	register_t value_msb, value;
  	unsigned size;
  
  	/*
  	 * ADDR_ERR faults have higher priority than TLB
  	 * Miss faults.  Therefore, it is necessary to
  	 * verify that the faulting address is a valid
  	 * virtual address within the process' address space
  	 * before trying to emulate the unaligned access.
  	 */
  	switch (MIPS_INST_OPCODE(inst)) {
  	case OP_LHU: case OP_LH:
  	case OP_SH:
  		size = 2;
  		break;
  	case OP_LWU: case OP_LW:
  	case OP_SW:
  		size = 4;
  		break;
  	case OP_LD:
  	case OP_SD:
  		size = 8;
  		break;
  	default:
  		printf("%s: unhandled opcode in address error: %#x\n", __func__, MIPS_INST_OPCODE(inst));
  		return (0);
  	}
  
  	if (!useracc((void *)((vm_offset_t)addr & ~(size - 1)), size * 2, mode))
  		return (0);
  
  	/*
  	 * XXX
  	 * Handle LL/SC LLD/SCD.
  	 */
  	switch (MIPS_INST_OPCODE(inst)) {
  	case OP_LHU:
  		KASSERT(mode == VM_PROT_READ, ("access mode must be read for load instruction."));
  		lbu_macro(value_msb, addr);
  		addr += 1;
  		lbu_macro(value, addr);
  		value |= value_msb << 8;
  		reg[MIPS_INST_RT(inst)] = value;
  		return (MIPS_LHU_ACCESS);
  
  	case OP_LH:
  		KASSERT(mode == VM_PROT_READ, ("access mode must be read for load instruction."));
  		lb_macro(value_msb, addr);
  		addr += 1;
  		lbu_macro(value, addr);
  		value |= value_msb << 8;
  		reg[MIPS_INST_RT(inst)] = value;
  		return (MIPS_LH_ACCESS);
  
  	case OP_LWU:
  		KASSERT(mode == VM_PROT_READ, ("access mode must be read for load instruction."));
  		lwl_macro(value, addr);
  		addr += 3;
  		lwr_macro(value, addr);
  		value &= 0xffffffff;
  		reg[MIPS_INST_RT(inst)] = value;
  		return (MIPS_LWU_ACCESS);
  
  	case OP_LW:
  		KASSERT(mode == VM_PROT_READ, ("access mode must be read for load instruction."));
  		lwl_macro(value, addr);
  		addr += 3;
  		lwr_macro(value, addr);
  		reg[MIPS_INST_RT(inst)] = value;
  		return (MIPS_LW_ACCESS);
  
  #if defined(__mips_n32) || defined(__mips_n64)
  	case OP_LD:
  		KASSERT(mode == VM_PROT_READ, ("access mode must be read for load instruction."));
  		ldl_macro(value, addr);
  		addr += 7;
  		ldr_macro(value, addr);
  		reg[MIPS_INST_RT(inst)] = value;
  		return (MIPS_LD_ACCESS);
  #endif
  
  	case OP_SH:
  		KASSERT(mode == VM_PROT_WRITE, ("access mode must be write for store instruction."));
  		value = reg[MIPS_INST_RT(inst)];
  		value_msb = value >> 8;
  		sb_macro(value_msb, addr);
  		addr += 1;
  		sb_macro(value, addr);
  		return (MIPS_SH_ACCESS);
  
  	case OP_SW:
  		KASSERT(mode == VM_PROT_WRITE, ("access mode must be write for store instruction."));
  		value = reg[MIPS_INST_RT(inst)];
  		swl_macro(value, addr);
  		addr += 3;
  		swr_macro(value, addr);
  		return (MIPS_SW_ACCESS);
  
  #if defined(__mips_n32) || defined(__mips_n64)
  	case OP_SD:
  		KASSERT(mode == VM_PROT_WRITE, ("access mode must be write for store instruction."));
  		value = reg[MIPS_INST_RT(inst)];
  		sdl_macro(value, addr);
  		addr += 7;
  		sdr_macro(value, addr);
  		return (MIPS_SD_ACCESS);
  #endif
  	}
  	panic("%s: should not be reached.", __func__);
  }
  
  
  static int
  emulate_unaligned_access(struct trapframe *frame, int mode)
  {
  	register_t pc;
  	int access_type = 0;
  
  	pc = frame->pc + (DELAYBRANCH(frame->cause) ? 4 : 0);
  
  	/*
  	 * Fall through if it's instruction fetch exception
  	 */
  	if (!((pc & 3) || (pc == frame->badvaddr))) {
  
  		/*
  		 * Handle unaligned load and store
  		 */
  
  		/*
  		 * Return access type if the instruction was emulated.
  		 * Otherwise restore pc and fall through.
  		 */
  		access_type = mips_unaligned_load_store(frame,
  		    mode, frame->badvaddr, pc);
  
  		if (access_type) {
  			if (DELAYBRANCH(frame->cause))
  				frame->pc = MipsEmulateBranch(frame, frame->pc,
  				    0, 0);
  			else
  				frame->pc += 4;
  
  			log(LOG_INFO, "Unaligned %s: pc=%#jx, badvaddr=%#jx\n",
  			    access_name[access_type - 1], (intmax_t)pc,
  			    (intmax_t)frame->badvaddr);
  		}
  	}
  	return access_type;
  }
  