Patch State
Viewing Patch 4447/1
Not logged in Login...
Please note: this is not a patch review system;
please submit all patches to the linux-arm-kernel mailing
list for community review prior to submission here.
This patch system only deals with core ARM patches, not board support.

Go to patch:
Search summaries:
Add new patch Download this patch Help
Updated - Please read
(November 02, 2019)
ID Version From Date Status
4447/1 2.6.21.4 Quentin Barnes 11 Jun 2007 23:20:10 UTC Discarded
Summary:  Kprobes for ARM
Notes:  - - - Note 1 submitted by Quentin Barnes on 11 Jun 2007 23:20:10 (UTC) - - -
This change adds Kprobes for ARM. This is a full implementation of
Kprobes including Jprobes and Kretprobes support.

The ARM architecture has a limitation previously unencountered with
any kprobes' supporting architecture to date. ARM has neither a
Next-PC register nor a single-stepping state, so there is no way to
regain control of any instruction that modifies the PC. Hence, this
implementation of Kprobes for ARM is a new model. Even though it is
a new model, it drops in without modifying existing kprobes kernel
code since it is completely compatible with all existing kernel
interfaces. (This new model is not inherently limited to ARM, but
could be reimplemented other architectures as well.)

To get around the limitation of no Next-PC and no single-stepping,
all kprobe'd instructions are split into three camps: simulation,
emulation, and rejected. "Simulated" instructions are what
instructions are called that their behavior is reproduced by
straight C code. These are ones that modify the PC. "Emulated"
instructions are ones that are executed directly in the instruction
slot to reproduce their behavior. "Rejected" instructions are ones
that could be simulated, but work hasn't been put into simulating
them. These instructions should be very rare, if not unencountered,
in the kernel. If ever needed, code could be added to simulate
them.

This implementation for ARM also does not follow the usual kprobes
double exception model. The old model is where the initial
kprobes breakpoint runs calling kprobe_handler(), it returns from
exception to execute the instruction in its original context, then
immediately re-enters after a second breakpoint (or single-stepping
exception) into post_kprobe_handler(). The new model here only
executes one kprobes exception ever, so no post_kprobe_handler()
phase. All side-effects from the kprobe'd instruction are resolved
before returning from the initial exception. As a result, all
instructions are _always_ effectively boosted regardless of the type
of instruction, and even regardless of whether or not there is a
post-handler.

Signed-off-by: Quentin Barnes <(address hidden)>
Signed-off-by: Abhishek Sagar <(address hidden)>
- - - Note 2 submitted by Russell King on 21 Jan 2008 13:03:12 (UTC) - - -
Moved to Discarded.

Nicolas Pitre is now maintaining a git tree for this, so this patch no longer
needs to be in 'incoming'.
diff -uprN linux-2.6.21.4/arch/arm/Kconfig linux/arch/arm/Kconfig
--- linux-2.6.21.4/arch/arm/Kconfig	2007-05-07 18:16:40.000000000 -0500
+++ linux/arch/arm/Kconfig	2007-06-09 18:07:06.000000000 -0500
@@ -1001,8 +1001,15 @@ endmenu
 
 source "fs/Kconfig"
 
+menu "Instrumentation Support"
+	depends on EXPERIMENTAL
+
 source "arch/arm/oprofile/Kconfig"
 
+source "arch/arm/kernel/Kconfig"
+
+endmenu
+
 source "arch/arm/Kconfig.debug"
 
 source "security/Kconfig"
diff -uprN linux-2.6.21.4/arch/arm/kernel/entry-armv.S linux/arch/arm/kernel/entry-armv.S
--- linux-2.6.21.4/arch/arm/kernel/entry-armv.S	2007-05-07 18:16:40.000000000 -0500
+++ linux/arch/arm/kernel/entry-armv.S	2007-06-11 17:07:20.000000000 -0500
@@ -11,8 +11,8 @@
  *
  *  Low-level vector interface routines
  *
- *  Note:  there is a StrongARM bug in the STMIA rn, {regs}^ instruction that causes
- *  it to save wrong values...  Be aware!
+ *  Note:  there is a StrongARM bug in the STMIA rn, {regs}^ instruction
+ *  that causes it to save wrong values...  Be aware!
  */
 
 #include <asm/memory.h>
@@ -58,6 +58,12 @@
 
 	.endm
 
+#ifdef CONFIG_KPROBES
+	.section	.kprobes.text,"ax",%progbits
+#else
+	.text
+#endif
+
 /*
  * Invalid mode handlers
  */
@@ -242,7 +248,16 @@ svc_preempt:
 
 	.align	5
 __und_svc:
+#ifdef CONFIG_KPROBES
+	@ Give kprobe'd instruction stack space to finish in case of STM.
+	sub	sp, sp, #64
+#endif
 	svc_entry
