]> git.saurik.com Git - apple/xnu.git/blobdiff - osfmk/arm/trap.c
xnu-4570.1.46.tar.gz
[apple/xnu.git] / osfmk / arm / trap.c
diff --git a/osfmk/arm/trap.c b/osfmk/arm/trap.c
new file mode 100644 (file)
index 0000000..617c652
--- /dev/null
@@ -0,0 +1,897 @@
+/*
+ * Copyright (c) 2007 Apple Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ *
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
+#include <kern/debug.h>
+#include <mach_kdp.h>
+#include <machine/endian.h>
+#include <mach/mach_types.h>
+#include <mach/boolean.h>
+#include <mach/vm_prot.h>
+#include <mach/vm_types.h>
+#include <mach/mach_traps.h>
+
+#include <mach/exception.h>
+#include <mach/kern_return.h>
+#include <mach/vm_param.h>
+#include <mach/message.h>
+#include <mach/machine/thread_status.h>
+
+#include <vm/vm_page.h>
+#include <vm/pmap.h>
+#include <vm/vm_fault.h>
+#include <vm/vm_kern.h>
+
+#include <kern/ast.h>
+#include <kern/thread.h>
+#include <kern/task.h>
+#include <kern/sched_prim.h>
+
+#include <sys/kdebug.h>
+
+#include <arm/trap.h>
+#include <arm/caches_internal.h>
+#include <arm/cpu_data_internal.h>
+#include <arm/machdep_call.h>
+#include <arm/machine_routines.h>
+#include <arm/misc_protos.h>
+#include <arm/setjmp.h>
+#include <arm/proc_reg.h>
+
+/*
+ * External function prototypes.
+ */
+#include <kern/syscall_sw.h>
+#include <kern/host.h>
+#include <kern/processor.h>
+
+
+#if CONFIG_DTRACE
+extern kern_return_t dtrace_user_probe(arm_saved_state_t* regs, unsigned int instr);
+extern boolean_t dtrace_tally_fault(user_addr_t);
+
+/* Traps for userland processing. Can't include bsd/sys/fasttrap_isa.h, so copy and paste the trap instructions
+   over from that file. Need to keep these in sync! */
+#define FASTTRAP_ARM_INSTR 0xe7ffdefc
+#define FASTTRAP_THUMB_INSTR 0xdefc
+
+#define FASTTRAP_ARM_RET_INSTR 0xe7ffdefb
+#define FASTTRAP_THUMB_RET_INSTR 0xdefb
+
+/* See <rdar://problem/4613924> */
+perfCallback tempDTraceTrapHook = NULL; /* Pointer to DTrace fbt trap hook routine */
+#endif
+
+#define COPYIN(dst, src, size)                                 \
+       ((regs->cpsr & PSR_MODE_MASK) != PSR_USER_MODE) ?       \
+               copyin_kern(dst, src, size)                     \
+       :                                                       \
+               copyin(dst, src, size)
+
+#define COPYOUT(src, dst, size)                                        \
+       ((regs->cpsr & PSR_MODE_MASK) != PSR_USER_MODE) ?       \
+               copyout_kern(src, dst, size)                    \
+       :                                                       \
+               copyout(src, dst, size)
+
+/* Second-level exception handlers forward declarations */
+void            sleh_undef(struct arm_saved_state *, struct arm_vfpsaved_state *);
+void            sleh_abort(struct arm_saved_state *, int);
+static kern_return_t sleh_alignment(struct arm_saved_state *);
+static void    panic_with_thread_kernel_state(const char *msg, arm_saved_state_t *regs);
+
+
+volatile perfCallback    perfTrapHook = NULL;  /* Pointer to CHUD trap hook routine */
+
+int             sleh_alignment_count = 0;
+int             trap_on_alignment_fault = 0;
+
+/*
+ *     Routine:        sleh_undef
+ *     Function:       Second level exception handler for undefined exception
+ */
+
+void
+sleh_undef(struct arm_saved_state * regs, struct arm_vfpsaved_state * vfp_ss __unused)
+{
+       exception_type_t exception = EXC_BAD_INSTRUCTION;
+       mach_exception_data_type_t code[2] = {EXC_ARM_UNDEFINED};
+       mach_msg_type_number_t codeCnt = 2;
+       thread_t        thread = current_thread();
+       vm_offset_t     recover;
+
+       recover = thread->recover;
+       thread->recover = 0;
+
+       getCpuDatap()->cpu_stat.undef_ex_cnt++;
+
+       /* Inherit the interrupt masks from previous */
+       if (!(regs->cpsr & PSR_INTMASK))
+               ml_set_interrupts_enabled(TRUE);
+
+#if CONFIG_DTRACE
+       if (tempDTraceTrapHook) {
+               if (tempDTraceTrapHook(exception, regs, 0, 0) == KERN_SUCCESS) {
+                       /*
+                        * If it succeeds, we are done...
+                        */
+                       goto exit;
+               }
+       }
+
+       /* Check to see if we've hit a userland probe */
+       if ((regs->cpsr & PSR_MODE_MASK) == PSR_USER_MODE) {
+               if (regs->cpsr & PSR_TF) {
+                       uint16_t instr;
+
+                       if(COPYIN((user_addr_t)(regs->pc), (char *)&instr,(vm_size_t)(sizeof(uint16_t))) != KERN_SUCCESS)
+                               goto exit;
+
+                       if (instr == FASTTRAP_THUMB_INSTR || instr == FASTTRAP_THUMB_RET_INSTR) {
+                               if (dtrace_user_probe(regs, instr) == KERN_SUCCESS)
+                                       /* If it succeeds, we are done... */
+                                       goto exit;
+                       }
+               } else {
+                       uint32_t instr;
+
+                       if(COPYIN((user_addr_t)(regs->pc), (char *)&instr,(vm_size_t)(sizeof(uint32_t))) != KERN_SUCCESS)
+                               goto exit;
+
+                       if (instr == FASTTRAP_ARM_INSTR || instr == FASTTRAP_ARM_RET_INSTR) {
+                               if (dtrace_user_probe(regs, instr) == KERN_SUCCESS)
+                                       /* If it succeeds, we are done... */
+                                       goto exit;
+                       }
+               }
+       }
+#endif /* CONFIG_DTRACE */
+
+
+       if (regs->cpsr & PSR_TF) {
+               unsigned short instr;
+
+               if(COPYIN((user_addr_t)(regs->pc), (char *)&instr,(vm_size_t)(sizeof(unsigned short))) != KERN_SUCCESS)
+                       goto exit;
+
+               if (IS_THUMB32(instr)) {
+                       unsigned int    instr32;
+
+                       instr32 = (instr<<16);
+
+                       if(COPYIN((user_addr_t)(((unsigned short *) (regs->pc))+1), (char *)&instr,(vm_size_t)(sizeof(unsigned short))) != KERN_SUCCESS)
+                               goto exit;
+
+                       instr32 |= instr;
+                       code[1] = instr32;
+
+#if    __ARM_VFP__
+                       if (IS_THUMB_VFP(instr32)) {
+                               /* We no longer manage FPEXC beyond bootstrap, so verify that VFP is still enabled. */
+                               if (!get_vfp_enabled())
+                                       panic("VFP was disabled (thumb); VFP should always be enabled");
+                       }
+#endif
+               } else {
+                       /* I don't believe we have any 16 bit VFP instructions, so just set code[1]. */
+                       code[1] = instr;
+
+                       if (IS_THUMB_GDB_TRAP(instr)) {
+                               exception = EXC_BREAKPOINT;
+                               code[0] = EXC_ARM_BREAKPOINT;
+                       }
+               }
+       } else {
+               uint32_t instr;
+
+               if(COPYIN((user_addr_t)(regs->pc), (char *)&instr,(vm_size_t)(sizeof(uint32_t))) != KERN_SUCCESS)
+                       goto exit;
+
+               code[1] = instr;
+#if    __ARM_VFP__
+               if (IS_ARM_VFP(instr)) {
+                       /* We no longer manage FPEXC beyond bootstrap, so verify that VFP is still enabled. */
+                       if (!get_vfp_enabled())
+                               panic("VFP was disabled (arm); VFP should always be enabled");
+               }
+#endif
+
+               if (IS_ARM_GDB_TRAP(instr)) {
+                       exception = EXC_BREAKPOINT;
+                       code[0] = EXC_ARM_BREAKPOINT;
+               }
+       }
+
+       if (!((regs->cpsr & PSR_MODE_MASK) == PSR_USER_MODE)) {
+               boolean_t       intr;
+
+               intr = ml_set_interrupts_enabled(FALSE);
+
+               if (exception == EXC_BREAKPOINT) {
+                       /* Save off the context here (so that the debug logic
+                        * can see the original state of this thread).
+                        */
+                       vm_offset_t kstackptr = current_thread()->machine.kstackptr;
+                       *((arm_saved_state_t *) kstackptr) = *regs;
+
+                       DebuggerCall(exception, regs);
+                       (void) ml_set_interrupts_enabled(intr);
+                       goto exit;
+               }
+               panic_context(exception, (void *)regs, "undefined kernel instruction\n"
+                     "r0:   0x%08x  r1: 0x%08x  r2: 0x%08x  r3: 0x%08x\n"
+                     "r4:   0x%08x  r5: 0x%08x  r6: 0x%08x  r7: 0x%08x\n"
+                     "r8:   0x%08x  r9: 0x%08x r10: 0x%08x r11: 0x%08x\n"
+                     "r12:  0x%08x  sp: 0x%08x  lr: 0x%08x  pc: 0x%08x\n"
+                     "cpsr: 0x%08x fsr: 0x%08x far: 0x%08x\n",
+                     regs->r[0], regs->r[1], regs->r[2], regs->r[3],
+                     regs->r[4], regs->r[5], regs->r[6], regs->r[7],
+                     regs->r[8], regs->r[9], regs->r[10], regs->r[11],
+                     regs->r[12], regs->sp, regs->lr, regs->pc,
+                     regs->cpsr, regs->fsr, regs->far);
+
+               (void) ml_set_interrupts_enabled(intr);
+
+       } else {
+               exception_triage(exception, code, codeCnt);
+               /* NOTREACHED */
+       }
+
+exit:
+       if (recover)
+               thread->recover = recover;
+}
+
+/*
+ *     Routine:        sleh_abort
+ *     Function:       Second level exception handler for abort(Pref/Data)
+ */
+
+void
+sleh_abort(struct arm_saved_state * regs, int type)
+{
+       int             status; 
+       int             debug_status=0;
+       int             spsr;
+       int             exc;
+       mach_exception_data_type_t codes[2];
+       vm_map_t        map;
+       vm_map_address_t vaddr;
+       vm_map_address_t fault_addr;
+       vm_prot_t       fault_type;
+       kern_return_t   result;
+       vm_offset_t     recover;
+       thread_t        thread = current_thread();
+       boolean_t               intr;
+
+       recover = thread->recover;
+       thread->recover = 0;
+
+       status = regs->fsr & FSR_MASK;
+       spsr = regs->cpsr;
+
+       /* The DSFR/IFSR.ExT bit indicates "IMPLEMENTATION DEFINED" classification.
+        * Allow a platform-level error handler to decode it.
+        */
+       if ((regs->fsr) & FSR_EXT) {
+               cpu_data_t      *cdp = getCpuDatap();
+
+               if (cdp->platform_error_handler != (platform_error_handler_t) NULL) {
+                       (*(platform_error_handler_t)cdp->platform_error_handler) (cdp->cpu_id, 0);
+                       /* If a platform error handler is registered, expect it to panic, not fall through */
+                       panic("Unexpected return from platform_error_handler");
+               }
+       }
+
+       /* Done with asynchronous handling; re-enable here so that subsequent aborts are taken as early as possible. */
+       reenable_async_aborts();
+
+       if (ml_at_interrupt_context())
+               panic_with_thread_kernel_state("sleh_abort at interrupt context", regs);
+
+       fault_addr = vaddr = regs->far;
+
+       if (type == T_DATA_ABT) {
+               getCpuDatap()->cpu_stat.data_ex_cnt++;
+       } else { /* T_PREFETCH_ABT */
+               getCpuDatap()->cpu_stat.instr_ex_cnt++;
+               fault_type = VM_PROT_READ | VM_PROT_EXECUTE;
+       }
+
+       if (status == FSR_DEBUG)
+           debug_status = arm_debug_read_dscr() & ARM_DBGDSCR_MOE_MASK;
+
+       /* Inherit the interrupt masks from previous */
+       if (!(spsr & PSR_INTMASK))
+               ml_set_interrupts_enabled(TRUE);
+
+       if (type == T_DATA_ABT) {
+               /*
+                * Now that interrupts are reenabled, we can perform any needed
+                * copyin operations.
+                *
+                * Because we have reenabled interrupts, any instruction copy
+                * must be a copyin, even on UP systems.
+                */
+
+               if (regs->fsr & DFSR_WRITE) {
+                       fault_type = (VM_PROT_READ | VM_PROT_WRITE);
+                       /* Cache operations report faults as write access, change these to read access */
+                       /* Cache operations are invoked from arm mode for now */
+                       if (!(regs->cpsr & PSR_TF)) {
+                               unsigned int    ins;
+
+                               if(COPYIN((user_addr_t)(regs->pc), (char *)&ins,(vm_size_t)(sizeof(unsigned int))) != KERN_SUCCESS)
+                                       goto exit;
+
+                               if (arm_mcr_cp15(ins) || arm_mcrr_cp15(ins))
+                                       fault_type = VM_PROT_READ;
+                       }
+               } else {
+                       fault_type = VM_PROT_READ;
+                       /*
+                        * DFSR is not getting the "write" bit set
+                        * when a swp instruction is encountered (even when it is
+                        * a write fault.
+                        */
+                       if (!(regs->cpsr & PSR_TF)) {
+                               unsigned int    ins;
+
+                               if(COPYIN((user_addr_t)(regs->pc), (char *)&ins,(vm_size_t)(sizeof(unsigned int))) != KERN_SUCCESS)
+                                       goto exit;
+
+                               if ((ins & ARM_SWP_MASK) == ARM_SWP)
+                                       fault_type = VM_PROT_WRITE;
+                       }
+               }
+       }
+
+       if ((spsr & PSR_MODE_MASK) != PSR_USER_MODE) {
+               /* Fault in kernel mode */
+
+               if ((status == FSR_DEBUG)
+                   && ((debug_status == ARM_DBGDSCR_MOE_ASYNC_WATCHPOINT) || (debug_status == ARM_DBGDSCR_MOE_SYNC_WATCHPOINT))
+                   && (recover != 0) && (getCpuDatap()->cpu_user_debug != 0)) {
+                       /* If we hit a watchpoint in kernel mode, probably in a copyin/copyout which we don't want to
+                        * abort.  Turn off watchpoints and keep going; we'll turn them back on in load_and_go_user.
+                        */
+                       arm_debug_set(NULL);
+                       goto exit;
+               }
+
+               if ((type == T_PREFETCH_ABT) || (status == FSR_DEBUG)) {
+
+                       intr = ml_set_interrupts_enabled(FALSE);
+                       if (status == FSR_DEBUG) {
+                               DebuggerCall(EXC_BREAKPOINT, regs);
+                               (void) ml_set_interrupts_enabled(intr);
+                               goto exit;
+                       }
+                       panic_context(EXC_BAD_ACCESS, (void*)regs, "sleh_abort: prefetch abort in kernel mode: fault_addr=0x%x\n"
+                             "r0:   0x%08x  r1: 0x%08x  r2: 0x%08x  r3: 0x%08x\n"
+                             "r4:   0x%08x  r5: 0x%08x  r6: 0x%08x  r7: 0x%08x\n"
+                             "r8:   0x%08x  r9: 0x%08x r10: 0x%08x r11: 0x%08x\n"
+                             "r12:  0x%08x  sp: 0x%08x  lr: 0x%08x  pc: 0x%08x\n"
+                             "cpsr: 0x%08x fsr: 0x%08x far: 0x%08x\n",
+                             fault_addr,
+                             regs->r[0], regs->r[1], regs->r[2], regs->r[3],
+                             regs->r[4], regs->r[5], regs->r[6], regs->r[7],
+                             regs->r[8], regs->r[9], regs->r[10], regs->r[11],
+                             regs->r[12], regs->sp, regs->lr, regs->pc,
+                             regs->cpsr, regs->fsr, regs->far);
+
+                       (void) ml_set_interrupts_enabled(intr);
+
+               } else if (TEST_FSR_VMFAULT(status)) {
+
+#if CONFIG_DTRACE
+                       if (thread->options & TH_OPT_DTRACE) {  /* Executing under dtrace_probe? */
+                               if (dtrace_tally_fault(fault_addr)) { /* Should a fault under dtrace be ignored? */
+                                       /* Point to next instruction */
+                                       regs->pc += ((regs->cpsr & PSR_TF) && !IS_THUMB32(*((uint16_t*) (regs->pc)))) ? 2 : 4;
+                                       goto exit;
+                               } else {
+                                       intr = ml_set_interrupts_enabled(FALSE);
+                                       panic_context(EXC_BAD_ACCESS, (void *)regs, "Unexpected page fault under dtrace_probe"
+                                             "r0:   0x%08x  r1: 0x%08x  r2: 0x%08x  r3: 0x%08x\n"
+                                             "r4:   0x%08x  r5: 0x%08x  r6: 0x%08x  r7: 0x%08x\n"
+                                             "r8:   0x%08x  r9: 0x%08x r10: 0x%08x r11: 0x%08x\n"
+                                             "r12:  0x%08x  sp: 0x%08x  lr: 0x%08x  pc: 0x%08x\n"
+                                             "cpsr: 0x%08x fsr: 0x%08x far: 0x%08x\n",
+                                             regs->r[0], regs->r[1], regs->r[2], regs->r[3],
+                                             regs->r[4], regs->r[5], regs->r[6], regs->r[7],
+                                             regs->r[8], regs->r[9], regs->r[10], regs->r[11],
+                                             regs->r[12], regs->sp, regs->lr, regs->pc,
+                                             regs->cpsr, regs->fsr, regs->far);
+
+                                       (void) ml_set_interrupts_enabled(intr);
+
+                                       goto exit;
+                               }
+                       }
+#endif
+
+                       if (VM_KERNEL_ADDRESS(vaddr) || thread == THREAD_NULL)
+                               map = kernel_map;
+                       else
+                               map = thread->map;
+
+                       /* check to see if it is just a pmap ref/modify fault */
+                       result = arm_fast_fault(map->pmap, trunc_page(fault_addr), fault_type, FALSE);
+                       if (result == KERN_SUCCESS)
+                               goto exit;
+
+                       /*
+                        *  We have to "fault" the page in.
+                        */
+                       result = vm_fault(map, fault_addr,
+                                         fault_type,
+                                         FALSE /* change_wiring */, VM_KERN_MEMORY_NONE,
+                                         (map == kernel_map) ? THREAD_UNINT : THREAD_ABORTSAFE, NULL, 0);
+
+                       if (result == KERN_SUCCESS) {
+                               goto exit;
+                       } else {
+                               /*
+                                *  If we have a recover handler, invoke it now.
+                                */
+                               if (recover != 0) {
+                                       regs->pc = (register_t) (recover & ~0x1);
+                                       regs->cpsr = (regs->cpsr & ~PSR_TF) | ((recover & 0x1) << PSR_TFb);
+                                       goto exit;
+                               }
+                       }
+               } else if ((status & FSR_ALIGN_MASK) == FSR_ALIGN) {
+                       result = sleh_alignment(regs);
+                       if (result == KERN_SUCCESS) {
+                               goto exit;
+                       } else {
+                               intr = ml_set_interrupts_enabled(FALSE);
+
+                               panic_context(EXC_BAD_ACCESS, (void *)regs, "unaligned kernel data access: pc=0x%08x fault_addr=0x%x\n"
+                                     "r0:   0x%08x  r1: 0x%08x  r2: 0x%08x  r3: 0x%08x\n"
+                                     "r4:   0x%08x  r5: 0x%08x  r6: 0x%08x  r7: 0x%08x\n"
+                                     "r8:   0x%08x  r9: 0x%08x r10: 0x%08x r11: 0x%08x\n"
+                                     "r12:  0x%08x  sp: 0x%08x  lr: 0x%08x  pc: 0x%08x\n"
+                                     "cpsr: 0x%08x fsr: 0x%08x far: 0x%08x\n",
+                                     regs->pc, fault_addr,
+                                     regs->r[0], regs->r[1], regs->r[2], regs->r[3],
+                                     regs->r[4], regs->r[5], regs->r[6], regs->r[7],
+                                     regs->r[8], regs->r[9], regs->r[10], regs->r[11],
+                                     regs->r[12], regs->sp, regs->lr, regs->pc,
+                                     regs->cpsr, regs->fsr, regs->far);
+
+                               (void) ml_set_interrupts_enabled(intr);
+
+                               goto exit;
+                       }
+
+               }
+               intr = ml_set_interrupts_enabled(FALSE);
+
+               panic_context(EXC_BAD_ACCESS, (void *)regs, "kernel abort type %d: fault_type=0x%x, fault_addr=0x%x\n"
+                     "r0:   0x%08x  r1: 0x%08x  r2: 0x%08x  r3: 0x%08x\n"
+                     "r4:   0x%08x  r5: 0x%08x  r6: 0x%08x  r7: 0x%08x\n"
+                     "r8:   0x%08x  r9: 0x%08x r10: 0x%08x r11: 0x%08x\n"
+                     "r12:  0x%08x  sp: 0x%08x  lr: 0x%08x  pc: 0x%08x\n"
+                     "cpsr: 0x%08x fsr: 0x%08x far: 0x%08x\n",
+                     type, fault_type, fault_addr,
+                     regs->r[0], regs->r[1], regs->r[2], regs->r[3],
+                     regs->r[4], regs->r[5], regs->r[6], regs->r[7],
+                     regs->r[8], regs->r[9], regs->r[10], regs->r[11],
+                     regs->r[12], regs->sp, regs->lr, regs->pc,
+                     regs->cpsr, regs->fsr, regs->far);
+
+               (void) ml_set_interrupts_enabled(intr);
+
+               goto exit;
+       }
+       /* Fault in user mode */
+
+       if (TEST_FSR_VMFAULT(status)) {
+               map = thread->map;
+
+#if CONFIG_DTRACE
+               if (thread->options & TH_OPT_DTRACE) {  /* Executing under dtrace_probe? */
+                       if (dtrace_tally_fault(fault_addr)) { /* Should a user mode fault under dtrace be ignored? */
+                               if (recover) {
+                                       regs->pc = recover;
+                               } else {
+                                       intr = ml_set_interrupts_enabled(FALSE);
+
+                                       panic_context(EXC_BAD_ACCESS, (void *)regs, "copyin/out has no recovery point"
+                                             "r0:   0x%08x  r1: 0x%08x  r2: 0x%08x  r3: 0x%08x\n"
+                                             "r4:   0x%08x  r5: 0x%08x  r6: 0x%08x  r7: 0x%08x\n"
+                                             "r8:   0x%08x  r9: 0x%08x r10: 0x%08x r11: 0x%08x\n"
+                                             "r12:  0x%08x  sp: 0x%08x  lr: 0x%08x  pc: 0x%08x\n"
+                                             "cpsr: 0x%08x fsr: 0x%08x far: 0x%08x\n",
+                                             regs->r[0], regs->r[1], regs->r[2], regs->r[3],
+                                             regs->r[4], regs->r[5], regs->r[6], regs->r[7],
+                                             regs->r[8], regs->r[9], regs->r[10], regs->r[11],
+                                             regs->r[12], regs->sp, regs->lr, regs->pc,
+                                             regs->cpsr, regs->fsr, regs->far);
+
+                                       (void) ml_set_interrupts_enabled(intr);
+                               }
+                               goto exit;
+                       } else {
+                               intr = ml_set_interrupts_enabled(FALSE);
+
+                               panic_context(EXC_BAD_ACCESS, (void*)regs, "Unexpected UMW page fault under dtrace_probe"
+                                     "r0:   0x%08x  r1: 0x%08x  r2: 0x%08x  r3: 0x%08x\n"
+                                     "r4:   0x%08x  r5: 0x%08x  r6: 0x%08x  r7: 0x%08x\n"
+                                     "r8:   0x%08x  r9: 0x%08x r10: 0x%08x r11: 0x%08x\n"
+                                     "r12:  0x%08x  sp: 0x%08x  lr: 0x%08x  pc: 0x%08x\n"
+                                     "cpsr: 0x%08x fsr: 0x%08x far: 0x%08x\n",
+                                     regs->r[0], regs->r[1], regs->r[2], regs->r[3],
+                                     regs->r[4], regs->r[5], regs->r[6], regs->r[7],
+                                     regs->r[8], regs->r[9], regs->r[10], regs->r[11],
+                                     regs->r[12], regs->sp, regs->lr, regs->pc,
+                                     regs->cpsr, regs->fsr, regs->far);
+
+                               (void) ml_set_interrupts_enabled(intr);
+
+                               goto exit;
+                       }
+               }
+#endif
+
+               /* check to see if it is just a pmap ref/modify fault */
+               result = arm_fast_fault(map->pmap, trunc_page(fault_addr), fault_type, TRUE);
+               if (result != KERN_SUCCESS) {
+                       /*
+                        * We have to "fault" the page in.
+                        */
+                       result = vm_fault(map, fault_addr, fault_type,
+                                         FALSE /* change_wiring */, VM_KERN_MEMORY_NONE,
+                                         THREAD_ABORTSAFE, NULL, 0);
+               }
+               if (result == KERN_SUCCESS || result == KERN_ABORTED) {
+                       goto exception_return;
+               }
+               exc = EXC_BAD_ACCESS;
+               codes[0] = result;
+       } else if ((status & FSR_ALIGN_MASK) == FSR_ALIGN) {
+               if (sleh_alignment(regs) == KERN_SUCCESS) {
+                       goto exception_return;
+               }
+               exc = EXC_BAD_ACCESS;
+               codes[0] = EXC_ARM_DA_ALIGN;
+       } else if (status == FSR_DEBUG) {
+               exc = EXC_BREAKPOINT;
+               codes[0] = EXC_ARM_DA_DEBUG;
+       } else if ((status == FSR_SDOM) || (status == FSR_PDOM)) {
+               exc = EXC_BAD_ACCESS;
+               codes[0] = KERN_INVALID_ADDRESS;
+       } else {
+               exc = EXC_BAD_ACCESS;
+               codes[0] = KERN_FAILURE;
+       }
+
+       codes[1] = vaddr;
+       exception_triage(exc, codes, 2);
+       /* NOTREACHED */
+
+exception_return:
+       if (recover)
+               thread->recover = recover;
+       thread_exception_return();
+       /* NOTREACHED */
+
+exit:
+       if (recover)
+               thread->recover = recover;
+       return;
+}
+
+
+/*
+ *     Routine:        sleh_alignment
+ *     Function:       Second level exception handler for alignment data fault
+ */
+
+static kern_return_t
+sleh_alignment(struct arm_saved_state * regs)
+{
+       unsigned int    status;
+       unsigned int    ins;
+       unsigned int    rd_index;
+       unsigned int    base_index;
+       unsigned int    paddr;
+       void           *src;
+       unsigned int    reg_list;
+       unsigned int    pre;
+       unsigned int    up;
+       unsigned int    write_back;
+       kern_return_t   rc = KERN_SUCCESS;
+
+       getCpuDatap()->cpu_stat.unaligned_cnt++;
+
+       /* Do not try to emulate in modified execution states */
+       if (regs->cpsr & (PSR_EF | PSR_JF))
+               return KERN_NOT_SUPPORTED;
+
+       /* Disallow emulation of kernel instructions */
+       if ((regs->cpsr & PSR_MODE_MASK) != PSR_USER_MODE)
+               return KERN_NOT_SUPPORTED;
+               
+
+#define ALIGN_THRESHOLD 1024
+       if ((sleh_alignment_count++ & (ALIGN_THRESHOLD - 1)) ==
+           (ALIGN_THRESHOLD - 1))
+               kprintf("sleh_alignment: %d more alignment faults: %d total\n",
+                       ALIGN_THRESHOLD, sleh_alignment_count);
+
+       if ((trap_on_alignment_fault != 0)
+           && (sleh_alignment_count % trap_on_alignment_fault == 0))
+               return KERN_NOT_SUPPORTED;
+
+       status = regs->fsr;
+       paddr = regs->far;
+
+       if (regs->cpsr & PSR_TF) {
+                unsigned short  ins16;
+
+               /* Get aborted instruction */
+#if    __ARM_SMP__ || __ARM_USER_PROTECT__
+               if(COPYIN((user_addr_t)(regs->pc), (char *)&ins16,(vm_size_t)(sizeof(uint16_t))) != KERN_SUCCESS) {
+                       /* Failed to fetch instruction, return success to re-drive the exception */
+                       return KERN_SUCCESS;
+               }
+#else
+               ins16 = *(unsigned short *) (regs->pc);
+#endif
+
+               /*
+                * Map multi-word Thumb loads and stores to their ARM
+                * equivalents.
+                * Don't worry about single-word instructions, since those are
+                * handled in hardware.
+                */
+
+               reg_list = ins16 & 0xff;
+               if (reg_list == 0)
+                       return KERN_NOT_SUPPORTED;
+
+               if (((ins16 & THUMB_STR_1_MASK) == THUMB_LDMIA) ||
+                   ((ins16 & THUMB_STR_1_MASK) == THUMB_STMIA)) {
+                       base_index = (ins16 >> 8) & 0x7;
+                       ins = 0xE8800000 | (base_index << 16) | reg_list;
+                       if ((ins16 & THUMB_STR_1_MASK) == THUMB_LDMIA)
+                               ins |= (1 << 20);
+                       if (((ins16 & THUMB_STR_1_MASK) == THUMB_STMIA) ||
+                           !(reg_list & (1 << base_index)))
+                               ins |= (1 << 21);
+               } else if ((ins16 & THUMB_PUSH_MASK) == THUMB_POP) {
+                       unsigned int    r = (ins16 >> 8) & 1;
+                       ins = 0xE8BD0000 | (r << 15) | reg_list;
+               } else if ((ins16 & THUMB_PUSH_MASK) == THUMB_PUSH) {
+                       unsigned int    r = (ins16 >> 8) & 1;
+                       ins = 0xE92D0000 | (r << 14) | reg_list;
+               } else {
+                       return KERN_NOT_SUPPORTED;
+               }
+       } else {
+               /* Get aborted instruction */
+#if    __ARM_SMP__ || __ARM_USER_PROTECT__
+               if(COPYIN((user_addr_t)(regs->pc), (char *)&ins,(vm_size_t)(sizeof(unsigned int))) != KERN_SUCCESS) {
+                       /* Failed to fetch instruction, return success to re-drive the exception */
+                       return KERN_SUCCESS;
+               }
+#else
+               ins = *(unsigned int *) (regs->pc);
+#endif
+       }
+
+       /* Don't try to emulate unconditional instructions */
+       if ((ins & 0xF0000000) == 0xF0000000)
+               return KERN_NOT_SUPPORTED;
+
+       pre = (ins >> 24) & 1;
+       up = (ins >> 23) & 1;
+       reg_list = ins & 0xffff;
+       write_back = (ins >> 21) & 1;
+       base_index = (ins >> 16) & 0xf;
+
+       if ((ins & ARM_BLK_MASK) == ARM_STM) {  /* STM or LDM */
+               int             reg_count = 0;
+               int             waddr;
+
+               for (rd_index = 0; rd_index < 16; rd_index++) {
+                       if (reg_list & (1 << rd_index))
+                               reg_count++;
+               }
+
+               paddr = regs->r[base_index];
+
+               switch (ins & (ARM_POST_INDEXING | ARM_INCREMENT)) {
+                       /* Increment after */
+               case ARM_INCREMENT:
+                       waddr = paddr + reg_count * 4;
+                       break;
+
+                       /* Increment before */
+               case ARM_POST_INDEXING | ARM_INCREMENT:
+                       waddr = paddr + reg_count * 4;
+                       paddr += 4;
+                       break;
+
+                       /* Decrement after */
+               case 0:
+                       waddr = paddr - reg_count * 4;
+                       paddr = waddr + 4;
+                       break;
+
+                       /* Decrement before */
+               case ARM_POST_INDEXING:
+                       waddr = paddr - reg_count * 4;
+                       paddr = waddr;
+                       break;
+
+               default:
+                       waddr = 0;
+               }
+
+               for (rd_index = 0; rd_index < 16; rd_index++) {
+                       if (reg_list & (1 << rd_index)) {
+                               src = &regs->r[rd_index];
+
+                               if ((ins & (1 << 20)) == 0)     /* STM */
+                                       rc = COPYOUT(src, paddr, 4);
+                               else    /* LDM */
+                                       rc = COPYIN(paddr, src, 4);
+
+                               if (rc != KERN_SUCCESS)
+                                       break;
+
+                               paddr += 4;
+                       }
+               }
+
+               paddr = waddr;
+       } else {
+               rc = 1;
+       }
+
+       if (rc == KERN_SUCCESS) {
+               if (regs->cpsr & PSR_TF)
+                       regs->pc += 2;
+               else
+                       regs->pc += 4;
+
+               if (write_back)
+                       regs->r[base_index] = paddr;
+       }
+       return (rc);
+}
+
+
+#ifndef        NO_KDEBUG
+/* XXX quell warnings */
+void            syscall_trace(struct arm_saved_state * regs);
+void            syscall_trace_exit(unsigned int, unsigned int);
+void            mach_syscall_trace(struct arm_saved_state * regs, unsigned int call_number);
+void            mach_syscall_trace_exit(unsigned int retval, unsigned int call_number);
+void            interrupt_trace(struct arm_saved_state * regs);
+void            interrupt_trace_exit(void);
+
+/* called from the fleh_swi handler, if TRACE_SYSCALL is enabled */
+void
+syscall_trace(
+             struct arm_saved_state * regs)
+{
+       kprintf("syscall: %d\n", regs->r[12]);
+}
+
+void
+syscall_trace_exit(
+                  unsigned int r0,
+                  unsigned int r1)
+{
+       kprintf("syscall exit: 0x%x 0x%x\n", r0, r1);
+}
+
+void
+mach_syscall_trace(
+                  struct arm_saved_state * regs,
+                  unsigned int call_number)
+{
+       int             i, argc;
+       int             kdarg[3] = {0, 0, 0};
+
+       argc = mach_trap_table[call_number].mach_trap_arg_count;
+
+       if (argc > 3)
+               argc = 3;
+
+       for (i = 0; i < argc; i++)
+               kdarg[i] = (int) regs->r[i];
+
+       KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
+               MACHDBG_CODE(DBG_MACH_EXCP_SC, (call_number)) | DBG_FUNC_START,
+               kdarg[0], kdarg[1], kdarg[2], 0, 0);
+
+}
+
+void
+mach_syscall_trace_exit(
+                       unsigned int retval,
+                       unsigned int call_number)
+{
+       KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
+               MACHDBG_CODE(DBG_MACH_EXCP_SC, (call_number)) | DBG_FUNC_END,
+               retval, 0, 0, 0, 0);
+}
+
+void
+interrupt_trace(
+               struct arm_saved_state * regs)
+{
+#define        UMODE(rp)       (((rp)->cpsr & PSR_MODE_MASK) == PSR_USER_MODE)
+
+       KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
+               MACHDBG_CODE(DBG_MACH_EXCP_INTR, 0) | DBG_FUNC_START,
+               0, UMODE(regs) ? regs->pc : VM_KERNEL_UNSLIDE(regs->pc),
+               UMODE(regs), 0, 0);
+}
+
+void
+interrupt_trace_exit(
+                    void)
+{
+       KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
+               MACHDBG_CODE(DBG_MACH_EXCP_INTR, 0) | DBG_FUNC_END,
+               0, 0, 0, 0, 0);
+}
+#endif
+
+/* XXX quell warnings */
+void interrupt_stats(void);
+
+/* This is called from locore.s directly. We only update per-processor interrupt counters in this function */
+void
+interrupt_stats(void)
+{
+       SCHED_STATS_INTERRUPT(current_processor());
+}
+
+static void 
+panic_with_thread_kernel_state(const char *msg, struct arm_saved_state *regs)
+{
+               panic_context(0, (void*)regs, "%s (saved state:%p)\n"
+                             "r0:   0x%08x  r1: 0x%08x  r2: 0x%08x  r3: 0x%08x\n"
+                             "r4:   0x%08x  r5: 0x%08x  r6: 0x%08x  r7: 0x%08x\n"
+                             "r8:   0x%08x  r9: 0x%08x r10: 0x%08x r11: 0x%08x\n"
+                             "r12:  0x%08x  sp: 0x%08x  lr: 0x%08x  pc: 0x%08x\n"
+                             "cpsr: 0x%08x fsr: 0x%08x far: 0x%08x\n",
+                                 msg, regs,
+                             regs->r[0], regs->r[1], regs->r[2], regs->r[3],
+                             regs->r[4], regs->r[5], regs->r[6], regs->r[7],
+                             regs->r[8], regs->r[9], regs->r[10], regs->r[11],
+                             regs->r[12], regs->sp, regs->lr, regs->pc,
+                             regs->cpsr, regs->fsr, regs->far);
+
+}