+#ifdef CONFIG_KPROBES
+	@ Adjust saved SP back to its original value.
+	add	r0, r0, #64
+	str	r0, [sp, #S_SP]
+#endif
 
 	@
 	@ call emulation code, which returns using r9 if it has emulated
@@ -548,7 +563,7 @@ do_fpe:
 	.data
 ENTRY(fp_enter)
 	.word	no_fp
-	.text
+	.previous
 
 no_fp:	mov	pc, lr
 
diff -uprN linux-2.6.21.4/arch/arm/kernel/Kconfig linux/arch/arm/kernel/Kconfig
--- linux-2.6.21.4/arch/arm/kernel/Kconfig	1969-12-31 18:00:00.000000000 -0600
+++ linux/arch/arm/kernel/Kconfig	2007-06-09 18:07:06.000000000 -0500
@@ -0,0 +1,13 @@
+menu "Kernel Probes(Kprobes)"
+        depends on KALLSYMS && EXPERIMENTAL && MODULES
+
+config KPROBES
+	bool "Kernel Probes (EXPERIMENTAL)"
+	help
+	  Kprobes allows you to trap at almost any kernel address and
+	  execute a callback function.  register_kprobe() establishes
+	  a probepoint and specifies the callback.  Kprobes is useful
+	  for kernel debugging, non-intrusive instrumentation and testing.
+	  If in doubt, say "N".
+
+endmenu
diff -uprN linux-2.6.21.4/arch/arm/kernel/kprobes.c linux/arch/arm/kernel/kprobes.c
--- linux-2.6.21.4/arch/arm/kernel/kprobes.c	1969-12-31 18:00:00.000000000 -0600
+++ linux/arch/arm/kernel/kprobes.c	2007-06-11 14:19:40.000000000 -0500
@@ -0,0 +1,534 @@
+/* arch/arm/kernel/kprobes.c
+ *
+ * Kprobes on ARM
+ *
+ * Abhishek Sagar <sagar.abhishek@gmail.com>
+ *
+ * Copyright (C) 2006, 2007 Motorola Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/kprobes.h>
+#include <linux/module.h>
+#include <asm/kdebug.h>
+#include <asm/traps.h>
+#include <asm/signal.h>
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
+#include <asm/uaccess.h>
+
+#define flush_insns(addr, cnt) 				\
+	flush_icache_range((unsigned long)(addr),	\
+			   (unsigned long)(addr) +	\
+			   sizeof(kprobe_opcode_t) * (cnt))
+
+/* Used as a marker in ARM_pc to note when we're in a jprobe. */
+#define JPROBE_MAGIC_ADDR		0xffffffff
+
+void jprobe_return_end(void);
+void kretprobe_trampoline(void);
+
+DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL;
+DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
+
+int kprobes_str_pc_offset;
+
+
+int __kprobes arch_prepare_kprobe(struct kprobe *p)
+{
+	kprobe_opcode_t insn;
+	kprobe_opcode_t tmp_insn[MAX_INSN_SIZE];
+	int	is;
+
+	if ((unsigned long)p->addr & 0x3)
+		return -EINVAL;
+
+	insn = *p->addr;
+
+	p->opcode = insn;
+	p->ainsn.insn = tmp_insn;
+
+	switch (kprobe_decode_insn(insn, &p->ainsn)) {
+	case INSN_REJECTED:	/* not supported */
+		return -EINVAL;
+
+	case INSN_GOOD:		/* instruction uses slot */
+		p->ainsn.insn = get_insn_slot();
+		if (!p->ainsn.insn)
+			return -ENOMEM;
+
+		for (is = 0; is < MAX_INSN_SIZE; ++is)
+			p->ainsn.insn[is] = tmp_insn[is];
+
+		flush_insns(&p->ainsn.insn, MAX_INSN_SIZE);
+		break;
+
+	case INSN_GOOD_NO_SLOT:	/* instruction doesn't need insn slot */
+		p->ainsn.insn = 0;
+		break;
+	}
+
+	return 0;
+}
+
+
+void __kprobes arch_arm_kprobe(struct kprobe *p)
+{
+	*p->addr = KPROBE_BREAKPOINT_INSTRUCTION;
+	flush_insns(p->addr, 1);
+}
+
+
+void __kprobes arch_disarm_kprobe(struct kprobe *p)
+{
+	*p->addr = p->opcode;
+	flush_insns(p->addr, 1);
+}
+
+
+void __kprobes arch_remove_kprobe(struct kprobe *p)
+{
+	if (p->ainsn.insn) {
+		mutex_lock(&kprobe_mutex);
+		free_insn_slot(p->ainsn.insn, 0);
+		mutex_unlock(&kprobe_mutex);
+		p->ainsn.insn = 0;
+	}
+}
+
+static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb)
+{
+	kcb->prev_kprobe.kp = kprobe_running();
+	kcb->prev_kprobe.status = kcb->kprobe_status;
+}
+
+
+static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb)
+{
+	__get_cpu_var(current_kprobe) = kcb->prev_kprobe.kp;
+	kcb->kprobe_status = kcb->prev_kprobe.status;
+}
+
+
+static void __kprobes set_current_kprobe(struct kprobe *p)
+{
+	__get_cpu_var(current_kprobe) = p;
+}
+
+
+/* Called with kretprobe_lock held. */
+void __kprobes arch_prepare_kretprobe(struct kretprobe *rp,
+				      struct pt_regs *regs)
+{
+	struct kretprobe_instance *ri;
+
+	if ((ri = get_free_rp_inst(rp)) != NULL) {
+		ri->rp = rp;
+		ri->task = current;
+		ri->ret_addr = (kprobe_opcode_t *)regs->ARM_lr;
+
+		/* Replace the return addr with trampoline addr. */
+		regs->ARM_lr = (unsigned long)&kretprobe_trampoline;
+		add_rp_inst(ri);
+	} else {
+		rp->nmissed++;
+	}
+}
+
+
+static void __kprobes singlestep(struct kprobe *p, struct pt_regs *regs,
+					struct kprobe_ctlblk *kcb)
+{
+	regs->ARM_pc += 4;
+
+	p->ainsn.insn_handler(p, regs);
+
+	if ((kcb->kprobe_status != KPROBE_REENTER) && p->post_handler) {
+		kcb->kprobe_status = KPROBE_HIT_SSDONE;
+		p->post_handler(p, regs, 0);
+	}
+
+	if (kcb->kprobe_status == KPROBE_REENTER) {
+		restore_previous_kprobe(kcb);
+	} else {
+		reset_current_kprobe();
+	}
+}
+
+
+/* IRQs were automatically disabled when executing the kprobe
+ * instruction.  IRQs must remain disabled from that point all the
+ * way until processing this kprobe is complete.  The current kprobes
+ * implementation cannot process more than one nested level of kprobe,
+ * and that level is reserved for user kprobe handlers, so we can't
+ * risk encountering a new kprobe in an interrupt handler.
+ */
+
+int __kprobes kprobe_handler(struct pt_regs *regs)
+{
+	struct kprobe *p, *cur;
+	struct kprobe_ctlblk *kcb;
+	kprobe_opcode_t	*addr = (kprobe_opcode_t *)regs->ARM_pc;
+
+	kcb = get_kprobe_ctlblk();
+	cur = kprobe_running();
+	p   = get_kprobe(addr);
+
+	if (p) {
+		if (cur) {  /* Kprobe is pending, so we're recursing. */
+			switch (kcb->kprobe_status) {
+			case KPROBE_HIT_ACTIVE:
+			case KPROBE_HIT_SSDONE:
+				/* A pre- or post-handler probe got us here. */
+				kprobes_inc_nmissed_count(p);
+				save_previous_kprobe(kcb);
+				set_current_kprobe(p);
+				kcb->kprobe_status = KPROBE_REENTER;
+				singlestep(p, regs, kcb);
+				break;
+			default:
+				/* impossible cases */
+				BUG();
+			}
+		} else {
+			set_current_kprobe(p);
+			kcb->kprobe_status = KPROBE_HIT_ACTIVE;
+
+			/* If we have no pre-handler or it returned 0, we
+			 * continue with normal processing.  If we have a
+			 * pre-handler and it returned non-zero, it prepped
+			 * for calling the break_handler below on re-entry,
+			 * so get out doing nothing more here.
+			 */
+
+			if (!p->pre_handler || !p->pre_handler(p, regs)) {
+				kcb->kprobe_status = KPROBE_HIT_SS;
+				singlestep(p, regs, kcb);
+			}
+		}
+	} else {
+		/* Two ways we could have gotten here.  Either the probe
+		 * was removed and a race is in progress or we hit a
+		 * jprobe.
+		 *
+		 * If it's a race, nothing we can do about it.  Restart
+		 * the instruction.  By the time we can restart, the
+		 * real instruction will be there.  If we're in a jprobe
+		 * call its break handler.
+		 */
+
+		if (cur) {
+			if ( (addr == (kprobe_opcode_t *)JPROBE_MAGIC_ADDR) &&
+			     cur->break_handler &&
+			     cur->break_handler(cur, regs) ) {
+				kcb->kprobe_status = KPROBE_HIT_SS;
+				singlestep(cur, regs, kcb);
+			} else {
+				reset_current_kprobe();
+			}
+		}
+	}
+
+	return 1;
+}
+
+
+static int __kprobes kprobe_fault_handler(struct pt_regs *regs,
+							unsigned int fsr)
+{
+	struct kprobe *cur = kprobe_running();
+	struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+
+	switch (kcb->kprobe_status) {
+	case KPROBE_HIT_SS:
+	case KPROBE_REENTER:
+		/*
+		 * We are here because the instruction being single
+		 * stepped caused a page fault. We reset the current
+		 * kprobe and the PC to point back to the probe address
+		 * and allow the page fault handler to continue as a
+		 * normal page fault.
+		 */
+		regs->ARM_pc = (long)cur->addr;
+		if (kcb->kprobe_status == KPROBE_REENTER) {
+			restore_previous_kprobe(kcb);
+		} else {
+			reset_current_kprobe();
+		}
+		break;
+
+	case KPROBE_HIT_ACTIVE:
+	case KPROBE_HIT_SSDONE:
+		/*
+		 * We increment the nmissed count for accounting,
+		 * we can also use npre/npostfault count for accounting
+		 * these specific fault cases.
+		 */
+		kprobes_inc_nmissed_count(cur);
+
+		/*
+		 * We come here because instructions in the pre/post
+		 * handler caused the page_fault, this could happen
+		 * if handler tries to access user space by
+		 * copy_from_user(), get_user() etc. Let the
+		 * user-specified handler try to fix it first.
+		 */
+		if (cur->fault_handler && cur->fault_handler(cur, regs, fsr))
+			return 1;
+
+		/*
+		 * In case the user-specified fault handler returned
+		 * zero, try to fix up.
+		 */
+
+		if (fixup_exception(regs))
+			return 1;
+
+		/*
+		 * fixup_exception() could not handle it,
+		 * Let do_page_fault() fix it.
+		 */
+		break;
+
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+
+/*
+ * Wrapper routine to for handling exceptions.
+ */
+int __kprobes kprobe_exceptions_notify(struct notifier_block *self,
+				       unsigned long val, void *data)
+{
+	struct die_args *args = (struct die_args *)data;
+	struct pt_regs *regs = args->regs;
+	int ret = NOTIFY_DONE;
+
+	/* This function is in a very critical path.  If in user
+	 * mode, get out as quick as we can. */
+	if (!regs || user_mode(regs))
+		return ret;
+
+	switch (val) {
+	case DIE_TRANS_FAULT:
+	case DIE_PAGE_FAULT:
+		/* To be potentially processing a kprobe fault and to
+		 * trust the result from kprobe_running(), we have
+		 * be non-preemptible. */
+		if (!preemptible() &&
+		     kprobe_running() &&
+		     kprobe_fault_handler(regs, args->fsr)) {
+			ret = NOTIFY_STOP;
+		}
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+
+/*
+ * When a retprobed function returns, trampoline_handler() is called,
+ * calling the kretprobe's handler. We contruct a struct pt_regs to
+ * give a view of registers r0-r11 to the user return-handler.
+ */
+void __attribute__((naked)) __kprobes kretprobe_trampoline_holder(void)
+{
+	__asm__ volatile (
+		".global kretprobe_trampoline    \n\t"
+		"kretprobe_trampoline:           \n\t"
+		"stmdb  sp!, {r0-r11}            \n\t"
+		"mov    r0, sp                   \n\t"
+		"bl     trampoline_handler       \n\t"
+		"mov    lr, r0                   \n\t"
+		"ldmia  sp!, {r0-r11}            \n\t"
+		"mov    pc, lr                   \n\t"
+		:
+		:
+		:"memory");
+}
+
+
+/*
+ * Called from kretprobe_trampoline_holder
+ */
+void * __kprobes trampoline_handler(struct pt_regs *regs)
+{
+	struct kretprobe_instance *ri = NULL;
+	struct hlist_head *head, empty_rp;
+	struct hlist_node *node, *tmp;
+	unsigned long flags, orig_ret_address = 0;
+	unsigned long trampoline_address =(unsigned long)&kretprobe_trampoline;
+
+	INIT_HLIST_HEAD(&empty_rp);
+	spin_lock_irqsave(&kretprobe_lock, flags);
+	head = kretprobe_inst_table_head(current);
+
+	/*
+	 * It is possible to have multiple instances associated with a given
+	 * task either because multiple functions in the call path have
+	 * a return probe installed on them, and/or more then one return
+	 * return probe was registered for a target function.
+	 *
+	 * We can handle this because:
+	 *     - instances are always inserted at the head of the list
+	 *     - when multiple return probes are registered for the same
+	 *       function, the first instance's ret_addr will point to the
+	 *       real return address, and all the rest will point to
+	 *       kretprobe_trampoline
+	 */
+	hlist_for_each_entry_safe(ri, node, tmp, head, hlist) {
+		if (ri->task != current)
+			/* another task is sharing our hash bucket */
+			continue;
+
+		if (ri->rp && ri->rp->handler) {
+			__get_cpu_var(current_kprobe) = &ri->rp->kp;
+			get_kprobe_ctlblk()->kprobe_status = KPROBE_HIT_ACTIVE;
+			ri->rp->handler(ri, regs);
+			__get_cpu_var(current_kprobe) = NULL;
+		}
+
+		orig_ret_address = (unsigned long)ri->ret_addr;
+		recycle_rp_inst(ri, &empty_rp);
+
+		if (orig_ret_address != trampoline_address)
+			/*
+			 * This is the real return address. Any other
+			 * instances associated with this task are for
+			 * other calls deeper on the call stack
+			 */
+			break;
+	}
+
+	BUG_ON(!orig_ret_address || (orig_ret_address == trampoline_address));
+
+	spin_unlock_irqrestore(&kretprobe_lock, flags);
+
+	hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) {
+		hlist_del(&ri->hlist);
+		kfree(ri);
+	}
+
+	return (void *)orig_ret_address;
+}
+
+
+int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
+{
+	struct jprobe *jp = container_of(p, struct jprobe, kp);
+	struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+	long sp_addr = regs->ARM_sp;
+
+	kcb->jprobe_saved_regs = *regs;
+	memcpy(kcb->jprobes_stack, (void *)sp_addr, MIN_STACK_SIZE(sp_addr));
+	regs->ARM_pc = (long)jp->entry;
+	regs->ARM_cpsr |= PSR_I_BIT;
+	preempt_disable();
+	return 1;
+}
+
+
+#define str(s) #s
+#define xstr(s) str(s)
+
+void __kprobes jprobe_return(void)
+{
+	struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+
+	__asm__ volatile(
+		/* Setup an empty pt_regs. Fill SP and PC fields as
+		 * they're needed by longjmp_break_handler.
+		 */
+		"sub    sp, %0, %1		\n\t"
+		"ldr    r0, =" xstr(JPROBE_MAGIC_ADDR) "\n\t"
+		"str    r0, [sp, %3]		\n\t"
+		"str    %0, [sp, %2]		\n\t"
+
+		"mov    r0, sp			\n\t"
+		"bl     kprobe_handler		\n\t"
+
+		/* Return to the context saved by setjmp_pre_handler
+		 * and restored by longjmp_break_handler. */
+		"ldr	r0, [sp, %4]		\n\t"
+		"msr	cpsr_cxsf, r0		\n\t"
+		"ldmia	sp, {r0-pc}		\n\t"
+		:
+		: "r" (kcb->jprobe_saved_regs.ARM_sp),
+		  "I"(sizeof(struct pt_regs)),
+		  "J"(offsetof(struct pt_regs, ARM_sp)),
+		  "J"(offsetof(struct pt_regs, ARM_pc)),
+		  "J"(offsetof(struct pt_regs, ARM_cpsr))
+		: "memory", "cc");
+}
+
+int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
+{
+	struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+	long stack_addr = kcb->jprobe_saved_regs.ARM_sp;
+	long orig_sp = regs->ARM_sp;
+	struct jprobe *jp = container_of(p, struct jprobe, kp);
+
+	if (orig_sp != stack_addr) {
+		struct pt_regs *saved_regs =
+			(struct pt_regs *)kcb->jprobe_saved_regs.ARM_sp;
+		printk("current sp %lx does not match saved sp %lx\n",
+		       orig_sp, stack_addr);
+		printk("Saved registers for jprobe %p\n", jp);
+		show_regs(saved_regs);
+		printk("Current registers\n");
+		show_regs(regs);
+		BUG();
+	}
+
+	*regs = kcb->jprobe_saved_regs;
+	memcpy((void *)stack_addr, kcb->jprobes_stack,
+	       MIN_STACK_SIZE(stack_addr));
+	preempt_enable_no_resched();
+
+	return 1;
+}
+
+
+/*
+ * For STR and STM instructions, an ARM core may choose to use either
+ * a +8 or a +12 displacement from the current instruction's address.
+ * Whichever value is chosen for a given core, it must be the same for
+ * both instructions and may not change.  This function measures it.
+ */
+
+static int __init find_str_pc_offset(void)
+{
+	int addr;
+	int scratch;
+	int ret;
+
+	__asm__("sub	%[ret], pc, #4		\n\t"
+		"str	pc, %[addr]		\n\t"
+		"ldr	%[scr], %[addr]		\n\t"
+		"sub	%[ret], %[scr], %[ret]	\n\t"
+		: [ret] "=r" (ret), [scr] "=r" (scratch), [addr] "+m" (addr) );
+
+	return ret;
+}
+
+int __init arch_init_kprobes()
+{
+	kprobes_str_pc_offset = find_str_pc_offset();
+	return 0;
+}
diff -uprN linux-2.6.21.4/arch/arm/kernel/kprobes-decode.c linux/arch/arm/kernel/kprobes-decode.c
--- linux-2.6.21.4/arch/arm/kernel/kprobes-decode.c	1969-12-31 18:00:00.000000000 -0600
+++ linux/arch/arm/kernel/kprobes-decode.c	2007-06-09 18:07:06.000000000 -0500
@@ -0,0 +1,1599 @@
+/* arch/arm/kernel/kprobes-decode.c
+ *
+ * Copyright (C) 2006, 2007 Motorola Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+/* We do not have hardware single-stepping on ARM, This
+ * effort is further complicated by the ARM not having a
+ * "next PC" register.  Instructions that change the PC
+ * can't be safely single-stepped in a MP environment, so
+ * we have a lot of work to do:
+ *
+ * In the prepare phase:
+ *   *) If it is an instruction that does anything
+ *      with the CPU mode, we reject it for a kprobe.
+ *      (This is out of laziness rather than need.  The
+ *      instructions could be simulated.)
+ *
+ *   *) Otherwise, decode the instruction rewriting its
+ *      registers to take fixed, ordered registers and
+ *      setting a handler for it to run the instruction.
+ *
+ * In the execution phase by an instruction's handler:
+ *
+ *   *) If the PC is written to by the instruction, the
+ *      instruction must be fully simulated in software.
+ *      If it is a conditional instruction, the handler
+ *      will use insn[0] to copy its condition code to
+ *	set r0 to 1 and insn[1] to "mov pc, lr" to return.
+ *
+ *   *) Otherwise, a modified form of the instruction can
+ *      directly executed.  Its handler calls the
+ *      instruction in insn[0].  In insn[1] is a
+ *      "mov pc, lr" to return.
+ *
+ *      Before calling, load up the reordered registers
+ *      from the original instruction's registers.  If one
+ *      of the original input registers is the PC, compute
+ *      and adjust the appropriate input register.
+ *
+ *	After call completes, copy the output registers to
+ *      the original instruction's original registers.
+ *
+ * We don't use a real breakpoint instruction since that
+ * would have us in the kernel go from SVC mode to SVC
+ * mode losing the link register.  Instead we use an
+ * undefined instruction.  To simply processing, the
+ * undefined instruction used for kprobes must be reserved
+ * exclusively for kprobes use.
+ */
+
+/* TODO: ifdef instruction decoding based on architecture.
+ */
+
+#include <linux/kernel.h>
+#include <linux/kprobes.h>
+#include <asm/kdebug.h>
+
+#define array_cnt(x) (sizeof((x))/sizeof(*(x)))
+
+#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
+
+#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25)
+
+#define PSR_fs	(PSR_f|PSR_s)
+
+#define SET_R0_TRUE_INSTRUCTION	0xe3a00001	/* mov	r0, #1 */
+#define	truecc_insn(insn)	( ((insn) & 0xf0000000) | \
+				  (SET_R0_TRUE_INSTRUCTION & 0x0fffffff) )
+
+typedef long (insn_0arg_fn_t)(void);
+typedef long (insn_1arg_fn_t)(long);
+typedef long (insn_2arg_fn_t)(long,long);
+typedef long (insn_3arg_fn_t)(long,long,long);
+typedef long (insn_4arg_fn_t)(long,long,long,long);
+typedef long long (insn_llret_0arg_fn_t)(void);
+typedef long long (insn_llret_3arg_fn_t)(long,long,long);
+typedef long long (insn_llret_4arg_fn_t)(long,long,long,long);
+
+typedef union {
+	long long	dr;
+#ifdef __LITTLE_ENDIAN
+	struct { long	r0, r1; };
+#else
+	struct { long	r1, r0; };
+#endif
+} reg_pair;
+
+extern int kprobes_str_pc_offset;
+
+
+/* The insnslot_?arg_r[w]flags() functions below are to keep the
+ * msr -> *fn -> mrs instruction sequences indivisible so that
+ * the state of the CPSR flags aren't inadvertently modified
+ * just before or just after the call.
+ */
+
+static inline long __kprobes insnslot_0arg_rflags(long cpsr, insn_0arg_fn_t *fn)
+{
+	register long ret asm("r0");
+
+	__asm__ volatile(
+		"msr	cpsr_fs, %[cpsr]	\n\t"
+		"mov	lr, pc			\n\t"
+		"mov	pc, %[fn]		\n\t"
+		: "=r" (ret)
+		: [cpsr] "r" (cpsr), [fn] "r" (fn)
+		: "lr", "cc"
+	);
+	return ret;
+}
+
+static inline long long __kprobes insnslot_llret_0arg_rflags(long cpsr,
+						insn_llret_0arg_fn_t *fn)
+{
+	register long ret0 asm("r0");
+	register long ret1 asm("r1");
+	reg_pair fnr;
+
+	__asm__ volatile(
+		"msr	cpsr_fs, %[cpsr]	\n\t"
+		"mov	lr, pc			\n\t"
+		"mov	pc, %[fn]		\n\t"
+		: "=r" (ret0), "=r" (ret1)
+		: [cpsr] "r" (cpsr), [fn] "r" (fn)
+		: "lr", "cc"
+	);
+	fnr.r0 = ret0;
+	fnr.r1 = ret1;
+	return fnr.dr;
+}
+
+
+static inline long __kprobes insnslot_1arg_rflags(long r0, long cpsr,
+						insn_1arg_fn_t *fn)
+{
+	register long rr0 asm("r0") = r0;
+	register long ret asm("r0");
+
+	__asm__ volatile(
+		"msr	cpsr_fs, %[cpsr]	\n\t"
+		"mov	lr, pc			\n\t"
+		"mov	pc, %[fn]		\n\t"
+		: "=r" (ret)
+		: "0" (rr0), [cpsr] "r" (cpsr), [fn] "r" (fn)
+		: "lr", "cc"
+	);
+	return ret;
+}
+
+
+static inline long __kprobes insnslot_2arg_rflags(long r0, long r1, long cpsr,
+						insn_2arg_fn_t *fn)
+{
+	register long rr0 asm("r0") = r0;
+	register long rr1 asm("r1") = r1;
+	register long ret asm("r0");
+
+	__asm__ volatile(
+		"msr	cpsr_fs, %[cpsr]	\n\t"
+		"mov	lr, pc			\n\t"
+		"mov	pc, %[fn]		\n\t"
+		: "=r" (ret)
+		: "0" (rr0), "r" (rr1),
+		  [cpsr] "r" (cpsr), [fn] "r" (fn)
+		: "lr", "cc"
+	);
+	return ret;
+}
+
+
+static inline long __kprobes insnslot_3arg_rflags(long r0, long r1, long r2,
+						long cpsr, insn_3arg_fn_t *fn)
+{
+	register long rr0 asm("r0") = r0;
+	register long rr1 asm("r1") = r1;
+	register long rr2 asm("r2") = r2;
+	register long ret asm("r0");
+
+	__asm__ volatile(
+		"msr	cpsr_fs, %[cpsr]	\n\t"
+		"mov	lr, pc			\n\t"
+		"mov	pc, %[fn]		\n\t"
+		: "=r" (ret)
+		: "0" (rr0), "r" (rr1), "r" (rr2),
+		  [cpsr] "r" (cpsr), [fn] "r" (fn)
+		: "lr", "cc"
+	);
+	return ret;
+}
+
+
+static inline long long __kprobes insnslot_llret_3arg_rflags(long r0, long r1,
+						long r2, long cpsr,
+						insn_llret_3arg_fn_t *fn)
+{
+	register long rr0 asm("r0") = r0;
+	register long rr1 asm("r1") = r1;
+	register long rr2 asm("r2") = r2;
+	register long ret0 asm("r0");
+	register long ret1 asm("r1");
+	reg_pair fnr;
+
+	__asm__ volatile(
+		"msr	cpsr_fs, %[cpsr]	\n\t"
+		"mov	lr, pc			\n\t"
+		"mov	pc, %[fn]		\n\t"
+		: "=r" (ret0), "=r" (ret1)
+		: "0" (rr0), "r" (rr1), "r" (rr2),
+		  [cpsr] "r" (cpsr), [fn] "r" (fn)
+		: "lr", "cc"
+	);
+	fnr.r0 = ret0;
+	fnr.r1 = ret1;
+	return fnr.dr;
+}
+
+
+static inline long __kprobes insnslot_4arg_rflags(long r0, long r1, long r2,
+							long r3, long cpsr,
+							insn_4arg_fn_t *fn)
+{
+	register long rr0 asm("r0") = r0;
+	register long rr1 asm("r1") = r1;
+	register long rr2 asm("r2") = r2;
+	register long rr3 asm("r3") = r3;
+	register long ret asm("r0");
+
+	__asm__ volatile(
+		"msr	cpsr_fs, %[cpsr]	\n\t"
+		"mov	lr, pc			\n\t"
+		"mov	pc, %[fn]		\n\t"
+		: "=r" (ret)
+		: "0" (rr0), "r" (rr1), "r" (rr2), "r" (rr3),
+		  [cpsr] "r" (cpsr), [fn] "r" (fn)
+		: "lr", "cc"
+	);
+	return ret;
+}
+
+
+static inline long __kprobes insnslot_1arg_rwflags(long r0, long *cpsr,
+							insn_1arg_fn_t *fn)
+{
+	register long rr0 asm("r0") = r0;
+	register long ret asm("r0");
+	long	oldcpsr = *cpsr;
+	long	newcpsr;
+
+	__asm__ volatile(
+		"msr	cpsr_fs, %[oldcpsr]	\n\t"
+		"mov	lr, pc			\n\t"
+		"mov	pc, %[fn]		\n\t"
+		"mrs	%[newcpsr], cpsr	\n\t"
+		: "=r" (ret), [newcpsr] "=r" (newcpsr)
+		: "0" (rr0), [oldcpsr] "r" (oldcpsr), [fn] "r" (fn)
+		: "lr", "cc"
+	);
+	*cpsr = (oldcpsr & ~PSR_fs) | (newcpsr & PSR_fs);
+	return ret;
+}
+
+
+static inline long __kprobes insnslot_2arg_rwflags(long r0, long r1, long *cpsr,
+							insn_2arg_fn_t *fn)
+{
+	register long rr0 asm("r0") = r0;
+	register long rr1 asm("r1") = r1;
+	register long ret asm("r0");
+	long	oldcpsr = *cpsr;
+	long	newcpsr;
+
+	__asm__ volatile(
+		"msr	cpsr_fs, %[oldcpsr]	\n\t"
+		"mov	lr, pc			\n\t"
+		"mov	pc, %[fn]		\n\t"
+		"mrs	%[newcpsr], cpsr	\n\t"
+		: "=r" (ret), [newcpsr] "=r" (newcpsr)
+		: "0" (rr0), "r" (rr1), [oldcpsr] "r" (oldcpsr), [fn] "r" (fn)
+		: "lr", "cc"
+	);
+	*cpsr = (oldcpsr & ~PSR_fs) | (newcpsr & PSR_fs);
+	return ret;
+}
+
+
+static inline long __kprobes insnslot_3arg_rwflags(long r0, long r1, long r2,
+							long *cpsr,
+							insn_3arg_fn_t *fn)
+{
+	register long rr0 asm("r0") = r0;
+	register long rr1 asm("r1") = r1;
+	register long rr2 asm("r2") = r2;
+	register long ret asm("r0");
+	long	oldcpsr = *cpsr;
+	long	newcpsr;
+
+	__asm__ volatile(
+		"msr	cpsr_fs, %[oldcpsr]	\n\t"
+		"mov	lr, pc			\n\t"
+		"mov	pc, %[fn]		\n\t"
+		"mrs	%[newcpsr], cpsr	\n\t"
+		: "=r" (ret), [newcpsr] "=r" (newcpsr)
+		: "0" (rr0), "r" (rr1), "r" (rr2),
+		  [oldcpsr] "r" (oldcpsr), [fn] "r" (fn)
+		: "lr", "cc"
+	);
+	*cpsr = (oldcpsr & ~PSR_fs) | (newcpsr & PSR_fs);
+	return ret;
+}
+
+
+static inline long __kprobes insnslot_4arg_rwflags(long r0, long r1, long r2,
+							long r3, long *cpsr,
+							insn_4arg_fn_t *fn)
+{
+	register long rr0 asm("r0") = r0;
+	register long rr1 asm("r1") = r1;
+	register long rr2 asm("r2") = r2;
+	register long rr3 asm("r3") = r3;
+	register long ret asm("r0");
+	long	oldcpsr = *cpsr;
+	long	newcpsr;
+
+	__asm__ volatile(
+		"msr	cpsr_fs, %[oldcpsr]	\n\t"
+		"mov	lr, pc			\n\t"
+		"mov	pc, %[fn]		\n\t"
+		"mrs	%[newcpsr], cpsr	\n\t"
+		: "=r" (ret), [newcpsr] "=r" (newcpsr)
+		: "0" (rr0), "r" (rr1), "r" (rr2), "r" (rr3),
+		  [oldcpsr] "r" (oldcpsr), [fn] "r" (fn)
+		: "lr", "cc"
+	);
+	*cpsr = (oldcpsr & ~PSR_fs) | (newcpsr & PSR_fs);
+	return ret;
+}
+
+
+static inline long long __kprobes insnslot_llret_4arg_rwflags(long r0, long r1,
+						long r2, long r3, long *cpsr,
+						insn_llret_4arg_fn_t *fn)
+{
+	register long rr0 asm("r0") = r0;
+	register long rr1 asm("r1") = r1;
+	register long rr2 asm("r2") = r2;
+	register long rr3 asm("r3") = r3;
+	register long ret0 asm("r0");
+	register long ret1 asm("r1");
+	long	 oldcpsr = *cpsr;
+	long	 newcpsr;
+	reg_pair fnr;
+
+	__asm__ volatile(
+		"msr	cpsr_fs, %[oldcpsr]	\n\t"
+		"mov	lr, pc			\n\t"
+		"mov	pc, %[fn]		\n\t"
+		"mrs	%[newcpsr], cpsr	\n\t"
+		: "=r" (ret0), "=r" (ret1), [newcpsr] "=r" (newcpsr)
+		: "0" (rr0), "r" (rr1), "r" (rr2), "r" (rr3),
+		  [oldcpsr] "r" (oldcpsr), [fn] "r" (fn)
+		: "lr", "cc"
+	);
+	*cpsr = (oldcpsr & ~PSR_fs) | (newcpsr & PSR_fs);
+	fnr.r0 = ret0;
+	fnr.r1 = ret1;
+	return fnr.dr;
+}
+
+
+/*
+ * To avoid having to the complications of mimicing single-stepping
+ * on a processor without a Next-PC or a single-step mode, and to
+ * avoid having to deal with the side-effects of boosting, we
+ * simulate or emulate (almost) all ARM instructions.
+ *
+ * "Simulation" is where the instruction's behavior is duplicated in
+ * C code.  "Emulation" is where the original instruction is rewritten
+ * and executed, often by altering its registers.
+ *
+ * By having all behavior of the kprobe'd instruction completed before
+ * returning from the kprobe_handler(), all locks (scheduler and
+ * interrupt) can safely be released.  There is no need for secondary
+ * breakpoints, no race with MP or preemptable kernels, nor having to
+ * clean up resources counts at a later time impacting overall system
+ * performance.  By rewriting the instruction, only the minimum registers
+ * need to be loaded and saved back optimizing perfoamance.
+ *
+ * Calling the insnslot_*_rwflags version of a function doesn't hurt
+ * anything even when the CPSR flags aren't updated by the
+ * instruction.  It's just a little slower in return for saving
+ * a little space by not having a duplicate function that doesn't
+ * update the flags.  (The same optimization can be said for
+ * instructions that do or don't update the writeback register.)
+ * Also, instructions can either read the flags, only write the
+ * flags, or read and write the flags.  To save combinations
+ * rather than for sheer performance, flag functions just assume
+ * read and write of flags.
+ */
+
+static void __kprobes simulate_bbl(struct kprobe *p, struct pt_regs *regs)
+{
+	insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
+	kprobe_opcode_t insn = p->opcode;
+	long	iaddr = (long)p->addr;
+	int	disp  = branch_displacement(insn);
+
+	if (!insnslot_1arg_rflags(0, regs->ARM_cpsr, i_fn))
+		return;
+
+	if (insn & (1 << 24))
+		regs->ARM_lr = iaddr + 4;
+
+	regs->ARM_pc = iaddr + 8 + disp;
+}
+
+
+static void __kprobes simulate_blx1(struct kprobe *p, struct pt_regs *regs)
+{
+	kprobe_opcode_t insn = p->opcode;
+	long	iaddr = (long)p->addr;
+	int	disp  = branch_displacement(insn);
+
+	regs->ARM_lr    = iaddr + 4;
+	regs->ARM_pc    = iaddr + 8 + disp + ((insn >> 23) & 0x2);
+	regs->ARM_cpsr |= PSR_T_BIT;
+}
+
+
+static void __kprobes simulate_blx2bx(struct kprobe *p, struct pt_regs *regs)
+{
+	insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
+	kprobe_opcode_t insn = p->opcode;
+	int	rm  = insn & 0xf;
+	long	rmv = regs->uregs[rm];
+
+	if (!insnslot_1arg_rflags(0, regs->ARM_cpsr, i_fn))
+		return;
+
+	if (insn & (1 << 5))
+		regs->ARM_lr = (long)p->addr + 4;
+
+	regs->ARM_pc = rmv & ~0x1;
+	regs->ARM_cpsr = (regs->ARM_cpsr & ~PSR_T_BIT) | ((rmv & 0x1) << 5);
+}
+
+
+static void __kprobes simulate_ldm1stm1(struct kprobe *p, struct pt_regs *regs)
+{
+	insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
+	kprobe_opcode_t insn = p->opcode;
+	int	rn    = (insn >> 16) & 0xf;
+	int	lbit  = (insn >> 20) & 0x1;
+	int	wbit  = (insn >> 21) & 0x1;
+	int	ubit  = (insn >> 23) & 0x1;
+	int	pbit  = (insn >> 24) & 0x1;
+	long	*addr = (long *)regs->uregs[rn];
+	int	reg_bit_vector;
+	int	reg_count;
+
+	if (!insnslot_1arg_rflags(0, regs->ARM_cpsr, i_fn))
+		return;
+
+	reg_count = 0;
+	reg_bit_vector = insn & 0xffff;
+
+	while (reg_bit_vector) {
+		reg_bit_vector &= ~(1 << __ffs(reg_bit_vector));
+		++reg_count;
+	}
+
+	if (!ubit) addr -= reg_count;
+	if (!(pbit ^ ubit)) addr += 1;
+
+	reg_bit_vector = insn & 0xffff;
+
+	while (reg_bit_vector) {
+		int	reg = __ffs(reg_bit_vector);
+		reg_bit_vector &= ~(1 << reg);
+		if (lbit) {
+			regs->uregs[reg] = *addr++;
+		} else {
+			*addr++ = regs->uregs[reg];
+		}
+	}
+
+	if (wbit) {
+		if (!ubit) addr -= reg_count;
+		if (!(pbit ^ ubit)) addr -= 1;
+		regs->uregs[rn] = (long)addr;
+	}
+}
+
+
+static void __kprobes simulate_stm1_pc(struct kprobe *p, struct pt_regs *regs)
+{
+	insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
+
+	if (!insnslot_1arg_rflags(0, regs->ARM_cpsr, i_fn))
+		return;
+
+	regs->ARM_pc = (long)p->addr + kprobes_str_pc_offset;
+	simulate_ldm1stm1(p,regs);
+	regs->ARM_pc = (long)p->addr + 4;
+}
+
+
+static void __kprobes simulate_mov_ipsp(struct kprobe *p, struct pt_regs *regs)
+{
+	regs->uregs[12] = regs->uregs[13];
+}
+
+
+static void __kprobes emulate_ldcstc(struct kprobe *p, struct pt_regs *regs)
+{
+	insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
+	kprobe_opcode_t insn = p->opcode;
+	int	rn  = (insn >> 16) & 0xf;
+	long	rnv = regs->uregs[rn];
+
+	/* Save Rn in case of writeback. */
+	regs->uregs[rn] = insnslot_1arg_rflags(rnv, regs->ARM_cpsr, i_fn);
+}
+
+
+static void __kprobes emulate_ldrd(struct kprobe *p, struct pt_regs *regs)
+{
+	insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0];
+	kprobe_opcode_t insn = p->opcode;
+	int	rd  = (insn >> 12) & 0xf;
+	int	rn  = (insn >> 16) & 0xf;
+	int	rm  = insn & 0xf;
+	long	rnv = regs->uregs[rn];
+	long	rmv = regs->uregs[rm];  /* rm/rmv may be invalid, don't care. */
+
+	/* Not following the C calling convention here, so need asm(). */
+	__asm__("mov	r0, %3		\n\t"
+		"mov	r1, %4		\n\t"
+		"msr	cpsr_fs, %5	\n\t"
+		"mov	lr, pc		\n\t"
+		"mov	pc, %5		\n\t"
+		"str	r0, %0		\n\t"  /* Rn in case of writeback. */
+		"str	r2, %1		\n\t"  /* Rd                       */
+		"str	r3, %2		\n\t"  /* R(d+1)                   */
+		: "=m" (regs->uregs[rn]),
+		  "=m" (regs->uregs[rd]),
+		  "=m" (regs->uregs[rd+1])
+		: "r" (rnv), "r" (rmv), "r" (i_fn), "r" (regs->ARM_cpsr)
+		: "r0", "r1", "r2", "r3", "lr", "cc"
+	);
+}
+
+
+static void __kprobes emulate_strd(struct kprobe *p, struct pt_regs *regs)
+{
+	insn_4arg_fn_t *i_fn = (insn_4arg_fn_t *)&p->ainsn.insn[0];
+	kprobe_opcode_t insn = p->opcode;
+	int	rd  = (insn >> 12) & 0xf;
+	int	rn  = (insn >> 16) & 0xf;
+	int	rm  = insn & 0xf;
+	long	rnv = regs->uregs[rn];
+	long	rmv = regs->uregs[rm];  /* rm/rmv may be invalid, don't care. */
+
+	regs->uregs[rn] = insnslot_4arg_rflags(rnv, rmv, regs->uregs[rd],
+						regs->uregs[rd+1],
+						regs->ARM_cpsr, i_fn);
+}
+
+
+static void __kprobes emulate_ldr(struct kprobe *p, struct pt_regs *regs)
+{
+	insn_llret_3arg_fn_t *i_fn = (insn_llret_3arg_fn_t *)&p->ainsn.insn[0];
+	kprobe_opcode_t insn = p->opcode;
+	reg_pair	fnr;
+	int	rd   = (insn >> 12) & 0xf;
+	int	rn   = (insn >> 16) & 0xf;
+	int	rm   = insn & 0xf;
+	long	rdv;
+	long	rnv  = regs->uregs[rn];
+	long	rmv  = regs->uregs[rm]; /* rm/rmv may be invalid, don't care. */
+	long	cpsr = regs->ARM_cpsr;
+
+	fnr.dr = insnslot_llret_3arg_rflags(rnv, 0, rmv, cpsr, i_fn);
+	regs->uregs[rn] = fnr.r0;  /* Save Rn in case of writeback. */
+	rdv = fnr.r1;
+
+	if (rd == 15) {
+#if __LINUX_ARM_ARCH__ >= 5
+		rdv &= ~0x1;
+		regs->ARM_cpsr = (cpsr & ~PSR_T_BIT) | ((rdv & 0x1) << 5);
+#else
+		rdv &= ~0x2;
+#endif
+	}
+
+	regs->uregs[rd] = rdv;
+}
+
+
+static void __kprobes emulate_str(struct kprobe *p, struct pt_regs *regs)
+{
+	insn_3arg_fn_t *i_fn = (insn_3arg_fn_t *)&p->ainsn.insn[0];
+	kprobe_opcode_t insn = p->opcode;
+	long	iaddr = (long)p->addr;
+	int	rd  = (insn >> 12) & 0xf;
+	int	rn  = (insn >> 16) & 0xf;
+	int	rm  = insn & 0xf;
+	long	rdv = (rd == 15) ? iaddr + kprobes_str_pc_offset :
+								regs->uregs[rd];
+	long	rnv = (rn == 15) ? iaddr +  8 : regs->uregs[rn];
+	long	rmv = regs->uregs[rm];  /* rm/rmv may be invalid, don't care. */
+
+	/* Save Rn in case of writeback. */
+	regs->uregs[rn] = insnslot_3arg_rflags(rnv, rdv, rmv,
+						regs->ARM_cpsr, i_fn);
+}
+
+
+static void __kprobes emulate_mrrc(struct kprobe *p, struct pt_regs *regs)
+{
+	insn_llret_0arg_fn_t *i_fn = (insn_llret_0arg_fn_t *)&p->ainsn.insn[0];
+	kprobe_opcode_t insn = p->opcode;
+	reg_pair	fnr;
+	int	rd  = (insn >> 12) & 0xf;
+	int	rn  = (insn >> 16) & 0xf;
+
+	fnr.dr = insnslot_llret_0arg_rflags(regs->ARM_cpsr, i_fn);
+	regs->uregs[rn] = fnr.r0;
+	regs->uregs[rd] = fnr.r1;
+}
+
+
+static void __kprobes emulate_mcrr(struct kprobe *p, struct pt_regs *regs)
+{
+	insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0];
+	kprobe_opcode_t insn = p->opcode;
+	int	rd  = (insn >> 12) & 0xf;
+	int	rn  = (insn >> 16) & 0xf;
+	long	rnv = regs->uregs[rn];
+	long	rdv = regs->uregs[rd];
+
+	insnslot_2arg_rflags(rnv, rdv, regs->ARM_cpsr, i_fn);
+}
+
+
+static void __kprobes emulate_sat(struct kprobe *p, struct pt_regs *regs)
+{
+	insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
+	kprobe_opcode_t insn = p->opcode;
+	int	rd  = (insn >> 12) & 0xf;
+	int	rm  = insn & 0xf;
+	long	rmv = regs->uregs[rm];
+
+	/* Writes Q flag */
+	regs->uregs[rd] = insnslot_1arg_rwflags(rmv, &regs->ARM_cpsr, i_fn);
+}
+
+
+static void __kprobes emulate_sel(struct kprobe *p, struct pt_regs *regs)
+{
+	insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0];
+	kprobe_opcode_t insn = p->opcode;
+	int	rd  = (insn >> 12) & 0xf;
+	int	rn  = (insn >> 16) & 0xf;
+	int	rm  = insn & 0xf;
+	long	rnv = regs->uregs[rn];
+	long	rmv = regs->uregs[rm];
+
+	/* Reads GE bits */
+	regs->uregs[rd] = insnslot_2arg_rflags(rnv, rmv, regs->ARM_cpsr, i_fn);
+}
+
+
+static void __kprobes emulate_none(struct kprobe *p, struct pt_regs *regs)
+{
+	insn_0arg_fn_t *i_fn = (insn_0arg_fn_t *)&p->ainsn.insn[0];
+
+	insnslot_0arg_rflags(regs->ARM_cpsr, i_fn);
+}
+
+
+static void __kprobes emulate_rd12(struct kprobe *p, struct pt_regs *regs)
+{
+	insn_0arg_fn_t *i_fn = (insn_0arg_fn_t *)&p->ainsn.insn[0];
+	kprobe_opcode_t insn = p->opcode;
+	int	rd = (insn >> 12) & 0xf;
+
+	regs->uregs[rd] = insnslot_0arg_rflags(regs->ARM_cpsr, i_fn);
+}
+
+
+static void __kprobes emulate_ird12(struct kprobe *p, struct pt_regs *regs)
+{
+	insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
+	kprobe_opcode_t insn = p->opcode;
+	int	ird = (insn >> 12) & 0xf;
+
+	insnslot_1arg_rflags(regs->uregs[ird], regs->ARM_cpsr, i_fn);
+}
+
+
+static void __kprobes emulate_rn16(struct kprobe *p, struct pt_regs *regs)
+{
+	insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
+	kprobe_opcode_t insn = p->opcode;
+	int	rn  = (insn >> 16) & 0xf;
+	long	rnv = regs->uregs[rn];
+
+	insnslot_1arg_rflags(rnv, regs->ARM_cpsr, i_fn);
+}
+
+
+static void __kprobes emulate_rd12rm0(struct kprobe *p, struct pt_regs *regs)
+{
+	insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
+	kprobe_opcode_t insn = p->opcode;
+	int	rd  = (insn >> 12) & 0xf;
+	int	rm  = insn & 0xf;
+	long	rmv = regs->uregs[rm];
+
+	regs->uregs[rd] = insnslot_1arg_rflags(rmv, regs->ARM_cpsr, i_fn);
+}
+
+
+static void __kprobes emulate_rd12rn16rm0_rwflags(struct kprobe *p,
+						struct pt_regs *regs)
+{
+	insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0];
+	kprobe_opcode_t insn = p->opcode;
+	int	rd  = (insn >> 12) & 0xf;
+	int	rn  = (insn >> 16) & 0xf;
+	int	rm  = insn & 0xf;
+	long	rnv = regs->uregs[rn];
+	long	rmv = regs->uregs[rm];
+
+	regs->uregs[rd] = insnslot_2arg_rwflags(rnv, rmv,
+						&regs->ARM_cpsr, i_fn);
+}
+
+
+static void __kprobes emulate_rd16rn12rs8rm0_rwflags(struct kprobe *p,
+						struct pt_regs *regs)
+{
+	insn_3arg_fn_t *i_fn = (insn_3arg_fn_t *)&p->ainsn.insn[0];
+	kprobe_opcode_t insn = p->opcode;
+	int	rd  = (insn >> 16) & 0xf;
+	int	rn  = (insn >> 12) & 0xf;
+	int	rs  = (insn >> 8) & 0xf;
+	int	rm  = insn & 0xf;
+	long	rnv = regs->uregs[rn];
+	long	rsv = regs->uregs[rs];
+	long	rmv = regs->uregs[rm];
+
+	regs->uregs[rd] = insnslot_3arg_rwflags(rnv, rsv, rmv,
+							&regs->ARM_cpsr, i_fn);
+}
+
+
+static void __kprobes emulate_rd16rs8rm0_rwflags(struct kprobe *p,
+						struct pt_regs *regs)
+{
+	insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0];
+	kprobe_opcode_t insn = p->opcode;
+	int	rd  = (insn >> 16) & 0xf;
+	int	rs  = (insn >> 8) & 0xf;
+	int	rm  = insn & 0xf;
+	long	rsv = regs->uregs[rs];
+	long	rmv = regs->uregs[rm];
+
+	regs->uregs[rd] = insnslot_2arg_rwflags(rsv, rmv,
+							&regs->ARM_cpsr, i_fn);
+}
+
+
+static void __kprobes emulate_rdhi16rdlo12rs8rm0_rwflags(struct kprobe *p,
+							struct pt_regs *regs)
+{
+	insn_llret_4arg_fn_t *i_fn = (insn_llret_4arg_fn_t *)&p->ainsn.insn[0];
+	kprobe_opcode_t insn = p->opcode;
+	reg_pair	fnr;
+	int	rdhi = (insn >> 16) & 0xf;
+	int	rdlo = (insn >> 12) & 0xf;
+	int	rs   = (insn >> 8) & 0xf;
+	int	rm   = insn & 0xf;
+	long	rsv  = regs->uregs[rs];
+	long	rmv  = regs->uregs[rm];
+
+	fnr.dr = insnslot_llret_4arg_rwflags(regs->uregs[rdhi],
+						regs->uregs[rdlo], rsv, rmv,
+						&regs->ARM_cpsr, i_fn);
+	regs->uregs[rdhi] = fnr.r0;
+	regs->uregs[rdlo] = fnr.r1;
+}
+
+
+static void __kprobes emulate_alu_imm_rflags(struct kprobe *p,
+						struct pt_regs *regs)
+{
+	insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
+	kprobe_opcode_t insn = p->opcode;
+	int	rd  = (insn >> 12) & 0xf;
+	int	rn  = (insn >> 16) & 0xf;
+	long	rnv = (rn == 15) ? (long)p->addr + 8 : regs->uregs[rn];
+
+	regs->uregs[rd] = insnslot_1arg_rflags(rnv, regs->ARM_cpsr, i_fn);
+}
+
+
+static void __kprobes emulate_alu_imm_rwflags(struct kprobe *p,
+						struct pt_regs *regs)
+{
+	insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
+	kprobe_opcode_t insn = p->opcode;
+	int	rd  = (insn >> 12) & 0xf;
+	int	rn  = (insn >> 16) & 0xf;
+	long	rnv = (rn == 15) ? (long)p->addr + 8 : regs->uregs[rn];
+
+	regs->uregs[rd] = insnslot_1arg_rwflags(rnv, &regs->ARM_cpsr, i_fn);
+}
+
+
+static void __kprobes emulate_alu_rflags(struct kprobe *p,
+						struct pt_regs *regs)
+{
+	insn_3arg_fn_t *i_fn = (insn_3arg_fn_t *)&p->ainsn.insn[0];
+	kprobe_opcode_t insn = p->opcode;
+	long	ppc = (long)p->addr + 8;
+	int	rd  = (insn >> 12) & 0xf;
+	int	rn  = (insn >> 16) & 0xf;  /* rn/rnv/rs/rsv may be */
+	int	rs  = (insn >> 8) & 0xf;   /* invalid, don't care. */
+	int	rm  = insn & 0xf;
+	long	rnv = (rn == 15) ? ppc : regs->uregs[rn];
+	long	rmv = (rm == 15) ? ppc : regs->uregs[rm];
+	long	rsv = regs->uregs[rs];
+
+	regs->uregs[rd] = insnslot_3arg_rflags(rnv, rmv, rsv,
+							regs->ARM_cpsr, i_fn);
+}
+
+
+static void __kprobes emulate_alu_rwflags(struct kprobe *p,
+						struct pt_regs *regs)
+{
+	insn_3arg_fn_t *i_fn = (insn_3arg_fn_t *)&p->ainsn.insn[0];
+	kprobe_opcode_t insn = p->opcode;
+	long	ppc = (long)p->addr + 8;
+	int	rd  = (insn >> 12) & 0xf;
+	int	rn  = (insn >> 16) & 0xf;  /* rn/rnv/rs/rsv may be */
+	int	rs  = (insn >> 8) & 0xf;   /* invalid, don't care. */
+	int	rm  = insn & 0xf;
+	long	rnv = (rn == 15) ? ppc : regs->uregs[rn];
+	long	rmv = (rm == 15) ? ppc : regs->uregs[rm];
+	long	rsv = regs->uregs[rs];
+
+	regs->uregs[rd] = insnslot_3arg_rwflags(rnv, rmv, rsv,
+							&regs->ARM_cpsr, i_fn);
+}
+
+
+static enum kprobe_insn __kprobes prep_emulate_ldr_str(kprobe_opcode_t insn,
+						struct arch_specific_insn *asi)
+{
+	int ibit = (insn & (1 << 26)) ? 25 : 22;
+
+	insn &= 0xfff00fff;
+	insn |= 0x00001000;      /* Rn = r0, Rd = r1 */
+	if (insn & (1 << ibit)) {
+		insn &= ~0xf;
+		insn |= 2;       /* Rm = r2 */
+	}
+	asi->insn[0]      = insn;
+	asi->insn_handler = (insn & (1 << 20)) ?  emulate_ldr : emulate_str;
+	return INSN_GOOD;
+}
+
+static enum kprobe_insn __kprobes prep_emulate_rd12rm0(kprobe_opcode_t insn,
+						struct arch_specific_insn *asi)
+{
+	insn &= 0xffff0ff0;	/* Rd = r0, Rm = r0 */
+	asi->insn[0]      = insn;
+	asi->insn_handler = emulate_rd12rm0;
+	return INSN_GOOD;
+}
+
+static enum kprobe_insn __kprobes prep_emulate_rd12(kprobe_opcode_t insn,
+						struct arch_specific_insn *asi)
+{
+	insn &= 0xffff0fff;	/* Rd = r0 */
+	asi->insn[0]      = insn;
+	asi->insn_handler = emulate_rd12;
+	return INSN_GOOD;
+}
+
+static enum kprobe_insn __kprobes
+			prep_emulate_rd12rn16rm0_wflags(kprobe_opcode_t insn,
+						struct arch_specific_insn *asi)
+{
+	insn &= 0xfff00ff0;	/* Rd = r0, Rn = r0 */
+	insn |= 0x00000001;	/* Rm = r1 */
+	asi->insn[0]      = insn;
+	asi->insn_handler = emulate_rd12rn16rm0_rwflags;
+	return INSN_GOOD;
+}
+
+static enum kprobe_insn __kprobes
+			prep_emulate_rd16rs8rm0_wflags(kprobe_opcode_t insn,
+						struct arch_specific_insn *asi)
+{
+	insn &= 0xfff0f0f0;	/* Rd = r0, Rs = r0 */
+	insn |= 0x00000001;	/* Rm = r1          */
+	asi->insn[0]      = insn;
+	asi->insn_handler = emulate_rd16rs8rm0_rwflags;
+	return INSN_GOOD;
+}
+
+static enum kprobe_insn __kprobes
+			prep_emulate_rd16rn12rs8rm0_wflags(kprobe_opcode_t insn,
+						struct arch_specific_insn *asi)
+{
+	insn &= 0xfff000f0;	/* Rd = r0, Rn = r0 */
+	insn |= 0x00000102;	/* Rs = r1, Rm = r2 */
+	asi->insn[0]      = insn;
+	asi->insn_handler = emulate_rd16rn12rs8rm0_rwflags;
+	return INSN_GOOD;
+}
+
+static enum kprobe_insn __kprobes
+		prep_emulate_rdhi16rdlo12rs8rm0_wflags(kprobe_opcode_t insn,
+						struct arch_specific_insn *asi)
+{
+	insn &= 0xfff000f0;	/* RdHi = r0, RdLo = r1 */
+	insn |= 0x00001203;	/* Rs = r2, Rm = r3 */
+	asi->insn[0]      = insn;
+	asi->insn_handler = emulate_rdhi16rdlo12rs8rm0_rwflags;
+	return INSN_GOOD;
+}
+
+
+/*
+ * For the instruction masking and comparisons in all the "space_*"
+ * functions below, Do _not_ rearrange the order of tests unless
+ * you're very, very sure of what you are doing.  For the sake of
+ * efficiency, the masks for some tests sometimes assume other test
+ * have been done prior to them so the number of patterns to test
+ * for an instruction set can be as broad as possible to reduce the
+ * number of tests needed.
+ */
+
+static enum kprobe_insn __kprobes space_1111(kprobe_opcode_t insn,
+						struct arch_specific_insn *asi)
+{
+	/* CPS mmod == 1 : 1111 0001 0000 xx10 xxxx xxxx xx0x xxxx
+	 * RFE : 1111 100x x0x1 xxxx xxxx 1010 xxxx xxxx
+	 * SRS : 1111 100x x1x0 1101 xxxx 0101 xxxx xxxx
+	 */
+	if ( (insn & 0xfff30020) == 0xf1020000 ||
+	     (insn & 0xfe500f00) == 0xf8100a00 ||
+	     (insn & 0xfe5f0f00) == 0xf84d0500 )
+		return INSN_REJECTED;
+
+	/* PLD : 1111 01x1 x101 xxxx xxxx xxxx xxxx xxxx : */
+	if ( (insn & 0xfd700000) == 0xf4500000 ) {
+		insn &= 0xfff0ffff;	/* Rn = r0 */
+
+		asi->insn[0]      = insn;
+		asi->insn_handler = emulate_rn16;
+		return INSN_GOOD;
+	}
+
+	/* BLX(1) : 1111 101x xxxx xxxx xxxx xxxx xxxx xxxx : */
+	if ( (insn & 0xfe000000) == 0xfa000000 ) {
+		asi->insn_handler = simulate_blx1;
+		return INSN_GOOD_NO_SLOT;
+	}
+
+	/* SETEND : 1111 0001 0000 0001 xxxx xxxx 0000 xxxx :
+	 * CDP2   : 1111 1110 xxxx xxxx xxxx xxxx xxx0 xxxx :
+	 */
+	if ( (insn & 0xffff00f0) == 0xf1010000 ||
+	     (insn & 0xff000010) == 0xfe000000 ) {
+		asi->insn[0]      = insn;
+		asi->insn_handler = emulate_none;
+		return INSN_GOOD;
+	}
+
+	/* MCRR2 : 1111 1100 0100 xxxx xxxx xxxx xxxx xxxx :   (Rd != Rn)
+	 * MRRC2 : 1111 1100 0101 xxxx xxxx xxxx xxxx xxxx :   (Rd != Rn)
+	 */
+	if ( (insn & 0xffe00000) == 0xfc400000 ) {
+		insn &= 0xfff00fff;	/* Rn = r0 */
+		insn |= 0x00001000;	/* Rd = r1 */
+
+		asi->insn[0]      = insn;
+		asi->insn_handler = (insn & (1 << 20)) ?
+					emulate_mrrc : emulate_mcrr;
+		return INSN_GOOD;
+	}
+
+	/* LDC2 : 1111 110x xxx1 xxxx xxxx xxxx xxxx xxxx :
+	 * STC2 : 1111 110x xxx0 xxxx xxxx xxxx xxxx xxxx :
+	 */
+	if ( (insn & 0xfe000000) == 0xfc000000 ) {
+		insn &= 0xfff0ffff;      /* Rn = r0 */
+
+		asi->insn[0]      = insn;
+		asi->insn_handler = emulate_ldcstc;
+		return INSN_GOOD;
+	}
+
+	/* MCR2 : 1111 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx :
+	 * MRC2 : 1111 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx :
+	 */
+	insn &= 0xffff0fff;	/* Rd = r0 */
+
+	asi->insn[0]      = insn;
+	asi->insn_handler = (insn & (1 << 20)) ? emulate_rd12 : emulate_ird12;
+	return INSN_GOOD;
+}
+
+
+static enum kprobe_insn __kprobes space_cccc_000x(kprobe_opcode_t insn,
+						struct arch_specific_insn *asi)
+{
+	if ( (insn & 0x0f900010) == 0x01000000 ) {
+		/* cccc 0001 0xx0 xxxx xxxx xxxx xxxx xxx0 xxxx */
+
+		/* BXJ  : cccc 0001 0010 xxxx xxxx xxxx 0010 xxxx
+		 * MSR  : cccc 0001 0x10 xxxx xxxx xxxx 0000 xxxx
+		 */
+		if ( (insn & 0x0ff000f0) == 0x01200020 ||
+		     (insn & 0x0fb000f0) == 0x01200000 )
+			return INSN_REJECTED;
+
+		/* MRS : cccc 0001 0x00 xxxx xxxx xxxx 0000 xxxx : */
+		if ( (insn & 0x0fb00010) == 0x01000000 )
+			return prep_emulate_rd12(insn, asi);
+
+		/* SMLALxy : cccc 0001 0100 xxxx xxxx xxxx 1xx0 xxxx : */
+		if ( (insn & 0x0ff00090) == 0x01400080 )
+		       return prep_emulate_rdhi16rdlo12rs8rm0_wflags(insn, asi);
+
+		/* SMULWy : cccc 0001 0010 xxxx xxxx xxxx 1x10 xxxx :
+		 * SMULxy : cccc 0001 0110 xxxx xxxx xxxx 1xx0 xxxx :
+		 */
+		if ( (insn & 0x0ff000b0) == 0x012000a0 ||
+		     (insn & 0x0ff00090) == 0x01600080 )
+			return prep_emulate_rd16rs8rm0_wflags(insn, asi);
+
+		/* SMLAxy : cccc 0001 0000 xxxx xxxx xxxx 1xx0 xxxx :Q
+		 * SMLAWy : cccc 0001 0010 xxxx xxxx xxxx 0x00 xxxx :Q
+		 */
+		return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi);
+
+	} else if ( (insn & 0x0f900090) == 0x01000010 ) {
+		/* cccc 0001 0xx0 xxxx xxxx xxxx xxxx 0xx1 xxxx */
+
+		/* BKPT : 1110 0001 0010 xxxx xxxx xxxx 0111 xxxx */
+		if ( (insn & 0xfff000f0) == 0xe1200070 )
+			return INSN_REJECTED;
+
+		/* BLX(2) : cccc 0001 0010 xxxx xxxx xxxx 0011 xxxx :
+		 * BX     : cccc 0001 0010 xxxx xxxx xxxx 0001 xxxx :
+		 */
+
+		if ( (insn & 0x0ff000d0) == 0x01200010 ) {
+			asi->insn[0]      = truecc_insn(insn);
+			asi->insn_handler = simulate_blx2bx;
+			return INSN_GOOD;
+		}
+
+		/* CLZ : cccc 0001 0110 xxxx xxxx xxxx 0001 xxxx : */
+		if ( (insn & 0x0ff000f0) == 0x01600010 )
+			return prep_emulate_rd12rm0(insn, asi);
+
+		/* QADD    : cccc 0001 0000 xxxx xxxx xxxx 0101 xxxx :Q
+		 * QSUB    : cccc 0001 0010 xxxx xxxx xxxx 0101 xxxx :Q
+		 * QDADD   : cccc 0001 0100 xxxx xxxx xxxx 0101 xxxx :Q
+		 * QDSUB   : cccc 0001 0110 xxxx xxxx xxxx 0101 xxxx :Q
+		 */
+		return prep_emulate_rd12rn16rm0_wflags(insn, asi);
+
+	} else if ( (insn & 0x0f000090) == 0x00000090 ) {
+		/* cccc 0000 xxxx xxxx xxxx xxxx xxxx 1001 xxxx */
+
+		/* MUL    : cccc 0000 0000 xxxx xxxx xxxx 1001 xxxx :
+		 * MULS   : cccc 0000 0001 xxxx xxxx xxxx 1001 xxxx :cc
+		 * MLA    : cccc 0000 0010 xxxx xxxx xxxx 1001 xxxx :
+		 * MLAS   : cccc 0000 0011 xxxx xxxx xxxx 1001 xxxx :cc
+		 * UMAAL  : cccc 0000 0100 xxxx xxxx xxxx 1001 xxxx :
+		 * UMULL  : cccc 0000 1000 xxxx xxxx xxxx 1001 xxxx :
+		 * UMULLS : cccc 0000 1001 xxxx xxxx xxxx 1001 xxxx :cc
+		 * UMLAL  : cccc 0000 1010 xxxx xxxx xxxx 1001 xxxx :
+		 * UMLALS : cccc 0000 1011 xxxx xxxx xxxx 1001 xxxx :cc
+		 * SMULL  : cccc 0000 1100 xxxx xxxx xxxx 1001 xxxx :
+		 * SMULLS : cccc 0000 1101 xxxx xxxx xxxx 1001 xxxx :cc
+		 * SMLAL  : cccc 0000 1110 xxxx xxxx xxxx 1001 xxxx :
+		 * SMLALS : cccc 0000 1111 xxxx xxxx xxxx 1001 xxxx :cc
+		 */
+
+		if ( (insn & 0x0fe000f0) == 0x00000090 ) {
+		       return prep_emulate_rd16rs8rm0_wflags(insn, asi);
+		} else if  ( (insn & 0x0fe000f0) == 0x00200090 ) {
+		       return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi);
+		} else {
+		       return prep_emulate_rdhi16rdlo12rs8rm0_wflags(insn, asi);
+		}
+
+	} else if ( (insn & 0x0e000090) == 0x00000090 ) {
+		/* cccc 000x xxxx xxxx xxxx xxxx xxxx 1xx1 xxxx */
+
+		/* SWP   : cccc 0001 0000 xxxx xxxx xxxx 1001 xxxx :
+		 * SWPB  : cccc 0001 0100 xxxx xxxx xxxx 1001 xxxx :
+		 * LDRD  : cccc 000x xxx0 xxxx xxxx xxxx 1101 xxxx :
+		 * STRD  : cccc 000x xxx0 xxxx xxxx xxxx 1111 xxxx :
+		 * STREX : cccc 0001 1000 xxxx xxxx xxxx 1001 xxxx :
+		 * LDREX : cccc 0001 1001 xxxx xxxx xxxx 1001 xxxx :
+		 * LDRH  : cccc 000x xxx1 xxxx xxxx xxxx 1011 xxxx :
+		 * STRH  : cccc 000x xxx0 xxxx xxxx xxxx 1011 xxxx :
+		 * LDRSB : cccc 000x xxx1 xxxx xxxx xxxx 1101 xxxx :
+		 * LDRSH : cccc 000x xxx1 xxxx xxxx xxxx 1111 xxxx :
+		 */
+
+		if ( (insn & 0x0fb000f0) == 0x01000090 ) {  /* SWP/SWPB */
+			return prep_emulate_rd12rn16rm0_wflags(insn, asi);
+		} else if ( (insn & 0x0e1000d0) == 0x00000d0 ) { /* STRD/LDRD */
+			insn &= 0xfff00fff;
+			insn |= 0x00002000;      /* Rn = r0, Rd = r2 */
+
+			if (insn & (1 << 22)) {  /* I bit */
+				insn &= ~0xf;
+				insn |= 1;       /* Rm = r1 */
+			}
+			asi->insn[0]      = insn;
+			asi->insn_handler = (insn & (1 << 5)) ?
+						emulate_strd :
+						emulate_ldrd;
+			return INSN_GOOD;
+		}
+
+		return prep_emulate_ldr_str(insn, asi);
+	}
+
+	/* cccc 000x xxxx xxxx xxxx xxxx xxxx xxxx xxxx */
+
+	/* ALU op with S bit and Rd == 15 :
+	 * 	cccc 000x xxx1 xxxx 1111 xxxx xxxx xxxx
+	 */
+	if ( (insn & 0x0e10f000) == 0x0010f000 )
+		return INSN_REJECTED;
+
+	/* MOV  r12,r13 is the most common kprobe'd instruction by far.
+	 * Check and optimize for it explicitly.
+	 */
+
+	if (insn == 0xe1a0c00d) {
+		asi->insn_handler = simulate_mov_ipsp;
+		return INSN_GOOD_NO_SLOT;
+	}
+
+	/* Data processing: Immediate-shift / Register-shift
+	 * ALU op : cccc 000x xxxx xxxx xxxx xxxx xxxx xxxx
+	 * CPY    : cccc 0001 1010 xxxx xxxx 0000 0000 xxxx
+	 * MOV    : cccc 0001 101x xxxx xxxx xxxx xxxx xxxx
+	 * *S (bit 20) updates condition codes
+	 * ADC/SBC/RSC reads the C flag
+	 */
+
+	insn &= 0xfff00ff0;	/* Rn = r0, Rd = r0 */
+	insn |= 0x00000001;	/* Rm = r1 */
+
+	if (insn & 0x010) {
+		insn &= 0xfffff0ff;     /* register shift */
+		insn |= 0x00000200;     /* Rs = r2 */
+	}
+
+	asi->insn[0]      = insn;
+	asi->insn_handler = emulate_alu_rwflags;
+	asi->insn_handler = (insn & (1 << 20)) ?  /* S-bit */
+				emulate_alu_rwflags : emulate_alu_rflags;
+	return INSN_GOOD;
+}
+
+
+static enum kprobe_insn __kprobes space_cccc_001x(kprobe_opcode_t insn,
+						struct arch_specific_insn *asi)
+{
+	/* MSR   : cccc 0011 0x10 xxxx xxxx xxxx xxxx xxxx
+	 * Undef : cccc 0011 0x00 xxxx xxxx xxxx xxxx xxxx
+	 * ALU op with S bit and Rd == 15 :
+	 *	   cccc 001x xxx1 xxxx 1111 xxxx xxxx xxxx
+	 */
+
+	if ( (insn & 0x0f900000) == 0x03200000 ||  /* MSR & Undef */
+	     (insn & 0x0e10f000) == 0x0210f000 )   /* ALU s-bit, R15  */
+		return INSN_REJECTED;
+
+	/* Data processing: 32-bit Immediate
+	 * ALU op : cccc 001x xxxx xxxx xxxx xxxx xxxx xxxx
+	 * MOV    : cccc 0011 101x xxxx xxxx xxxx xxxx xxxx
+	 * *S (bit 20) updates condition codes
+	 * ADC/SBC/RSC reads the C flag
+	 */
+
+	insn &= 0xfff00ff0;	/* Rn = r0, Rd = r0 */
+	asi->insn[0]      = insn;
+	asi->insn_handler = (insn & (1 << 20)) ?  /* S-bit */
+			emulate_alu_imm_rwflags : emulate_alu_imm_rflags;
+	return INSN_GOOD;
+}
+
+
+static enum kprobe_insn __kprobes space_cccc_0110__1(kprobe_opcode_t insn,
+						struct arch_specific_insn *asi)
+{
+	/* SEL : cccc 0110 1000 xxxx xxxx xxxx 1011 xxxx GE: !!! */
+
+	if ( (insn & 0x0ff000f0) == 0x068000b0 ) {
+		insn &= 0xfff00ff0;	/* Rd = r0, Rn = r0 */
+		insn |= 0x00000001;	/* Rm = r1 */
+
+		asi->insn[0]      = insn;
+		asi->insn_handler = emulate_sel;
+		return INSN_GOOD;
+	}
+
+	/* SSAT   : cccc 0110 101x xxxx xxxx xxxx xx01 xxxx :Q
+	 * USAT   : cccc 0110 111x xxxx xxxx xxxx xx01 xxxx :Q
+	 * SSAT16 : cccc 0110 1010 xxxx xxxx xxxx 0011 xxxx :Q
+	 * USAT16 : cccc 0110 1110 xxxx xxxx xxxx 0011 xxxx :Q
+	 */
+
+	if ( (insn & 0x0fa00030) == 0x06a00010 ||
+	     (insn & 0x0fb000f0) == 0x06a00030 ) {
+		insn &= 0xffff0ff0;	/* Rd = r0, Rm = r0 */
+
+		asi->insn[0]      = insn;
+		asi->insn_handler = emulate_sat;
+		return INSN_GOOD;
+	}
+
+	/*
+	 * REV    : cccc 0110 1011 xxxx xxxx xxxx 0011 xxxx :
+	 * REV16  : cccc 0110 1011 xxxx xxxx xxxx 1011 xxxx :
+	 * REVSH  : cccc 0110 1111 xxxx xxxx xxxx 1011 xxxx :
+	 */
+
+	if ( (insn & 0x0ff00070) == 0x06b00030 ||
+	     (insn & 0x0ff000f0) == 0x06f000b0 )
+		return prep_emulate_rd12rm0(insn, asi);
+
+	/* SADD16    : cccc 0110 0001 xxxx xxxx xxxx 0001 xxxx :GE
+	 * SADDSUBX  : cccc 0110 0001 xxxx xxxx xxxx 0011 xxxx :GE
+	 * SSUBADDX  : cccc 0110 0001 xxxx xxxx xxxx 0101 xxxx :GE
+	 * SSUB16    : cccc 0110 0001 xxxx xxxx xxxx 0111 xxxx :GE
+	 * SADD8     : cccc 0110 0001 xxxx xxxx xxxx 1001 xxxx :GE
+	 * SSUB8     : cccc 0110 0001 xxxx xxxx xxxx 1111 xxxx :GE
+	 * QADD16    : cccc 0110 0010 xxxx xxxx xxxx 0001 xxxx :
+	 * QADDSUBX  : cccc 0110 0010 xxxx xxxx xxxx 0011 xxxx :
+	 * QSUBADDX  : cccc 0110 0010 xxxx xxxx xxxx 0101 xxxx :
+	 * QSUB16    : cccc 0110 0010 xxxx xxxx xxxx 0111 xxxx :
+	 * QADD8     : cccc 0110 0010 xxxx xxxx xxxx 1001 xxxx :
+	 * QSUB8     : cccc 0110 0010 xxxx xxxx xxxx 1111 xxxx :
+	 * SHADD16   : cccc 0110 0011 xxxx xxxx xxxx 0001 xxxx :
+	 * SHADDSUBX : cccc 0110 0011 xxxx xxxx xxxx 0011 xxxx :
+	 * SHSUBADDX : cccc 0110 0011 xxxx xxxx xxxx 0101 xxxx :
+	 * SHSUB16   : cccc 0110 0011 xxxx xxxx xxxx 0111 xxxx :
+	 * SHADD8    : cccc 0110 0011 xxxx xxxx xxxx 1001 xxxx :
+	 * SHSUB8    : cccc 0110 0011 xxxx xxxx xxxx 1111 xxxx :
+	 * UADD16    : cccc 0110 0101 xxxx xxxx xxxx 0001 xxxx :GE
+	 * UADDSUBX  : cccc 0110 0101 xxxx xxxx xxxx 0011 xxxx :GE
+	 * USUBADDX  : cccc 0110 0101 xxxx xxxx xxxx 0101 xxxx :GE
+	 * USUB16    : cccc 0110 0101 xxxx xxxx xxxx 0111 xxxx :GE
+	 * UADD8     : cccc 0110 0101 xxxx xxxx xxxx 1001 xxxx :GE
+	 * USUB8     : cccc 0110 0101 xxxx xxxx xxxx 1111 xxxx :GE
+	 * UQADD16   : cccc 0110 0110 xxxx xxxx xxxx 0001 xxxx :
+	 * UQADDSUBX : cccc 0110 0110 xxxx xxxx xxxx 0011 xxxx :
+	 * UQSUBADDX : cccc 0110 0110 xxxx xxxx xxxx 0101 xxxx :
+	 * UQSUB16   : cccc 0110 0110 xxxx xxxx xxxx 0111 xxxx :
+	 * UQADD8    : cccc 0110 0110 xxxx xxxx xxxx 1001 xxxx :
+	 * UQSUB8    : cccc 0110 0110 xxxx xxxx xxxx 1111 xxxx :
+	 * UHADD16   : cccc 0110 0111 xxxx xxxx xxxx 0001 xxxx :
+	 * UHADDSUBX : cccc 0110 0111 xxxx xxxx xxxx 0011 xxxx :
+	 * UHSUBADDX : cccc 0110 0111 xxxx xxxx xxxx 0101 xxxx :
+	 * UHSUB16   : cccc 0110 0111 xxxx xxxx xxxx 0111 xxxx :
+	 * UHADD8    : cccc 0110 0111 xxxx xxxx xxxx 1001 xxxx :
+	 * UHSUB8    : cccc 0110 0111 xxxx xxxx xxxx 1111 xxxx :
+	 * PKHBT     : cccc 0110 1000 xxxx xxxx xxxx x001 xxxx :
+	 * PKHTB     : cccc 0110 1000 xxxx xxxx xxxx x101 xxxx :
+	 * SXTAB16   : cccc 0110 1000 xxxx xxxx xxxx 0111 xxxx :
+	 * SXTB      : cccc 0110 1010 xxxx xxxx xxxx 0111 xxxx :
+	 * SXTAB     : cccc 0110 1010 xxxx xxxx xxxx 0111 xxxx :
+	 * SXTAH     : cccc 0110 1011 xxxx xxxx xxxx 0111 xxxx :
+	 * UXTAB16   : cccc 0110 1100 xxxx xxxx xxxx 0111 xxxx :
+	 * UXTAB     : cccc 0110 1110 xxxx xxxx xxxx 0111 xxxx :
+	 * UXTAH     : cccc 0110 1111 xxxx xxxx xxxx 0111 xxxx :
+	 */
+
+	return prep_emulate_rd12rn16rm0_wflags(insn, asi);
+}
+
+static enum kprobe_insn __kprobes space_cccc_0111__1(kprobe_opcode_t insn,
+					struct arch_specific_insn *asi)
+{
+	/* Undef : cccc 0111 1111 xxxx xxxx xxxx 1111 xxxx */
+	if ( (insn & 0x0ff000f0) == 0x03f000f0 )
+		return INSN_REJECTED;
+
+	/* USADA8 : cccc 0111 1000 xxxx xxxx xxxx 0001 xxxx :
+	 * USAD8  : cccc 0111 1000 xxxx 1111 xxxx 0001 xxxx :
+	 */
+
+	if ( (insn & 0x0ff000f0) == 0x07800010 )
+		 return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi);
+
+	/* SMLALD : cccc 0111 0100 xxxx xxxx xxxx 00x1 xxxx :
+	 * SMLSLD : cccc 0111 0100 xxxx xxxx xxxx 01x1 xxxx :
+	 */
+
+	if ( (insn & 0x0ff00090) == 0x07400010 )
+		return prep_emulate_rdhi16rdlo12rs8rm0_wflags(insn, asi);
+
+	/* SMLAD  : cccc 0111 0000 xxxx xxxx xxxx 00x1 xxxx :Q
+	 * SMLSD  : cccc 0111 0000 xxxx xxxx xxxx 01x1 xxxx :Q
+	 * SMMLA  : cccc 0111 0101 xxxx xxxx xxxx 00x1 xxxx :
+	 * SMMLS  : cccc 0111 0101 xxxx xxxx xxxx 11x1 xxxx :
+	 */
+
+	if ( (insn & 0x0ff00090) == 0x07000010 ||
+	     (insn & 0x0ff000d0) == 0x07500010 ||
+	     (insn & 0x0ff000d0) == 0x075000d0 )
+		return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi);
+
+	/* SMUSD  : cccc 0111 0000 xxxx xxxx xxxx 01x1 xxxx :
+	 * SMUAD  : cccc 0111 0000 xxxx 1111 xxxx 00x1 xxxx :Q
+	 * SMMUL  : cccc 0111 0101 xxxx 1111 xxxx 00x1 xxxx :
+	 */
+
+	return prep_emulate_rd16rs8rm0_wflags(insn, asi);
+}
+
+
+static enum kprobe_insn __kprobes space_cccc_01xx(kprobe_opcode_t insn,
+					struct arch_specific_insn *asi)
+{
+	/* LDR   : cccc 01xx x0x1 xxxx xxxx xxxx xxxx xxxx :
+	 * LDRB  : cccc 01xx x1x1 xxxx xxxx xxxx xxxx xxxx :
+	 * LDRBT : cccc 01x0 x111 xxxx xxxx xxxx xxxx xxxx :
+	 * LDRT  : cccc 01x0 x011 xxxx xxxx xxxx xxxx xxxx :
+	 * STR   : cccc 01xx x0x0 xxxx xxxx xxxx xxxx xxxx :
+	 * STRB  : cccc 01xx x1x0 xxxx xxxx xxxx xxxx xxxx :
+	 * STRBT : cccc 01x0 x110 xxxx xxxx xxxx xxxx xxxx :
+	 * STRT  : cccc 01x0 x010 xxxx xxxx xxxx xxxx xxxx :
+	 */
+
+	return prep_emulate_ldr_str(insn, asi);
+}
+
+
+static enum kprobe_insn __kprobes space_cccc_100x(kprobe_opcode_t insn,
+						struct arch_specific_insn *asi)
+{
+	/* LDM(2) : cccc 100x x101 xxxx 0xxx xxxx xxxx xxxx
+	 * LDM(3) : cccc 100x x1x1 xxxx 1xxx xxxx xxxx xxxx
+	 */
+
+	if ( (insn & 0x0e708000) == 0x85000000 ||
+	     (insn & 0x0e508000) == 0x85010000 )
+		return INSN_REJECTED;
+
+	/* LDM(1) : cccc 100x x0x1 xxxx xxxx xxxx xxxx xxxx :
+	 * STM(1) : cccc 100x x0x0 xxxx xxxx xxxx xxxx xxxx :
+	 */
+
+	asi->insn[0]      = truecc_insn(insn);
+	asi->insn_handler = ((insn & 0x108000) == 0x008000) ? /* STM & R15 */
+				simulate_stm1_pc : simulate_ldm1stm1;
+
+	return INSN_GOOD;
+}
+
+
+static enum kprobe_insn __kprobes space_cccc_101x(kprobe_opcode_t insn,
+						struct arch_specific_insn *asi)
+{
+	/* B  : cccc 1010 xxxx xxxx xxxx xxxx xxxx xxxx :
+	 * BL : cccc 1011 xxxx xxxx xxxx xxxx xxxx xxxx :
+	 */
+
+	asi->insn[0]      = truecc_insn(insn);
+	asi->insn_handler = simulate_bbl;
+
+	return INSN_GOOD;
+}
+
+
+static enum kprobe_insn __kprobes space_cccc_1100_010x(kprobe_opcode_t insn,
+						struct arch_specific_insn *asi)
+{
+	/* MCRR : cccc 1100 0100 xxxx xxxx xxxx xxxx xxxx : (Rd!=Rn)
+	 * MRRC : cccc 1100 0101 xxxx xxxx xxxx xxxx xxxx : (Rd!=Rn)
+	 */
+	insn &= 0xfff00fff;
+	insn |= 0x00001000;      /* Rn = r0, Rd = r1 */
+	asi->insn[0]      = insn;
+	asi->insn_handler = (insn & (1 << 20)) ? emulate_mrrc : emulate_mcrr;
+	return INSN_GOOD;
+}
+
+
+static enum kprobe_insn __kprobes space_cccc_110x(kprobe_opcode_t insn,
+						struct arch_specific_insn *asi)
+{
+	/* LDC : cccc 110x xxx1 xxxx xxxx xxxx xxxx xxxx :
+	 * STC : cccc 110x xxx0 xxxx xxxx xxxx xxxx xxxx :
+	 */
+	insn &= 0xfff0ffff;      /* Rn = r0 */
+	asi->insn[0]      = insn;
+	asi->insn_handler = emulate_ldcstc;
+
+	return INSN_GOOD;
+}
+
+
+static enum kprobe_insn __kprobes space_cccc_111x(kprobe_opcode_t insn,
+						struct arch_specific_insn *asi)
+{
+	/* BKPT : 1110 0001 0010 xxxx xxxx xxxx 0111 xxxx
+	 * SWI  : cccc 1111 xxxx xxxx xxxx xxxx xxxx xxxx
+	 */
+	if ( (insn & 0xfff000f0) == 0xe1200070 ||
+	     (insn & 0x0f000000) == 0x0f000000 )
+		return INSN_REJECTED;
+
+	/* CDP : cccc 1110 xxxx xxxx xxxx xxxx xxx0 xxxx : */
+	if ( (insn & 0x0f000010) == 0x0e000000 ) {
+		asi->insn[0]      = insn;
+		asi->insn_handler = emulate_none;
+		return INSN_GOOD;
+	}
+
+	/* MCR : cccc 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx :
+	 * MRC : cccc 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx :
+	 */
+	insn &= 0xffff0fff;	/* Rd = r0 */
+	asi->insn[0]      = insn;
+	asi->insn_handler = (insn & (1 << 20)) ? emulate_rd12 : emulate_ird12;
+	return INSN_GOOD;
+}
+
+
+/* Return:
+ *   INSN_REJECTED     If instruction is one not allowed to kprobe,
+ *   INSN_GOOD         If instruction is supported and uses instruction slot,
+ *   INSN_GOOD_NO_SLOT If instruction is supported but doesn't use its slot.
+ *
+ * For instructions we don't want to kprobe (INSN_REJECTED return result):
+ *   These are generally ones that modify the processor state making
+ *   them "hard" to simulate such as switches processor modes or
+ *   make accesses in alternate modes.  Any of these could be simulated
+ *   if the work was put into it, but low return considering they
+ *   should also be very rare.
+ */
+
+enum kprobe_insn __kprobes kprobe_decode_insn(kprobe_opcode_t insn,
+					struct arch_specific_insn *asi)
+{
+	asi->insn[1] = KPROBE_RETURN_INSTRUCTION;
+
+	if ( (insn & 0xf0000000) == 0xf0000000 ) {
+
+		return space_1111(insn, asi);
+
+	} else if ( (insn & 0x0e000000) == 0x00000000 ) {
+
+		return space_cccc_000x(insn, asi);
+
+	} else if ( (insn & 0x0e000000) == 0x02000000 ) {
+
+		return space_cccc_001x(insn, asi);
+
+	} else if ( (insn & 0x0f000010) == 0x06000010 ) {
+
+		return space_cccc_0110__1(insn, asi);
+
+	} else if ( (insn & 0x0f000010) == 0x07000010 ) {
+
+		return space_cccc_0111__1(insn, asi);
+
+	} else if ( (insn & 0x0c000000) == 0x04000000 ) {
+
+		return space_cccc_01xx(insn, asi);
+
+	} else if ( (insn & 0x0e000000) == 0x08000000 ) {
+
+		return space_cccc_100x(insn, asi);
+
+	} else if ( (insn & 0x0e000000) == 0x0a000000 ) {
+
+		return space_cccc_101x(insn, asi);
+
+	} else if ( (insn & 0x0fe00000) == 0x0c400000 ) {
+
+		return space_cccc_1100_010x(insn, asi);
+
+	} else if ( (insn & 0x0e000000) == 0x0c400000 ) {
+
+		return space_cccc_110x(insn, asi);
+
+	}
+
+	return space_cccc_111x(insn, asi);
+}
+
+
+/*
+ * All ARM instructions listed below.
+ *
+ * Instructions and their general purpose registers are given.
+ * If a particular register may not use R15, it it prefixed with a "!".
+ * If it is parked with a "*" means the value returned by readering R15
+ * is implementation defined.
+ *
+ * ADC/ADD/AND/BIC/CMN/CMP/EOR/MOV/MVN/ORR/RSB/RSC/SBC/SUB/TEQ
+ *     TST: Rd, Rn, Rm, !Rs
+ * BX: Rm
+ * BLX(2): !Rm
+ * BX: Rm (R15 legal, but discouraged)
+ * BXJ: !Rm,
+ * CLZ: !Rd, !Rm
+ * CPY: Rd, Rm
+ * LDC/2,STC/2 immediate offset & unindex: Rn
+ * LDC/2,STC/2 immediate pre/post-indexed: !Rn
+ * LDM(1/3): !Rn, register_list
+ * LDM(2): !Rn, !register_list
+ * LDR,STR,PLD immediate offset: Rd, Rn
+ * LDR,STR,PLD register offset: Rd, Rn, !Rm
+ * LDR,STR,PLD scaled register offset: Rd, !Rn, !Rm
+ * LDR,STR immediate pre/post-indexed: Rd, !Rn
+ * LDR,STR register pre/post-indexed: Rd, !Rn, !Rm
+ * LDR,STR scaled register pre/post-indexed: Rd, !Rn, !Rm
+ * LDRB,STRB immediate offset: !Rd, Rn
+ * LDRB,STRB register offset: !Rd, Rn, !Rm
+ * LDRB,STRB scaled register offset: !Rd, !Rn, !Rm
+ * LDRB,STRB immediate pre/post-indexed: !Rd, !Rn
+ * LDRB,STRB register pre/post-indexed: !Rd, !Rn, !Rm
+ * LDRB,STRB scaled register pre/post-indexed: !Rd, !Rn, !Rm
+ * LDRT,LDRBT,STRBT immediate pre/post-indexed: !Rd, !Rn
+ * LDRT,LDRBT,STRBT register pre/post-indexed: !Rd, !Rn, !Rm
+ * LDRT,LDRBT,STRBT scaled register pre/post-indexed: !Rd, !Rn, !Rm
+ * LDRH/SH/SB/D,STRH/SH/SB/D immediate offset: !Rd, Rn
+ * LDRH/SH/SB/D,STRH/SH/SB/D register offset: !Rd, Rn, !Rm
+ * LDRH/SH/SB/D,STRH/SH/SB/D immediate pre/post-indexed: !Rd, !Rn
+ * LDRH/SH/SB/D,STRH/SH/SB/D register pre/post-indexed: !Rd, !Rn, !Rm
+ * LDREX: !Rd, !Rn
+ * MCR/2: !Rd
+ * MCRR/2,MRRC/2: !Rd, !Rn
+ * MLA: !Rd, !Rn, !Rm, !Rs
+ * MOV: Rd
+ * MRC/2: !Rd (if Rd==15, only changes cond codes, not the register)
+ * MRS,MSR: !Rd
+ * MUL: !Rd, !Rm, !Rs
+ * PKH{BT,TB}: !Rd, !Rn, !Rm
+ * QDADD,[U]QADD/16/8/SUBX: !Rd, !Rm, !Rn
+ * QDSUB,[U]QSUB/16/8/ADDX: !Rd, !Rm, !Rn
+ * REV/16/SH: !Rd, !Rm
+ * RFE: !Rn
+ * {S,U}[H]ADD{16,8,SUBX},{S,U}[H]SUB{16,8,ADDX}: !Rd, !Rn, !Rm
+ * SEL: !Rd, !Rn, !Rm
+ * SMLA<x><y>,SMLA{D,W<y>},SMLSD,SMML{A,S}: !Rd, !Rn, !Rm, !Rs
+ * SMLAL<x><y>,SMLA{D,LD},SMLSLD,SMMULL,SMULW<y>: !RdHi, !RdLo, !Rm, !Rs
+ * SMMUL,SMUAD,SMUL<x><y>,SMUSD: !Rd, !Rm, !Rs
+ * SSAT/16: !Rd, !Rm
+ * STM(1/2): !Rn, register_list* (R15 in reg list not recommended)
+ * STRT immediate pre/post-indexed: Rd*, !Rn
+ * STRT register pre/post-indexed: Rd*, !Rn, !Rm
+ * STRT scaled register pre/post-indexed: Rd*, !Rn, !Rm
+ * STREX: !Rd, !Rn, !Rm
+ * SWP/B: !Rd, !Rn, !Rm
+ * {S,U}XTA{B,B16,H}: !Rd, !Rn, !Rm
+ * {S,U}XT{B,B16,H}: !Rd, !Rm
+ * UM{AA,LA,UL}L: !RdHi, !RdLo, !Rm, !Rs
+ * USA{D8,A8,T,T16}: !Rd, !Rm, !Rs
+ *
+ * May transfer control by writing R15 (possible mode changes or alternate
+ * mode accesses marked by "*"):
+ * ALU op (* with s-bit), B, BL, BKPT, BLX(1/2), BX, BXJ, CPS*, CPY,
+ * LDM(1), LDM(2/3)*, LDR, MOV, RFE*, SWI*
+ *
+ * Instructions that do not take general registers, nor transfer control:
+ * CDP/2, SETEND, SRS*
+ */
diff -uprN linux-2.6.21.4/arch/arm/kernel/Makefile linux/arch/arm/kernel/Makefile
--- linux-2.6.21.4/arch/arm/kernel/Makefile	2007-05-07 18:16:40.000000000 -0500
+++ linux/arch/arm/kernel/Makefile	2007-06-09 18:07:06.000000000 -0500
@@ -19,6 +19,7 @@ obj-$(CONFIG_ISA_DMA)		+= dma-isa.o
 obj-$(CONFIG_PCI)		+= bios32.o isa.o
 obj-$(CONFIG_SMP)		+= smp.o
 obj-$(CONFIG_KEXEC)		+= machine_kexec.o relocate_kernel.o
+obj-$(CONFIG_KPROBES)		+= kprobes.o kprobes-decode.o
 obj-$(CONFIG_OABI_COMPAT)	+= sys_oabi-compat.o
 
 obj-$(CONFIG_CRUNCH)		+= crunch.o crunch-bits.o
diff -uprN linux-2.6.21.4/arch/arm/kernel/traps.c linux/arch/arm/kernel/traps.c
--- linux-2.6.21.4/arch/arm/kernel/traps.c	2007-06-09 17:34:00.000000000 -0500
+++ linux/arch/arm/kernel/traps.c	2007-06-09 18:07:06.000000000 -0500
@@ -20,6 +20,7 @@
 #include <linux/kallsyms.h>
 #include <linux/delay.h>
 #include <linux/init.h>
+#include <linux/kprobes.h>
 
 #include <asm/atomic.h>
 #include <asm/cacheflush.h>
@@ -266,7 +267,7 @@ void unregister_undef_hook(struct undef_
 	spin_unlock_irqrestore(&undef_lock, flags);
 }
 
-asmlinkage void do_undefinstr(struct pt_regs *regs)
+asmlinkage void __kprobes do_undefinstr(struct pt_regs *regs)
 {
 	unsigned int correction = thumb_mode(regs) ? 2 : 4;
 	unsigned int instr;
@@ -282,6 +283,19 @@ asmlinkage void do_undefinstr(struct pt_
 	 */
 	regs->ARM_pc -= correction;
 
+#ifdef CONFIG_KPROBES
+        /*
+         * Redirect kernel mode undef exceptions to kprobes.
+         */
+	if (!user_mode(regs) && !thumb_mode(regs)) {
+		if ( (*(unsigned long *)regs->ARM_pc ==
+			KPROBE_BREAKPOINT_INSTRUCTION) &&
+			kprobe_handler(regs) ) {
+				return;
+		}
+	}
+#endif /* CONFIG_KPROBES */
+
 	pc = (void __user *)instruction_pointer(regs);
 
 	if (processor_mode(regs) == SVC_MODE) {
diff -uprN linux-2.6.21.4/arch/arm/kernel/vmlinux.lds.S linux/arch/arm/kernel/vmlinux.lds.S
--- linux-2.6.21.4/arch/arm/kernel/vmlinux.lds.S	2007-05-07 18:16:40.000000000 -0500
+++ linux/arch/arm/kernel/vmlinux.lds.S	2007-06-09 18:07:06.000000000 -0500
@@ -86,6 +86,7 @@ SECTIONS
 			*(.text)
 			SCHED_TEXT
 			LOCK_TEXT
+			KPROBES_TEXT
 #ifdef CONFIG_MMU
 			*(.fixup)
 #endif
diff -uprN linux-2.6.21.4/arch/arm/mm/fault.c linux/arch/arm/mm/fault.c
--- linux-2.6.21.4/arch/arm/mm/fault.c	2007-03-25 13:40:55.000000000 -0500
+++ linux/arch/arm/mm/fault.c	2007-06-11 14:19:40.000000000 -0500
@@ -13,11 +13,13 @@
 #include <linux/ptrace.h>
 #include <linux/mm.h>
 #include <linux/init.h>
+#include <linux/kprobes.h>
 
 #include <asm/system.h>
 #include <asm/pgtable.h>
 #include <asm/tlbflush.h>
 #include <asm/uaccess.h>
+#include <asm/kdebug.h>
 
 #include "fault.h"
 
@@ -73,6 +75,44 @@ void show_pte(struct mm_struct *mm, unsi
 	printk("\n");
 }
 
+#ifdef CONFIG_KPROBES
+ATOMIC_NOTIFIER_HEAD(notify_page_fault_chain);
+
+/* Hook to register for page fault notifications */
+int register_page_fault_notifier(struct notifier_block *nb)
+{
+	return atomic_notifier_chain_register(&notify_page_fault_chain, nb);
+}
+
+int unregister_page_fault_notifier(struct notifier_block *nb)
+{
+	return atomic_notifier_chain_unregister(&notify_page_fault_chain, nb);
+}
+
+static inline int __kprobes notify_page_fault(enum die_val val, const char *str,
+					unsigned long addr,
+					struct pt_regs *regs,
+					unsigned int fsr)
+{
+	struct die_args args = {
+		.regs = regs,
+		.str  = str,
+		.addr = addr,
+		.fsr  = fsr
+	};
+
+	return atomic_notifier_call_chain(&notify_page_fault_chain, val, &args);
+}
+#else
+static inline int notify_page_fault(enum die_val val, const char *str,
+					unsigned long addr,
+					struct pt_regs *regs,
+					unsigned int fsr)
+{
+	return NOTIFY_DONE;
+}
+#endif
+
 /*
  * Oops.  The kernel tried to access some page that wasn't present.
  */
@@ -216,7 +256,7 @@ out:
 	return fault;
 }
 
-static int
+static int __kprobes
 do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
 {
 	struct task_struct *tsk;
@@ -226,6 +266,10 @@ do_page_fault(unsigned long addr, unsign
 	tsk = current;
 	mm  = tsk->mm;
 
+	if (notify_page_fault(DIE_PAGE_FAULT, "page fault", addr, regs,
+				fsr) == NOTIFY_STOP)
+		return 0;
+
 	/*
 	 * If we're in an interrupt or have no user
 	 * context, we must not take the fault..
@@ -316,7 +360,7 @@ no_context:
  * interrupt or a critical region, and should only copy the information
  * from the master page table, nothing more.
  */
-static int
+static int __kprobes
 do_translation_fault(unsigned long addr, unsigned int fsr,
 		     struct pt_regs *regs)
 {
@@ -438,7 +482,7 @@ hook_fault_code(int nr, int (*fn)(unsign
 /*
  * Dispatch a data abort to the relevant handler.
  */
-asmlinkage void
+asmlinkage void __kprobes
 do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
 {
 	const struct fsr_info *inf = fsr_info + (fsr & 15) + ((fsr & (1 << 10)) >> 6);
@@ -457,7 +501,7 @@ do_DataAbort(unsigned long addr, unsigne
 	notify_die("", regs, &info, fsr, 0);
 }
 
-asmlinkage void
+asmlinkage void __kprobes
 do_PrefetchAbort(unsigned long addr, struct pt_regs *regs)
 {
 	do_translation_fault(addr, 0, regs);
diff -uprN linux-2.6.21.4/Documentation/kprobes.txt linux/Documentation/kprobes.txt
--- linux-2.6.21.4/Documentation/kprobes.txt	2007-01-01 12:23:48.000000000 -0600
+++ linux/Documentation/kprobes.txt	2007-06-09 18:07:06.000000000 -0500
@@ -140,6 +140,7 @@ architectures:
 - ppc64
 - ia64 (Does not support probes on instruction slot1.)
 - sparc64 (Return probes not yet implemented.)
+- arm
 
 3. Configuring Kprobes
 
diff -uprN linux-2.6.21.4/include/asm-arm/kdebug.h linux/include/asm-arm/kdebug.h
--- linux-2.6.21.4/include/asm-arm/kdebug.h	1969-12-31 18:00:00.000000000 -0600
+++ linux/include/asm-arm/kdebug.h	2007-06-09 18:07:06.000000000 -0500
@@ -0,0 +1,35 @@
+#ifndef _ARM_KDEBUG_H
+#define _ARM_KDEBUG_H
+
+#include <linux/notifier.h>
+
+struct pt_regs;
+
+struct die_args {
+	struct pt_regs	*regs;
+	const char	*str;
+	unsigned long	addr;
+	unsigned int	fsr;
+};
+
+extern int register_page_fault_notifier(struct notifier_block *);
+extern int unregister_page_fault_notifier(struct notifier_block *);
+
+extern void notify_die(const char *str, struct pt_regs *regs,
+			struct siginfo *info, unsigned long err,
+			unsigned long trap);
+
+enum die_val {
+	DIE_TRANS_FAULT = 1,
+	DIE_PAGE_FAULT
+};
+
+
+/* Die notifier kernel feature only used by kprobes.  We don't use it
+ * for kprobes on ARM.  Disable it with this dummy function. */
+static inline int register_die_notifier(struct notifier_block *nb)
+{
+	return 0;
+}
+
+#endif /* _ARM_KDEBUG_H */
diff -uprN linux-2.6.21.4/include/asm-arm/kprobes.h linux/include/asm-arm/kprobes.h
--- linux-2.6.21.4/include/asm-arm/kprobes.h	1969-12-31 18:00:00.000000000 -0600
+++ linux/include/asm-arm/kprobes.h	2007-06-09 18:07:06.000000000 -0500
@@ -0,0 +1,84 @@
+#ifndef _ARM_KPROBES_H
+#define _ARM_KPROBES_H
+
+/* include/asm-arm/kprobes.h
+ *
+ * Copyright (C) 2006, 2007 Motorola Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/percpu.h>
+#include <linux/ptrace.h>
+#include <asm/cacheflush.h>
+
+#define ARCH_SUPPORTS_KRETPROBES
+#define __ARCH_WANT_KPROBES_INSN_SLOT
+#define MAX_INSN_SIZE		   2
+#define ARCH_INACTIVE_KPROBE_COUNT 0
+
+#define flush_insn_slot(p)	do { } while (0)
+
+#define MAX_STACK_SIZE          64 /* Probably 32 should suffice */
+#define MIN_STACK_SIZE(addr) \
+			min( (unsigned long)MAX_STACK_SIZE, \
+			     (unsigned long)current_thread_info() + \
+			     THREAD_START_SP - (addr) )
+
+/* This undefined instruction must be unique and
+ * reserved solely for kprobes' use. */
+#define KPROBE_BREAKPOINT_INSTRUCTION	0xe7f001f8
+
+#define KPROBE_RETURN_INSTRUCTION	0xe1a0f00e    /* mov pc, lr */
+
+#define JPROBE_ENTRY(pentry)		((kprobe_opcode_t *)(pentry))
+
+enum kprobe_insn {
+	INSN_REJECTED,
+	INSN_GOOD,
+	INSN_GOOD_NO_SLOT
+};
+
+typedef u32 kprobe_opcode_t;
+
+struct kprobe;
+typedef void (kprobe_insn_handler_t)(struct kprobe *, struct pt_regs *);
+
+/* Architecture specific copy of original instruction. */
+struct arch_specific_insn {
+	kprobe_opcode_t		*insn;
+	kprobe_insn_handler_t	*insn_handler;
+};
+
+struct prev_kprobe {
+	struct kprobe *kp;
+	unsigned int status;
+};
+
+
+/* per-cpu kprobe control block */
+struct kprobe_ctlblk {
+	unsigned int kprobe_status;
+	struct prev_kprobe prev_kprobe;
+	struct pt_regs jprobe_saved_regs;
+	char jprobes_stack[MAX_STACK_SIZE];
+};
+
+extern void arch_flush_insn_slot(struct kprobe *);
+extern int kprobe_exceptions_notify(struct notifier_block *self,
+                    unsigned long val, void *data);
+extern void arch_remove_kprobe(struct kprobe *);
+extern enum kprobe_insn kprobe_decode_insn(kprobe_opcode_t,
+						struct arch_specific_insn *);
+extern int kprobe_handler(struct pt_regs *regs);
+
+#endif /* _ARM_KPROBES_H */
diff -uprN linux-2.6.21.4/include/asm-arm/ptrace.h linux/include/asm-arm/ptrace.h
--- linux-2.6.21.4/include/asm-arm/ptrace.h	2006-10-14 18:39:18.000000000 -0500
+++ linux/include/asm-arm/ptrace.h	2007-06-09 18:07:06.000000000 -0500
@@ -144,6 +144,8 @@ static inline int valid_user_regs(struct
 #define instruction_pointer(regs) \
 	(pc_pointer((regs)->ARM_pc))
 
+#define regs_return_value(regs) ((regs)->ARM_r0)
+
 #ifdef CONFIG_SMP
 extern unsigned long profile_pc(struct pt_regs *regs);
 #else