All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/9] uprobes: Add uprobes support for ARM
@ 2013-08-01 23:45 ` David Long
  0 siblings, 0 replies; 32+ messages in thread
From: David Long @ 2013-08-01 23:45 UTC (permalink / raw)
  To: linux-arm-kernel; +Cc: Rabin Vincent, Jon Medhurst (Tixy), linux-kernel

From: "David A. Long" <dave.long@linaro.org>

This patch series adds basic uprobes support to ARM. It is based on patches
developed earlier by Rabin Vincent.  That approach of adding hooks into
the kprobes instruction parsing code was not well received.  This approach
separates the ARM instruction parsing code in kprobes out into a separate set
of functions which can be used by both kprobes and uprobes. Both kprobes and
uprobes then provide their own semantic action tables to process the results of
the parsing.

These patches are based on v3.11-rc3

David A. Long (6):
  uprobes: move function declarations out of arch
  uprobes: add arch write opcode hook
  ARM: move shared uprobe/kprobe definitions into new include file
  ARM: Move uprobes/kprobes shared functions to common file
  ARM: Add "action" table for kprobes/uprobes instruction
  ARM: add uprobes support

Rabin Vincent (3):
  uprobes: allow ignoring of probe hits
  uprobes: allow arch access to xol slot
  uprobes: allow arch-specific initialization

 arch/arm/Kconfig                   |   4 +
 arch/arm/include/asm/kprobes.h     |  17 +-
 arch/arm/include/asm/probes.h      |  23 ++
 arch/arm/include/asm/ptrace.h      |   6 +
 arch/arm/include/asm/thread_info.h |   5 +-
 arch/arm/include/asm/uprobes.h     |  34 +++
 arch/arm/kernel/Makefile           |   3 +-
 arch/arm/kernel/kprobes-arm.c      | 476 ++++++++-----------------------------
 arch/arm/kernel/kprobes-common.c   | 269 +--------------------
 arch/arm/kernel/kprobes-thumb.c    | 217 +++++++++++------
 arch/arm/kernel/kprobes.c          |  11 +-
 arch/arm/kernel/kprobes.h          |  64 ++---
 arch/arm/kernel/probes-arm.c       | 311 ++++++++++++++++++++++++
 arch/arm/kernel/probes-arm.h       |  60 +++++
 arch/arm/kernel/probes-thumb.h     |  59 +++++
 arch/arm/kernel/probes.c           | 325 +++++++++++++++++++++++++
 arch/arm/kernel/probes.h           |  35 +++
 arch/arm/kernel/signal.c           |   4 +
 arch/arm/kernel/uprobes-arm.c      | 221 +++++++++++++++++
 arch/arm/kernel/uprobes.c          | 203 ++++++++++++++++
 arch/arm/kernel/uprobes.h          |  25 ++
 arch/powerpc/include/asm/uprobes.h |   1 -
 arch/x86/include/asm/uprobes.h     |   7 -
 include/linux/uprobes.h            |  17 ++
 kernel/events/uprobes.c            |  58 ++++-
 25 files changed, 1668 insertions(+), 787 deletions(-)
 create mode 100644 arch/arm/include/asm/probes.h
 create mode 100644 arch/arm/include/asm/uprobes.h
 create mode 100644 arch/arm/kernel/probes-arm.c
 create mode 100644 arch/arm/kernel/probes-arm.h
 create mode 100644 arch/arm/kernel/probes-thumb.h
 create mode 100644 arch/arm/kernel/probes.c
 create mode 100644 arch/arm/kernel/probes.h
 create mode 100644 arch/arm/kernel/uprobes-arm.c
 create mode 100644 arch/arm/kernel/uprobes.c
 create mode 100644 arch/arm/kernel/uprobes.h

-- 
1.8.1.2


^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH 0/9] uprobes: Add uprobes support for ARM
@ 2013-08-01 23:45 ` David Long
  0 siblings, 0 replies; 32+ messages in thread
From: David Long @ 2013-08-01 23:45 UTC (permalink / raw)
  To: linux-arm-kernel

From: "David A. Long" <dave.long@linaro.org>

This patch series adds basic uprobes support to ARM. It is based on patches
developed earlier by Rabin Vincent.  That approach of adding hooks into
the kprobes instruction parsing code was not well received.  This approach
separates the ARM instruction parsing code in kprobes out into a separate set
of functions which can be used by both kprobes and uprobes. Both kprobes and
uprobes then provide their own semantic action tables to process the results of
the parsing.

These patches are based on v3.11-rc3

David A. Long (6):
  uprobes: move function declarations out of arch
  uprobes: add arch write opcode hook
  ARM: move shared uprobe/kprobe definitions into new include file
  ARM: Move uprobes/kprobes shared functions to common file
  ARM: Add "action" table for kprobes/uprobes instruction
  ARM: add uprobes support

Rabin Vincent (3):
  uprobes: allow ignoring of probe hits
  uprobes: allow arch access to xol slot
  uprobes: allow arch-specific initialization

 arch/arm/Kconfig                   |   4 +
 arch/arm/include/asm/kprobes.h     |  17 +-
 arch/arm/include/asm/probes.h      |  23 ++
 arch/arm/include/asm/ptrace.h      |   6 +
 arch/arm/include/asm/thread_info.h |   5 +-
 arch/arm/include/asm/uprobes.h     |  34 +++
 arch/arm/kernel/Makefile           |   3 +-
 arch/arm/kernel/kprobes-arm.c      | 476 ++++++++-----------------------------
 arch/arm/kernel/kprobes-common.c   | 269 +--------------------
 arch/arm/kernel/kprobes-thumb.c    | 217 +++++++++++------
 arch/arm/kernel/kprobes.c          |  11 +-
 arch/arm/kernel/kprobes.h          |  64 ++---
 arch/arm/kernel/probes-arm.c       | 311 ++++++++++++++++++++++++
 arch/arm/kernel/probes-arm.h       |  60 +++++
 arch/arm/kernel/probes-thumb.h     |  59 +++++
 arch/arm/kernel/probes.c           | 325 +++++++++++++++++++++++++
 arch/arm/kernel/probes.h           |  35 +++
 arch/arm/kernel/signal.c           |   4 +
 arch/arm/kernel/uprobes-arm.c      | 221 +++++++++++++++++
 arch/arm/kernel/uprobes.c          | 203 ++++++++++++++++
 arch/arm/kernel/uprobes.h          |  25 ++
 arch/powerpc/include/asm/uprobes.h |   1 -
 arch/x86/include/asm/uprobes.h     |   7 -
 include/linux/uprobes.h            |  17 ++
 kernel/events/uprobes.c            |  58 ++++-
 25 files changed, 1668 insertions(+), 787 deletions(-)
 create mode 100644 arch/arm/include/asm/probes.h
 create mode 100644 arch/arm/include/asm/uprobes.h
 create mode 100644 arch/arm/kernel/probes-arm.c
 create mode 100644 arch/arm/kernel/probes-arm.h
 create mode 100644 arch/arm/kernel/probes-thumb.h
 create mode 100644 arch/arm/kernel/probes.c
 create mode 100644 arch/arm/kernel/probes.h
 create mode 100644 arch/arm/kernel/uprobes-arm.c
 create mode 100644 arch/arm/kernel/uprobes.c
 create mode 100644 arch/arm/kernel/uprobes.h

-- 
1.8.1.2

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH 1/9] uprobes: move function declarations out of arch
  2013-08-01 23:45 ` David Long
@ 2013-08-01 23:45   ` David Long
  -1 siblings, 0 replies; 32+ messages in thread
From: David Long @ 2013-08-01 23:45 UTC (permalink / raw)
  To: linux-arm-kernel; +Cc: Rabin Vincent, Jon Medhurst (Tixy), linux-kernel

From: "David A. Long" <dave.long@linaro.org>

Move the function declarations from the arch header to the common
header, since only the function bodies are architecture-specific.
These changes are from Vincent Rabin's uprobes patch.

Signed-off-by: Rabin Vincent <rabin@rab.in>
Signed-off-by: David A. Long <dave.long@linaro.org>
---
 arch/x86/include/asm/uprobes.h | 7 -------
 include/linux/uprobes.h        | 8 ++++++++
 2 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/arch/x86/include/asm/uprobes.h b/arch/x86/include/asm/uprobes.h
index 6e51979..b20b4d6 100644
--- a/arch/x86/include/asm/uprobes.h
+++ b/arch/x86/include/asm/uprobes.h
@@ -49,11 +49,4 @@ struct arch_uprobe_task {
 	unsigned int			saved_tf;
 };
 
-extern int  arch_uprobe_analyze_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long addr);
-extern int  arch_uprobe_pre_xol(struct arch_uprobe *aup, struct pt_regs *regs);
-extern int  arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs);
-extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk);
-extern int  arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data);
-extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs);
-extern unsigned long arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr, struct pt_regs *regs);
 #endif	/* _ASM_UPROBES_H */
diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h
index 06f28be..4e3b847 100644
--- a/include/linux/uprobes.h
+++ b/include/linux/uprobes.h
@@ -30,6 +30,7 @@
 struct vm_area_struct;
 struct mm_struct;
 struct inode;
+struct notifier_block;
 
 #ifdef CONFIG_ARCH_SUPPORTS_UPROBES
 # include <asm/uprobes.h>
@@ -125,6 +126,13 @@ extern void uprobe_notify_resume(struct pt_regs *regs);
 extern bool uprobe_deny_signal(void);
 extern bool __weak arch_uprobe_skip_sstep(struct arch_uprobe *aup, struct pt_regs *regs);
 extern void uprobe_clear_state(struct mm_struct *mm);
+extern void uprobe_reset_state(struct mm_struct *mm);
+extern int  arch_uprobe_analyze_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long addr);
+extern int  arch_uprobe_pre_xol(struct arch_uprobe *aup, struct pt_regs *regs);
+extern int  arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs);
+extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk);
+extern int  arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data);
+extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs);
 #else /* !CONFIG_UPROBES */
 struct uprobes_state {
 };
-- 
1.8.1.2


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 1/9] uprobes: move function declarations out of arch
@ 2013-08-01 23:45   ` David Long
  0 siblings, 0 replies; 32+ messages in thread
From: David Long @ 2013-08-01 23:45 UTC (permalink / raw)
  To: linux-arm-kernel

From: "David A. Long" <dave.long@linaro.org>

Move the function declarations from the arch header to the common
header, since only the function bodies are architecture-specific.
These changes are from Vincent Rabin's uprobes patch.

Signed-off-by: Rabin Vincent <rabin@rab.in>
Signed-off-by: David A. Long <dave.long@linaro.org>
---
 arch/x86/include/asm/uprobes.h | 7 -------
 include/linux/uprobes.h        | 8 ++++++++
 2 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/arch/x86/include/asm/uprobes.h b/arch/x86/include/asm/uprobes.h
index 6e51979..b20b4d6 100644
--- a/arch/x86/include/asm/uprobes.h
+++ b/arch/x86/include/asm/uprobes.h
@@ -49,11 +49,4 @@ struct arch_uprobe_task {
 	unsigned int			saved_tf;
 };
 
-extern int  arch_uprobe_analyze_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long addr);
-extern int  arch_uprobe_pre_xol(struct arch_uprobe *aup, struct pt_regs *regs);
-extern int  arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs);
-extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk);
-extern int  arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data);
-extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs);
-extern unsigned long arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr, struct pt_regs *regs);
 #endif	/* _ASM_UPROBES_H */
diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h
index 06f28be..4e3b847 100644
--- a/include/linux/uprobes.h
+++ b/include/linux/uprobes.h
@@ -30,6 +30,7 @@
 struct vm_area_struct;
 struct mm_struct;
 struct inode;
+struct notifier_block;
 
 #ifdef CONFIG_ARCH_SUPPORTS_UPROBES
 # include <asm/uprobes.h>
@@ -125,6 +126,13 @@ extern void uprobe_notify_resume(struct pt_regs *regs);
 extern bool uprobe_deny_signal(void);
 extern bool __weak arch_uprobe_skip_sstep(struct arch_uprobe *aup, struct pt_regs *regs);
 extern void uprobe_clear_state(struct mm_struct *mm);
+extern void uprobe_reset_state(struct mm_struct *mm);
+extern int  arch_uprobe_analyze_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long addr);
+extern int  arch_uprobe_pre_xol(struct arch_uprobe *aup, struct pt_regs *regs);
+extern int  arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs);
+extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk);
+extern int  arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data);
+extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs);
 #else /* !CONFIG_UPROBES */
 struct uprobes_state {
 };
-- 
1.8.1.2

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 2/9] uprobes: allow ignoring of probe hits
  2013-08-01 23:45 ` David Long
@ 2013-08-01 23:45   ` David Long
  -1 siblings, 0 replies; 32+ messages in thread
From: David Long @ 2013-08-01 23:45 UTC (permalink / raw)
  To: linux-arm-kernel; +Cc: Rabin Vincent, Jon Medhurst (Tixy), linux-kernel

From: Rabin Vincent <rabin@rab.in>

Allow arches to decided to ignore a probe hit.  ARM will use this to
only call handlers if the conditions to execute a conditionally executed
instruction are satisfied.

Upleveled for v3.10-rc7.

Signed-off-by: David A. Long <dave.long@linaro.org>
---
 include/linux/uprobes.h |  1 +
 kernel/events/uprobes.c | 17 ++++++++++++-----
 2 files changed, 13 insertions(+), 5 deletions(-)

diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h
index 4e3b847..7a7c035 100644
--- a/include/linux/uprobes.h
+++ b/include/linux/uprobes.h
@@ -133,6 +133,7 @@ extern int  arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs);
 extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk);
 extern int  arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data);
 extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs);
+extern bool __weak arch_uprobe_ignore(struct arch_uprobe *aup, struct pt_regs *regs);
 #else /* !CONFIG_UPROBES */
 struct uprobes_state {
 };
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index f356974..2f3a4cb 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -1734,9 +1734,6 @@ static void handle_swbp(struct pt_regs *regs)
 		return;
 	}
 
-	/* change it in advance for ->handler() and restart */
-	instruction_pointer_set(regs, bp_vaddr);
-
 	/*
 	 * TODO: move copy_insn/etc into _register and remove this hack.
 	 * After we hit the bp, _unregister + _register can install the
@@ -1744,16 +1741,26 @@ static void handle_swbp(struct pt_regs *regs)
 	 */
 	smp_rmb(); /* pairs with wmb() in install_breakpoint() */
 	if (unlikely(!test_bit(UPROBE_COPY_INSN, &uprobe->flags)))
-		goto out;
+		goto restart;
 
 	handler_chain(uprobe, regs);
+
+	if (arch_uprobe_ignore(&uprobe->arch, regs))
+		goto out;
+
 	if (can_skip_sstep(uprobe, regs))
 		goto out;
 
 	if (!pre_ssout(uprobe, regs, bp_vaddr))
 		return;
 
-	/* can_skip_sstep() succeeded, or restart if can't singlestep */
+restart:
+	/*
+	 * cannot singlestep; cannot skip instruction;
+	 * re-execute the instruction.
+	 */
+	instruction_pointer_set(regs, bp_vaddr);
+
 out:
 	put_uprobe(uprobe);
 }
-- 
1.8.1.2


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 2/9] uprobes: allow ignoring of probe hits
@ 2013-08-01 23:45   ` David Long
  0 siblings, 0 replies; 32+ messages in thread
From: David Long @ 2013-08-01 23:45 UTC (permalink / raw)
  To: linux-arm-kernel

From: Rabin Vincent <rabin@rab.in>

Allow arches to decided to ignore a probe hit.  ARM will use this to
only call handlers if the conditions to execute a conditionally executed
instruction are satisfied.

Upleveled for v3.10-rc7.

Signed-off-by: David A. Long <dave.long@linaro.org>
---
 include/linux/uprobes.h |  1 +
 kernel/events/uprobes.c | 17 ++++++++++++-----
 2 files changed, 13 insertions(+), 5 deletions(-)

diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h
index 4e3b847..7a7c035 100644
--- a/include/linux/uprobes.h
+++ b/include/linux/uprobes.h
@@ -133,6 +133,7 @@ extern int  arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs);
 extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk);
 extern int  arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data);
 extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs);
+extern bool __weak arch_uprobe_ignore(struct arch_uprobe *aup, struct pt_regs *regs);
 #else /* !CONFIG_UPROBES */
 struct uprobes_state {
 };
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index f356974..2f3a4cb 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -1734,9 +1734,6 @@ static void handle_swbp(struct pt_regs *regs)
 		return;
 	}
 
-	/* change it in advance for ->handler() and restart */
-	instruction_pointer_set(regs, bp_vaddr);
-
 	/*
 	 * TODO: move copy_insn/etc into _register and remove this hack.
 	 * After we hit the bp, _unregister + _register can install the
@@ -1744,16 +1741,26 @@ static void handle_swbp(struct pt_regs *regs)
 	 */
 	smp_rmb(); /* pairs with wmb() in install_breakpoint() */
 	if (unlikely(!test_bit(UPROBE_COPY_INSN, &uprobe->flags)))
-		goto out;
+		goto restart;
 
 	handler_chain(uprobe, regs);
+
+	if (arch_uprobe_ignore(&uprobe->arch, regs))
+		goto out;
+
 	if (can_skip_sstep(uprobe, regs))
 		goto out;
 
 	if (!pre_ssout(uprobe, regs, bp_vaddr))
 		return;
 
-	/* can_skip_sstep() succeeded, or restart if can't singlestep */
+restart:
+	/*
+	 * cannot singlestep; cannot skip instruction;
+	 * re-execute the instruction.
+	 */
+	instruction_pointer_set(regs, bp_vaddr);
+
 out:
 	put_uprobe(uprobe);
 }
-- 
1.8.1.2

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 3/9] uprobes: allow arch access to xol slot
  2013-08-01 23:45 ` David Long
@ 2013-08-01 23:45   ` David Long
  -1 siblings, 0 replies; 32+ messages in thread
From: David Long @ 2013-08-01 23:45 UTC (permalink / raw)
  To: linux-arm-kernel; +Cc: Rabin Vincent, Jon Medhurst (Tixy), linux-kernel

From: Rabin Vincent <rabin@rab.in>

Allow arches to customize how the instruction is filled into the xol
slot.  ARM will use this to insert an undefined instruction after the
real instruction in order to simulate a single step of the instruction
without hardware support.

Signed-off-by: Rabin Vincent <rabin@rab.in>
Signed-off-by: David A. Long <dave.long@linaro.org>
---
 include/linux/uprobes.h |  1 +
 kernel/events/uprobes.c | 10 +++++++++-
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h
index 7a7c035..f6287f0 100644
--- a/include/linux/uprobes.h
+++ b/include/linux/uprobes.h
@@ -134,6 +134,7 @@ extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk);
 extern int  arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data);
 extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs);
 extern bool __weak arch_uprobe_ignore(struct arch_uprobe *aup, struct pt_regs *regs);
+extern void __weak arch_uprobe_xol_copy(struct arch_uprobe *auprobe, void *vaddr);
 #else /* !CONFIG_UPROBES */
 struct uprobes_state {
 };
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 2f3a4cb..64471bd 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -1238,6 +1238,11 @@ static unsigned long xol_take_insn_slot(struct xol_area *area)
 	return slot_addr;
 }
 
+void __weak arch_uprobe_xol_copy(struct arch_uprobe *auprobe, void *vaddr)
+{
+	memcpy(vaddr, auprobe->insn, MAX_UINSN_BYTES);
+}
+
 /*
  * xol_get_insn_slot - allocate a slot for xol.
  * Returns the allocated slot address or 0.
@@ -1246,6 +1251,7 @@ static unsigned long xol_get_insn_slot(struct uprobe *uprobe)
 {
 	struct xol_area *area;
 	unsigned long xol_vaddr;
+	void *kaddr;
 
 	area = get_xol_area();
 	if (!area)
@@ -1256,7 +1262,9 @@ static unsigned long xol_get_insn_slot(struct uprobe *uprobe)
 		return 0;
 
 	/* Initialize the slot */
-	copy_to_page(area->page, xol_vaddr, uprobe->arch.insn, MAX_UINSN_BYTES);
+	kaddr = kmap_atomic(area->page);
+	arch_uprobe_xol_copy(&uprobe->arch, kaddr + (xol_vaddr & ~PAGE_MASK));
+	kunmap_atomic(kaddr);
 	/*
 	 * We probably need flush_icache_user_range() but it needs vma.
 	 * This should work on supported architectures too.
-- 
1.8.1.2


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 3/9] uprobes: allow arch access to xol slot
@ 2013-08-01 23:45   ` David Long
  0 siblings, 0 replies; 32+ messages in thread
From: David Long @ 2013-08-01 23:45 UTC (permalink / raw)
  To: linux-arm-kernel

From: Rabin Vincent <rabin@rab.in>

Allow arches to customize how the instruction is filled into the xol
slot.  ARM will use this to insert an undefined instruction after the
real instruction in order to simulate a single step of the instruction
without hardware support.

Signed-off-by: Rabin Vincent <rabin@rab.in>
Signed-off-by: David A. Long <dave.long@linaro.org>
---
 include/linux/uprobes.h |  1 +
 kernel/events/uprobes.c | 10 +++++++++-
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h
index 7a7c035..f6287f0 100644
--- a/include/linux/uprobes.h
+++ b/include/linux/uprobes.h
@@ -134,6 +134,7 @@ extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk);
 extern int  arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data);
 extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs);
 extern bool __weak arch_uprobe_ignore(struct arch_uprobe *aup, struct pt_regs *regs);
+extern void __weak arch_uprobe_xol_copy(struct arch_uprobe *auprobe, void *vaddr);
 #else /* !CONFIG_UPROBES */
 struct uprobes_state {
 };
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 2f3a4cb..64471bd 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -1238,6 +1238,11 @@ static unsigned long xol_take_insn_slot(struct xol_area *area)
 	return slot_addr;
 }
 
+void __weak arch_uprobe_xol_copy(struct arch_uprobe *auprobe, void *vaddr)
+{
+	memcpy(vaddr, auprobe->insn, MAX_UINSN_BYTES);
+}
+
 /*
  * xol_get_insn_slot - allocate a slot for xol.
  * Returns the allocated slot address or 0.
@@ -1246,6 +1251,7 @@ static unsigned long xol_get_insn_slot(struct uprobe *uprobe)
 {
 	struct xol_area *area;
 	unsigned long xol_vaddr;
+	void *kaddr;
 
 	area = get_xol_area();
 	if (!area)
@@ -1256,7 +1262,9 @@ static unsigned long xol_get_insn_slot(struct uprobe *uprobe)
 		return 0;
 
 	/* Initialize the slot */
-	copy_to_page(area->page, xol_vaddr, uprobe->arch.insn, MAX_UINSN_BYTES);
+	kaddr = kmap_atomic(area->page);
+	arch_uprobe_xol_copy(&uprobe->arch, kaddr + (xol_vaddr & ~PAGE_MASK));
+	kunmap_atomic(kaddr);
 	/*
 	 * We probably need flush_icache_user_range() but it needs vma.
 	 * This should work on supported architectures too.
-- 
1.8.1.2

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 4/9] uprobes: allow arch-specific initialization
  2013-08-01 23:45 ` David Long
@ 2013-08-01 23:45   ` David Long
  -1 siblings, 0 replies; 32+ messages in thread
From: David Long @ 2013-08-01 23:45 UTC (permalink / raw)
  To: linux-arm-kernel; +Cc: Rabin Vincent, Jon Medhurst (Tixy), linux-kernel

From: Rabin Vincent <rabin@rab.in>

Add a weak function for any architecture-specific initialization.  ARM
will use this to register the handlers for the undefined instructions it
uses to implement uprobes.

Signed-off-by: Rabin Vincent <rabin@rab.in>
Signed-off-by: David A. Long <dave.long@linaro.org>
---
 include/linux/uprobes.h |  1 +
 kernel/events/uprobes.c | 10 ++++++++++
 2 files changed, 11 insertions(+)

diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h
index f6287f0..6feae5d 100644
--- a/include/linux/uprobes.h
+++ b/include/linux/uprobes.h
@@ -135,6 +135,7 @@ extern int  arch_uprobe_exception_notify(struct notifier_block *self, unsigned l
 extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs);
 extern bool __weak arch_uprobe_ignore(struct arch_uprobe *aup, struct pt_regs *regs);
 extern void __weak arch_uprobe_xol_copy(struct arch_uprobe *auprobe, void *vaddr);
+extern int __weak arch_uprobes_init(void);
 #else /* !CONFIG_UPROBES */
 struct uprobes_state {
 };
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 64471bd..5004f6f 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -1862,8 +1862,14 @@ static struct notifier_block uprobe_exception_nb = {
 	.priority		= INT_MAX-1,	/* notified after kprobes, kgdb */
 };
 
+int __weak __init arch_uprobes_init(void)
+{
+	return 0;
+}
+
 static int __init init_uprobes(void)
 {
+	int ret;
 	int i;
 
 	for (i = 0; i < UPROBES_HASH_SZ; i++)
@@ -1872,6 +1878,10 @@ static int __init init_uprobes(void)
 	if (percpu_init_rwsem(&dup_mmap_sem))
 		return -ENOMEM;
 
+	ret = arch_uprobes_init();
+	if (ret)
+		return ret;
+
 	return register_die_notifier(&uprobe_exception_nb);
 }
 module_init(init_uprobes);
-- 
1.8.1.2


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 4/9] uprobes: allow arch-specific initialization
@ 2013-08-01 23:45   ` David Long
  0 siblings, 0 replies; 32+ messages in thread
From: David Long @ 2013-08-01 23:45 UTC (permalink / raw)
  To: linux-arm-kernel

From: Rabin Vincent <rabin@rab.in>

Add a weak function for any architecture-specific initialization.  ARM
will use this to register the handlers for the undefined instructions it
uses to implement uprobes.

Signed-off-by: Rabin Vincent <rabin@rab.in>
Signed-off-by: David A. Long <dave.long@linaro.org>
---
 include/linux/uprobes.h |  1 +
 kernel/events/uprobes.c | 10 ++++++++++
 2 files changed, 11 insertions(+)

diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h
index f6287f0..6feae5d 100644
--- a/include/linux/uprobes.h
+++ b/include/linux/uprobes.h
@@ -135,6 +135,7 @@ extern int  arch_uprobe_exception_notify(struct notifier_block *self, unsigned l
 extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs);
 extern bool __weak arch_uprobe_ignore(struct arch_uprobe *aup, struct pt_regs *regs);
 extern void __weak arch_uprobe_xol_copy(struct arch_uprobe *auprobe, void *vaddr);
+extern int __weak arch_uprobes_init(void);
 #else /* !CONFIG_UPROBES */
 struct uprobes_state {
 };
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 64471bd..5004f6f 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -1862,8 +1862,14 @@ static struct notifier_block uprobe_exception_nb = {
 	.priority		= INT_MAX-1,	/* notified after kprobes, kgdb */
 };
 
+int __weak __init arch_uprobes_init(void)
+{
+	return 0;
+}
+
 static int __init init_uprobes(void)
 {
+	int ret;
 	int i;
 
 	for (i = 0; i < UPROBES_HASH_SZ; i++)
@@ -1872,6 +1878,10 @@ static int __init init_uprobes(void)
 	if (percpu_init_rwsem(&dup_mmap_sem))
 		return -ENOMEM;
 
+	ret = arch_uprobes_init();
+	if (ret)
+		return ret;
+
 	return register_die_notifier(&uprobe_exception_nb);
 }
 module_init(init_uprobes);
-- 
1.8.1.2

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 5/9] uprobes: add arch write opcode hook
  2013-08-01 23:45 ` David Long
@ 2013-08-01 23:45   ` David Long
  -1 siblings, 0 replies; 32+ messages in thread
From: David Long @ 2013-08-01 23:45 UTC (permalink / raw)
  To: linux-arm-kernel; +Cc: Rabin Vincent, Jon Medhurst (Tixy), linux-kernel

From: "David A. Long" <dave.long@linaro.org>

Allow arches to write the opcode with a custom function.  ARM needs to
customize the swbp instruction depending on the condition code of the
instruction it replaces.

Signed-off-by: Rabin Vincent <rabin@rab.in>
Signed-off-by: David A. Long <dave.long@linaro.org>
---
 include/linux/uprobes.h |  3 +++
 kernel/events/uprobes.c | 20 +++++++++++++++-----
 2 files changed, 18 insertions(+), 5 deletions(-)

diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h
index 6feae5d..9cd3b25 100644
--- a/include/linux/uprobes.h
+++ b/include/linux/uprobes.h
@@ -136,6 +136,9 @@ extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs)
 extern bool __weak arch_uprobe_ignore(struct arch_uprobe *aup, struct pt_regs *regs);
 extern void __weak arch_uprobe_xol_copy(struct arch_uprobe *auprobe, void *vaddr);
 extern int __weak arch_uprobes_init(void);
+extern void __weak arch_uprobe_write_opcode(struct arch_uprobe *auprobe,
+					    void *vaddr,
+					    uprobe_opcode_t opcode);
 #else /* !CONFIG_UPROBES */
 struct uprobes_state {
 };
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 5004f6f..6a60eec 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -248,6 +248,12 @@ static int verify_opcode(struct page *page, unsigned long vaddr, uprobe_opcode_t
  * have fixed length instructions.
  */
 
+void __weak arch_uprobe_write_opcode(struct arch_uprobe *auprobe, void *vaddr,
+				     uprobe_opcode_t opcode)
+{
+	memcpy(vaddr, &opcode, UPROBE_SWBP_INSN_SIZE);
+}
+
 /*
  * write_opcode - write the opcode at a given virtual address.
  * @mm: the probed process address space.
@@ -260,8 +266,8 @@ static int verify_opcode(struct page *page, unsigned long vaddr, uprobe_opcode_t
  * For mm @mm, write the opcode at @vaddr.
  * Return 0 (success) or a negative errno.
  */
-static int write_opcode(struct mm_struct *mm, unsigned long vaddr,
-			uprobe_opcode_t opcode)
+static int write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm,
+			unsigned long vaddr, uprobe_opcode_t opcode)
 {
 	struct page *old_page, *new_page;
 	struct vm_area_struct *vma;
@@ -285,7 +291,10 @@ retry:
 	__SetPageUptodate(new_page);
 
 	copy_highpage(new_page, old_page);
-	copy_to_page(new_page, vaddr, &opcode, UPROBE_SWBP_INSN_SIZE);
+	vaddr_new = kmap_atomic(new_page);
+	arch_uprobe_write_opcode(auprobe, vaddr_new + (vaddr & ~PAGE_MASK),
+				 opcode);
+	kunmap_atomic(vaddr_new);
 
 	ret = anon_vma_prepare(vma);
 	if (ret)
@@ -314,7 +323,7 @@ put_old:
  */
 int __weak set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr)
 {
-	return write_opcode(mm, vaddr, UPROBE_SWBP_INSN);
+	return write_opcode(auprobe, mm, vaddr, UPROBE_SWBP_INSN);
 }
 
 /**
@@ -329,7 +338,8 @@ int __weak set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned
 int __weak
 set_orig_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr)
 {
-	return write_opcode(mm, vaddr, *(uprobe_opcode_t *)auprobe->insn);
+	return write_opcode(auprobe, mm, vaddr,
+			    *(uprobe_opcode_t *)auprobe->insn);
 }
 
 static int match_uprobe(struct uprobe *l, struct uprobe *r)
-- 
1.8.1.2


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 5/9] uprobes: add arch write opcode hook
@ 2013-08-01 23:45   ` David Long
  0 siblings, 0 replies; 32+ messages in thread
From: David Long @ 2013-08-01 23:45 UTC (permalink / raw)
  To: linux-arm-kernel

From: "David A. Long" <dave.long@linaro.org>

Allow arches to write the opcode with a custom function.  ARM needs to
customize the swbp instruction depending on the condition code of the
instruction it replaces.

Signed-off-by: Rabin Vincent <rabin@rab.in>
Signed-off-by: David A. Long <dave.long@linaro.org>
---
 include/linux/uprobes.h |  3 +++
 kernel/events/uprobes.c | 20 +++++++++++++++-----
 2 files changed, 18 insertions(+), 5 deletions(-)

diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h
index 6feae5d..9cd3b25 100644
--- a/include/linux/uprobes.h
+++ b/include/linux/uprobes.h
@@ -136,6 +136,9 @@ extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs)
 extern bool __weak arch_uprobe_ignore(struct arch_uprobe *aup, struct pt_regs *regs);
 extern void __weak arch_uprobe_xol_copy(struct arch_uprobe *auprobe, void *vaddr);
 extern int __weak arch_uprobes_init(void);
+extern void __weak arch_uprobe_write_opcode(struct arch_uprobe *auprobe,
+					    void *vaddr,
+					    uprobe_opcode_t opcode);
 #else /* !CONFIG_UPROBES */
 struct uprobes_state {
 };
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 5004f6f..6a60eec 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -248,6 +248,12 @@ static int verify_opcode(struct page *page, unsigned long vaddr, uprobe_opcode_t
  * have fixed length instructions.
  */
 
+void __weak arch_uprobe_write_opcode(struct arch_uprobe *auprobe, void *vaddr,
+				     uprobe_opcode_t opcode)
+{
+	memcpy(vaddr, &opcode, UPROBE_SWBP_INSN_SIZE);
+}
+
 /*
  * write_opcode - write the opcode at a given virtual address.
  * @mm: the probed process address space.
@@ -260,8 +266,8 @@ static int verify_opcode(struct page *page, unsigned long vaddr, uprobe_opcode_t
  * For mm @mm, write the opcode at @vaddr.
  * Return 0 (success) or a negative errno.
  */
-static int write_opcode(struct mm_struct *mm, unsigned long vaddr,
-			uprobe_opcode_t opcode)
+static int write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm,
+			unsigned long vaddr, uprobe_opcode_t opcode)
 {
 	struct page *old_page, *new_page;
 	struct vm_area_struct *vma;
@@ -285,7 +291,10 @@ retry:
 	__SetPageUptodate(new_page);
 
 	copy_highpage(new_page, old_page);
-	copy_to_page(new_page, vaddr, &opcode, UPROBE_SWBP_INSN_SIZE);
+	vaddr_new = kmap_atomic(new_page);
+	arch_uprobe_write_opcode(auprobe, vaddr_new + (vaddr & ~PAGE_MASK),
+				 opcode);
+	kunmap_atomic(vaddr_new);
 
 	ret = anon_vma_prepare(vma);
 	if (ret)
@@ -314,7 +323,7 @@ put_old:
  */
 int __weak set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr)
 {
-	return write_opcode(mm, vaddr, UPROBE_SWBP_INSN);
+	return write_opcode(auprobe, mm, vaddr, UPROBE_SWBP_INSN);
 }
 
 /**
@@ -329,7 +338,8 @@ int __weak set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned
 int __weak
 set_orig_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr)
 {
-	return write_opcode(mm, vaddr, *(uprobe_opcode_t *)auprobe->insn);
+	return write_opcode(auprobe, mm, vaddr,
+			    *(uprobe_opcode_t *)auprobe->insn);
 }
 
 static int match_uprobe(struct uprobe *l, struct uprobe *r)
-- 
1.8.1.2

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 6/9] ARM: move shared uprobe/kprobe definitions into new include file
  2013-08-01 23:45 ` David Long
@ 2013-08-01 23:45   ` David Long
  -1 siblings, 0 replies; 32+ messages in thread
From: David Long @ 2013-08-01 23:45 UTC (permalink / raw)
  To: linux-arm-kernel; +Cc: Rabin Vincent, Jon Medhurst (Tixy), linux-kernel

From: "David A. Long" <dave.long@linaro.org>

Separate the kprobe-only definitions from the definitions needed by
both kprobes and uprobes.

Signed-off-by: David A. Long <dave.long@linaro.org>
---
 arch/arm/include/asm/kprobes.h | 17 +----------------
 arch/arm/include/asm/probes.h  | 23 +++++++++++++++++++++++
 2 files changed, 24 insertions(+), 16 deletions(-)
 create mode 100644 arch/arm/include/asm/probes.h

diff --git a/arch/arm/include/asm/kprobes.h b/arch/arm/include/asm/kprobes.h
index f82ec22..53b1a80 100644
--- a/arch/arm/include/asm/kprobes.h
+++ b/arch/arm/include/asm/kprobes.h
@@ -27,22 +27,7 @@
 #define flush_insn_slot(p)		do { } while (0)
 #define kretprobe_blacklist_size	0
 
-typedef u32 kprobe_opcode_t;
-
-struct kprobe;
-typedef void (kprobe_insn_handler_t)(struct kprobe *, struct pt_regs *);
-typedef unsigned long (kprobe_check_cc)(unsigned long);
-typedef void (kprobe_insn_singlestep_t)(struct kprobe *, struct pt_regs *);
-typedef void (kprobe_insn_fn_t)(void);
-
-/* Architecture specific copy of original instruction. */
-struct arch_specific_insn {
-	kprobe_opcode_t			*insn;
-	kprobe_insn_handler_t		*insn_handler;
-	kprobe_check_cc			*insn_check_cc;
-	kprobe_insn_singlestep_t	*insn_singlestep;
-	kprobe_insn_fn_t		*insn_fn;
-};
+#include <asm/probes.h>
 
 struct prev_kprobe {
 	struct kprobe *kp;
diff --git a/arch/arm/include/asm/probes.h b/arch/arm/include/asm/probes.h
new file mode 100644
index 0000000..df46994
--- /dev/null
+++ b/arch/arm/include/asm/probes.h
@@ -0,0 +1,23 @@
+#ifndef _ASM_PROBES_H
+#define _ASM_PROBES_H
+
+#ifdef CONFIG_KPROBES
+typedef u32 kprobe_opcode_t;
+
+struct kprobe;
+typedef void (kprobe_insn_handler_t)(struct kprobe *, struct pt_regs *);
+typedef unsigned long (kprobe_check_cc)(unsigned long);
+typedef void (kprobe_insn_singlestep_t)(struct kprobe *, struct pt_regs *);
+typedef void (kprobe_insn_fn_t)(void);
+
+/* Architecture specific copy of original instruction. */
+struct arch_specific_insn {
+	kprobe_opcode_t			*insn;
+	kprobe_insn_handler_t		*insn_handler;
+	kprobe_check_cc			*insn_check_cc;
+	kprobe_insn_singlestep_t	*insn_singlestep;
+	kprobe_insn_fn_t		*insn_fn;
+};
+#endif
+
+#endif
-- 
1.8.1.2


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 6/9] ARM: move shared uprobe/kprobe definitions into new include file
@ 2013-08-01 23:45   ` David Long
  0 siblings, 0 replies; 32+ messages in thread
From: David Long @ 2013-08-01 23:45 UTC (permalink / raw)
  To: linux-arm-kernel

From: "David A. Long" <dave.long@linaro.org>

Separate the kprobe-only definitions from the definitions needed by
both kprobes and uprobes.

Signed-off-by: David A. Long <dave.long@linaro.org>
---
 arch/arm/include/asm/kprobes.h | 17 +----------------
 arch/arm/include/asm/probes.h  | 23 +++++++++++++++++++++++
 2 files changed, 24 insertions(+), 16 deletions(-)
 create mode 100644 arch/arm/include/asm/probes.h

diff --git a/arch/arm/include/asm/kprobes.h b/arch/arm/include/asm/kprobes.h
index f82ec22..53b1a80 100644
--- a/arch/arm/include/asm/kprobes.h
+++ b/arch/arm/include/asm/kprobes.h
@@ -27,22 +27,7 @@
 #define flush_insn_slot(p)		do { } while (0)
 #define kretprobe_blacklist_size	0
 
-typedef u32 kprobe_opcode_t;
-
-struct kprobe;
-typedef void (kprobe_insn_handler_t)(struct kprobe *, struct pt_regs *);
-typedef unsigned long (kprobe_check_cc)(unsigned long);
-typedef void (kprobe_insn_singlestep_t)(struct kprobe *, struct pt_regs *);
-typedef void (kprobe_insn_fn_t)(void);
-
-/* Architecture specific copy of original instruction. */
-struct arch_specific_insn {
-	kprobe_opcode_t			*insn;
-	kprobe_insn_handler_t		*insn_handler;
-	kprobe_check_cc			*insn_check_cc;
-	kprobe_insn_singlestep_t	*insn_singlestep;
-	kprobe_insn_fn_t		*insn_fn;
-};
+#include <asm/probes.h>
 
 struct prev_kprobe {
 	struct kprobe *kp;
diff --git a/arch/arm/include/asm/probes.h b/arch/arm/include/asm/probes.h
new file mode 100644
index 0000000..df46994
--- /dev/null
+++ b/arch/arm/include/asm/probes.h
@@ -0,0 +1,23 @@
+#ifndef _ASM_PROBES_H
+#define _ASM_PROBES_H
+
+#ifdef CONFIG_KPROBES
+typedef u32 kprobe_opcode_t;
+
+struct kprobe;
+typedef void (kprobe_insn_handler_t)(struct kprobe *, struct pt_regs *);
+typedef unsigned long (kprobe_check_cc)(unsigned long);
+typedef void (kprobe_insn_singlestep_t)(struct kprobe *, struct pt_regs *);
+typedef void (kprobe_insn_fn_t)(void);
+
+/* Architecture specific copy of original instruction. */
+struct arch_specific_insn {
+	kprobe_opcode_t			*insn;
+	kprobe_insn_handler_t		*insn_handler;
+	kprobe_check_cc			*insn_check_cc;
+	kprobe_insn_singlestep_t	*insn_singlestep;
+	kprobe_insn_fn_t		*insn_fn;
+};
+#endif
+
+#endif
-- 
1.8.1.2

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 7/9] ARM: Move uprobes/kprobes shared functions to common file
  2013-08-01 23:45 ` David Long
@ 2013-08-01 23:45   ` David Long
  -1 siblings, 0 replies; 32+ messages in thread
From: David Long @ 2013-08-01 23:45 UTC (permalink / raw)
  To: linux-arm-kernel; +Cc: Rabin Vincent, Jon Medhurst (Tixy), linux-kernel

From: "David A. Long" <dave.long@linaro.org>

Separate the kprobe-only functions from the functions needed by
both kprobes and uprobes.

Signed-off-by: David A. Long <dave.long@linaro.org>
---
 arch/arm/kernel/Makefile         |   2 +-
 arch/arm/kernel/kprobes-arm.c    | 298 +------------------------------------
 arch/arm/kernel/kprobes-common.c | 266 ---------------------------------
 arch/arm/kernel/kprobes-thumb.c  |  14 +-
 arch/arm/kernel/kprobes.h        |   4 +-
 arch/arm/kernel/probes-arm.c     | 311 +++++++++++++++++++++++++++++++++++++++
 arch/arm/kernel/probes.c         | 286 +++++++++++++++++++++++++++++++++++
 arch/arm/kernel/probes.h         |  23 +++
 8 files changed, 635 insertions(+), 569 deletions(-)
 create mode 100644 arch/arm/kernel/probes-arm.c
 create mode 100644 arch/arm/kernel/probes.c
 create mode 100644 arch/arm/kernel/probes.h

diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index 86d10dd..3292023 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -49,7 +49,7 @@ obj-$(CONFIG_DYNAMIC_FTRACE)	+= ftrace.o insn.o
 obj-$(CONFIG_FUNCTION_GRAPH_TRACER)	+= ftrace.o insn.o
 obj-$(CONFIG_JUMP_LABEL)	+= jump_label.o insn.o patch.o
 obj-$(CONFIG_KEXEC)		+= machine_kexec.o relocate_kernel.o
-obj-$(CONFIG_KPROBES)		+= kprobes.o kprobes-common.o patch.o
+obj-$(CONFIG_KPROBES)		+= probes.o probes-arm.o kprobes.o kprobes-common.o patch.o
 ifdef CONFIG_THUMB2_KERNEL
 obj-$(CONFIG_KPROBES)		+= kprobes-thumb.o
 else
diff --git a/arch/arm/kernel/kprobes-arm.c b/arch/arm/kernel/kprobes-arm.c
index 8a30c89..d6503cc 100644
--- a/arch/arm/kernel/kprobes-arm.c
+++ b/arch/arm/kernel/kprobes-arm.c
@@ -62,19 +62,9 @@
 #include <linux/kprobes.h>
 #include <linux/module.h>
 
+#include "probes.h"
 #include "kprobes.h"
 
-#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
-
-#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25)
-
-#if  __LINUX_ARM_ARCH__ >= 6
-#define BLX(reg)	"blx	"reg"		\n\t"
-#else
-#define BLX(reg)	"mov	lr, pc		\n\t"	\
-			"mov	pc, "reg"	\n\t"
-#endif
-
 /*
  * To avoid the complications of mimicing single-stepping on a
  * processor without a Next-PC or a single-step mode, and to
@@ -105,284 +95,6 @@
  * read and write of flags.
  */
 
-static void __kprobes simulate_bbl(struct kprobe *p, struct pt_regs *regs)
-{
-	kprobe_opcode_t insn = p->opcode;
-	long iaddr = (long)p->addr;
-	int disp  = branch_displacement(insn);
-
-	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)
-{
-	kprobe_opcode_t insn = p->opcode;
-	int rm = insn & 0xf;
-	long rmv = regs->uregs[rm];
-
-	if (insn & (1 << 5))
-		regs->ARM_lr = (long)p->addr + 4;
-
-	regs->ARM_pc = rmv & ~0x1;
-	regs->ARM_cpsr &= ~PSR_T_BIT;
-	if (rmv & 0x1)
-		regs->ARM_cpsr |= PSR_T_BIT;
-}
-
-static void __kprobes simulate_mrs(struct kprobe *p, struct pt_regs *regs)
-{
-	kprobe_opcode_t insn = p->opcode;
-	int rd = (insn >> 12) & 0xf;
-	unsigned long mask = 0xf8ff03df; /* Mask out execution state */
-	regs->uregs[rd] = regs->ARM_cpsr & mask;
-}
-
-static void __kprobes simulate_mov_ipsp(struct kprobe *p, struct pt_regs *regs)
-{
-	regs->uregs[12] = regs->uregs[13];
-}
-
-static void __kprobes
-emulate_ldrdstrd(struct kprobe *p, struct pt_regs *regs)
-{
-	kprobe_opcode_t insn = p->opcode;
-	unsigned long pc = (unsigned long)p->addr + 8;
-	int rt = (insn >> 12) & 0xf;
-	int rn = (insn >> 16) & 0xf;
-	int rm = insn & 0xf;
-
-	register unsigned long rtv asm("r0") = regs->uregs[rt];
-	register unsigned long rt2v asm("r1") = regs->uregs[rt+1];
-	register unsigned long rnv asm("r2") = (rn == 15) ? pc
-							  : regs->uregs[rn];
-	register unsigned long rmv asm("r3") = regs->uregs[rm];
-
-	__asm__ __volatile__ (
-		BLX("%[fn]")
-		: "=r" (rtv), "=r" (rt2v), "=r" (rnv)
-		: "0" (rtv), "1" (rt2v), "2" (rnv), "r" (rmv),
-		  [fn] "r" (p->ainsn.insn_fn)
-		: "lr", "memory", "cc"
-	);
-
-	regs->uregs[rt] = rtv;
-	regs->uregs[rt+1] = rt2v;
-	if (is_writeback(insn))
-		regs->uregs[rn] = rnv;
-}
-
-static void __kprobes
-emulate_ldr(struct kprobe *p, struct pt_regs *regs)
-{
-	kprobe_opcode_t insn = p->opcode;
-	unsigned long pc = (unsigned long)p->addr + 8;
-	int rt = (insn >> 12) & 0xf;
-	int rn = (insn >> 16) & 0xf;
-	int rm = insn & 0xf;
-
-	register unsigned long rtv asm("r0");
-	register unsigned long rnv asm("r2") = (rn == 15) ? pc
-							  : regs->uregs[rn];
-	register unsigned long rmv asm("r3") = regs->uregs[rm];
-
-	__asm__ __volatile__ (
-		BLX("%[fn]")
-		: "=r" (rtv), "=r" (rnv)
-		: "1" (rnv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
-		: "lr", "memory", "cc"
-	);
-
-	if (rt == 15)
-		load_write_pc(rtv, regs);
-	else
-		regs->uregs[rt] = rtv;
-
-	if (is_writeback(insn))
-		regs->uregs[rn] = rnv;
-}
-
-static void __kprobes
-emulate_str(struct kprobe *p, struct pt_regs *regs)
-{
-	kprobe_opcode_t insn = p->opcode;
-	unsigned long rtpc = (unsigned long)p->addr + str_pc_offset;
-	unsigned long rnpc = (unsigned long)p->addr + 8;
-	int rt = (insn >> 12) & 0xf;
-	int rn = (insn >> 16) & 0xf;
-	int rm = insn & 0xf;
-
-	register unsigned long rtv asm("r0") = (rt == 15) ? rtpc
-							  : regs->uregs[rt];
-	register unsigned long rnv asm("r2") = (rn == 15) ? rnpc
-							  : regs->uregs[rn];
-	register unsigned long rmv asm("r3") = regs->uregs[rm];
-
-	__asm__ __volatile__ (
-		BLX("%[fn]")
-		: "=r" (rnv)
-		: "r" (rtv), "0" (rnv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
-		: "lr", "memory", "cc"
-	);
-
-	if (is_writeback(insn))
-		regs->uregs[rn] = rnv;
-}
-
-static void __kprobes
-emulate_rd12rn16rm0rs8_rwflags(struct kprobe *p, struct pt_regs *regs)
-{
-	kprobe_opcode_t insn = p->opcode;
-	unsigned long pc = (unsigned long)p->addr + 8;
-	int rd = (insn >> 12) & 0xf;
-	int rn = (insn >> 16) & 0xf;
-	int rm = insn & 0xf;
-	int rs = (insn >> 8) & 0xf;
-
-	register unsigned long rdv asm("r0") = regs->uregs[rd];
-	register unsigned long rnv asm("r2") = (rn == 15) ? pc
-							  : regs->uregs[rn];
-	register unsigned long rmv asm("r3") = (rm == 15) ? pc
-							  : regs->uregs[rm];
-	register unsigned long rsv asm("r1") = regs->uregs[rs];
-	unsigned long cpsr = regs->ARM_cpsr;
-
-	__asm__ __volatile__ (
-		"msr	cpsr_fs, %[cpsr]	\n\t"
-		BLX("%[fn]")
-		"mrs	%[cpsr], cpsr		\n\t"
-		: "=r" (rdv), [cpsr] "=r" (cpsr)
-		: "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv),
-		  "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
-		: "lr", "memory", "cc"
-	);
-
-	if (rd == 15)
-		alu_write_pc(rdv, regs);
-	else
-		regs->uregs[rd] = rdv;
-	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
-}
-
-static void __kprobes
-emulate_rd12rn16rm0_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
-{
-	kprobe_opcode_t insn = p->opcode;
-	int rd = (insn >> 12) & 0xf;
-	int rn = (insn >> 16) & 0xf;
-	int rm = insn & 0xf;
-
-	register unsigned long rdv asm("r0") = regs->uregs[rd];
-	register unsigned long rnv asm("r2") = regs->uregs[rn];
-	register unsigned long rmv asm("r3") = regs->uregs[rm];
-	unsigned long cpsr = regs->ARM_cpsr;
-
-	__asm__ __volatile__ (
-		"msr	cpsr_fs, %[cpsr]	\n\t"
-		BLX("%[fn]")
-		"mrs	%[cpsr], cpsr		\n\t"
-		: "=r" (rdv), [cpsr] "=r" (cpsr)
-		: "0" (rdv), "r" (rnv), "r" (rmv),
-		  "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
-		: "lr", "memory", "cc"
-	);
-
-	regs->uregs[rd] = rdv;
-	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
-}
-
-static void __kprobes
-emulate_rd16rn12rm0rs8_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
-{
-	kprobe_opcode_t insn = p->opcode;
-	int rd = (insn >> 16) & 0xf;
-	int rn = (insn >> 12) & 0xf;
-	int rm = insn & 0xf;
-	int rs = (insn >> 8) & 0xf;
-
-	register unsigned long rdv asm("r2") = regs->uregs[rd];
-	register unsigned long rnv asm("r0") = regs->uregs[rn];
-	register unsigned long rmv asm("r3") = regs->uregs[rm];
-	register unsigned long rsv asm("r1") = regs->uregs[rs];
-	unsigned long cpsr = regs->ARM_cpsr;
-
-	__asm__ __volatile__ (
-		"msr	cpsr_fs, %[cpsr]	\n\t"
-		BLX("%[fn]")
-		"mrs	%[cpsr], cpsr		\n\t"
-		: "=r" (rdv), [cpsr] "=r" (cpsr)
-		: "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv),
-		  "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
-		: "lr", "memory", "cc"
-	);
-
-	regs->uregs[rd] = rdv;
-	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
-}
-
-static void __kprobes
-emulate_rd12rm0_noflags_nopc(struct kprobe *p, struct pt_regs *regs)
-{
-	kprobe_opcode_t insn = p->opcode;
-	int rd = (insn >> 12) & 0xf;
-	int rm = insn & 0xf;
-
-	register unsigned long rdv asm("r0") = regs->uregs[rd];
-	register unsigned long rmv asm("r3") = regs->uregs[rm];
-
-	__asm__ __volatile__ (
-		BLX("%[fn]")
-		: "=r" (rdv)
-		: "0" (rdv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
-		: "lr", "memory", "cc"
-	);
-
-	regs->uregs[rd] = rdv;
-}
-
-static void __kprobes
-emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
-{
-	kprobe_opcode_t insn = p->opcode;
-	int rdlo = (insn >> 12) & 0xf;
-	int rdhi = (insn >> 16) & 0xf;
-	int rn = insn & 0xf;
-	int rm = (insn >> 8) & 0xf;
-
-	register unsigned long rdlov asm("r0") = regs->uregs[rdlo];
-	register unsigned long rdhiv asm("r2") = regs->uregs[rdhi];
-	register unsigned long rnv asm("r3") = regs->uregs[rn];
-	register unsigned long rmv asm("r1") = regs->uregs[rm];
-	unsigned long cpsr = regs->ARM_cpsr;
-
-	__asm__ __volatile__ (
-		"msr	cpsr_fs, %[cpsr]	\n\t"
-		BLX("%[fn]")
-		"mrs	%[cpsr], cpsr		\n\t"
-		: "=r" (rdlov), "=r" (rdhiv), [cpsr] "=r" (cpsr)
-		: "0" (rdlov), "1" (rdhiv), "r" (rnv), "r" (rmv),
-		  "2" (cpsr), [fn] "r" (p->ainsn.insn_fn)
-		: "lr", "memory", "cc"
-	);
-
-	regs->uregs[rdlo] = rdlov;
-	regs->uregs[rdhi] = rdhiv;
-	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
-}
-
 /*
  * For the instruction masking and comparisons in all the "space_*"
  * functions below, Do _not_ rearrange the order of tests unless
@@ -400,13 +112,13 @@ static const union decode_item arm_1111_table[] = {
 	/* PLDI (immediate)	1111 0100 x101 xxxx xxxx xxxx xxxx xxxx */
 	/* PLDW (immediate)	1111 0101 x001 xxxx xxxx xxxx xxxx xxxx */
 	/* PLD (immediate)	1111 0101 x101 xxxx xxxx xxxx xxxx xxxx */
-	DECODE_SIMULATE	(0xfe300000, 0xf4100000, kprobe_simulate_nop),
+	DECODE_SIMULATE	(0xfe300000, 0xf4100000, probes_simulate_nop),
 
 	/* memory hint		1111 0110 x001 xxxx xxxx xxxx xxx0 xxxx */
 	/* PLDI (register)	1111 0110 x101 xxxx xxxx xxxx xxx0 xxxx */
 	/* PLDW (register)	1111 0111 x001 xxxx xxxx xxxx xxx0 xxxx */
 	/* PLD (register)	1111 0111 x101 xxxx xxxx xxxx xxx0 xxxx */
-	DECODE_SIMULATE	(0xfe300010, 0xf6100000, kprobe_simulate_nop),
+	DECODE_SIMULATE	(0xfe300010, 0xf6100000, probes_simulate_nop),
 
 	/* BLX (immediate)	1111 101x xxxx xxxx xxxx xxxx xxxx xxxx */
 	DECODE_SIMULATE	(0xfe000000, 0xfa000000, simulate_blx1),
@@ -649,11 +361,11 @@ static const union decode_item arm_cccc_001x_table[] = {
 	/* YIELD		cccc 0011 0010 0000 xxxx xxxx 0000 0001 */
 	DECODE_OR	(0x0fff00ff, 0x03200001),
 	/* SEV			cccc 0011 0010 0000 xxxx xxxx 0000 0100 */
-	DECODE_EMULATE	(0x0fff00ff, 0x03200004, kprobe_emulate_none),
+	DECODE_EMULATE	(0x0fff00ff, 0x03200004, probes_emulate_none),
 	/* NOP			cccc 0011 0010 0000 xxxx xxxx 0000 0000 */
 	/* WFE			cccc 0011 0010 0000 xxxx xxxx 0000 0010 */
 	/* WFI			cccc 0011 0010 0000 xxxx xxxx 0000 0011 */
-	DECODE_SIMULATE	(0x0fff00fc, 0x03200000, kprobe_simulate_nop),
+	DECODE_SIMULATE	(0x0fff00fc, 0x03200000, probes_simulate_nop),
 	/* DBG			cccc 0011 0010 0000 xxxx xxxx ffff xxxx */
 	/* unallocated hints	cccc 0011 0010 0000 xxxx xxxx xxxx xxxx */
 	/* MSR (immediate)	cccc 0011 0x10 xxxx xxxx xxxx xxxx xxxx */
diff --git a/arch/arm/kernel/kprobes-common.c b/arch/arm/kernel/kprobes-common.c
index 18a7628..b66e9f7 100644
--- a/arch/arm/kernel/kprobes-common.c
+++ b/arch/arm/kernel/kprobes-common.c
@@ -173,15 +173,6 @@ kprobe_check_cc * const kprobe_condition_checks[16] = {
 };
 
 
-void __kprobes kprobe_simulate_nop(struct kprobe *p, struct pt_regs *regs)
-{
-}
-
-void __kprobes kprobe_emulate_none(struct kprobe *p, struct pt_regs *regs)
-{
-	p->ainsn.insn_fn();
-}
-
 static void __kprobes simulate_ldm1stm1(struct kprobe *p, struct pt_regs *regs)
 {
 	kprobe_opcode_t insn = p->opcode;
@@ -319,260 +310,3 @@ kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi)
 	return INSN_GOOD_NO_SLOT;
 }
 
-
-/*
- * Prepare an instruction slot to receive an instruction for emulating.
- * This is done by placing a subroutine return after the location where the
- * instruction will be placed. We also modify ARM instructions to be
- * unconditional as the condition code will already be checked before any
- * emulation handler is called.
- */
-static kprobe_opcode_t __kprobes
-prepare_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
-								bool thumb)
-{
-#ifdef CONFIG_THUMB2_KERNEL
-	if (thumb) {
-		u16 *thumb_insn = (u16 *)asi->insn;
-		thumb_insn[1] = 0x4770; /* Thumb bx lr */
-		thumb_insn[2] = 0x4770; /* Thumb bx lr */
-		return insn;
-	}
-	asi->insn[1] = 0xe12fff1e; /* ARM bx lr */
-#else
-	asi->insn[1] = 0xe1a0f00e; /* mov pc, lr */
-#endif
-	/* Make an ARM instruction unconditional */
-	if (insn < 0xe0000000)
-		insn = (insn | 0xe0000000) & ~0x10000000;
-	return insn;
-}
-
-/*
- * Write a (probably modified) instruction into the slot previously prepared by
- * prepare_emulated_insn
- */
-static void  __kprobes
-set_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
-								bool thumb)
-{
-#ifdef CONFIG_THUMB2_KERNEL
-	if (thumb) {
-		u16 *ip = (u16 *)asi->insn;
-		if (is_wide_instruction(insn))
-			*ip++ = insn >> 16;
-		*ip++ = insn;
-		return;
-	}
-#endif
-	asi->insn[0] = insn;
-}
-
-/*
- * When we modify the register numbers encoded in an instruction to be emulated,
- * the new values come from this define. For ARM and 32-bit Thumb instructions
- * this gives...
- *
- *	bit position	  16  12   8   4   0
- *	---------------+---+---+---+---+---+
- *	register	 r2  r0  r1  --  r3
- */
-#define INSN_NEW_BITS		0x00020103
-
-/* Each nibble has same value as that at INSN_NEW_BITS bit 16 */
-#define INSN_SAMEAS16_BITS	0x22222222
-
-/*
- * Validate and modify each of the registers encoded in an instruction.
- *
- * Each nibble in regs contains a value from enum decode_reg_type. For each
- * non-zero value, the corresponding nibble in pinsn is validated and modified
- * according to the type.
- */
-static bool __kprobes decode_regs(kprobe_opcode_t* pinsn, u32 regs)
-{
-	kprobe_opcode_t insn = *pinsn;
-	kprobe_opcode_t mask = 0xf; /* Start at least significant nibble */
-
-	for (; regs != 0; regs >>= 4, mask <<= 4) {
-
-		kprobe_opcode_t new_bits = INSN_NEW_BITS;
-
-		switch (regs & 0xf) {
-
-		case REG_TYPE_NONE:
-			/* Nibble not a register, skip to next */
-			continue;
-
-		case REG_TYPE_ANY:
-			/* Any register is allowed */
-			break;
-
-		case REG_TYPE_SAMEAS16:
-			/* Replace register with same as at bit position 16 */
-			new_bits = INSN_SAMEAS16_BITS;
-			break;
-
-		case REG_TYPE_SP:
-			/* Only allow SP (R13) */
-			if ((insn ^ 0xdddddddd) & mask)
-				goto reject;
-			break;
-
-		case REG_TYPE_PC:
-			/* Only allow PC (R15) */
-			if ((insn ^ 0xffffffff) & mask)
-				goto reject;
-			break;
-
-		case REG_TYPE_NOSP:
-			/* Reject SP (R13) */
-			if (((insn ^ 0xdddddddd) & mask) == 0)
-				goto reject;
-			break;
-
-		case REG_TYPE_NOSPPC:
-		case REG_TYPE_NOSPPCX:
-			/* Reject SP and PC (R13 and R15) */
-			if (((insn ^ 0xdddddddd) & 0xdddddddd & mask) == 0)
-				goto reject;
-			break;
-
-		case REG_TYPE_NOPCWB:
-			if (!is_writeback(insn))
-				break; /* No writeback, so any register is OK */
-			/* fall through... */
-		case REG_TYPE_NOPC:
-		case REG_TYPE_NOPCX:
-			/* Reject PC (R15) */
-			if (((insn ^ 0xffffffff) & mask) == 0)
-				goto reject;
-			break;
-		}
-
-		/* Replace value of nibble with new register number... */
-		insn &= ~mask;
-		insn |= new_bits & mask;
-	}
-
-	*pinsn = insn;
-	return true;
-
-reject:
-	return false;
-}
-
-static const int decode_struct_sizes[NUM_DECODE_TYPES] = {
-	[DECODE_TYPE_TABLE]	= sizeof(struct decode_table),
-	[DECODE_TYPE_CUSTOM]	= sizeof(struct decode_custom),
-	[DECODE_TYPE_SIMULATE]	= sizeof(struct decode_simulate),
-	[DECODE_TYPE_EMULATE]	= sizeof(struct decode_emulate),
-	[DECODE_TYPE_OR]	= sizeof(struct decode_or),
-	[DECODE_TYPE_REJECT]	= sizeof(struct decode_reject)
-};
-
-/*
- * kprobe_decode_insn operates on data tables in order to decode an ARM
- * architecture instruction onto which a kprobe has been placed.
- *
- * These instruction decoding tables are a concatenation of entries each
- * of which consist of one of the following structs:
- *
- *	decode_table
- *	decode_custom
- *	decode_simulate
- *	decode_emulate
- *	decode_or
- *	decode_reject
- *
- * Each of these starts with a struct decode_header which has the following
- * fields:
- *
- *	type_regs
- *	mask
- *	value
- *
- * The least significant DECODE_TYPE_BITS of type_regs contains a value
- * from enum decode_type, this indicates which of the decode_* structs
- * the entry contains. The value DECODE_TYPE_END indicates the end of the
- * table.
- *
- * When the table is parsed, each entry is checked in turn to see if it
- * matches the instruction to be decoded using the test:
- *
- *	(insn & mask) == value
- *
- * If no match is found before the end of the table is reached then decoding
- * fails with INSN_REJECTED.
- *
- * When a match is found, decode_regs() is called to validate and modify each
- * of the registers encoded in the instruction; the data it uses to do this
- * is (type_regs >> DECODE_TYPE_BITS). A validation failure will cause decoding
- * to fail with INSN_REJECTED.
- *
- * Once the instruction has passed the above tests, further processing
- * depends on the type of the table entry's decode struct.
- *
- */
-int __kprobes
-kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
-				const union decode_item *table, bool thumb)
-{
-	const struct decode_header *h = (struct decode_header *)table;
-	const struct decode_header *next;
-	bool matched = false;
-
-	insn = prepare_emulated_insn(insn, asi, thumb);
-
-	for (;; h = next) {
-		enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK;
-		u32 regs = h->type_regs.bits >> DECODE_TYPE_BITS;
-
-		if (type == DECODE_TYPE_END)
-			return INSN_REJECTED;
-
-		next = (struct decode_header *)
-				((uintptr_t)h + decode_struct_sizes[type]);
-
-		if (!matched && (insn & h->mask.bits) != h->value.bits)
-			continue;
-
-		if (!decode_regs(&insn, regs))
-			return INSN_REJECTED;
-
-		switch (type) {
-
-		case DECODE_TYPE_TABLE: {
-			struct decode_table *d = (struct decode_table *)h;
-			next = (struct decode_header *)d->table.table;
-			break;
-		}
-
-		case DECODE_TYPE_CUSTOM: {
-			struct decode_custom *d = (struct decode_custom *)h;
-			return (*d->decoder.decoder)(insn, asi);
-		}
-
-		case DECODE_TYPE_SIMULATE: {
-			struct decode_simulate *d = (struct decode_simulate *)h;
-			asi->insn_handler = d->handler.handler;
-			return INSN_GOOD_NO_SLOT;
-		}
-
-		case DECODE_TYPE_EMULATE: {
-			struct decode_emulate *d = (struct decode_emulate *)h;
-			asi->insn_handler = d->handler.handler;
-			set_emulated_insn(insn, asi, thumb);
-			return INSN_GOOD;
-		}
-
-		case DECODE_TYPE_OR:
-			matched = true;
-			break;
-
-		case DECODE_TYPE_REJECT:
-		default:
-			return INSN_REJECTED;
-		}
-		}
-	}
diff --git a/arch/arm/kernel/kprobes-thumb.c b/arch/arm/kernel/kprobes-thumb.c
index 6123daf..173b2bc 100644
--- a/arch/arm/kernel/kprobes-thumb.c
+++ b/arch/arm/kernel/kprobes-thumb.c
@@ -544,11 +544,11 @@ static const union decode_item t32_table_1111_0xxx___1[] = {
 	/* YIELD		1111 0011 1010 xxxx 10x0 x000 0000 0001 */
 	DECODE_OR	(0xfff0d7ff, 0xf3a08001),
 	/* SEV			1111 0011 1010 xxxx 10x0 x000 0000 0100 */
-	DECODE_EMULATE	(0xfff0d7ff, 0xf3a08004, kprobe_emulate_none),
+	DECODE_EMULATE	(0xfff0d7ff, 0xf3a08004, probes_emulate_none),
 	/* NOP			1111 0011 1010 xxxx 10x0 x000 0000 0000 */
 	/* WFE			1111 0011 1010 xxxx 10x0 x000 0000 0010 */
 	/* WFI			1111 0011 1010 xxxx 10x0 x000 0000 0011 */
-	DECODE_SIMULATE	(0xfff0d7fc, 0xf3a08000, kprobe_simulate_nop),
+	DECODE_SIMULATE	(0xfff0d7fc, 0xf3a08000, probes_simulate_nop),
 
 	/* MRS Rd, CPSR		1111 0011 1110 xxxx 10x0 xxxx xxxx xxxx */
 	DECODE_SIMULATEX(0xfff0d000, 0xf3e08000, t32_simulate_mrs,
@@ -589,7 +589,7 @@ static const union decode_item t32_table_1111_100x_x0x1__1111[] = {
 
 	/* PLD (literal)	1111 1000 x001 1111 1111 xxxx xxxx xxxx */
 	/* PLI (literal)	1111 1001 x001 1111 1111 xxxx xxxx xxxx */
-	DECODE_SIMULATE	(0xfe7ff000, 0xf81ff000, kprobe_simulate_nop),
+	DECODE_SIMULATE	(0xfe7ff000, 0xf81ff000, probes_simulate_nop),
 
 	/* PLD{W} (immediate)	1111 1000 10x1 xxxx 1111 xxxx xxxx xxxx */
 	DECODE_OR	(0xffd0f000, 0xf890f000),
@@ -598,13 +598,13 @@ static const union decode_item t32_table_1111_100x_x0x1__1111[] = {
 	/* PLI (immediate)	1111 1001 1001 xxxx 1111 xxxx xxxx xxxx */
 	DECODE_OR	(0xfff0f000, 0xf990f000),
 	/* PLI (immediate)	1111 1001 0001 xxxx 1111 1100 xxxx xxxx */
-	DECODE_SIMULATEX(0xfff0ff00, 0xf910fc00, kprobe_simulate_nop,
+	DECODE_SIMULATEX(0xfff0ff00, 0xf910fc00, probes_simulate_nop,
 						 REGS(NOPCX, 0, 0, 0, 0)),
 
 	/* PLD{W} (register)	1111 1000 00x1 xxxx 1111 0000 00xx xxxx */
 	DECODE_OR	(0xffd0ffc0, 0xf810f000),
 	/* PLI (register)	1111 1001 0001 xxxx 1111 0000 00xx xxxx */
-	DECODE_SIMULATEX(0xfff0ffc0, 0xf910f000, kprobe_simulate_nop,
+	DECODE_SIMULATEX(0xfff0ffc0, 0xf910f000, probes_simulate_nop,
 						 REGS(NOPCX, 0, 0, 0, NOSPPC)),
 
 	/* Other unallocated instructions...				*/
@@ -1274,11 +1274,11 @@ static const union decode_item t16_table_1011[] = {
 	/* YIELD			1011 1111 0001 0000 */
 	DECODE_OR	(0xffff, 0xbf10),
 	/* SEV				1011 1111 0100 0000 */
-	DECODE_EMULATE	(0xffff, 0xbf40, kprobe_emulate_none),
+	DECODE_EMULATE	(0xffff, 0xbf40, probes_emulate_none),
 	/* NOP				1011 1111 0000 0000 */
 	/* WFE				1011 1111 0010 0000 */
 	/* WFI				1011 1111 0011 0000 */
-	DECODE_SIMULATE	(0xffcf, 0xbf00, kprobe_simulate_nop),
+	DECODE_SIMULATE	(0xffcf, 0xbf00, probes_simulate_nop),
 	/* Unassigned hints		1011 1111 xxxx 0000 */
 	DECODE_REJECT	(0xff0f, 0xbf00),
 	/* IT				1011 1111 xxxx xxxx */
diff --git a/arch/arm/kernel/kprobes.h b/arch/arm/kernel/kprobes.h
index 38945f7..9aa2f15 100644
--- a/arch/arm/kernel/kprobes.h
+++ b/arch/arm/kernel/kprobes.h
@@ -161,8 +161,8 @@ static inline void __kprobes alu_write_pc(long pcv, struct pt_regs *regs)
 }
 
 
-void __kprobes kprobe_simulate_nop(struct kprobe *p, struct pt_regs *regs);
-void __kprobes kprobe_emulate_none(struct kprobe *p, struct pt_regs *regs);
+void __kprobes probes_simulate_nop(struct kprobe *p, struct pt_regs *regs);
+void __kprobes probes_emulate_none(struct kprobe *p, struct pt_regs *regs);
 
 enum kprobe_insn __kprobes
 kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi);
diff --git a/arch/arm/kernel/probes-arm.c b/arch/arm/kernel/probes-arm.c
new file mode 100644
index 0000000..e1b1a6e
--- /dev/null
+++ b/arch/arm/kernel/probes-arm.c
@@ -0,0 +1,311 @@
+/*
+ * arch/arm/kernel/probes-arm.c
+ *
+ * Some code moved here from arch/arm/kernel/kprobes-arm.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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/kprobes.h>
+
+#include "probes.h"
+#include "kprobes.h"
+
+#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
+
+#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25)
+
+#if  __LINUX_ARM_ARCH__ >= 6
+#define BLX(reg)	"blx	"reg"		\n\t"
+#else
+#define BLX(reg)	"mov	lr, pc		\n\t"	\
+			"mov	pc, "reg"	\n\t"
+#endif
+
+void __kprobes simulate_bbl(struct kprobe *p, struct pt_regs *regs)
+{
+	kprobe_opcode_t insn = p->opcode;
+	long iaddr = (long)p->addr;
+	int disp  = branch_displacement(insn);
+
+	if (insn & (1 << 24))
+		regs->ARM_lr = iaddr + 4;
+
+	regs->ARM_pc = iaddr + 8 + disp;
+}
+
+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;
+}
+
+void __kprobes simulate_blx2bx(struct kprobe *p, struct pt_regs *regs)
+{
+	kprobe_opcode_t insn = p->opcode;
+	int rm = insn & 0xf;
+	long rmv = regs->uregs[rm];
+
+	if (insn & (1 << 5))
+		regs->ARM_lr = (long)p->addr + 4;
+
+	regs->ARM_pc = rmv & ~0x1;
+	regs->ARM_cpsr &= ~PSR_T_BIT;
+	if (rmv & 0x1)
+		regs->ARM_cpsr |= PSR_T_BIT;
+}
+
+void __kprobes simulate_mrs(struct kprobe *p, struct pt_regs *regs)
+{
+	kprobe_opcode_t insn = p->opcode;
+	int rd = (insn >> 12) & 0xf;
+	unsigned long mask = 0xf8ff03df; /* Mask out execution state */
+	regs->uregs[rd] = regs->ARM_cpsr & mask;
+}
+
+void __kprobes simulate_mov_ipsp(struct kprobe *p, struct pt_regs *regs)
+{
+	regs->uregs[12] = regs->uregs[13];
+}
+
+void __kprobes
+emulate_ldrdstrd(struct kprobe *p, struct pt_regs *regs)
+{
+	kprobe_opcode_t insn = p->opcode;
+	unsigned long pc = (unsigned long)p->addr + 8;
+	int rt = (insn >> 12) & 0xf;
+	int rn = (insn >> 16) & 0xf;
+	int rm = insn & 0xf;
+
+	register unsigned long rtv asm("r0") = regs->uregs[rt];
+	register unsigned long rt2v asm("r1") = regs->uregs[rt+1];
+	register unsigned long rnv asm("r2") = (rn == 15) ? pc
+							  : regs->uregs[rn];
+	register unsigned long rmv asm("r3") = regs->uregs[rm];
+
+	__asm__ __volatile__ (
+		BLX("%[fn]")
+		: "=r" (rtv), "=r" (rt2v), "=r" (rnv)
+		: "0" (rtv), "1" (rt2v), "2" (rnv), "r" (rmv),
+		  [fn] "r" (p->ainsn.insn_fn)
+		: "lr", "memory", "cc"
+	);
+
+	regs->uregs[rt] = rtv;
+	regs->uregs[rt+1] = rt2v;
+	if (is_writeback(insn))
+		regs->uregs[rn] = rnv;
+}
+
+void __kprobes
+emulate_ldr(struct kprobe *p, struct pt_regs *regs)
+{
+	kprobe_opcode_t insn = p->opcode;
+	unsigned long pc = (unsigned long)p->addr + 8;
+	int rt = (insn >> 12) & 0xf;
+	int rn = (insn >> 16) & 0xf;
+	int rm = insn & 0xf;
+
+	register unsigned long rtv asm("r0");
+	register unsigned long rnv asm("r2") = (rn == 15) ? pc
+							  : regs->uregs[rn];
+	register unsigned long rmv asm("r3") = regs->uregs[rm];
+
+	__asm__ __volatile__ (
+		BLX("%[fn]")
+		: "=r" (rtv), "=r" (rnv)
+		: "1" (rnv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
+		: "lr", "memory", "cc"
+	);
+
+	if (rt == 15)
+		load_write_pc(rtv, regs);
+	else
+		regs->uregs[rt] = rtv;
+
+	if (is_writeback(insn))
+		regs->uregs[rn] = rnv;
+}
+
+void __kprobes
+emulate_str(struct kprobe *p, struct pt_regs *regs)
+{
+	kprobe_opcode_t insn = p->opcode;
+	unsigned long rtpc = (unsigned long)p->addr + str_pc_offset;
+	unsigned long rnpc = (unsigned long)p->addr + 8;
+	int rt = (insn >> 12) & 0xf;
+	int rn = (insn >> 16) & 0xf;
+	int rm = insn & 0xf;
+
+	register unsigned long rtv asm("r0") = (rt == 15) ? rtpc
+							  : regs->uregs[rt];
+	register unsigned long rnv asm("r2") = (rn == 15) ? rnpc
+							  : regs->uregs[rn];
+	register unsigned long rmv asm("r3") = regs->uregs[rm];
+
+	__asm__ __volatile__ (
+		BLX("%[fn]")
+		: "=r" (rnv)
+		: "r" (rtv), "0" (rnv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
+		: "lr", "memory", "cc"
+	);
+
+	if (is_writeback(insn))
+		regs->uregs[rn] = rnv;
+}
+
+void __kprobes
+emulate_rd12rn16rm0rs8_rwflags(struct kprobe *p, struct pt_regs *regs)
+{
+	kprobe_opcode_t insn = p->opcode;
+	unsigned long pc = (unsigned long)p->addr + 8;
+	int rd = (insn >> 12) & 0xf;
+	int rn = (insn >> 16) & 0xf;
+	int rm = insn & 0xf;
+	int rs = (insn >> 8) & 0xf;
+
+	register unsigned long rdv asm("r0") = regs->uregs[rd];
+	register unsigned long rnv asm("r2") = (rn == 15) ? pc
+							  : regs->uregs[rn];
+	register unsigned long rmv asm("r3") = (rm == 15) ? pc
+							  : regs->uregs[rm];
+	register unsigned long rsv asm("r1") = regs->uregs[rs];
+	unsigned long cpsr = regs->ARM_cpsr;
+
+	__asm__ __volatile__ (
+		"msr	cpsr_fs, %[cpsr]	\n\t"
+		BLX("%[fn]")
+		"mrs	%[cpsr], cpsr		\n\t"
+		: "=r" (rdv), [cpsr] "=r" (cpsr)
+		: "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv),
+		  "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
+		: "lr", "memory", "cc"
+	);
+
+	if (rd == 15)
+		alu_write_pc(rdv, regs);
+	else
+		regs->uregs[rd] = rdv;
+	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
+}
+
+void __kprobes
+emulate_rd12rn16rm0_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
+{
+	kprobe_opcode_t insn = p->opcode;
+	int rd = (insn >> 12) & 0xf;
+	int rn = (insn >> 16) & 0xf;
+	int rm = insn & 0xf;
+
+	register unsigned long rdv asm("r0") = regs->uregs[rd];
+	register unsigned long rnv asm("r2") = regs->uregs[rn];
+	register unsigned long rmv asm("r3") = regs->uregs[rm];
+	unsigned long cpsr = regs->ARM_cpsr;
+
+	__asm__ __volatile__ (
+		"msr	cpsr_fs, %[cpsr]	\n\t"
+		BLX("%[fn]")
+		"mrs	%[cpsr], cpsr		\n\t"
+		: "=r" (rdv), [cpsr] "=r" (cpsr)
+		: "0" (rdv), "r" (rnv), "r" (rmv),
+		  "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
+		: "lr", "memory", "cc"
+	);
+
+	regs->uregs[rd] = rdv;
+	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
+}
+
+void __kprobes
+emulate_rd16rn12rm0rs8_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
+{
+	kprobe_opcode_t insn = p->opcode;
+	int rd = (insn >> 16) & 0xf;
+	int rn = (insn >> 12) & 0xf;
+	int rm = insn & 0xf;
+	int rs = (insn >> 8) & 0xf;
+
+	register unsigned long rdv asm("r2") = regs->uregs[rd];
+	register unsigned long rnv asm("r0") = regs->uregs[rn];
+	register unsigned long rmv asm("r3") = regs->uregs[rm];
+	register unsigned long rsv asm("r1") = regs->uregs[rs];
+	unsigned long cpsr = regs->ARM_cpsr;
+
+	__asm__ __volatile__ (
+		"msr	cpsr_fs, %[cpsr]	\n\t"
+		BLX("%[fn]")
+		"mrs	%[cpsr], cpsr		\n\t"
+		: "=r" (rdv), [cpsr] "=r" (cpsr)
+		: "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv),
+		  "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
+		: "lr", "memory", "cc"
+	);
+
+	regs->uregs[rd] = rdv;
+	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
+}
+
+void __kprobes
+emulate_rd12rm0_noflags_nopc(struct kprobe *p, struct pt_regs *regs)
+{
+	kprobe_opcode_t insn = p->opcode;
+	int rd = (insn >> 12) & 0xf;
+	int rm = insn & 0xf;
+
+	register unsigned long rdv asm("r0") = regs->uregs[rd];
+	register unsigned long rmv asm("r3") = regs->uregs[rm];
+
+	__asm__ __volatile__ (
+		BLX("%[fn]")
+		: "=r" (rdv)
+		: "0" (rdv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
+		: "lr", "memory", "cc"
+	);
+
+	regs->uregs[rd] = rdv;
+}
+
+void __kprobes
+emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
+{
+	kprobe_opcode_t insn = p->opcode;
+	int rdlo = (insn >> 12) & 0xf;
+	int rdhi = (insn >> 16) & 0xf;
+	int rn = insn & 0xf;
+	int rm = (insn >> 8) & 0xf;
+
+	register unsigned long rdlov asm("r0") = regs->uregs[rdlo];
+	register unsigned long rdhiv asm("r2") = regs->uregs[rdhi];
+	register unsigned long rnv asm("r3") = regs->uregs[rn];
+	register unsigned long rmv asm("r1") = regs->uregs[rm];
+	unsigned long cpsr = regs->ARM_cpsr;
+
+	__asm__ __volatile__ (
+		"msr	cpsr_fs, %[cpsr]	\n\t"
+		BLX("%[fn]")
+		"mrs	%[cpsr], cpsr		\n\t"
+		: "=r" (rdlov), "=r" (rdhiv), [cpsr] "=r" (cpsr)
+		: "0" (rdlov), "1" (rdhiv), "r" (rnv), "r" (rmv),
+		  "2" (cpsr), [fn] "r" (p->ainsn.insn_fn)
+		: "lr", "memory", "cc"
+	);
+
+	regs->uregs[rdlo] = rdlov;
+	regs->uregs[rdhi] = rdhiv;
+	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
+}
diff --git a/arch/arm/kernel/probes.c b/arch/arm/kernel/probes.c
new file mode 100644
index 0000000..86c63f3
--- /dev/null
+++ b/arch/arm/kernel/probes.c
@@ -0,0 +1,286 @@
+/*
+ * arch/arm/kernel/probes.c
+ *
+ * Some contents moved here from arch/arm/include/asm/kprobes-common.c
+ *
+ * Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
+ *
+ * Some contents moved here from arch/arm/include/asm/kprobes-arm.c which is
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/kprobes.h>
+
+#include "probes.h"
+#include "kprobes.h"
+
+void __kprobes probes_simulate_nop(struct kprobe *p, struct pt_regs *regs)
+{
+}
+
+void __kprobes probes_emulate_none(struct kprobe *p, struct pt_regs *regs)
+{
+	p->ainsn.insn_fn();
+}
+
+/*
+ * Prepare an instruction slot to receive an instruction for emulating.
+ * This is done by placing a subroutine return after the location where the
+ * instruction will be placed. We also modify ARM instructions to be
+ * unconditional as the condition code will already be checked before any
+ * emulation handler is called.
+ */
+static kprobe_opcode_t __kprobes
+prepare_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
+								bool thumb)
+{
+#ifdef CONFIG_THUMB2_KERNEL
+	if (thumb) {
+		u16 *thumb_insn = (u16 *)asi->insn;
+		thumb_insn[1] = 0x4770; /* Thumb bx lr */
+		thumb_insn[2] = 0x4770; /* Thumb bx lr */
+		return insn;
+	}
+	asi->insn[1] = 0xe12fff1e; /* ARM bx lr */
+#else
+	asi->insn[1] = 0xe1a0f00e; /* mov pc, lr */
+#endif
+	/* Make an ARM instruction unconditional */
+	if (insn < 0xe0000000)
+		insn = (insn | 0xe0000000) & ~0x10000000;
+	return insn;
+}
+
+/*
+ * Write a (probably modified) instruction into the slot previously prepared by
+ * prepare_emulated_insn
+ */
+static void  __kprobes
+set_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
+								bool thumb)
+{
+#ifdef CONFIG_THUMB2_KERNEL
+	if (thumb) {
+		u16 *ip = (u16 *)asi->insn;
+		if (is_wide_instruction(insn))
+			*ip++ = insn >> 16;
+		*ip++ = insn;
+		return;
+	}
+#endif
+	asi->insn[0] = insn;
+}
+
+/*
+ * When we modify the register numbers encoded in an instruction to be emulated,
+ * the new values come from this define. For ARM and 32-bit Thumb instructions
+ * this gives...
+ *
+ *	bit position	  16  12   8   4   0
+ *	---------------+---+---+---+---+---+
+ *	register	 r2  r0  r1  --  r3
+ */
+#define INSN_NEW_BITS		0x00020103
+
+/* Each nibble has same value as that at INSN_NEW_BITS bit 16 */
+#define INSN_SAMEAS16_BITS	0x22222222
+
+/*
+ * Validate and modify each of the registers encoded in an instruction.
+ *
+ * Each nibble in regs contains a value from enum decode_reg_type. For each
+ * non-zero value, the corresponding nibble in pinsn is validated and modified
+ * according to the type.
+ */
+static bool __kprobes decode_regs(kprobe_opcode_t *pinsn, u32 regs)
+{
+	kprobe_opcode_t insn = *pinsn;
+	kprobe_opcode_t mask = 0xf; /* Start at least significant nibble */
+
+	for (; regs != 0; regs >>= 4, mask <<= 4) {
+
+		kprobe_opcode_t new_bits = INSN_NEW_BITS;
+
+		switch (regs & 0xf) {
+
+		case REG_TYPE_NONE:
+			/* Nibble not a register, skip to next */
+			continue;
+
+		case REG_TYPE_ANY:
+			/* Any register is allowed */
+			break;
+
+		case REG_TYPE_SAMEAS16:
+			/* Replace register with same as at bit position 16 */
+			new_bits = INSN_SAMEAS16_BITS;
+			break;
+
+		case REG_TYPE_SP:
+			/* Only allow SP (R13) */
+			if ((insn ^ 0xdddddddd) & mask)
+				goto reject;
+			break;
+
+		case REG_TYPE_PC:
+			/* Only allow PC (R15) */
+			if ((insn ^ 0xffffffff) & mask)
+				goto reject;
+			break;
+
+		case REG_TYPE_NOSP:
+			/* Reject SP (R13) */
+			if (((insn ^ 0xdddddddd) & mask) == 0)
+				goto reject;
+			break;
+
+		case REG_TYPE_NOSPPC:
+		case REG_TYPE_NOSPPCX:
+			/* Reject SP and PC (R13 and R15) */
+			if (((insn ^ 0xdddddddd) & 0xdddddddd & mask) == 0)
+				goto reject;
+			break;
+
+		case REG_TYPE_NOPCWB:
+			if (!is_writeback(insn))
+				break; /* No writeback, so any register is OK */
+			/* fall through... */
+		case REG_TYPE_NOPC:
+		case REG_TYPE_NOPCX:
+			/* Reject PC (R15) */
+			if (((insn ^ 0xffffffff) & mask) == 0)
+				goto reject;
+			break;
+		}
+
+		/* Replace value of nibble with new register number... */
+		insn &= ~mask;
+		insn |= new_bits & mask;
+	}
+
+	*pinsn = insn;
+	return true;
+
+reject:
+	return false;
+}
+
+static const int decode_struct_sizes[NUM_DECODE_TYPES] = {
+	[DECODE_TYPE_TABLE]	= sizeof(struct decode_table),
+	[DECODE_TYPE_CUSTOM]	= sizeof(struct decode_custom),
+	[DECODE_TYPE_SIMULATE]	= sizeof(struct decode_simulate),
+	[DECODE_TYPE_EMULATE]	= sizeof(struct decode_emulate),
+	[DECODE_TYPE_OR]	= sizeof(struct decode_or),
+	[DECODE_TYPE_REJECT]	= sizeof(struct decode_reject)
+};
+
+/*
+ * kprobe_decode_insn operates on data tables in order to decode an ARM
+ * architecture instruction onto which a kprobe has been placed.
+ *
+ * These instruction decoding tables are a concatenation of entries each
+ * of which consist of one of the following structs:
+ *
+ *	decode_table
+ *	decode_custom
+ *	decode_simulate
+ *	decode_emulate
+ *	decode_or
+ *	decode_reject
+ *
+ * Each of these starts with a struct decode_header which has the following
+ * fields:
+ *
+ *	type_regs
+ *	mask
+ *	value
+ *
+ * The least significant DECODE_TYPE_BITS of type_regs contains a value
+ * from enum decode_type, this indicates which of the decode_* structs
+ * the entry contains. The value DECODE_TYPE_END indicates the end of the
+ * table.
+ *
+ * When the table is parsed, each entry is checked in turn to see if it
+ * matches the instruction to be decoded using the test:
+ *
+ *	(insn & mask) == value
+ *
+ * If no match is found before the end of the table is reached then decoding
+ * fails with INSN_REJECTED.
+ *
+ * When a match is found, decode_regs() is called to validate and modify each
+ * of the registers encoded in the instruction; the data it uses to do this
+ * is (type_regs >> DECODE_TYPE_BITS). A validation failure will cause decoding
+ * to fail with INSN_REJECTED.
+ *
+ * Once the instruction has passed the above tests, further processing
+ * depends on the type of the table entry's decode struct.
+ *
+ */
+int __kprobes
+kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
+				const union decode_item *table, bool thumb)
+{
+	const struct decode_header *h = (struct decode_header *)table;
+	const struct decode_header *next;
+	bool matched = false;
+
+	insn = prepare_emulated_insn(insn, asi, thumb);
+
+	for (;; h = next) {
+		enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK;
+		u32 regs = h->type_regs.bits >> DECODE_TYPE_BITS;
+
+		if (type == DECODE_TYPE_END)
+			return INSN_REJECTED;
+
+		next = (struct decode_header *)
+				((uintptr_t)h + decode_struct_sizes[type]);
+
+		if (!matched && (insn & h->mask.bits) != h->value.bits)
+			continue;
+
+		if (!decode_regs(&insn, regs))
+			return INSN_REJECTED;
+
+		switch (type) {
+
+		case DECODE_TYPE_TABLE: {
+			struct decode_table *d = (struct decode_table *)h;
+			next = (struct decode_header *)d->table.table;
+			break;
+		}
+
+		case DECODE_TYPE_CUSTOM: {
+			struct decode_custom *d = (struct decode_custom *)h;
+			return (*d->decoder.decoder)(insn, asi);
+		}
+
+		case DECODE_TYPE_SIMULATE: {
+			struct decode_simulate *d = (struct decode_simulate *)h;
+			asi->insn_handler = d->handler.handler;
+			return INSN_GOOD_NO_SLOT;
+		}
+
+		case DECODE_TYPE_EMULATE: {
+			struct decode_emulate *d = (struct decode_emulate *)h;
+			asi->insn_handler = d->handler.handler;
+			set_emulated_insn(insn, asi, thumb);
+			return INSN_GOOD;
+		}
+
+		case DECODE_TYPE_OR:
+			matched = true;
+			break;
+
+		case DECODE_TYPE_REJECT:
+		default:
+			return INSN_REJECTED;
+		}
+	}
+}
diff --git a/arch/arm/kernel/probes.h b/arch/arm/kernel/probes.h
new file mode 100644
index 0000000..56eec12
--- /dev/null
+++ b/arch/arm/kernel/probes.h
@@ -0,0 +1,23 @@
+#ifndef _ARM_KERNEL_PROBES_H
+#define  _ARM_KERNEL_PROBES_H
+
+void __kprobes simulate_bbl(struct kprobe *p, struct pt_regs *regs);
+void __kprobes simulate_blx1(struct kprobe *p, struct pt_regs *regs);
+void __kprobes simulate_blx2bx(struct kprobe *p, struct pt_regs *regs);
+void __kprobes simulate_mrs(struct kprobe *p, struct pt_regs *regs);
+void __kprobes simulate_mov_ipsp(struct kprobe *p, struct pt_regs *regs);
+void __kprobes emulate_ldrdstrd(struct kprobe *p, struct pt_regs *regs);
+void __kprobes emulate_ldr(struct kprobe *p, struct pt_regs *regs);
+void __kprobes emulate_str(struct kprobe *p, struct pt_regs *regs);
+void __kprobes emulate_rd12rn16rm0rs8_rwflags(struct kprobe *p,
+					      struct pt_regs *regs);
+void __kprobes emulate_rd12rn16rm0_rwflags_nopc(struct kprobe *p,
+						struct pt_regs *regs);
+void __kprobes emulate_rd16rn12rm0rs8_rwflags_nopc(struct kprobe *p,
+						   struct pt_regs *regs);
+void __kprobes emulate_rd12rm0_noflags_nopc(struct kprobe *p,
+					    struct pt_regs *regs);
+void __kprobes emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(struct kprobe *p,
+						       struct pt_regs *regs);
+
+#endif
-- 
1.8.1.2


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 7/9] ARM: Move uprobes/kprobes shared functions to common file
@ 2013-08-01 23:45   ` David Long
  0 siblings, 0 replies; 32+ messages in thread
From: David Long @ 2013-08-01 23:45 UTC (permalink / raw)
  To: linux-arm-kernel

From: "David A. Long" <dave.long@linaro.org>

Separate the kprobe-only functions from the functions needed by
both kprobes and uprobes.

Signed-off-by: David A. Long <dave.long@linaro.org>
---
 arch/arm/kernel/Makefile         |   2 +-
 arch/arm/kernel/kprobes-arm.c    | 298 +------------------------------------
 arch/arm/kernel/kprobes-common.c | 266 ---------------------------------
 arch/arm/kernel/kprobes-thumb.c  |  14 +-
 arch/arm/kernel/kprobes.h        |   4 +-
 arch/arm/kernel/probes-arm.c     | 311 +++++++++++++++++++++++++++++++++++++++
 arch/arm/kernel/probes.c         | 286 +++++++++++++++++++++++++++++++++++
 arch/arm/kernel/probes.h         |  23 +++
 8 files changed, 635 insertions(+), 569 deletions(-)
 create mode 100644 arch/arm/kernel/probes-arm.c
 create mode 100644 arch/arm/kernel/probes.c
 create mode 100644 arch/arm/kernel/probes.h

diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index 86d10dd..3292023 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -49,7 +49,7 @@ obj-$(CONFIG_DYNAMIC_FTRACE)	+= ftrace.o insn.o
 obj-$(CONFIG_FUNCTION_GRAPH_TRACER)	+= ftrace.o insn.o
 obj-$(CONFIG_JUMP_LABEL)	+= jump_label.o insn.o patch.o
 obj-$(CONFIG_KEXEC)		+= machine_kexec.o relocate_kernel.o
-obj-$(CONFIG_KPROBES)		+= kprobes.o kprobes-common.o patch.o
+obj-$(CONFIG_KPROBES)		+= probes.o probes-arm.o kprobes.o kprobes-common.o patch.o
 ifdef CONFIG_THUMB2_KERNEL
 obj-$(CONFIG_KPROBES)		+= kprobes-thumb.o
 else
diff --git a/arch/arm/kernel/kprobes-arm.c b/arch/arm/kernel/kprobes-arm.c
index 8a30c89..d6503cc 100644
--- a/arch/arm/kernel/kprobes-arm.c
+++ b/arch/arm/kernel/kprobes-arm.c
@@ -62,19 +62,9 @@
 #include <linux/kprobes.h>
 #include <linux/module.h>
 
+#include "probes.h"
 #include "kprobes.h"
 
-#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
-
-#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25)
-
-#if  __LINUX_ARM_ARCH__ >= 6
-#define BLX(reg)	"blx	"reg"		\n\t"
-#else
-#define BLX(reg)	"mov	lr, pc		\n\t"	\
-			"mov	pc, "reg"	\n\t"
-#endif
-
 /*
  * To avoid the complications of mimicing single-stepping on a
  * processor without a Next-PC or a single-step mode, and to
@@ -105,284 +95,6 @@
  * read and write of flags.
  */
 
-static void __kprobes simulate_bbl(struct kprobe *p, struct pt_regs *regs)
-{
-	kprobe_opcode_t insn = p->opcode;
-	long iaddr = (long)p->addr;
-	int disp  = branch_displacement(insn);
-
-	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)
-{
-	kprobe_opcode_t insn = p->opcode;
-	int rm = insn & 0xf;
-	long rmv = regs->uregs[rm];
-
-	if (insn & (1 << 5))
-		regs->ARM_lr = (long)p->addr + 4;
-
-	regs->ARM_pc = rmv & ~0x1;
-	regs->ARM_cpsr &= ~PSR_T_BIT;
-	if (rmv & 0x1)
-		regs->ARM_cpsr |= PSR_T_BIT;
-}
-
-static void __kprobes simulate_mrs(struct kprobe *p, struct pt_regs *regs)
-{
-	kprobe_opcode_t insn = p->opcode;
-	int rd = (insn >> 12) & 0xf;
-	unsigned long mask = 0xf8ff03df; /* Mask out execution state */
-	regs->uregs[rd] = regs->ARM_cpsr & mask;
-}
-
-static void __kprobes simulate_mov_ipsp(struct kprobe *p, struct pt_regs *regs)
-{
-	regs->uregs[12] = regs->uregs[13];
-}
-
-static void __kprobes
-emulate_ldrdstrd(struct kprobe *p, struct pt_regs *regs)
-{
-	kprobe_opcode_t insn = p->opcode;
-	unsigned long pc = (unsigned long)p->addr + 8;
-	int rt = (insn >> 12) & 0xf;
-	int rn = (insn >> 16) & 0xf;
-	int rm = insn & 0xf;
-
-	register unsigned long rtv asm("r0") = regs->uregs[rt];
-	register unsigned long rt2v asm("r1") = regs->uregs[rt+1];
-	register unsigned long rnv asm("r2") = (rn == 15) ? pc
-							  : regs->uregs[rn];
-	register unsigned long rmv asm("r3") = regs->uregs[rm];
-
-	__asm__ __volatile__ (
-		BLX("%[fn]")
-		: "=r" (rtv), "=r" (rt2v), "=r" (rnv)
-		: "0" (rtv), "1" (rt2v), "2" (rnv), "r" (rmv),
-		  [fn] "r" (p->ainsn.insn_fn)
-		: "lr", "memory", "cc"
-	);
-
-	regs->uregs[rt] = rtv;
-	regs->uregs[rt+1] = rt2v;
-	if (is_writeback(insn))
-		regs->uregs[rn] = rnv;
-}
-
-static void __kprobes
-emulate_ldr(struct kprobe *p, struct pt_regs *regs)
-{
-	kprobe_opcode_t insn = p->opcode;
-	unsigned long pc = (unsigned long)p->addr + 8;
-	int rt = (insn >> 12) & 0xf;
-	int rn = (insn >> 16) & 0xf;
-	int rm = insn & 0xf;
-
-	register unsigned long rtv asm("r0");
-	register unsigned long rnv asm("r2") = (rn == 15) ? pc
-							  : regs->uregs[rn];
-	register unsigned long rmv asm("r3") = regs->uregs[rm];
-
-	__asm__ __volatile__ (
-		BLX("%[fn]")
-		: "=r" (rtv), "=r" (rnv)
-		: "1" (rnv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
-		: "lr", "memory", "cc"
-	);
-
-	if (rt == 15)
-		load_write_pc(rtv, regs);
-	else
-		regs->uregs[rt] = rtv;
-
-	if (is_writeback(insn))
-		regs->uregs[rn] = rnv;
-}
-
-static void __kprobes
-emulate_str(struct kprobe *p, struct pt_regs *regs)
-{
-	kprobe_opcode_t insn = p->opcode;
-	unsigned long rtpc = (unsigned long)p->addr + str_pc_offset;
-	unsigned long rnpc = (unsigned long)p->addr + 8;
-	int rt = (insn >> 12) & 0xf;
-	int rn = (insn >> 16) & 0xf;
-	int rm = insn & 0xf;
-
-	register unsigned long rtv asm("r0") = (rt == 15) ? rtpc
-							  : regs->uregs[rt];
-	register unsigned long rnv asm("r2") = (rn == 15) ? rnpc
-							  : regs->uregs[rn];
-	register unsigned long rmv asm("r3") = regs->uregs[rm];
-
-	__asm__ __volatile__ (
-		BLX("%[fn]")
-		: "=r" (rnv)
-		: "r" (rtv), "0" (rnv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
-		: "lr", "memory", "cc"
-	);
-
-	if (is_writeback(insn))
-		regs->uregs[rn] = rnv;
-}
-
-static void __kprobes
-emulate_rd12rn16rm0rs8_rwflags(struct kprobe *p, struct pt_regs *regs)
-{
-	kprobe_opcode_t insn = p->opcode;
-	unsigned long pc = (unsigned long)p->addr + 8;
-	int rd = (insn >> 12) & 0xf;
-	int rn = (insn >> 16) & 0xf;
-	int rm = insn & 0xf;
-	int rs = (insn >> 8) & 0xf;
-
-	register unsigned long rdv asm("r0") = regs->uregs[rd];
-	register unsigned long rnv asm("r2") = (rn == 15) ? pc
-							  : regs->uregs[rn];
-	register unsigned long rmv asm("r3") = (rm == 15) ? pc
-							  : regs->uregs[rm];
-	register unsigned long rsv asm("r1") = regs->uregs[rs];
-	unsigned long cpsr = regs->ARM_cpsr;
-
-	__asm__ __volatile__ (
-		"msr	cpsr_fs, %[cpsr]	\n\t"
-		BLX("%[fn]")
-		"mrs	%[cpsr], cpsr		\n\t"
-		: "=r" (rdv), [cpsr] "=r" (cpsr)
-		: "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv),
-		  "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
-		: "lr", "memory", "cc"
-	);
-
-	if (rd == 15)
-		alu_write_pc(rdv, regs);
-	else
-		regs->uregs[rd] = rdv;
-	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
-}
-
-static void __kprobes
-emulate_rd12rn16rm0_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
-{
-	kprobe_opcode_t insn = p->opcode;
-	int rd = (insn >> 12) & 0xf;
-	int rn = (insn >> 16) & 0xf;
-	int rm = insn & 0xf;
-
-	register unsigned long rdv asm("r0") = regs->uregs[rd];
-	register unsigned long rnv asm("r2") = regs->uregs[rn];
-	register unsigned long rmv asm("r3") = regs->uregs[rm];
-	unsigned long cpsr = regs->ARM_cpsr;
-
-	__asm__ __volatile__ (
-		"msr	cpsr_fs, %[cpsr]	\n\t"
-		BLX("%[fn]")
-		"mrs	%[cpsr], cpsr		\n\t"
-		: "=r" (rdv), [cpsr] "=r" (cpsr)
-		: "0" (rdv), "r" (rnv), "r" (rmv),
-		  "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
-		: "lr", "memory", "cc"
-	);
-
-	regs->uregs[rd] = rdv;
-	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
-}
-
-static void __kprobes
-emulate_rd16rn12rm0rs8_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
-{
-	kprobe_opcode_t insn = p->opcode;
-	int rd = (insn >> 16) & 0xf;
-	int rn = (insn >> 12) & 0xf;
-	int rm = insn & 0xf;
-	int rs = (insn >> 8) & 0xf;
-
-	register unsigned long rdv asm("r2") = regs->uregs[rd];
-	register unsigned long rnv asm("r0") = regs->uregs[rn];
-	register unsigned long rmv asm("r3") = regs->uregs[rm];
-	register unsigned long rsv asm("r1") = regs->uregs[rs];
-	unsigned long cpsr = regs->ARM_cpsr;
-
-	__asm__ __volatile__ (
-		"msr	cpsr_fs, %[cpsr]	\n\t"
-		BLX("%[fn]")
-		"mrs	%[cpsr], cpsr		\n\t"
-		: "=r" (rdv), [cpsr] "=r" (cpsr)
-		: "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv),
-		  "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
-		: "lr", "memory", "cc"
-	);
-
-	regs->uregs[rd] = rdv;
-	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
-}
-
-static void __kprobes
-emulate_rd12rm0_noflags_nopc(struct kprobe *p, struct pt_regs *regs)
-{
-	kprobe_opcode_t insn = p->opcode;
-	int rd = (insn >> 12) & 0xf;
-	int rm = insn & 0xf;
-
-	register unsigned long rdv asm("r0") = regs->uregs[rd];
-	register unsigned long rmv asm("r3") = regs->uregs[rm];
-
-	__asm__ __volatile__ (
-		BLX("%[fn]")
-		: "=r" (rdv)
-		: "0" (rdv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
-		: "lr", "memory", "cc"
-	);
-
-	regs->uregs[rd] = rdv;
-}
-
-static void __kprobes
-emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
-{
-	kprobe_opcode_t insn = p->opcode;
-	int rdlo = (insn >> 12) & 0xf;
-	int rdhi = (insn >> 16) & 0xf;
-	int rn = insn & 0xf;
-	int rm = (insn >> 8) & 0xf;
-
-	register unsigned long rdlov asm("r0") = regs->uregs[rdlo];
-	register unsigned long rdhiv asm("r2") = regs->uregs[rdhi];
-	register unsigned long rnv asm("r3") = regs->uregs[rn];
-	register unsigned long rmv asm("r1") = regs->uregs[rm];
-	unsigned long cpsr = regs->ARM_cpsr;
-
-	__asm__ __volatile__ (
-		"msr	cpsr_fs, %[cpsr]	\n\t"
-		BLX("%[fn]")
-		"mrs	%[cpsr], cpsr		\n\t"
-		: "=r" (rdlov), "=r" (rdhiv), [cpsr] "=r" (cpsr)
-		: "0" (rdlov), "1" (rdhiv), "r" (rnv), "r" (rmv),
-		  "2" (cpsr), [fn] "r" (p->ainsn.insn_fn)
-		: "lr", "memory", "cc"
-	);
-
-	regs->uregs[rdlo] = rdlov;
-	regs->uregs[rdhi] = rdhiv;
-	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
-}
-
 /*
  * For the instruction masking and comparisons in all the "space_*"
  * functions below, Do _not_ rearrange the order of tests unless
@@ -400,13 +112,13 @@ static const union decode_item arm_1111_table[] = {
 	/* PLDI (immediate)	1111 0100 x101 xxxx xxxx xxxx xxxx xxxx */
 	/* PLDW (immediate)	1111 0101 x001 xxxx xxxx xxxx xxxx xxxx */
 	/* PLD (immediate)	1111 0101 x101 xxxx xxxx xxxx xxxx xxxx */
-	DECODE_SIMULATE	(0xfe300000, 0xf4100000, kprobe_simulate_nop),
+	DECODE_SIMULATE	(0xfe300000, 0xf4100000, probes_simulate_nop),
 
 	/* memory hint		1111 0110 x001 xxxx xxxx xxxx xxx0 xxxx */
 	/* PLDI (register)	1111 0110 x101 xxxx xxxx xxxx xxx0 xxxx */
 	/* PLDW (register)	1111 0111 x001 xxxx xxxx xxxx xxx0 xxxx */
 	/* PLD (register)	1111 0111 x101 xxxx xxxx xxxx xxx0 xxxx */
-	DECODE_SIMULATE	(0xfe300010, 0xf6100000, kprobe_simulate_nop),
+	DECODE_SIMULATE	(0xfe300010, 0xf6100000, probes_simulate_nop),
 
 	/* BLX (immediate)	1111 101x xxxx xxxx xxxx xxxx xxxx xxxx */
 	DECODE_SIMULATE	(0xfe000000, 0xfa000000, simulate_blx1),
@@ -649,11 +361,11 @@ static const union decode_item arm_cccc_001x_table[] = {
 	/* YIELD		cccc 0011 0010 0000 xxxx xxxx 0000 0001 */
 	DECODE_OR	(0x0fff00ff, 0x03200001),
 	/* SEV			cccc 0011 0010 0000 xxxx xxxx 0000 0100 */
-	DECODE_EMULATE	(0x0fff00ff, 0x03200004, kprobe_emulate_none),
+	DECODE_EMULATE	(0x0fff00ff, 0x03200004, probes_emulate_none),
 	/* NOP			cccc 0011 0010 0000 xxxx xxxx 0000 0000 */
 	/* WFE			cccc 0011 0010 0000 xxxx xxxx 0000 0010 */
 	/* WFI			cccc 0011 0010 0000 xxxx xxxx 0000 0011 */
-	DECODE_SIMULATE	(0x0fff00fc, 0x03200000, kprobe_simulate_nop),
+	DECODE_SIMULATE	(0x0fff00fc, 0x03200000, probes_simulate_nop),
 	/* DBG			cccc 0011 0010 0000 xxxx xxxx ffff xxxx */
 	/* unallocated hints	cccc 0011 0010 0000 xxxx xxxx xxxx xxxx */
 	/* MSR (immediate)	cccc 0011 0x10 xxxx xxxx xxxx xxxx xxxx */
diff --git a/arch/arm/kernel/kprobes-common.c b/arch/arm/kernel/kprobes-common.c
index 18a7628..b66e9f7 100644
--- a/arch/arm/kernel/kprobes-common.c
+++ b/arch/arm/kernel/kprobes-common.c
@@ -173,15 +173,6 @@ kprobe_check_cc * const kprobe_condition_checks[16] = {
 };
 
 
-void __kprobes kprobe_simulate_nop(struct kprobe *p, struct pt_regs *regs)
-{
-}
-
-void __kprobes kprobe_emulate_none(struct kprobe *p, struct pt_regs *regs)
-{
-	p->ainsn.insn_fn();
-}
-
 static void __kprobes simulate_ldm1stm1(struct kprobe *p, struct pt_regs *regs)
 {
 	kprobe_opcode_t insn = p->opcode;
@@ -319,260 +310,3 @@ kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi)
 	return INSN_GOOD_NO_SLOT;
 }
 
-
-/*
- * Prepare an instruction slot to receive an instruction for emulating.
- * This is done by placing a subroutine return after the location where the
- * instruction will be placed. We also modify ARM instructions to be
- * unconditional as the condition code will already be checked before any
- * emulation handler is called.
- */
-static kprobe_opcode_t __kprobes
-prepare_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
-								bool thumb)
-{
-#ifdef CONFIG_THUMB2_KERNEL
-	if (thumb) {
-		u16 *thumb_insn = (u16 *)asi->insn;
-		thumb_insn[1] = 0x4770; /* Thumb bx lr */
-		thumb_insn[2] = 0x4770; /* Thumb bx lr */
-		return insn;
-	}
-	asi->insn[1] = 0xe12fff1e; /* ARM bx lr */
-#else
-	asi->insn[1] = 0xe1a0f00e; /* mov pc, lr */
-#endif
-	/* Make an ARM instruction unconditional */
-	if (insn < 0xe0000000)
-		insn = (insn | 0xe0000000) & ~0x10000000;
-	return insn;
-}
-
-/*
- * Write a (probably modified) instruction into the slot previously prepared by
- * prepare_emulated_insn
- */
-static void  __kprobes
-set_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
-								bool thumb)
-{
-#ifdef CONFIG_THUMB2_KERNEL
-	if (thumb) {
-		u16 *ip = (u16 *)asi->insn;
-		if (is_wide_instruction(insn))
-			*ip++ = insn >> 16;
-		*ip++ = insn;
-		return;
-	}
-#endif
-	asi->insn[0] = insn;
-}
-
-/*
- * When we modify the register numbers encoded in an instruction to be emulated,
- * the new values come from this define. For ARM and 32-bit Thumb instructions
- * this gives...
- *
- *	bit position	  16  12   8   4   0
- *	---------------+---+---+---+---+---+
- *	register	 r2  r0  r1  --  r3
- */
-#define INSN_NEW_BITS		0x00020103
-
-/* Each nibble has same value as that at INSN_NEW_BITS bit 16 */
-#define INSN_SAMEAS16_BITS	0x22222222
-
-/*
- * Validate and modify each of the registers encoded in an instruction.
- *
- * Each nibble in regs contains a value from enum decode_reg_type. For each
- * non-zero value, the corresponding nibble in pinsn is validated and modified
- * according to the type.
- */
-static bool __kprobes decode_regs(kprobe_opcode_t* pinsn, u32 regs)
-{
-	kprobe_opcode_t insn = *pinsn;
-	kprobe_opcode_t mask = 0xf; /* Start at least significant nibble */
-
-	for (; regs != 0; regs >>= 4, mask <<= 4) {
-
-		kprobe_opcode_t new_bits = INSN_NEW_BITS;
-
-		switch (regs & 0xf) {
-
-		case REG_TYPE_NONE:
-			/* Nibble not a register, skip to next */
-			continue;
-
-		case REG_TYPE_ANY:
-			/* Any register is allowed */
-			break;
-
-		case REG_TYPE_SAMEAS16:
-			/* Replace register with same as@bit position 16 */
-			new_bits = INSN_SAMEAS16_BITS;
-			break;
-
-		case REG_TYPE_SP:
-			/* Only allow SP (R13) */
-			if ((insn ^ 0xdddddddd) & mask)
-				goto reject;
-			break;
-
-		case REG_TYPE_PC:
-			/* Only allow PC (R15) */
-			if ((insn ^ 0xffffffff) & mask)
-				goto reject;
-			break;
-
-		case REG_TYPE_NOSP:
-			/* Reject SP (R13) */
-			if (((insn ^ 0xdddddddd) & mask) == 0)
-				goto reject;
-			break;
-
-		case REG_TYPE_NOSPPC:
-		case REG_TYPE_NOSPPCX:
-			/* Reject SP and PC (R13 and R15) */
-			if (((insn ^ 0xdddddddd) & 0xdddddddd & mask) == 0)
-				goto reject;
-			break;
-
-		case REG_TYPE_NOPCWB:
-			if (!is_writeback(insn))
-				break; /* No writeback, so any register is OK */
-			/* fall through... */
-		case REG_TYPE_NOPC:
-		case REG_TYPE_NOPCX:
-			/* Reject PC (R15) */
-			if (((insn ^ 0xffffffff) & mask) == 0)
-				goto reject;
-			break;
-		}
-
-		/* Replace value of nibble with new register number... */
-		insn &= ~mask;
-		insn |= new_bits & mask;
-	}
-
-	*pinsn = insn;
-	return true;
-
-reject:
-	return false;
-}
-
-static const int decode_struct_sizes[NUM_DECODE_TYPES] = {
-	[DECODE_TYPE_TABLE]	= sizeof(struct decode_table),
-	[DECODE_TYPE_CUSTOM]	= sizeof(struct decode_custom),
-	[DECODE_TYPE_SIMULATE]	= sizeof(struct decode_simulate),
-	[DECODE_TYPE_EMULATE]	= sizeof(struct decode_emulate),
-	[DECODE_TYPE_OR]	= sizeof(struct decode_or),
-	[DECODE_TYPE_REJECT]	= sizeof(struct decode_reject)
-};
-
-/*
- * kprobe_decode_insn operates on data tables in order to decode an ARM
- * architecture instruction onto which a kprobe has been placed.
- *
- * These instruction decoding tables are a concatenation of entries each
- * of which consist of one of the following structs:
- *
- *	decode_table
- *	decode_custom
- *	decode_simulate
- *	decode_emulate
- *	decode_or
- *	decode_reject
- *
- * Each of these starts with a struct decode_header which has the following
- * fields:
- *
- *	type_regs
- *	mask
- *	value
- *
- * The least significant DECODE_TYPE_BITS of type_regs contains a value
- * from enum decode_type, this indicates which of the decode_* structs
- * the entry contains. The value DECODE_TYPE_END indicates the end of the
- * table.
- *
- * When the table is parsed, each entry is checked in turn to see if it
- * matches the instruction to be decoded using the test:
- *
- *	(insn & mask) == value
- *
- * If no match is found before the end of the table is reached then decoding
- * fails with INSN_REJECTED.
- *
- * When a match is found, decode_regs() is called to validate and modify each
- * of the registers encoded in the instruction; the data it uses to do this
- * is (type_regs >> DECODE_TYPE_BITS). A validation failure will cause decoding
- * to fail with INSN_REJECTED.
- *
- * Once the instruction has passed the above tests, further processing
- * depends on the type of the table entry's decode struct.
- *
- */
-int __kprobes
-kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
-				const union decode_item *table, bool thumb)
-{
-	const struct decode_header *h = (struct decode_header *)table;
-	const struct decode_header *next;
-	bool matched = false;
-
-	insn = prepare_emulated_insn(insn, asi, thumb);
-
-	for (;; h = next) {
-		enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK;
-		u32 regs = h->type_regs.bits >> DECODE_TYPE_BITS;
-
-		if (type == DECODE_TYPE_END)
-			return INSN_REJECTED;
-
-		next = (struct decode_header *)
-				((uintptr_t)h + decode_struct_sizes[type]);
-
-		if (!matched && (insn & h->mask.bits) != h->value.bits)
-			continue;
-
-		if (!decode_regs(&insn, regs))
-			return INSN_REJECTED;
-
-		switch (type) {
-
-		case DECODE_TYPE_TABLE: {
-			struct decode_table *d = (struct decode_table *)h;
-			next = (struct decode_header *)d->table.table;
-			break;
-		}
-
-		case DECODE_TYPE_CUSTOM: {
-			struct decode_custom *d = (struct decode_custom *)h;
-			return (*d->decoder.decoder)(insn, asi);
-		}
-
-		case DECODE_TYPE_SIMULATE: {
-			struct decode_simulate *d = (struct decode_simulate *)h;
-			asi->insn_handler = d->handler.handler;
-			return INSN_GOOD_NO_SLOT;
-		}
-
-		case DECODE_TYPE_EMULATE: {
-			struct decode_emulate *d = (struct decode_emulate *)h;
-			asi->insn_handler = d->handler.handler;
-			set_emulated_insn(insn, asi, thumb);
-			return INSN_GOOD;
-		}
-
-		case DECODE_TYPE_OR:
-			matched = true;
-			break;
-
-		case DECODE_TYPE_REJECT:
-		default:
-			return INSN_REJECTED;
-		}
-		}
-	}
diff --git a/arch/arm/kernel/kprobes-thumb.c b/arch/arm/kernel/kprobes-thumb.c
index 6123daf..173b2bc 100644
--- a/arch/arm/kernel/kprobes-thumb.c
+++ b/arch/arm/kernel/kprobes-thumb.c
@@ -544,11 +544,11 @@ static const union decode_item t32_table_1111_0xxx___1[] = {
 	/* YIELD		1111 0011 1010 xxxx 10x0 x000 0000 0001 */
 	DECODE_OR	(0xfff0d7ff, 0xf3a08001),
 	/* SEV			1111 0011 1010 xxxx 10x0 x000 0000 0100 */
-	DECODE_EMULATE	(0xfff0d7ff, 0xf3a08004, kprobe_emulate_none),
+	DECODE_EMULATE	(0xfff0d7ff, 0xf3a08004, probes_emulate_none),
 	/* NOP			1111 0011 1010 xxxx 10x0 x000 0000 0000 */
 	/* WFE			1111 0011 1010 xxxx 10x0 x000 0000 0010 */
 	/* WFI			1111 0011 1010 xxxx 10x0 x000 0000 0011 */
-	DECODE_SIMULATE	(0xfff0d7fc, 0xf3a08000, kprobe_simulate_nop),
+	DECODE_SIMULATE	(0xfff0d7fc, 0xf3a08000, probes_simulate_nop),
 
 	/* MRS Rd, CPSR		1111 0011 1110 xxxx 10x0 xxxx xxxx xxxx */
 	DECODE_SIMULATEX(0xfff0d000, 0xf3e08000, t32_simulate_mrs,
@@ -589,7 +589,7 @@ static const union decode_item t32_table_1111_100x_x0x1__1111[] = {
 
 	/* PLD (literal)	1111 1000 x001 1111 1111 xxxx xxxx xxxx */
 	/* PLI (literal)	1111 1001 x001 1111 1111 xxxx xxxx xxxx */
-	DECODE_SIMULATE	(0xfe7ff000, 0xf81ff000, kprobe_simulate_nop),
+	DECODE_SIMULATE	(0xfe7ff000, 0xf81ff000, probes_simulate_nop),
 
 	/* PLD{W} (immediate)	1111 1000 10x1 xxxx 1111 xxxx xxxx xxxx */
 	DECODE_OR	(0xffd0f000, 0xf890f000),
@@ -598,13 +598,13 @@ static const union decode_item t32_table_1111_100x_x0x1__1111[] = {
 	/* PLI (immediate)	1111 1001 1001 xxxx 1111 xxxx xxxx xxxx */
 	DECODE_OR	(0xfff0f000, 0xf990f000),
 	/* PLI (immediate)	1111 1001 0001 xxxx 1111 1100 xxxx xxxx */
-	DECODE_SIMULATEX(0xfff0ff00, 0xf910fc00, kprobe_simulate_nop,
+	DECODE_SIMULATEX(0xfff0ff00, 0xf910fc00, probes_simulate_nop,
 						 REGS(NOPCX, 0, 0, 0, 0)),
 
 	/* PLD{W} (register)	1111 1000 00x1 xxxx 1111 0000 00xx xxxx */
 	DECODE_OR	(0xffd0ffc0, 0xf810f000),
 	/* PLI (register)	1111 1001 0001 xxxx 1111 0000 00xx xxxx */
-	DECODE_SIMULATEX(0xfff0ffc0, 0xf910f000, kprobe_simulate_nop,
+	DECODE_SIMULATEX(0xfff0ffc0, 0xf910f000, probes_simulate_nop,
 						 REGS(NOPCX, 0, 0, 0, NOSPPC)),
 
 	/* Other unallocated instructions...				*/
@@ -1274,11 +1274,11 @@ static const union decode_item t16_table_1011[] = {
 	/* YIELD			1011 1111 0001 0000 */
 	DECODE_OR	(0xffff, 0xbf10),
 	/* SEV				1011 1111 0100 0000 */
-	DECODE_EMULATE	(0xffff, 0xbf40, kprobe_emulate_none),
+	DECODE_EMULATE	(0xffff, 0xbf40, probes_emulate_none),
 	/* NOP				1011 1111 0000 0000 */
 	/* WFE				1011 1111 0010 0000 */
 	/* WFI				1011 1111 0011 0000 */
-	DECODE_SIMULATE	(0xffcf, 0xbf00, kprobe_simulate_nop),
+	DECODE_SIMULATE	(0xffcf, 0xbf00, probes_simulate_nop),
 	/* Unassigned hints		1011 1111 xxxx 0000 */
 	DECODE_REJECT	(0xff0f, 0xbf00),
 	/* IT				1011 1111 xxxx xxxx */
diff --git a/arch/arm/kernel/kprobes.h b/arch/arm/kernel/kprobes.h
index 38945f7..9aa2f15 100644
--- a/arch/arm/kernel/kprobes.h
+++ b/arch/arm/kernel/kprobes.h
@@ -161,8 +161,8 @@ static inline void __kprobes alu_write_pc(long pcv, struct pt_regs *regs)
 }
 
 
-void __kprobes kprobe_simulate_nop(struct kprobe *p, struct pt_regs *regs);
-void __kprobes kprobe_emulate_none(struct kprobe *p, struct pt_regs *regs);
+void __kprobes probes_simulate_nop(struct kprobe *p, struct pt_regs *regs);
+void __kprobes probes_emulate_none(struct kprobe *p, struct pt_regs *regs);
 
 enum kprobe_insn __kprobes
 kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi);
diff --git a/arch/arm/kernel/probes-arm.c b/arch/arm/kernel/probes-arm.c
new file mode 100644
index 0000000..e1b1a6e
--- /dev/null
+++ b/arch/arm/kernel/probes-arm.c
@@ -0,0 +1,311 @@
+/*
+ * arch/arm/kernel/probes-arm.c
+ *
+ * Some code moved here from arch/arm/kernel/kprobes-arm.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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/kprobes.h>
+
+#include "probes.h"
+#include "kprobes.h"
+
+#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
+
+#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25)
+
+#if  __LINUX_ARM_ARCH__ >= 6
+#define BLX(reg)	"blx	"reg"		\n\t"
+#else
+#define BLX(reg)	"mov	lr, pc		\n\t"	\
+			"mov	pc, "reg"	\n\t"
+#endif
+
+void __kprobes simulate_bbl(struct kprobe *p, struct pt_regs *regs)
+{
+	kprobe_opcode_t insn = p->opcode;
+	long iaddr = (long)p->addr;
+	int disp  = branch_displacement(insn);
+
+	if (insn & (1 << 24))
+		regs->ARM_lr = iaddr + 4;
+
+	regs->ARM_pc = iaddr + 8 + disp;
+}
+
+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;
+}
+
+void __kprobes simulate_blx2bx(struct kprobe *p, struct pt_regs *regs)
+{
+	kprobe_opcode_t insn = p->opcode;
+	int rm = insn & 0xf;
+	long rmv = regs->uregs[rm];
+
+	if (insn & (1 << 5))
+		regs->ARM_lr = (long)p->addr + 4;
+
+	regs->ARM_pc = rmv & ~0x1;
+	regs->ARM_cpsr &= ~PSR_T_BIT;
+	if (rmv & 0x1)
+		regs->ARM_cpsr |= PSR_T_BIT;
+}
+
+void __kprobes simulate_mrs(struct kprobe *p, struct pt_regs *regs)
+{
+	kprobe_opcode_t insn = p->opcode;
+	int rd = (insn >> 12) & 0xf;
+	unsigned long mask = 0xf8ff03df; /* Mask out execution state */
+	regs->uregs[rd] = regs->ARM_cpsr & mask;
+}
+
+void __kprobes simulate_mov_ipsp(struct kprobe *p, struct pt_regs *regs)
+{
+	regs->uregs[12] = regs->uregs[13];
+}
+
+void __kprobes
+emulate_ldrdstrd(struct kprobe *p, struct pt_regs *regs)
+{
+	kprobe_opcode_t insn = p->opcode;
+	unsigned long pc = (unsigned long)p->addr + 8;
+	int rt = (insn >> 12) & 0xf;
+	int rn = (insn >> 16) & 0xf;
+	int rm = insn & 0xf;
+
+	register unsigned long rtv asm("r0") = regs->uregs[rt];
+	register unsigned long rt2v asm("r1") = regs->uregs[rt+1];
+	register unsigned long rnv asm("r2") = (rn == 15) ? pc
+							  : regs->uregs[rn];
+	register unsigned long rmv asm("r3") = regs->uregs[rm];
+
+	__asm__ __volatile__ (
+		BLX("%[fn]")
+		: "=r" (rtv), "=r" (rt2v), "=r" (rnv)
+		: "0" (rtv), "1" (rt2v), "2" (rnv), "r" (rmv),
+		  [fn] "r" (p->ainsn.insn_fn)
+		: "lr", "memory", "cc"
+	);
+
+	regs->uregs[rt] = rtv;
+	regs->uregs[rt+1] = rt2v;
+	if (is_writeback(insn))
+		regs->uregs[rn] = rnv;
+}
+
+void __kprobes
+emulate_ldr(struct kprobe *p, struct pt_regs *regs)
+{
+	kprobe_opcode_t insn = p->opcode;
+	unsigned long pc = (unsigned long)p->addr + 8;
+	int rt = (insn >> 12) & 0xf;
+	int rn = (insn >> 16) & 0xf;
+	int rm = insn & 0xf;
+
+	register unsigned long rtv asm("r0");
+	register unsigned long rnv asm("r2") = (rn == 15) ? pc
+							  : regs->uregs[rn];
+	register unsigned long rmv asm("r3") = regs->uregs[rm];
+
+	__asm__ __volatile__ (
+		BLX("%[fn]")
+		: "=r" (rtv), "=r" (rnv)
+		: "1" (rnv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
+		: "lr", "memory", "cc"
+	);
+
+	if (rt == 15)
+		load_write_pc(rtv, regs);
+	else
+		regs->uregs[rt] = rtv;
+
+	if (is_writeback(insn))
+		regs->uregs[rn] = rnv;
+}
+
+void __kprobes
+emulate_str(struct kprobe *p, struct pt_regs *regs)
+{
+	kprobe_opcode_t insn = p->opcode;
+	unsigned long rtpc = (unsigned long)p->addr + str_pc_offset;
+	unsigned long rnpc = (unsigned long)p->addr + 8;
+	int rt = (insn >> 12) & 0xf;
+	int rn = (insn >> 16) & 0xf;
+	int rm = insn & 0xf;
+
+	register unsigned long rtv asm("r0") = (rt == 15) ? rtpc
+							  : regs->uregs[rt];
+	register unsigned long rnv asm("r2") = (rn == 15) ? rnpc
+							  : regs->uregs[rn];
+	register unsigned long rmv asm("r3") = regs->uregs[rm];
+
+	__asm__ __volatile__ (
+		BLX("%[fn]")
+		: "=r" (rnv)
+		: "r" (rtv), "0" (rnv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
+		: "lr", "memory", "cc"
+	);
+
+	if (is_writeback(insn))
+		regs->uregs[rn] = rnv;
+}
+
+void __kprobes
+emulate_rd12rn16rm0rs8_rwflags(struct kprobe *p, struct pt_regs *regs)
+{
+	kprobe_opcode_t insn = p->opcode;
+	unsigned long pc = (unsigned long)p->addr + 8;
+	int rd = (insn >> 12) & 0xf;
+	int rn = (insn >> 16) & 0xf;
+	int rm = insn & 0xf;
+	int rs = (insn >> 8) & 0xf;
+
+	register unsigned long rdv asm("r0") = regs->uregs[rd];
+	register unsigned long rnv asm("r2") = (rn == 15) ? pc
+							  : regs->uregs[rn];
+	register unsigned long rmv asm("r3") = (rm == 15) ? pc
+							  : regs->uregs[rm];
+	register unsigned long rsv asm("r1") = regs->uregs[rs];
+	unsigned long cpsr = regs->ARM_cpsr;
+
+	__asm__ __volatile__ (
+		"msr	cpsr_fs, %[cpsr]	\n\t"
+		BLX("%[fn]")
+		"mrs	%[cpsr], cpsr		\n\t"
+		: "=r" (rdv), [cpsr] "=r" (cpsr)
+		: "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv),
+		  "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
+		: "lr", "memory", "cc"
+	);
+
+	if (rd == 15)
+		alu_write_pc(rdv, regs);
+	else
+		regs->uregs[rd] = rdv;
+	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
+}
+
+void __kprobes
+emulate_rd12rn16rm0_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
+{
+	kprobe_opcode_t insn = p->opcode;
+	int rd = (insn >> 12) & 0xf;
+	int rn = (insn >> 16) & 0xf;
+	int rm = insn & 0xf;
+
+	register unsigned long rdv asm("r0") = regs->uregs[rd];
+	register unsigned long rnv asm("r2") = regs->uregs[rn];
+	register unsigned long rmv asm("r3") = regs->uregs[rm];
+	unsigned long cpsr = regs->ARM_cpsr;
+
+	__asm__ __volatile__ (
+		"msr	cpsr_fs, %[cpsr]	\n\t"
+		BLX("%[fn]")
+		"mrs	%[cpsr], cpsr		\n\t"
+		: "=r" (rdv), [cpsr] "=r" (cpsr)
+		: "0" (rdv), "r" (rnv), "r" (rmv),
+		  "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
+		: "lr", "memory", "cc"
+	);
+
+	regs->uregs[rd] = rdv;
+	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
+}
+
+void __kprobes
+emulate_rd16rn12rm0rs8_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
+{
+	kprobe_opcode_t insn = p->opcode;
+	int rd = (insn >> 16) & 0xf;
+	int rn = (insn >> 12) & 0xf;
+	int rm = insn & 0xf;
+	int rs = (insn >> 8) & 0xf;
+
+	register unsigned long rdv asm("r2") = regs->uregs[rd];
+	register unsigned long rnv asm("r0") = regs->uregs[rn];
+	register unsigned long rmv asm("r3") = regs->uregs[rm];
+	register unsigned long rsv asm("r1") = regs->uregs[rs];
+	unsigned long cpsr = regs->ARM_cpsr;
+
+	__asm__ __volatile__ (
+		"msr	cpsr_fs, %[cpsr]	\n\t"
+		BLX("%[fn]")
+		"mrs	%[cpsr], cpsr		\n\t"
+		: "=r" (rdv), [cpsr] "=r" (cpsr)
+		: "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv),
+		  "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
+		: "lr", "memory", "cc"
+	);
+
+	regs->uregs[rd] = rdv;
+	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
+}
+
+void __kprobes
+emulate_rd12rm0_noflags_nopc(struct kprobe *p, struct pt_regs *regs)
+{
+	kprobe_opcode_t insn = p->opcode;
+	int rd = (insn >> 12) & 0xf;
+	int rm = insn & 0xf;
+
+	register unsigned long rdv asm("r0") = regs->uregs[rd];
+	register unsigned long rmv asm("r3") = regs->uregs[rm];
+
+	__asm__ __volatile__ (
+		BLX("%[fn]")
+		: "=r" (rdv)
+		: "0" (rdv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
+		: "lr", "memory", "cc"
+	);
+
+	regs->uregs[rd] = rdv;
+}
+
+void __kprobes
+emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
+{
+	kprobe_opcode_t insn = p->opcode;
+	int rdlo = (insn >> 12) & 0xf;
+	int rdhi = (insn >> 16) & 0xf;
+	int rn = insn & 0xf;
+	int rm = (insn >> 8) & 0xf;
+
+	register unsigned long rdlov asm("r0") = regs->uregs[rdlo];
+	register unsigned long rdhiv asm("r2") = regs->uregs[rdhi];
+	register unsigned long rnv asm("r3") = regs->uregs[rn];
+	register unsigned long rmv asm("r1") = regs->uregs[rm];
+	unsigned long cpsr = regs->ARM_cpsr;
+
+	__asm__ __volatile__ (
+		"msr	cpsr_fs, %[cpsr]	\n\t"
+		BLX("%[fn]")
+		"mrs	%[cpsr], cpsr		\n\t"
+		: "=r" (rdlov), "=r" (rdhiv), [cpsr] "=r" (cpsr)
+		: "0" (rdlov), "1" (rdhiv), "r" (rnv), "r" (rmv),
+		  "2" (cpsr), [fn] "r" (p->ainsn.insn_fn)
+		: "lr", "memory", "cc"
+	);
+
+	regs->uregs[rdlo] = rdlov;
+	regs->uregs[rdhi] = rdhiv;
+	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
+}
diff --git a/arch/arm/kernel/probes.c b/arch/arm/kernel/probes.c
new file mode 100644
index 0000000..86c63f3
--- /dev/null
+++ b/arch/arm/kernel/probes.c
@@ -0,0 +1,286 @@
+/*
+ * arch/arm/kernel/probes.c
+ *
+ * Some contents moved here from arch/arm/include/asm/kprobes-common.c
+ *
+ * Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
+ *
+ * Some contents moved here from arch/arm/include/asm/kprobes-arm.c which is
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/kprobes.h>
+
+#include "probes.h"
+#include "kprobes.h"
+
+void __kprobes probes_simulate_nop(struct kprobe *p, struct pt_regs *regs)
+{
+}
+
+void __kprobes probes_emulate_none(struct kprobe *p, struct pt_regs *regs)
+{
+	p->ainsn.insn_fn();
+}
+
+/*
+ * Prepare an instruction slot to receive an instruction for emulating.
+ * This is done by placing a subroutine return after the location where the
+ * instruction will be placed. We also modify ARM instructions to be
+ * unconditional as the condition code will already be checked before any
+ * emulation handler is called.
+ */
+static kprobe_opcode_t __kprobes
+prepare_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
+								bool thumb)
+{
+#ifdef CONFIG_THUMB2_KERNEL
+	if (thumb) {
+		u16 *thumb_insn = (u16 *)asi->insn;
+		thumb_insn[1] = 0x4770; /* Thumb bx lr */
+		thumb_insn[2] = 0x4770; /* Thumb bx lr */
+		return insn;
+	}
+	asi->insn[1] = 0xe12fff1e; /* ARM bx lr */
+#else
+	asi->insn[1] = 0xe1a0f00e; /* mov pc, lr */
+#endif
+	/* Make an ARM instruction unconditional */
+	if (insn < 0xe0000000)
+		insn = (insn | 0xe0000000) & ~0x10000000;
+	return insn;
+}
+
+/*
+ * Write a (probably modified) instruction into the slot previously prepared by
+ * prepare_emulated_insn
+ */
+static void  __kprobes
+set_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
+								bool thumb)
+{
+#ifdef CONFIG_THUMB2_KERNEL
+	if (thumb) {
+		u16 *ip = (u16 *)asi->insn;
+		if (is_wide_instruction(insn))
+			*ip++ = insn >> 16;
+		*ip++ = insn;
+		return;
+	}
+#endif
+	asi->insn[0] = insn;
+}
+
+/*
+ * When we modify the register numbers encoded in an instruction to be emulated,
+ * the new values come from this define. For ARM and 32-bit Thumb instructions
+ * this gives...
+ *
+ *	bit position	  16  12   8   4   0
+ *	---------------+---+---+---+---+---+
+ *	register	 r2  r0  r1  --  r3
+ */
+#define INSN_NEW_BITS		0x00020103
+
+/* Each nibble has same value as that at INSN_NEW_BITS bit 16 */
+#define INSN_SAMEAS16_BITS	0x22222222
+
+/*
+ * Validate and modify each of the registers encoded in an instruction.
+ *
+ * Each nibble in regs contains a value from enum decode_reg_type. For each
+ * non-zero value, the corresponding nibble in pinsn is validated and modified
+ * according to the type.
+ */
+static bool __kprobes decode_regs(kprobe_opcode_t *pinsn, u32 regs)
+{
+	kprobe_opcode_t insn = *pinsn;
+	kprobe_opcode_t mask = 0xf; /* Start at least significant nibble */
+
+	for (; regs != 0; regs >>= 4, mask <<= 4) {
+
+		kprobe_opcode_t new_bits = INSN_NEW_BITS;
+
+		switch (regs & 0xf) {
+
+		case REG_TYPE_NONE:
+			/* Nibble not a register, skip to next */
+			continue;
+
+		case REG_TYPE_ANY:
+			/* Any register is allowed */
+			break;
+
+		case REG_TYPE_SAMEAS16:
+			/* Replace register with same as@bit position 16 */
+			new_bits = INSN_SAMEAS16_BITS;
+			break;
+
+		case REG_TYPE_SP:
+			/* Only allow SP (R13) */
+			if ((insn ^ 0xdddddddd) & mask)
+				goto reject;
+			break;
+
+		case REG_TYPE_PC:
+			/* Only allow PC (R15) */
+			if ((insn ^ 0xffffffff) & mask)
+				goto reject;
+			break;
+
+		case REG_TYPE_NOSP:
+			/* Reject SP (R13) */
+			if (((insn ^ 0xdddddddd) & mask) == 0)
+				goto reject;
+			break;
+
+		case REG_TYPE_NOSPPC:
+		case REG_TYPE_NOSPPCX:
+			/* Reject SP and PC (R13 and R15) */
+			if (((insn ^ 0xdddddddd) & 0xdddddddd & mask) == 0)
+				goto reject;
+			break;
+
+		case REG_TYPE_NOPCWB:
+			if (!is_writeback(insn))
+				break; /* No writeback, so any register is OK */
+			/* fall through... */
+		case REG_TYPE_NOPC:
+		case REG_TYPE_NOPCX:
+			/* Reject PC (R15) */
+			if (((insn ^ 0xffffffff) & mask) == 0)
+				goto reject;
+			break;
+		}
+
+		/* Replace value of nibble with new register number... */
+		insn &= ~mask;
+		insn |= new_bits & mask;
+	}
+
+	*pinsn = insn;
+	return true;
+
+reject:
+	return false;
+}
+
+static const int decode_struct_sizes[NUM_DECODE_TYPES] = {
+	[DECODE_TYPE_TABLE]	= sizeof(struct decode_table),
+	[DECODE_TYPE_CUSTOM]	= sizeof(struct decode_custom),
+	[DECODE_TYPE_SIMULATE]	= sizeof(struct decode_simulate),
+	[DECODE_TYPE_EMULATE]	= sizeof(struct decode_emulate),
+	[DECODE_TYPE_OR]	= sizeof(struct decode_or),
+	[DECODE_TYPE_REJECT]	= sizeof(struct decode_reject)
+};
+
+/*
+ * kprobe_decode_insn operates on data tables in order to decode an ARM
+ * architecture instruction onto which a kprobe has been placed.
+ *
+ * These instruction decoding tables are a concatenation of entries each
+ * of which consist of one of the following structs:
+ *
+ *	decode_table
+ *	decode_custom
+ *	decode_simulate
+ *	decode_emulate
+ *	decode_or
+ *	decode_reject
+ *
+ * Each of these starts with a struct decode_header which has the following
+ * fields:
+ *
+ *	type_regs
+ *	mask
+ *	value
+ *
+ * The least significant DECODE_TYPE_BITS of type_regs contains a value
+ * from enum decode_type, this indicates which of the decode_* structs
+ * the entry contains. The value DECODE_TYPE_END indicates the end of the
+ * table.
+ *
+ * When the table is parsed, each entry is checked in turn to see if it
+ * matches the instruction to be decoded using the test:
+ *
+ *	(insn & mask) == value
+ *
+ * If no match is found before the end of the table is reached then decoding
+ * fails with INSN_REJECTED.
+ *
+ * When a match is found, decode_regs() is called to validate and modify each
+ * of the registers encoded in the instruction; the data it uses to do this
+ * is (type_regs >> DECODE_TYPE_BITS). A validation failure will cause decoding
+ * to fail with INSN_REJECTED.
+ *
+ * Once the instruction has passed the above tests, further processing
+ * depends on the type of the table entry's decode struct.
+ *
+ */
+int __kprobes
+kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
+				const union decode_item *table, bool thumb)
+{
+	const struct decode_header *h = (struct decode_header *)table;
+	const struct decode_header *next;
+	bool matched = false;
+
+	insn = prepare_emulated_insn(insn, asi, thumb);
+
+	for (;; h = next) {
+		enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK;
+		u32 regs = h->type_regs.bits >> DECODE_TYPE_BITS;
+
+		if (type == DECODE_TYPE_END)
+			return INSN_REJECTED;
+
+		next = (struct decode_header *)
+				((uintptr_t)h + decode_struct_sizes[type]);
+
+		if (!matched && (insn & h->mask.bits) != h->value.bits)
+			continue;
+
+		if (!decode_regs(&insn, regs))
+			return INSN_REJECTED;
+
+		switch (type) {
+
+		case DECODE_TYPE_TABLE: {
+			struct decode_table *d = (struct decode_table *)h;
+			next = (struct decode_header *)d->table.table;
+			break;
+		}
+
+		case DECODE_TYPE_CUSTOM: {
+			struct decode_custom *d = (struct decode_custom *)h;
+			return (*d->decoder.decoder)(insn, asi);
+		}
+
+		case DECODE_TYPE_SIMULATE: {
+			struct decode_simulate *d = (struct decode_simulate *)h;
+			asi->insn_handler = d->handler.handler;
+			return INSN_GOOD_NO_SLOT;
+		}
+
+		case DECODE_TYPE_EMULATE: {
+			struct decode_emulate *d = (struct decode_emulate *)h;
+			asi->insn_handler = d->handler.handler;
+			set_emulated_insn(insn, asi, thumb);
+			return INSN_GOOD;
+		}
+
+		case DECODE_TYPE_OR:
+			matched = true;
+			break;
+
+		case DECODE_TYPE_REJECT:
+		default:
+			return INSN_REJECTED;
+		}
+	}
+}
diff --git a/arch/arm/kernel/probes.h b/arch/arm/kernel/probes.h
new file mode 100644
index 0000000..56eec12
--- /dev/null
+++ b/arch/arm/kernel/probes.h
@@ -0,0 +1,23 @@
+#ifndef _ARM_KERNEL_PROBES_H
+#define  _ARM_KERNEL_PROBES_H
+
+void __kprobes simulate_bbl(struct kprobe *p, struct pt_regs *regs);
+void __kprobes simulate_blx1(struct kprobe *p, struct pt_regs *regs);
+void __kprobes simulate_blx2bx(struct kprobe *p, struct pt_regs *regs);
+void __kprobes simulate_mrs(struct kprobe *p, struct pt_regs *regs);
+void __kprobes simulate_mov_ipsp(struct kprobe *p, struct pt_regs *regs);
+void __kprobes emulate_ldrdstrd(struct kprobe *p, struct pt_regs *regs);
+void __kprobes emulate_ldr(struct kprobe *p, struct pt_regs *regs);
+void __kprobes emulate_str(struct kprobe *p, struct pt_regs *regs);
+void __kprobes emulate_rd12rn16rm0rs8_rwflags(struct kprobe *p,
+					      struct pt_regs *regs);
+void __kprobes emulate_rd12rn16rm0_rwflags_nopc(struct kprobe *p,
+						struct pt_regs *regs);
+void __kprobes emulate_rd16rn12rm0rs8_rwflags_nopc(struct kprobe *p,
+						   struct pt_regs *regs);
+void __kprobes emulate_rd12rm0_noflags_nopc(struct kprobe *p,
+					    struct pt_regs *regs);
+void __kprobes emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(struct kprobe *p,
+						       struct pt_regs *regs);
+
+#endif
-- 
1.8.1.2

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 8/9] ARM: Add "action" table for kprobes/uprobes instruction
  2013-08-01 23:45 ` David Long
@ 2013-08-01 23:45   ` David Long
  -1 siblings, 0 replies; 32+ messages in thread
From: David Long @ 2013-08-01 23:45 UTC (permalink / raw)
  To: linux-arm-kernel; +Cc: Rabin Vincent, Jon Medhurst (Tixy), linux-kernel

From: "David A. Long" <dave.long@linaro.org>

Add a table of functions used by the (previously) kprobes instruction
parsing code so that it can also be used by uprobes. kprobes and
(eventually) uprobes will each provide their own unique table of
actions for each category of instruction recognized by the parser.

Signed-off-by: David A. Long <dave.long@linaro.org>
---
 arch/arm/kernel/kprobes-arm.c    | 188 +++++++++++++++++----------------
 arch/arm/kernel/kprobes-common.c |   3 +-
 arch/arm/kernel/kprobes-thumb.c  | 217 +++++++++++++++++++++++++--------------
 arch/arm/kernel/kprobes.c        |  11 +-
 arch/arm/kernel/kprobes.h        |  64 ++++++------
 arch/arm/kernel/probes-arm.c     |   2 +-
 arch/arm/kernel/probes-arm.h     |  60 +++++++++++
 arch/arm/kernel/probes-thumb.h   |  59 +++++++++++
 arch/arm/kernel/probes.c         |  99 ++++++++++++------
 arch/arm/kernel/probes.h         |  12 +++
 10 files changed, 488 insertions(+), 227 deletions(-)
 create mode 100644 arch/arm/kernel/probes-arm.h
 create mode 100644 arch/arm/kernel/probes-thumb.h

diff --git a/arch/arm/kernel/kprobes-arm.c b/arch/arm/kernel/kprobes-arm.c
index d6503cc..0620eba 100644
--- a/arch/arm/kernel/kprobes-arm.c
+++ b/arch/arm/kernel/kprobes-arm.c
@@ -62,38 +62,50 @@
 #include <linux/kprobes.h>
 #include <linux/module.h>
 
-#include "probes.h"
 #include "kprobes.h"
-
-/*
- * To avoid 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 performance.
- *
- * 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 perform register writeback)
- * 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.
- */
+#include "probes.h"
+#include "probes-arm.h"
+
+const union decode_item kprobes_probes_actions[] = {
+	[PROBES_EMULATE_NONE] = {.handler = probes_emulate_none},
+	[PROBES_SIMULATE_NOP] = {.handler = probes_simulate_nop},
+	[PROBES_PRELOAD_IMM] = {.handler = probes_simulate_nop},
+	[PROBES_PRELOAD_REG] = {.handler = probes_simulate_nop},
+	[PROBES_BRANCH_IMM] = {.handler = simulate_blx1},
+	[PROBES_MRS] = {.handler = simulate_mrs},
+	[PROBES_BRANCH_REG] = {.handler = simulate_blx2bx},
+	[PROBES_CLZ] = {.handler = emulate_rd12rm0_noflags_nopc},
+	[PROBES_SATURATING_ARITHMETIC] = {
+		.handler = emulate_rd12rn16rm0_rwflags_nopc},
+	[PROBES_MUL1] = {.handler = emulate_rdlo12rdhi16rn0rm8_rwflags_nopc},
+	[PROBES_MUL2] = {.handler = emulate_rd16rn12rm0rs8_rwflags_nopc},
+	[PROBES_SWP] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
+	[PROBES_LDRSTRD] = {.handler = emulate_ldrdstrd},
+	[PROBES_LOAD_EXTRA] = {.handler = emulate_ldr},
+	[PROBES_LOAD] = {.handler = emulate_ldr},
+	[PROBES_STORE_EXTRA] = {.handler = emulate_str},
+	[PROBES_STORE] = {.handler = emulate_str},
+	[PROBES_MOV_IP_SP] = {.handler = simulate_mov_ipsp},
+	[PROBES_DATA_PROCESSING_REG] = {
+		.handler = emulate_rd12rn16rm0rs8_rwflags},
+	[PROBES_DATA_PROCESSING_IMM] = {
+		.handler = emulate_rd12rn16rm0rs8_rwflags},
+	[PROBES_MOV_HALFWORD] = {.handler = emulate_rd12rm0_noflags_nopc},
+	[PROBES_SEV] = {.handler = probes_emulate_none},
+	[PROBES_WFE] = {.handler = probes_simulate_nop},
+	[PROBES_SATURATE] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
+	[PROBES_REV] = {.handler = emulate_rd12rm0_noflags_nopc},
+	[PROBES_MMI] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
+	[PROBES_PACK] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
+	[PROBES_EXTEND] = {.handler = emulate_rd12rm0_noflags_nopc},
+	[PROBES_EXTEND_ADD] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
+	[PROBES_MUL_ADD_LONG] = {
+		.handler = emulate_rdlo12rdhi16rn0rm8_rwflags_nopc},
+	[PROBES_MUL_ADD] = {.handler = emulate_rd16rn12rm0rs8_rwflags_nopc},
+	[PROBES_BITFIELD] = {.handler = emulate_rd12rm0_noflags_nopc},
+	[PROBES_BRANCH] = {.handler = simulate_bbl},
+	[PROBES_LDMSTM] = {.decoder = kprobe_decode_ldmstm}
+};
 
 /*
  * For the instruction masking and comparisons in all the "space_*"
@@ -112,16 +124,16 @@ static const union decode_item arm_1111_table[] = {
 	/* PLDI (immediate)	1111 0100 x101 xxxx xxxx xxxx xxxx xxxx */
 	/* PLDW (immediate)	1111 0101 x001 xxxx xxxx xxxx xxxx xxxx */
 	/* PLD (immediate)	1111 0101 x101 xxxx xxxx xxxx xxxx xxxx */
-	DECODE_SIMULATE	(0xfe300000, 0xf4100000, probes_simulate_nop),
+	DECODE_SIMULATE	(0xfe300000, 0xf4100000, PROBES_PRELOAD_IMM),
 
 	/* memory hint		1111 0110 x001 xxxx xxxx xxxx xxx0 xxxx */
 	/* PLDI (register)	1111 0110 x101 xxxx xxxx xxxx xxx0 xxxx */
 	/* PLDW (register)	1111 0111 x001 xxxx xxxx xxxx xxx0 xxxx */
 	/* PLD (register)	1111 0111 x101 xxxx xxxx xxxx xxx0 xxxx */
-	DECODE_SIMULATE	(0xfe300010, 0xf6100000, probes_simulate_nop),
+	DECODE_SIMULATE	(0xfe300010, 0xf6100000, PROBES_PRELOAD_REG),
 
 	/* BLX (immediate)	1111 101x xxxx xxxx xxxx xxxx xxxx xxxx */
-	DECODE_SIMULATE	(0xfe000000, 0xfa000000, simulate_blx1),
+	DECODE_SIMULATE	(0xfe000000, 0xfa000000, PROBES_BRANCH_IMM),
 
 	/* CPS			1111 0001 0000 xxx0 xxxx xxxx xx0x xxxx */
 	/* SETEND		1111 0001 0000 0001 xxxx xxxx 0000 xxxx */
@@ -145,25 +157,25 @@ static const union decode_item arm_cccc_0001_0xx0____0xxx_table[] = {
 	/* Miscellaneous instructions					*/
 
 	/* MRS cpsr		cccc 0001 0000 xxxx xxxx xxxx 0000 xxxx */
-	DECODE_SIMULATEX(0x0ff000f0, 0x01000000, simulate_mrs,
+	DECODE_SIMULATEX(0x0ff000f0, 0x01000000, PROBES_MRS,
 						 REGS(0, NOPC, 0, 0, 0)),
 
 	/* BX			cccc 0001 0010 xxxx xxxx xxxx 0001 xxxx */
-	DECODE_SIMULATE	(0x0ff000f0, 0x01200010, simulate_blx2bx),
+	DECODE_SIMULATE	(0x0ff000f0, 0x01200010, PROBES_BRANCH_REG),
 
 	/* BLX (register)	cccc 0001 0010 xxxx xxxx xxxx 0011 xxxx */
-	DECODE_SIMULATEX(0x0ff000f0, 0x01200030, simulate_blx2bx,
+	DECODE_SIMULATEX(0x0ff000f0, 0x01200030, PROBES_BRANCH_REG,
 						 REGS(0, 0, 0, 0, NOPC)),
 
 	/* CLZ			cccc 0001 0110 xxxx xxxx xxxx 0001 xxxx */
-	DECODE_EMULATEX	(0x0ff000f0, 0x01600010, emulate_rd12rm0_noflags_nopc,
+	DECODE_EMULATEX	(0x0ff000f0, 0x01600010, PROBES_CLZ,
 						 REGS(0, NOPC, 0, 0, NOPC)),
 
 	/* QADD			cccc 0001 0000 xxxx xxxx xxxx 0101 xxxx */
 	/* QSUB			cccc 0001 0010 xxxx xxxx xxxx 0101 xxxx */
 	/* QDADD		cccc 0001 0100 xxxx xxxx xxxx 0101 xxxx */
 	/* QDSUB		cccc 0001 0110 xxxx xxxx xxxx 0101 xxxx */
-	DECODE_EMULATEX	(0x0f9000f0, 0x01000050, emulate_rd12rn16rm0_rwflags_nopc,
+	DECODE_EMULATEX	(0x0f9000f0, 0x01000050, PROBES_SATURATING_ARITHMETIC,
 						 REGS(NOPC, NOPC, 0, 0, NOPC)),
 
 	/* BXJ			cccc 0001 0010 xxxx xxxx xxxx 0010 xxxx */
@@ -179,19 +191,19 @@ static const union decode_item arm_cccc_0001_0xx0____1xx0_table[] = {
 	/* Halfword multiply and multiply-accumulate			*/
 
 	/* SMLALxy		cccc 0001 0100 xxxx xxxx xxxx 1xx0 xxxx */
-	DECODE_EMULATEX	(0x0ff00090, 0x01400080, emulate_rdlo12rdhi16rn0rm8_rwflags_nopc,
+	DECODE_EMULATEX	(0x0ff00090, 0x01400080, PROBES_MUL1,
 						 REGS(NOPC, NOPC, NOPC, 0, NOPC)),
 
 	/* SMULWy		cccc 0001 0010 xxxx xxxx xxxx 1x10 xxxx */
 	DECODE_OR	(0x0ff000b0, 0x012000a0),
 	/* SMULxy		cccc 0001 0110 xxxx xxxx xxxx 1xx0 xxxx */
-	DECODE_EMULATEX	(0x0ff00090, 0x01600080, emulate_rd16rn12rm0rs8_rwflags_nopc,
+	DECODE_EMULATEX	(0x0ff00090, 0x01600080, PROBES_MUL2,
 						 REGS(NOPC, 0, NOPC, 0, NOPC)),
 
 	/* SMLAxy		cccc 0001 0000 xxxx xxxx xxxx 1xx0 xxxx */
 	DECODE_OR	(0x0ff00090, 0x01000080),
 	/* SMLAWy		cccc 0001 0010 xxxx xxxx xxxx 1x00 xxxx */
-	DECODE_EMULATEX	(0x0ff000b0, 0x01200080, emulate_rd16rn12rm0rs8_rwflags_nopc,
+	DECODE_EMULATEX	(0x0ff000b0, 0x01200080, PROBES_MUL2,
 						 REGS(NOPC, NOPC, NOPC, 0, NOPC)),
 
 	DECODE_END
@@ -202,14 +214,14 @@ static const union decode_item arm_cccc_0000_____1001_table[] = {
 
 	/* MUL			cccc 0000 0000 xxxx xxxx xxxx 1001 xxxx */
 	/* MULS			cccc 0000 0001 xxxx xxxx xxxx 1001 xxxx */
-	DECODE_EMULATEX	(0x0fe000f0, 0x00000090, emulate_rd16rn12rm0rs8_rwflags_nopc,
+	DECODE_EMULATEX	(0x0fe000f0, 0x00000090, PROBES_MUL2,
 						 REGS(NOPC, 0, NOPC, 0, NOPC)),
 
 	/* MLA			cccc 0000 0010 xxxx xxxx xxxx 1001 xxxx */
 	/* MLAS			cccc 0000 0011 xxxx xxxx xxxx 1001 xxxx */
 	DECODE_OR	(0x0fe000f0, 0x00200090),
 	/* MLS			cccc 0000 0110 xxxx xxxx xxxx 1001 xxxx */
-	DECODE_EMULATEX	(0x0ff000f0, 0x00600090, emulate_rd16rn12rm0rs8_rwflags_nopc,
+	DECODE_EMULATEX	(0x0ff000f0, 0x00600090, PROBES_MUL2,
 						 REGS(NOPC, NOPC, NOPC, 0, NOPC)),
 
 	/* UMAAL		cccc 0000 0100 xxxx xxxx xxxx 1001 xxxx */
@@ -222,7 +234,7 @@ static const union decode_item arm_cccc_0000_____1001_table[] = {
 	/* SMULLS		cccc 0000 1101 xxxx xxxx xxxx 1001 xxxx */
 	/* SMLAL		cccc 0000 1110 xxxx xxxx xxxx 1001 xxxx */
 	/* SMLALS		cccc 0000 1111 xxxx xxxx xxxx 1001 xxxx */
-	DECODE_EMULATEX	(0x0f8000f0, 0x00800090, emulate_rdlo12rdhi16rn0rm8_rwflags_nopc,
+	DECODE_EMULATEX	(0x0f8000f0, 0x00800090, PROBES_MUL1,
 						 REGS(NOPC, NOPC, NOPC, 0, NOPC)),
 
 	DECODE_END
@@ -234,7 +246,7 @@ static const union decode_item arm_cccc_0001_____1001_table[] = {
 #if __LINUX_ARM_ARCH__ < 6
 	/* Deprecated on ARMv6 and may be UNDEFINED on v7		*/
 	/* SMP/SWPB		cccc 0001 0x00 xxxx xxxx xxxx 1001 xxxx */
-	DECODE_EMULATEX	(0x0fb000f0, 0x01000090, emulate_rd12rn16rm0_rwflags_nopc,
+	DECODE_EMULATEX	(0x0fb000f0, 0x01000090, PROBES_SWP,
 						 REGS(NOPC, NOPC, 0, 0, NOPC)),
 #endif
 	/* LDREX/STREX{,D,B,H}	cccc 0001 1xxx xxxx xxxx xxxx 1001 xxxx */
@@ -257,32 +269,32 @@ static const union decode_item arm_cccc_000x_____1xx1_table[] = {
 
 	/* LDRD (register)	cccc 000x x0x0 xxxx xxxx xxxx 1101 xxxx */
 	/* STRD (register)	cccc 000x x0x0 xxxx xxxx xxxx 1111 xxxx */
-	DECODE_EMULATEX	(0x0e5000d0, 0x000000d0, emulate_ldrdstrd,
+	DECODE_EMULATEX	(0x0e5000d0, 0x000000d0, PROBES_LDRSTRD,
 						 REGS(NOPCWB, NOPCX, 0, 0, NOPC)),
 
 	/* LDRD (immediate)	cccc 000x x1x0 xxxx xxxx xxxx 1101 xxxx */
 	/* STRD (immediate)	cccc 000x x1x0 xxxx xxxx xxxx 1111 xxxx */
-	DECODE_EMULATEX	(0x0e5000d0, 0x004000d0, emulate_ldrdstrd,
+	DECODE_EMULATEX	(0x0e5000d0, 0x004000d0, PROBES_LDRSTRD,
 						 REGS(NOPCWB, NOPCX, 0, 0, 0)),
 
 	/* STRH (register)	cccc 000x x0x0 xxxx xxxx xxxx 1011 xxxx */
-	DECODE_EMULATEX	(0x0e5000f0, 0x000000b0, emulate_str,
+	DECODE_EMULATEX	(0x0e5000f0, 0x000000b0, PROBES_STORE_EXTRA,
 						 REGS(NOPCWB, NOPC, 0, 0, NOPC)),
 
 	/* LDRH (register)	cccc 000x x0x1 xxxx xxxx xxxx 1011 xxxx */
 	/* LDRSB (register)	cccc 000x x0x1 xxxx xxxx xxxx 1101 xxxx */
 	/* LDRSH (register)	cccc 000x x0x1 xxxx xxxx xxxx 1111 xxxx */
-	DECODE_EMULATEX	(0x0e500090, 0x00100090, emulate_ldr,
+	DECODE_EMULATEX	(0x0e500090, 0x00100090, PROBES_LOAD_EXTRA,
 						 REGS(NOPCWB, NOPC, 0, 0, NOPC)),
 
 	/* STRH (immediate)	cccc 000x x1x0 xxxx xxxx xxxx 1011 xxxx */
-	DECODE_EMULATEX	(0x0e5000f0, 0x004000b0, emulate_str,
+	DECODE_EMULATEX	(0x0e5000f0, 0x004000b0, PROBES_STORE_EXTRA,
 						 REGS(NOPCWB, NOPC, 0, 0, 0)),
 
 	/* LDRH (immediate)	cccc 000x x1x1 xxxx xxxx xxxx 1011 xxxx */
 	/* LDRSB (immediate)	cccc 000x x1x1 xxxx xxxx xxxx 1101 xxxx */
 	/* LDRSH (immediate)	cccc 000x x1x1 xxxx xxxx xxxx 1111 xxxx */
-	DECODE_EMULATEX	(0x0e500090, 0x00500090, emulate_ldr,
+	DECODE_EMULATEX	(0x0e500090, 0x00500090, PROBES_LOAD_EXTRA,
 						 REGS(NOPCWB, NOPC, 0, 0, 0)),
 
 	DECODE_END
@@ -295,18 +307,18 @@ static const union decode_item arm_cccc_000x_table[] = {
 	DECODE_REJECT	(0x0e10f000, 0x0010f000),
 
 	/* MOV IP, SP		1110 0001 1010 0000 1100 0000 0000 1101 */
-	DECODE_SIMULATE	(0xffffffff, 0xe1a0c00d, simulate_mov_ipsp),
+	DECODE_SIMULATE	(0xffffffff, 0xe1a0c00d, PROBES_MOV_IP_SP),
 
 	/* TST (register)	cccc 0001 0001 xxxx xxxx xxxx xxx0 xxxx */
 	/* TEQ (register)	cccc 0001 0011 xxxx xxxx xxxx xxx0 xxxx */
 	/* CMP (register)	cccc 0001 0101 xxxx xxxx xxxx xxx0 xxxx */
 	/* CMN (register)	cccc 0001 0111 xxxx xxxx xxxx xxx0 xxxx */
-	DECODE_EMULATEX	(0x0f900010, 0x01100000, emulate_rd12rn16rm0rs8_rwflags,
+	DECODE_EMULATEX	(0x0f900010, 0x01100000, PROBES_DATA_PROCESSING_REG,
 						 REGS(ANY, 0, 0, 0, ANY)),
 
 	/* MOV (register)	cccc 0001 101x xxxx xxxx xxxx xxx0 xxxx */
 	/* MVN (register)	cccc 0001 111x xxxx xxxx xxxx xxx0 xxxx */
-	DECODE_EMULATEX	(0x0fa00010, 0x01a00000, emulate_rd12rn16rm0rs8_rwflags,
+	DECODE_EMULATEX	(0x0fa00010, 0x01a00000, PROBES_DATA_PROCESSING_REG,
 						 REGS(0, ANY, 0, 0, ANY)),
 
 	/* AND (register)	cccc 0000 000x xxxx xxxx xxxx xxx0 xxxx */
@@ -319,19 +331,19 @@ static const union decode_item arm_cccc_000x_table[] = {
 	/* RSC (register)	cccc 0000 111x xxxx xxxx xxxx xxx0 xxxx */
 	/* ORR (register)	cccc 0001 100x xxxx xxxx xxxx xxx0 xxxx */
 	/* BIC (register)	cccc 0001 110x xxxx xxxx xxxx xxx0 xxxx */
-	DECODE_EMULATEX	(0x0e000010, 0x00000000, emulate_rd12rn16rm0rs8_rwflags,
+	DECODE_EMULATEX	(0x0e000010, 0x00000000, PROBES_DATA_PROCESSING_REG,
 						 REGS(ANY, ANY, 0, 0, ANY)),
 
 	/* TST (reg-shift reg)	cccc 0001 0001 xxxx xxxx xxxx 0xx1 xxxx */
 	/* TEQ (reg-shift reg)	cccc 0001 0011 xxxx xxxx xxxx 0xx1 xxxx */
 	/* CMP (reg-shift reg)	cccc 0001 0101 xxxx xxxx xxxx 0xx1 xxxx */
 	/* CMN (reg-shift reg)	cccc 0001 0111 xxxx xxxx xxxx 0xx1 xxxx */
-	DECODE_EMULATEX	(0x0f900090, 0x01100010, emulate_rd12rn16rm0rs8_rwflags,
+	DECODE_EMULATEX	(0x0f900090, 0x01100010, PROBES_DATA_PROCESSING_REG,
 						 REGS(ANY, 0, NOPC, 0, ANY)),
 
 	/* MOV (reg-shift reg)	cccc 0001 101x xxxx xxxx xxxx 0xx1 xxxx */
 	/* MVN (reg-shift reg)	cccc 0001 111x xxxx xxxx xxxx 0xx1 xxxx */
-	DECODE_EMULATEX	(0x0fa00090, 0x01a00010, emulate_rd12rn16rm0rs8_rwflags,
+	DECODE_EMULATEX	(0x0fa00090, 0x01a00010, PROBES_DATA_PROCESSING_REG,
 						 REGS(0, ANY, NOPC, 0, ANY)),
 
 	/* AND (reg-shift reg)	cccc 0000 000x xxxx xxxx xxxx 0xx1 xxxx */
@@ -344,7 +356,7 @@ static const union decode_item arm_cccc_000x_table[] = {
 	/* RSC (reg-shift reg)	cccc 0000 111x xxxx xxxx xxxx 0xx1 xxxx */
 	/* ORR (reg-shift reg)	cccc 0001 100x xxxx xxxx xxxx 0xx1 xxxx */
 	/* BIC (reg-shift reg)	cccc 0001 110x xxxx xxxx xxxx 0xx1 xxxx */
-	DECODE_EMULATEX	(0x0e000090, 0x00000010, emulate_rd12rn16rm0rs8_rwflags,
+	DECODE_EMULATEX	(0x0e000090, 0x00000010, PROBES_DATA_PROCESSING_REG,
 						 REGS(ANY, ANY, NOPC, 0, ANY)),
 
 	DECODE_END
@@ -355,17 +367,17 @@ static const union decode_item arm_cccc_001x_table[] = {
 
 	/* MOVW			cccc 0011 0000 xxxx xxxx xxxx xxxx xxxx */
 	/* MOVT			cccc 0011 0100 xxxx xxxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0x0fb00000, 0x03000000, emulate_rd12rm0_noflags_nopc,
+	DECODE_EMULATEX	(0x0fb00000, 0x03000000, PROBES_DATA_PROCESSING_IMM,
 						 REGS(0, NOPC, 0, 0, 0)),
 
 	/* YIELD		cccc 0011 0010 0000 xxxx xxxx 0000 0001 */
 	DECODE_OR	(0x0fff00ff, 0x03200001),
 	/* SEV			cccc 0011 0010 0000 xxxx xxxx 0000 0100 */
-	DECODE_EMULATE	(0x0fff00ff, 0x03200004, probes_emulate_none),
+	DECODE_EMULATE	(0x0fff00ff, 0x03200004, PROBES_EMULATE_NONE),
 	/* NOP			cccc 0011 0010 0000 xxxx xxxx 0000 0000 */
 	/* WFE			cccc 0011 0010 0000 xxxx xxxx 0000 0010 */
 	/* WFI			cccc 0011 0010 0000 xxxx xxxx 0000 0011 */
-	DECODE_SIMULATE	(0x0fff00fc, 0x03200000, probes_simulate_nop),
+	DECODE_SIMULATE	(0x0fff00fc, 0x03200000, PROBES_SIMULATE_NOP),
 	/* DBG			cccc 0011 0010 0000 xxxx xxxx ffff xxxx */
 	/* unallocated hints	cccc 0011 0010 0000 xxxx xxxx xxxx xxxx */
 	/* MSR (immediate)	cccc 0011 0x10 xxxx xxxx xxxx xxxx xxxx */
@@ -378,12 +390,12 @@ static const union decode_item arm_cccc_001x_table[] = {
 	/* TEQ (immediate)	cccc 0011 0011 xxxx xxxx xxxx xxxx xxxx */
 	/* CMP (immediate)	cccc 0011 0101 xxxx xxxx xxxx xxxx xxxx */
 	/* CMN (immediate)	cccc 0011 0111 xxxx xxxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0x0f900000, 0x03100000, emulate_rd12rn16rm0rs8_rwflags,
+	DECODE_EMULATEX	(0x0f900000, 0x03100000, PROBES_DATA_PROCESSING_IMM,
 						 REGS(ANY, 0, 0, 0, 0)),
 
 	/* MOV (immediate)	cccc 0011 101x xxxx xxxx xxxx xxxx xxxx */
 	/* MVN (immediate)	cccc 0011 111x xxxx xxxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0x0fa00000, 0x03a00000, emulate_rd12rn16rm0rs8_rwflags,
+	DECODE_EMULATEX	(0x0fa00000, 0x03a00000, PROBES_DATA_PROCESSING_IMM,
 						 REGS(0, ANY, 0, 0, 0)),
 
 	/* AND (immediate)	cccc 0010 000x xxxx xxxx xxxx xxxx xxxx */
@@ -396,7 +408,7 @@ static const union decode_item arm_cccc_001x_table[] = {
 	/* RSC (immediate)	cccc 0010 111x xxxx xxxx xxxx xxxx xxxx */
 	/* ORR (immediate)	cccc 0011 100x xxxx xxxx xxxx xxxx xxxx */
 	/* BIC (immediate)	cccc 0011 110x xxxx xxxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0x0e000000, 0x02000000, emulate_rd12rn16rm0rs8_rwflags,
+	DECODE_EMULATEX	(0x0e000000, 0x02000000, PROBES_DATA_PROCESSING_IMM,
 						 REGS(ANY, ANY, 0, 0, 0)),
 
 	DECODE_END
@@ -406,7 +418,7 @@ static const union decode_item arm_cccc_0110_____xxx1_table[] = {
 	/* Media instructions						*/
 
 	/* SEL			cccc 0110 1000 xxxx xxxx xxxx 1011 xxxx */
-	DECODE_EMULATEX	(0x0ff000f0, 0x068000b0, emulate_rd12rn16rm0_rwflags_nopc,
+	DECODE_EMULATEX	(0x0ff000f0, 0x068000b0, PROBES_SATURATE,
 						 REGS(NOPC, NOPC, 0, 0, NOPC)),
 
 	/* SSAT			cccc 0110 101x xxxx xxxx xxxx xx01 xxxx */
@@ -414,14 +426,14 @@ static const union decode_item arm_cccc_0110_____xxx1_table[] = {
 	DECODE_OR(0x0fa00030, 0x06a00010),
 	/* SSAT16		cccc 0110 1010 xxxx xxxx xxxx 0011 xxxx */
 	/* USAT16		cccc 0110 1110 xxxx xxxx xxxx 0011 xxxx */
-	DECODE_EMULATEX	(0x0fb000f0, 0x06a00030, emulate_rd12rn16rm0_rwflags_nopc,
+	DECODE_EMULATEX	(0x0fb000f0, 0x06a00030, PROBES_SATURATE,
 						 REGS(0, NOPC, 0, 0, NOPC)),
 
 	/* REV			cccc 0110 1011 xxxx xxxx xxxx 0011 xxxx */
 	/* REV16		cccc 0110 1011 xxxx xxxx xxxx 1011 xxxx */
 	/* RBIT			cccc 0110 1111 xxxx xxxx xxxx 0011 xxxx */
 	/* REVSH		cccc 0110 1111 xxxx xxxx xxxx 1011 xxxx */
-	DECODE_EMULATEX	(0x0fb00070, 0x06b00030, emulate_rd12rm0_noflags_nopc,
+	DECODE_EMULATEX	(0x0fb00070, 0x06b00030, PROBES_REV,
 						 REGS(0, NOPC, 0, 0, NOPC)),
 
 	/* ???			cccc 0110 0x00 xxxx xxxx xxxx xxx1 xxxx */
@@ -466,12 +478,12 @@ static const union decode_item arm_cccc_0110_____xxx1_table[] = {
 	/* 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 */
-	DECODE_EMULATEX	(0x0f800010, 0x06000010, emulate_rd12rn16rm0_rwflags_nopc,
+	DECODE_EMULATEX	(0x0f800010, 0x06000010, PROBES_MMI,
 						 REGS(NOPC, NOPC, 0, 0, NOPC)),
 
 	/* PKHBT		cccc 0110 1000 xxxx xxxx xxxx x001 xxxx */
 	/* PKHTB		cccc 0110 1000 xxxx xxxx xxxx x101 xxxx */
-	DECODE_EMULATEX	(0x0ff00030, 0x06800010, emulate_rd12rn16rm0_rwflags_nopc,
+	DECODE_EMULATEX	(0x0ff00030, 0x06800010, PROBES_PACK,
 						 REGS(NOPC, NOPC, 0, 0, NOPC)),
 
 	/* ???			cccc 0110 1001 xxxx xxxx xxxx 0111 xxxx */
@@ -484,7 +496,7 @@ static const union decode_item arm_cccc_0110_____xxx1_table[] = {
 	/* UXTB16		cccc 0110 1100 1111 xxxx xxxx 0111 xxxx */
 	/* UXTB			cccc 0110 1110 1111 xxxx xxxx 0111 xxxx */
 	/* UXTH			cccc 0110 1111 1111 xxxx xxxx 0111 xxxx */
-	DECODE_EMULATEX	(0x0f8f00f0, 0x068f0070, emulate_rd12rm0_noflags_nopc,
+	DECODE_EMULATEX	(0x0f8f00f0, 0x068f0070, PROBES_EXTEND,
 						 REGS(0, NOPC, 0, 0, NOPC)),
 
 	/* SXTAB16		cccc 0110 1000 xxxx xxxx xxxx 0111 xxxx */
@@ -493,7 +505,7 @@ static const union decode_item arm_cccc_0110_____xxx1_table[] = {
 	/* 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 */
-	DECODE_EMULATEX	(0x0f8000f0, 0x06800070, emulate_rd12rn16rm0_rwflags_nopc,
+	DECODE_EMULATEX	(0x0f8000f0, 0x06800070, PROBES_EXTEND_ADD,
 						 REGS(NOPCX, NOPC, 0, 0, NOPC)),
 
 	DECODE_END
@@ -507,7 +519,7 @@ static const union decode_item arm_cccc_0111_____xxx1_table[] = {
 
 	/* SMLALD		cccc 0111 0100 xxxx xxxx xxxx 00x1 xxxx */
 	/* SMLSLD		cccc 0111 0100 xxxx xxxx xxxx 01x1 xxxx */
-	DECODE_EMULATEX	(0x0ff00090, 0x07400010, emulate_rdlo12rdhi16rn0rm8_rwflags_nopc,
+	DECODE_EMULATEX	(0x0ff00090, 0x07400010, PROBES_MUL_ADD_LONG,
 						 REGS(NOPC, NOPC, NOPC, 0, NOPC)),
 
 	/* SMUAD		cccc 0111 0000 xxxx 1111 xxxx 00x1 xxxx */
@@ -516,7 +528,7 @@ static const union decode_item arm_cccc_0111_____xxx1_table[] = {
 	/* SMMUL		cccc 0111 0101 xxxx 1111 xxxx 00x1 xxxx */
 	DECODE_OR	(0x0ff0f0d0, 0x0750f010),
 	/* USAD8		cccc 0111 1000 xxxx 1111 xxxx 0001 xxxx */
-	DECODE_EMULATEX	(0x0ff0f0f0, 0x0780f010, emulate_rd16rn12rm0rs8_rwflags_nopc,
+	DECODE_EMULATEX	(0x0ff0f0f0, 0x0780f010, PROBES_MUL_ADD,
 						 REGS(NOPC, 0, NOPC, 0, NOPC)),
 
 	/* SMLAD		cccc 0111 0000 xxxx xxxx xxxx 00x1 xxxx */
@@ -525,24 +537,24 @@ static const union decode_item arm_cccc_0111_____xxx1_table[] = {
 	/* SMMLA		cccc 0111 0101 xxxx xxxx xxxx 00x1 xxxx */
 	DECODE_OR	(0x0ff000d0, 0x07500010),
 	/* USADA8		cccc 0111 1000 xxxx xxxx xxxx 0001 xxxx */
-	DECODE_EMULATEX	(0x0ff000f0, 0x07800010, emulate_rd16rn12rm0rs8_rwflags_nopc,
+	DECODE_EMULATEX	(0x0ff000f0, 0x07800010, PROBES_MUL_ADD,
 						 REGS(NOPC, NOPCX, NOPC, 0, NOPC)),
 
 	/* SMMLS		cccc 0111 0101 xxxx xxxx xxxx 11x1 xxxx */
-	DECODE_EMULATEX	(0x0ff000d0, 0x075000d0, emulate_rd16rn12rm0rs8_rwflags_nopc,
+	DECODE_EMULATEX	(0x0ff000d0, 0x075000d0, PROBES_MUL_ADD,
 						 REGS(NOPC, NOPC, NOPC, 0, NOPC)),
 
 	/* SBFX			cccc 0111 101x xxxx xxxx xxxx x101 xxxx */
 	/* UBFX			cccc 0111 111x xxxx xxxx xxxx x101 xxxx */
-	DECODE_EMULATEX	(0x0fa00070, 0x07a00050, emulate_rd12rm0_noflags_nopc,
+	DECODE_EMULATEX	(0x0fa00070, 0x07a00050, PROBES_BITFIELD,
 						 REGS(0, NOPC, 0, 0, NOPC)),
 
 	/* BFC			cccc 0111 110x xxxx xxxx xxxx x001 1111 */
-	DECODE_EMULATEX	(0x0fe0007f, 0x07c0001f, emulate_rd12rm0_noflags_nopc,
+	DECODE_EMULATEX	(0x0fe0007f, 0x07c0001f, PROBES_BITFIELD,
 						 REGS(0, NOPC, 0, 0, 0)),
 
 	/* BFI			cccc 0111 110x xxxx xxxx xxxx x001 xxxx */
-	DECODE_EMULATEX	(0x0fe00070, 0x07c00010, emulate_rd12rm0_noflags_nopc,
+	DECODE_EMULATEX	(0x0fe00070, 0x07c00010, PROBES_BITFIELD,
 						 REGS(0, NOPC, 0, 0, NOPCX)),
 
 	DECODE_END
@@ -562,22 +574,22 @@ static const union decode_item arm_cccc_01xx_table[] = {
 
 	/* STR (immediate)	cccc 010x x0x0 xxxx xxxx xxxx xxxx xxxx */
 	/* STRB (immediate)	cccc 010x x1x0 xxxx xxxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0x0e100000, 0x04000000, emulate_str,
+	DECODE_EMULATEX	(0x0e100000, 0x04000000, PROBES_STORE,
 						 REGS(NOPCWB, ANY, 0, 0, 0)),
 
 	/* LDR (immediate)	cccc 010x x0x1 xxxx xxxx xxxx xxxx xxxx */
 	/* LDRB (immediate)	cccc 010x x1x1 xxxx xxxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0x0e100000, 0x04100000, emulate_ldr,
+	DECODE_EMULATEX	(0x0e100000, 0x04100000, PROBES_LOAD,
 						 REGS(NOPCWB, ANY, 0, 0, 0)),
 
 	/* STR (register)	cccc 011x x0x0 xxxx xxxx xxxx xxxx xxxx */
 	/* STRB (register)	cccc 011x x1x0 xxxx xxxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0x0e100000, 0x06000000, emulate_str,
+	DECODE_EMULATEX	(0x0e100000, 0x06000000, PROBES_STORE,
 						 REGS(NOPCWB, ANY, 0, 0, NOPC)),
 
 	/* LDR (register)	cccc 011x x0x1 xxxx xxxx xxxx xxxx xxxx */
 	/* LDRB (register)	cccc 011x x1x1 xxxx xxxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0x0e100000, 0x06100000, emulate_ldr,
+	DECODE_EMULATEX	(0x0e100000, 0x06100000, PROBES_LOAD,
 						 REGS(NOPCWB, ANY, 0, 0, NOPC)),
 
 	DECODE_END
@@ -588,7 +600,7 @@ static const union decode_item arm_cccc_100x_table[] = {
 
 	/* LDM			cccc 100x x0x1 xxxx xxxx xxxx xxxx xxxx */
 	/* STM			cccc 100x x0x0 xxxx xxxx xxxx xxxx xxxx */
-	DECODE_CUSTOM	(0x0e400000, 0x08000000, kprobe_decode_ldmstm),
+	DECODE_CUSTOM	(0x0e400000, 0x08000000, PROBES_LDMSTM),
 
 	/* STM (user registers)	cccc 100x x1x0 xxxx xxxx xxxx xxxx xxxx */
 	/* LDM (user registers)	cccc 100x x1x1 xxxx 0xxx xxxx xxxx xxxx */
@@ -668,7 +680,7 @@ const union decode_item kprobe_decode_arm_table[] = {
 
 	/* B			cccc 1010 xxxx xxxx xxxx xxxx xxxx xxxx */
 	/* BL			cccc 1011 xxxx xxxx xxxx xxxx xxxx xxxx */
-	DECODE_SIMULATE	(0x0e000000, 0x0a000000, simulate_bbl),
+	DECODE_SIMULATE	(0x0e000000, 0x0a000000, PROBES_BRANCH),
 
 	/*
 	 * Supervisor Call, and coprocessor instructions
@@ -709,9 +721,11 @@ static void __kprobes arm_singlestep(struct kprobe *p, struct pt_regs *regs)
  *   should also be very rare.
  */
 enum kprobe_insn __kprobes
-arm_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+arm_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
+			bool usermode, const union decode_item *actions)
 {
 	asi->insn_singlestep = arm_singlestep;
 	asi->insn_check_cc = kprobe_condition_checks[insn>>28];
-	return kprobe_decode_insn(insn, asi, kprobe_decode_arm_table, false);
+	return probes_decode_insn(insn, asi, kprobe_decode_arm_table, false,
+				  usermode, actions);
 }
diff --git a/arch/arm/kernel/kprobes-common.c b/arch/arm/kernel/kprobes-common.c
index b66e9f7..4e19498 100644
--- a/arch/arm/kernel/kprobes-common.c
+++ b/arch/arm/kernel/kprobes-common.c
@@ -268,7 +268,8 @@ emulate_ldm_r3_15(struct kprobe *p, struct pt_regs *regs)
 }
 
 enum kprobe_insn __kprobes
-kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi,
+		     void *d)
 {
 	kprobe_insn_handler_t *handler = 0;
 	unsigned reglist = insn & 0xffff;
diff --git a/arch/arm/kernel/kprobes-thumb.c b/arch/arm/kernel/kprobes-thumb.c
index 173b2bc..f5d57a8 100644
--- a/arch/arm/kernel/kprobes-thumb.c
+++ b/arch/arm/kernel/kprobes-thumb.c
@@ -13,6 +13,8 @@
 #include <linux/module.h>
 
 #include "kprobes.h"
+#include "probes.h"
+#include "probes-thumb.h"
 
 
 /*
@@ -83,7 +85,8 @@ t32_simulate_cond_branch(struct kprobe *p, struct pt_regs *regs)
 }
 
 static enum kprobe_insn __kprobes
-t32_decode_cond_branch(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+t32_decode_cond_branch(kprobe_opcode_t insn, struct arch_specific_insn *asi,
+		       void *d)
 {
 	int cc = (insn >> 22) & 0xf;
 	asi->insn_check_cc = kprobe_condition_checks[cc];
@@ -158,9 +161,10 @@ t32_simulate_ldr_literal(struct kprobe *p, struct pt_regs *regs)
 }
 
 static enum kprobe_insn __kprobes
-t32_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+t32_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi,
+		  void *d)
 {
-	enum kprobe_insn ret = kprobe_decode_ldmstm(insn, asi);
+	enum kprobe_insn ret = kprobe_decode_ldmstm(insn, asi, d);
 
 	/* Fixup modified instruction to have halfwords in correct order...*/
 	insn = asi->insn[0];
@@ -344,7 +348,7 @@ static const union decode_item t32_table_1110_100x_x0xx[] = {
 	/* LDMIA		1110 1000 10x1 xxxx xxxx xxxx xxxx xxxx */
 	/* STMDB		1110 1001 00x0 xxxx xxxx xxxx xxxx xxxx */
 	/* LDMDB		1110 1001 00x1 xxxx xxxx xxxx xxxx xxxx */
-	DECODE_CUSTOM	(0xfe400000, 0xe8000000, t32_decode_ldmstm),
+	DECODE_CUSTOM	(0xfe400000, 0xe8000000, PROBES_T32_LDMSTM),
 
 	DECODE_END
 };
@@ -357,12 +361,12 @@ static const union decode_item t32_table_1110_100x_x1xx[] = {
 	DECODE_OR	(0xff600000, 0xe8600000),
 	/* STRD (immediate)	1110 1001 x1x0 xxxx xxxx xxxx xxxx xxxx */
 	/* LDRD (immediate)	1110 1001 x1x1 xxxx xxxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0xff400000, 0xe9400000, t32_emulate_ldrdstrd,
+	DECODE_EMULATEX	(0xff400000, 0xe9400000, PROBES_T32_LDRDSTRD,
 						 REGS(NOPCWB, NOSPPC, NOSPPC, 0, 0)),
 
 	/* TBB			1110 1000 1101 xxxx xxxx xxxx 0000 xxxx */
 	/* TBH			1110 1000 1101 xxxx xxxx xxxx 0001 xxxx */
-	DECODE_SIMULATEX(0xfff000e0, 0xe8d00000, t32_simulate_table_branch,
+	DECODE_SIMULATEX(0xfff000e0, 0xe8d00000, PROBES_T32_TABLE_BRANCH,
 						 REGS(NOSP, 0, 0, 0, NOSPPC)),
 
 	/* STREX		1110 1000 0100 xxxx xxxx xxxx xxxx xxxx */
@@ -382,18 +386,18 @@ static const union decode_item t32_table_1110_101x[] = {
 
 	/* TST			1110 1010 0001 xxxx xxxx 1111 xxxx xxxx */
 	/* TEQ			1110 1010 1001 xxxx xxxx 1111 xxxx xxxx */
-	DECODE_EMULATEX	(0xff700f00, 0xea100f00, t32_emulate_rd8rn16rm0_rwflags,
+	DECODE_EMULATEX	(0xff700f00, 0xea100f00, PROBES_T32_TST,
 						 REGS(NOSPPC, 0, 0, 0, NOSPPC)),
 
 	/* CMN			1110 1011 0001 xxxx xxxx 1111 xxxx xxxx */
 	DECODE_OR	(0xfff00f00, 0xeb100f00),
 	/* CMP			1110 1011 1011 xxxx xxxx 1111 xxxx xxxx */
-	DECODE_EMULATEX	(0xfff00f00, 0xebb00f00, t32_emulate_rd8rn16rm0_rwflags,
+	DECODE_EMULATEX	(0xfff00f00, 0xebb00f00, PROBES_T32_TST,
 						 REGS(NOPC, 0, 0, 0, NOSPPC)),
 
 	/* MOV			1110 1010 010x 1111 xxxx xxxx xxxx xxxx */
 	/* MVN			1110 1010 011x 1111 xxxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0xffcf0000, 0xea4f0000, t32_emulate_rd8rn16rm0_rwflags,
+	DECODE_EMULATEX	(0xffcf0000, 0xea4f0000, PROBES_T32_MOV,
 						 REGS(0, 0, NOSPPC, 0, NOSPPC)),
 
 	/* ???			1110 1010 101x xxxx xxxx xxxx xxxx xxxx */
@@ -408,7 +412,7 @@ static const union decode_item t32_table_1110_101x[] = {
 
 	/* ADD/SUB SP, SP, Rm, LSL #0..3				*/
 	/*			1110 1011 x0xx 1101 x000 1101 xx00 xxxx */
-	DECODE_EMULATEX	(0xff4f7f30, 0xeb0d0d00, t32_emulate_rd8rn16rm0_rwflags,
+	DECODE_EMULATEX	(0xff4f7f30, 0xeb0d0d00, PROBES_T32_ADDSUB,
 						 REGS(SP, 0, SP, 0, NOSPPC)),
 
 	/* ADD/SUB SP, SP, Rm, shift					*/
@@ -417,7 +421,7 @@ static const union decode_item t32_table_1110_101x[] = {
 
 	/* ADD/SUB Rd, SP, Rm, shift					*/
 	/*			1110 1011 x0xx 1101 xxxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0xff4f0000, 0xeb0d0000, t32_emulate_rd8rn16rm0_rwflags,
+	DECODE_EMULATEX	(0xff4f0000, 0xeb0d0000, PROBES_T32_ADDSUB,
 						 REGS(SP, 0, NOPC, 0, NOSPPC)),
 
 	/* AND			1110 1010 000x xxxx xxxx xxxx xxxx xxxx */
@@ -431,7 +435,7 @@ static const union decode_item t32_table_1110_101x[] = {
 	/* SBC			1110 1011 011x xxxx xxxx xxxx xxxx xxxx */
 	/* SUB			1110 1011 101x xxxx xxxx xxxx xxxx xxxx */
 	/* RSB			1110 1011 110x xxxx xxxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0xfe000000, 0xea000000, t32_emulate_rd8rn16rm0_rwflags,
+	DECODE_EMULATEX	(0xfe000000, 0xea000000, PROBES_T32_LOGICAL,
 						 REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)),
 
 	DECODE_END
@@ -442,18 +446,18 @@ static const union decode_item t32_table_1111_0x0x___0[] = {
 
 	/* TST			1111 0x00 0001 xxxx 0xxx 1111 xxxx xxxx */
 	/* TEQ			1111 0x00 1001 xxxx 0xxx 1111 xxxx xxxx */
-	DECODE_EMULATEX	(0xfb708f00, 0xf0100f00, t32_emulate_rd8rn16rm0_rwflags,
+	DECODE_EMULATEX	(0xfb708f00, 0xf0100f00, PROBES_T32_TST,
 						 REGS(NOSPPC, 0, 0, 0, 0)),
 
 	/* CMN			1111 0x01 0001 xxxx 0xxx 1111 xxxx xxxx */
 	DECODE_OR	(0xfbf08f00, 0xf1100f00),
 	/* CMP			1111 0x01 1011 xxxx 0xxx 1111 xxxx xxxx */
-	DECODE_EMULATEX	(0xfbf08f00, 0xf1b00f00, t32_emulate_rd8rn16rm0_rwflags,
+	DECODE_EMULATEX	(0xfbf08f00, 0xf1b00f00, PROBES_T32_CMP,
 						 REGS(NOPC, 0, 0, 0, 0)),
 
 	/* MOV			1111 0x00 010x 1111 0xxx xxxx xxxx xxxx */
 	/* MVN			1111 0x00 011x 1111 0xxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0xfbcf8000, 0xf04f0000, t32_emulate_rd8rn16rm0_rwflags,
+	DECODE_EMULATEX	(0xfbcf8000, 0xf04f0000, PROBES_T32_MOV,
 						 REGS(0, 0, NOSPPC, 0, 0)),
 
 	/* ???			1111 0x00 101x xxxx 0xxx xxxx xxxx xxxx */
@@ -470,7 +474,7 @@ static const union decode_item t32_table_1111_0x0x___0[] = {
 
 	/* ADD Rd, SP, #imm	1111 0x01 000x 1101 0xxx xxxx xxxx xxxx */
 	/* SUB Rd, SP, #imm	1111 0x01 101x 1101 0xxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0xfb4f8000, 0xf10d0000, t32_emulate_rd8rn16rm0_rwflags,
+	DECODE_EMULATEX	(0xfb4f8000, 0xf10d0000, PROBES_T32_ADDSUB,
 						 REGS(SP, 0, NOPC, 0, 0)),
 
 	/* AND			1111 0x00 000x xxxx 0xxx xxxx xxxx xxxx */
@@ -483,7 +487,7 @@ static const union decode_item t32_table_1111_0x0x___0[] = {
 	/* SBC			1111 0x01 011x xxxx 0xxx xxxx xxxx xxxx */
 	/* SUB			1111 0x01 101x xxxx 0xxx xxxx xxxx xxxx */
 	/* RSB			1111 0x01 110x xxxx 0xxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0xfa008000, 0xf0000000, t32_emulate_rd8rn16rm0_rwflags,
+	DECODE_EMULATEX	(0xfa008000, 0xf0000000, PROBES_T32_LOGICAL,
 						 REGS(NOSPPC, 0, NOSPPC, 0, 0)),
 
 	DECODE_END
@@ -495,44 +499,44 @@ static const union decode_item t32_table_1111_0x1x___0[] = {
 	/* ADDW Rd, PC, #imm	1111 0x10 0000 1111 0xxx xxxx xxxx xxxx */
 	DECODE_OR	(0xfbff8000, 0xf20f0000),
 	/* SUBW	Rd, PC, #imm	1111 0x10 1010 1111 0xxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0xfbff8000, 0xf2af0000, t32_emulate_rd8pc16_noflags,
+	DECODE_EMULATEX	(0xfbff8000, 0xf2af0000, PROBES_T32_ADDWSUBW_PC,
 						 REGS(PC, 0, NOSPPC, 0, 0)),
 
 	/* ADDW SP, SP, #imm	1111 0x10 0000 1101 0xxx 1101 xxxx xxxx */
 	DECODE_OR	(0xfbff8f00, 0xf20d0d00),
 	/* SUBW	SP, SP, #imm	1111 0x10 1010 1101 0xxx 1101 xxxx xxxx */
-	DECODE_EMULATEX	(0xfbff8f00, 0xf2ad0d00, t32_emulate_rd8rn16_noflags,
+	DECODE_EMULATEX	(0xfbff8f00, 0xf2ad0d00, PROBES_T32_ADDWSUBW,
 						 REGS(SP, 0, SP, 0, 0)),
 
 	/* ADDW			1111 0x10 0000 xxxx 0xxx xxxx xxxx xxxx */
 	DECODE_OR	(0xfbf08000, 0xf2000000),
 	/* SUBW			1111 0x10 1010 xxxx 0xxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0xfbf08000, 0xf2a00000, t32_emulate_rd8rn16_noflags,
+	DECODE_EMULATEX	(0xfbf08000, 0xf2a00000, PROBES_T32_ADDWSUBW,
 						 REGS(NOPCX, 0, NOSPPC, 0, 0)),
 
 	/* MOVW			1111 0x10 0100 xxxx 0xxx xxxx xxxx xxxx */
 	/* MOVT			1111 0x10 1100 xxxx 0xxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0xfb708000, 0xf2400000, t32_emulate_rd8rn16_noflags,
+	DECODE_EMULATEX	(0xfb708000, 0xf2400000, PROBES_T32_MOVW,
 						 REGS(0, 0, NOSPPC, 0, 0)),
 
 	/* SSAT16		1111 0x11 0010 xxxx 0000 xxxx 00xx xxxx */
 	/* SSAT			1111 0x11 00x0 xxxx 0xxx xxxx xxxx xxxx */
 	/* USAT16		1111 0x11 1010 xxxx 0000 xxxx 00xx xxxx */
 	/* USAT			1111 0x11 10x0 xxxx 0xxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0xfb508000, 0xf3000000, t32_emulate_rd8rn16rm0_rwflags,
+	DECODE_EMULATEX	(0xfb508000, 0xf3000000, PROBES_T32_SAT,
 						 REGS(NOSPPC, 0, NOSPPC, 0, 0)),
 
 	/* SFBX			1111 0x11 0100 xxxx 0xxx xxxx xxxx xxxx */
 	/* UFBX			1111 0x11 1100 xxxx 0xxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0xfb708000, 0xf3400000, t32_emulate_rd8rn16_noflags,
+	DECODE_EMULATEX	(0xfb708000, 0xf3400000, PROBES_T32_BITFIELD,
 						 REGS(NOSPPC, 0, NOSPPC, 0, 0)),
 
 	/* BFC			1111 0x11 0110 1111 0xxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0xfbff8000, 0xf36f0000, t32_emulate_rd8rn16_noflags,
+	DECODE_EMULATEX	(0xfbff8000, 0xf36f0000, PROBES_T32_BITFIELD,
 						 REGS(0, 0, NOSPPC, 0, 0)),
 
 	/* BFI			1111 0x11 0110 xxxx 0xxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0xfbf08000, 0xf3600000, t32_emulate_rd8rn16_noflags,
+	DECODE_EMULATEX	(0xfbf08000, 0xf3600000, PROBES_T32_BITFIELD,
 						 REGS(NOSPPCX, 0, NOSPPC, 0, 0)),
 
 	DECODE_END
@@ -544,14 +548,14 @@ static const union decode_item t32_table_1111_0xxx___1[] = {
 	/* YIELD		1111 0011 1010 xxxx 10x0 x000 0000 0001 */
 	DECODE_OR	(0xfff0d7ff, 0xf3a08001),
 	/* SEV			1111 0011 1010 xxxx 10x0 x000 0000 0100 */
-	DECODE_EMULATE	(0xfff0d7ff, 0xf3a08004, probes_emulate_none),
+	DECODE_EMULATE	(0xfff0d7ff, 0xf3a08004, PROBES_T32_SEV),
 	/* NOP			1111 0011 1010 xxxx 10x0 x000 0000 0000 */
 	/* WFE			1111 0011 1010 xxxx 10x0 x000 0000 0010 */
 	/* WFI			1111 0011 1010 xxxx 10x0 x000 0000 0011 */
-	DECODE_SIMULATE	(0xfff0d7fc, 0xf3a08000, probes_simulate_nop),
+	DECODE_SIMULATE	(0xfff0d7fc, 0xf3a08000, PROBES_T32_WFE),
 
 	/* MRS Rd, CPSR		1111 0011 1110 xxxx 10x0 xxxx xxxx xxxx */
-	DECODE_SIMULATEX(0xfff0d000, 0xf3e08000, t32_simulate_mrs,
+	DECODE_SIMULATEX(0xfff0d000, 0xf3e08000, PROBES_T32_MRS,
 						 REGS(0, 0, NOSPPC, 0, 0)),
 
 	/*
@@ -573,13 +577,13 @@ static const union decode_item t32_table_1111_0xxx___1[] = {
 	DECODE_REJECT	(0xfb80d000, 0xf3808000),
 
 	/* Bcc			1111 0xxx xxxx xxxx 10x0 xxxx xxxx xxxx */
-	DECODE_CUSTOM	(0xf800d000, 0xf0008000, t32_decode_cond_branch),
+	DECODE_CUSTOM	(0xf800d000, 0xf0008000, PROBES_T32_BRANCH_COND),
 
 	/* BLX			1111 0xxx xxxx xxxx 11x0 xxxx xxxx xxx0 */
 	DECODE_OR	(0xf800d001, 0xf000c000),
 	/* B			1111 0xxx xxxx xxxx 10x1 xxxx xxxx xxxx */
 	/* BL			1111 0xxx xxxx xxxx 11x1 xxxx xxxx xxxx */
-	DECODE_SIMULATE	(0xf8009000, 0xf0009000, t32_simulate_branch),
+	DECODE_SIMULATE	(0xf8009000, 0xf0009000, PROBES_T32_BRANCH),
 
 	DECODE_END
 };
@@ -589,7 +593,7 @@ static const union decode_item t32_table_1111_100x_x0x1__1111[] = {
 
 	/* PLD (literal)	1111 1000 x001 1111 1111 xxxx xxxx xxxx */
 	/* PLI (literal)	1111 1001 x001 1111 1111 xxxx xxxx xxxx */
-	DECODE_SIMULATE	(0xfe7ff000, 0xf81ff000, probes_simulate_nop),
+	DECODE_SIMULATE	(0xfe7ff000, 0xf81ff000, PROBES_T32_PLDI),
 
 	/* PLD{W} (immediate)	1111 1000 10x1 xxxx 1111 xxxx xxxx xxxx */
 	DECODE_OR	(0xffd0f000, 0xf890f000),
@@ -598,13 +602,13 @@ static const union decode_item t32_table_1111_100x_x0x1__1111[] = {
 	/* PLI (immediate)	1111 1001 1001 xxxx 1111 xxxx xxxx xxxx */
 	DECODE_OR	(0xfff0f000, 0xf990f000),
 	/* PLI (immediate)	1111 1001 0001 xxxx 1111 1100 xxxx xxxx */
-	DECODE_SIMULATEX(0xfff0ff00, 0xf910fc00, probes_simulate_nop,
+	DECODE_SIMULATEX(0xfff0ff00, 0xf910fc00, PROBES_T32_PLDI,
 						 REGS(NOPCX, 0, 0, 0, 0)),
 
 	/* PLD{W} (register)	1111 1000 00x1 xxxx 1111 0000 00xx xxxx */
 	DECODE_OR	(0xffd0ffc0, 0xf810f000),
 	/* PLI (register)	1111 1001 0001 xxxx 1111 0000 00xx xxxx */
-	DECODE_SIMULATEX(0xfff0ffc0, 0xf910f000, probes_simulate_nop,
+	DECODE_SIMULATEX(0xfff0ffc0, 0xf910f000, PROBES_T32_PLDI,
 						 REGS(NOPCX, 0, 0, 0, NOSPPC)),
 
 	/* Other unallocated instructions...				*/
@@ -640,7 +644,7 @@ static const union decode_item t32_table_1111_100x[] = {
 	DECODE_REJECT	(0xff10f000, 0xf800f000),
 
 	/* LDR (literal)	1111 1000 x101 1111 xxxx xxxx xxxx xxxx */
-	DECODE_SIMULATEX(0xff7f0000, 0xf85f0000, t32_simulate_ldr_literal,
+	DECODE_SIMULATEX(0xff7f0000, 0xf85f0000, PROBES_T32_LDR_LIT,
 						 REGS(PC, ANY, 0, 0, 0)),
 
 	/* STR (immediate)	1111 1000 0100 xxxx xxxx 1xxx xxxx xxxx */
@@ -648,19 +652,19 @@ static const union decode_item t32_table_1111_100x[] = {
 	DECODE_OR	(0xffe00800, 0xf8400800),
 	/* STR (immediate)	1111 1000 1100 xxxx xxxx xxxx xxxx xxxx */
 	/* LDR (immediate)	1111 1000 1101 xxxx xxxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0xffe00000, 0xf8c00000, t32_emulate_ldrstr,
+	DECODE_EMULATEX	(0xffe00000, 0xf8c00000, PROBES_T32_LDRSTR,
 						 REGS(NOPCX, ANY, 0, 0, 0)),
 
 	/* STR (register)	1111 1000 0100 xxxx xxxx 0000 00xx xxxx */
 	/* LDR (register)	1111 1000 0101 xxxx xxxx 0000 00xx xxxx */
-	DECODE_EMULATEX	(0xffe00fc0, 0xf8400000, t32_emulate_ldrstr,
+	DECODE_EMULATEX	(0xffe00fc0, 0xf8400000, PROBES_T32_LDRSTR,
 						 REGS(NOPCX, ANY, 0, 0, NOSPPC)),
 
 	/* LDRB (literal)	1111 1000 x001 1111 xxxx xxxx xxxx xxxx */
 	/* LDRSB (literal)	1111 1001 x001 1111 xxxx xxxx xxxx xxxx */
 	/* LDRH (literal)	1111 1000 x011 1111 xxxx xxxx xxxx xxxx */
 	/* LDRSH (literal)	1111 1001 x011 1111 xxxx xxxx xxxx xxxx */
-	DECODE_SIMULATEX(0xfe5f0000, 0xf81f0000, t32_simulate_ldr_literal,
+	DECODE_SIMULATEX(0xfe5f0000, 0xf81f0000, PROBES_T32_LDR_LIT,
 						 REGS(PC, NOSPPCX, 0, 0, 0)),
 
 	/* STRB (immediate)	1111 1000 0000 xxxx xxxx 1xxx xxxx xxxx */
@@ -676,7 +680,7 @@ static const union decode_item t32_table_1111_100x[] = {
 	/* LDRSB (immediate)	1111 1001 1001 xxxx xxxx xxxx xxxx xxxx */
 	/* LDRH (immediate)	1111 1000 1011 xxxx xxxx xxxx xxxx xxxx */
 	/* LDRSH (immediate)	1111 1001 1011 xxxx xxxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0xfec00000, 0xf8800000, t32_emulate_ldrstr,
+	DECODE_EMULATEX	(0xfec00000, 0xf8800000, PROBES_T32_LDRSTR,
 						 REGS(NOPCX, NOSPPCX, 0, 0, 0)),
 
 	/* STRB (register)	1111 1000 0000 xxxx xxxx 0000 00xx xxxx */
@@ -685,7 +689,7 @@ static const union decode_item t32_table_1111_100x[] = {
 	/* LDRSB (register)	1111 1001 0001 xxxx xxxx 0000 00xx xxxx */
 	/* LDRH (register)	1111 1000 0011 xxxx xxxx 0000 00xx xxxx */
 	/* LDRSH (register)	1111 1001 0011 xxxx xxxx 0000 00xx xxxx */
-	DECODE_EMULATEX	(0xfe800fc0, 0xf8000000, t32_emulate_ldrstr,
+	DECODE_EMULATEX	(0xfe800fc0, 0xf8000000, PROBES_T32_LDRSTR,
 						 REGS(NOPCX, NOSPPCX, 0, 0, NOSPPC)),
 
 	/* Other unallocated instructions...				*/
@@ -704,7 +708,7 @@ static const union decode_item t32_table_1111_1010___1111[] = {
 	/* UXTB16		1111 1010 0011 1111 1111 xxxx 1xxx xxxx */
 	/* SXTB			1111 1010 0100 1111 1111 xxxx 1xxx xxxx */
 	/* UXTB			1111 1010 0101 1111 1111 xxxx 1xxx xxxx */
-	DECODE_EMULATEX	(0xff8ff080, 0xfa0ff080, t32_emulate_rd8rn16rm0_rwflags,
+	DECODE_EMULATEX	(0xff8ff080, 0xfa0ff080, PROBES_T32_SIGN_EXTEND,
 						 REGS(0, 0, NOSPPC, 0, NOSPPC)),
 
 
@@ -777,7 +781,7 @@ static const union decode_item t32_table_1111_1010___1111[] = {
 	/* LSR			1111 1010 001x xxxx 1111 xxxx 0000 xxxx */
 	/* ASR			1111 1010 010x xxxx 1111 xxxx 0000 xxxx */
 	/* ROR			1111 1010 011x xxxx 1111 xxxx 0000 xxxx */
-	DECODE_EMULATEX	(0xff80f0f0, 0xfa00f000, t32_emulate_rd8rn16rm0_rwflags,
+	DECODE_EMULATEX	(0xff80f0f0, 0xfa00f000, PROBES_T32_MEDIA,
 						 REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)),
 
 	/* CLZ			1111 1010 1010 xxxx 1111 xxxx 1000 xxxx */
@@ -787,7 +791,7 @@ static const union decode_item t32_table_1111_1010___1111[] = {
 	/* REV16		1111 1010 1001 xxxx 1111 xxxx 1001 xxxx */
 	/* RBIT			1111 1010 1001 xxxx 1111 xxxx 1010 xxxx */
 	/* REVSH		1111 1010 1001 xxxx 1111 xxxx 1011 xxxx */
-	DECODE_EMULATEX	(0xfff0f0c0, 0xfa90f080, t32_emulate_rd8rn16_noflags,
+	DECODE_EMULATEX	(0xfff0f0c0, 0xfa90f080, PROBES_T32_REVERSE,
 						 REGS(NOSPPC, 0, NOSPPC, 0, SAMEAS16)),
 
 	/* Other unallocated instructions...				*/
@@ -810,7 +814,7 @@ static const union decode_item t32_table_1111_1011_0[] = {
 	/* SMUSD{X}		1111 1011 0100 xxxx 1111 xxxx 000x xxxx */
 	/* SMMUL{R}		1111 1011 0101 xxxx 1111 xxxx 000x xxxx */
 	/* USAD8		1111 1011 0111 xxxx 1111 xxxx 0000 xxxx */
-	DECODE_EMULATEX	(0xff80f0e0, 0xfb00f000, t32_emulate_rd8rn16rm0_rwflags,
+	DECODE_EMULATEX	(0xff80f0e0, 0xfb00f000, PROBES_T32_MUL_ADD,
 						 REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)),
 
 	/* ???			1111 1011 0111 xxxx xxxx xxxx 0001 xxxx */
@@ -826,7 +830,7 @@ static const union decode_item t32_table_1111_1011_0[] = {
 	/* SMMLA{R}		1111 1011 0101 xxxx xxxx xxxx 000x xxxx */
 	/* SMMLS{R}		1111 1011 0110 xxxx xxxx xxxx 000x xxxx */
 	/* USADA8		1111 1011 0111 xxxx xxxx xxxx 0000 xxxx */
-	DECODE_EMULATEX	(0xff8000c0, 0xfb000000, t32_emulate_rd8rn16rm0ra12_noflags,
+	DECODE_EMULATEX	(0xff8000c0, 0xfb000000,  PROBES_T32_MUL_ADD2,
 						 REGS(NOSPPC, NOSPPCX, NOSPPC, 0, NOSPPC)),
 
 	/* Other unallocated instructions...				*/
@@ -847,7 +851,7 @@ static const union decode_item t32_table_1111_1011_1[] = {
 	/* UMULL		1111 1011 1010 xxxx xxxx xxxx 0000 xxxx */
 	/* SMLAL		1111 1011 1100 xxxx xxxx xxxx 0000 xxxx */
 	/* UMLAL		1111 1011 1110 xxxx xxxx xxxx 0000 xxxx */
-	DECODE_EMULATEX	(0xff9000f0, 0xfb800000, t32_emulate_rdlo12rdhi8rn16rm0_noflags,
+	DECODE_EMULATEX	(0xff9000f0, 0xfb800000, PROBES_T32_MUL_ADD_LONG,
 						 REGS(NOSPPC, NOSPPC, NOSPPC, 0, NOSPPC)),
 
 	/* SDIV			1111 1011 1001 xxxx xxxx xxxx 1111 xxxx */
@@ -1046,7 +1050,7 @@ t16_singlestep_it(struct kprobe *p, struct pt_regs *regs)
 }
 
 static enum kprobe_insn __kprobes
-t16_decode_it(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+t16_decode_it(kprobe_opcode_t insn, struct arch_specific_insn *asi, void *d)
 {
 	asi->insn_singlestep = t16_singlestep_it;
 	return INSN_GOOD_NO_SLOT;
@@ -1063,7 +1067,8 @@ t16_simulate_cond_branch(struct kprobe *p, struct pt_regs *regs)
 }
 
 static enum kprobe_insn __kprobes
-t16_decode_cond_branch(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+t16_decode_cond_branch(kprobe_opcode_t insn, struct arch_specific_insn *asi,
+		       void *d)
 {
 	int cc = (insn >> 8) & 0xf;
 	asi->insn_check_cc = kprobe_condition_checks[cc];
@@ -1149,7 +1154,7 @@ t16_emulate_hiregs(struct kprobe *p, struct pt_regs *regs)
 }
 
 static enum kprobe_insn __kprobes
-t16_decode_hiregs(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+t16_decode_hiregs(kprobe_opcode_t insn, struct arch_specific_insn *asi, void *d)
 {
 	insn &= ~0x00ff;
 	insn |= 0x001; /* Set Rdn = R1 and Rm = R0 */
@@ -1175,7 +1180,7 @@ t16_emulate_push(struct kprobe *p, struct pt_regs *regs)
 }
 
 static enum kprobe_insn __kprobes
-t16_decode_push(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+t16_decode_push(kprobe_opcode_t insn, struct arch_specific_insn *asi, void *d)
 {
 	/*
 	 * To simulate a PUSH we use a Thumb-2 "STMDB R9!, {registers}"
@@ -1225,7 +1230,7 @@ t16_emulate_pop_pc(struct kprobe *p, struct pt_regs *regs)
 }
 
 static enum kprobe_insn __kprobes
-t16_decode_pop(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+t16_decode_pop(kprobe_opcode_t insn, struct arch_specific_insn *asi, void *d)
 {
 	/*
 	 * To simulate a POP we use a Thumb-2 "LDMDB R9!, {registers}"
@@ -1244,11 +1249,11 @@ static const union decode_item t16_table_1011[] = {
 
 	/* ADD (SP plus immediate)	1011 0000 0xxx xxxx */
 	/* SUB (SP minus immediate)	1011 0000 1xxx xxxx */
-	DECODE_SIMULATE	(0xff00, 0xb000, t16_simulate_add_sp_imm),
+	DECODE_SIMULATE	(0xff00, 0xb000, PROBES_T16_ADD_SP),
 
 	/* CBZ				1011 00x1 xxxx xxxx */
 	/* CBNZ				1011 10x1 xxxx xxxx */
-	DECODE_SIMULATE	(0xf500, 0xb100, t16_simulate_cbz),
+	DECODE_SIMULATE	(0xf500, 0xb100, PROBES_T16_CBZ),
 
 	/* SXTH				1011 0010 00xx xxxx */
 	/* SXTB				1011 0010 01xx xxxx */
@@ -1259,12 +1264,12 @@ static const union decode_item t16_table_1011[] = {
 	/* ???				1011 1010 10xx xxxx */
 	/* REVSH			1011 1010 11xx xxxx */
 	DECODE_REJECT	(0xffc0, 0xba80),
-	DECODE_EMULATE	(0xf500, 0xb000, t16_emulate_loregs_rwflags),
+	DECODE_EMULATE	(0xf500, 0xb000, PROBES_T16_SIGN_EXTEND),
 
 	/* PUSH				1011 010x xxxx xxxx */
-	DECODE_CUSTOM	(0xfe00, 0xb400, t16_decode_push),
+	DECODE_CUSTOM	(0xfe00, 0xb400, PROBES_T16_PUSH),
 	/* POP				1011 110x xxxx xxxx */
-	DECODE_CUSTOM	(0xfe00, 0xbc00, t16_decode_pop),
+	DECODE_CUSTOM	(0xfe00, 0xbc00, PROBES_T16_POP),
 
 	/*
 	 * If-Then, and hints
@@ -1274,15 +1279,15 @@ static const union decode_item t16_table_1011[] = {
 	/* YIELD			1011 1111 0001 0000 */
 	DECODE_OR	(0xffff, 0xbf10),
 	/* SEV				1011 1111 0100 0000 */
-	DECODE_EMULATE	(0xffff, 0xbf40, probes_emulate_none),
+	DECODE_EMULATE	(0xffff, 0xbf40, PROBES_T16_SEV),
 	/* NOP				1011 1111 0000 0000 */
 	/* WFE				1011 1111 0010 0000 */
 	/* WFI				1011 1111 0011 0000 */
-	DECODE_SIMULATE	(0xffcf, 0xbf00, probes_simulate_nop),
+	DECODE_SIMULATE	(0xffcf, 0xbf00, PROBES_T16_WFE),
 	/* Unassigned hints		1011 1111 xxxx 0000 */
 	DECODE_REJECT	(0xff0f, 0xbf00),
 	/* IT				1011 1111 xxxx xxxx */
-	DECODE_CUSTOM	(0xff00, 0xbf00, t16_decode_it),
+	DECODE_CUSTOM	(0xff00, 0xbf00, PROBES_T16_IT),
 
 	/* SETEND			1011 0110 010x xxxx */
 	/* CPS				1011 0110 011x xxxx */
@@ -1299,7 +1304,7 @@ const union decode_item kprobe_decode_thumb16_table[] = {
 	 */
 
 	/* CMP (immediate)		0010 1xxx xxxx xxxx */
-	DECODE_EMULATE	(0xf800, 0x2800, t16_emulate_loregs_rwflags),
+	DECODE_EMULATE	(0xf800, 0x2800, PROBES_T16_CMP),
 
 	/* ADD (register)		0001 100x xxxx xxxx */
 	/* SUB (register)		0001 101x xxxx xxxx */
@@ -1311,7 +1316,7 @@ const union decode_item kprobe_decode_thumb16_table[] = {
 	/* MOV (immediate)		0010 0xxx xxxx xxxx */
 	/* ADD (immediate, Thumb)	0011 0xxx xxxx xxxx */
 	/* SUB (immediate, Thumb)	0011 1xxx xxxx xxxx */
-	DECODE_EMULATE	(0xc000, 0x0000, t16_emulate_loregs_noitrwflags),
+	DECODE_EMULATE	(0xc000, 0x0000, PROBES_T16_ADDSUB),
 
 	/*
 	 * 16-bit Thumb data-processing instructions
@@ -1319,10 +1324,10 @@ const union decode_item kprobe_decode_thumb16_table[] = {
 	 */
 
 	/* TST (register)		0100 0010 00xx xxxx */
-	DECODE_EMULATE	(0xffc0, 0x4200, t16_emulate_loregs_rwflags),
+	DECODE_EMULATE	(0xffc0, 0x4200, PROBES_T16_CMP),
 	/* CMP (register)		0100 0010 10xx xxxx */
 	/* CMN (register)		0100 0010 11xx xxxx */
-	DECODE_EMULATE	(0xff80, 0x4280, t16_emulate_loregs_rwflags),
+	DECODE_EMULATE	(0xff80, 0x4280, PROBES_T16_CMP),
 	/* AND (register)		0100 0000 00xx xxxx */
 	/* EOR (register)		0100 0000 01xx xxxx */
 	/* LSL (register)		0100 0000 10xx xxxx */
@@ -1336,7 +1341,7 @@ const union decode_item kprobe_decode_thumb16_table[] = {
 	/* MUL				0100 0011 00xx xxxx */
 	/* BIC (register)		0100 0011 10xx xxxx */
 	/* MVN (register)		0100 0011 10xx xxxx */
-	DECODE_EMULATE	(0xfc00, 0x4000, t16_emulate_loregs_noitrwflags),
+	DECODE_EMULATE	(0xfc00, 0x4000, PROBES_T16_LOGICAL),
 
 	/*
 	 * Special data instructions and branch and exchange
@@ -1348,7 +1353,7 @@ const union decode_item kprobe_decode_thumb16_table[] = {
 
 	/* BX (register)		0100 0111 0xxx xxxx */
 	/* BLX (register)		0100 0111 1xxx xxxx */
-	DECODE_SIMULATE (0xff00, 0x4700, t16_simulate_bxblx),
+	DECODE_SIMULATE (0xff00, 0x4700, PROBES_T16_BLX),
 
 	/* ADD pc, pc			0100 0100 1111 1111 */
 	DECODE_REJECT	(0xffff, 0x44ff),
@@ -1356,13 +1361,13 @@ const union decode_item kprobe_decode_thumb16_table[] = {
 	/* ADD (register)		0100 0100 xxxx xxxx */
 	/* CMP (register)		0100 0101 xxxx xxxx */
 	/* MOV (register)		0100 0110 xxxx xxxx */
-	DECODE_CUSTOM	(0xfc00, 0x4400, t16_decode_hiregs),
+	DECODE_CUSTOM	(0xfc00, 0x4400, PROBES_T16_HIREGOPS),
 
 	/*
 	 * Load from Literal Pool
 	 * LDR (literal)		0100 1xxx xxxx xxxx
 	 */
-	DECODE_SIMULATE	(0xf800, 0x4800, t16_simulate_ldr_literal),
+	DECODE_SIMULATE	(0xf800, 0x4800, PROBES_T16_LDR_LIT),
 
 	/*
 	 * 16-bit Thumb Load/store instructions
@@ -1383,20 +1388,20 @@ const union decode_item kprobe_decode_thumb16_table[] = {
 	/* LDR (immediate, Thumb)	0110 1xxx xxxx xxxx */
 	/* STRB (immediate, Thumb)	0111 0xxx xxxx xxxx */
 	/* LDRB (immediate, Thumb)	0111 1xxx xxxx xxxx */
-	DECODE_EMULATE	(0xc000, 0x4000, t16_emulate_loregs_rwflags),
+	DECODE_EMULATE	(0xc000, 0x4000, PROBES_T16_LDRHSTRH),
 	/* STRH (immediate, Thumb)	1000 0xxx xxxx xxxx */
 	/* LDRH (immediate, Thumb)	1000 1xxx xxxx xxxx */
-	DECODE_EMULATE	(0xf000, 0x8000, t16_emulate_loregs_rwflags),
+	DECODE_EMULATE	(0xf000, 0x8000, PROBES_T16_LDRHSTRH),
 	/* STR (immediate, Thumb)	1001 0xxx xxxx xxxx */
 	/* LDR (immediate, Thumb)	1001 1xxx xxxx xxxx */
-	DECODE_SIMULATE	(0xf000, 0x9000, t16_simulate_ldrstr_sp_relative),
+	DECODE_SIMULATE	(0xf000, 0x9000, PROBES_T16_LDRSTR),
 
 	/*
 	 * Generate PC-/SP-relative address
 	 * ADR (literal)		1010 0xxx xxxx xxxx
 	 * ADD (SP plus immediate)	1010 1xxx xxxx xxxx
 	 */
-	DECODE_SIMULATE	(0xf000, 0xa000, t16_simulate_reladr),
+	DECODE_SIMULATE	(0xf000, 0xa000, PROBES_T16_ADR),
 
 	/*
 	 * Miscellaneous 16-bit instructions
@@ -1406,7 +1411,7 @@ const union decode_item kprobe_decode_thumb16_table[] = {
 
 	/* STM				1100 0xxx xxxx xxxx */
 	/* LDM				1100 1xxx xxxx xxxx */
-	DECODE_EMULATE	(0xf000, 0xc000, t16_emulate_loregs_rwflags),
+	DECODE_EMULATE	(0xf000, 0xc000, PROBES_T16_LDMSTM),
 
 	/*
 	 * Conditional branch, and Supervisor Call
@@ -1417,16 +1422,70 @@ const union decode_item kprobe_decode_thumb16_table[] = {
 	DECODE_REJECT	(0xfe00, 0xde00),
 
 	/* Conditional branch		1101 xxxx xxxx xxxx */
-	DECODE_CUSTOM	(0xf000, 0xd000, t16_decode_cond_branch),
+	DECODE_CUSTOM	(0xf000, 0xd000, PROBES_T16_BRANCH_COND),
 
 	/*
 	 * Unconditional branch
 	 * B				1110 0xxx xxxx xxxx
 	 */
-	DECODE_SIMULATE	(0xf800, 0xe000, t16_simulate_branch),
+	DECODE_SIMULATE	(0xf800, 0xe000, PROBES_T16_BRANCH),
 
 	DECODE_END
 };
+
+const union decode_item kprobes_t32_actions[] = {
+	[PROBES_T32_LDMSTM] = {.decoder = t32_decode_ldmstm},
+	[PROBES_T32_LDRDSTRD] = {.handler = t32_emulate_ldrdstrd},
+	[PROBES_T32_TABLE_BRANCH] = {.handler = t32_simulate_table_branch},
+	[PROBES_T32_TST] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
+	[PROBES_T32_MOV] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
+	[PROBES_T32_ADDSUB] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
+	[PROBES_T32_LOGICAL] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
+	[PROBES_T32_CMP] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
+	[PROBES_T32_ADDWSUBW_PC] = {.handler = t32_emulate_rd8pc16_noflags,},
+	[PROBES_T32_ADDWSUBW] = {.handler = t32_emulate_rd8rn16_noflags},
+	[PROBES_T32_MOVW] = {.handler = t32_emulate_rd8rn16_noflags},
+	[PROBES_T32_SAT] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
+	[PROBES_T32_BITFIELD] = {.handler = t32_emulate_rd8rn16_noflags},
+	[PROBES_T32_SEV] = {.handler = probes_emulate_none},
+	[PROBES_T32_WFE] = {.handler = probes_simulate_nop},
+	[PROBES_T32_MRS] = {.handler = t32_simulate_mrs},
+	[PROBES_T32_BRANCH_COND] = {.decoder = t32_decode_cond_branch},
+	[PROBES_T32_BRANCH] = {.handler = t32_simulate_branch},
+	[PROBES_T32_PLDI] = {.handler = probes_simulate_nop},
+	[PROBES_T32_LDR_LIT] = {.handler = t32_simulate_ldr_literal},
+	[PROBES_T32_LDRSTR] = {.handler = t32_emulate_ldrstr},
+	[PROBES_T32_SIGN_EXTEND] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
+	[PROBES_T32_MEDIA] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
+	[PROBES_T32_REVERSE] = {.handler = t32_emulate_rd8rn16_noflags},
+	[PROBES_T32_MUL_ADD] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
+	[PROBES_T32_MUL_ADD2] = {.handler = t32_emulate_rd8rn16rm0ra12_noflags},
+	[PROBES_T32_MUL_ADD_LONG] = {.handler = t32_emulate_rdlo12rdhi8rn16rm0_noflags},
+};
+
+const union decode_item kprobes_t16_actions[] = {
+	[PROBES_T16_ADD_SP] = {.handler = t16_simulate_add_sp_imm},
+	[PROBES_T16_CBZ] = {.handler = t16_simulate_cbz},
+	[PROBES_T16_SIGN_EXTEND] = {.handler = t16_emulate_loregs_rwflags},
+	[PROBES_T16_PUSH] = {.decoder = t16_decode_push},
+	[PROBES_T16_POP] = {.decoder = t16_decode_pop},
+	[PROBES_T16_SEV] = {.handler = probes_emulate_none},
+	[PROBES_T16_WFE] = {.handler = probes_simulate_nop},
+	[PROBES_T16_IT] = {.decoder = t16_decode_it},
+	[PROBES_T16_CMP] = {.handler = t16_emulate_loregs_rwflags},
+	[PROBES_T16_ADDSUB] = {.handler = t16_emulate_loregs_noitrwflags},
+	[PROBES_T16_LOGICAL] = {.handler = t16_emulate_loregs_noitrwflags},
+	[PROBES_T16_LDR_LIT] = {.handler = t16_simulate_ldr_literal},
+	[PROBES_T16_BLX] = {.handler = t16_simulate_bxblx},
+	[PROBES_T16_HIREGOPS] = {.decoder = t16_decode_hiregs},
+	[PROBES_T16_LDRHSTRH] = {.handler = t16_emulate_loregs_rwflags},
+	[PROBES_T16_LDRSTR] = {.handler = t16_simulate_ldrstr_sp_relative},
+	[PROBES_T16_ADR] = {.handler = t16_simulate_reladr},
+	[PROBES_T16_LDMSTM] = {.handler = t16_emulate_loregs_rwflags},
+	[PROBES_T16_BRANCH_COND] = {.decoder = t16_decode_cond_branch},
+	[PROBES_T16_BRANCH] = {.handler = t16_simulate_branch},
+};
+
 #ifdef CONFIG_ARM_KPROBES_TEST_MODULE
 EXPORT_SYMBOL_GPL(kprobe_decode_thumb16_table);
 #endif
@@ -1453,17 +1512,21 @@ static void __kprobes thumb32_singlestep(struct kprobe *p, struct pt_regs *regs)
 }
 
 enum kprobe_insn __kprobes
-thumb16_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+thumb16_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
+			   bool usermode, const union decode_item *actions)
 {
 	asi->insn_singlestep = thumb16_singlestep;
 	asi->insn_check_cc = thumb_check_cc;
-	return kprobe_decode_insn(insn, asi, kprobe_decode_thumb16_table, true);
+	return probes_decode_insn(insn, asi, kprobe_decode_thumb16_table, true,
+				  usermode, actions);
 }
 
 enum kprobe_insn __kprobes
-thumb32_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+thumb32_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
+			   bool usermode, const union decode_item *actions)
 {
 	asi->insn_singlestep = thumb32_singlestep;
 	asi->insn_check_cc = thumb_check_cc;
-	return kprobe_decode_insn(insn, asi, kprobe_decode_thumb32_table, true);
+	return probes_decode_insn(insn, asi, kprobe_decode_thumb32_table, true,
+				  usermode, actions);
 }
diff --git a/arch/arm/kernel/kprobes.c b/arch/arm/kernel/kprobes.c
index 170e9f3..7b484d3 100644
--- a/arch/arm/kernel/kprobes.c
+++ b/arch/arm/kernel/kprobes.c
@@ -29,6 +29,7 @@
 #include <asm/cacheflush.h>
 
 #include "kprobes.h"
+#include "probes.h"
 #include "patch.h"
 
 #define MIN_STACK_SIZE(addr) 				\
@@ -54,6 +55,7 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
 	unsigned long addr = (unsigned long)p->addr;
 	bool thumb;
 	kprobe_decode_insn_t *decode_insn;
+	const union decode_item *actions;
 	int is;
 
 	if (in_exception_text(addr))
@@ -67,20 +69,25 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
 		insn <<= 16;
 		insn |= ((u16 *)addr)[1];
 		decode_insn = thumb32_kprobe_decode_insn;
-	} else
+		actions = kprobes_t32_actions;
+	} else {
 		decode_insn = thumb16_kprobe_decode_insn;
+		actions = kprobes_t16_actions;
+	}
 #else /* !CONFIG_THUMB2_KERNEL */
 	thumb = false;
 	if (addr & 0x3)
 		return -EINVAL;
 	insn = *p->addr;
 	decode_insn = arm_kprobe_decode_insn;
+	actions = kprobes_probes_actions;
 #endif
 
 	p->opcode = insn;
 	p->ainsn.insn = tmp_insn;
 
-	switch ((*decode_insn)(insn, &p->ainsn)) {
+	switch ((*decode_insn)(insn, &p->ainsn, false,
+			       actions)) {
 	case INSN_REJECTED:	/* not supported */
 		return -EINVAL;
 
diff --git a/arch/arm/kernel/kprobes.h b/arch/arm/kernel/kprobes.h
index 9aa2f15..6ca1cbe 100644
--- a/arch/arm/kernel/kprobes.h
+++ b/arch/arm/kernel/kprobes.h
@@ -34,22 +34,6 @@ enum kprobe_insn {
 	INSN_GOOD_NO_SLOT
 };
 
-typedef enum kprobe_insn (kprobe_decode_insn_t)(kprobe_opcode_t,
-						struct arch_specific_insn *);
-
-#ifdef CONFIG_THUMB2_KERNEL
-
-enum kprobe_insn thumb16_kprobe_decode_insn(kprobe_opcode_t,
-						struct arch_specific_insn *);
-enum kprobe_insn thumb32_kprobe_decode_insn(kprobe_opcode_t,
-						struct arch_specific_insn *);
-
-#else /* !CONFIG_THUMB2_KERNEL */
-
-enum kprobe_insn arm_kprobe_decode_insn(kprobe_opcode_t,
-					struct arch_specific_insn *);
-#endif
-
 void __init arm_kprobe_decode_init(void);
 
 extern kprobe_check_cc * const kprobe_condition_checks[16];
@@ -161,11 +145,9 @@ static inline void __kprobes alu_write_pc(long pcv, struct pt_regs *regs)
 }
 
 
-void __kprobes probes_simulate_nop(struct kprobe *p, struct pt_regs *regs);
-void __kprobes probes_emulate_none(struct kprobe *p, struct pt_regs *regs);
-
 enum kprobe_insn __kprobes
-kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi);
+kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi,
+		     void *d);
 
 /*
  * Test if load/store instructions writeback the address register.
@@ -331,7 +313,9 @@ union decode_item {
 	u32			bits;
 	const union decode_item	*table;
 	kprobe_insn_handler_t	*handler;
-	kprobe_decode_insn_t	*decoder;
+	enum kprobe_insn (*decoder)(kprobe_opcode_t,
+				    struct arch_specific_insn *,
+				    void *d);
 };
 
 
@@ -368,7 +352,7 @@ struct decode_custom {
 
 #define DECODE_CUSTOM(_mask, _value, _decoder)			\
 	DECODE_HEADER(DECODE_TYPE_CUSTOM, _mask, _value, 0),	\
-	{.decoder = (_decoder)}
+	{.bits = (_decoder)}
 
 
 struct decode_simulate {
@@ -378,7 +362,7 @@ struct decode_simulate {
 
 #define DECODE_SIMULATEX(_mask, _value, _handler, _regs)		\
 	DECODE_HEADER(DECODE_TYPE_SIMULATE, _mask, _value, _regs),	\
-	{.handler = (_handler)}
+	{.bits = (_handler)}
 
 #define DECODE_SIMULATE(_mask, _value, _handler)	\
 	DECODE_SIMULATEX(_mask, _value, _handler, 0)
@@ -391,12 +375,11 @@ struct decode_emulate {
 
 #define DECODE_EMULATEX(_mask, _value, _handler, _regs)			\
 	DECODE_HEADER(DECODE_TYPE_EMULATE, _mask, _value, _regs),	\
-	{.handler = (_handler)}
+	{.bits = (_handler)}
 
 #define DECODE_EMULATE(_mask, _value, _handler)		\
 	DECODE_EMULATEX(_mask, _value, _handler, 0)
 
-
 struct decode_or {
 	struct decode_header	header;
 };
@@ -413,16 +396,39 @@ struct decode_reject {
 	DECODE_HEADER(DECODE_TYPE_REJECT, _mask, _value, 0)
 
 
+typedef enum kprobe_insn (kprobe_decode_insn_t)(kprobe_opcode_t,
+						struct arch_specific_insn *,
+						bool usermode,
+						const union decode_item *actions);
+
 #ifdef CONFIG_THUMB2_KERNEL
+
+enum kprobe_insn thumb16_kprobe_decode_insn(kprobe_opcode_t,
+					    struct arch_specific_insn *,
+					    bool usermode,
+					    const union decode_item *actions);
+
+enum kprobe_insn thumb32_kprobe_decode_insn(kprobe_opcode_t,
+					    struct arch_specific_insn *,
+					    bool usermode,
+					    const union decode_item *actions);
+
 extern const union decode_item kprobe_decode_thumb16_table[];
 extern const union decode_item kprobe_decode_thumb32_table[];
-#else
-extern const union decode_item kprobe_decode_arm_table[];
-#endif
+extern const union decode_item kprobes_t32_actions[];
+extern const union decode_item kprobes_t16_actions[];
 
+#else
 
 int kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
-			const union decode_item *table, bool thumb16);
+			const union decode_item *table, bool thumb16,
+			bool usermode,
+			const union decode_item *actions);
+
+extern const union decode_item kprobe_decode_arm_table[];
+extern const union decode_item kprobes_probes_actions[];
+
+#endif
 
 
 #endif /* _ARM_KERNEL_KPROBES_H */
diff --git a/arch/arm/kernel/probes-arm.c b/arch/arm/kernel/probes-arm.c
index e1b1a6e..e67bf3f 100644
--- a/arch/arm/kernel/probes-arm.c
+++ b/arch/arm/kernel/probes-arm.c
@@ -18,8 +18,8 @@
 #include <linux/kernel.h>
 #include <linux/kprobes.h>
 
-#include "probes.h"
 #include "kprobes.h"
+#include "probes.h"
 
 #define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
 
diff --git a/arch/arm/kernel/probes-arm.h b/arch/arm/kernel/probes-arm.h
new file mode 100644
index 0000000..b1690878
--- /dev/null
+++ b/arch/arm/kernel/probes-arm.h
@@ -0,0 +1,60 @@
+#ifndef _ARM_KERNEL_PROBES_ARM_H
+#define  _ARM_KERNEL_PROBES_ARM_H
+
+enum probes_action {
+	PROBES_EMULATE_NONE,
+	PROBES_SIMULATE_NOP,
+	PROBES_PRELOAD_IMM,
+	PROBES_PRELOAD_REG,
+	PROBES_BRANCH_IMM,
+	PROBES_BRANCH_REG,
+	PROBES_MRS,
+	PROBES_CLZ,
+	PROBES_SATURATING_ARITHMETIC,
+	PROBES_MUL1,
+	PROBES_MUL2,
+	PROBES_SWP,
+	PROBES_LDRSTRD,
+	PROBES_LOAD,
+	PROBES_STORE,
+	PROBES_LOAD_EXTRA,
+	PROBES_STORE_EXTRA,
+	PROBES_MOV_IP_SP,
+	PROBES_DATA_PROCESSING_REG,
+	PROBES_DATA_PROCESSING_IMM,
+	PROBES_MOV_HALFWORD,
+	PROBES_SEV,
+	PROBES_WFE,
+	PROBES_SATURATE,
+	PROBES_REV,
+	PROBES_MMI,
+	PROBES_PACK,
+	PROBES_EXTEND,
+	PROBES_EXTEND_ADD,
+	PROBES_MUL_ADD_LONG,
+	PROBES_MUL_ADD,
+	PROBES_BITFIELD,
+	PROBES_BRANCH,
+	PROBES_LDMSTM
+};
+
+void __kprobes simulate_bbl(struct kprobe *p, struct pt_regs *regs);
+void __kprobes simulate_blx1(struct kprobe *p, struct pt_regs *regs);
+void __kprobes simulate_blx2bx(struct kprobe *p, struct pt_regs *regs);
+void __kprobes simulate_mrs(struct kprobe *p, struct pt_regs *regs);
+void __kprobes simulate_mov_ipsp(struct kprobe *p, struct pt_regs *regs);
+void __kprobes emulate_ldrdstrd(struct kprobe *p, struct pt_regs *regs);
+void __kprobes emulate_ldr(struct kprobe *p, struct pt_regs *regs);
+void __kprobes emulate_str(struct kprobe *p, struct pt_regs *regs);
+void __kprobes emulate_rd12rn16rm0rs8_rwflags(struct kprobe *p,
+					      struct pt_regs *regs);
+void __kprobes emulate_rd12rn16rm0_rwflags_nopc(struct kprobe *p,
+						struct pt_regs *regs);
+void __kprobes emulate_rd16rn12rm0rs8_rwflags_nopc(struct kprobe *p,
+						   struct pt_regs *regs);
+void __kprobes emulate_rd12rm0_noflags_nopc(struct kprobe *p,
+					    struct pt_regs *regs);
+void __kprobes emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(struct kprobe *p,
+						       struct pt_regs *regs);
+
+#endif
diff --git a/arch/arm/kernel/probes-thumb.h b/arch/arm/kernel/probes-thumb.h
new file mode 100644
index 0000000..4b9365c
--- /dev/null
+++ b/arch/arm/kernel/probes-thumb.h
@@ -0,0 +1,59 @@
+#ifndef _ARM_KERNEL_PROBES_THUMB_H
+#define  _ARM_KERNEL_PROBES_THUMB_H
+
+enum probes_t32_action {
+	PROBES_T32_EMULATE_NONE,
+	PROBES_T32_SIMULATE_NOP,
+	PROBES_T32_LDMSTM,
+	PROBES_T32_LDRDSTRD,
+	PROBES_T32_TABLE_BRANCH,
+	PROBES_T32_TST,
+	PROBES_T32_CMP,
+	PROBES_T32_MOV,
+	PROBES_T32_ADDSUB,
+	PROBES_T32_LOGICAL,
+	PROBES_T32_ADDWSUBW_PC,
+	PROBES_T32_ADDWSUBW,
+	PROBES_T32_MOVW,
+	PROBES_T32_SAT,
+	PROBES_T32_BITFIELD,
+	PROBES_T32_SEV,
+	PROBES_T32_WFE,
+	PROBES_T32_MRS,
+	PROBES_T32_BRANCH_COND,
+	PROBES_T32_BRANCH,
+	PROBES_T32_PLDI,
+	PROBES_T32_LDR_LIT,
+	PROBES_T32_LDRSTR,
+	PROBES_T32_SIGN_EXTEND,
+	PROBES_T32_MEDIA,
+	PROBES_T32_REVERSE,
+	PROBES_T32_MUL_ADD,
+	PROBES_T32_MUL_ADD2,
+	PROBES_T32_MUL_ADD_LONG
+};
+
+enum probes_t16_action {
+	PROBES_T16_ADD_SP,
+	PROBES_T16_CBZ,
+	PROBES_T16_SIGN_EXTEND,
+	PROBES_T16_PUSH,
+	PROBES_T16_POP,
+	PROBES_T16_SEV,
+	PROBES_T16_WFE,
+	PROBES_T16_IT,
+	PROBES_T16_CMP,
+	PROBES_T16_ADDSUB,
+	PROBES_T16_LOGICAL,
+	PROBES_T16_BLX,
+	PROBES_T16_HIREGOPS,
+	PROBES_T16_LDR_LIT,
+	PROBES_T16_LDRHSTRH,
+	PROBES_T16_LDRSTR,
+	PROBES_T16_ADR,
+	PROBES_T16_LDMSTM,
+	PROBES_T16_BRANCH_COND,
+	PROBES_T16_BRANCH
+};
+
+#endif
diff --git a/arch/arm/kernel/probes.c b/arch/arm/kernel/probes.c
index 86c63f3..8cff27a 100644
--- a/arch/arm/kernel/probes.c
+++ b/arch/arm/kernel/probes.c
@@ -1,23 +1,8 @@
-/*
- * arch/arm/kernel/probes.c
- *
- * Some contents moved here from arch/arm/include/asm/kprobes-common.c
- *
- * Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
- *
- * Some contents moved here from arch/arm/include/asm/kprobes-arm.c which is
- * 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.
- */
-
 #include <linux/kernel.h>
 #include <linux/kprobes.h>
 
-#include "probes.h"
 #include "kprobes.h"
+#include "probes.h"
 
 void __kprobes probes_simulate_nop(struct kprobe *p, struct pt_regs *regs)
 {
@@ -28,6 +13,47 @@ void __kprobes probes_emulate_none(struct kprobe *p, struct pt_regs *regs)
 	p->ainsn.insn_fn();
 }
 
+#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
+
+#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25)
+
+#if  __LINUX_ARM_ARCH__ >= 6
+#define BLX(reg)	"blx	"reg"		\n\t"
+#else
+#define BLX(reg)	"mov	lr, pc		\n\t"	\
+			"mov	pc, "reg"	\n\t"
+#endif
+
+/*
+ * To avoid 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 performance.
+ *
+ * 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 perform register writeback)
+ * 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.
+ */
+
 /*
  * Prepare an instruction slot to receive an instruction for emulating.
  * This is done by placing a subroutine return after the location where the
@@ -97,7 +123,7 @@ set_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
  * non-zero value, the corresponding nibble in pinsn is validated and modified
  * according to the type.
  */
-static bool __kprobes decode_regs(kprobe_opcode_t *pinsn, u32 regs)
+static bool __kprobes decode_regs(kprobe_opcode_t *pinsn, u32 regs, bool modify)
 {
 	kprobe_opcode_t insn = *pinsn;
 	kprobe_opcode_t mask = 0xf; /* Start at least significant nibble */
@@ -158,12 +184,16 @@ static bool __kprobes decode_regs(kprobe_opcode_t *pinsn, u32 regs)
 			break;
 		}
 
-		/* Replace value of nibble with new register number... */
-		insn &= ~mask;
-		insn |= new_bits & mask;
+		if (modify) {
+			/* Replace value of nibble with new register number */
+			insn &= ~mask;
+			insn |= new_bits & mask;
+		}
 	}
 
-	*pinsn = insn;
+	if (modify)
+		*pinsn = insn;
+
 	return true;
 
 reject:
@@ -223,14 +253,17 @@ static const int decode_struct_sizes[NUM_DECODE_TYPES] = {
  *
  */
 int __kprobes
-kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
-				const union decode_item *table, bool thumb)
+probes_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
+				const union decode_item *table, bool thumb,
+				bool usermode,
+				const union decode_item *actions)
 {
 	const struct decode_header *h = (struct decode_header *)table;
 	const struct decode_header *next;
 	bool matched = false;
 
-	insn = prepare_emulated_insn(insn, asi, thumb);
+	if (!usermode)
+		insn = prepare_emulated_insn(insn, asi, thumb);
 
 	for (;; h = next) {
 		enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK;
@@ -242,14 +275,14 @@ kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
 		next = (struct decode_header *)
 				((uintptr_t)h + decode_struct_sizes[type]);
 
-		if (!matched && (insn & h->mask.bits) != h->value.bits)
+		if (!matched &&
+		    (insn & h->mask.bits) != h->value.bits)
 			continue;
 
-		if (!decode_regs(&insn, regs))
+		if (!decode_regs(&insn, regs, !usermode))
 			return INSN_REJECTED;
 
 		switch (type) {
-
 		case DECODE_TYPE_TABLE: {
 			struct decode_table *d = (struct decode_table *)h;
 			next = (struct decode_header *)d->table.table;
@@ -258,18 +291,24 @@ kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
 
 		case DECODE_TYPE_CUSTOM: {
 			struct decode_custom *d = (struct decode_custom *)h;
-			return (*d->decoder.decoder)(insn, asi);
+
+			return actions[d->decoder.bits].decoder(insn, asi, d);
 		}
 
 		case DECODE_TYPE_SIMULATE: {
 			struct decode_simulate *d = (struct decode_simulate *)h;
-			asi->insn_handler = d->handler.handler;
+			asi->insn_handler = actions[d->handler.bits].handler;
 			return INSN_GOOD_NO_SLOT;
 		}
 
 		case DECODE_TYPE_EMULATE: {
 			struct decode_emulate *d = (struct decode_emulate *)h;
-			asi->insn_handler = d->handler.handler;
+
+			if (usermode)
+				return actions[d->handler.bits].decoder(insn,
+									asi, d);
+
+			asi->insn_handler = actions[d->handler.bits].handler;
 			set_emulated_insn(insn, asi, thumb);
 			return INSN_GOOD;
 		}
diff --git a/arch/arm/kernel/probes.h b/arch/arm/kernel/probes.h
index 56eec12..bc03c690 100644
--- a/arch/arm/kernel/probes.h
+++ b/arch/arm/kernel/probes.h
@@ -1,6 +1,18 @@
 #ifndef _ARM_KERNEL_PROBES_H
 #define  _ARM_KERNEL_PROBES_H
 
+int __kprobes
+probes_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
+				const union decode_item *table, bool thumb,
+				bool usermode,
+				const union decode_item *actions);
+enum kprobe_insn arm_kprobe_decode_insn(kprobe_opcode_t,
+					struct arch_specific_insn *,
+					bool usermode,
+					const union decode_item *actions);
+
+void __kprobes probes_simulate_nop(struct kprobe *p, struct pt_regs *regs);
+void __kprobes probes_emulate_none(struct kprobe *p, struct pt_regs *regs);
 void __kprobes simulate_bbl(struct kprobe *p, struct pt_regs *regs);
 void __kprobes simulate_blx1(struct kprobe *p, struct pt_regs *regs);
 void __kprobes simulate_blx2bx(struct kprobe *p, struct pt_regs *regs);
-- 
1.8.1.2


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 8/9] ARM: Add "action" table for kprobes/uprobes instruction
@ 2013-08-01 23:45   ` David Long
  0 siblings, 0 replies; 32+ messages in thread
From: David Long @ 2013-08-01 23:45 UTC (permalink / raw)
  To: linux-arm-kernel

From: "David A. Long" <dave.long@linaro.org>

Add a table of functions used by the (previously) kprobes instruction
parsing code so that it can also be used by uprobes. kprobes and
(eventually) uprobes will each provide their own unique table of
actions for each category of instruction recognized by the parser.

Signed-off-by: David A. Long <dave.long@linaro.org>
---
 arch/arm/kernel/kprobes-arm.c    | 188 +++++++++++++++++----------------
 arch/arm/kernel/kprobes-common.c |   3 +-
 arch/arm/kernel/kprobes-thumb.c  | 217 +++++++++++++++++++++++++--------------
 arch/arm/kernel/kprobes.c        |  11 +-
 arch/arm/kernel/kprobes.h        |  64 ++++++------
 arch/arm/kernel/probes-arm.c     |   2 +-
 arch/arm/kernel/probes-arm.h     |  60 +++++++++++
 arch/arm/kernel/probes-thumb.h   |  59 +++++++++++
 arch/arm/kernel/probes.c         |  99 ++++++++++++------
 arch/arm/kernel/probes.h         |  12 +++
 10 files changed, 488 insertions(+), 227 deletions(-)
 create mode 100644 arch/arm/kernel/probes-arm.h
 create mode 100644 arch/arm/kernel/probes-thumb.h

diff --git a/arch/arm/kernel/kprobes-arm.c b/arch/arm/kernel/kprobes-arm.c
index d6503cc..0620eba 100644
--- a/arch/arm/kernel/kprobes-arm.c
+++ b/arch/arm/kernel/kprobes-arm.c
@@ -62,38 +62,50 @@
 #include <linux/kprobes.h>
 #include <linux/module.h>
 
-#include "probes.h"
 #include "kprobes.h"
-
-/*
- * To avoid 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 performance.
- *
- * 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 perform register writeback)
- * 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.
- */
+#include "probes.h"
+#include "probes-arm.h"
+
+const union decode_item kprobes_probes_actions[] = {
+	[PROBES_EMULATE_NONE] = {.handler = probes_emulate_none},
+	[PROBES_SIMULATE_NOP] = {.handler = probes_simulate_nop},
+	[PROBES_PRELOAD_IMM] = {.handler = probes_simulate_nop},
+	[PROBES_PRELOAD_REG] = {.handler = probes_simulate_nop},
+	[PROBES_BRANCH_IMM] = {.handler = simulate_blx1},
+	[PROBES_MRS] = {.handler = simulate_mrs},
+	[PROBES_BRANCH_REG] = {.handler = simulate_blx2bx},
+	[PROBES_CLZ] = {.handler = emulate_rd12rm0_noflags_nopc},
+	[PROBES_SATURATING_ARITHMETIC] = {
+		.handler = emulate_rd12rn16rm0_rwflags_nopc},
+	[PROBES_MUL1] = {.handler = emulate_rdlo12rdhi16rn0rm8_rwflags_nopc},
+	[PROBES_MUL2] = {.handler = emulate_rd16rn12rm0rs8_rwflags_nopc},
+	[PROBES_SWP] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
+	[PROBES_LDRSTRD] = {.handler = emulate_ldrdstrd},
+	[PROBES_LOAD_EXTRA] = {.handler = emulate_ldr},
+	[PROBES_LOAD] = {.handler = emulate_ldr},
+	[PROBES_STORE_EXTRA] = {.handler = emulate_str},
+	[PROBES_STORE] = {.handler = emulate_str},
+	[PROBES_MOV_IP_SP] = {.handler = simulate_mov_ipsp},
+	[PROBES_DATA_PROCESSING_REG] = {
+		.handler = emulate_rd12rn16rm0rs8_rwflags},
+	[PROBES_DATA_PROCESSING_IMM] = {
+		.handler = emulate_rd12rn16rm0rs8_rwflags},
+	[PROBES_MOV_HALFWORD] = {.handler = emulate_rd12rm0_noflags_nopc},
+	[PROBES_SEV] = {.handler = probes_emulate_none},
+	[PROBES_WFE] = {.handler = probes_simulate_nop},
+	[PROBES_SATURATE] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
+	[PROBES_REV] = {.handler = emulate_rd12rm0_noflags_nopc},
+	[PROBES_MMI] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
+	[PROBES_PACK] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
+	[PROBES_EXTEND] = {.handler = emulate_rd12rm0_noflags_nopc},
+	[PROBES_EXTEND_ADD] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
+	[PROBES_MUL_ADD_LONG] = {
+		.handler = emulate_rdlo12rdhi16rn0rm8_rwflags_nopc},
+	[PROBES_MUL_ADD] = {.handler = emulate_rd16rn12rm0rs8_rwflags_nopc},
+	[PROBES_BITFIELD] = {.handler = emulate_rd12rm0_noflags_nopc},
+	[PROBES_BRANCH] = {.handler = simulate_bbl},
+	[PROBES_LDMSTM] = {.decoder = kprobe_decode_ldmstm}
+};
 
 /*
  * For the instruction masking and comparisons in all the "space_*"
@@ -112,16 +124,16 @@ static const union decode_item arm_1111_table[] = {
 	/* PLDI (immediate)	1111 0100 x101 xxxx xxxx xxxx xxxx xxxx */
 	/* PLDW (immediate)	1111 0101 x001 xxxx xxxx xxxx xxxx xxxx */
 	/* PLD (immediate)	1111 0101 x101 xxxx xxxx xxxx xxxx xxxx */
-	DECODE_SIMULATE	(0xfe300000, 0xf4100000, probes_simulate_nop),
+	DECODE_SIMULATE	(0xfe300000, 0xf4100000, PROBES_PRELOAD_IMM),
 
 	/* memory hint		1111 0110 x001 xxxx xxxx xxxx xxx0 xxxx */
 	/* PLDI (register)	1111 0110 x101 xxxx xxxx xxxx xxx0 xxxx */
 	/* PLDW (register)	1111 0111 x001 xxxx xxxx xxxx xxx0 xxxx */
 	/* PLD (register)	1111 0111 x101 xxxx xxxx xxxx xxx0 xxxx */
-	DECODE_SIMULATE	(0xfe300010, 0xf6100000, probes_simulate_nop),
+	DECODE_SIMULATE	(0xfe300010, 0xf6100000, PROBES_PRELOAD_REG),
 
 	/* BLX (immediate)	1111 101x xxxx xxxx xxxx xxxx xxxx xxxx */
-	DECODE_SIMULATE	(0xfe000000, 0xfa000000, simulate_blx1),
+	DECODE_SIMULATE	(0xfe000000, 0xfa000000, PROBES_BRANCH_IMM),
 
 	/* CPS			1111 0001 0000 xxx0 xxxx xxxx xx0x xxxx */
 	/* SETEND		1111 0001 0000 0001 xxxx xxxx 0000 xxxx */
@@ -145,25 +157,25 @@ static const union decode_item arm_cccc_0001_0xx0____0xxx_table[] = {
 	/* Miscellaneous instructions					*/
 
 	/* MRS cpsr		cccc 0001 0000 xxxx xxxx xxxx 0000 xxxx */
-	DECODE_SIMULATEX(0x0ff000f0, 0x01000000, simulate_mrs,
+	DECODE_SIMULATEX(0x0ff000f0, 0x01000000, PROBES_MRS,
 						 REGS(0, NOPC, 0, 0, 0)),
 
 	/* BX			cccc 0001 0010 xxxx xxxx xxxx 0001 xxxx */
-	DECODE_SIMULATE	(0x0ff000f0, 0x01200010, simulate_blx2bx),
+	DECODE_SIMULATE	(0x0ff000f0, 0x01200010, PROBES_BRANCH_REG),
 
 	/* BLX (register)	cccc 0001 0010 xxxx xxxx xxxx 0011 xxxx */
-	DECODE_SIMULATEX(0x0ff000f0, 0x01200030, simulate_blx2bx,
+	DECODE_SIMULATEX(0x0ff000f0, 0x01200030, PROBES_BRANCH_REG,
 						 REGS(0, 0, 0, 0, NOPC)),
 
 	/* CLZ			cccc 0001 0110 xxxx xxxx xxxx 0001 xxxx */
-	DECODE_EMULATEX	(0x0ff000f0, 0x01600010, emulate_rd12rm0_noflags_nopc,
+	DECODE_EMULATEX	(0x0ff000f0, 0x01600010, PROBES_CLZ,
 						 REGS(0, NOPC, 0, 0, NOPC)),
 
 	/* QADD			cccc 0001 0000 xxxx xxxx xxxx 0101 xxxx */
 	/* QSUB			cccc 0001 0010 xxxx xxxx xxxx 0101 xxxx */
 	/* QDADD		cccc 0001 0100 xxxx xxxx xxxx 0101 xxxx */
 	/* QDSUB		cccc 0001 0110 xxxx xxxx xxxx 0101 xxxx */
-	DECODE_EMULATEX	(0x0f9000f0, 0x01000050, emulate_rd12rn16rm0_rwflags_nopc,
+	DECODE_EMULATEX	(0x0f9000f0, 0x01000050, PROBES_SATURATING_ARITHMETIC,
 						 REGS(NOPC, NOPC, 0, 0, NOPC)),
 
 	/* BXJ			cccc 0001 0010 xxxx xxxx xxxx 0010 xxxx */
@@ -179,19 +191,19 @@ static const union decode_item arm_cccc_0001_0xx0____1xx0_table[] = {
 	/* Halfword multiply and multiply-accumulate			*/
 
 	/* SMLALxy		cccc 0001 0100 xxxx xxxx xxxx 1xx0 xxxx */
-	DECODE_EMULATEX	(0x0ff00090, 0x01400080, emulate_rdlo12rdhi16rn0rm8_rwflags_nopc,
+	DECODE_EMULATEX	(0x0ff00090, 0x01400080, PROBES_MUL1,
 						 REGS(NOPC, NOPC, NOPC, 0, NOPC)),
 
 	/* SMULWy		cccc 0001 0010 xxxx xxxx xxxx 1x10 xxxx */
 	DECODE_OR	(0x0ff000b0, 0x012000a0),
 	/* SMULxy		cccc 0001 0110 xxxx xxxx xxxx 1xx0 xxxx */
-	DECODE_EMULATEX	(0x0ff00090, 0x01600080, emulate_rd16rn12rm0rs8_rwflags_nopc,
+	DECODE_EMULATEX	(0x0ff00090, 0x01600080, PROBES_MUL2,
 						 REGS(NOPC, 0, NOPC, 0, NOPC)),
 
 	/* SMLAxy		cccc 0001 0000 xxxx xxxx xxxx 1xx0 xxxx */
 	DECODE_OR	(0x0ff00090, 0x01000080),
 	/* SMLAWy		cccc 0001 0010 xxxx xxxx xxxx 1x00 xxxx */
-	DECODE_EMULATEX	(0x0ff000b0, 0x01200080, emulate_rd16rn12rm0rs8_rwflags_nopc,
+	DECODE_EMULATEX	(0x0ff000b0, 0x01200080, PROBES_MUL2,
 						 REGS(NOPC, NOPC, NOPC, 0, NOPC)),
 
 	DECODE_END
@@ -202,14 +214,14 @@ static const union decode_item arm_cccc_0000_____1001_table[] = {
 
 	/* MUL			cccc 0000 0000 xxxx xxxx xxxx 1001 xxxx */
 	/* MULS			cccc 0000 0001 xxxx xxxx xxxx 1001 xxxx */
-	DECODE_EMULATEX	(0x0fe000f0, 0x00000090, emulate_rd16rn12rm0rs8_rwflags_nopc,
+	DECODE_EMULATEX	(0x0fe000f0, 0x00000090, PROBES_MUL2,
 						 REGS(NOPC, 0, NOPC, 0, NOPC)),
 
 	/* MLA			cccc 0000 0010 xxxx xxxx xxxx 1001 xxxx */
 	/* MLAS			cccc 0000 0011 xxxx xxxx xxxx 1001 xxxx */
 	DECODE_OR	(0x0fe000f0, 0x00200090),
 	/* MLS			cccc 0000 0110 xxxx xxxx xxxx 1001 xxxx */
-	DECODE_EMULATEX	(0x0ff000f0, 0x00600090, emulate_rd16rn12rm0rs8_rwflags_nopc,
+	DECODE_EMULATEX	(0x0ff000f0, 0x00600090, PROBES_MUL2,
 						 REGS(NOPC, NOPC, NOPC, 0, NOPC)),
 
 	/* UMAAL		cccc 0000 0100 xxxx xxxx xxxx 1001 xxxx */
@@ -222,7 +234,7 @@ static const union decode_item arm_cccc_0000_____1001_table[] = {
 	/* SMULLS		cccc 0000 1101 xxxx xxxx xxxx 1001 xxxx */
 	/* SMLAL		cccc 0000 1110 xxxx xxxx xxxx 1001 xxxx */
 	/* SMLALS		cccc 0000 1111 xxxx xxxx xxxx 1001 xxxx */
-	DECODE_EMULATEX	(0x0f8000f0, 0x00800090, emulate_rdlo12rdhi16rn0rm8_rwflags_nopc,
+	DECODE_EMULATEX	(0x0f8000f0, 0x00800090, PROBES_MUL1,
 						 REGS(NOPC, NOPC, NOPC, 0, NOPC)),
 
 	DECODE_END
@@ -234,7 +246,7 @@ static const union decode_item arm_cccc_0001_____1001_table[] = {
 #if __LINUX_ARM_ARCH__ < 6
 	/* Deprecated on ARMv6 and may be UNDEFINED on v7		*/
 	/* SMP/SWPB		cccc 0001 0x00 xxxx xxxx xxxx 1001 xxxx */
-	DECODE_EMULATEX	(0x0fb000f0, 0x01000090, emulate_rd12rn16rm0_rwflags_nopc,
+	DECODE_EMULATEX	(0x0fb000f0, 0x01000090, PROBES_SWP,
 						 REGS(NOPC, NOPC, 0, 0, NOPC)),
 #endif
 	/* LDREX/STREX{,D,B,H}	cccc 0001 1xxx xxxx xxxx xxxx 1001 xxxx */
@@ -257,32 +269,32 @@ static const union decode_item arm_cccc_000x_____1xx1_table[] = {
 
 	/* LDRD (register)	cccc 000x x0x0 xxxx xxxx xxxx 1101 xxxx */
 	/* STRD (register)	cccc 000x x0x0 xxxx xxxx xxxx 1111 xxxx */
-	DECODE_EMULATEX	(0x0e5000d0, 0x000000d0, emulate_ldrdstrd,
+	DECODE_EMULATEX	(0x0e5000d0, 0x000000d0, PROBES_LDRSTRD,
 						 REGS(NOPCWB, NOPCX, 0, 0, NOPC)),
 
 	/* LDRD (immediate)	cccc 000x x1x0 xxxx xxxx xxxx 1101 xxxx */
 	/* STRD (immediate)	cccc 000x x1x0 xxxx xxxx xxxx 1111 xxxx */
-	DECODE_EMULATEX	(0x0e5000d0, 0x004000d0, emulate_ldrdstrd,
+	DECODE_EMULATEX	(0x0e5000d0, 0x004000d0, PROBES_LDRSTRD,
 						 REGS(NOPCWB, NOPCX, 0, 0, 0)),
 
 	/* STRH (register)	cccc 000x x0x0 xxxx xxxx xxxx 1011 xxxx */
-	DECODE_EMULATEX	(0x0e5000f0, 0x000000b0, emulate_str,
+	DECODE_EMULATEX	(0x0e5000f0, 0x000000b0, PROBES_STORE_EXTRA,
 						 REGS(NOPCWB, NOPC, 0, 0, NOPC)),
 
 	/* LDRH (register)	cccc 000x x0x1 xxxx xxxx xxxx 1011 xxxx */
 	/* LDRSB (register)	cccc 000x x0x1 xxxx xxxx xxxx 1101 xxxx */
 	/* LDRSH (register)	cccc 000x x0x1 xxxx xxxx xxxx 1111 xxxx */
-	DECODE_EMULATEX	(0x0e500090, 0x00100090, emulate_ldr,
+	DECODE_EMULATEX	(0x0e500090, 0x00100090, PROBES_LOAD_EXTRA,
 						 REGS(NOPCWB, NOPC, 0, 0, NOPC)),
 
 	/* STRH (immediate)	cccc 000x x1x0 xxxx xxxx xxxx 1011 xxxx */
-	DECODE_EMULATEX	(0x0e5000f0, 0x004000b0, emulate_str,
+	DECODE_EMULATEX	(0x0e5000f0, 0x004000b0, PROBES_STORE_EXTRA,
 						 REGS(NOPCWB, NOPC, 0, 0, 0)),
 
 	/* LDRH (immediate)	cccc 000x x1x1 xxxx xxxx xxxx 1011 xxxx */
 	/* LDRSB (immediate)	cccc 000x x1x1 xxxx xxxx xxxx 1101 xxxx */
 	/* LDRSH (immediate)	cccc 000x x1x1 xxxx xxxx xxxx 1111 xxxx */
-	DECODE_EMULATEX	(0x0e500090, 0x00500090, emulate_ldr,
+	DECODE_EMULATEX	(0x0e500090, 0x00500090, PROBES_LOAD_EXTRA,
 						 REGS(NOPCWB, NOPC, 0, 0, 0)),
 
 	DECODE_END
@@ -295,18 +307,18 @@ static const union decode_item arm_cccc_000x_table[] = {
 	DECODE_REJECT	(0x0e10f000, 0x0010f000),
 
 	/* MOV IP, SP		1110 0001 1010 0000 1100 0000 0000 1101 */
-	DECODE_SIMULATE	(0xffffffff, 0xe1a0c00d, simulate_mov_ipsp),
+	DECODE_SIMULATE	(0xffffffff, 0xe1a0c00d, PROBES_MOV_IP_SP),
 
 	/* TST (register)	cccc 0001 0001 xxxx xxxx xxxx xxx0 xxxx */
 	/* TEQ (register)	cccc 0001 0011 xxxx xxxx xxxx xxx0 xxxx */
 	/* CMP (register)	cccc 0001 0101 xxxx xxxx xxxx xxx0 xxxx */
 	/* CMN (register)	cccc 0001 0111 xxxx xxxx xxxx xxx0 xxxx */
-	DECODE_EMULATEX	(0x0f900010, 0x01100000, emulate_rd12rn16rm0rs8_rwflags,
+	DECODE_EMULATEX	(0x0f900010, 0x01100000, PROBES_DATA_PROCESSING_REG,
 						 REGS(ANY, 0, 0, 0, ANY)),
 
 	/* MOV (register)	cccc 0001 101x xxxx xxxx xxxx xxx0 xxxx */
 	/* MVN (register)	cccc 0001 111x xxxx xxxx xxxx xxx0 xxxx */
-	DECODE_EMULATEX	(0x0fa00010, 0x01a00000, emulate_rd12rn16rm0rs8_rwflags,
+	DECODE_EMULATEX	(0x0fa00010, 0x01a00000, PROBES_DATA_PROCESSING_REG,
 						 REGS(0, ANY, 0, 0, ANY)),
 
 	/* AND (register)	cccc 0000 000x xxxx xxxx xxxx xxx0 xxxx */
@@ -319,19 +331,19 @@ static const union decode_item arm_cccc_000x_table[] = {
 	/* RSC (register)	cccc 0000 111x xxxx xxxx xxxx xxx0 xxxx */
 	/* ORR (register)	cccc 0001 100x xxxx xxxx xxxx xxx0 xxxx */
 	/* BIC (register)	cccc 0001 110x xxxx xxxx xxxx xxx0 xxxx */
-	DECODE_EMULATEX	(0x0e000010, 0x00000000, emulate_rd12rn16rm0rs8_rwflags,
+	DECODE_EMULATEX	(0x0e000010, 0x00000000, PROBES_DATA_PROCESSING_REG,
 						 REGS(ANY, ANY, 0, 0, ANY)),
 
 	/* TST (reg-shift reg)	cccc 0001 0001 xxxx xxxx xxxx 0xx1 xxxx */
 	/* TEQ (reg-shift reg)	cccc 0001 0011 xxxx xxxx xxxx 0xx1 xxxx */
 	/* CMP (reg-shift reg)	cccc 0001 0101 xxxx xxxx xxxx 0xx1 xxxx */
 	/* CMN (reg-shift reg)	cccc 0001 0111 xxxx xxxx xxxx 0xx1 xxxx */
-	DECODE_EMULATEX	(0x0f900090, 0x01100010, emulate_rd12rn16rm0rs8_rwflags,
+	DECODE_EMULATEX	(0x0f900090, 0x01100010, PROBES_DATA_PROCESSING_REG,
 						 REGS(ANY, 0, NOPC, 0, ANY)),
 
 	/* MOV (reg-shift reg)	cccc 0001 101x xxxx xxxx xxxx 0xx1 xxxx */
 	/* MVN (reg-shift reg)	cccc 0001 111x xxxx xxxx xxxx 0xx1 xxxx */
-	DECODE_EMULATEX	(0x0fa00090, 0x01a00010, emulate_rd12rn16rm0rs8_rwflags,
+	DECODE_EMULATEX	(0x0fa00090, 0x01a00010, PROBES_DATA_PROCESSING_REG,
 						 REGS(0, ANY, NOPC, 0, ANY)),
 
 	/* AND (reg-shift reg)	cccc 0000 000x xxxx xxxx xxxx 0xx1 xxxx */
@@ -344,7 +356,7 @@ static const union decode_item arm_cccc_000x_table[] = {
 	/* RSC (reg-shift reg)	cccc 0000 111x xxxx xxxx xxxx 0xx1 xxxx */
 	/* ORR (reg-shift reg)	cccc 0001 100x xxxx xxxx xxxx 0xx1 xxxx */
 	/* BIC (reg-shift reg)	cccc 0001 110x xxxx xxxx xxxx 0xx1 xxxx */
-	DECODE_EMULATEX	(0x0e000090, 0x00000010, emulate_rd12rn16rm0rs8_rwflags,
+	DECODE_EMULATEX	(0x0e000090, 0x00000010, PROBES_DATA_PROCESSING_REG,
 						 REGS(ANY, ANY, NOPC, 0, ANY)),
 
 	DECODE_END
@@ -355,17 +367,17 @@ static const union decode_item arm_cccc_001x_table[] = {
 
 	/* MOVW			cccc 0011 0000 xxxx xxxx xxxx xxxx xxxx */
 	/* MOVT			cccc 0011 0100 xxxx xxxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0x0fb00000, 0x03000000, emulate_rd12rm0_noflags_nopc,
+	DECODE_EMULATEX	(0x0fb00000, 0x03000000, PROBES_DATA_PROCESSING_IMM,
 						 REGS(0, NOPC, 0, 0, 0)),
 
 	/* YIELD		cccc 0011 0010 0000 xxxx xxxx 0000 0001 */
 	DECODE_OR	(0x0fff00ff, 0x03200001),
 	/* SEV			cccc 0011 0010 0000 xxxx xxxx 0000 0100 */
-	DECODE_EMULATE	(0x0fff00ff, 0x03200004, probes_emulate_none),
+	DECODE_EMULATE	(0x0fff00ff, 0x03200004, PROBES_EMULATE_NONE),
 	/* NOP			cccc 0011 0010 0000 xxxx xxxx 0000 0000 */
 	/* WFE			cccc 0011 0010 0000 xxxx xxxx 0000 0010 */
 	/* WFI			cccc 0011 0010 0000 xxxx xxxx 0000 0011 */
-	DECODE_SIMULATE	(0x0fff00fc, 0x03200000, probes_simulate_nop),
+	DECODE_SIMULATE	(0x0fff00fc, 0x03200000, PROBES_SIMULATE_NOP),
 	/* DBG			cccc 0011 0010 0000 xxxx xxxx ffff xxxx */
 	/* unallocated hints	cccc 0011 0010 0000 xxxx xxxx xxxx xxxx */
 	/* MSR (immediate)	cccc 0011 0x10 xxxx xxxx xxxx xxxx xxxx */
@@ -378,12 +390,12 @@ static const union decode_item arm_cccc_001x_table[] = {
 	/* TEQ (immediate)	cccc 0011 0011 xxxx xxxx xxxx xxxx xxxx */
 	/* CMP (immediate)	cccc 0011 0101 xxxx xxxx xxxx xxxx xxxx */
 	/* CMN (immediate)	cccc 0011 0111 xxxx xxxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0x0f900000, 0x03100000, emulate_rd12rn16rm0rs8_rwflags,
+	DECODE_EMULATEX	(0x0f900000, 0x03100000, PROBES_DATA_PROCESSING_IMM,
 						 REGS(ANY, 0, 0, 0, 0)),
 
 	/* MOV (immediate)	cccc 0011 101x xxxx xxxx xxxx xxxx xxxx */
 	/* MVN (immediate)	cccc 0011 111x xxxx xxxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0x0fa00000, 0x03a00000, emulate_rd12rn16rm0rs8_rwflags,
+	DECODE_EMULATEX	(0x0fa00000, 0x03a00000, PROBES_DATA_PROCESSING_IMM,
 						 REGS(0, ANY, 0, 0, 0)),
 
 	/* AND (immediate)	cccc 0010 000x xxxx xxxx xxxx xxxx xxxx */
@@ -396,7 +408,7 @@ static const union decode_item arm_cccc_001x_table[] = {
 	/* RSC (immediate)	cccc 0010 111x xxxx xxxx xxxx xxxx xxxx */
 	/* ORR (immediate)	cccc 0011 100x xxxx xxxx xxxx xxxx xxxx */
 	/* BIC (immediate)	cccc 0011 110x xxxx xxxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0x0e000000, 0x02000000, emulate_rd12rn16rm0rs8_rwflags,
+	DECODE_EMULATEX	(0x0e000000, 0x02000000, PROBES_DATA_PROCESSING_IMM,
 						 REGS(ANY, ANY, 0, 0, 0)),
 
 	DECODE_END
@@ -406,7 +418,7 @@ static const union decode_item arm_cccc_0110_____xxx1_table[] = {
 	/* Media instructions						*/
 
 	/* SEL			cccc 0110 1000 xxxx xxxx xxxx 1011 xxxx */
-	DECODE_EMULATEX	(0x0ff000f0, 0x068000b0, emulate_rd12rn16rm0_rwflags_nopc,
+	DECODE_EMULATEX	(0x0ff000f0, 0x068000b0, PROBES_SATURATE,
 						 REGS(NOPC, NOPC, 0, 0, NOPC)),
 
 	/* SSAT			cccc 0110 101x xxxx xxxx xxxx xx01 xxxx */
@@ -414,14 +426,14 @@ static const union decode_item arm_cccc_0110_____xxx1_table[] = {
 	DECODE_OR(0x0fa00030, 0x06a00010),
 	/* SSAT16		cccc 0110 1010 xxxx xxxx xxxx 0011 xxxx */
 	/* USAT16		cccc 0110 1110 xxxx xxxx xxxx 0011 xxxx */
-	DECODE_EMULATEX	(0x0fb000f0, 0x06a00030, emulate_rd12rn16rm0_rwflags_nopc,
+	DECODE_EMULATEX	(0x0fb000f0, 0x06a00030, PROBES_SATURATE,
 						 REGS(0, NOPC, 0, 0, NOPC)),
 
 	/* REV			cccc 0110 1011 xxxx xxxx xxxx 0011 xxxx */
 	/* REV16		cccc 0110 1011 xxxx xxxx xxxx 1011 xxxx */
 	/* RBIT			cccc 0110 1111 xxxx xxxx xxxx 0011 xxxx */
 	/* REVSH		cccc 0110 1111 xxxx xxxx xxxx 1011 xxxx */
-	DECODE_EMULATEX	(0x0fb00070, 0x06b00030, emulate_rd12rm0_noflags_nopc,
+	DECODE_EMULATEX	(0x0fb00070, 0x06b00030, PROBES_REV,
 						 REGS(0, NOPC, 0, 0, NOPC)),
 
 	/* ???			cccc 0110 0x00 xxxx xxxx xxxx xxx1 xxxx */
@@ -466,12 +478,12 @@ static const union decode_item arm_cccc_0110_____xxx1_table[] = {
 	/* 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 */
-	DECODE_EMULATEX	(0x0f800010, 0x06000010, emulate_rd12rn16rm0_rwflags_nopc,
+	DECODE_EMULATEX	(0x0f800010, 0x06000010, PROBES_MMI,
 						 REGS(NOPC, NOPC, 0, 0, NOPC)),
 
 	/* PKHBT		cccc 0110 1000 xxxx xxxx xxxx x001 xxxx */
 	/* PKHTB		cccc 0110 1000 xxxx xxxx xxxx x101 xxxx */
-	DECODE_EMULATEX	(0x0ff00030, 0x06800010, emulate_rd12rn16rm0_rwflags_nopc,
+	DECODE_EMULATEX	(0x0ff00030, 0x06800010, PROBES_PACK,
 						 REGS(NOPC, NOPC, 0, 0, NOPC)),
 
 	/* ???			cccc 0110 1001 xxxx xxxx xxxx 0111 xxxx */
@@ -484,7 +496,7 @@ static const union decode_item arm_cccc_0110_____xxx1_table[] = {
 	/* UXTB16		cccc 0110 1100 1111 xxxx xxxx 0111 xxxx */
 	/* UXTB			cccc 0110 1110 1111 xxxx xxxx 0111 xxxx */
 	/* UXTH			cccc 0110 1111 1111 xxxx xxxx 0111 xxxx */
-	DECODE_EMULATEX	(0x0f8f00f0, 0x068f0070, emulate_rd12rm0_noflags_nopc,
+	DECODE_EMULATEX	(0x0f8f00f0, 0x068f0070, PROBES_EXTEND,
 						 REGS(0, NOPC, 0, 0, NOPC)),
 
 	/* SXTAB16		cccc 0110 1000 xxxx xxxx xxxx 0111 xxxx */
@@ -493,7 +505,7 @@ static const union decode_item arm_cccc_0110_____xxx1_table[] = {
 	/* 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 */
-	DECODE_EMULATEX	(0x0f8000f0, 0x06800070, emulate_rd12rn16rm0_rwflags_nopc,
+	DECODE_EMULATEX	(0x0f8000f0, 0x06800070, PROBES_EXTEND_ADD,
 						 REGS(NOPCX, NOPC, 0, 0, NOPC)),
 
 	DECODE_END
@@ -507,7 +519,7 @@ static const union decode_item arm_cccc_0111_____xxx1_table[] = {
 
 	/* SMLALD		cccc 0111 0100 xxxx xxxx xxxx 00x1 xxxx */
 	/* SMLSLD		cccc 0111 0100 xxxx xxxx xxxx 01x1 xxxx */
-	DECODE_EMULATEX	(0x0ff00090, 0x07400010, emulate_rdlo12rdhi16rn0rm8_rwflags_nopc,
+	DECODE_EMULATEX	(0x0ff00090, 0x07400010, PROBES_MUL_ADD_LONG,
 						 REGS(NOPC, NOPC, NOPC, 0, NOPC)),
 
 	/* SMUAD		cccc 0111 0000 xxxx 1111 xxxx 00x1 xxxx */
@@ -516,7 +528,7 @@ static const union decode_item arm_cccc_0111_____xxx1_table[] = {
 	/* SMMUL		cccc 0111 0101 xxxx 1111 xxxx 00x1 xxxx */
 	DECODE_OR	(0x0ff0f0d0, 0x0750f010),
 	/* USAD8		cccc 0111 1000 xxxx 1111 xxxx 0001 xxxx */
-	DECODE_EMULATEX	(0x0ff0f0f0, 0x0780f010, emulate_rd16rn12rm0rs8_rwflags_nopc,
+	DECODE_EMULATEX	(0x0ff0f0f0, 0x0780f010, PROBES_MUL_ADD,
 						 REGS(NOPC, 0, NOPC, 0, NOPC)),
 
 	/* SMLAD		cccc 0111 0000 xxxx xxxx xxxx 00x1 xxxx */
@@ -525,24 +537,24 @@ static const union decode_item arm_cccc_0111_____xxx1_table[] = {
 	/* SMMLA		cccc 0111 0101 xxxx xxxx xxxx 00x1 xxxx */
 	DECODE_OR	(0x0ff000d0, 0x07500010),
 	/* USADA8		cccc 0111 1000 xxxx xxxx xxxx 0001 xxxx */
-	DECODE_EMULATEX	(0x0ff000f0, 0x07800010, emulate_rd16rn12rm0rs8_rwflags_nopc,
+	DECODE_EMULATEX	(0x0ff000f0, 0x07800010, PROBES_MUL_ADD,
 						 REGS(NOPC, NOPCX, NOPC, 0, NOPC)),
 
 	/* SMMLS		cccc 0111 0101 xxxx xxxx xxxx 11x1 xxxx */
-	DECODE_EMULATEX	(0x0ff000d0, 0x075000d0, emulate_rd16rn12rm0rs8_rwflags_nopc,
+	DECODE_EMULATEX	(0x0ff000d0, 0x075000d0, PROBES_MUL_ADD,
 						 REGS(NOPC, NOPC, NOPC, 0, NOPC)),
 
 	/* SBFX			cccc 0111 101x xxxx xxxx xxxx x101 xxxx */
 	/* UBFX			cccc 0111 111x xxxx xxxx xxxx x101 xxxx */
-	DECODE_EMULATEX	(0x0fa00070, 0x07a00050, emulate_rd12rm0_noflags_nopc,
+	DECODE_EMULATEX	(0x0fa00070, 0x07a00050, PROBES_BITFIELD,
 						 REGS(0, NOPC, 0, 0, NOPC)),
 
 	/* BFC			cccc 0111 110x xxxx xxxx xxxx x001 1111 */
-	DECODE_EMULATEX	(0x0fe0007f, 0x07c0001f, emulate_rd12rm0_noflags_nopc,
+	DECODE_EMULATEX	(0x0fe0007f, 0x07c0001f, PROBES_BITFIELD,
 						 REGS(0, NOPC, 0, 0, 0)),
 
 	/* BFI			cccc 0111 110x xxxx xxxx xxxx x001 xxxx */
-	DECODE_EMULATEX	(0x0fe00070, 0x07c00010, emulate_rd12rm0_noflags_nopc,
+	DECODE_EMULATEX	(0x0fe00070, 0x07c00010, PROBES_BITFIELD,
 						 REGS(0, NOPC, 0, 0, NOPCX)),
 
 	DECODE_END
@@ -562,22 +574,22 @@ static const union decode_item arm_cccc_01xx_table[] = {
 
 	/* STR (immediate)	cccc 010x x0x0 xxxx xxxx xxxx xxxx xxxx */
 	/* STRB (immediate)	cccc 010x x1x0 xxxx xxxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0x0e100000, 0x04000000, emulate_str,
+	DECODE_EMULATEX	(0x0e100000, 0x04000000, PROBES_STORE,
 						 REGS(NOPCWB, ANY, 0, 0, 0)),
 
 	/* LDR (immediate)	cccc 010x x0x1 xxxx xxxx xxxx xxxx xxxx */
 	/* LDRB (immediate)	cccc 010x x1x1 xxxx xxxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0x0e100000, 0x04100000, emulate_ldr,
+	DECODE_EMULATEX	(0x0e100000, 0x04100000, PROBES_LOAD,
 						 REGS(NOPCWB, ANY, 0, 0, 0)),
 
 	/* STR (register)	cccc 011x x0x0 xxxx xxxx xxxx xxxx xxxx */
 	/* STRB (register)	cccc 011x x1x0 xxxx xxxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0x0e100000, 0x06000000, emulate_str,
+	DECODE_EMULATEX	(0x0e100000, 0x06000000, PROBES_STORE,
 						 REGS(NOPCWB, ANY, 0, 0, NOPC)),
 
 	/* LDR (register)	cccc 011x x0x1 xxxx xxxx xxxx xxxx xxxx */
 	/* LDRB (register)	cccc 011x x1x1 xxxx xxxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0x0e100000, 0x06100000, emulate_ldr,
+	DECODE_EMULATEX	(0x0e100000, 0x06100000, PROBES_LOAD,
 						 REGS(NOPCWB, ANY, 0, 0, NOPC)),
 
 	DECODE_END
@@ -588,7 +600,7 @@ static const union decode_item arm_cccc_100x_table[] = {
 
 	/* LDM			cccc 100x x0x1 xxxx xxxx xxxx xxxx xxxx */
 	/* STM			cccc 100x x0x0 xxxx xxxx xxxx xxxx xxxx */
-	DECODE_CUSTOM	(0x0e400000, 0x08000000, kprobe_decode_ldmstm),
+	DECODE_CUSTOM	(0x0e400000, 0x08000000, PROBES_LDMSTM),
 
 	/* STM (user registers)	cccc 100x x1x0 xxxx xxxx xxxx xxxx xxxx */
 	/* LDM (user registers)	cccc 100x x1x1 xxxx 0xxx xxxx xxxx xxxx */
@@ -668,7 +680,7 @@ const union decode_item kprobe_decode_arm_table[] = {
 
 	/* B			cccc 1010 xxxx xxxx xxxx xxxx xxxx xxxx */
 	/* BL			cccc 1011 xxxx xxxx xxxx xxxx xxxx xxxx */
-	DECODE_SIMULATE	(0x0e000000, 0x0a000000, simulate_bbl),
+	DECODE_SIMULATE	(0x0e000000, 0x0a000000, PROBES_BRANCH),
 
 	/*
 	 * Supervisor Call, and coprocessor instructions
@@ -709,9 +721,11 @@ static void __kprobes arm_singlestep(struct kprobe *p, struct pt_regs *regs)
  *   should also be very rare.
  */
 enum kprobe_insn __kprobes
-arm_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+arm_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
+			bool usermode, const union decode_item *actions)
 {
 	asi->insn_singlestep = arm_singlestep;
 	asi->insn_check_cc = kprobe_condition_checks[insn>>28];
-	return kprobe_decode_insn(insn, asi, kprobe_decode_arm_table, false);
+	return probes_decode_insn(insn, asi, kprobe_decode_arm_table, false,
+				  usermode, actions);
 }
diff --git a/arch/arm/kernel/kprobes-common.c b/arch/arm/kernel/kprobes-common.c
index b66e9f7..4e19498 100644
--- a/arch/arm/kernel/kprobes-common.c
+++ b/arch/arm/kernel/kprobes-common.c
@@ -268,7 +268,8 @@ emulate_ldm_r3_15(struct kprobe *p, struct pt_regs *regs)
 }
 
 enum kprobe_insn __kprobes
-kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi,
+		     void *d)
 {
 	kprobe_insn_handler_t *handler = 0;
 	unsigned reglist = insn & 0xffff;
diff --git a/arch/arm/kernel/kprobes-thumb.c b/arch/arm/kernel/kprobes-thumb.c
index 173b2bc..f5d57a8 100644
--- a/arch/arm/kernel/kprobes-thumb.c
+++ b/arch/arm/kernel/kprobes-thumb.c
@@ -13,6 +13,8 @@
 #include <linux/module.h>
 
 #include "kprobes.h"
+#include "probes.h"
+#include "probes-thumb.h"
 
 
 /*
@@ -83,7 +85,8 @@ t32_simulate_cond_branch(struct kprobe *p, struct pt_regs *regs)
 }
 
 static enum kprobe_insn __kprobes
-t32_decode_cond_branch(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+t32_decode_cond_branch(kprobe_opcode_t insn, struct arch_specific_insn *asi,
+		       void *d)
 {
 	int cc = (insn >> 22) & 0xf;
 	asi->insn_check_cc = kprobe_condition_checks[cc];
@@ -158,9 +161,10 @@ t32_simulate_ldr_literal(struct kprobe *p, struct pt_regs *regs)
 }
 
 static enum kprobe_insn __kprobes
-t32_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+t32_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi,
+		  void *d)
 {
-	enum kprobe_insn ret = kprobe_decode_ldmstm(insn, asi);
+	enum kprobe_insn ret = kprobe_decode_ldmstm(insn, asi, d);
 
 	/* Fixup modified instruction to have halfwords in correct order...*/
 	insn = asi->insn[0];
@@ -344,7 +348,7 @@ static const union decode_item t32_table_1110_100x_x0xx[] = {
 	/* LDMIA		1110 1000 10x1 xxxx xxxx xxxx xxxx xxxx */
 	/* STMDB		1110 1001 00x0 xxxx xxxx xxxx xxxx xxxx */
 	/* LDMDB		1110 1001 00x1 xxxx xxxx xxxx xxxx xxxx */
-	DECODE_CUSTOM	(0xfe400000, 0xe8000000, t32_decode_ldmstm),
+	DECODE_CUSTOM	(0xfe400000, 0xe8000000, PROBES_T32_LDMSTM),
 
 	DECODE_END
 };
@@ -357,12 +361,12 @@ static const union decode_item t32_table_1110_100x_x1xx[] = {
 	DECODE_OR	(0xff600000, 0xe8600000),
 	/* STRD (immediate)	1110 1001 x1x0 xxxx xxxx xxxx xxxx xxxx */
 	/* LDRD (immediate)	1110 1001 x1x1 xxxx xxxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0xff400000, 0xe9400000, t32_emulate_ldrdstrd,
+	DECODE_EMULATEX	(0xff400000, 0xe9400000, PROBES_T32_LDRDSTRD,
 						 REGS(NOPCWB, NOSPPC, NOSPPC, 0, 0)),
 
 	/* TBB			1110 1000 1101 xxxx xxxx xxxx 0000 xxxx */
 	/* TBH			1110 1000 1101 xxxx xxxx xxxx 0001 xxxx */
-	DECODE_SIMULATEX(0xfff000e0, 0xe8d00000, t32_simulate_table_branch,
+	DECODE_SIMULATEX(0xfff000e0, 0xe8d00000, PROBES_T32_TABLE_BRANCH,
 						 REGS(NOSP, 0, 0, 0, NOSPPC)),
 
 	/* STREX		1110 1000 0100 xxxx xxxx xxxx xxxx xxxx */
@@ -382,18 +386,18 @@ static const union decode_item t32_table_1110_101x[] = {
 
 	/* TST			1110 1010 0001 xxxx xxxx 1111 xxxx xxxx */
 	/* TEQ			1110 1010 1001 xxxx xxxx 1111 xxxx xxxx */
-	DECODE_EMULATEX	(0xff700f00, 0xea100f00, t32_emulate_rd8rn16rm0_rwflags,
+	DECODE_EMULATEX	(0xff700f00, 0xea100f00, PROBES_T32_TST,
 						 REGS(NOSPPC, 0, 0, 0, NOSPPC)),
 
 	/* CMN			1110 1011 0001 xxxx xxxx 1111 xxxx xxxx */
 	DECODE_OR	(0xfff00f00, 0xeb100f00),
 	/* CMP			1110 1011 1011 xxxx xxxx 1111 xxxx xxxx */
-	DECODE_EMULATEX	(0xfff00f00, 0xebb00f00, t32_emulate_rd8rn16rm0_rwflags,
+	DECODE_EMULATEX	(0xfff00f00, 0xebb00f00, PROBES_T32_TST,
 						 REGS(NOPC, 0, 0, 0, NOSPPC)),
 
 	/* MOV			1110 1010 010x 1111 xxxx xxxx xxxx xxxx */
 	/* MVN			1110 1010 011x 1111 xxxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0xffcf0000, 0xea4f0000, t32_emulate_rd8rn16rm0_rwflags,
+	DECODE_EMULATEX	(0xffcf0000, 0xea4f0000, PROBES_T32_MOV,
 						 REGS(0, 0, NOSPPC, 0, NOSPPC)),
 
 	/* ???			1110 1010 101x xxxx xxxx xxxx xxxx xxxx */
@@ -408,7 +412,7 @@ static const union decode_item t32_table_1110_101x[] = {
 
 	/* ADD/SUB SP, SP, Rm, LSL #0..3				*/
 	/*			1110 1011 x0xx 1101 x000 1101 xx00 xxxx */
-	DECODE_EMULATEX	(0xff4f7f30, 0xeb0d0d00, t32_emulate_rd8rn16rm0_rwflags,
+	DECODE_EMULATEX	(0xff4f7f30, 0xeb0d0d00, PROBES_T32_ADDSUB,
 						 REGS(SP, 0, SP, 0, NOSPPC)),
 
 	/* ADD/SUB SP, SP, Rm, shift					*/
@@ -417,7 +421,7 @@ static const union decode_item t32_table_1110_101x[] = {
 
 	/* ADD/SUB Rd, SP, Rm, shift					*/
 	/*			1110 1011 x0xx 1101 xxxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0xff4f0000, 0xeb0d0000, t32_emulate_rd8rn16rm0_rwflags,
+	DECODE_EMULATEX	(0xff4f0000, 0xeb0d0000, PROBES_T32_ADDSUB,
 						 REGS(SP, 0, NOPC, 0, NOSPPC)),
 
 	/* AND			1110 1010 000x xxxx xxxx xxxx xxxx xxxx */
@@ -431,7 +435,7 @@ static const union decode_item t32_table_1110_101x[] = {
 	/* SBC			1110 1011 011x xxxx xxxx xxxx xxxx xxxx */
 	/* SUB			1110 1011 101x xxxx xxxx xxxx xxxx xxxx */
 	/* RSB			1110 1011 110x xxxx xxxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0xfe000000, 0xea000000, t32_emulate_rd8rn16rm0_rwflags,
+	DECODE_EMULATEX	(0xfe000000, 0xea000000, PROBES_T32_LOGICAL,
 						 REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)),
 
 	DECODE_END
@@ -442,18 +446,18 @@ static const union decode_item t32_table_1111_0x0x___0[] = {
 
 	/* TST			1111 0x00 0001 xxxx 0xxx 1111 xxxx xxxx */
 	/* TEQ			1111 0x00 1001 xxxx 0xxx 1111 xxxx xxxx */
-	DECODE_EMULATEX	(0xfb708f00, 0xf0100f00, t32_emulate_rd8rn16rm0_rwflags,
+	DECODE_EMULATEX	(0xfb708f00, 0xf0100f00, PROBES_T32_TST,
 						 REGS(NOSPPC, 0, 0, 0, 0)),
 
 	/* CMN			1111 0x01 0001 xxxx 0xxx 1111 xxxx xxxx */
 	DECODE_OR	(0xfbf08f00, 0xf1100f00),
 	/* CMP			1111 0x01 1011 xxxx 0xxx 1111 xxxx xxxx */
-	DECODE_EMULATEX	(0xfbf08f00, 0xf1b00f00, t32_emulate_rd8rn16rm0_rwflags,
+	DECODE_EMULATEX	(0xfbf08f00, 0xf1b00f00, PROBES_T32_CMP,
 						 REGS(NOPC, 0, 0, 0, 0)),
 
 	/* MOV			1111 0x00 010x 1111 0xxx xxxx xxxx xxxx */
 	/* MVN			1111 0x00 011x 1111 0xxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0xfbcf8000, 0xf04f0000, t32_emulate_rd8rn16rm0_rwflags,
+	DECODE_EMULATEX	(0xfbcf8000, 0xf04f0000, PROBES_T32_MOV,
 						 REGS(0, 0, NOSPPC, 0, 0)),
 
 	/* ???			1111 0x00 101x xxxx 0xxx xxxx xxxx xxxx */
@@ -470,7 +474,7 @@ static const union decode_item t32_table_1111_0x0x___0[] = {
 
 	/* ADD Rd, SP, #imm	1111 0x01 000x 1101 0xxx xxxx xxxx xxxx */
 	/* SUB Rd, SP, #imm	1111 0x01 101x 1101 0xxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0xfb4f8000, 0xf10d0000, t32_emulate_rd8rn16rm0_rwflags,
+	DECODE_EMULATEX	(0xfb4f8000, 0xf10d0000, PROBES_T32_ADDSUB,
 						 REGS(SP, 0, NOPC, 0, 0)),
 
 	/* AND			1111 0x00 000x xxxx 0xxx xxxx xxxx xxxx */
@@ -483,7 +487,7 @@ static const union decode_item t32_table_1111_0x0x___0[] = {
 	/* SBC			1111 0x01 011x xxxx 0xxx xxxx xxxx xxxx */
 	/* SUB			1111 0x01 101x xxxx 0xxx xxxx xxxx xxxx */
 	/* RSB			1111 0x01 110x xxxx 0xxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0xfa008000, 0xf0000000, t32_emulate_rd8rn16rm0_rwflags,
+	DECODE_EMULATEX	(0xfa008000, 0xf0000000, PROBES_T32_LOGICAL,
 						 REGS(NOSPPC, 0, NOSPPC, 0, 0)),
 
 	DECODE_END
@@ -495,44 +499,44 @@ static const union decode_item t32_table_1111_0x1x___0[] = {
 	/* ADDW Rd, PC, #imm	1111 0x10 0000 1111 0xxx xxxx xxxx xxxx */
 	DECODE_OR	(0xfbff8000, 0xf20f0000),
 	/* SUBW	Rd, PC, #imm	1111 0x10 1010 1111 0xxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0xfbff8000, 0xf2af0000, t32_emulate_rd8pc16_noflags,
+	DECODE_EMULATEX	(0xfbff8000, 0xf2af0000, PROBES_T32_ADDWSUBW_PC,
 						 REGS(PC, 0, NOSPPC, 0, 0)),
 
 	/* ADDW SP, SP, #imm	1111 0x10 0000 1101 0xxx 1101 xxxx xxxx */
 	DECODE_OR	(0xfbff8f00, 0xf20d0d00),
 	/* SUBW	SP, SP, #imm	1111 0x10 1010 1101 0xxx 1101 xxxx xxxx */
-	DECODE_EMULATEX	(0xfbff8f00, 0xf2ad0d00, t32_emulate_rd8rn16_noflags,
+	DECODE_EMULATEX	(0xfbff8f00, 0xf2ad0d00, PROBES_T32_ADDWSUBW,
 						 REGS(SP, 0, SP, 0, 0)),
 
 	/* ADDW			1111 0x10 0000 xxxx 0xxx xxxx xxxx xxxx */
 	DECODE_OR	(0xfbf08000, 0xf2000000),
 	/* SUBW			1111 0x10 1010 xxxx 0xxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0xfbf08000, 0xf2a00000, t32_emulate_rd8rn16_noflags,
+	DECODE_EMULATEX	(0xfbf08000, 0xf2a00000, PROBES_T32_ADDWSUBW,
 						 REGS(NOPCX, 0, NOSPPC, 0, 0)),
 
 	/* MOVW			1111 0x10 0100 xxxx 0xxx xxxx xxxx xxxx */
 	/* MOVT			1111 0x10 1100 xxxx 0xxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0xfb708000, 0xf2400000, t32_emulate_rd8rn16_noflags,
+	DECODE_EMULATEX	(0xfb708000, 0xf2400000, PROBES_T32_MOVW,
 						 REGS(0, 0, NOSPPC, 0, 0)),
 
 	/* SSAT16		1111 0x11 0010 xxxx 0000 xxxx 00xx xxxx */
 	/* SSAT			1111 0x11 00x0 xxxx 0xxx xxxx xxxx xxxx */
 	/* USAT16		1111 0x11 1010 xxxx 0000 xxxx 00xx xxxx */
 	/* USAT			1111 0x11 10x0 xxxx 0xxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0xfb508000, 0xf3000000, t32_emulate_rd8rn16rm0_rwflags,
+	DECODE_EMULATEX	(0xfb508000, 0xf3000000, PROBES_T32_SAT,
 						 REGS(NOSPPC, 0, NOSPPC, 0, 0)),
 
 	/* SFBX			1111 0x11 0100 xxxx 0xxx xxxx xxxx xxxx */
 	/* UFBX			1111 0x11 1100 xxxx 0xxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0xfb708000, 0xf3400000, t32_emulate_rd8rn16_noflags,
+	DECODE_EMULATEX	(0xfb708000, 0xf3400000, PROBES_T32_BITFIELD,
 						 REGS(NOSPPC, 0, NOSPPC, 0, 0)),
 
 	/* BFC			1111 0x11 0110 1111 0xxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0xfbff8000, 0xf36f0000, t32_emulate_rd8rn16_noflags,
+	DECODE_EMULATEX	(0xfbff8000, 0xf36f0000, PROBES_T32_BITFIELD,
 						 REGS(0, 0, NOSPPC, 0, 0)),
 
 	/* BFI			1111 0x11 0110 xxxx 0xxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0xfbf08000, 0xf3600000, t32_emulate_rd8rn16_noflags,
+	DECODE_EMULATEX	(0xfbf08000, 0xf3600000, PROBES_T32_BITFIELD,
 						 REGS(NOSPPCX, 0, NOSPPC, 0, 0)),
 
 	DECODE_END
@@ -544,14 +548,14 @@ static const union decode_item t32_table_1111_0xxx___1[] = {
 	/* YIELD		1111 0011 1010 xxxx 10x0 x000 0000 0001 */
 	DECODE_OR	(0xfff0d7ff, 0xf3a08001),
 	/* SEV			1111 0011 1010 xxxx 10x0 x000 0000 0100 */
-	DECODE_EMULATE	(0xfff0d7ff, 0xf3a08004, probes_emulate_none),
+	DECODE_EMULATE	(0xfff0d7ff, 0xf3a08004, PROBES_T32_SEV),
 	/* NOP			1111 0011 1010 xxxx 10x0 x000 0000 0000 */
 	/* WFE			1111 0011 1010 xxxx 10x0 x000 0000 0010 */
 	/* WFI			1111 0011 1010 xxxx 10x0 x000 0000 0011 */
-	DECODE_SIMULATE	(0xfff0d7fc, 0xf3a08000, probes_simulate_nop),
+	DECODE_SIMULATE	(0xfff0d7fc, 0xf3a08000, PROBES_T32_WFE),
 
 	/* MRS Rd, CPSR		1111 0011 1110 xxxx 10x0 xxxx xxxx xxxx */
-	DECODE_SIMULATEX(0xfff0d000, 0xf3e08000, t32_simulate_mrs,
+	DECODE_SIMULATEX(0xfff0d000, 0xf3e08000, PROBES_T32_MRS,
 						 REGS(0, 0, NOSPPC, 0, 0)),
 
 	/*
@@ -573,13 +577,13 @@ static const union decode_item t32_table_1111_0xxx___1[] = {
 	DECODE_REJECT	(0xfb80d000, 0xf3808000),
 
 	/* Bcc			1111 0xxx xxxx xxxx 10x0 xxxx xxxx xxxx */
-	DECODE_CUSTOM	(0xf800d000, 0xf0008000, t32_decode_cond_branch),
+	DECODE_CUSTOM	(0xf800d000, 0xf0008000, PROBES_T32_BRANCH_COND),
 
 	/* BLX			1111 0xxx xxxx xxxx 11x0 xxxx xxxx xxx0 */
 	DECODE_OR	(0xf800d001, 0xf000c000),
 	/* B			1111 0xxx xxxx xxxx 10x1 xxxx xxxx xxxx */
 	/* BL			1111 0xxx xxxx xxxx 11x1 xxxx xxxx xxxx */
-	DECODE_SIMULATE	(0xf8009000, 0xf0009000, t32_simulate_branch),
+	DECODE_SIMULATE	(0xf8009000, 0xf0009000, PROBES_T32_BRANCH),
 
 	DECODE_END
 };
@@ -589,7 +593,7 @@ static const union decode_item t32_table_1111_100x_x0x1__1111[] = {
 
 	/* PLD (literal)	1111 1000 x001 1111 1111 xxxx xxxx xxxx */
 	/* PLI (literal)	1111 1001 x001 1111 1111 xxxx xxxx xxxx */
-	DECODE_SIMULATE	(0xfe7ff000, 0xf81ff000, probes_simulate_nop),
+	DECODE_SIMULATE	(0xfe7ff000, 0xf81ff000, PROBES_T32_PLDI),
 
 	/* PLD{W} (immediate)	1111 1000 10x1 xxxx 1111 xxxx xxxx xxxx */
 	DECODE_OR	(0xffd0f000, 0xf890f000),
@@ -598,13 +602,13 @@ static const union decode_item t32_table_1111_100x_x0x1__1111[] = {
 	/* PLI (immediate)	1111 1001 1001 xxxx 1111 xxxx xxxx xxxx */
 	DECODE_OR	(0xfff0f000, 0xf990f000),
 	/* PLI (immediate)	1111 1001 0001 xxxx 1111 1100 xxxx xxxx */
-	DECODE_SIMULATEX(0xfff0ff00, 0xf910fc00, probes_simulate_nop,
+	DECODE_SIMULATEX(0xfff0ff00, 0xf910fc00, PROBES_T32_PLDI,
 						 REGS(NOPCX, 0, 0, 0, 0)),
 
 	/* PLD{W} (register)	1111 1000 00x1 xxxx 1111 0000 00xx xxxx */
 	DECODE_OR	(0xffd0ffc0, 0xf810f000),
 	/* PLI (register)	1111 1001 0001 xxxx 1111 0000 00xx xxxx */
-	DECODE_SIMULATEX(0xfff0ffc0, 0xf910f000, probes_simulate_nop,
+	DECODE_SIMULATEX(0xfff0ffc0, 0xf910f000, PROBES_T32_PLDI,
 						 REGS(NOPCX, 0, 0, 0, NOSPPC)),
 
 	/* Other unallocated instructions...				*/
@@ -640,7 +644,7 @@ static const union decode_item t32_table_1111_100x[] = {
 	DECODE_REJECT	(0xff10f000, 0xf800f000),
 
 	/* LDR (literal)	1111 1000 x101 1111 xxxx xxxx xxxx xxxx */
-	DECODE_SIMULATEX(0xff7f0000, 0xf85f0000, t32_simulate_ldr_literal,
+	DECODE_SIMULATEX(0xff7f0000, 0xf85f0000, PROBES_T32_LDR_LIT,
 						 REGS(PC, ANY, 0, 0, 0)),
 
 	/* STR (immediate)	1111 1000 0100 xxxx xxxx 1xxx xxxx xxxx */
@@ -648,19 +652,19 @@ static const union decode_item t32_table_1111_100x[] = {
 	DECODE_OR	(0xffe00800, 0xf8400800),
 	/* STR (immediate)	1111 1000 1100 xxxx xxxx xxxx xxxx xxxx */
 	/* LDR (immediate)	1111 1000 1101 xxxx xxxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0xffe00000, 0xf8c00000, t32_emulate_ldrstr,
+	DECODE_EMULATEX	(0xffe00000, 0xf8c00000, PROBES_T32_LDRSTR,
 						 REGS(NOPCX, ANY, 0, 0, 0)),
 
 	/* STR (register)	1111 1000 0100 xxxx xxxx 0000 00xx xxxx */
 	/* LDR (register)	1111 1000 0101 xxxx xxxx 0000 00xx xxxx */
-	DECODE_EMULATEX	(0xffe00fc0, 0xf8400000, t32_emulate_ldrstr,
+	DECODE_EMULATEX	(0xffe00fc0, 0xf8400000, PROBES_T32_LDRSTR,
 						 REGS(NOPCX, ANY, 0, 0, NOSPPC)),
 
 	/* LDRB (literal)	1111 1000 x001 1111 xxxx xxxx xxxx xxxx */
 	/* LDRSB (literal)	1111 1001 x001 1111 xxxx xxxx xxxx xxxx */
 	/* LDRH (literal)	1111 1000 x011 1111 xxxx xxxx xxxx xxxx */
 	/* LDRSH (literal)	1111 1001 x011 1111 xxxx xxxx xxxx xxxx */
-	DECODE_SIMULATEX(0xfe5f0000, 0xf81f0000, t32_simulate_ldr_literal,
+	DECODE_SIMULATEX(0xfe5f0000, 0xf81f0000, PROBES_T32_LDR_LIT,
 						 REGS(PC, NOSPPCX, 0, 0, 0)),
 
 	/* STRB (immediate)	1111 1000 0000 xxxx xxxx 1xxx xxxx xxxx */
@@ -676,7 +680,7 @@ static const union decode_item t32_table_1111_100x[] = {
 	/* LDRSB (immediate)	1111 1001 1001 xxxx xxxx xxxx xxxx xxxx */
 	/* LDRH (immediate)	1111 1000 1011 xxxx xxxx xxxx xxxx xxxx */
 	/* LDRSH (immediate)	1111 1001 1011 xxxx xxxx xxxx xxxx xxxx */
-	DECODE_EMULATEX	(0xfec00000, 0xf8800000, t32_emulate_ldrstr,
+	DECODE_EMULATEX	(0xfec00000, 0xf8800000, PROBES_T32_LDRSTR,
 						 REGS(NOPCX, NOSPPCX, 0, 0, 0)),
 
 	/* STRB (register)	1111 1000 0000 xxxx xxxx 0000 00xx xxxx */
@@ -685,7 +689,7 @@ static const union decode_item t32_table_1111_100x[] = {
 	/* LDRSB (register)	1111 1001 0001 xxxx xxxx 0000 00xx xxxx */
 	/* LDRH (register)	1111 1000 0011 xxxx xxxx 0000 00xx xxxx */
 	/* LDRSH (register)	1111 1001 0011 xxxx xxxx 0000 00xx xxxx */
-	DECODE_EMULATEX	(0xfe800fc0, 0xf8000000, t32_emulate_ldrstr,
+	DECODE_EMULATEX	(0xfe800fc0, 0xf8000000, PROBES_T32_LDRSTR,
 						 REGS(NOPCX, NOSPPCX, 0, 0, NOSPPC)),
 
 	/* Other unallocated instructions...				*/
@@ -704,7 +708,7 @@ static const union decode_item t32_table_1111_1010___1111[] = {
 	/* UXTB16		1111 1010 0011 1111 1111 xxxx 1xxx xxxx */
 	/* SXTB			1111 1010 0100 1111 1111 xxxx 1xxx xxxx */
 	/* UXTB			1111 1010 0101 1111 1111 xxxx 1xxx xxxx */
-	DECODE_EMULATEX	(0xff8ff080, 0xfa0ff080, t32_emulate_rd8rn16rm0_rwflags,
+	DECODE_EMULATEX	(0xff8ff080, 0xfa0ff080, PROBES_T32_SIGN_EXTEND,
 						 REGS(0, 0, NOSPPC, 0, NOSPPC)),
 
 
@@ -777,7 +781,7 @@ static const union decode_item t32_table_1111_1010___1111[] = {
 	/* LSR			1111 1010 001x xxxx 1111 xxxx 0000 xxxx */
 	/* ASR			1111 1010 010x xxxx 1111 xxxx 0000 xxxx */
 	/* ROR			1111 1010 011x xxxx 1111 xxxx 0000 xxxx */
-	DECODE_EMULATEX	(0xff80f0f0, 0xfa00f000, t32_emulate_rd8rn16rm0_rwflags,
+	DECODE_EMULATEX	(0xff80f0f0, 0xfa00f000, PROBES_T32_MEDIA,
 						 REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)),
 
 	/* CLZ			1111 1010 1010 xxxx 1111 xxxx 1000 xxxx */
@@ -787,7 +791,7 @@ static const union decode_item t32_table_1111_1010___1111[] = {
 	/* REV16		1111 1010 1001 xxxx 1111 xxxx 1001 xxxx */
 	/* RBIT			1111 1010 1001 xxxx 1111 xxxx 1010 xxxx */
 	/* REVSH		1111 1010 1001 xxxx 1111 xxxx 1011 xxxx */
-	DECODE_EMULATEX	(0xfff0f0c0, 0xfa90f080, t32_emulate_rd8rn16_noflags,
+	DECODE_EMULATEX	(0xfff0f0c0, 0xfa90f080, PROBES_T32_REVERSE,
 						 REGS(NOSPPC, 0, NOSPPC, 0, SAMEAS16)),
 
 	/* Other unallocated instructions...				*/
@@ -810,7 +814,7 @@ static const union decode_item t32_table_1111_1011_0[] = {
 	/* SMUSD{X}		1111 1011 0100 xxxx 1111 xxxx 000x xxxx */
 	/* SMMUL{R}		1111 1011 0101 xxxx 1111 xxxx 000x xxxx */
 	/* USAD8		1111 1011 0111 xxxx 1111 xxxx 0000 xxxx */
-	DECODE_EMULATEX	(0xff80f0e0, 0xfb00f000, t32_emulate_rd8rn16rm0_rwflags,
+	DECODE_EMULATEX	(0xff80f0e0, 0xfb00f000, PROBES_T32_MUL_ADD,
 						 REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)),
 
 	/* ???			1111 1011 0111 xxxx xxxx xxxx 0001 xxxx */
@@ -826,7 +830,7 @@ static const union decode_item t32_table_1111_1011_0[] = {
 	/* SMMLA{R}		1111 1011 0101 xxxx xxxx xxxx 000x xxxx */
 	/* SMMLS{R}		1111 1011 0110 xxxx xxxx xxxx 000x xxxx */
 	/* USADA8		1111 1011 0111 xxxx xxxx xxxx 0000 xxxx */
-	DECODE_EMULATEX	(0xff8000c0, 0xfb000000, t32_emulate_rd8rn16rm0ra12_noflags,
+	DECODE_EMULATEX	(0xff8000c0, 0xfb000000,  PROBES_T32_MUL_ADD2,
 						 REGS(NOSPPC, NOSPPCX, NOSPPC, 0, NOSPPC)),
 
 	/* Other unallocated instructions...				*/
@@ -847,7 +851,7 @@ static const union decode_item t32_table_1111_1011_1[] = {
 	/* UMULL		1111 1011 1010 xxxx xxxx xxxx 0000 xxxx */
 	/* SMLAL		1111 1011 1100 xxxx xxxx xxxx 0000 xxxx */
 	/* UMLAL		1111 1011 1110 xxxx xxxx xxxx 0000 xxxx */
-	DECODE_EMULATEX	(0xff9000f0, 0xfb800000, t32_emulate_rdlo12rdhi8rn16rm0_noflags,
+	DECODE_EMULATEX	(0xff9000f0, 0xfb800000, PROBES_T32_MUL_ADD_LONG,
 						 REGS(NOSPPC, NOSPPC, NOSPPC, 0, NOSPPC)),
 
 	/* SDIV			1111 1011 1001 xxxx xxxx xxxx 1111 xxxx */
@@ -1046,7 +1050,7 @@ t16_singlestep_it(struct kprobe *p, struct pt_regs *regs)
 }
 
 static enum kprobe_insn __kprobes
-t16_decode_it(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+t16_decode_it(kprobe_opcode_t insn, struct arch_specific_insn *asi, void *d)
 {
 	asi->insn_singlestep = t16_singlestep_it;
 	return INSN_GOOD_NO_SLOT;
@@ -1063,7 +1067,8 @@ t16_simulate_cond_branch(struct kprobe *p, struct pt_regs *regs)
 }
 
 static enum kprobe_insn __kprobes
-t16_decode_cond_branch(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+t16_decode_cond_branch(kprobe_opcode_t insn, struct arch_specific_insn *asi,
+		       void *d)
 {
 	int cc = (insn >> 8) & 0xf;
 	asi->insn_check_cc = kprobe_condition_checks[cc];
@@ -1149,7 +1154,7 @@ t16_emulate_hiregs(struct kprobe *p, struct pt_regs *regs)
 }
 
 static enum kprobe_insn __kprobes
-t16_decode_hiregs(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+t16_decode_hiregs(kprobe_opcode_t insn, struct arch_specific_insn *asi, void *d)
 {
 	insn &= ~0x00ff;
 	insn |= 0x001; /* Set Rdn = R1 and Rm = R0 */
@@ -1175,7 +1180,7 @@ t16_emulate_push(struct kprobe *p, struct pt_regs *regs)
 }
 
 static enum kprobe_insn __kprobes
-t16_decode_push(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+t16_decode_push(kprobe_opcode_t insn, struct arch_specific_insn *asi, void *d)
 {
 	/*
 	 * To simulate a PUSH we use a Thumb-2 "STMDB R9!, {registers}"
@@ -1225,7 +1230,7 @@ t16_emulate_pop_pc(struct kprobe *p, struct pt_regs *regs)
 }
 
 static enum kprobe_insn __kprobes
-t16_decode_pop(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+t16_decode_pop(kprobe_opcode_t insn, struct arch_specific_insn *asi, void *d)
 {
 	/*
 	 * To simulate a POP we use a Thumb-2 "LDMDB R9!, {registers}"
@@ -1244,11 +1249,11 @@ static const union decode_item t16_table_1011[] = {
 
 	/* ADD (SP plus immediate)	1011 0000 0xxx xxxx */
 	/* SUB (SP minus immediate)	1011 0000 1xxx xxxx */
-	DECODE_SIMULATE	(0xff00, 0xb000, t16_simulate_add_sp_imm),
+	DECODE_SIMULATE	(0xff00, 0xb000, PROBES_T16_ADD_SP),
 
 	/* CBZ				1011 00x1 xxxx xxxx */
 	/* CBNZ				1011 10x1 xxxx xxxx */
-	DECODE_SIMULATE	(0xf500, 0xb100, t16_simulate_cbz),
+	DECODE_SIMULATE	(0xf500, 0xb100, PROBES_T16_CBZ),
 
 	/* SXTH				1011 0010 00xx xxxx */
 	/* SXTB				1011 0010 01xx xxxx */
@@ -1259,12 +1264,12 @@ static const union decode_item t16_table_1011[] = {
 	/* ???				1011 1010 10xx xxxx */
 	/* REVSH			1011 1010 11xx xxxx */
 	DECODE_REJECT	(0xffc0, 0xba80),
-	DECODE_EMULATE	(0xf500, 0xb000, t16_emulate_loregs_rwflags),
+	DECODE_EMULATE	(0xf500, 0xb000, PROBES_T16_SIGN_EXTEND),
 
 	/* PUSH				1011 010x xxxx xxxx */
-	DECODE_CUSTOM	(0xfe00, 0xb400, t16_decode_push),
+	DECODE_CUSTOM	(0xfe00, 0xb400, PROBES_T16_PUSH),
 	/* POP				1011 110x xxxx xxxx */
-	DECODE_CUSTOM	(0xfe00, 0xbc00, t16_decode_pop),
+	DECODE_CUSTOM	(0xfe00, 0xbc00, PROBES_T16_POP),
 
 	/*
 	 * If-Then, and hints
@@ -1274,15 +1279,15 @@ static const union decode_item t16_table_1011[] = {
 	/* YIELD			1011 1111 0001 0000 */
 	DECODE_OR	(0xffff, 0xbf10),
 	/* SEV				1011 1111 0100 0000 */
-	DECODE_EMULATE	(0xffff, 0xbf40, probes_emulate_none),
+	DECODE_EMULATE	(0xffff, 0xbf40, PROBES_T16_SEV),
 	/* NOP				1011 1111 0000 0000 */
 	/* WFE				1011 1111 0010 0000 */
 	/* WFI				1011 1111 0011 0000 */
-	DECODE_SIMULATE	(0xffcf, 0xbf00, probes_simulate_nop),
+	DECODE_SIMULATE	(0xffcf, 0xbf00, PROBES_T16_WFE),
 	/* Unassigned hints		1011 1111 xxxx 0000 */
 	DECODE_REJECT	(0xff0f, 0xbf00),
 	/* IT				1011 1111 xxxx xxxx */
-	DECODE_CUSTOM	(0xff00, 0xbf00, t16_decode_it),
+	DECODE_CUSTOM	(0xff00, 0xbf00, PROBES_T16_IT),
 
 	/* SETEND			1011 0110 010x xxxx */
 	/* CPS				1011 0110 011x xxxx */
@@ -1299,7 +1304,7 @@ const union decode_item kprobe_decode_thumb16_table[] = {
 	 */
 
 	/* CMP (immediate)		0010 1xxx xxxx xxxx */
-	DECODE_EMULATE	(0xf800, 0x2800, t16_emulate_loregs_rwflags),
+	DECODE_EMULATE	(0xf800, 0x2800, PROBES_T16_CMP),
 
 	/* ADD (register)		0001 100x xxxx xxxx */
 	/* SUB (register)		0001 101x xxxx xxxx */
@@ -1311,7 +1316,7 @@ const union decode_item kprobe_decode_thumb16_table[] = {
 	/* MOV (immediate)		0010 0xxx xxxx xxxx */
 	/* ADD (immediate, Thumb)	0011 0xxx xxxx xxxx */
 	/* SUB (immediate, Thumb)	0011 1xxx xxxx xxxx */
-	DECODE_EMULATE	(0xc000, 0x0000, t16_emulate_loregs_noitrwflags),
+	DECODE_EMULATE	(0xc000, 0x0000, PROBES_T16_ADDSUB),
 
 	/*
 	 * 16-bit Thumb data-processing instructions
@@ -1319,10 +1324,10 @@ const union decode_item kprobe_decode_thumb16_table[] = {
 	 */
 
 	/* TST (register)		0100 0010 00xx xxxx */
-	DECODE_EMULATE	(0xffc0, 0x4200, t16_emulate_loregs_rwflags),
+	DECODE_EMULATE	(0xffc0, 0x4200, PROBES_T16_CMP),
 	/* CMP (register)		0100 0010 10xx xxxx */
 	/* CMN (register)		0100 0010 11xx xxxx */
-	DECODE_EMULATE	(0xff80, 0x4280, t16_emulate_loregs_rwflags),
+	DECODE_EMULATE	(0xff80, 0x4280, PROBES_T16_CMP),
 	/* AND (register)		0100 0000 00xx xxxx */
 	/* EOR (register)		0100 0000 01xx xxxx */
 	/* LSL (register)		0100 0000 10xx xxxx */
@@ -1336,7 +1341,7 @@ const union decode_item kprobe_decode_thumb16_table[] = {
 	/* MUL				0100 0011 00xx xxxx */
 	/* BIC (register)		0100 0011 10xx xxxx */
 	/* MVN (register)		0100 0011 10xx xxxx */
-	DECODE_EMULATE	(0xfc00, 0x4000, t16_emulate_loregs_noitrwflags),
+	DECODE_EMULATE	(0xfc00, 0x4000, PROBES_T16_LOGICAL),
 
 	/*
 	 * Special data instructions and branch and exchange
@@ -1348,7 +1353,7 @@ const union decode_item kprobe_decode_thumb16_table[] = {
 
 	/* BX (register)		0100 0111 0xxx xxxx */
 	/* BLX (register)		0100 0111 1xxx xxxx */
-	DECODE_SIMULATE (0xff00, 0x4700, t16_simulate_bxblx),
+	DECODE_SIMULATE (0xff00, 0x4700, PROBES_T16_BLX),
 
 	/* ADD pc, pc			0100 0100 1111 1111 */
 	DECODE_REJECT	(0xffff, 0x44ff),
@@ -1356,13 +1361,13 @@ const union decode_item kprobe_decode_thumb16_table[] = {
 	/* ADD (register)		0100 0100 xxxx xxxx */
 	/* CMP (register)		0100 0101 xxxx xxxx */
 	/* MOV (register)		0100 0110 xxxx xxxx */
-	DECODE_CUSTOM	(0xfc00, 0x4400, t16_decode_hiregs),
+	DECODE_CUSTOM	(0xfc00, 0x4400, PROBES_T16_HIREGOPS),
 
 	/*
 	 * Load from Literal Pool
 	 * LDR (literal)		0100 1xxx xxxx xxxx
 	 */
-	DECODE_SIMULATE	(0xf800, 0x4800, t16_simulate_ldr_literal),
+	DECODE_SIMULATE	(0xf800, 0x4800, PROBES_T16_LDR_LIT),
 
 	/*
 	 * 16-bit Thumb Load/store instructions
@@ -1383,20 +1388,20 @@ const union decode_item kprobe_decode_thumb16_table[] = {
 	/* LDR (immediate, Thumb)	0110 1xxx xxxx xxxx */
 	/* STRB (immediate, Thumb)	0111 0xxx xxxx xxxx */
 	/* LDRB (immediate, Thumb)	0111 1xxx xxxx xxxx */
-	DECODE_EMULATE	(0xc000, 0x4000, t16_emulate_loregs_rwflags),
+	DECODE_EMULATE	(0xc000, 0x4000, PROBES_T16_LDRHSTRH),
 	/* STRH (immediate, Thumb)	1000 0xxx xxxx xxxx */
 	/* LDRH (immediate, Thumb)	1000 1xxx xxxx xxxx */
-	DECODE_EMULATE	(0xf000, 0x8000, t16_emulate_loregs_rwflags),
+	DECODE_EMULATE	(0xf000, 0x8000, PROBES_T16_LDRHSTRH),
 	/* STR (immediate, Thumb)	1001 0xxx xxxx xxxx */
 	/* LDR (immediate, Thumb)	1001 1xxx xxxx xxxx */
-	DECODE_SIMULATE	(0xf000, 0x9000, t16_simulate_ldrstr_sp_relative),
+	DECODE_SIMULATE	(0xf000, 0x9000, PROBES_T16_LDRSTR),
 
 	/*
 	 * Generate PC-/SP-relative address
 	 * ADR (literal)		1010 0xxx xxxx xxxx
 	 * ADD (SP plus immediate)	1010 1xxx xxxx xxxx
 	 */
-	DECODE_SIMULATE	(0xf000, 0xa000, t16_simulate_reladr),
+	DECODE_SIMULATE	(0xf000, 0xa000, PROBES_T16_ADR),
 
 	/*
 	 * Miscellaneous 16-bit instructions
@@ -1406,7 +1411,7 @@ const union decode_item kprobe_decode_thumb16_table[] = {
 
 	/* STM				1100 0xxx xxxx xxxx */
 	/* LDM				1100 1xxx xxxx xxxx */
-	DECODE_EMULATE	(0xf000, 0xc000, t16_emulate_loregs_rwflags),
+	DECODE_EMULATE	(0xf000, 0xc000, PROBES_T16_LDMSTM),
 
 	/*
 	 * Conditional branch, and Supervisor Call
@@ -1417,16 +1422,70 @@ const union decode_item kprobe_decode_thumb16_table[] = {
 	DECODE_REJECT	(0xfe00, 0xde00),
 
 	/* Conditional branch		1101 xxxx xxxx xxxx */
-	DECODE_CUSTOM	(0xf000, 0xd000, t16_decode_cond_branch),
+	DECODE_CUSTOM	(0xf000, 0xd000, PROBES_T16_BRANCH_COND),
 
 	/*
 	 * Unconditional branch
 	 * B				1110 0xxx xxxx xxxx
 	 */
-	DECODE_SIMULATE	(0xf800, 0xe000, t16_simulate_branch),
+	DECODE_SIMULATE	(0xf800, 0xe000, PROBES_T16_BRANCH),
 
 	DECODE_END
 };
+
+const union decode_item kprobes_t32_actions[] = {
+	[PROBES_T32_LDMSTM] = {.decoder = t32_decode_ldmstm},
+	[PROBES_T32_LDRDSTRD] = {.handler = t32_emulate_ldrdstrd},
+	[PROBES_T32_TABLE_BRANCH] = {.handler = t32_simulate_table_branch},
+	[PROBES_T32_TST] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
+	[PROBES_T32_MOV] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
+	[PROBES_T32_ADDSUB] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
+	[PROBES_T32_LOGICAL] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
+	[PROBES_T32_CMP] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
+	[PROBES_T32_ADDWSUBW_PC] = {.handler = t32_emulate_rd8pc16_noflags,},
+	[PROBES_T32_ADDWSUBW] = {.handler = t32_emulate_rd8rn16_noflags},
+	[PROBES_T32_MOVW] = {.handler = t32_emulate_rd8rn16_noflags},
+	[PROBES_T32_SAT] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
+	[PROBES_T32_BITFIELD] = {.handler = t32_emulate_rd8rn16_noflags},
+	[PROBES_T32_SEV] = {.handler = probes_emulate_none},
+	[PROBES_T32_WFE] = {.handler = probes_simulate_nop},
+	[PROBES_T32_MRS] = {.handler = t32_simulate_mrs},
+	[PROBES_T32_BRANCH_COND] = {.decoder = t32_decode_cond_branch},
+	[PROBES_T32_BRANCH] = {.handler = t32_simulate_branch},
+	[PROBES_T32_PLDI] = {.handler = probes_simulate_nop},
+	[PROBES_T32_LDR_LIT] = {.handler = t32_simulate_ldr_literal},
+	[PROBES_T32_LDRSTR] = {.handler = t32_emulate_ldrstr},
+	[PROBES_T32_SIGN_EXTEND] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
+	[PROBES_T32_MEDIA] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
+	[PROBES_T32_REVERSE] = {.handler = t32_emulate_rd8rn16_noflags},
+	[PROBES_T32_MUL_ADD] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
+	[PROBES_T32_MUL_ADD2] = {.handler = t32_emulate_rd8rn16rm0ra12_noflags},
+	[PROBES_T32_MUL_ADD_LONG] = {.handler = t32_emulate_rdlo12rdhi8rn16rm0_noflags},
+};
+
+const union decode_item kprobes_t16_actions[] = {
+	[PROBES_T16_ADD_SP] = {.handler = t16_simulate_add_sp_imm},
+	[PROBES_T16_CBZ] = {.handler = t16_simulate_cbz},
+	[PROBES_T16_SIGN_EXTEND] = {.handler = t16_emulate_loregs_rwflags},
+	[PROBES_T16_PUSH] = {.decoder = t16_decode_push},
+	[PROBES_T16_POP] = {.decoder = t16_decode_pop},
+	[PROBES_T16_SEV] = {.handler = probes_emulate_none},
+	[PROBES_T16_WFE] = {.handler = probes_simulate_nop},
+	[PROBES_T16_IT] = {.decoder = t16_decode_it},
+	[PROBES_T16_CMP] = {.handler = t16_emulate_loregs_rwflags},
+	[PROBES_T16_ADDSUB] = {.handler = t16_emulate_loregs_noitrwflags},
+	[PROBES_T16_LOGICAL] = {.handler = t16_emulate_loregs_noitrwflags},
+	[PROBES_T16_LDR_LIT] = {.handler = t16_simulate_ldr_literal},
+	[PROBES_T16_BLX] = {.handler = t16_simulate_bxblx},
+	[PROBES_T16_HIREGOPS] = {.decoder = t16_decode_hiregs},
+	[PROBES_T16_LDRHSTRH] = {.handler = t16_emulate_loregs_rwflags},
+	[PROBES_T16_LDRSTR] = {.handler = t16_simulate_ldrstr_sp_relative},
+	[PROBES_T16_ADR] = {.handler = t16_simulate_reladr},
+	[PROBES_T16_LDMSTM] = {.handler = t16_emulate_loregs_rwflags},
+	[PROBES_T16_BRANCH_COND] = {.decoder = t16_decode_cond_branch},
+	[PROBES_T16_BRANCH] = {.handler = t16_simulate_branch},
+};
+
 #ifdef CONFIG_ARM_KPROBES_TEST_MODULE
 EXPORT_SYMBOL_GPL(kprobe_decode_thumb16_table);
 #endif
@@ -1453,17 +1512,21 @@ static void __kprobes thumb32_singlestep(struct kprobe *p, struct pt_regs *regs)
 }
 
 enum kprobe_insn __kprobes
-thumb16_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+thumb16_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
+			   bool usermode, const union decode_item *actions)
 {
 	asi->insn_singlestep = thumb16_singlestep;
 	asi->insn_check_cc = thumb_check_cc;
-	return kprobe_decode_insn(insn, asi, kprobe_decode_thumb16_table, true);
+	return probes_decode_insn(insn, asi, kprobe_decode_thumb16_table, true,
+				  usermode, actions);
 }
 
 enum kprobe_insn __kprobes
-thumb32_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+thumb32_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
+			   bool usermode, const union decode_item *actions)
 {
 	asi->insn_singlestep = thumb32_singlestep;
 	asi->insn_check_cc = thumb_check_cc;
-	return kprobe_decode_insn(insn, asi, kprobe_decode_thumb32_table, true);
+	return probes_decode_insn(insn, asi, kprobe_decode_thumb32_table, true,
+				  usermode, actions);
 }
diff --git a/arch/arm/kernel/kprobes.c b/arch/arm/kernel/kprobes.c
index 170e9f3..7b484d3 100644
--- a/arch/arm/kernel/kprobes.c
+++ b/arch/arm/kernel/kprobes.c
@@ -29,6 +29,7 @@
 #include <asm/cacheflush.h>
 
 #include "kprobes.h"
+#include "probes.h"
 #include "patch.h"
 
 #define MIN_STACK_SIZE(addr) 				\
@@ -54,6 +55,7 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
 	unsigned long addr = (unsigned long)p->addr;
 	bool thumb;
 	kprobe_decode_insn_t *decode_insn;
+	const union decode_item *actions;
 	int is;
 
 	if (in_exception_text(addr))
@@ -67,20 +69,25 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
 		insn <<= 16;
 		insn |= ((u16 *)addr)[1];
 		decode_insn = thumb32_kprobe_decode_insn;
-	} else
+		actions = kprobes_t32_actions;
+	} else {
 		decode_insn = thumb16_kprobe_decode_insn;
+		actions = kprobes_t16_actions;
+	}
 #else /* !CONFIG_THUMB2_KERNEL */
 	thumb = false;
 	if (addr & 0x3)
 		return -EINVAL;
 	insn = *p->addr;
 	decode_insn = arm_kprobe_decode_insn;
+	actions = kprobes_probes_actions;
 #endif
 
 	p->opcode = insn;
 	p->ainsn.insn = tmp_insn;
 
-	switch ((*decode_insn)(insn, &p->ainsn)) {
+	switch ((*decode_insn)(insn, &p->ainsn, false,
+			       actions)) {
 	case INSN_REJECTED:	/* not supported */
 		return -EINVAL;
 
diff --git a/arch/arm/kernel/kprobes.h b/arch/arm/kernel/kprobes.h
index 9aa2f15..6ca1cbe 100644
--- a/arch/arm/kernel/kprobes.h
+++ b/arch/arm/kernel/kprobes.h
@@ -34,22 +34,6 @@ enum kprobe_insn {
 	INSN_GOOD_NO_SLOT
 };
 
-typedef enum kprobe_insn (kprobe_decode_insn_t)(kprobe_opcode_t,
-						struct arch_specific_insn *);
-
-#ifdef CONFIG_THUMB2_KERNEL
-
-enum kprobe_insn thumb16_kprobe_decode_insn(kprobe_opcode_t,
-						struct arch_specific_insn *);
-enum kprobe_insn thumb32_kprobe_decode_insn(kprobe_opcode_t,
-						struct arch_specific_insn *);
-
-#else /* !CONFIG_THUMB2_KERNEL */
-
-enum kprobe_insn arm_kprobe_decode_insn(kprobe_opcode_t,
-					struct arch_specific_insn *);
-#endif
-
 void __init arm_kprobe_decode_init(void);
 
 extern kprobe_check_cc * const kprobe_condition_checks[16];
@@ -161,11 +145,9 @@ static inline void __kprobes alu_write_pc(long pcv, struct pt_regs *regs)
 }
 
 
-void __kprobes probes_simulate_nop(struct kprobe *p, struct pt_regs *regs);
-void __kprobes probes_emulate_none(struct kprobe *p, struct pt_regs *regs);
-
 enum kprobe_insn __kprobes
-kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi);
+kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi,
+		     void *d);
 
 /*
  * Test if load/store instructions writeback the address register.
@@ -331,7 +313,9 @@ union decode_item {
 	u32			bits;
 	const union decode_item	*table;
 	kprobe_insn_handler_t	*handler;
-	kprobe_decode_insn_t	*decoder;
+	enum kprobe_insn (*decoder)(kprobe_opcode_t,
+				    struct arch_specific_insn *,
+				    void *d);
 };
 
 
@@ -368,7 +352,7 @@ struct decode_custom {
 
 #define DECODE_CUSTOM(_mask, _value, _decoder)			\
 	DECODE_HEADER(DECODE_TYPE_CUSTOM, _mask, _value, 0),	\
-	{.decoder = (_decoder)}
+	{.bits = (_decoder)}
 
 
 struct decode_simulate {
@@ -378,7 +362,7 @@ struct decode_simulate {
 
 #define DECODE_SIMULATEX(_mask, _value, _handler, _regs)		\
 	DECODE_HEADER(DECODE_TYPE_SIMULATE, _mask, _value, _regs),	\
-	{.handler = (_handler)}
+	{.bits = (_handler)}
 
 #define DECODE_SIMULATE(_mask, _value, _handler)	\
 	DECODE_SIMULATEX(_mask, _value, _handler, 0)
@@ -391,12 +375,11 @@ struct decode_emulate {
 
 #define DECODE_EMULATEX(_mask, _value, _handler, _regs)			\
 	DECODE_HEADER(DECODE_TYPE_EMULATE, _mask, _value, _regs),	\
-	{.handler = (_handler)}
+	{.bits = (_handler)}
 
 #define DECODE_EMULATE(_mask, _value, _handler)		\
 	DECODE_EMULATEX(_mask, _value, _handler, 0)
 
-
 struct decode_or {
 	struct decode_header	header;
 };
@@ -413,16 +396,39 @@ struct decode_reject {
 	DECODE_HEADER(DECODE_TYPE_REJECT, _mask, _value, 0)
 
 
+typedef enum kprobe_insn (kprobe_decode_insn_t)(kprobe_opcode_t,
+						struct arch_specific_insn *,
+						bool usermode,
+						const union decode_item *actions);
+
 #ifdef CONFIG_THUMB2_KERNEL
+
+enum kprobe_insn thumb16_kprobe_decode_insn(kprobe_opcode_t,
+					    struct arch_specific_insn *,
+					    bool usermode,
+					    const union decode_item *actions);
+
+enum kprobe_insn thumb32_kprobe_decode_insn(kprobe_opcode_t,
+					    struct arch_specific_insn *,
+					    bool usermode,
+					    const union decode_item *actions);
+
 extern const union decode_item kprobe_decode_thumb16_table[];
 extern const union decode_item kprobe_decode_thumb32_table[];
-#else
-extern const union decode_item kprobe_decode_arm_table[];
-#endif
+extern const union decode_item kprobes_t32_actions[];
+extern const union decode_item kprobes_t16_actions[];
 
+#else
 
 int kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
-			const union decode_item *table, bool thumb16);
+			const union decode_item *table, bool thumb16,
+			bool usermode,
+			const union decode_item *actions);
+
+extern const union decode_item kprobe_decode_arm_table[];
+extern const union decode_item kprobes_probes_actions[];
+
+#endif
 
 
 #endif /* _ARM_KERNEL_KPROBES_H */
diff --git a/arch/arm/kernel/probes-arm.c b/arch/arm/kernel/probes-arm.c
index e1b1a6e..e67bf3f 100644
--- a/arch/arm/kernel/probes-arm.c
+++ b/arch/arm/kernel/probes-arm.c
@@ -18,8 +18,8 @@
 #include <linux/kernel.h>
 #include <linux/kprobes.h>
 
-#include "probes.h"
 #include "kprobes.h"
+#include "probes.h"
 
 #define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
 
diff --git a/arch/arm/kernel/probes-arm.h b/arch/arm/kernel/probes-arm.h
new file mode 100644
index 0000000..b1690878
--- /dev/null
+++ b/arch/arm/kernel/probes-arm.h
@@ -0,0 +1,60 @@
+#ifndef _ARM_KERNEL_PROBES_ARM_H
+#define  _ARM_KERNEL_PROBES_ARM_H
+
+enum probes_action {
+	PROBES_EMULATE_NONE,
+	PROBES_SIMULATE_NOP,
+	PROBES_PRELOAD_IMM,
+	PROBES_PRELOAD_REG,
+	PROBES_BRANCH_IMM,
+	PROBES_BRANCH_REG,
+	PROBES_MRS,
+	PROBES_CLZ,
+	PROBES_SATURATING_ARITHMETIC,
+	PROBES_MUL1,
+	PROBES_MUL2,
+	PROBES_SWP,
+	PROBES_LDRSTRD,
+	PROBES_LOAD,
+	PROBES_STORE,
+	PROBES_LOAD_EXTRA,
+	PROBES_STORE_EXTRA,
+	PROBES_MOV_IP_SP,
+	PROBES_DATA_PROCESSING_REG,
+	PROBES_DATA_PROCESSING_IMM,
+	PROBES_MOV_HALFWORD,
+	PROBES_SEV,
+	PROBES_WFE,
+	PROBES_SATURATE,
+	PROBES_REV,
+	PROBES_MMI,
+	PROBES_PACK,
+	PROBES_EXTEND,
+	PROBES_EXTEND_ADD,
+	PROBES_MUL_ADD_LONG,
+	PROBES_MUL_ADD,
+	PROBES_BITFIELD,
+	PROBES_BRANCH,
+	PROBES_LDMSTM
+};
+
+void __kprobes simulate_bbl(struct kprobe *p, struct pt_regs *regs);
+void __kprobes simulate_blx1(struct kprobe *p, struct pt_regs *regs);
+void __kprobes simulate_blx2bx(struct kprobe *p, struct pt_regs *regs);
+void __kprobes simulate_mrs(struct kprobe *p, struct pt_regs *regs);
+void __kprobes simulate_mov_ipsp(struct kprobe *p, struct pt_regs *regs);
+void __kprobes emulate_ldrdstrd(struct kprobe *p, struct pt_regs *regs);
+void __kprobes emulate_ldr(struct kprobe *p, struct pt_regs *regs);
+void __kprobes emulate_str(struct kprobe *p, struct pt_regs *regs);
+void __kprobes emulate_rd12rn16rm0rs8_rwflags(struct kprobe *p,
+					      struct pt_regs *regs);
+void __kprobes emulate_rd12rn16rm0_rwflags_nopc(struct kprobe *p,
+						struct pt_regs *regs);
+void __kprobes emulate_rd16rn12rm0rs8_rwflags_nopc(struct kprobe *p,
+						   struct pt_regs *regs);
+void __kprobes emulate_rd12rm0_noflags_nopc(struct kprobe *p,
+					    struct pt_regs *regs);
+void __kprobes emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(struct kprobe *p,
+						       struct pt_regs *regs);
+
+#endif
diff --git a/arch/arm/kernel/probes-thumb.h b/arch/arm/kernel/probes-thumb.h
new file mode 100644
index 0000000..4b9365c
--- /dev/null
+++ b/arch/arm/kernel/probes-thumb.h
@@ -0,0 +1,59 @@
+#ifndef _ARM_KERNEL_PROBES_THUMB_H
+#define  _ARM_KERNEL_PROBES_THUMB_H
+
+enum probes_t32_action {
+	PROBES_T32_EMULATE_NONE,
+	PROBES_T32_SIMULATE_NOP,
+	PROBES_T32_LDMSTM,
+	PROBES_T32_LDRDSTRD,
+	PROBES_T32_TABLE_BRANCH,
+	PROBES_T32_TST,
+	PROBES_T32_CMP,
+	PROBES_T32_MOV,
+	PROBES_T32_ADDSUB,
+	PROBES_T32_LOGICAL,
+	PROBES_T32_ADDWSUBW_PC,
+	PROBES_T32_ADDWSUBW,
+	PROBES_T32_MOVW,
+	PROBES_T32_SAT,
+	PROBES_T32_BITFIELD,
+	PROBES_T32_SEV,
+	PROBES_T32_WFE,
+	PROBES_T32_MRS,
+	PROBES_T32_BRANCH_COND,
+	PROBES_T32_BRANCH,
+	PROBES_T32_PLDI,
+	PROBES_T32_LDR_LIT,
+	PROBES_T32_LDRSTR,
+	PROBES_T32_SIGN_EXTEND,
+	PROBES_T32_MEDIA,
+	PROBES_T32_REVERSE,
+	PROBES_T32_MUL_ADD,
+	PROBES_T32_MUL_ADD2,
+	PROBES_T32_MUL_ADD_LONG
+};
+
+enum probes_t16_action {
+	PROBES_T16_ADD_SP,
+	PROBES_T16_CBZ,
+	PROBES_T16_SIGN_EXTEND,
+	PROBES_T16_PUSH,
+	PROBES_T16_POP,
+	PROBES_T16_SEV,
+	PROBES_T16_WFE,
+	PROBES_T16_IT,
+	PROBES_T16_CMP,
+	PROBES_T16_ADDSUB,
+	PROBES_T16_LOGICAL,
+	PROBES_T16_BLX,
+	PROBES_T16_HIREGOPS,
+	PROBES_T16_LDR_LIT,
+	PROBES_T16_LDRHSTRH,
+	PROBES_T16_LDRSTR,
+	PROBES_T16_ADR,
+	PROBES_T16_LDMSTM,
+	PROBES_T16_BRANCH_COND,
+	PROBES_T16_BRANCH
+};
+
+#endif
diff --git a/arch/arm/kernel/probes.c b/arch/arm/kernel/probes.c
index 86c63f3..8cff27a 100644
--- a/arch/arm/kernel/probes.c
+++ b/arch/arm/kernel/probes.c
@@ -1,23 +1,8 @@
-/*
- * arch/arm/kernel/probes.c
- *
- * Some contents moved here from arch/arm/include/asm/kprobes-common.c
- *
- * Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
- *
- * Some contents moved here from arch/arm/include/asm/kprobes-arm.c which is
- * 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.
- */
-
 #include <linux/kernel.h>
 #include <linux/kprobes.h>
 
-#include "probes.h"
 #include "kprobes.h"
+#include "probes.h"
 
 void __kprobes probes_simulate_nop(struct kprobe *p, struct pt_regs *regs)
 {
@@ -28,6 +13,47 @@ void __kprobes probes_emulate_none(struct kprobe *p, struct pt_regs *regs)
 	p->ainsn.insn_fn();
 }
 
+#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
+
+#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25)
+
+#if  __LINUX_ARM_ARCH__ >= 6
+#define BLX(reg)	"blx	"reg"		\n\t"
+#else
+#define BLX(reg)	"mov	lr, pc		\n\t"	\
+			"mov	pc, "reg"	\n\t"
+#endif
+
+/*
+ * To avoid 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 performance.
+ *
+ * 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 perform register writeback)
+ * 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.
+ */
+
 /*
  * Prepare an instruction slot to receive an instruction for emulating.
  * This is done by placing a subroutine return after the location where the
@@ -97,7 +123,7 @@ set_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
  * non-zero value, the corresponding nibble in pinsn is validated and modified
  * according to the type.
  */
-static bool __kprobes decode_regs(kprobe_opcode_t *pinsn, u32 regs)
+static bool __kprobes decode_regs(kprobe_opcode_t *pinsn, u32 regs, bool modify)
 {
 	kprobe_opcode_t insn = *pinsn;
 	kprobe_opcode_t mask = 0xf; /* Start at least significant nibble */
@@ -158,12 +184,16 @@ static bool __kprobes decode_regs(kprobe_opcode_t *pinsn, u32 regs)
 			break;
 		}
 
-		/* Replace value of nibble with new register number... */
-		insn &= ~mask;
-		insn |= new_bits & mask;
+		if (modify) {
+			/* Replace value of nibble with new register number */
+			insn &= ~mask;
+			insn |= new_bits & mask;
+		}
 	}
 
-	*pinsn = insn;
+	if (modify)
+		*pinsn = insn;
+
 	return true;
 
 reject:
@@ -223,14 +253,17 @@ static const int decode_struct_sizes[NUM_DECODE_TYPES] = {
  *
  */
 int __kprobes
-kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
-				const union decode_item *table, bool thumb)
+probes_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
+				const union decode_item *table, bool thumb,
+				bool usermode,
+				const union decode_item *actions)
 {
 	const struct decode_header *h = (struct decode_header *)table;
 	const struct decode_header *next;
 	bool matched = false;
 
-	insn = prepare_emulated_insn(insn, asi, thumb);
+	if (!usermode)
+		insn = prepare_emulated_insn(insn, asi, thumb);
 
 	for (;; h = next) {
 		enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK;
@@ -242,14 +275,14 @@ kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
 		next = (struct decode_header *)
 				((uintptr_t)h + decode_struct_sizes[type]);
 
-		if (!matched && (insn & h->mask.bits) != h->value.bits)
+		if (!matched &&
+		    (insn & h->mask.bits) != h->value.bits)
 			continue;
 
-		if (!decode_regs(&insn, regs))
+		if (!decode_regs(&insn, regs, !usermode))
 			return INSN_REJECTED;
 
 		switch (type) {
-
 		case DECODE_TYPE_TABLE: {
 			struct decode_table *d = (struct decode_table *)h;
 			next = (struct decode_header *)d->table.table;
@@ -258,18 +291,24 @@ kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
 
 		case DECODE_TYPE_CUSTOM: {
 			struct decode_custom *d = (struct decode_custom *)h;
-			return (*d->decoder.decoder)(insn, asi);
+
+			return actions[d->decoder.bits].decoder(insn, asi, d);
 		}
 
 		case DECODE_TYPE_SIMULATE: {
 			struct decode_simulate *d = (struct decode_simulate *)h;
-			asi->insn_handler = d->handler.handler;
+			asi->insn_handler = actions[d->handler.bits].handler;
 			return INSN_GOOD_NO_SLOT;
 		}
 
 		case DECODE_TYPE_EMULATE: {
 			struct decode_emulate *d = (struct decode_emulate *)h;
-			asi->insn_handler = d->handler.handler;
+
+			if (usermode)
+				return actions[d->handler.bits].decoder(insn,
+									asi, d);
+
+			asi->insn_handler = actions[d->handler.bits].handler;
 			set_emulated_insn(insn, asi, thumb);
 			return INSN_GOOD;
 		}
diff --git a/arch/arm/kernel/probes.h b/arch/arm/kernel/probes.h
index 56eec12..bc03c690 100644
--- a/arch/arm/kernel/probes.h
+++ b/arch/arm/kernel/probes.h
@@ -1,6 +1,18 @@
 #ifndef _ARM_KERNEL_PROBES_H
 #define  _ARM_KERNEL_PROBES_H
 
+int __kprobes
+probes_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
+				const union decode_item *table, bool thumb,
+				bool usermode,
+				const union decode_item *actions);
+enum kprobe_insn arm_kprobe_decode_insn(kprobe_opcode_t,
+					struct arch_specific_insn *,
+					bool usermode,
+					const union decode_item *actions);
+
+void __kprobes probes_simulate_nop(struct kprobe *p, struct pt_regs *regs);
+void __kprobes probes_emulate_none(struct kprobe *p, struct pt_regs *regs);
 void __kprobes simulate_bbl(struct kprobe *p, struct pt_regs *regs);
 void __kprobes simulate_blx1(struct kprobe *p, struct pt_regs *regs);
 void __kprobes simulate_blx2bx(struct kprobe *p, struct pt_regs *regs);
-- 
1.8.1.2

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 9/9] ARM: add uprobes support
  2013-08-01 23:45 ` David Long
@ 2013-08-01 23:45   ` David Long
  -1 siblings, 0 replies; 32+ messages in thread
From: David Long @ 2013-08-01 23:45 UTC (permalink / raw)
  To: linux-arm-kernel; +Cc: Rabin Vincent, Jon Medhurst (Tixy), linux-kernel

From: "David A. Long" <dave.long@linaro.org>

Using Rabin Vincent's ARM uprobes patches as a base, enable uprobes
support on ARM.

Caveats:

 - Thumb is not supported
 - XOL abort/trap handling is not implemented

Signed-off-by: David A. Long <dave.long@linaro.org>
---
 arch/arm/Kconfig                   |   4 +
 arch/arm/include/asm/ptrace.h      |   6 +
 arch/arm/include/asm/thread_info.h |   5 +-
 arch/arm/include/asm/uprobes.h     |  34 ++++++
 arch/arm/kernel/Makefile           |   1 +
 arch/arm/kernel/signal.c           |   4 +
 arch/arm/kernel/uprobes-arm.c      | 221 +++++++++++++++++++++++++++++++++++++
 arch/arm/kernel/uprobes.c          | 203 ++++++++++++++++++++++++++++++++++
 arch/arm/kernel/uprobes.h          |  25 +++++
 arch/powerpc/include/asm/uprobes.h |   1 -
 include/linux/uprobes.h            |   3 +
 kernel/events/uprobes.c            |   1 +
 12 files changed, 506 insertions(+), 2 deletions(-)
 create mode 100644 arch/arm/include/asm/uprobes.h
 create mode 100644 arch/arm/kernel/uprobes-arm.c
 create mode 100644 arch/arm/kernel/uprobes.c
 create mode 100644 arch/arm/kernel/uprobes.h

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 37c0f4e..06efe09 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -197,6 +197,10 @@ config ZONE_DMA
 config NEED_DMA_MAP_STATE
        def_bool y
 
+config ARCH_SUPPORTS_UPROBES
+	depends on KPROBES
+	def_bool y
+
 config ARCH_HAS_DMA_SET_COHERENT_MASK
 	bool
 
diff --git a/arch/arm/include/asm/ptrace.h b/arch/arm/include/asm/ptrace.h
index 04c99f3..ee688b0a 100644
--- a/arch/arm/include/asm/ptrace.h
+++ b/arch/arm/include/asm/ptrace.h
@@ -80,6 +80,12 @@ static inline long regs_return_value(struct pt_regs *regs)
 
 #define instruction_pointer(regs)	(regs)->ARM_pc
 
+static inline void instruction_pointer_set(struct pt_regs *regs,
+					   unsigned long val)
+{
+	instruction_pointer(regs) = val;
+}
+
 #ifdef CONFIG_SMP
 extern unsigned long profile_pc(struct pt_regs *regs);
 #else
diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h
index 214d415..c219f8d 100644
--- a/arch/arm/include/asm/thread_info.h
+++ b/arch/arm/include/asm/thread_info.h
@@ -148,6 +148,7 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *,
 #define TIF_SIGPENDING		0
 #define TIF_NEED_RESCHED	1
 #define TIF_NOTIFY_RESUME	2	/* callback before returning to user */
+#define TIF_UPROBE		7
 #define TIF_SYSCALL_TRACE	8
 #define TIF_SYSCALL_AUDIT	9
 #define TIF_SYSCALL_TRACEPOINT	10
@@ -161,6 +162,7 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *,
 #define _TIF_SIGPENDING		(1 << TIF_SIGPENDING)
 #define _TIF_NEED_RESCHED	(1 << TIF_NEED_RESCHED)
 #define _TIF_NOTIFY_RESUME	(1 << TIF_NOTIFY_RESUME)
+#define _TIF_UPROBE		(1 << TIF_UPROBE)
 #define _TIF_SYSCALL_TRACE	(1 << TIF_SYSCALL_TRACE)
 #define _TIF_SYSCALL_AUDIT	(1 << TIF_SYSCALL_AUDIT)
 #define _TIF_SYSCALL_TRACEPOINT	(1 << TIF_SYSCALL_TRACEPOINT)
@@ -174,7 +176,8 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *,
 /*
  * Change these and you break ASM code in entry-common.S
  */
-#define _TIF_WORK_MASK		(_TIF_NEED_RESCHED | _TIF_SIGPENDING | _TIF_NOTIFY_RESUME)
+#define _TIF_WORK_MASK		(_TIF_NEED_RESCHED | _TIF_SIGPENDING | \
+				 _TIF_NOTIFY_RESUME | _TIF_UPROBE)
 
 #endif /* __KERNEL__ */
 #endif /* __ASM_ARM_THREAD_INFO_H */
diff --git a/arch/arm/include/asm/uprobes.h b/arch/arm/include/asm/uprobes.h
new file mode 100644
index 0000000..fa4b81e
--- /dev/null
+++ b/arch/arm/include/asm/uprobes.h
@@ -0,0 +1,34 @@
+#ifndef _ASM_UPROBES_H
+#define _ASM_UPROBES_H
+
+#include <asm/probes.h>
+
+typedef u32 uprobe_opcode_t;
+
+#define MAX_UINSN_BYTES		4
+#define UPROBE_XOL_SLOT_BYTES	64
+
+#define UPROBE_SWBP_INSN	0x07f001f9
+#define UPROBE_SS_INSN		0x07f001fa
+#define UPROBE_SWBP_INSN_SIZE	4
+
+struct arch_uprobe_task {
+	u32 backup;
+};
+
+struct arch_uprobe {
+	u8 insn[MAX_UINSN_BYTES];
+	uprobe_opcode_t modinsn;
+	uprobe_opcode_t bpinsn;
+	bool simulate;
+	u32 pcreg;
+	void (*prehandler)(struct arch_uprobe *auprobe,
+			   struct arch_uprobe_task *autask,
+			   struct pt_regs *regs);
+	void (*posthandler)(struct arch_uprobe *auprobe,
+			    struct arch_uprobe_task *autask,
+			    struct pt_regs *regs);
+	struct arch_specific_insn asi;
+};
+
+#endif
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index 3292023..853f6f02 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -49,6 +49,7 @@ obj-$(CONFIG_DYNAMIC_FTRACE)	+= ftrace.o insn.o
 obj-$(CONFIG_FUNCTION_GRAPH_TRACER)	+= ftrace.o insn.o
 obj-$(CONFIG_JUMP_LABEL)	+= jump_label.o insn.o patch.o
 obj-$(CONFIG_KEXEC)		+= machine_kexec.o relocate_kernel.o
+obj-$(CONFIG_UPROBES)		+= probes.o uprobes.o uprobes-arm.o kprobes-arm.o
 obj-$(CONFIG_KPROBES)		+= probes.o probes-arm.o kprobes.o kprobes-common.o patch.o
 ifdef CONFIG_THUMB2_KERNEL
 obj-$(CONFIG_KPROBES)		+= kprobes-thumb.o
diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c
index 1c16c35..20e4e61 100644
--- a/arch/arm/kernel/signal.c
+++ b/arch/arm/kernel/signal.c
@@ -12,6 +12,7 @@
 #include <linux/personality.h>
 #include <linux/uaccess.h>
 #include <linux/tracehook.h>
+#include <linux/uprobes.h>
 
 #include <asm/elf.h>
 #include <asm/cacheflush.h>
@@ -598,6 +599,9 @@ do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall)
 					return restart;
 				}
 				syscall = 0;
+			} else if (thread_flags & _TIF_UPROBE) {
+				clear_thread_flag(TIF_UPROBE);
+				uprobe_notify_resume(regs);
 			} else {
 				clear_thread_flag(TIF_NOTIFY_RESUME);
 				tracehook_notify_resume(regs);
diff --git a/arch/arm/kernel/uprobes-arm.c b/arch/arm/kernel/uprobes-arm.c
new file mode 100644
index 0000000..dc298ec
--- /dev/null
+++ b/arch/arm/kernel/uprobes-arm.c
@@ -0,0 +1,221 @@
+#include <linux/kernel.h>
+#include <linux/kprobes.h>
+#include <linux/uprobes.h>
+#include <linux/module.h>
+
+#include "kprobes.h"
+#include "probes.h"
+#include "probes-arm.h"
+#include "uprobes.h"
+
+static int uprobes_substitute_pc(kprobe_opcode_t *pinsn, u32 oregs)
+{
+	kprobe_opcode_t insn = *pinsn;
+	kprobe_opcode_t temp;
+	kprobe_opcode_t mask;
+	int freereg;
+	u32 free = 0xffff;
+	u32 regs;
+
+	for (regs = oregs; regs; regs >>= 4, insn >>= 4) {
+		if ((regs & 0xf) == REG_TYPE_NONE)
+			continue;
+
+		free &= ~(1 << (insn & 0xf));
+	}
+
+	/* No PC, no problem */
+	if (free & (1 << 15))
+		return 15;
+
+	if (!free)
+		return -1;
+
+	/*
+	 * fls instead of ffs ensures that for "ldrd r0, r1, [pc]" we would
+	 * pick LR instead of R1.
+	 */
+	freereg = free = fls(free) - 1;
+
+	temp = *pinsn;
+	insn = *pinsn;
+	regs = oregs;
+	mask = 0xf;
+
+	for (; regs; regs >>= 4, mask <<= 4, free <<= 4, temp >>= 4) {
+		if ((regs & 0xf) == REG_TYPE_NONE)
+			continue;
+
+		if ((temp & 0xf) != 15)
+			continue;
+
+		insn &= ~mask;
+		insn |= free & mask;
+	}
+
+	*pinsn = insn;
+	return freereg;
+}
+
+static void uprobe_set_pc(struct arch_uprobe *auprobe,
+			  struct arch_uprobe_task *autask,
+			  struct pt_regs *regs)
+{
+	u32 pcreg = auprobe->pcreg;
+
+	autask->backup = regs->uregs[pcreg];
+	regs->uregs[pcreg] = regs->ARM_pc + 8;
+}
+
+static void uprobe_unset_pc(struct arch_uprobe *auprobe,
+			    struct arch_uprobe_task *autask,
+			    struct pt_regs *regs)
+{
+	/* PC will be taken care of by common code */
+	regs->uregs[auprobe->pcreg] = autask->backup;
+}
+
+static void uprobe_aluwrite_pc(struct arch_uprobe *auprobe,
+			       struct arch_uprobe_task *autask,
+			       struct pt_regs *regs)
+{
+	u32 pcreg = auprobe->pcreg;
+
+	alu_write_pc(regs->uregs[pcreg], regs);
+	regs->uregs[pcreg] = autask->backup;
+}
+
+static void uprobe_write_pc(struct arch_uprobe *auprobe,
+			    struct arch_uprobe_task *autask,
+			    struct pt_regs *regs)
+{
+	u32 pcreg = auprobe->pcreg;
+
+	load_write_pc(regs->uregs[pcreg], regs);
+	regs->uregs[pcreg] = autask->backup;
+}
+
+enum kprobe_insn
+decode_pc_ro(kprobe_opcode_t insn, struct arch_specific_insn *asi, void *d)
+{
+	struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe,
+						   asi);
+	struct decode_emulate *decode = d;
+	u32 regs = decode->header.type_regs.bits >> DECODE_TYPE_BITS;
+	int reg;
+
+	reg = uprobes_substitute_pc(&auprobe->modinsn, regs);
+	if (reg == 15)
+		return INSN_GOOD;
+
+	if (reg == -1)
+		return INSN_REJECTED;
+
+	auprobe->pcreg = reg;
+	auprobe->prehandler = uprobe_set_pc;
+	auprobe->posthandler = uprobe_unset_pc;
+
+	return INSN_GOOD;
+}
+
+enum kprobe_insn
+decode_wb_pc(kprobe_opcode_t insn, struct arch_specific_insn *asi,
+	     void *d, bool alu)
+{
+	struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe,
+						   asi);
+	enum kprobe_insn ret = decode_pc_ro(insn, asi, d);
+
+	if (((insn >> 12) & 0xf) == 15)
+		auprobe->posthandler = alu ? uprobe_aluwrite_pc
+					   : uprobe_write_pc;
+
+	return ret;
+}
+
+enum kprobe_insn
+decode_rd12rn16rm0rs8_rwflags(kprobe_opcode_t insn,
+			      struct arch_specific_insn *asi, void *d)
+{
+	return decode_wb_pc(insn, asi, d, true);
+}
+
+enum kprobe_insn
+decode_ldr(kprobe_opcode_t insn, struct arch_specific_insn *asi, void *d)
+{
+	return decode_wb_pc(insn, asi, d, false);
+}
+
+enum kprobe_insn
+uprobe_decode_ldmstm(kprobe_opcode_t insn,
+		     struct arch_specific_insn *asi, void *d)
+{
+	struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe,
+						   asi);
+	unsigned reglist = insn & 0xffff;
+	int rn = (insn >> 16) & 0xf;
+	int lbit = insn & (1 << 20);
+	unsigned used = reglist | (1 << rn);
+
+	if (rn == 15)
+		return INSN_REJECTED;
+
+	if (!(used & (1 << 15)))
+		return INSN_GOOD;
+
+	if (used & (1 << 14))
+		return INSN_REJECTED;
+
+	/* Use LR instead of PC */
+	insn ^= 0xc000;
+
+	auprobe->pcreg = 14;
+	auprobe->modinsn = insn;
+
+	auprobe->prehandler = uprobe_set_pc;
+	if (lbit)
+		auprobe->posthandler = uprobe_write_pc;
+	else
+		auprobe->posthandler = uprobe_unset_pc;
+
+	return INSN_GOOD;
+}
+
+const union decode_item uprobes_probes_actions[] = {
+	[PROBES_EMULATE_NONE] {.handler = probes_simulate_nop},
+	[PROBES_SIMULATE_NOP] = {.handler = probes_simulate_nop},
+	[PROBES_PRELOAD_IMM] = {.handler = probes_simulate_nop},
+	[PROBES_PRELOAD_REG] = {.handler = probes_simulate_nop},
+	[PROBES_BRANCH_IMM] = {.handler = simulate_blx1},
+	[PROBES_MRS] = {.handler = simulate_mrs},
+	[PROBES_BRANCH_REG] = {.handler = simulate_blx2bx},
+	[PROBES_CLZ] = {.handler = probes_simulate_nop},
+	[PROBES_SATURATING_ARITHMETIC] = {.handler = probes_simulate_nop},
+	[PROBES_MUL1] = {.handler = probes_simulate_nop},
+	[PROBES_MUL2] = {.handler = probes_simulate_nop},
+	[PROBES_SWP] = {.handler = probes_simulate_nop},
+	[PROBES_LDRSTRD] = {.decoder = decode_pc_ro},
+	[PROBES_LOAD_EXTRA] = {.decoder = decode_pc_ro},
+	[PROBES_LOAD] = {.decoder = decode_ldr},
+	[PROBES_STORE_EXTRA] = {.decoder = decode_pc_ro},
+	[PROBES_STORE] = {.decoder = decode_pc_ro},
+	[PROBES_MOV_IP_SP] = {.handler = simulate_mov_ipsp},
+	[PROBES_DATA_PROCESSING_REG] = {
+		.decoder = decode_rd12rn16rm0rs8_rwflags},
+	[PROBES_DATA_PROCESSING_IMM] = {
+		.decoder = decode_rd12rn16rm0rs8_rwflags},
+	[PROBES_MOV_HALFWORD] = {.handler = probes_simulate_nop},
+	[PROBES_SEV] = {.handler = probes_simulate_nop},
+	[PROBES_WFE] = {.handler = probes_simulate_nop},
+	[PROBES_SATURATE] = {.handler = probes_simulate_nop},
+	[PROBES_REV] = {.handler = probes_simulate_nop},
+	[PROBES_MMI] = {.handler = probes_simulate_nop},
+	[PROBES_PACK] = {.handler = probes_simulate_nop},
+	[PROBES_EXTEND] = {.handler = probes_simulate_nop},
+	[PROBES_EXTEND_ADD] = {.handler = probes_simulate_nop},
+	[PROBES_MUL_ADD_LONG] = {.handler = probes_simulate_nop},
+	[PROBES_MUL_ADD] = {.handler = probes_simulate_nop},
+	[PROBES_BITFIELD] = {.handler = probes_simulate_nop},
+	[PROBES_BRANCH] = {.handler = simulate_bbl},
+	[PROBES_LDMSTM] = {.decoder = uprobe_decode_ldmstm}
+};
diff --git a/arch/arm/kernel/uprobes.c b/arch/arm/kernel/uprobes.c
new file mode 100644
index 0000000..90a32d2
--- /dev/null
+++ b/arch/arm/kernel/uprobes.c
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2012 Rabin Vincent <rabin at rab.in>
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/uprobes.h>
+#include <linux/notifier.h>
+#include <linux/kprobes.h>
+
+#include <asm/opcodes.h>
+#include <asm/traps.h>
+
+#include "kprobes.h"
+#include "probes.h"
+#include "probes-arm.h"
+#include "uprobes.h"
+
+bool is_swbp_insn(uprobe_opcode_t *insn)
+{
+	return (__mem_to_opcode_arm(*insn) & 0x0fffffff) == UPROBE_SWBP_INSN;
+}
+
+bool arch_uprobe_ignore(struct arch_uprobe *auprobe, struct pt_regs *regs)
+{
+	if (!auprobe->asi.insn_check_cc(regs->ARM_cpsr)) {
+		regs->ARM_pc += 4;
+		return true;
+	}
+
+	return false;
+}
+
+bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
+{
+	struct kprobe kp;
+
+	if (!auprobe->simulate)
+		return false;
+
+	kp.addr = (void *) regs->ARM_pc;
+	kp.opcode = __mem_to_opcode_arm(*(unsigned int *) auprobe->insn);
+	kp.ainsn.insn_handler = auprobe->asi.insn_handler;
+
+	auprobe->asi.insn_singlestep(&kp, regs);
+
+	return true;
+}
+
+unsigned long
+arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr,
+				  struct pt_regs *regs)
+{
+	regs->ARM_pc = trampoline_vaddr;
+	return trampoline_vaddr;
+}
+
+int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm,
+			     unsigned long addr)
+{
+	unsigned int insn;
+	unsigned int bpinsn;
+	enum kprobe_insn ret;
+
+	/* Thumb not yet support */
+	if (addr & 0x3)
+		return -EINVAL;
+
+	insn = __mem_to_opcode_arm(*(unsigned int *)auprobe->insn);
+	auprobe->modinsn = insn;
+
+	ret = arm_kprobe_decode_insn(insn, &auprobe->asi, true,
+				     uprobes_probes_actions);
+	switch (ret) {
+	case INSN_REJECTED:
+		return -EINVAL;
+
+	case INSN_GOOD_NO_SLOT:
+		auprobe->simulate = true;
+		break;
+
+	case INSN_GOOD:
+	default:
+		break;
+	}
+
+	bpinsn = UPROBE_SWBP_INSN;
+	if (insn >= 0xe0000000)
+		bpinsn |= 0xe0000000;  /* Unconditional instruction */
+	else
+		bpinsn |= insn & 0xf0000000;  /* Copy condition from insn */
+
+	auprobe->bpinsn = bpinsn;
+
+	return 0;
+}
+
+void arch_uprobe_write_opcode(struct arch_uprobe *auprobe, void *vaddr,
+			      uprobe_opcode_t opcode)
+{
+	unsigned long *addr = vaddr;
+
+	if (opcode == UPROBE_SWBP_INSN)
+		opcode = __opcode_to_mem_arm(auprobe->bpinsn);
+
+	*addr = opcode;
+}
+
+void arch_uprobe_xol_copy(struct arch_uprobe *auprobe, void *vaddr)
+{
+	unsigned long *addr = vaddr;
+
+	addr[0] = __opcode_to_mem_arm(auprobe->modinsn);
+	addr[1] = __opcode_to_mem_arm(0xe0000000 | UPROBE_SS_INSN);
+}
+
+int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
+{
+	struct uprobe_task *utask = current->utask;
+
+	if (auprobe->prehandler)
+		auprobe->prehandler(auprobe, &utask->autask, regs);
+
+	regs->ARM_pc = utask->xol_vaddr;
+
+	return 0;
+}
+
+int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
+{
+	struct uprobe_task *utask = current->utask;
+
+	regs->ARM_pc = utask->vaddr + 4;
+
+	if (auprobe->posthandler)
+		auprobe->posthandler(auprobe, &utask->autask, regs);
+
+	return 0;
+}
+
+bool arch_uprobe_xol_was_trapped(struct task_struct *t)
+{
+	/* TODO: implement */
+	return false;
+}
+
+void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
+{
+	/* TODO: implement */
+}
+
+int arch_uprobe_exception_notify(struct notifier_block *self,
+				 unsigned long val, void *data)
+{
+	return NOTIFY_DONE;
+}
+
+static int uprobe_trap_handler(struct pt_regs *regs, unsigned int instr)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+	if ((instr & 0x0fffffff) == UPROBE_SWBP_INSN)
+		uprobe_pre_sstep_notifier(regs);
+	else
+		uprobe_post_sstep_notifier(regs);
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+unsigned long uprobe_get_swbp_addr(struct pt_regs *regs)
+{
+	return instruction_pointer(regs);
+}
+
+static struct undef_hook uprobes_arm_break_hook = {
+	.instr_mask	= 0x0fffffff,
+	.instr_val	= UPROBE_SWBP_INSN,
+	.cpsr_mask	= MODE_MASK,
+	.cpsr_val	= USR_MODE,
+	.fn		= uprobe_trap_handler,
+};
+
+static struct undef_hook uprobes_arm_ss_hook = {
+	.instr_mask	= 0x0fffffff,
+	.instr_val	= UPROBE_SS_INSN,
+	.cpsr_mask	= MODE_MASK,
+	.cpsr_val	= USR_MODE,
+	.fn		= uprobe_trap_handler,
+};
+
+int arch_uprobes_init(void)
+{
+	register_undef_hook(&uprobes_arm_break_hook);
+	register_undef_hook(&uprobes_arm_ss_hook);
+
+	return 0;
+}
diff --git a/arch/arm/kernel/uprobes.h b/arch/arm/kernel/uprobes.h
new file mode 100644
index 0000000..3887c19
--- /dev/null
+++ b/arch/arm/kernel/uprobes.h
@@ -0,0 +1,25 @@
+#ifndef __ARM_KERNEL_UPROBES_H
+#define __ARM_KERNEL_UPROBES_H
+
+enum kprobe_insn uprobe_decode_ldmstm(kprobe_opcode_t insn,
+				      struct arch_specific_insn *asi,
+				      void *d);
+
+enum kprobe_insn decode_ldr(kprobe_opcode_t insn,
+			    struct arch_specific_insn *asi,
+			    void *d);
+
+enum kprobe_insn
+decode_rd12rn16rm0rs8_rwflags(kprobe_opcode_t insn,
+			      struct arch_specific_insn *asi, void *d);
+
+enum kprobe_insn
+decode_wb_pc(kprobe_opcode_t insn, struct arch_specific_insn *asi,
+	     void *d, bool alu);
+
+enum kprobe_insn
+decode_pc_ro(kprobe_opcode_t insn, struct arch_specific_insn *asi, void *d);
+
+extern const union decode_item uprobes_probes_actions[];
+
+#endif
diff --git a/arch/powerpc/include/asm/uprobes.h b/arch/powerpc/include/asm/uprobes.h
index 2301602..b532060 100644
--- a/arch/powerpc/include/asm/uprobes.h
+++ b/arch/powerpc/include/asm/uprobes.h
@@ -51,5 +51,4 @@ extern int  arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs);
 extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk);
 extern int  arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data);
 extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs);
-extern unsigned long arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr, struct pt_regs *regs);
 #endif	/* _ASM_UPROBES_H */
diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h
index 9cd3b25..41341e9 100644
--- a/include/linux/uprobes.h
+++ b/include/linux/uprobes.h
@@ -139,6 +139,9 @@ extern int __weak arch_uprobes_init(void);
 extern void __weak arch_uprobe_write_opcode(struct arch_uprobe *auprobe,
 					    void *vaddr,
 					    uprobe_opcode_t opcode);
+extern
+unsigned long arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr,
+						       struct pt_regs *regs);
 #else /* !CONFIG_UPROBES */
 struct uprobes_state {
 };
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 6a60eec..3e9d596 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -271,6 +271,7 @@ static int write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm,
 {
 	struct page *old_page, *new_page;
 	struct vm_area_struct *vma;
+	void *vaddr_new;
 	int ret;
 
 retry:
-- 
1.8.1.2


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 9/9] ARM: add uprobes support
@ 2013-08-01 23:45   ` David Long
  0 siblings, 0 replies; 32+ messages in thread
From: David Long @ 2013-08-01 23:45 UTC (permalink / raw)
  To: linux-arm-kernel

From: "David A. Long" <dave.long@linaro.org>

Using Rabin Vincent's ARM uprobes patches as a base, enable uprobes
support on ARM.

Caveats:

 - Thumb is not supported
 - XOL abort/trap handling is not implemented

Signed-off-by: David A. Long <dave.long@linaro.org>
---
 arch/arm/Kconfig                   |   4 +
 arch/arm/include/asm/ptrace.h      |   6 +
 arch/arm/include/asm/thread_info.h |   5 +-
 arch/arm/include/asm/uprobes.h     |  34 ++++++
 arch/arm/kernel/Makefile           |   1 +
 arch/arm/kernel/signal.c           |   4 +
 arch/arm/kernel/uprobes-arm.c      | 221 +++++++++++++++++++++++++++++++++++++
 arch/arm/kernel/uprobes.c          | 203 ++++++++++++++++++++++++++++++++++
 arch/arm/kernel/uprobes.h          |  25 +++++
 arch/powerpc/include/asm/uprobes.h |   1 -
 include/linux/uprobes.h            |   3 +
 kernel/events/uprobes.c            |   1 +
 12 files changed, 506 insertions(+), 2 deletions(-)
 create mode 100644 arch/arm/include/asm/uprobes.h
 create mode 100644 arch/arm/kernel/uprobes-arm.c
 create mode 100644 arch/arm/kernel/uprobes.c
 create mode 100644 arch/arm/kernel/uprobes.h

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 37c0f4e..06efe09 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -197,6 +197,10 @@ config ZONE_DMA
 config NEED_DMA_MAP_STATE
        def_bool y
 
+config ARCH_SUPPORTS_UPROBES
+	depends on KPROBES
+	def_bool y
+
 config ARCH_HAS_DMA_SET_COHERENT_MASK
 	bool
 
diff --git a/arch/arm/include/asm/ptrace.h b/arch/arm/include/asm/ptrace.h
index 04c99f3..ee688b0a 100644
--- a/arch/arm/include/asm/ptrace.h
+++ b/arch/arm/include/asm/ptrace.h
@@ -80,6 +80,12 @@ static inline long regs_return_value(struct pt_regs *regs)
 
 #define instruction_pointer(regs)	(regs)->ARM_pc
 
+static inline void instruction_pointer_set(struct pt_regs *regs,
+					   unsigned long val)
+{
+	instruction_pointer(regs) = val;
+}
+
 #ifdef CONFIG_SMP
 extern unsigned long profile_pc(struct pt_regs *regs);
 #else
diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h
index 214d415..c219f8d 100644
--- a/arch/arm/include/asm/thread_info.h
+++ b/arch/arm/include/asm/thread_info.h
@@ -148,6 +148,7 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *,
 #define TIF_SIGPENDING		0
 #define TIF_NEED_RESCHED	1
 #define TIF_NOTIFY_RESUME	2	/* callback before returning to user */
+#define TIF_UPROBE		7
 #define TIF_SYSCALL_TRACE	8
 #define TIF_SYSCALL_AUDIT	9
 #define TIF_SYSCALL_TRACEPOINT	10
@@ -161,6 +162,7 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *,
 #define _TIF_SIGPENDING		(1 << TIF_SIGPENDING)
 #define _TIF_NEED_RESCHED	(1 << TIF_NEED_RESCHED)
 #define _TIF_NOTIFY_RESUME	(1 << TIF_NOTIFY_RESUME)
+#define _TIF_UPROBE		(1 << TIF_UPROBE)
 #define _TIF_SYSCALL_TRACE	(1 << TIF_SYSCALL_TRACE)
 #define _TIF_SYSCALL_AUDIT	(1 << TIF_SYSCALL_AUDIT)
 #define _TIF_SYSCALL_TRACEPOINT	(1 << TIF_SYSCALL_TRACEPOINT)
@@ -174,7 +176,8 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *,
 /*
  * Change these and you break ASM code in entry-common.S
  */
-#define _TIF_WORK_MASK		(_TIF_NEED_RESCHED | _TIF_SIGPENDING | _TIF_NOTIFY_RESUME)
+#define _TIF_WORK_MASK		(_TIF_NEED_RESCHED | _TIF_SIGPENDING | \
+				 _TIF_NOTIFY_RESUME | _TIF_UPROBE)
 
 #endif /* __KERNEL__ */
 #endif /* __ASM_ARM_THREAD_INFO_H */
diff --git a/arch/arm/include/asm/uprobes.h b/arch/arm/include/asm/uprobes.h
new file mode 100644
index 0000000..fa4b81e
--- /dev/null
+++ b/arch/arm/include/asm/uprobes.h
@@ -0,0 +1,34 @@
+#ifndef _ASM_UPROBES_H
+#define _ASM_UPROBES_H
+
+#include <asm/probes.h>
+
+typedef u32 uprobe_opcode_t;
+
+#define MAX_UINSN_BYTES		4
+#define UPROBE_XOL_SLOT_BYTES	64
+
+#define UPROBE_SWBP_INSN	0x07f001f9
+#define UPROBE_SS_INSN		0x07f001fa
+#define UPROBE_SWBP_INSN_SIZE	4
+
+struct arch_uprobe_task {
+	u32 backup;
+};
+
+struct arch_uprobe {
+	u8 insn[MAX_UINSN_BYTES];
+	uprobe_opcode_t modinsn;
+	uprobe_opcode_t bpinsn;
+	bool simulate;
+	u32 pcreg;
+	void (*prehandler)(struct arch_uprobe *auprobe,
+			   struct arch_uprobe_task *autask,
+			   struct pt_regs *regs);
+	void (*posthandler)(struct arch_uprobe *auprobe,
+			    struct arch_uprobe_task *autask,
+			    struct pt_regs *regs);
+	struct arch_specific_insn asi;
+};
+
+#endif
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index 3292023..853f6f02 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -49,6 +49,7 @@ obj-$(CONFIG_DYNAMIC_FTRACE)	+= ftrace.o insn.o
 obj-$(CONFIG_FUNCTION_GRAPH_TRACER)	+= ftrace.o insn.o
 obj-$(CONFIG_JUMP_LABEL)	+= jump_label.o insn.o patch.o
 obj-$(CONFIG_KEXEC)		+= machine_kexec.o relocate_kernel.o
+obj-$(CONFIG_UPROBES)		+= probes.o uprobes.o uprobes-arm.o kprobes-arm.o
 obj-$(CONFIG_KPROBES)		+= probes.o probes-arm.o kprobes.o kprobes-common.o patch.o
 ifdef CONFIG_THUMB2_KERNEL
 obj-$(CONFIG_KPROBES)		+= kprobes-thumb.o
diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c
index 1c16c35..20e4e61 100644
--- a/arch/arm/kernel/signal.c
+++ b/arch/arm/kernel/signal.c
@@ -12,6 +12,7 @@
 #include <linux/personality.h>
 #include <linux/uaccess.h>
 #include <linux/tracehook.h>
+#include <linux/uprobes.h>
 
 #include <asm/elf.h>
 #include <asm/cacheflush.h>
@@ -598,6 +599,9 @@ do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall)
 					return restart;
 				}
 				syscall = 0;
+			} else if (thread_flags & _TIF_UPROBE) {
+				clear_thread_flag(TIF_UPROBE);
+				uprobe_notify_resume(regs);
 			} else {
 				clear_thread_flag(TIF_NOTIFY_RESUME);
 				tracehook_notify_resume(regs);
diff --git a/arch/arm/kernel/uprobes-arm.c b/arch/arm/kernel/uprobes-arm.c
new file mode 100644
index 0000000..dc298ec
--- /dev/null
+++ b/arch/arm/kernel/uprobes-arm.c
@@ -0,0 +1,221 @@
+#include <linux/kernel.h>
+#include <linux/kprobes.h>
+#include <linux/uprobes.h>
+#include <linux/module.h>
+
+#include "kprobes.h"
+#include "probes.h"
+#include "probes-arm.h"
+#include "uprobes.h"
+
+static int uprobes_substitute_pc(kprobe_opcode_t *pinsn, u32 oregs)
+{
+	kprobe_opcode_t insn = *pinsn;
+	kprobe_opcode_t temp;
+	kprobe_opcode_t mask;
+	int freereg;
+	u32 free = 0xffff;
+	u32 regs;
+
+	for (regs = oregs; regs; regs >>= 4, insn >>= 4) {
+		if ((regs & 0xf) == REG_TYPE_NONE)
+			continue;
+
+		free &= ~(1 << (insn & 0xf));
+	}
+
+	/* No PC, no problem */
+	if (free & (1 << 15))
+		return 15;
+
+	if (!free)
+		return -1;
+
+	/*
+	 * fls instead of ffs ensures that for "ldrd r0, r1, [pc]" we would
+	 * pick LR instead of R1.
+	 */
+	freereg = free = fls(free) - 1;
+
+	temp = *pinsn;
+	insn = *pinsn;
+	regs = oregs;
+	mask = 0xf;
+
+	for (; regs; regs >>= 4, mask <<= 4, free <<= 4, temp >>= 4) {
+		if ((regs & 0xf) == REG_TYPE_NONE)
+			continue;
+
+		if ((temp & 0xf) != 15)
+			continue;
+
+		insn &= ~mask;
+		insn |= free & mask;
+	}
+
+	*pinsn = insn;
+	return freereg;
+}
+
+static void uprobe_set_pc(struct arch_uprobe *auprobe,
+			  struct arch_uprobe_task *autask,
+			  struct pt_regs *regs)
+{
+	u32 pcreg = auprobe->pcreg;
+
+	autask->backup = regs->uregs[pcreg];
+	regs->uregs[pcreg] = regs->ARM_pc + 8;
+}
+
+static void uprobe_unset_pc(struct arch_uprobe *auprobe,
+			    struct arch_uprobe_task *autask,
+			    struct pt_regs *regs)
+{
+	/* PC will be taken care of by common code */
+	regs->uregs[auprobe->pcreg] = autask->backup;
+}
+
+static void uprobe_aluwrite_pc(struct arch_uprobe *auprobe,
+			       struct arch_uprobe_task *autask,
+			       struct pt_regs *regs)
+{
+	u32 pcreg = auprobe->pcreg;
+
+	alu_write_pc(regs->uregs[pcreg], regs);
+	regs->uregs[pcreg] = autask->backup;
+}
+
+static void uprobe_write_pc(struct arch_uprobe *auprobe,
+			    struct arch_uprobe_task *autask,
+			    struct pt_regs *regs)
+{
+	u32 pcreg = auprobe->pcreg;
+
+	load_write_pc(regs->uregs[pcreg], regs);
+	regs->uregs[pcreg] = autask->backup;
+}
+
+enum kprobe_insn
+decode_pc_ro(kprobe_opcode_t insn, struct arch_specific_insn *asi, void *d)
+{
+	struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe,
+						   asi);
+	struct decode_emulate *decode = d;
+	u32 regs = decode->header.type_regs.bits >> DECODE_TYPE_BITS;
+	int reg;
+
+	reg = uprobes_substitute_pc(&auprobe->modinsn, regs);
+	if (reg == 15)
+		return INSN_GOOD;
+
+	if (reg == -1)
+		return INSN_REJECTED;
+
+	auprobe->pcreg = reg;
+	auprobe->prehandler = uprobe_set_pc;
+	auprobe->posthandler = uprobe_unset_pc;
+
+	return INSN_GOOD;
+}
+
+enum kprobe_insn
+decode_wb_pc(kprobe_opcode_t insn, struct arch_specific_insn *asi,
+	     void *d, bool alu)
+{
+	struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe,
+						   asi);
+	enum kprobe_insn ret = decode_pc_ro(insn, asi, d);
+
+	if (((insn >> 12) & 0xf) == 15)
+		auprobe->posthandler = alu ? uprobe_aluwrite_pc
+					   : uprobe_write_pc;
+
+	return ret;
+}
+
+enum kprobe_insn
+decode_rd12rn16rm0rs8_rwflags(kprobe_opcode_t insn,
+			      struct arch_specific_insn *asi, void *d)
+{
+	return decode_wb_pc(insn, asi, d, true);
+}
+
+enum kprobe_insn
+decode_ldr(kprobe_opcode_t insn, struct arch_specific_insn *asi, void *d)
+{
+	return decode_wb_pc(insn, asi, d, false);
+}
+
+enum kprobe_insn
+uprobe_decode_ldmstm(kprobe_opcode_t insn,
+		     struct arch_specific_insn *asi, void *d)
+{
+	struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe,
+						   asi);
+	unsigned reglist = insn & 0xffff;
+	int rn = (insn >> 16) & 0xf;
+	int lbit = insn & (1 << 20);
+	unsigned used = reglist | (1 << rn);
+
+	if (rn == 15)
+		return INSN_REJECTED;
+
+	if (!(used & (1 << 15)))
+		return INSN_GOOD;
+
+	if (used & (1 << 14))
+		return INSN_REJECTED;
+
+	/* Use LR instead of PC */
+	insn ^= 0xc000;
+
+	auprobe->pcreg = 14;
+	auprobe->modinsn = insn;
+
+	auprobe->prehandler = uprobe_set_pc;
+	if (lbit)
+		auprobe->posthandler = uprobe_write_pc;
+	else
+		auprobe->posthandler = uprobe_unset_pc;
+
+	return INSN_GOOD;
+}
+
+const union decode_item uprobes_probes_actions[] = {
+	[PROBES_EMULATE_NONE] {.handler = probes_simulate_nop},
+	[PROBES_SIMULATE_NOP] = {.handler = probes_simulate_nop},
+	[PROBES_PRELOAD_IMM] = {.handler = probes_simulate_nop},
+	[PROBES_PRELOAD_REG] = {.handler = probes_simulate_nop},
+	[PROBES_BRANCH_IMM] = {.handler = simulate_blx1},
+	[PROBES_MRS] = {.handler = simulate_mrs},
+	[PROBES_BRANCH_REG] = {.handler = simulate_blx2bx},
+	[PROBES_CLZ] = {.handler = probes_simulate_nop},
+	[PROBES_SATURATING_ARITHMETIC] = {.handler = probes_simulate_nop},
+	[PROBES_MUL1] = {.handler = probes_simulate_nop},
+	[PROBES_MUL2] = {.handler = probes_simulate_nop},
+	[PROBES_SWP] = {.handler = probes_simulate_nop},
+	[PROBES_LDRSTRD] = {.decoder = decode_pc_ro},
+	[PROBES_LOAD_EXTRA] = {.decoder = decode_pc_ro},
+	[PROBES_LOAD] = {.decoder = decode_ldr},
+	[PROBES_STORE_EXTRA] = {.decoder = decode_pc_ro},
+	[PROBES_STORE] = {.decoder = decode_pc_ro},
+	[PROBES_MOV_IP_SP] = {.handler = simulate_mov_ipsp},
+	[PROBES_DATA_PROCESSING_REG] = {
+		.decoder = decode_rd12rn16rm0rs8_rwflags},
+	[PROBES_DATA_PROCESSING_IMM] = {
+		.decoder = decode_rd12rn16rm0rs8_rwflags},
+	[PROBES_MOV_HALFWORD] = {.handler = probes_simulate_nop},
+	[PROBES_SEV] = {.handler = probes_simulate_nop},
+	[PROBES_WFE] = {.handler = probes_simulate_nop},
+	[PROBES_SATURATE] = {.handler = probes_simulate_nop},
+	[PROBES_REV] = {.handler = probes_simulate_nop},
+	[PROBES_MMI] = {.handler = probes_simulate_nop},
+	[PROBES_PACK] = {.handler = probes_simulate_nop},
+	[PROBES_EXTEND] = {.handler = probes_simulate_nop},
+	[PROBES_EXTEND_ADD] = {.handler = probes_simulate_nop},
+	[PROBES_MUL_ADD_LONG] = {.handler = probes_simulate_nop},
+	[PROBES_MUL_ADD] = {.handler = probes_simulate_nop},
+	[PROBES_BITFIELD] = {.handler = probes_simulate_nop},
+	[PROBES_BRANCH] = {.handler = simulate_bbl},
+	[PROBES_LDMSTM] = {.decoder = uprobe_decode_ldmstm}
+};
diff --git a/arch/arm/kernel/uprobes.c b/arch/arm/kernel/uprobes.c
new file mode 100644
index 0000000..90a32d2
--- /dev/null
+++ b/arch/arm/kernel/uprobes.c
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2012 Rabin Vincent <rabin@rab.in>
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/uprobes.h>
+#include <linux/notifier.h>
+#include <linux/kprobes.h>
+
+#include <asm/opcodes.h>
+#include <asm/traps.h>
+
+#include "kprobes.h"
+#include "probes.h"
+#include "probes-arm.h"
+#include "uprobes.h"
+
+bool is_swbp_insn(uprobe_opcode_t *insn)
+{
+	return (__mem_to_opcode_arm(*insn) & 0x0fffffff) == UPROBE_SWBP_INSN;
+}
+
+bool arch_uprobe_ignore(struct arch_uprobe *auprobe, struct pt_regs *regs)
+{
+	if (!auprobe->asi.insn_check_cc(regs->ARM_cpsr)) {
+		regs->ARM_pc += 4;
+		return true;
+	}
+
+	return false;
+}
+
+bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
+{
+	struct kprobe kp;
+
+	if (!auprobe->simulate)
+		return false;
+
+	kp.addr = (void *) regs->ARM_pc;
+	kp.opcode = __mem_to_opcode_arm(*(unsigned int *) auprobe->insn);
+	kp.ainsn.insn_handler = auprobe->asi.insn_handler;
+
+	auprobe->asi.insn_singlestep(&kp, regs);
+
+	return true;
+}
+
+unsigned long
+arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr,
+				  struct pt_regs *regs)
+{
+	regs->ARM_pc = trampoline_vaddr;
+	return trampoline_vaddr;
+}
+
+int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm,
+			     unsigned long addr)
+{
+	unsigned int insn;
+	unsigned int bpinsn;
+	enum kprobe_insn ret;
+
+	/* Thumb not yet support */
+	if (addr & 0x3)
+		return -EINVAL;
+
+	insn = __mem_to_opcode_arm(*(unsigned int *)auprobe->insn);
+	auprobe->modinsn = insn;
+
+	ret = arm_kprobe_decode_insn(insn, &auprobe->asi, true,
+				     uprobes_probes_actions);
+	switch (ret) {
+	case INSN_REJECTED:
+		return -EINVAL;
+
+	case INSN_GOOD_NO_SLOT:
+		auprobe->simulate = true;
+		break;
+
+	case INSN_GOOD:
+	default:
+		break;
+	}
+
+	bpinsn = UPROBE_SWBP_INSN;
+	if (insn >= 0xe0000000)
+		bpinsn |= 0xe0000000;  /* Unconditional instruction */
+	else
+		bpinsn |= insn & 0xf0000000;  /* Copy condition from insn */
+
+	auprobe->bpinsn = bpinsn;
+
+	return 0;
+}
+
+void arch_uprobe_write_opcode(struct arch_uprobe *auprobe, void *vaddr,
+			      uprobe_opcode_t opcode)
+{
+	unsigned long *addr = vaddr;
+
+	if (opcode == UPROBE_SWBP_INSN)
+		opcode = __opcode_to_mem_arm(auprobe->bpinsn);
+
+	*addr = opcode;
+}
+
+void arch_uprobe_xol_copy(struct arch_uprobe *auprobe, void *vaddr)
+{
+	unsigned long *addr = vaddr;
+
+	addr[0] = __opcode_to_mem_arm(auprobe->modinsn);
+	addr[1] = __opcode_to_mem_arm(0xe0000000 | UPROBE_SS_INSN);
+}
+
+int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
+{
+	struct uprobe_task *utask = current->utask;
+
+	if (auprobe->prehandler)
+		auprobe->prehandler(auprobe, &utask->autask, regs);
+
+	regs->ARM_pc = utask->xol_vaddr;
+
+	return 0;
+}
+
+int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
+{
+	struct uprobe_task *utask = current->utask;
+
+	regs->ARM_pc = utask->vaddr + 4;
+
+	if (auprobe->posthandler)
+		auprobe->posthandler(auprobe, &utask->autask, regs);
+
+	return 0;
+}
+
+bool arch_uprobe_xol_was_trapped(struct task_struct *t)
+{
+	/* TODO: implement */
+	return false;
+}
+
+void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
+{
+	/* TODO: implement */
+}
+
+int arch_uprobe_exception_notify(struct notifier_block *self,
+				 unsigned long val, void *data)
+{
+	return NOTIFY_DONE;
+}
+
+static int uprobe_trap_handler(struct pt_regs *regs, unsigned int instr)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+	if ((instr & 0x0fffffff) == UPROBE_SWBP_INSN)
+		uprobe_pre_sstep_notifier(regs);
+	else
+		uprobe_post_sstep_notifier(regs);
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+unsigned long uprobe_get_swbp_addr(struct pt_regs *regs)
+{
+	return instruction_pointer(regs);
+}
+
+static struct undef_hook uprobes_arm_break_hook = {
+	.instr_mask	= 0x0fffffff,
+	.instr_val	= UPROBE_SWBP_INSN,
+	.cpsr_mask	= MODE_MASK,
+	.cpsr_val	= USR_MODE,
+	.fn		= uprobe_trap_handler,
+};
+
+static struct undef_hook uprobes_arm_ss_hook = {
+	.instr_mask	= 0x0fffffff,
+	.instr_val	= UPROBE_SS_INSN,
+	.cpsr_mask	= MODE_MASK,
+	.cpsr_val	= USR_MODE,
+	.fn		= uprobe_trap_handler,
+};
+
+int arch_uprobes_init(void)
+{
+	register_undef_hook(&uprobes_arm_break_hook);
+	register_undef_hook(&uprobes_arm_ss_hook);
+
+	return 0;
+}
diff --git a/arch/arm/kernel/uprobes.h b/arch/arm/kernel/uprobes.h
new file mode 100644
index 0000000..3887c19
--- /dev/null
+++ b/arch/arm/kernel/uprobes.h
@@ -0,0 +1,25 @@
+#ifndef __ARM_KERNEL_UPROBES_H
+#define __ARM_KERNEL_UPROBES_H
+
+enum kprobe_insn uprobe_decode_ldmstm(kprobe_opcode_t insn,
+				      struct arch_specific_insn *asi,
+				      void *d);
+
+enum kprobe_insn decode_ldr(kprobe_opcode_t insn,
+			    struct arch_specific_insn *asi,
+			    void *d);
+
+enum kprobe_insn
+decode_rd12rn16rm0rs8_rwflags(kprobe_opcode_t insn,
+			      struct arch_specific_insn *asi, void *d);
+
+enum kprobe_insn
+decode_wb_pc(kprobe_opcode_t insn, struct arch_specific_insn *asi,
+	     void *d, bool alu);
+
+enum kprobe_insn
+decode_pc_ro(kprobe_opcode_t insn, struct arch_specific_insn *asi, void *d);
+
+extern const union decode_item uprobes_probes_actions[];
+
+#endif
diff --git a/arch/powerpc/include/asm/uprobes.h b/arch/powerpc/include/asm/uprobes.h
index 2301602..b532060 100644
--- a/arch/powerpc/include/asm/uprobes.h
+++ b/arch/powerpc/include/asm/uprobes.h
@@ -51,5 +51,4 @@ extern int  arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs);
 extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk);
 extern int  arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data);
 extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs);
-extern unsigned long arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr, struct pt_regs *regs);
 #endif	/* _ASM_UPROBES_H */
diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h
index 9cd3b25..41341e9 100644
--- a/include/linux/uprobes.h
+++ b/include/linux/uprobes.h
@@ -139,6 +139,9 @@ extern int __weak arch_uprobes_init(void);
 extern void __weak arch_uprobe_write_opcode(struct arch_uprobe *auprobe,
 					    void *vaddr,
 					    uprobe_opcode_t opcode);
+extern
+unsigned long arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr,
+						       struct pt_regs *regs);
 #else /* !CONFIG_UPROBES */
 struct uprobes_state {
 };
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 6a60eec..3e9d596 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -271,6 +271,7 @@ static int write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm,
 {
 	struct page *old_page, *new_page;
 	struct vm_area_struct *vma;
+	void *vaddr_new;
 	int ret;
 
 retry:
-- 
1.8.1.2

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PATCH 7/9] ARM: Move uprobes/kprobes shared functions to common file
  2013-08-01 23:45   ` David Long
@ 2013-08-29 14:08     ` Jon Medhurst (Tixy)
  -1 siblings, 0 replies; 32+ messages in thread
From: Jon Medhurst (Tixy) @ 2013-08-29 14:08 UTC (permalink / raw)
  To: David Long; +Cc: linux-arm-kernel, Rabin Vincent, linux-kernel

On Thu, 2013-08-01 at 19:45 -0400, David Long wrote:
> From: "David A. Long" <dave.long@linaro.org>
> 
> Separate the kprobe-only functions from the functions needed by
> both kprobes and uprobes.
> 
> Signed-off-by: David A. Long <dave.long@linaro.org>
> ---

This re-factoring looks a bit back-to-front to me, e.g. the instruction
emulation routines in kprobes-arm.c, which are only used by kprobes are
moved into the common probes-arm.c file, whilst the common decoding
tables are left in the kprobes-arm.c. Surely it should be the other way
around, or have I missed something?

As is, it leads to things like emulate_rd12rn16rm0rs8_rwflags being
defined in probes-arm.c and used in kprobes-arm.c with a prototype added
to kprobes.h, when it could just stay completely local to kprobes-arm.c.

And in a later patch when uprobes is added, it has to link in
kprobes-arm.o to get the generic decoding table. That was the red flag
which made meet look again, as it seems wrong that after all this  code
re-factoring uprobes should need any kprobe files.

I have no other comments on this patch, save you scrolling down :-)
but I'm leaving the patch quoted here for others' reference as I'm late
in replying and people may no longer have the original...

>  arch/arm/kernel/Makefile         |   2 +-
>  arch/arm/kernel/kprobes-arm.c    | 298 +------------------------------------
>  arch/arm/kernel/kprobes-common.c | 266 ---------------------------------
>  arch/arm/kernel/kprobes-thumb.c  |  14 +-
>  arch/arm/kernel/kprobes.h        |   4 +-
>  arch/arm/kernel/probes-arm.c     | 311 +++++++++++++++++++++++++++++++++++++++
>  arch/arm/kernel/probes.c         | 286 +++++++++++++++++++++++++++++++++++
>  arch/arm/kernel/probes.h         |  23 +++
>  8 files changed, 635 insertions(+), 569 deletions(-)
>  create mode 100644 arch/arm/kernel/probes-arm.c
>  create mode 100644 arch/arm/kernel/probes.c
>  create mode 100644 arch/arm/kernel/probes.h
> 
> diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
> index 86d10dd..3292023 100644
> --- a/arch/arm/kernel/Makefile
> +++ b/arch/arm/kernel/Makefile
> @@ -49,7 +49,7 @@ obj-$(CONFIG_DYNAMIC_FTRACE)	+= ftrace.o insn.o
>  obj-$(CONFIG_FUNCTION_GRAPH_TRACER)	+= ftrace.o insn.o
>  obj-$(CONFIG_JUMP_LABEL)	+= jump_label.o insn.o patch.o
>  obj-$(CONFIG_KEXEC)		+= machine_kexec.o relocate_kernel.o
> -obj-$(CONFIG_KPROBES)		+= kprobes.o kprobes-common.o patch.o
> +obj-$(CONFIG_KPROBES)		+= probes.o probes-arm.o kprobes.o kprobes-common.o patch.o
>  ifdef CONFIG_THUMB2_KERNEL
>  obj-$(CONFIG_KPROBES)		+= kprobes-thumb.o
>  else
> diff --git a/arch/arm/kernel/kprobes-arm.c b/arch/arm/kernel/kprobes-arm.c
> index 8a30c89..d6503cc 100644
> --- a/arch/arm/kernel/kprobes-arm.c
> +++ b/arch/arm/kernel/kprobes-arm.c
> @@ -62,19 +62,9 @@
>  #include <linux/kprobes.h>
>  #include <linux/module.h>
>  
> +#include "probes.h"
>  #include "kprobes.h"
>  
> -#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
> -
> -#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25)
> -
> -#if  __LINUX_ARM_ARCH__ >= 6
> -#define BLX(reg)	"blx	"reg"		\n\t"
> -#else
> -#define BLX(reg)	"mov	lr, pc		\n\t"	\
> -			"mov	pc, "reg"	\n\t"
> -#endif
> -
>  /*
>   * To avoid the complications of mimicing single-stepping on a
>   * processor without a Next-PC or a single-step mode, and to
> @@ -105,284 +95,6 @@
>   * read and write of flags.
>   */
>  
> -static void __kprobes simulate_bbl(struct kprobe *p, struct pt_regs *regs)
> -{
> -	kprobe_opcode_t insn = p->opcode;
> -	long iaddr = (long)p->addr;
> -	int disp  = branch_displacement(insn);
> -
> -	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)
> -{
> -	kprobe_opcode_t insn = p->opcode;
> -	int rm = insn & 0xf;
> -	long rmv = regs->uregs[rm];
> -
> -	if (insn & (1 << 5))
> -		regs->ARM_lr = (long)p->addr + 4;
> -
> -	regs->ARM_pc = rmv & ~0x1;
> -	regs->ARM_cpsr &= ~PSR_T_BIT;
> -	if (rmv & 0x1)
> -		regs->ARM_cpsr |= PSR_T_BIT;
> -}
> -
> -static void __kprobes simulate_mrs(struct kprobe *p, struct pt_regs *regs)
> -{
> -	kprobe_opcode_t insn = p->opcode;
> -	int rd = (insn >> 12) & 0xf;
> -	unsigned long mask = 0xf8ff03df; /* Mask out execution state */
> -	regs->uregs[rd] = regs->ARM_cpsr & mask;
> -}
> -
> -static void __kprobes simulate_mov_ipsp(struct kprobe *p, struct pt_regs *regs)
> -{
> -	regs->uregs[12] = regs->uregs[13];
> -}
> -
> -static void __kprobes
> -emulate_ldrdstrd(struct kprobe *p, struct pt_regs *regs)
> -{
> -	kprobe_opcode_t insn = p->opcode;
> -	unsigned long pc = (unsigned long)p->addr + 8;
> -	int rt = (insn >> 12) & 0xf;
> -	int rn = (insn >> 16) & 0xf;
> -	int rm = insn & 0xf;
> -
> -	register unsigned long rtv asm("r0") = regs->uregs[rt];
> -	register unsigned long rt2v asm("r1") = regs->uregs[rt+1];
> -	register unsigned long rnv asm("r2") = (rn == 15) ? pc
> -							  : regs->uregs[rn];
> -	register unsigned long rmv asm("r3") = regs->uregs[rm];
> -
> -	__asm__ __volatile__ (
> -		BLX("%[fn]")
> -		: "=r" (rtv), "=r" (rt2v), "=r" (rnv)
> -		: "0" (rtv), "1" (rt2v), "2" (rnv), "r" (rmv),
> -		  [fn] "r" (p->ainsn.insn_fn)
> -		: "lr", "memory", "cc"
> -	);
> -
> -	regs->uregs[rt] = rtv;
> -	regs->uregs[rt+1] = rt2v;
> -	if (is_writeback(insn))
> -		regs->uregs[rn] = rnv;
> -}
> -
> -static void __kprobes
> -emulate_ldr(struct kprobe *p, struct pt_regs *regs)
> -{
> -	kprobe_opcode_t insn = p->opcode;
> -	unsigned long pc = (unsigned long)p->addr + 8;
> -	int rt = (insn >> 12) & 0xf;
> -	int rn = (insn >> 16) & 0xf;
> -	int rm = insn & 0xf;
> -
> -	register unsigned long rtv asm("r0");
> -	register unsigned long rnv asm("r2") = (rn == 15) ? pc
> -							  : regs->uregs[rn];
> -	register unsigned long rmv asm("r3") = regs->uregs[rm];
> -
> -	__asm__ __volatile__ (
> -		BLX("%[fn]")
> -		: "=r" (rtv), "=r" (rnv)
> -		: "1" (rnv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
> -		: "lr", "memory", "cc"
> -	);
> -
> -	if (rt == 15)
> -		load_write_pc(rtv, regs);
> -	else
> -		regs->uregs[rt] = rtv;
> -
> -	if (is_writeback(insn))
> -		regs->uregs[rn] = rnv;
> -}
> -
> -static void __kprobes
> -emulate_str(struct kprobe *p, struct pt_regs *regs)
> -{
> -	kprobe_opcode_t insn = p->opcode;
> -	unsigned long rtpc = (unsigned long)p->addr + str_pc_offset;
> -	unsigned long rnpc = (unsigned long)p->addr + 8;
> -	int rt = (insn >> 12) & 0xf;
> -	int rn = (insn >> 16) & 0xf;
> -	int rm = insn & 0xf;
> -
> -	register unsigned long rtv asm("r0") = (rt == 15) ? rtpc
> -							  : regs->uregs[rt];
> -	register unsigned long rnv asm("r2") = (rn == 15) ? rnpc
> -							  : regs->uregs[rn];
> -	register unsigned long rmv asm("r3") = regs->uregs[rm];
> -
> -	__asm__ __volatile__ (
> -		BLX("%[fn]")
> -		: "=r" (rnv)
> -		: "r" (rtv), "0" (rnv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
> -		: "lr", "memory", "cc"
> -	);
> -
> -	if (is_writeback(insn))
> -		regs->uregs[rn] = rnv;
> -}
> -
> -static void __kprobes
> -emulate_rd12rn16rm0rs8_rwflags(struct kprobe *p, struct pt_regs *regs)
> -{
> -	kprobe_opcode_t insn = p->opcode;
> -	unsigned long pc = (unsigned long)p->addr + 8;
> -	int rd = (insn >> 12) & 0xf;
> -	int rn = (insn >> 16) & 0xf;
> -	int rm = insn & 0xf;
> -	int rs = (insn >> 8) & 0xf;
> -
> -	register unsigned long rdv asm("r0") = regs->uregs[rd];
> -	register unsigned long rnv asm("r2") = (rn == 15) ? pc
> -							  : regs->uregs[rn];
> -	register unsigned long rmv asm("r3") = (rm == 15) ? pc
> -							  : regs->uregs[rm];
> -	register unsigned long rsv asm("r1") = regs->uregs[rs];
> -	unsigned long cpsr = regs->ARM_cpsr;
> -
> -	__asm__ __volatile__ (
> -		"msr	cpsr_fs, %[cpsr]	\n\t"
> -		BLX("%[fn]")
> -		"mrs	%[cpsr], cpsr		\n\t"
> -		: "=r" (rdv), [cpsr] "=r" (cpsr)
> -		: "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv),
> -		  "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
> -		: "lr", "memory", "cc"
> -	);
> -
> -	if (rd == 15)
> -		alu_write_pc(rdv, regs);
> -	else
> -		regs->uregs[rd] = rdv;
> -	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
> -}
> -
> -static void __kprobes
> -emulate_rd12rn16rm0_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
> -{
> -	kprobe_opcode_t insn = p->opcode;
> -	int rd = (insn >> 12) & 0xf;
> -	int rn = (insn >> 16) & 0xf;
> -	int rm = insn & 0xf;
> -
> -	register unsigned long rdv asm("r0") = regs->uregs[rd];
> -	register unsigned long rnv asm("r2") = regs->uregs[rn];
> -	register unsigned long rmv asm("r3") = regs->uregs[rm];
> -	unsigned long cpsr = regs->ARM_cpsr;
> -
> -	__asm__ __volatile__ (
> -		"msr	cpsr_fs, %[cpsr]	\n\t"
> -		BLX("%[fn]")
> -		"mrs	%[cpsr], cpsr		\n\t"
> -		: "=r" (rdv), [cpsr] "=r" (cpsr)
> -		: "0" (rdv), "r" (rnv), "r" (rmv),
> -		  "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
> -		: "lr", "memory", "cc"
> -	);
> -
> -	regs->uregs[rd] = rdv;
> -	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
> -}
> -
> -static void __kprobes
> -emulate_rd16rn12rm0rs8_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
> -{
> -	kprobe_opcode_t insn = p->opcode;
> -	int rd = (insn >> 16) & 0xf;
> -	int rn = (insn >> 12) & 0xf;
> -	int rm = insn & 0xf;
> -	int rs = (insn >> 8) & 0xf;
> -
> -	register unsigned long rdv asm("r2") = regs->uregs[rd];
> -	register unsigned long rnv asm("r0") = regs->uregs[rn];
> -	register unsigned long rmv asm("r3") = regs->uregs[rm];
> -	register unsigned long rsv asm("r1") = regs->uregs[rs];
> -	unsigned long cpsr = regs->ARM_cpsr;
> -
> -	__asm__ __volatile__ (
> -		"msr	cpsr_fs, %[cpsr]	\n\t"
> -		BLX("%[fn]")
> -		"mrs	%[cpsr], cpsr		\n\t"
> -		: "=r" (rdv), [cpsr] "=r" (cpsr)
> -		: "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv),
> -		  "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
> -		: "lr", "memory", "cc"
> -	);
> -
> -	regs->uregs[rd] = rdv;
> -	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
> -}
> -
> -static void __kprobes
> -emulate_rd12rm0_noflags_nopc(struct kprobe *p, struct pt_regs *regs)
> -{
> -	kprobe_opcode_t insn = p->opcode;
> -	int rd = (insn >> 12) & 0xf;
> -	int rm = insn & 0xf;
> -
> -	register unsigned long rdv asm("r0") = regs->uregs[rd];
> -	register unsigned long rmv asm("r3") = regs->uregs[rm];
> -
> -	__asm__ __volatile__ (
> -		BLX("%[fn]")
> -		: "=r" (rdv)
> -		: "0" (rdv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
> -		: "lr", "memory", "cc"
> -	);
> -
> -	regs->uregs[rd] = rdv;
> -}
> -
> -static void __kprobes
> -emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
> -{
> -	kprobe_opcode_t insn = p->opcode;
> -	int rdlo = (insn >> 12) & 0xf;
> -	int rdhi = (insn >> 16) & 0xf;
> -	int rn = insn & 0xf;
> -	int rm = (insn >> 8) & 0xf;
> -
> -	register unsigned long rdlov asm("r0") = regs->uregs[rdlo];
> -	register unsigned long rdhiv asm("r2") = regs->uregs[rdhi];
> -	register unsigned long rnv asm("r3") = regs->uregs[rn];
> -	register unsigned long rmv asm("r1") = regs->uregs[rm];
> -	unsigned long cpsr = regs->ARM_cpsr;
> -
> -	__asm__ __volatile__ (
> -		"msr	cpsr_fs, %[cpsr]	\n\t"
> -		BLX("%[fn]")
> -		"mrs	%[cpsr], cpsr		\n\t"
> -		: "=r" (rdlov), "=r" (rdhiv), [cpsr] "=r" (cpsr)
> -		: "0" (rdlov), "1" (rdhiv), "r" (rnv), "r" (rmv),
> -		  "2" (cpsr), [fn] "r" (p->ainsn.insn_fn)
> -		: "lr", "memory", "cc"
> -	);
> -
> -	regs->uregs[rdlo] = rdlov;
> -	regs->uregs[rdhi] = rdhiv;
> -	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
> -}
> -
>  /*
>   * For the instruction masking and comparisons in all the "space_*"
>   * functions below, Do _not_ rearrange the order of tests unless
> @@ -400,13 +112,13 @@ static const union decode_item arm_1111_table[] = {
>  	/* PLDI (immediate)	1111 0100 x101 xxxx xxxx xxxx xxxx xxxx */
>  	/* PLDW (immediate)	1111 0101 x001 xxxx xxxx xxxx xxxx xxxx */
>  	/* PLD (immediate)	1111 0101 x101 xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_SIMULATE	(0xfe300000, 0xf4100000, kprobe_simulate_nop),
> +	DECODE_SIMULATE	(0xfe300000, 0xf4100000, probes_simulate_nop),
>  
>  	/* memory hint		1111 0110 x001 xxxx xxxx xxxx xxx0 xxxx */
>  	/* PLDI (register)	1111 0110 x101 xxxx xxxx xxxx xxx0 xxxx */
>  	/* PLDW (register)	1111 0111 x001 xxxx xxxx xxxx xxx0 xxxx */
>  	/* PLD (register)	1111 0111 x101 xxxx xxxx xxxx xxx0 xxxx */
> -	DECODE_SIMULATE	(0xfe300010, 0xf6100000, kprobe_simulate_nop),
> +	DECODE_SIMULATE	(0xfe300010, 0xf6100000, probes_simulate_nop),
>  
>  	/* BLX (immediate)	1111 101x xxxx xxxx xxxx xxxx xxxx xxxx */
>  	DECODE_SIMULATE	(0xfe000000, 0xfa000000, simulate_blx1),
> @@ -649,11 +361,11 @@ static const union decode_item arm_cccc_001x_table[] = {
>  	/* YIELD		cccc 0011 0010 0000 xxxx xxxx 0000 0001 */
>  	DECODE_OR	(0x0fff00ff, 0x03200001),
>  	/* SEV			cccc 0011 0010 0000 xxxx xxxx 0000 0100 */
> -	DECODE_EMULATE	(0x0fff00ff, 0x03200004, kprobe_emulate_none),
> +	DECODE_EMULATE	(0x0fff00ff, 0x03200004, probes_emulate_none),
>  	/* NOP			cccc 0011 0010 0000 xxxx xxxx 0000 0000 */
>  	/* WFE			cccc 0011 0010 0000 xxxx xxxx 0000 0010 */
>  	/* WFI			cccc 0011 0010 0000 xxxx xxxx 0000 0011 */
> -	DECODE_SIMULATE	(0x0fff00fc, 0x03200000, kprobe_simulate_nop),
> +	DECODE_SIMULATE	(0x0fff00fc, 0x03200000, probes_simulate_nop),
>  	/* DBG			cccc 0011 0010 0000 xxxx xxxx ffff xxxx */
>  	/* unallocated hints	cccc 0011 0010 0000 xxxx xxxx xxxx xxxx */
>  	/* MSR (immediate)	cccc 0011 0x10 xxxx xxxx xxxx xxxx xxxx */
> diff --git a/arch/arm/kernel/kprobes-common.c b/arch/arm/kernel/kprobes-common.c
> index 18a7628..b66e9f7 100644
> --- a/arch/arm/kernel/kprobes-common.c
> +++ b/arch/arm/kernel/kprobes-common.c
> @@ -173,15 +173,6 @@ kprobe_check_cc * const kprobe_condition_checks[16] = {
>  };
>  
> 
> -void __kprobes kprobe_simulate_nop(struct kprobe *p, struct pt_regs *regs)
> -{
> -}
> -
> -void __kprobes kprobe_emulate_none(struct kprobe *p, struct pt_regs *regs)
> -{
> -	p->ainsn.insn_fn();
> -}
> -
>  static void __kprobes simulate_ldm1stm1(struct kprobe *p, struct pt_regs *regs)
>  {
>  	kprobe_opcode_t insn = p->opcode;
> @@ -319,260 +310,3 @@ kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi)
>  	return INSN_GOOD_NO_SLOT;
>  }
>  
> -
> -/*
> - * Prepare an instruction slot to receive an instruction for emulating.
> - * This is done by placing a subroutine return after the location where the
> - * instruction will be placed. We also modify ARM instructions to be
> - * unconditional as the condition code will already be checked before any
> - * emulation handler is called.
> - */
> -static kprobe_opcode_t __kprobes
> -prepare_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> -								bool thumb)
> -{
> -#ifdef CONFIG_THUMB2_KERNEL
> -	if (thumb) {
> -		u16 *thumb_insn = (u16 *)asi->insn;
> -		thumb_insn[1] = 0x4770; /* Thumb bx lr */
> -		thumb_insn[2] = 0x4770; /* Thumb bx lr */
> -		return insn;
> -	}
> -	asi->insn[1] = 0xe12fff1e; /* ARM bx lr */
> -#else
> -	asi->insn[1] = 0xe1a0f00e; /* mov pc, lr */
> -#endif
> -	/* Make an ARM instruction unconditional */
> -	if (insn < 0xe0000000)
> -		insn = (insn | 0xe0000000) & ~0x10000000;
> -	return insn;
> -}
> -
> -/*
> - * Write a (probably modified) instruction into the slot previously prepared by
> - * prepare_emulated_insn
> - */
> -static void  __kprobes
> -set_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> -								bool thumb)
> -{
> -#ifdef CONFIG_THUMB2_KERNEL
> -	if (thumb) {
> -		u16 *ip = (u16 *)asi->insn;
> -		if (is_wide_instruction(insn))
> -			*ip++ = insn >> 16;
> -		*ip++ = insn;
> -		return;
> -	}
> -#endif
> -	asi->insn[0] = insn;
> -}
> -
> -/*
> - * When we modify the register numbers encoded in an instruction to be emulated,
> - * the new values come from this define. For ARM and 32-bit Thumb instructions
> - * this gives...
> - *
> - *	bit position	  16  12   8   4   0
> - *	---------------+---+---+---+---+---+
> - *	register	 r2  r0  r1  --  r3
> - */
> -#define INSN_NEW_BITS		0x00020103
> -
> -/* Each nibble has same value as that at INSN_NEW_BITS bit 16 */
> -#define INSN_SAMEAS16_BITS	0x22222222
> -
> -/*
> - * Validate and modify each of the registers encoded in an instruction.
> - *
> - * Each nibble in regs contains a value from enum decode_reg_type. For each
> - * non-zero value, the corresponding nibble in pinsn is validated and modified
> - * according to the type.
> - */
> -static bool __kprobes decode_regs(kprobe_opcode_t* pinsn, u32 regs)
> -{
> -	kprobe_opcode_t insn = *pinsn;
> -	kprobe_opcode_t mask = 0xf; /* Start at least significant nibble */
> -
> -	for (; regs != 0; regs >>= 4, mask <<= 4) {
> -
> -		kprobe_opcode_t new_bits = INSN_NEW_BITS;
> -
> -		switch (regs & 0xf) {
> -
> -		case REG_TYPE_NONE:
> -			/* Nibble not a register, skip to next */
> -			continue;
> -
> -		case REG_TYPE_ANY:
> -			/* Any register is allowed */
> -			break;
> -
> -		case REG_TYPE_SAMEAS16:
> -			/* Replace register with same as at bit position 16 */
> -			new_bits = INSN_SAMEAS16_BITS;
> -			break;
> -
> -		case REG_TYPE_SP:
> -			/* Only allow SP (R13) */
> -			if ((insn ^ 0xdddddddd) & mask)
> -				goto reject;
> -			break;
> -
> -		case REG_TYPE_PC:
> -			/* Only allow PC (R15) */
> -			if ((insn ^ 0xffffffff) & mask)
> -				goto reject;
> -			break;
> -
> -		case REG_TYPE_NOSP:
> -			/* Reject SP (R13) */
> -			if (((insn ^ 0xdddddddd) & mask) == 0)
> -				goto reject;
> -			break;
> -
> -		case REG_TYPE_NOSPPC:
> -		case REG_TYPE_NOSPPCX:
> -			/* Reject SP and PC (R13 and R15) */
> -			if (((insn ^ 0xdddddddd) & 0xdddddddd & mask) == 0)
> -				goto reject;
> -			break;
> -
> -		case REG_TYPE_NOPCWB:
> -			if (!is_writeback(insn))
> -				break; /* No writeback, so any register is OK */
> -			/* fall through... */
> -		case REG_TYPE_NOPC:
> -		case REG_TYPE_NOPCX:
> -			/* Reject PC (R15) */
> -			if (((insn ^ 0xffffffff) & mask) == 0)
> -				goto reject;
> -			break;
> -		}
> -
> -		/* Replace value of nibble with new register number... */
> -		insn &= ~mask;
> -		insn |= new_bits & mask;
> -	}
> -
> -	*pinsn = insn;
> -	return true;
> -
> -reject:
> -	return false;
> -}
> -
> -static const int decode_struct_sizes[NUM_DECODE_TYPES] = {
> -	[DECODE_TYPE_TABLE]	= sizeof(struct decode_table),
> -	[DECODE_TYPE_CUSTOM]	= sizeof(struct decode_custom),
> -	[DECODE_TYPE_SIMULATE]	= sizeof(struct decode_simulate),
> -	[DECODE_TYPE_EMULATE]	= sizeof(struct decode_emulate),
> -	[DECODE_TYPE_OR]	= sizeof(struct decode_or),
> -	[DECODE_TYPE_REJECT]	= sizeof(struct decode_reject)
> -};
> -
> -/*
> - * kprobe_decode_insn operates on data tables in order to decode an ARM
> - * architecture instruction onto which a kprobe has been placed.
> - *
> - * These instruction decoding tables are a concatenation of entries each
> - * of which consist of one of the following structs:
> - *
> - *	decode_table
> - *	decode_custom
> - *	decode_simulate
> - *	decode_emulate
> - *	decode_or
> - *	decode_reject
> - *
> - * Each of these starts with a struct decode_header which has the following
> - * fields:
> - *
> - *	type_regs
> - *	mask
> - *	value
> - *
> - * The least significant DECODE_TYPE_BITS of type_regs contains a value
> - * from enum decode_type, this indicates which of the decode_* structs
> - * the entry contains. The value DECODE_TYPE_END indicates the end of the
> - * table.
> - *
> - * When the table is parsed, each entry is checked in turn to see if it
> - * matches the instruction to be decoded using the test:
> - *
> - *	(insn & mask) == value
> - *
> - * If no match is found before the end of the table is reached then decoding
> - * fails with INSN_REJECTED.
> - *
> - * When a match is found, decode_regs() is called to validate and modify each
> - * of the registers encoded in the instruction; the data it uses to do this
> - * is (type_regs >> DECODE_TYPE_BITS). A validation failure will cause decoding
> - * to fail with INSN_REJECTED.
> - *
> - * Once the instruction has passed the above tests, further processing
> - * depends on the type of the table entry's decode struct.
> - *
> - */
> -int __kprobes
> -kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> -				const union decode_item *table, bool thumb)
> -{
> -	const struct decode_header *h = (struct decode_header *)table;
> -	const struct decode_header *next;
> -	bool matched = false;
> -
> -	insn = prepare_emulated_insn(insn, asi, thumb);
> -
> -	for (;; h = next) {
> -		enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK;
> -		u32 regs = h->type_regs.bits >> DECODE_TYPE_BITS;
> -
> -		if (type == DECODE_TYPE_END)
> -			return INSN_REJECTED;
> -
> -		next = (struct decode_header *)
> -				((uintptr_t)h + decode_struct_sizes[type]);
> -
> -		if (!matched && (insn & h->mask.bits) != h->value.bits)
> -			continue;
> -
> -		if (!decode_regs(&insn, regs))
> -			return INSN_REJECTED;
> -
> -		switch (type) {
> -
> -		case DECODE_TYPE_TABLE: {
> -			struct decode_table *d = (struct decode_table *)h;
> -			next = (struct decode_header *)d->table.table;
> -			break;
> -		}
> -
> -		case DECODE_TYPE_CUSTOM: {
> -			struct decode_custom *d = (struct decode_custom *)h;
> -			return (*d->decoder.decoder)(insn, asi);
> -		}
> -
> -		case DECODE_TYPE_SIMULATE: {
> -			struct decode_simulate *d = (struct decode_simulate *)h;
> -			asi->insn_handler = d->handler.handler;
> -			return INSN_GOOD_NO_SLOT;
> -		}
> -
> -		case DECODE_TYPE_EMULATE: {
> -			struct decode_emulate *d = (struct decode_emulate *)h;
> -			asi->insn_handler = d->handler.handler;
> -			set_emulated_insn(insn, asi, thumb);
> -			return INSN_GOOD;
> -		}
> -
> -		case DECODE_TYPE_OR:
> -			matched = true;
> -			break;
> -
> -		case DECODE_TYPE_REJECT:
> -		default:
> -			return INSN_REJECTED;
> -		}
> -		}
> -	}
> diff --git a/arch/arm/kernel/kprobes-thumb.c b/arch/arm/kernel/kprobes-thumb.c
> index 6123daf..173b2bc 100644
> --- a/arch/arm/kernel/kprobes-thumb.c
> +++ b/arch/arm/kernel/kprobes-thumb.c
> @@ -544,11 +544,11 @@ static const union decode_item t32_table_1111_0xxx___1[] = {
>  	/* YIELD		1111 0011 1010 xxxx 10x0 x000 0000 0001 */
>  	DECODE_OR	(0xfff0d7ff, 0xf3a08001),
>  	/* SEV			1111 0011 1010 xxxx 10x0 x000 0000 0100 */
> -	DECODE_EMULATE	(0xfff0d7ff, 0xf3a08004, kprobe_emulate_none),
> +	DECODE_EMULATE	(0xfff0d7ff, 0xf3a08004, probes_emulate_none),
>  	/* NOP			1111 0011 1010 xxxx 10x0 x000 0000 0000 */
>  	/* WFE			1111 0011 1010 xxxx 10x0 x000 0000 0010 */
>  	/* WFI			1111 0011 1010 xxxx 10x0 x000 0000 0011 */
> -	DECODE_SIMULATE	(0xfff0d7fc, 0xf3a08000, kprobe_simulate_nop),
> +	DECODE_SIMULATE	(0xfff0d7fc, 0xf3a08000, probes_simulate_nop),
>  
>  	/* MRS Rd, CPSR		1111 0011 1110 xxxx 10x0 xxxx xxxx xxxx */
>  	DECODE_SIMULATEX(0xfff0d000, 0xf3e08000, t32_simulate_mrs,
> @@ -589,7 +589,7 @@ static const union decode_item t32_table_1111_100x_x0x1__1111[] = {
>  
>  	/* PLD (literal)	1111 1000 x001 1111 1111 xxxx xxxx xxxx */
>  	/* PLI (literal)	1111 1001 x001 1111 1111 xxxx xxxx xxxx */
> -	DECODE_SIMULATE	(0xfe7ff000, 0xf81ff000, kprobe_simulate_nop),
> +	DECODE_SIMULATE	(0xfe7ff000, 0xf81ff000, probes_simulate_nop),
>  
>  	/* PLD{W} (immediate)	1111 1000 10x1 xxxx 1111 xxxx xxxx xxxx */
>  	DECODE_OR	(0xffd0f000, 0xf890f000),
> @@ -598,13 +598,13 @@ static const union decode_item t32_table_1111_100x_x0x1__1111[] = {
>  	/* PLI (immediate)	1111 1001 1001 xxxx 1111 xxxx xxxx xxxx */
>  	DECODE_OR	(0xfff0f000, 0xf990f000),
>  	/* PLI (immediate)	1111 1001 0001 xxxx 1111 1100 xxxx xxxx */
> -	DECODE_SIMULATEX(0xfff0ff00, 0xf910fc00, kprobe_simulate_nop,
> +	DECODE_SIMULATEX(0xfff0ff00, 0xf910fc00, probes_simulate_nop,
>  						 REGS(NOPCX, 0, 0, 0, 0)),
>  
>  	/* PLD{W} (register)	1111 1000 00x1 xxxx 1111 0000 00xx xxxx */
>  	DECODE_OR	(0xffd0ffc0, 0xf810f000),
>  	/* PLI (register)	1111 1001 0001 xxxx 1111 0000 00xx xxxx */
> -	DECODE_SIMULATEX(0xfff0ffc0, 0xf910f000, kprobe_simulate_nop,
> +	DECODE_SIMULATEX(0xfff0ffc0, 0xf910f000, probes_simulate_nop,
>  						 REGS(NOPCX, 0, 0, 0, NOSPPC)),
>  
>  	/* Other unallocated instructions...				*/
> @@ -1274,11 +1274,11 @@ static const union decode_item t16_table_1011[] = {
>  	/* YIELD			1011 1111 0001 0000 */
>  	DECODE_OR	(0xffff, 0xbf10),
>  	/* SEV				1011 1111 0100 0000 */
> -	DECODE_EMULATE	(0xffff, 0xbf40, kprobe_emulate_none),
> +	DECODE_EMULATE	(0xffff, 0xbf40, probes_emulate_none),
>  	/* NOP				1011 1111 0000 0000 */
>  	/* WFE				1011 1111 0010 0000 */
>  	/* WFI				1011 1111 0011 0000 */
> -	DECODE_SIMULATE	(0xffcf, 0xbf00, kprobe_simulate_nop),
> +	DECODE_SIMULATE	(0xffcf, 0xbf00, probes_simulate_nop),
>  	/* Unassigned hints		1011 1111 xxxx 0000 */
>  	DECODE_REJECT	(0xff0f, 0xbf00),
>  	/* IT				1011 1111 xxxx xxxx */
> diff --git a/arch/arm/kernel/kprobes.h b/arch/arm/kernel/kprobes.h
> index 38945f7..9aa2f15 100644
> --- a/arch/arm/kernel/kprobes.h
> +++ b/arch/arm/kernel/kprobes.h
> @@ -161,8 +161,8 @@ static inline void __kprobes alu_write_pc(long pcv, struct pt_regs *regs)
>  }
>  
> 
> -void __kprobes kprobe_simulate_nop(struct kprobe *p, struct pt_regs *regs);
> -void __kprobes kprobe_emulate_none(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes probes_simulate_nop(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes probes_emulate_none(struct kprobe *p, struct pt_regs *regs);
>  
>  enum kprobe_insn __kprobes
>  kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi);
> diff --git a/arch/arm/kernel/probes-arm.c b/arch/arm/kernel/probes-arm.c
> new file mode 100644
> index 0000000..e1b1a6e
> --- /dev/null
> +++ b/arch/arm/kernel/probes-arm.c
> @@ -0,0 +1,311 @@
> +/*
> + * arch/arm/kernel/probes-arm.c
> + *
> + * Some code moved here from arch/arm/kernel/kprobes-arm.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.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/kprobes.h>
> +
> +#include "probes.h"
> +#include "kprobes.h"
> +
> +#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
> +
> +#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25)
> +
> +#if  __LINUX_ARM_ARCH__ >= 6
> +#define BLX(reg)	"blx	"reg"		\n\t"
> +#else
> +#define BLX(reg)	"mov	lr, pc		\n\t"	\
> +			"mov	pc, "reg"	\n\t"
> +#endif
> +
> +void __kprobes simulate_bbl(struct kprobe *p, struct pt_regs *regs)
> +{
> +	kprobe_opcode_t insn = p->opcode;
> +	long iaddr = (long)p->addr;
> +	int disp  = branch_displacement(insn);
> +
> +	if (insn & (1 << 24))
> +		regs->ARM_lr = iaddr + 4;
> +
> +	regs->ARM_pc = iaddr + 8 + disp;
> +}
> +
> +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;
> +}
> +
> +void __kprobes simulate_blx2bx(struct kprobe *p, struct pt_regs *regs)
> +{
> +	kprobe_opcode_t insn = p->opcode;
> +	int rm = insn & 0xf;
> +	long rmv = regs->uregs[rm];
> +
> +	if (insn & (1 << 5))
> +		regs->ARM_lr = (long)p->addr + 4;
> +
> +	regs->ARM_pc = rmv & ~0x1;
> +	regs->ARM_cpsr &= ~PSR_T_BIT;
> +	if (rmv & 0x1)
> +		regs->ARM_cpsr |= PSR_T_BIT;
> +}
> +
> +void __kprobes simulate_mrs(struct kprobe *p, struct pt_regs *regs)
> +{
> +	kprobe_opcode_t insn = p->opcode;
> +	int rd = (insn >> 12) & 0xf;
> +	unsigned long mask = 0xf8ff03df; /* Mask out execution state */
> +	regs->uregs[rd] = regs->ARM_cpsr & mask;
> +}
> +
> +void __kprobes simulate_mov_ipsp(struct kprobe *p, struct pt_regs *regs)
> +{
> +	regs->uregs[12] = regs->uregs[13];
> +}
> +
> +void __kprobes
> +emulate_ldrdstrd(struct kprobe *p, struct pt_regs *regs)
> +{
> +	kprobe_opcode_t insn = p->opcode;
> +	unsigned long pc = (unsigned long)p->addr + 8;
> +	int rt = (insn >> 12) & 0xf;
> +	int rn = (insn >> 16) & 0xf;
> +	int rm = insn & 0xf;
> +
> +	register unsigned long rtv asm("r0") = regs->uregs[rt];
> +	register unsigned long rt2v asm("r1") = regs->uregs[rt+1];
> +	register unsigned long rnv asm("r2") = (rn == 15) ? pc
> +							  : regs->uregs[rn];
> +	register unsigned long rmv asm("r3") = regs->uregs[rm];
> +
> +	__asm__ __volatile__ (
> +		BLX("%[fn]")
> +		: "=r" (rtv), "=r" (rt2v), "=r" (rnv)
> +		: "0" (rtv), "1" (rt2v), "2" (rnv), "r" (rmv),
> +		  [fn] "r" (p->ainsn.insn_fn)
> +		: "lr", "memory", "cc"
> +	);
> +
> +	regs->uregs[rt] = rtv;
> +	regs->uregs[rt+1] = rt2v;
> +	if (is_writeback(insn))
> +		regs->uregs[rn] = rnv;
> +}
> +
> +void __kprobes
> +emulate_ldr(struct kprobe *p, struct pt_regs *regs)
> +{
> +	kprobe_opcode_t insn = p->opcode;
> +	unsigned long pc = (unsigned long)p->addr + 8;
> +	int rt = (insn >> 12) & 0xf;
> +	int rn = (insn >> 16) & 0xf;
> +	int rm = insn & 0xf;
> +
> +	register unsigned long rtv asm("r0");
> +	register unsigned long rnv asm("r2") = (rn == 15) ? pc
> +							  : regs->uregs[rn];
> +	register unsigned long rmv asm("r3") = regs->uregs[rm];
> +
> +	__asm__ __volatile__ (
> +		BLX("%[fn]")
> +		: "=r" (rtv), "=r" (rnv)
> +		: "1" (rnv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
> +		: "lr", "memory", "cc"
> +	);
> +
> +	if (rt == 15)
> +		load_write_pc(rtv, regs);
> +	else
> +		regs->uregs[rt] = rtv;
> +
> +	if (is_writeback(insn))
> +		regs->uregs[rn] = rnv;
> +}
> +
> +void __kprobes
> +emulate_str(struct kprobe *p, struct pt_regs *regs)
> +{
> +	kprobe_opcode_t insn = p->opcode;
> +	unsigned long rtpc = (unsigned long)p->addr + str_pc_offset;
> +	unsigned long rnpc = (unsigned long)p->addr + 8;
> +	int rt = (insn >> 12) & 0xf;
> +	int rn = (insn >> 16) & 0xf;
> +	int rm = insn & 0xf;
> +
> +	register unsigned long rtv asm("r0") = (rt == 15) ? rtpc
> +							  : regs->uregs[rt];
> +	register unsigned long rnv asm("r2") = (rn == 15) ? rnpc
> +							  : regs->uregs[rn];
> +	register unsigned long rmv asm("r3") = regs->uregs[rm];
> +
> +	__asm__ __volatile__ (
> +		BLX("%[fn]")
> +		: "=r" (rnv)
> +		: "r" (rtv), "0" (rnv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
> +		: "lr", "memory", "cc"
> +	);
> +
> +	if (is_writeback(insn))
> +		regs->uregs[rn] = rnv;
> +}
> +
> +void __kprobes
> +emulate_rd12rn16rm0rs8_rwflags(struct kprobe *p, struct pt_regs *regs)
> +{
> +	kprobe_opcode_t insn = p->opcode;
> +	unsigned long pc = (unsigned long)p->addr + 8;
> +	int rd = (insn >> 12) & 0xf;
> +	int rn = (insn >> 16) & 0xf;
> +	int rm = insn & 0xf;
> +	int rs = (insn >> 8) & 0xf;
> +
> +	register unsigned long rdv asm("r0") = regs->uregs[rd];
> +	register unsigned long rnv asm("r2") = (rn == 15) ? pc
> +							  : regs->uregs[rn];
> +	register unsigned long rmv asm("r3") = (rm == 15) ? pc
> +							  : regs->uregs[rm];
> +	register unsigned long rsv asm("r1") = regs->uregs[rs];
> +	unsigned long cpsr = regs->ARM_cpsr;
> +
> +	__asm__ __volatile__ (
> +		"msr	cpsr_fs, %[cpsr]	\n\t"
> +		BLX("%[fn]")
> +		"mrs	%[cpsr], cpsr		\n\t"
> +		: "=r" (rdv), [cpsr] "=r" (cpsr)
> +		: "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv),
> +		  "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
> +		: "lr", "memory", "cc"
> +	);
> +
> +	if (rd == 15)
> +		alu_write_pc(rdv, regs);
> +	else
> +		regs->uregs[rd] = rdv;
> +	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
> +}
> +
> +void __kprobes
> +emulate_rd12rn16rm0_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
> +{
> +	kprobe_opcode_t insn = p->opcode;
> +	int rd = (insn >> 12) & 0xf;
> +	int rn = (insn >> 16) & 0xf;
> +	int rm = insn & 0xf;
> +
> +	register unsigned long rdv asm("r0") = regs->uregs[rd];
> +	register unsigned long rnv asm("r2") = regs->uregs[rn];
> +	register unsigned long rmv asm("r3") = regs->uregs[rm];
> +	unsigned long cpsr = regs->ARM_cpsr;
> +
> +	__asm__ __volatile__ (
> +		"msr	cpsr_fs, %[cpsr]	\n\t"
> +		BLX("%[fn]")
> +		"mrs	%[cpsr], cpsr		\n\t"
> +		: "=r" (rdv), [cpsr] "=r" (cpsr)
> +		: "0" (rdv), "r" (rnv), "r" (rmv),
> +		  "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
> +		: "lr", "memory", "cc"
> +	);
> +
> +	regs->uregs[rd] = rdv;
> +	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
> +}
> +
> +void __kprobes
> +emulate_rd16rn12rm0rs8_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
> +{
> +	kprobe_opcode_t insn = p->opcode;
> +	int rd = (insn >> 16) & 0xf;
> +	int rn = (insn >> 12) & 0xf;
> +	int rm = insn & 0xf;
> +	int rs = (insn >> 8) & 0xf;
> +
> +	register unsigned long rdv asm("r2") = regs->uregs[rd];
> +	register unsigned long rnv asm("r0") = regs->uregs[rn];
> +	register unsigned long rmv asm("r3") = regs->uregs[rm];
> +	register unsigned long rsv asm("r1") = regs->uregs[rs];
> +	unsigned long cpsr = regs->ARM_cpsr;
> +
> +	__asm__ __volatile__ (
> +		"msr	cpsr_fs, %[cpsr]	\n\t"
> +		BLX("%[fn]")
> +		"mrs	%[cpsr], cpsr		\n\t"
> +		: "=r" (rdv), [cpsr] "=r" (cpsr)
> +		: "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv),
> +		  "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
> +		: "lr", "memory", "cc"
> +	);
> +
> +	regs->uregs[rd] = rdv;
> +	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
> +}
> +
> +void __kprobes
> +emulate_rd12rm0_noflags_nopc(struct kprobe *p, struct pt_regs *regs)
> +{
> +	kprobe_opcode_t insn = p->opcode;
> +	int rd = (insn >> 12) & 0xf;
> +	int rm = insn & 0xf;
> +
> +	register unsigned long rdv asm("r0") = regs->uregs[rd];
> +	register unsigned long rmv asm("r3") = regs->uregs[rm];
> +
> +	__asm__ __volatile__ (
> +		BLX("%[fn]")
> +		: "=r" (rdv)
> +		: "0" (rdv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
> +		: "lr", "memory", "cc"
> +	);
> +
> +	regs->uregs[rd] = rdv;
> +}
> +
> +void __kprobes
> +emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
> +{
> +	kprobe_opcode_t insn = p->opcode;
> +	int rdlo = (insn >> 12) & 0xf;
> +	int rdhi = (insn >> 16) & 0xf;
> +	int rn = insn & 0xf;
> +	int rm = (insn >> 8) & 0xf;
> +
> +	register unsigned long rdlov asm("r0") = regs->uregs[rdlo];
> +	register unsigned long rdhiv asm("r2") = regs->uregs[rdhi];
> +	register unsigned long rnv asm("r3") = regs->uregs[rn];
> +	register unsigned long rmv asm("r1") = regs->uregs[rm];
> +	unsigned long cpsr = regs->ARM_cpsr;
> +
> +	__asm__ __volatile__ (
> +		"msr	cpsr_fs, %[cpsr]	\n\t"
> +		BLX("%[fn]")
> +		"mrs	%[cpsr], cpsr		\n\t"
> +		: "=r" (rdlov), "=r" (rdhiv), [cpsr] "=r" (cpsr)
> +		: "0" (rdlov), "1" (rdhiv), "r" (rnv), "r" (rmv),
> +		  "2" (cpsr), [fn] "r" (p->ainsn.insn_fn)
> +		: "lr", "memory", "cc"
> +	);
> +
> +	regs->uregs[rdlo] = rdlov;
> +	regs->uregs[rdhi] = rdhiv;
> +	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
> +}
> diff --git a/arch/arm/kernel/probes.c b/arch/arm/kernel/probes.c
> new file mode 100644
> index 0000000..86c63f3
> --- /dev/null
> +++ b/arch/arm/kernel/probes.c
> @@ -0,0 +1,286 @@
> +/*
> + * arch/arm/kernel/probes.c
> + *
> + * Some contents moved here from arch/arm/include/asm/kprobes-common.c
> + *
> + * Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
> + *
> + * Some contents moved here from arch/arm/include/asm/kprobes-arm.c which is
> + * 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.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/kprobes.h>
> +
> +#include "probes.h"
> +#include "kprobes.h"
> +
> +void __kprobes probes_simulate_nop(struct kprobe *p, struct pt_regs *regs)
> +{
> +}
> +
> +void __kprobes probes_emulate_none(struct kprobe *p, struct pt_regs *regs)
> +{
> +	p->ainsn.insn_fn();
> +}
> +
> +/*
> + * Prepare an instruction slot to receive an instruction for emulating.
> + * This is done by placing a subroutine return after the location where the
> + * instruction will be placed. We also modify ARM instructions to be
> + * unconditional as the condition code will already be checked before any
> + * emulation handler is called.
> + */
> +static kprobe_opcode_t __kprobes
> +prepare_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> +								bool thumb)
> +{
> +#ifdef CONFIG_THUMB2_KERNEL
> +	if (thumb) {
> +		u16 *thumb_insn = (u16 *)asi->insn;
> +		thumb_insn[1] = 0x4770; /* Thumb bx lr */
> +		thumb_insn[2] = 0x4770; /* Thumb bx lr */
> +		return insn;
> +	}
> +	asi->insn[1] = 0xe12fff1e; /* ARM bx lr */
> +#else
> +	asi->insn[1] = 0xe1a0f00e; /* mov pc, lr */
> +#endif
> +	/* Make an ARM instruction unconditional */
> +	if (insn < 0xe0000000)
> +		insn = (insn | 0xe0000000) & ~0x10000000;
> +	return insn;
> +}
> +
> +/*
> + * Write a (probably modified) instruction into the slot previously prepared by
> + * prepare_emulated_insn
> + */
> +static void  __kprobes
> +set_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> +								bool thumb)
> +{
> +#ifdef CONFIG_THUMB2_KERNEL
> +	if (thumb) {
> +		u16 *ip = (u16 *)asi->insn;
> +		if (is_wide_instruction(insn))
> +			*ip++ = insn >> 16;
> +		*ip++ = insn;
> +		return;
> +	}
> +#endif
> +	asi->insn[0] = insn;
> +}
> +
> +/*
> + * When we modify the register numbers encoded in an instruction to be emulated,
> + * the new values come from this define. For ARM and 32-bit Thumb instructions
> + * this gives...
> + *
> + *	bit position	  16  12   8   4   0
> + *	---------------+---+---+---+---+---+
> + *	register	 r2  r0  r1  --  r3
> + */
> +#define INSN_NEW_BITS		0x00020103
> +
> +/* Each nibble has same value as that at INSN_NEW_BITS bit 16 */
> +#define INSN_SAMEAS16_BITS	0x22222222
> +
> +/*
> + * Validate and modify each of the registers encoded in an instruction.
> + *
> + * Each nibble in regs contains a value from enum decode_reg_type. For each
> + * non-zero value, the corresponding nibble in pinsn is validated and modified
> + * according to the type.
> + */
> +static bool __kprobes decode_regs(kprobe_opcode_t *pinsn, u32 regs)
> +{
> +	kprobe_opcode_t insn = *pinsn;
> +	kprobe_opcode_t mask = 0xf; /* Start at least significant nibble */
> +
> +	for (; regs != 0; regs >>= 4, mask <<= 4) {
> +
> +		kprobe_opcode_t new_bits = INSN_NEW_BITS;
> +
> +		switch (regs & 0xf) {
> +
> +		case REG_TYPE_NONE:
> +			/* Nibble not a register, skip to next */
> +			continue;
> +
> +		case REG_TYPE_ANY:
> +			/* Any register is allowed */
> +			break;
> +
> +		case REG_TYPE_SAMEAS16:
> +			/* Replace register with same as at bit position 16 */
> +			new_bits = INSN_SAMEAS16_BITS;
> +			break;
> +
> +		case REG_TYPE_SP:
> +			/* Only allow SP (R13) */
> +			if ((insn ^ 0xdddddddd) & mask)
> +				goto reject;
> +			break;
> +
> +		case REG_TYPE_PC:
> +			/* Only allow PC (R15) */
> +			if ((insn ^ 0xffffffff) & mask)
> +				goto reject;
> +			break;
> +
> +		case REG_TYPE_NOSP:
> +			/* Reject SP (R13) */
> +			if (((insn ^ 0xdddddddd) & mask) == 0)
> +				goto reject;
> +			break;
> +
> +		case REG_TYPE_NOSPPC:
> +		case REG_TYPE_NOSPPCX:
> +			/* Reject SP and PC (R13 and R15) */
> +			if (((insn ^ 0xdddddddd) & 0xdddddddd & mask) == 0)
> +				goto reject;
> +			break;
> +
> +		case REG_TYPE_NOPCWB:
> +			if (!is_writeback(insn))
> +				break; /* No writeback, so any register is OK */
> +			/* fall through... */
> +		case REG_TYPE_NOPC:
> +		case REG_TYPE_NOPCX:
> +			/* Reject PC (R15) */
> +			if (((insn ^ 0xffffffff) & mask) == 0)
> +				goto reject;
> +			break;
> +		}
> +
> +		/* Replace value of nibble with new register number... */
> +		insn &= ~mask;
> +		insn |= new_bits & mask;
> +	}
> +
> +	*pinsn = insn;
> +	return true;
> +
> +reject:
> +	return false;
> +}
> +
> +static const int decode_struct_sizes[NUM_DECODE_TYPES] = {
> +	[DECODE_TYPE_TABLE]	= sizeof(struct decode_table),
> +	[DECODE_TYPE_CUSTOM]	= sizeof(struct decode_custom),
> +	[DECODE_TYPE_SIMULATE]	= sizeof(struct decode_simulate),
> +	[DECODE_TYPE_EMULATE]	= sizeof(struct decode_emulate),
> +	[DECODE_TYPE_OR]	= sizeof(struct decode_or),
> +	[DECODE_TYPE_REJECT]	= sizeof(struct decode_reject)
> +};
> +
> +/*
> + * kprobe_decode_insn operates on data tables in order to decode an ARM
> + * architecture instruction onto which a kprobe has been placed.
> + *
> + * These instruction decoding tables are a concatenation of entries each
> + * of which consist of one of the following structs:
> + *
> + *	decode_table
> + *	decode_custom
> + *	decode_simulate
> + *	decode_emulate
> + *	decode_or
> + *	decode_reject
> + *
> + * Each of these starts with a struct decode_header which has the following
> + * fields:
> + *
> + *	type_regs
> + *	mask
> + *	value
> + *
> + * The least significant DECODE_TYPE_BITS of type_regs contains a value
> + * from enum decode_type, this indicates which of the decode_* structs
> + * the entry contains. The value DECODE_TYPE_END indicates the end of the
> + * table.
> + *
> + * When the table is parsed, each entry is checked in turn to see if it
> + * matches the instruction to be decoded using the test:
> + *
> + *	(insn & mask) == value
> + *
> + * If no match is found before the end of the table is reached then decoding
> + * fails with INSN_REJECTED.
> + *
> + * When a match is found, decode_regs() is called to validate and modify each
> + * of the registers encoded in the instruction; the data it uses to do this
> + * is (type_regs >> DECODE_TYPE_BITS). A validation failure will cause decoding
> + * to fail with INSN_REJECTED.
> + *
> + * Once the instruction has passed the above tests, further processing
> + * depends on the type of the table entry's decode struct.
> + *
> + */
> +int __kprobes
> +kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> +				const union decode_item *table, bool thumb)
> +{
> +	const struct decode_header *h = (struct decode_header *)table;
> +	const struct decode_header *next;
> +	bool matched = false;
> +
> +	insn = prepare_emulated_insn(insn, asi, thumb);
> +
> +	for (;; h = next) {
> +		enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK;
> +		u32 regs = h->type_regs.bits >> DECODE_TYPE_BITS;
> +
> +		if (type == DECODE_TYPE_END)
> +			return INSN_REJECTED;
> +
> +		next = (struct decode_header *)
> +				((uintptr_t)h + decode_struct_sizes[type]);
> +
> +		if (!matched && (insn & h->mask.bits) != h->value.bits)
> +			continue;
> +
> +		if (!decode_regs(&insn, regs))
> +			return INSN_REJECTED;
> +
> +		switch (type) {
> +
> +		case DECODE_TYPE_TABLE: {
> +			struct decode_table *d = (struct decode_table *)h;
> +			next = (struct decode_header *)d->table.table;
> +			break;
> +		}
> +
> +		case DECODE_TYPE_CUSTOM: {
> +			struct decode_custom *d = (struct decode_custom *)h;
> +			return (*d->decoder.decoder)(insn, asi);
> +		}
> +
> +		case DECODE_TYPE_SIMULATE: {
> +			struct decode_simulate *d = (struct decode_simulate *)h;
> +			asi->insn_handler = d->handler.handler;
> +			return INSN_GOOD_NO_SLOT;
> +		}
> +
> +		case DECODE_TYPE_EMULATE: {
> +			struct decode_emulate *d = (struct decode_emulate *)h;
> +			asi->insn_handler = d->handler.handler;
> +			set_emulated_insn(insn, asi, thumb);
> +			return INSN_GOOD;
> +		}
> +
> +		case DECODE_TYPE_OR:
> +			matched = true;
> +			break;
> +
> +		case DECODE_TYPE_REJECT:
> +		default:
> +			return INSN_REJECTED;
> +		}
> +	}
> +}
> diff --git a/arch/arm/kernel/probes.h b/arch/arm/kernel/probes.h
> new file mode 100644
> index 0000000..56eec12
> --- /dev/null
> +++ b/arch/arm/kernel/probes.h
> @@ -0,0 +1,23 @@
> +#ifndef _ARM_KERNEL_PROBES_H
> +#define  _ARM_KERNEL_PROBES_H
> +
> +void __kprobes simulate_bbl(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes simulate_blx1(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes simulate_blx2bx(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes simulate_mrs(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes simulate_mov_ipsp(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes emulate_ldrdstrd(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes emulate_ldr(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes emulate_str(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes emulate_rd12rn16rm0rs8_rwflags(struct kprobe *p,
> +					      struct pt_regs *regs);
> +void __kprobes emulate_rd12rn16rm0_rwflags_nopc(struct kprobe *p,
> +						struct pt_regs *regs);
> +void __kprobes emulate_rd16rn12rm0rs8_rwflags_nopc(struct kprobe *p,
> +						   struct pt_regs *regs);
> +void __kprobes emulate_rd12rm0_noflags_nopc(struct kprobe *p,
> +					    struct pt_regs *regs);
> +void __kprobes emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(struct kprobe *p,
> +						       struct pt_regs *regs);
> +
> +#endif

-- 
Tixy





^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH 7/9] ARM: Move uprobes/kprobes shared functions to common file
@ 2013-08-29 14:08     ` Jon Medhurst (Tixy)
  0 siblings, 0 replies; 32+ messages in thread
From: Jon Medhurst (Tixy) @ 2013-08-29 14:08 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, 2013-08-01 at 19:45 -0400, David Long wrote:
> From: "David A. Long" <dave.long@linaro.org>
> 
> Separate the kprobe-only functions from the functions needed by
> both kprobes and uprobes.
> 
> Signed-off-by: David A. Long <dave.long@linaro.org>
> ---

This re-factoring looks a bit back-to-front to me, e.g. the instruction
emulation routines in kprobes-arm.c, which are only used by kprobes are
moved into the common probes-arm.c file, whilst the common decoding
tables are left in the kprobes-arm.c. Surely it should be the other way
around, or have I missed something?

As is, it leads to things like emulate_rd12rn16rm0rs8_rwflags being
defined in probes-arm.c and used in kprobes-arm.c with a prototype added
to kprobes.h, when it could just stay completely local to kprobes-arm.c.

And in a later patch when uprobes is added, it has to link in
kprobes-arm.o to get the generic decoding table. That was the red flag
which made meet look again, as it seems wrong that after all this  code
re-factoring uprobes should need any kprobe files.

I have no other comments on this patch, save you scrolling down :-)
but I'm leaving the patch quoted here for others' reference as I'm late
in replying and people may no longer have the original...

>  arch/arm/kernel/Makefile         |   2 +-
>  arch/arm/kernel/kprobes-arm.c    | 298 +------------------------------------
>  arch/arm/kernel/kprobes-common.c | 266 ---------------------------------
>  arch/arm/kernel/kprobes-thumb.c  |  14 +-
>  arch/arm/kernel/kprobes.h        |   4 +-
>  arch/arm/kernel/probes-arm.c     | 311 +++++++++++++++++++++++++++++++++++++++
>  arch/arm/kernel/probes.c         | 286 +++++++++++++++++++++++++++++++++++
>  arch/arm/kernel/probes.h         |  23 +++
>  8 files changed, 635 insertions(+), 569 deletions(-)
>  create mode 100644 arch/arm/kernel/probes-arm.c
>  create mode 100644 arch/arm/kernel/probes.c
>  create mode 100644 arch/arm/kernel/probes.h
> 
> diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
> index 86d10dd..3292023 100644
> --- a/arch/arm/kernel/Makefile
> +++ b/arch/arm/kernel/Makefile
> @@ -49,7 +49,7 @@ obj-$(CONFIG_DYNAMIC_FTRACE)	+= ftrace.o insn.o
>  obj-$(CONFIG_FUNCTION_GRAPH_TRACER)	+= ftrace.o insn.o
>  obj-$(CONFIG_JUMP_LABEL)	+= jump_label.o insn.o patch.o
>  obj-$(CONFIG_KEXEC)		+= machine_kexec.o relocate_kernel.o
> -obj-$(CONFIG_KPROBES)		+= kprobes.o kprobes-common.o patch.o
> +obj-$(CONFIG_KPROBES)		+= probes.o probes-arm.o kprobes.o kprobes-common.o patch.o
>  ifdef CONFIG_THUMB2_KERNEL
>  obj-$(CONFIG_KPROBES)		+= kprobes-thumb.o
>  else
> diff --git a/arch/arm/kernel/kprobes-arm.c b/arch/arm/kernel/kprobes-arm.c
> index 8a30c89..d6503cc 100644
> --- a/arch/arm/kernel/kprobes-arm.c
> +++ b/arch/arm/kernel/kprobes-arm.c
> @@ -62,19 +62,9 @@
>  #include <linux/kprobes.h>
>  #include <linux/module.h>
>  
> +#include "probes.h"
>  #include "kprobes.h"
>  
> -#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
> -
> -#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25)
> -
> -#if  __LINUX_ARM_ARCH__ >= 6
> -#define BLX(reg)	"blx	"reg"		\n\t"
> -#else
> -#define BLX(reg)	"mov	lr, pc		\n\t"	\
> -			"mov	pc, "reg"	\n\t"
> -#endif
> -
>  /*
>   * To avoid the complications of mimicing single-stepping on a
>   * processor without a Next-PC or a single-step mode, and to
> @@ -105,284 +95,6 @@
>   * read and write of flags.
>   */
>  
> -static void __kprobes simulate_bbl(struct kprobe *p, struct pt_regs *regs)
> -{
> -	kprobe_opcode_t insn = p->opcode;
> -	long iaddr = (long)p->addr;
> -	int disp  = branch_displacement(insn);
> -
> -	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)
> -{
> -	kprobe_opcode_t insn = p->opcode;
> -	int rm = insn & 0xf;
> -	long rmv = regs->uregs[rm];
> -
> -	if (insn & (1 << 5))
> -		regs->ARM_lr = (long)p->addr + 4;
> -
> -	regs->ARM_pc = rmv & ~0x1;
> -	regs->ARM_cpsr &= ~PSR_T_BIT;
> -	if (rmv & 0x1)
> -		regs->ARM_cpsr |= PSR_T_BIT;
> -}
> -
> -static void __kprobes simulate_mrs(struct kprobe *p, struct pt_regs *regs)
> -{
> -	kprobe_opcode_t insn = p->opcode;
> -	int rd = (insn >> 12) & 0xf;
> -	unsigned long mask = 0xf8ff03df; /* Mask out execution state */
> -	regs->uregs[rd] = regs->ARM_cpsr & mask;
> -}
> -
> -static void __kprobes simulate_mov_ipsp(struct kprobe *p, struct pt_regs *regs)
> -{
> -	regs->uregs[12] = regs->uregs[13];
> -}
> -
> -static void __kprobes
> -emulate_ldrdstrd(struct kprobe *p, struct pt_regs *regs)
> -{
> -	kprobe_opcode_t insn = p->opcode;
> -	unsigned long pc = (unsigned long)p->addr + 8;
> -	int rt = (insn >> 12) & 0xf;
> -	int rn = (insn >> 16) & 0xf;
> -	int rm = insn & 0xf;
> -
> -	register unsigned long rtv asm("r0") = regs->uregs[rt];
> -	register unsigned long rt2v asm("r1") = regs->uregs[rt+1];
> -	register unsigned long rnv asm("r2") = (rn == 15) ? pc
> -							  : regs->uregs[rn];
> -	register unsigned long rmv asm("r3") = regs->uregs[rm];
> -
> -	__asm__ __volatile__ (
> -		BLX("%[fn]")
> -		: "=r" (rtv), "=r" (rt2v), "=r" (rnv)
> -		: "0" (rtv), "1" (rt2v), "2" (rnv), "r" (rmv),
> -		  [fn] "r" (p->ainsn.insn_fn)
> -		: "lr", "memory", "cc"
> -	);
> -
> -	regs->uregs[rt] = rtv;
> -	regs->uregs[rt+1] = rt2v;
> -	if (is_writeback(insn))
> -		regs->uregs[rn] = rnv;
> -}
> -
> -static void __kprobes
> -emulate_ldr(struct kprobe *p, struct pt_regs *regs)
> -{
> -	kprobe_opcode_t insn = p->opcode;
> -	unsigned long pc = (unsigned long)p->addr + 8;
> -	int rt = (insn >> 12) & 0xf;
> -	int rn = (insn >> 16) & 0xf;
> -	int rm = insn & 0xf;
> -
> -	register unsigned long rtv asm("r0");
> -	register unsigned long rnv asm("r2") = (rn == 15) ? pc
> -							  : regs->uregs[rn];
> -	register unsigned long rmv asm("r3") = regs->uregs[rm];
> -
> -	__asm__ __volatile__ (
> -		BLX("%[fn]")
> -		: "=r" (rtv), "=r" (rnv)
> -		: "1" (rnv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
> -		: "lr", "memory", "cc"
> -	);
> -
> -	if (rt == 15)
> -		load_write_pc(rtv, regs);
> -	else
> -		regs->uregs[rt] = rtv;
> -
> -	if (is_writeback(insn))
> -		regs->uregs[rn] = rnv;
> -}
> -
> -static void __kprobes
> -emulate_str(struct kprobe *p, struct pt_regs *regs)
> -{
> -	kprobe_opcode_t insn = p->opcode;
> -	unsigned long rtpc = (unsigned long)p->addr + str_pc_offset;
> -	unsigned long rnpc = (unsigned long)p->addr + 8;
> -	int rt = (insn >> 12) & 0xf;
> -	int rn = (insn >> 16) & 0xf;
> -	int rm = insn & 0xf;
> -
> -	register unsigned long rtv asm("r0") = (rt == 15) ? rtpc
> -							  : regs->uregs[rt];
> -	register unsigned long rnv asm("r2") = (rn == 15) ? rnpc
> -							  : regs->uregs[rn];
> -	register unsigned long rmv asm("r3") = regs->uregs[rm];
> -
> -	__asm__ __volatile__ (
> -		BLX("%[fn]")
> -		: "=r" (rnv)
> -		: "r" (rtv), "0" (rnv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
> -		: "lr", "memory", "cc"
> -	);
> -
> -	if (is_writeback(insn))
> -		regs->uregs[rn] = rnv;
> -}
> -
> -static void __kprobes
> -emulate_rd12rn16rm0rs8_rwflags(struct kprobe *p, struct pt_regs *regs)
> -{
> -	kprobe_opcode_t insn = p->opcode;
> -	unsigned long pc = (unsigned long)p->addr + 8;
> -	int rd = (insn >> 12) & 0xf;
> -	int rn = (insn >> 16) & 0xf;
> -	int rm = insn & 0xf;
> -	int rs = (insn >> 8) & 0xf;
> -
> -	register unsigned long rdv asm("r0") = regs->uregs[rd];
> -	register unsigned long rnv asm("r2") = (rn == 15) ? pc
> -							  : regs->uregs[rn];
> -	register unsigned long rmv asm("r3") = (rm == 15) ? pc
> -							  : regs->uregs[rm];
> -	register unsigned long rsv asm("r1") = regs->uregs[rs];
> -	unsigned long cpsr = regs->ARM_cpsr;
> -
> -	__asm__ __volatile__ (
> -		"msr	cpsr_fs, %[cpsr]	\n\t"
> -		BLX("%[fn]")
> -		"mrs	%[cpsr], cpsr		\n\t"
> -		: "=r" (rdv), [cpsr] "=r" (cpsr)
> -		: "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv),
> -		  "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
> -		: "lr", "memory", "cc"
> -	);
> -
> -	if (rd == 15)
> -		alu_write_pc(rdv, regs);
> -	else
> -		regs->uregs[rd] = rdv;
> -	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
> -}
> -
> -static void __kprobes
> -emulate_rd12rn16rm0_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
> -{
> -	kprobe_opcode_t insn = p->opcode;
> -	int rd = (insn >> 12) & 0xf;
> -	int rn = (insn >> 16) & 0xf;
> -	int rm = insn & 0xf;
> -
> -	register unsigned long rdv asm("r0") = regs->uregs[rd];
> -	register unsigned long rnv asm("r2") = regs->uregs[rn];
> -	register unsigned long rmv asm("r3") = regs->uregs[rm];
> -	unsigned long cpsr = regs->ARM_cpsr;
> -
> -	__asm__ __volatile__ (
> -		"msr	cpsr_fs, %[cpsr]	\n\t"
> -		BLX("%[fn]")
> -		"mrs	%[cpsr], cpsr		\n\t"
> -		: "=r" (rdv), [cpsr] "=r" (cpsr)
> -		: "0" (rdv), "r" (rnv), "r" (rmv),
> -		  "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
> -		: "lr", "memory", "cc"
> -	);
> -
> -	regs->uregs[rd] = rdv;
> -	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
> -}
> -
> -static void __kprobes
> -emulate_rd16rn12rm0rs8_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
> -{
> -	kprobe_opcode_t insn = p->opcode;
> -	int rd = (insn >> 16) & 0xf;
> -	int rn = (insn >> 12) & 0xf;
> -	int rm = insn & 0xf;
> -	int rs = (insn >> 8) & 0xf;
> -
> -	register unsigned long rdv asm("r2") = regs->uregs[rd];
> -	register unsigned long rnv asm("r0") = regs->uregs[rn];
> -	register unsigned long rmv asm("r3") = regs->uregs[rm];
> -	register unsigned long rsv asm("r1") = regs->uregs[rs];
> -	unsigned long cpsr = regs->ARM_cpsr;
> -
> -	__asm__ __volatile__ (
> -		"msr	cpsr_fs, %[cpsr]	\n\t"
> -		BLX("%[fn]")
> -		"mrs	%[cpsr], cpsr		\n\t"
> -		: "=r" (rdv), [cpsr] "=r" (cpsr)
> -		: "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv),
> -		  "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
> -		: "lr", "memory", "cc"
> -	);
> -
> -	regs->uregs[rd] = rdv;
> -	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
> -}
> -
> -static void __kprobes
> -emulate_rd12rm0_noflags_nopc(struct kprobe *p, struct pt_regs *regs)
> -{
> -	kprobe_opcode_t insn = p->opcode;
> -	int rd = (insn >> 12) & 0xf;
> -	int rm = insn & 0xf;
> -
> -	register unsigned long rdv asm("r0") = regs->uregs[rd];
> -	register unsigned long rmv asm("r3") = regs->uregs[rm];
> -
> -	__asm__ __volatile__ (
> -		BLX("%[fn]")
> -		: "=r" (rdv)
> -		: "0" (rdv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
> -		: "lr", "memory", "cc"
> -	);
> -
> -	regs->uregs[rd] = rdv;
> -}
> -
> -static void __kprobes
> -emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
> -{
> -	kprobe_opcode_t insn = p->opcode;
> -	int rdlo = (insn >> 12) & 0xf;
> -	int rdhi = (insn >> 16) & 0xf;
> -	int rn = insn & 0xf;
> -	int rm = (insn >> 8) & 0xf;
> -
> -	register unsigned long rdlov asm("r0") = regs->uregs[rdlo];
> -	register unsigned long rdhiv asm("r2") = regs->uregs[rdhi];
> -	register unsigned long rnv asm("r3") = regs->uregs[rn];
> -	register unsigned long rmv asm("r1") = regs->uregs[rm];
> -	unsigned long cpsr = regs->ARM_cpsr;
> -
> -	__asm__ __volatile__ (
> -		"msr	cpsr_fs, %[cpsr]	\n\t"
> -		BLX("%[fn]")
> -		"mrs	%[cpsr], cpsr		\n\t"
> -		: "=r" (rdlov), "=r" (rdhiv), [cpsr] "=r" (cpsr)
> -		: "0" (rdlov), "1" (rdhiv), "r" (rnv), "r" (rmv),
> -		  "2" (cpsr), [fn] "r" (p->ainsn.insn_fn)
> -		: "lr", "memory", "cc"
> -	);
> -
> -	regs->uregs[rdlo] = rdlov;
> -	regs->uregs[rdhi] = rdhiv;
> -	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
> -}
> -
>  /*
>   * For the instruction masking and comparisons in all the "space_*"
>   * functions below, Do _not_ rearrange the order of tests unless
> @@ -400,13 +112,13 @@ static const union decode_item arm_1111_table[] = {
>  	/* PLDI (immediate)	1111 0100 x101 xxxx xxxx xxxx xxxx xxxx */
>  	/* PLDW (immediate)	1111 0101 x001 xxxx xxxx xxxx xxxx xxxx */
>  	/* PLD (immediate)	1111 0101 x101 xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_SIMULATE	(0xfe300000, 0xf4100000, kprobe_simulate_nop),
> +	DECODE_SIMULATE	(0xfe300000, 0xf4100000, probes_simulate_nop),
>  
>  	/* memory hint		1111 0110 x001 xxxx xxxx xxxx xxx0 xxxx */
>  	/* PLDI (register)	1111 0110 x101 xxxx xxxx xxxx xxx0 xxxx */
>  	/* PLDW (register)	1111 0111 x001 xxxx xxxx xxxx xxx0 xxxx */
>  	/* PLD (register)	1111 0111 x101 xxxx xxxx xxxx xxx0 xxxx */
> -	DECODE_SIMULATE	(0xfe300010, 0xf6100000, kprobe_simulate_nop),
> +	DECODE_SIMULATE	(0xfe300010, 0xf6100000, probes_simulate_nop),
>  
>  	/* BLX (immediate)	1111 101x xxxx xxxx xxxx xxxx xxxx xxxx */
>  	DECODE_SIMULATE	(0xfe000000, 0xfa000000, simulate_blx1),
> @@ -649,11 +361,11 @@ static const union decode_item arm_cccc_001x_table[] = {
>  	/* YIELD		cccc 0011 0010 0000 xxxx xxxx 0000 0001 */
>  	DECODE_OR	(0x0fff00ff, 0x03200001),
>  	/* SEV			cccc 0011 0010 0000 xxxx xxxx 0000 0100 */
> -	DECODE_EMULATE	(0x0fff00ff, 0x03200004, kprobe_emulate_none),
> +	DECODE_EMULATE	(0x0fff00ff, 0x03200004, probes_emulate_none),
>  	/* NOP			cccc 0011 0010 0000 xxxx xxxx 0000 0000 */
>  	/* WFE			cccc 0011 0010 0000 xxxx xxxx 0000 0010 */
>  	/* WFI			cccc 0011 0010 0000 xxxx xxxx 0000 0011 */
> -	DECODE_SIMULATE	(0x0fff00fc, 0x03200000, kprobe_simulate_nop),
> +	DECODE_SIMULATE	(0x0fff00fc, 0x03200000, probes_simulate_nop),
>  	/* DBG			cccc 0011 0010 0000 xxxx xxxx ffff xxxx */
>  	/* unallocated hints	cccc 0011 0010 0000 xxxx xxxx xxxx xxxx */
>  	/* MSR (immediate)	cccc 0011 0x10 xxxx xxxx xxxx xxxx xxxx */
> diff --git a/arch/arm/kernel/kprobes-common.c b/arch/arm/kernel/kprobes-common.c
> index 18a7628..b66e9f7 100644
> --- a/arch/arm/kernel/kprobes-common.c
> +++ b/arch/arm/kernel/kprobes-common.c
> @@ -173,15 +173,6 @@ kprobe_check_cc * const kprobe_condition_checks[16] = {
>  };
>  
> 
> -void __kprobes kprobe_simulate_nop(struct kprobe *p, struct pt_regs *regs)
> -{
> -}
> -
> -void __kprobes kprobe_emulate_none(struct kprobe *p, struct pt_regs *regs)
> -{
> -	p->ainsn.insn_fn();
> -}
> -
>  static void __kprobes simulate_ldm1stm1(struct kprobe *p, struct pt_regs *regs)
>  {
>  	kprobe_opcode_t insn = p->opcode;
> @@ -319,260 +310,3 @@ kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi)
>  	return INSN_GOOD_NO_SLOT;
>  }
>  
> -
> -/*
> - * Prepare an instruction slot to receive an instruction for emulating.
> - * This is done by placing a subroutine return after the location where the
> - * instruction will be placed. We also modify ARM instructions to be
> - * unconditional as the condition code will already be checked before any
> - * emulation handler is called.
> - */
> -static kprobe_opcode_t __kprobes
> -prepare_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> -								bool thumb)
> -{
> -#ifdef CONFIG_THUMB2_KERNEL
> -	if (thumb) {
> -		u16 *thumb_insn = (u16 *)asi->insn;
> -		thumb_insn[1] = 0x4770; /* Thumb bx lr */
> -		thumb_insn[2] = 0x4770; /* Thumb bx lr */
> -		return insn;
> -	}
> -	asi->insn[1] = 0xe12fff1e; /* ARM bx lr */
> -#else
> -	asi->insn[1] = 0xe1a0f00e; /* mov pc, lr */
> -#endif
> -	/* Make an ARM instruction unconditional */
> -	if (insn < 0xe0000000)
> -		insn = (insn | 0xe0000000) & ~0x10000000;
> -	return insn;
> -}
> -
> -/*
> - * Write a (probably modified) instruction into the slot previously prepared by
> - * prepare_emulated_insn
> - */
> -static void  __kprobes
> -set_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> -								bool thumb)
> -{
> -#ifdef CONFIG_THUMB2_KERNEL
> -	if (thumb) {
> -		u16 *ip = (u16 *)asi->insn;
> -		if (is_wide_instruction(insn))
> -			*ip++ = insn >> 16;
> -		*ip++ = insn;
> -		return;
> -	}
> -#endif
> -	asi->insn[0] = insn;
> -}
> -
> -/*
> - * When we modify the register numbers encoded in an instruction to be emulated,
> - * the new values come from this define. For ARM and 32-bit Thumb instructions
> - * this gives...
> - *
> - *	bit position	  16  12   8   4   0
> - *	---------------+---+---+---+---+---+
> - *	register	 r2  r0  r1  --  r3
> - */
> -#define INSN_NEW_BITS		0x00020103
> -
> -/* Each nibble has same value as that at INSN_NEW_BITS bit 16 */
> -#define INSN_SAMEAS16_BITS	0x22222222
> -
> -/*
> - * Validate and modify each of the registers encoded in an instruction.
> - *
> - * Each nibble in regs contains a value from enum decode_reg_type. For each
> - * non-zero value, the corresponding nibble in pinsn is validated and modified
> - * according to the type.
> - */
> -static bool __kprobes decode_regs(kprobe_opcode_t* pinsn, u32 regs)
> -{
> -	kprobe_opcode_t insn = *pinsn;
> -	kprobe_opcode_t mask = 0xf; /* Start at least significant nibble */
> -
> -	for (; regs != 0; regs >>= 4, mask <<= 4) {
> -
> -		kprobe_opcode_t new_bits = INSN_NEW_BITS;
> -
> -		switch (regs & 0xf) {
> -
> -		case REG_TYPE_NONE:
> -			/* Nibble not a register, skip to next */
> -			continue;
> -
> -		case REG_TYPE_ANY:
> -			/* Any register is allowed */
> -			break;
> -
> -		case REG_TYPE_SAMEAS16:
> -			/* Replace register with same as at bit position 16 */
> -			new_bits = INSN_SAMEAS16_BITS;
> -			break;
> -
> -		case REG_TYPE_SP:
> -			/* Only allow SP (R13) */
> -			if ((insn ^ 0xdddddddd) & mask)
> -				goto reject;
> -			break;
> -
> -		case REG_TYPE_PC:
> -			/* Only allow PC (R15) */
> -			if ((insn ^ 0xffffffff) & mask)
> -				goto reject;
> -			break;
> -
> -		case REG_TYPE_NOSP:
> -			/* Reject SP (R13) */
> -			if (((insn ^ 0xdddddddd) & mask) == 0)
> -				goto reject;
> -			break;
> -
> -		case REG_TYPE_NOSPPC:
> -		case REG_TYPE_NOSPPCX:
> -			/* Reject SP and PC (R13 and R15) */
> -			if (((insn ^ 0xdddddddd) & 0xdddddddd & mask) == 0)
> -				goto reject;
> -			break;
> -
> -		case REG_TYPE_NOPCWB:
> -			if (!is_writeback(insn))
> -				break; /* No writeback, so any register is OK */
> -			/* fall through... */
> -		case REG_TYPE_NOPC:
> -		case REG_TYPE_NOPCX:
> -			/* Reject PC (R15) */
> -			if (((insn ^ 0xffffffff) & mask) == 0)
> -				goto reject;
> -			break;
> -		}
> -
> -		/* Replace value of nibble with new register number... */
> -		insn &= ~mask;
> -		insn |= new_bits & mask;
> -	}
> -
> -	*pinsn = insn;
> -	return true;
> -
> -reject:
> -	return false;
> -}
> -
> -static const int decode_struct_sizes[NUM_DECODE_TYPES] = {
> -	[DECODE_TYPE_TABLE]	= sizeof(struct decode_table),
> -	[DECODE_TYPE_CUSTOM]	= sizeof(struct decode_custom),
> -	[DECODE_TYPE_SIMULATE]	= sizeof(struct decode_simulate),
> -	[DECODE_TYPE_EMULATE]	= sizeof(struct decode_emulate),
> -	[DECODE_TYPE_OR]	= sizeof(struct decode_or),
> -	[DECODE_TYPE_REJECT]	= sizeof(struct decode_reject)
> -};
> -
> -/*
> - * kprobe_decode_insn operates on data tables in order to decode an ARM
> - * architecture instruction onto which a kprobe has been placed.
> - *
> - * These instruction decoding tables are a concatenation of entries each
> - * of which consist of one of the following structs:
> - *
> - *	decode_table
> - *	decode_custom
> - *	decode_simulate
> - *	decode_emulate
> - *	decode_or
> - *	decode_reject
> - *
> - * Each of these starts with a struct decode_header which has the following
> - * fields:
> - *
> - *	type_regs
> - *	mask
> - *	value
> - *
> - * The least significant DECODE_TYPE_BITS of type_regs contains a value
> - * from enum decode_type, this indicates which of the decode_* structs
> - * the entry contains. The value DECODE_TYPE_END indicates the end of the
> - * table.
> - *
> - * When the table is parsed, each entry is checked in turn to see if it
> - * matches the instruction to be decoded using the test:
> - *
> - *	(insn & mask) == value
> - *
> - * If no match is found before the end of the table is reached then decoding
> - * fails with INSN_REJECTED.
> - *
> - * When a match is found, decode_regs() is called to validate and modify each
> - * of the registers encoded in the instruction; the data it uses to do this
> - * is (type_regs >> DECODE_TYPE_BITS). A validation failure will cause decoding
> - * to fail with INSN_REJECTED.
> - *
> - * Once the instruction has passed the above tests, further processing
> - * depends on the type of the table entry's decode struct.
> - *
> - */
> -int __kprobes
> -kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> -				const union decode_item *table, bool thumb)
> -{
> -	const struct decode_header *h = (struct decode_header *)table;
> -	const struct decode_header *next;
> -	bool matched = false;
> -
> -	insn = prepare_emulated_insn(insn, asi, thumb);
> -
> -	for (;; h = next) {
> -		enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK;
> -		u32 regs = h->type_regs.bits >> DECODE_TYPE_BITS;
> -
> -		if (type == DECODE_TYPE_END)
> -			return INSN_REJECTED;
> -
> -		next = (struct decode_header *)
> -				((uintptr_t)h + decode_struct_sizes[type]);
> -
> -		if (!matched && (insn & h->mask.bits) != h->value.bits)
> -			continue;
> -
> -		if (!decode_regs(&insn, regs))
> -			return INSN_REJECTED;
> -
> -		switch (type) {
> -
> -		case DECODE_TYPE_TABLE: {
> -			struct decode_table *d = (struct decode_table *)h;
> -			next = (struct decode_header *)d->table.table;
> -			break;
> -		}
> -
> -		case DECODE_TYPE_CUSTOM: {
> -			struct decode_custom *d = (struct decode_custom *)h;
> -			return (*d->decoder.decoder)(insn, asi);
> -		}
> -
> -		case DECODE_TYPE_SIMULATE: {
> -			struct decode_simulate *d = (struct decode_simulate *)h;
> -			asi->insn_handler = d->handler.handler;
> -			return INSN_GOOD_NO_SLOT;
> -		}
> -
> -		case DECODE_TYPE_EMULATE: {
> -			struct decode_emulate *d = (struct decode_emulate *)h;
> -			asi->insn_handler = d->handler.handler;
> -			set_emulated_insn(insn, asi, thumb);
> -			return INSN_GOOD;
> -		}
> -
> -		case DECODE_TYPE_OR:
> -			matched = true;
> -			break;
> -
> -		case DECODE_TYPE_REJECT:
> -		default:
> -			return INSN_REJECTED;
> -		}
> -		}
> -	}
> diff --git a/arch/arm/kernel/kprobes-thumb.c b/arch/arm/kernel/kprobes-thumb.c
> index 6123daf..173b2bc 100644
> --- a/arch/arm/kernel/kprobes-thumb.c
> +++ b/arch/arm/kernel/kprobes-thumb.c
> @@ -544,11 +544,11 @@ static const union decode_item t32_table_1111_0xxx___1[] = {
>  	/* YIELD		1111 0011 1010 xxxx 10x0 x000 0000 0001 */
>  	DECODE_OR	(0xfff0d7ff, 0xf3a08001),
>  	/* SEV			1111 0011 1010 xxxx 10x0 x000 0000 0100 */
> -	DECODE_EMULATE	(0xfff0d7ff, 0xf3a08004, kprobe_emulate_none),
> +	DECODE_EMULATE	(0xfff0d7ff, 0xf3a08004, probes_emulate_none),
>  	/* NOP			1111 0011 1010 xxxx 10x0 x000 0000 0000 */
>  	/* WFE			1111 0011 1010 xxxx 10x0 x000 0000 0010 */
>  	/* WFI			1111 0011 1010 xxxx 10x0 x000 0000 0011 */
> -	DECODE_SIMULATE	(0xfff0d7fc, 0xf3a08000, kprobe_simulate_nop),
> +	DECODE_SIMULATE	(0xfff0d7fc, 0xf3a08000, probes_simulate_nop),
>  
>  	/* MRS Rd, CPSR		1111 0011 1110 xxxx 10x0 xxxx xxxx xxxx */
>  	DECODE_SIMULATEX(0xfff0d000, 0xf3e08000, t32_simulate_mrs,
> @@ -589,7 +589,7 @@ static const union decode_item t32_table_1111_100x_x0x1__1111[] = {
>  
>  	/* PLD (literal)	1111 1000 x001 1111 1111 xxxx xxxx xxxx */
>  	/* PLI (literal)	1111 1001 x001 1111 1111 xxxx xxxx xxxx */
> -	DECODE_SIMULATE	(0xfe7ff000, 0xf81ff000, kprobe_simulate_nop),
> +	DECODE_SIMULATE	(0xfe7ff000, 0xf81ff000, probes_simulate_nop),
>  
>  	/* PLD{W} (immediate)	1111 1000 10x1 xxxx 1111 xxxx xxxx xxxx */
>  	DECODE_OR	(0xffd0f000, 0xf890f000),
> @@ -598,13 +598,13 @@ static const union decode_item t32_table_1111_100x_x0x1__1111[] = {
>  	/* PLI (immediate)	1111 1001 1001 xxxx 1111 xxxx xxxx xxxx */
>  	DECODE_OR	(0xfff0f000, 0xf990f000),
>  	/* PLI (immediate)	1111 1001 0001 xxxx 1111 1100 xxxx xxxx */
> -	DECODE_SIMULATEX(0xfff0ff00, 0xf910fc00, kprobe_simulate_nop,
> +	DECODE_SIMULATEX(0xfff0ff00, 0xf910fc00, probes_simulate_nop,
>  						 REGS(NOPCX, 0, 0, 0, 0)),
>  
>  	/* PLD{W} (register)	1111 1000 00x1 xxxx 1111 0000 00xx xxxx */
>  	DECODE_OR	(0xffd0ffc0, 0xf810f000),
>  	/* PLI (register)	1111 1001 0001 xxxx 1111 0000 00xx xxxx */
> -	DECODE_SIMULATEX(0xfff0ffc0, 0xf910f000, kprobe_simulate_nop,
> +	DECODE_SIMULATEX(0xfff0ffc0, 0xf910f000, probes_simulate_nop,
>  						 REGS(NOPCX, 0, 0, 0, NOSPPC)),
>  
>  	/* Other unallocated instructions...				*/
> @@ -1274,11 +1274,11 @@ static const union decode_item t16_table_1011[] = {
>  	/* YIELD			1011 1111 0001 0000 */
>  	DECODE_OR	(0xffff, 0xbf10),
>  	/* SEV				1011 1111 0100 0000 */
> -	DECODE_EMULATE	(0xffff, 0xbf40, kprobe_emulate_none),
> +	DECODE_EMULATE	(0xffff, 0xbf40, probes_emulate_none),
>  	/* NOP				1011 1111 0000 0000 */
>  	/* WFE				1011 1111 0010 0000 */
>  	/* WFI				1011 1111 0011 0000 */
> -	DECODE_SIMULATE	(0xffcf, 0xbf00, kprobe_simulate_nop),
> +	DECODE_SIMULATE	(0xffcf, 0xbf00, probes_simulate_nop),
>  	/* Unassigned hints		1011 1111 xxxx 0000 */
>  	DECODE_REJECT	(0xff0f, 0xbf00),
>  	/* IT				1011 1111 xxxx xxxx */
> diff --git a/arch/arm/kernel/kprobes.h b/arch/arm/kernel/kprobes.h
> index 38945f7..9aa2f15 100644
> --- a/arch/arm/kernel/kprobes.h
> +++ b/arch/arm/kernel/kprobes.h
> @@ -161,8 +161,8 @@ static inline void __kprobes alu_write_pc(long pcv, struct pt_regs *regs)
>  }
>  
> 
> -void __kprobes kprobe_simulate_nop(struct kprobe *p, struct pt_regs *regs);
> -void __kprobes kprobe_emulate_none(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes probes_simulate_nop(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes probes_emulate_none(struct kprobe *p, struct pt_regs *regs);
>  
>  enum kprobe_insn __kprobes
>  kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi);
> diff --git a/arch/arm/kernel/probes-arm.c b/arch/arm/kernel/probes-arm.c
> new file mode 100644
> index 0000000..e1b1a6e
> --- /dev/null
> +++ b/arch/arm/kernel/probes-arm.c
> @@ -0,0 +1,311 @@
> +/*
> + * arch/arm/kernel/probes-arm.c
> + *
> + * Some code moved here from arch/arm/kernel/kprobes-arm.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.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/kprobes.h>
> +
> +#include "probes.h"
> +#include "kprobes.h"
> +
> +#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
> +
> +#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25)
> +
> +#if  __LINUX_ARM_ARCH__ >= 6
> +#define BLX(reg)	"blx	"reg"		\n\t"
> +#else
> +#define BLX(reg)	"mov	lr, pc		\n\t"	\
> +			"mov	pc, "reg"	\n\t"
> +#endif
> +
> +void __kprobes simulate_bbl(struct kprobe *p, struct pt_regs *regs)
> +{
> +	kprobe_opcode_t insn = p->opcode;
> +	long iaddr = (long)p->addr;
> +	int disp  = branch_displacement(insn);
> +
> +	if (insn & (1 << 24))
> +		regs->ARM_lr = iaddr + 4;
> +
> +	regs->ARM_pc = iaddr + 8 + disp;
> +}
> +
> +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;
> +}
> +
> +void __kprobes simulate_blx2bx(struct kprobe *p, struct pt_regs *regs)
> +{
> +	kprobe_opcode_t insn = p->opcode;
> +	int rm = insn & 0xf;
> +	long rmv = regs->uregs[rm];
> +
> +	if (insn & (1 << 5))
> +		regs->ARM_lr = (long)p->addr + 4;
> +
> +	regs->ARM_pc = rmv & ~0x1;
> +	regs->ARM_cpsr &= ~PSR_T_BIT;
> +	if (rmv & 0x1)
> +		regs->ARM_cpsr |= PSR_T_BIT;
> +}
> +
> +void __kprobes simulate_mrs(struct kprobe *p, struct pt_regs *regs)
> +{
> +	kprobe_opcode_t insn = p->opcode;
> +	int rd = (insn >> 12) & 0xf;
> +	unsigned long mask = 0xf8ff03df; /* Mask out execution state */
> +	regs->uregs[rd] = regs->ARM_cpsr & mask;
> +}
> +
> +void __kprobes simulate_mov_ipsp(struct kprobe *p, struct pt_regs *regs)
> +{
> +	regs->uregs[12] = regs->uregs[13];
> +}
> +
> +void __kprobes
> +emulate_ldrdstrd(struct kprobe *p, struct pt_regs *regs)
> +{
> +	kprobe_opcode_t insn = p->opcode;
> +	unsigned long pc = (unsigned long)p->addr + 8;
> +	int rt = (insn >> 12) & 0xf;
> +	int rn = (insn >> 16) & 0xf;
> +	int rm = insn & 0xf;
> +
> +	register unsigned long rtv asm("r0") = regs->uregs[rt];
> +	register unsigned long rt2v asm("r1") = regs->uregs[rt+1];
> +	register unsigned long rnv asm("r2") = (rn == 15) ? pc
> +							  : regs->uregs[rn];
> +	register unsigned long rmv asm("r3") = regs->uregs[rm];
> +
> +	__asm__ __volatile__ (
> +		BLX("%[fn]")
> +		: "=r" (rtv), "=r" (rt2v), "=r" (rnv)
> +		: "0" (rtv), "1" (rt2v), "2" (rnv), "r" (rmv),
> +		  [fn] "r" (p->ainsn.insn_fn)
> +		: "lr", "memory", "cc"
> +	);
> +
> +	regs->uregs[rt] = rtv;
> +	regs->uregs[rt+1] = rt2v;
> +	if (is_writeback(insn))
> +		regs->uregs[rn] = rnv;
> +}
> +
> +void __kprobes
> +emulate_ldr(struct kprobe *p, struct pt_regs *regs)
> +{
> +	kprobe_opcode_t insn = p->opcode;
> +	unsigned long pc = (unsigned long)p->addr + 8;
> +	int rt = (insn >> 12) & 0xf;
> +	int rn = (insn >> 16) & 0xf;
> +	int rm = insn & 0xf;
> +
> +	register unsigned long rtv asm("r0");
> +	register unsigned long rnv asm("r2") = (rn == 15) ? pc
> +							  : regs->uregs[rn];
> +	register unsigned long rmv asm("r3") = regs->uregs[rm];
> +
> +	__asm__ __volatile__ (
> +		BLX("%[fn]")
> +		: "=r" (rtv), "=r" (rnv)
> +		: "1" (rnv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
> +		: "lr", "memory", "cc"
> +	);
> +
> +	if (rt == 15)
> +		load_write_pc(rtv, regs);
> +	else
> +		regs->uregs[rt] = rtv;
> +
> +	if (is_writeback(insn))
> +		regs->uregs[rn] = rnv;
> +}
> +
> +void __kprobes
> +emulate_str(struct kprobe *p, struct pt_regs *regs)
> +{
> +	kprobe_opcode_t insn = p->opcode;
> +	unsigned long rtpc = (unsigned long)p->addr + str_pc_offset;
> +	unsigned long rnpc = (unsigned long)p->addr + 8;
> +	int rt = (insn >> 12) & 0xf;
> +	int rn = (insn >> 16) & 0xf;
> +	int rm = insn & 0xf;
> +
> +	register unsigned long rtv asm("r0") = (rt == 15) ? rtpc
> +							  : regs->uregs[rt];
> +	register unsigned long rnv asm("r2") = (rn == 15) ? rnpc
> +							  : regs->uregs[rn];
> +	register unsigned long rmv asm("r3") = regs->uregs[rm];
> +
> +	__asm__ __volatile__ (
> +		BLX("%[fn]")
> +		: "=r" (rnv)
> +		: "r" (rtv), "0" (rnv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
> +		: "lr", "memory", "cc"
> +	);
> +
> +	if (is_writeback(insn))
> +		regs->uregs[rn] = rnv;
> +}
> +
> +void __kprobes
> +emulate_rd12rn16rm0rs8_rwflags(struct kprobe *p, struct pt_regs *regs)
> +{
> +	kprobe_opcode_t insn = p->opcode;
> +	unsigned long pc = (unsigned long)p->addr + 8;
> +	int rd = (insn >> 12) & 0xf;
> +	int rn = (insn >> 16) & 0xf;
> +	int rm = insn & 0xf;
> +	int rs = (insn >> 8) & 0xf;
> +
> +	register unsigned long rdv asm("r0") = regs->uregs[rd];
> +	register unsigned long rnv asm("r2") = (rn == 15) ? pc
> +							  : regs->uregs[rn];
> +	register unsigned long rmv asm("r3") = (rm == 15) ? pc
> +							  : regs->uregs[rm];
> +	register unsigned long rsv asm("r1") = regs->uregs[rs];
> +	unsigned long cpsr = regs->ARM_cpsr;
> +
> +	__asm__ __volatile__ (
> +		"msr	cpsr_fs, %[cpsr]	\n\t"
> +		BLX("%[fn]")
> +		"mrs	%[cpsr], cpsr		\n\t"
> +		: "=r" (rdv), [cpsr] "=r" (cpsr)
> +		: "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv),
> +		  "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
> +		: "lr", "memory", "cc"
> +	);
> +
> +	if (rd == 15)
> +		alu_write_pc(rdv, regs);
> +	else
> +		regs->uregs[rd] = rdv;
> +	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
> +}
> +
> +void __kprobes
> +emulate_rd12rn16rm0_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
> +{
> +	kprobe_opcode_t insn = p->opcode;
> +	int rd = (insn >> 12) & 0xf;
> +	int rn = (insn >> 16) & 0xf;
> +	int rm = insn & 0xf;
> +
> +	register unsigned long rdv asm("r0") = regs->uregs[rd];
> +	register unsigned long rnv asm("r2") = regs->uregs[rn];
> +	register unsigned long rmv asm("r3") = regs->uregs[rm];
> +	unsigned long cpsr = regs->ARM_cpsr;
> +
> +	__asm__ __volatile__ (
> +		"msr	cpsr_fs, %[cpsr]	\n\t"
> +		BLX("%[fn]")
> +		"mrs	%[cpsr], cpsr		\n\t"
> +		: "=r" (rdv), [cpsr] "=r" (cpsr)
> +		: "0" (rdv), "r" (rnv), "r" (rmv),
> +		  "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
> +		: "lr", "memory", "cc"
> +	);
> +
> +	regs->uregs[rd] = rdv;
> +	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
> +}
> +
> +void __kprobes
> +emulate_rd16rn12rm0rs8_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
> +{
> +	kprobe_opcode_t insn = p->opcode;
> +	int rd = (insn >> 16) & 0xf;
> +	int rn = (insn >> 12) & 0xf;
> +	int rm = insn & 0xf;
> +	int rs = (insn >> 8) & 0xf;
> +
> +	register unsigned long rdv asm("r2") = regs->uregs[rd];
> +	register unsigned long rnv asm("r0") = regs->uregs[rn];
> +	register unsigned long rmv asm("r3") = regs->uregs[rm];
> +	register unsigned long rsv asm("r1") = regs->uregs[rs];
> +	unsigned long cpsr = regs->ARM_cpsr;
> +
> +	__asm__ __volatile__ (
> +		"msr	cpsr_fs, %[cpsr]	\n\t"
> +		BLX("%[fn]")
> +		"mrs	%[cpsr], cpsr		\n\t"
> +		: "=r" (rdv), [cpsr] "=r" (cpsr)
> +		: "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv),
> +		  "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
> +		: "lr", "memory", "cc"
> +	);
> +
> +	regs->uregs[rd] = rdv;
> +	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
> +}
> +
> +void __kprobes
> +emulate_rd12rm0_noflags_nopc(struct kprobe *p, struct pt_regs *regs)
> +{
> +	kprobe_opcode_t insn = p->opcode;
> +	int rd = (insn >> 12) & 0xf;
> +	int rm = insn & 0xf;
> +
> +	register unsigned long rdv asm("r0") = regs->uregs[rd];
> +	register unsigned long rmv asm("r3") = regs->uregs[rm];
> +
> +	__asm__ __volatile__ (
> +		BLX("%[fn]")
> +		: "=r" (rdv)
> +		: "0" (rdv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
> +		: "lr", "memory", "cc"
> +	);
> +
> +	regs->uregs[rd] = rdv;
> +}
> +
> +void __kprobes
> +emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
> +{
> +	kprobe_opcode_t insn = p->opcode;
> +	int rdlo = (insn >> 12) & 0xf;
> +	int rdhi = (insn >> 16) & 0xf;
> +	int rn = insn & 0xf;
> +	int rm = (insn >> 8) & 0xf;
> +
> +	register unsigned long rdlov asm("r0") = regs->uregs[rdlo];
> +	register unsigned long rdhiv asm("r2") = regs->uregs[rdhi];
> +	register unsigned long rnv asm("r3") = regs->uregs[rn];
> +	register unsigned long rmv asm("r1") = regs->uregs[rm];
> +	unsigned long cpsr = regs->ARM_cpsr;
> +
> +	__asm__ __volatile__ (
> +		"msr	cpsr_fs, %[cpsr]	\n\t"
> +		BLX("%[fn]")
> +		"mrs	%[cpsr], cpsr		\n\t"
> +		: "=r" (rdlov), "=r" (rdhiv), [cpsr] "=r" (cpsr)
> +		: "0" (rdlov), "1" (rdhiv), "r" (rnv), "r" (rmv),
> +		  "2" (cpsr), [fn] "r" (p->ainsn.insn_fn)
> +		: "lr", "memory", "cc"
> +	);
> +
> +	regs->uregs[rdlo] = rdlov;
> +	regs->uregs[rdhi] = rdhiv;
> +	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
> +}
> diff --git a/arch/arm/kernel/probes.c b/arch/arm/kernel/probes.c
> new file mode 100644
> index 0000000..86c63f3
> --- /dev/null
> +++ b/arch/arm/kernel/probes.c
> @@ -0,0 +1,286 @@
> +/*
> + * arch/arm/kernel/probes.c
> + *
> + * Some contents moved here from arch/arm/include/asm/kprobes-common.c
> + *
> + * Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
> + *
> + * Some contents moved here from arch/arm/include/asm/kprobes-arm.c which is
> + * 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.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/kprobes.h>
> +
> +#include "probes.h"
> +#include "kprobes.h"
> +
> +void __kprobes probes_simulate_nop(struct kprobe *p, struct pt_regs *regs)
> +{
> +}
> +
> +void __kprobes probes_emulate_none(struct kprobe *p, struct pt_regs *regs)
> +{
> +	p->ainsn.insn_fn();
> +}
> +
> +/*
> + * Prepare an instruction slot to receive an instruction for emulating.
> + * This is done by placing a subroutine return after the location where the
> + * instruction will be placed. We also modify ARM instructions to be
> + * unconditional as the condition code will already be checked before any
> + * emulation handler is called.
> + */
> +static kprobe_opcode_t __kprobes
> +prepare_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> +								bool thumb)
> +{
> +#ifdef CONFIG_THUMB2_KERNEL
> +	if (thumb) {
> +		u16 *thumb_insn = (u16 *)asi->insn;
> +		thumb_insn[1] = 0x4770; /* Thumb bx lr */
> +		thumb_insn[2] = 0x4770; /* Thumb bx lr */
> +		return insn;
> +	}
> +	asi->insn[1] = 0xe12fff1e; /* ARM bx lr */
> +#else
> +	asi->insn[1] = 0xe1a0f00e; /* mov pc, lr */
> +#endif
> +	/* Make an ARM instruction unconditional */
> +	if (insn < 0xe0000000)
> +		insn = (insn | 0xe0000000) & ~0x10000000;
> +	return insn;
> +}
> +
> +/*
> + * Write a (probably modified) instruction into the slot previously prepared by
> + * prepare_emulated_insn
> + */
> +static void  __kprobes
> +set_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> +								bool thumb)
> +{
> +#ifdef CONFIG_THUMB2_KERNEL
> +	if (thumb) {
> +		u16 *ip = (u16 *)asi->insn;
> +		if (is_wide_instruction(insn))
> +			*ip++ = insn >> 16;
> +		*ip++ = insn;
> +		return;
> +	}
> +#endif
> +	asi->insn[0] = insn;
> +}
> +
> +/*
> + * When we modify the register numbers encoded in an instruction to be emulated,
> + * the new values come from this define. For ARM and 32-bit Thumb instructions
> + * this gives...
> + *
> + *	bit position	  16  12   8   4   0
> + *	---------------+---+---+---+---+---+
> + *	register	 r2  r0  r1  --  r3
> + */
> +#define INSN_NEW_BITS		0x00020103
> +
> +/* Each nibble has same value as that at INSN_NEW_BITS bit 16 */
> +#define INSN_SAMEAS16_BITS	0x22222222
> +
> +/*
> + * Validate and modify each of the registers encoded in an instruction.
> + *
> + * Each nibble in regs contains a value from enum decode_reg_type. For each
> + * non-zero value, the corresponding nibble in pinsn is validated and modified
> + * according to the type.
> + */
> +static bool __kprobes decode_regs(kprobe_opcode_t *pinsn, u32 regs)
> +{
> +	kprobe_opcode_t insn = *pinsn;
> +	kprobe_opcode_t mask = 0xf; /* Start at least significant nibble */
> +
> +	for (; regs != 0; regs >>= 4, mask <<= 4) {
> +
> +		kprobe_opcode_t new_bits = INSN_NEW_BITS;
> +
> +		switch (regs & 0xf) {
> +
> +		case REG_TYPE_NONE:
> +			/* Nibble not a register, skip to next */
> +			continue;
> +
> +		case REG_TYPE_ANY:
> +			/* Any register is allowed */
> +			break;
> +
> +		case REG_TYPE_SAMEAS16:
> +			/* Replace register with same as at bit position 16 */
> +			new_bits = INSN_SAMEAS16_BITS;
> +			break;
> +
> +		case REG_TYPE_SP:
> +			/* Only allow SP (R13) */
> +			if ((insn ^ 0xdddddddd) & mask)
> +				goto reject;
> +			break;
> +
> +		case REG_TYPE_PC:
> +			/* Only allow PC (R15) */
> +			if ((insn ^ 0xffffffff) & mask)
> +				goto reject;
> +			break;
> +
> +		case REG_TYPE_NOSP:
> +			/* Reject SP (R13) */
> +			if (((insn ^ 0xdddddddd) & mask) == 0)
> +				goto reject;
> +			break;
> +
> +		case REG_TYPE_NOSPPC:
> +		case REG_TYPE_NOSPPCX:
> +			/* Reject SP and PC (R13 and R15) */
> +			if (((insn ^ 0xdddddddd) & 0xdddddddd & mask) == 0)
> +				goto reject;
> +			break;
> +
> +		case REG_TYPE_NOPCWB:
> +			if (!is_writeback(insn))
> +				break; /* No writeback, so any register is OK */
> +			/* fall through... */
> +		case REG_TYPE_NOPC:
> +		case REG_TYPE_NOPCX:
> +			/* Reject PC (R15) */
> +			if (((insn ^ 0xffffffff) & mask) == 0)
> +				goto reject;
> +			break;
> +		}
> +
> +		/* Replace value of nibble with new register number... */
> +		insn &= ~mask;
> +		insn |= new_bits & mask;
> +	}
> +
> +	*pinsn = insn;
> +	return true;
> +
> +reject:
> +	return false;
> +}
> +
> +static const int decode_struct_sizes[NUM_DECODE_TYPES] = {
> +	[DECODE_TYPE_TABLE]	= sizeof(struct decode_table),
> +	[DECODE_TYPE_CUSTOM]	= sizeof(struct decode_custom),
> +	[DECODE_TYPE_SIMULATE]	= sizeof(struct decode_simulate),
> +	[DECODE_TYPE_EMULATE]	= sizeof(struct decode_emulate),
> +	[DECODE_TYPE_OR]	= sizeof(struct decode_or),
> +	[DECODE_TYPE_REJECT]	= sizeof(struct decode_reject)
> +};
> +
> +/*
> + * kprobe_decode_insn operates on data tables in order to decode an ARM
> + * architecture instruction onto which a kprobe has been placed.
> + *
> + * These instruction decoding tables are a concatenation of entries each
> + * of which consist of one of the following structs:
> + *
> + *	decode_table
> + *	decode_custom
> + *	decode_simulate
> + *	decode_emulate
> + *	decode_or
> + *	decode_reject
> + *
> + * Each of these starts with a struct decode_header which has the following
> + * fields:
> + *
> + *	type_regs
> + *	mask
> + *	value
> + *
> + * The least significant DECODE_TYPE_BITS of type_regs contains a value
> + * from enum decode_type, this indicates which of the decode_* structs
> + * the entry contains. The value DECODE_TYPE_END indicates the end of the
> + * table.
> + *
> + * When the table is parsed, each entry is checked in turn to see if it
> + * matches the instruction to be decoded using the test:
> + *
> + *	(insn & mask) == value
> + *
> + * If no match is found before the end of the table is reached then decoding
> + * fails with INSN_REJECTED.
> + *
> + * When a match is found, decode_regs() is called to validate and modify each
> + * of the registers encoded in the instruction; the data it uses to do this
> + * is (type_regs >> DECODE_TYPE_BITS). A validation failure will cause decoding
> + * to fail with INSN_REJECTED.
> + *
> + * Once the instruction has passed the above tests, further processing
> + * depends on the type of the table entry's decode struct.
> + *
> + */
> +int __kprobes
> +kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> +				const union decode_item *table, bool thumb)
> +{
> +	const struct decode_header *h = (struct decode_header *)table;
> +	const struct decode_header *next;
> +	bool matched = false;
> +
> +	insn = prepare_emulated_insn(insn, asi, thumb);
> +
> +	for (;; h = next) {
> +		enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK;
> +		u32 regs = h->type_regs.bits >> DECODE_TYPE_BITS;
> +
> +		if (type == DECODE_TYPE_END)
> +			return INSN_REJECTED;
> +
> +		next = (struct decode_header *)
> +				((uintptr_t)h + decode_struct_sizes[type]);
> +
> +		if (!matched && (insn & h->mask.bits) != h->value.bits)
> +			continue;
> +
> +		if (!decode_regs(&insn, regs))
> +			return INSN_REJECTED;
> +
> +		switch (type) {
> +
> +		case DECODE_TYPE_TABLE: {
> +			struct decode_table *d = (struct decode_table *)h;
> +			next = (struct decode_header *)d->table.table;
> +			break;
> +		}
> +
> +		case DECODE_TYPE_CUSTOM: {
> +			struct decode_custom *d = (struct decode_custom *)h;
> +			return (*d->decoder.decoder)(insn, asi);
> +		}
> +
> +		case DECODE_TYPE_SIMULATE: {
> +			struct decode_simulate *d = (struct decode_simulate *)h;
> +			asi->insn_handler = d->handler.handler;
> +			return INSN_GOOD_NO_SLOT;
> +		}
> +
> +		case DECODE_TYPE_EMULATE: {
> +			struct decode_emulate *d = (struct decode_emulate *)h;
> +			asi->insn_handler = d->handler.handler;
> +			set_emulated_insn(insn, asi, thumb);
> +			return INSN_GOOD;
> +		}
> +
> +		case DECODE_TYPE_OR:
> +			matched = true;
> +			break;
> +
> +		case DECODE_TYPE_REJECT:
> +		default:
> +			return INSN_REJECTED;
> +		}
> +	}
> +}
> diff --git a/arch/arm/kernel/probes.h b/arch/arm/kernel/probes.h
> new file mode 100644
> index 0000000..56eec12
> --- /dev/null
> +++ b/arch/arm/kernel/probes.h
> @@ -0,0 +1,23 @@
> +#ifndef _ARM_KERNEL_PROBES_H
> +#define  _ARM_KERNEL_PROBES_H
> +
> +void __kprobes simulate_bbl(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes simulate_blx1(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes simulate_blx2bx(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes simulate_mrs(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes simulate_mov_ipsp(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes emulate_ldrdstrd(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes emulate_ldr(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes emulate_str(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes emulate_rd12rn16rm0rs8_rwflags(struct kprobe *p,
> +					      struct pt_regs *regs);
> +void __kprobes emulate_rd12rn16rm0_rwflags_nopc(struct kprobe *p,
> +						struct pt_regs *regs);
> +void __kprobes emulate_rd16rn12rm0rs8_rwflags_nopc(struct kprobe *p,
> +						   struct pt_regs *regs);
> +void __kprobes emulate_rd12rm0_noflags_nopc(struct kprobe *p,
> +					    struct pt_regs *regs);
> +void __kprobes emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(struct kprobe *p,
> +						       struct pt_regs *regs);
> +
> +#endif

-- 
Tixy

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH 8/9] ARM: Add "action" table for kprobes/uprobes instruction
  2013-08-01 23:45   ` David Long
@ 2013-08-29 14:09     ` Jon Medhurst (Tixy)
  -1 siblings, 0 replies; 32+ messages in thread
From: Jon Medhurst (Tixy) @ 2013-08-29 14:09 UTC (permalink / raw)
  To: David Long; +Cc: linux-arm-kernel, Rabin Vincent, linux-kernel

On Thu, 2013-08-01 at 19:45 -0400, David Long wrote:
> From: "David A. Long" <dave.long@linaro.org>
> 
> Add a table of functions used by the (previously) kprobes instruction
> parsing code so that it can also be used by uprobes. kprobes and
> (eventually) uprobes will each provide their own unique table of
> actions for each category of instruction recognized by the parser.
> 
> Signed-off-by: David A. Long <dave.long@linaro.org>
> ---

This is rather difficult to follow and I think it would be a lot easier
to understand if if was broken down into separate steps. E.g.

- A step which introduces the action enumeration to replace the function
  pointer (what the commit message to this patch implies it's doing).
- A step to add the extra 'void *d' argument to the decode functions, though
  I think this should be 'struct decode_header' not void as that's clearer
  and should avoid any user having to cast it.
- A step to add the 'usermode' to probes_decode_insn, and 'modify' 
  to decode_regs

More specific comments about this patch are inline below....



>  arch/arm/kernel/kprobes-arm.c    | 188 +++++++++++++++++----------------
>  arch/arm/kernel/kprobes-common.c |   3 +-
>  arch/arm/kernel/kprobes-thumb.c  | 217 +++++++++++++++++++++++++--------------
>  arch/arm/kernel/kprobes.c        |  11 +-
>  arch/arm/kernel/kprobes.h        |  64 ++++++------
>  arch/arm/kernel/probes-arm.c     |   2 +-
>  arch/arm/kernel/probes-arm.h     |  60 +++++++++++
>  arch/arm/kernel/probes-thumb.h   |  59 +++++++++++
>  arch/arm/kernel/probes.c         |  99 ++++++++++++------
>  arch/arm/kernel/probes.h         |  12 +++
>  10 files changed, 488 insertions(+), 227 deletions(-)
>  create mode 100644 arch/arm/kernel/probes-arm.h
>  create mode 100644 arch/arm/kernel/probes-thumb.h
> 
> diff --git a/arch/arm/kernel/kprobes-arm.c b/arch/arm/kernel/kprobes-arm.c
> index d6503cc..0620eba 100644
> --- a/arch/arm/kernel/kprobes-arm.c
> +++ b/arch/arm/kernel/kprobes-arm.c
> @@ -62,38 +62,50 @@
>  #include <linux/kprobes.h>
>  #include <linux/module.h>
>  
> -#include "probes.h"
>  #include "kprobes.h"
> -
> -/*
> - * To avoid 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 performance.
> - *
> - * 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 perform register writeback)
> - * 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.
> - */

Any reason to delete the above comment? I think it is still relevant and
has useful rational in.

> +#include "probes.h"
> +#include "probes-arm.h"
> +
> +const union decode_item kprobes_probes_actions[] = {

I think it's best if we don't reuse the decode_item type here, this is a
different sort of table so probably best to have it's own union. Also,
if we do that, then decode_item can be simplified as it won't need to
have function pointers in it, i.e. we could end up with...

typedef enum kprobe_insn (kprobe_custom_decode_t)(kprobe_opcode_t,
						  struct arch_specific_insn *,
						  void *);

union decode_action {
	kprobe_insn_handler_t	*handler;
	kprobe_custom_decode_t	*decoder;
};

union decode_item {
	u32			bits;
	const union decode_item	*table;
};



> +	[PROBES_EMULATE_NONE] = {.handler = probes_emulate_none},
> +	[PROBES_SIMULATE_NOP] = {.handler = probes_simulate_nop},
> +	[PROBES_PRELOAD_IMM] = {.handler = probes_simulate_nop},
> +	[PROBES_PRELOAD_REG] = {.handler = probes_simulate_nop},
> +	[PROBES_BRANCH_IMM] = {.handler = simulate_blx1},
> +	[PROBES_MRS] = {.handler = simulate_mrs},
> +	[PROBES_BRANCH_REG] = {.handler = simulate_blx2bx},
> +	[PROBES_CLZ] = {.handler = emulate_rd12rm0_noflags_nopc},
> +	[PROBES_SATURATING_ARITHMETIC] = {
> +		.handler = emulate_rd12rn16rm0_rwflags_nopc},
> +	[PROBES_MUL1] = {.handler = emulate_rdlo12rdhi16rn0rm8_rwflags_nopc},
> +	[PROBES_MUL2] = {.handler = emulate_rd16rn12rm0rs8_rwflags_nopc},
> +	[PROBES_SWP] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
> +	[PROBES_LDRSTRD] = {.handler = emulate_ldrdstrd},
> +	[PROBES_LOAD_EXTRA] = {.handler = emulate_ldr},
> +	[PROBES_LOAD] = {.handler = emulate_ldr},
> +	[PROBES_STORE_EXTRA] = {.handler = emulate_str},
> +	[PROBES_STORE] = {.handler = emulate_str},
> +	[PROBES_MOV_IP_SP] = {.handler = simulate_mov_ipsp},
> +	[PROBES_DATA_PROCESSING_REG] = {
> +		.handler = emulate_rd12rn16rm0rs8_rwflags},
> +	[PROBES_DATA_PROCESSING_IMM] = {
> +		.handler = emulate_rd12rn16rm0rs8_rwflags},
> +	[PROBES_MOV_HALFWORD] = {.handler = emulate_rd12rm0_noflags_nopc},
> +	[PROBES_SEV] = {.handler = probes_emulate_none},
> +	[PROBES_WFE] = {.handler = probes_simulate_nop},
> +	[PROBES_SATURATE] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
> +	[PROBES_REV] = {.handler = emulate_rd12rm0_noflags_nopc},
> +	[PROBES_MMI] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
> +	[PROBES_PACK] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
> +	[PROBES_EXTEND] = {.handler = emulate_rd12rm0_noflags_nopc},
> +	[PROBES_EXTEND_ADD] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
> +	[PROBES_MUL_ADD_LONG] = {
> +		.handler = emulate_rdlo12rdhi16rn0rm8_rwflags_nopc},
> +	[PROBES_MUL_ADD] = {.handler = emulate_rd16rn12rm0rs8_rwflags_nopc},
> +	[PROBES_BITFIELD] = {.handler = emulate_rd12rm0_noflags_nopc},
> +	[PROBES_BRANCH] = {.handler = simulate_bbl},
> +	[PROBES_LDMSTM] = {.decoder = kprobe_decode_ldmstm}
> +};
>  
>  /*
>   * For the instruction masking and comparisons in all the "space_*"
> @@ -112,16 +124,16 @@ static const union decode_item arm_1111_table[] = {
>  	/* PLDI (immediate)	1111 0100 x101 xxxx xxxx xxxx xxxx xxxx */
>  	/* PLDW (immediate)	1111 0101 x001 xxxx xxxx xxxx xxxx xxxx */
>  	/* PLD (immediate)	1111 0101 x101 xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_SIMULATE	(0xfe300000, 0xf4100000, probes_simulate_nop),
> +	DECODE_SIMULATE	(0xfe300000, 0xf4100000, PROBES_PRELOAD_IMM),
>  
>  	/* memory hint		1111 0110 x001 xxxx xxxx xxxx xxx0 xxxx */
>  	/* PLDI (register)	1111 0110 x101 xxxx xxxx xxxx xxx0 xxxx */
>  	/* PLDW (register)	1111 0111 x001 xxxx xxxx xxxx xxx0 xxxx */
>  	/* PLD (register)	1111 0111 x101 xxxx xxxx xxxx xxx0 xxxx */
> -	DECODE_SIMULATE	(0xfe300010, 0xf6100000, probes_simulate_nop),
> +	DECODE_SIMULATE	(0xfe300010, 0xf6100000, PROBES_PRELOAD_REG),
>  
>  	/* BLX (immediate)	1111 101x xxxx xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_SIMULATE	(0xfe000000, 0xfa000000, simulate_blx1),
> +	DECODE_SIMULATE	(0xfe000000, 0xfa000000, PROBES_BRANCH_IMM),
>  
>  	/* CPS			1111 0001 0000 xxx0 xxxx xxxx xx0x xxxx */
>  	/* SETEND		1111 0001 0000 0001 xxxx xxxx 0000 xxxx */
> @@ -145,25 +157,25 @@ static const union decode_item arm_cccc_0001_0xx0____0xxx_table[] = {
>  	/* Miscellaneous instructions					*/
>  
>  	/* MRS cpsr		cccc 0001 0000 xxxx xxxx xxxx 0000 xxxx */
> -	DECODE_SIMULATEX(0x0ff000f0, 0x01000000, simulate_mrs,
> +	DECODE_SIMULATEX(0x0ff000f0, 0x01000000, PROBES_MRS,
>  						 REGS(0, NOPC, 0, 0, 0)),
>  
>  	/* BX			cccc 0001 0010 xxxx xxxx xxxx 0001 xxxx */
> -	DECODE_SIMULATE	(0x0ff000f0, 0x01200010, simulate_blx2bx),
> +	DECODE_SIMULATE	(0x0ff000f0, 0x01200010, PROBES_BRANCH_REG),
>  
>  	/* BLX (register)	cccc 0001 0010 xxxx xxxx xxxx 0011 xxxx */
> -	DECODE_SIMULATEX(0x0ff000f0, 0x01200030, simulate_blx2bx,
> +	DECODE_SIMULATEX(0x0ff000f0, 0x01200030, PROBES_BRANCH_REG,
>  						 REGS(0, 0, 0, 0, NOPC)),
>  
>  	/* CLZ			cccc 0001 0110 xxxx xxxx xxxx 0001 xxxx */
> -	DECODE_EMULATEX	(0x0ff000f0, 0x01600010, emulate_rd12rm0_noflags_nopc,
> +	DECODE_EMULATEX	(0x0ff000f0, 0x01600010, PROBES_CLZ,
>  						 REGS(0, NOPC, 0, 0, NOPC)),
>  
>  	/* QADD			cccc 0001 0000 xxxx xxxx xxxx 0101 xxxx */
>  	/* QSUB			cccc 0001 0010 xxxx xxxx xxxx 0101 xxxx */
>  	/* QDADD		cccc 0001 0100 xxxx xxxx xxxx 0101 xxxx */
>  	/* QDSUB		cccc 0001 0110 xxxx xxxx xxxx 0101 xxxx */
> -	DECODE_EMULATEX	(0x0f9000f0, 0x01000050, emulate_rd12rn16rm0_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0f9000f0, 0x01000050, PROBES_SATURATING_ARITHMETIC,
>  						 REGS(NOPC, NOPC, 0, 0, NOPC)),
>  
>  	/* BXJ			cccc 0001 0010 xxxx xxxx xxxx 0010 xxxx */
> @@ -179,19 +191,19 @@ static const union decode_item arm_cccc_0001_0xx0____1xx0_table[] = {
>  	/* Halfword multiply and multiply-accumulate			*/
>  
>  	/* SMLALxy		cccc 0001 0100 xxxx xxxx xxxx 1xx0 xxxx */
> -	DECODE_EMULATEX	(0x0ff00090, 0x01400080, emulate_rdlo12rdhi16rn0rm8_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0ff00090, 0x01400080, PROBES_MUL1,
>  						 REGS(NOPC, NOPC, NOPC, 0, NOPC)),
>  
>  	/* SMULWy		cccc 0001 0010 xxxx xxxx xxxx 1x10 xxxx */
>  	DECODE_OR	(0x0ff000b0, 0x012000a0),
>  	/* SMULxy		cccc 0001 0110 xxxx xxxx xxxx 1xx0 xxxx */
> -	DECODE_EMULATEX	(0x0ff00090, 0x01600080, emulate_rd16rn12rm0rs8_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0ff00090, 0x01600080, PROBES_MUL2,
>  						 REGS(NOPC, 0, NOPC, 0, NOPC)),
>  
>  	/* SMLAxy		cccc 0001 0000 xxxx xxxx xxxx 1xx0 xxxx */
>  	DECODE_OR	(0x0ff00090, 0x01000080),
>  	/* SMLAWy		cccc 0001 0010 xxxx xxxx xxxx 1x00 xxxx */
> -	DECODE_EMULATEX	(0x0ff000b0, 0x01200080, emulate_rd16rn12rm0rs8_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0ff000b0, 0x01200080, PROBES_MUL2,
>  						 REGS(NOPC, NOPC, NOPC, 0, NOPC)),
>  
>  	DECODE_END
> @@ -202,14 +214,14 @@ static const union decode_item arm_cccc_0000_____1001_table[] = {
>  
>  	/* MUL			cccc 0000 0000 xxxx xxxx xxxx 1001 xxxx */
>  	/* MULS			cccc 0000 0001 xxxx xxxx xxxx 1001 xxxx */
> -	DECODE_EMULATEX	(0x0fe000f0, 0x00000090, emulate_rd16rn12rm0rs8_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0fe000f0, 0x00000090, PROBES_MUL2,
>  						 REGS(NOPC, 0, NOPC, 0, NOPC)),
>  
>  	/* MLA			cccc 0000 0010 xxxx xxxx xxxx 1001 xxxx */
>  	/* MLAS			cccc 0000 0011 xxxx xxxx xxxx 1001 xxxx */
>  	DECODE_OR	(0x0fe000f0, 0x00200090),
>  	/* MLS			cccc 0000 0110 xxxx xxxx xxxx 1001 xxxx */
> -	DECODE_EMULATEX	(0x0ff000f0, 0x00600090, emulate_rd16rn12rm0rs8_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0ff000f0, 0x00600090, PROBES_MUL2,
>  						 REGS(NOPC, NOPC, NOPC, 0, NOPC)),
>  
>  	/* UMAAL		cccc 0000 0100 xxxx xxxx xxxx 1001 xxxx */
> @@ -222,7 +234,7 @@ static const union decode_item arm_cccc_0000_____1001_table[] = {
>  	/* SMULLS		cccc 0000 1101 xxxx xxxx xxxx 1001 xxxx */
>  	/* SMLAL		cccc 0000 1110 xxxx xxxx xxxx 1001 xxxx */
>  	/* SMLALS		cccc 0000 1111 xxxx xxxx xxxx 1001 xxxx */
> -	DECODE_EMULATEX	(0x0f8000f0, 0x00800090, emulate_rdlo12rdhi16rn0rm8_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0f8000f0, 0x00800090, PROBES_MUL1,
>  						 REGS(NOPC, NOPC, NOPC, 0, NOPC)),
>  
>  	DECODE_END
> @@ -234,7 +246,7 @@ static const union decode_item arm_cccc_0001_____1001_table[] = {
>  #if __LINUX_ARM_ARCH__ < 6
>  	/* Deprecated on ARMv6 and may be UNDEFINED on v7		*/
>  	/* SMP/SWPB		cccc 0001 0x00 xxxx xxxx xxxx 1001 xxxx */
> -	DECODE_EMULATEX	(0x0fb000f0, 0x01000090, emulate_rd12rn16rm0_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0fb000f0, 0x01000090, PROBES_SWP,
>  						 REGS(NOPC, NOPC, 0, 0, NOPC)),
>  #endif
>  	/* LDREX/STREX{,D,B,H}	cccc 0001 1xxx xxxx xxxx xxxx 1001 xxxx */
> @@ -257,32 +269,32 @@ static const union decode_item arm_cccc_000x_____1xx1_table[] = {
>  
>  	/* LDRD (register)	cccc 000x x0x0 xxxx xxxx xxxx 1101 xxxx */
>  	/* STRD (register)	cccc 000x x0x0 xxxx xxxx xxxx 1111 xxxx */
> -	DECODE_EMULATEX	(0x0e5000d0, 0x000000d0, emulate_ldrdstrd,
> +	DECODE_EMULATEX	(0x0e5000d0, 0x000000d0, PROBES_LDRSTRD,
>  						 REGS(NOPCWB, NOPCX, 0, 0, NOPC)),
>  
>  	/* LDRD (immediate)	cccc 000x x1x0 xxxx xxxx xxxx 1101 xxxx */
>  	/* STRD (immediate)	cccc 000x x1x0 xxxx xxxx xxxx 1111 xxxx */
> -	DECODE_EMULATEX	(0x0e5000d0, 0x004000d0, emulate_ldrdstrd,
> +	DECODE_EMULATEX	(0x0e5000d0, 0x004000d0, PROBES_LDRSTRD,
>  						 REGS(NOPCWB, NOPCX, 0, 0, 0)),
>  
>  	/* STRH (register)	cccc 000x x0x0 xxxx xxxx xxxx 1011 xxxx */
> -	DECODE_EMULATEX	(0x0e5000f0, 0x000000b0, emulate_str,
> +	DECODE_EMULATEX	(0x0e5000f0, 0x000000b0, PROBES_STORE_EXTRA,
>  						 REGS(NOPCWB, NOPC, 0, 0, NOPC)),
>  
>  	/* LDRH (register)	cccc 000x x0x1 xxxx xxxx xxxx 1011 xxxx */
>  	/* LDRSB (register)	cccc 000x x0x1 xxxx xxxx xxxx 1101 xxxx */
>  	/* LDRSH (register)	cccc 000x x0x1 xxxx xxxx xxxx 1111 xxxx */
> -	DECODE_EMULATEX	(0x0e500090, 0x00100090, emulate_ldr,
> +	DECODE_EMULATEX	(0x0e500090, 0x00100090, PROBES_LOAD_EXTRA,
>  						 REGS(NOPCWB, NOPC, 0, 0, NOPC)),
>  
>  	/* STRH (immediate)	cccc 000x x1x0 xxxx xxxx xxxx 1011 xxxx */
> -	DECODE_EMULATEX	(0x0e5000f0, 0x004000b0, emulate_str,
> +	DECODE_EMULATEX	(0x0e5000f0, 0x004000b0, PROBES_STORE_EXTRA,
>  						 REGS(NOPCWB, NOPC, 0, 0, 0)),
>  
>  	/* LDRH (immediate)	cccc 000x x1x1 xxxx xxxx xxxx 1011 xxxx */
>  	/* LDRSB (immediate)	cccc 000x x1x1 xxxx xxxx xxxx 1101 xxxx */
>  	/* LDRSH (immediate)	cccc 000x x1x1 xxxx xxxx xxxx 1111 xxxx */
> -	DECODE_EMULATEX	(0x0e500090, 0x00500090, emulate_ldr,
> +	DECODE_EMULATEX	(0x0e500090, 0x00500090, PROBES_LOAD_EXTRA,
>  						 REGS(NOPCWB, NOPC, 0, 0, 0)),
>  
>  	DECODE_END
> @@ -295,18 +307,18 @@ static const union decode_item arm_cccc_000x_table[] = {
>  	DECODE_REJECT	(0x0e10f000, 0x0010f000),
>  
>  	/* MOV IP, SP		1110 0001 1010 0000 1100 0000 0000 1101 */
> -	DECODE_SIMULATE	(0xffffffff, 0xe1a0c00d, simulate_mov_ipsp),
> +	DECODE_SIMULATE	(0xffffffff, 0xe1a0c00d, PROBES_MOV_IP_SP),
>  
>  	/* TST (register)	cccc 0001 0001 xxxx xxxx xxxx xxx0 xxxx */
>  	/* TEQ (register)	cccc 0001 0011 xxxx xxxx xxxx xxx0 xxxx */
>  	/* CMP (register)	cccc 0001 0101 xxxx xxxx xxxx xxx0 xxxx */
>  	/* CMN (register)	cccc 0001 0111 xxxx xxxx xxxx xxx0 xxxx */
> -	DECODE_EMULATEX	(0x0f900010, 0x01100000, emulate_rd12rn16rm0rs8_rwflags,
> +	DECODE_EMULATEX	(0x0f900010, 0x01100000, PROBES_DATA_PROCESSING_REG,
>  						 REGS(ANY, 0, 0, 0, ANY)),
>  
>  	/* MOV (register)	cccc 0001 101x xxxx xxxx xxxx xxx0 xxxx */
>  	/* MVN (register)	cccc 0001 111x xxxx xxxx xxxx xxx0 xxxx */
> -	DECODE_EMULATEX	(0x0fa00010, 0x01a00000, emulate_rd12rn16rm0rs8_rwflags,
> +	DECODE_EMULATEX	(0x0fa00010, 0x01a00000, PROBES_DATA_PROCESSING_REG,
>  						 REGS(0, ANY, 0, 0, ANY)),
>  
>  	/* AND (register)	cccc 0000 000x xxxx xxxx xxxx xxx0 xxxx */
> @@ -319,19 +331,19 @@ static const union decode_item arm_cccc_000x_table[] = {
>  	/* RSC (register)	cccc 0000 111x xxxx xxxx xxxx xxx0 xxxx */
>  	/* ORR (register)	cccc 0001 100x xxxx xxxx xxxx xxx0 xxxx */
>  	/* BIC (register)	cccc 0001 110x xxxx xxxx xxxx xxx0 xxxx */
> -	DECODE_EMULATEX	(0x0e000010, 0x00000000, emulate_rd12rn16rm0rs8_rwflags,
> +	DECODE_EMULATEX	(0x0e000010, 0x00000000, PROBES_DATA_PROCESSING_REG,
>  						 REGS(ANY, ANY, 0, 0, ANY)),
>  
>  	/* TST (reg-shift reg)	cccc 0001 0001 xxxx xxxx xxxx 0xx1 xxxx */
>  	/* TEQ (reg-shift reg)	cccc 0001 0011 xxxx xxxx xxxx 0xx1 xxxx */
>  	/* CMP (reg-shift reg)	cccc 0001 0101 xxxx xxxx xxxx 0xx1 xxxx */
>  	/* CMN (reg-shift reg)	cccc 0001 0111 xxxx xxxx xxxx 0xx1 xxxx */
> -	DECODE_EMULATEX	(0x0f900090, 0x01100010, emulate_rd12rn16rm0rs8_rwflags,
> +	DECODE_EMULATEX	(0x0f900090, 0x01100010, PROBES_DATA_PROCESSING_REG,
>  						 REGS(ANY, 0, NOPC, 0, ANY)),
>  
>  	/* MOV (reg-shift reg)	cccc 0001 101x xxxx xxxx xxxx 0xx1 xxxx */
>  	/* MVN (reg-shift reg)	cccc 0001 111x xxxx xxxx xxxx 0xx1 xxxx */
> -	DECODE_EMULATEX	(0x0fa00090, 0x01a00010, emulate_rd12rn16rm0rs8_rwflags,
> +	DECODE_EMULATEX	(0x0fa00090, 0x01a00010, PROBES_DATA_PROCESSING_REG,
>  						 REGS(0, ANY, NOPC, 0, ANY)),
>  
>  	/* AND (reg-shift reg)	cccc 0000 000x xxxx xxxx xxxx 0xx1 xxxx */
> @@ -344,7 +356,7 @@ static const union decode_item arm_cccc_000x_table[] = {
>  	/* RSC (reg-shift reg)	cccc 0000 111x xxxx xxxx xxxx 0xx1 xxxx */
>  	/* ORR (reg-shift reg)	cccc 0001 100x xxxx xxxx xxxx 0xx1 xxxx */
>  	/* BIC (reg-shift reg)	cccc 0001 110x xxxx xxxx xxxx 0xx1 xxxx */
> -	DECODE_EMULATEX	(0x0e000090, 0x00000010, emulate_rd12rn16rm0rs8_rwflags,
> +	DECODE_EMULATEX	(0x0e000090, 0x00000010, PROBES_DATA_PROCESSING_REG,
>  						 REGS(ANY, ANY, NOPC, 0, ANY)),
>  
>  	DECODE_END
> @@ -355,17 +367,17 @@ static const union decode_item arm_cccc_001x_table[] = {
>  
>  	/* MOVW			cccc 0011 0000 xxxx xxxx xxxx xxxx xxxx */
>  	/* MOVT			cccc 0011 0100 xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0x0fb00000, 0x03000000, emulate_rd12rm0_noflags_nopc,
> +	DECODE_EMULATEX	(0x0fb00000, 0x03000000, PROBES_DATA_PROCESSING_IMM,
>  						 REGS(0, NOPC, 0, 0, 0)),
>  
>  	/* YIELD		cccc 0011 0010 0000 xxxx xxxx 0000 0001 */
>  	DECODE_OR	(0x0fff00ff, 0x03200001),
>  	/* SEV			cccc 0011 0010 0000 xxxx xxxx 0000 0100 */
> -	DECODE_EMULATE	(0x0fff00ff, 0x03200004, probes_emulate_none),
> +	DECODE_EMULATE	(0x0fff00ff, 0x03200004, PROBES_EMULATE_NONE),
>  	/* NOP			cccc 0011 0010 0000 xxxx xxxx 0000 0000 */
>  	/* WFE			cccc 0011 0010 0000 xxxx xxxx 0000 0010 */
>  	/* WFI			cccc 0011 0010 0000 xxxx xxxx 0000 0011 */
> -	DECODE_SIMULATE	(0x0fff00fc, 0x03200000, probes_simulate_nop),
> +	DECODE_SIMULATE	(0x0fff00fc, 0x03200000, PROBES_SIMULATE_NOP),
>  	/* DBG			cccc 0011 0010 0000 xxxx xxxx ffff xxxx */
>  	/* unallocated hints	cccc 0011 0010 0000 xxxx xxxx xxxx xxxx */
>  	/* MSR (immediate)	cccc 0011 0x10 xxxx xxxx xxxx xxxx xxxx */
> @@ -378,12 +390,12 @@ static const union decode_item arm_cccc_001x_table[] = {
>  	/* TEQ (immediate)	cccc 0011 0011 xxxx xxxx xxxx xxxx xxxx */
>  	/* CMP (immediate)	cccc 0011 0101 xxxx xxxx xxxx xxxx xxxx */
>  	/* CMN (immediate)	cccc 0011 0111 xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0x0f900000, 0x03100000, emulate_rd12rn16rm0rs8_rwflags,
> +	DECODE_EMULATEX	(0x0f900000, 0x03100000, PROBES_DATA_PROCESSING_IMM,
>  						 REGS(ANY, 0, 0, 0, 0)),
>  
>  	/* MOV (immediate)	cccc 0011 101x xxxx xxxx xxxx xxxx xxxx */
>  	/* MVN (immediate)	cccc 0011 111x xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0x0fa00000, 0x03a00000, emulate_rd12rn16rm0rs8_rwflags,
> +	DECODE_EMULATEX	(0x0fa00000, 0x03a00000, PROBES_DATA_PROCESSING_IMM,
>  						 REGS(0, ANY, 0, 0, 0)),
>  
>  	/* AND (immediate)	cccc 0010 000x xxxx xxxx xxxx xxxx xxxx */
> @@ -396,7 +408,7 @@ static const union decode_item arm_cccc_001x_table[] = {
>  	/* RSC (immediate)	cccc 0010 111x xxxx xxxx xxxx xxxx xxxx */
>  	/* ORR (immediate)	cccc 0011 100x xxxx xxxx xxxx xxxx xxxx */
>  	/* BIC (immediate)	cccc 0011 110x xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0x0e000000, 0x02000000, emulate_rd12rn16rm0rs8_rwflags,
> +	DECODE_EMULATEX	(0x0e000000, 0x02000000, PROBES_DATA_PROCESSING_IMM,
>  						 REGS(ANY, ANY, 0, 0, 0)),
>  
>  	DECODE_END
> @@ -406,7 +418,7 @@ static const union decode_item arm_cccc_0110_____xxx1_table[] = {
>  	/* Media instructions						*/
>  
>  	/* SEL			cccc 0110 1000 xxxx xxxx xxxx 1011 xxxx */
> -	DECODE_EMULATEX	(0x0ff000f0, 0x068000b0, emulate_rd12rn16rm0_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0ff000f0, 0x068000b0, PROBES_SATURATE,
>  						 REGS(NOPC, NOPC, 0, 0, NOPC)),
>  
>  	/* SSAT			cccc 0110 101x xxxx xxxx xxxx xx01 xxxx */
> @@ -414,14 +426,14 @@ static const union decode_item arm_cccc_0110_____xxx1_table[] = {
>  	DECODE_OR(0x0fa00030, 0x06a00010),
>  	/* SSAT16		cccc 0110 1010 xxxx xxxx xxxx 0011 xxxx */
>  	/* USAT16		cccc 0110 1110 xxxx xxxx xxxx 0011 xxxx */
> -	DECODE_EMULATEX	(0x0fb000f0, 0x06a00030, emulate_rd12rn16rm0_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0fb000f0, 0x06a00030, PROBES_SATURATE,
>  						 REGS(0, NOPC, 0, 0, NOPC)),
>  
>  	/* REV			cccc 0110 1011 xxxx xxxx xxxx 0011 xxxx */
>  	/* REV16		cccc 0110 1011 xxxx xxxx xxxx 1011 xxxx */
>  	/* RBIT			cccc 0110 1111 xxxx xxxx xxxx 0011 xxxx */
>  	/* REVSH		cccc 0110 1111 xxxx xxxx xxxx 1011 xxxx */
> -	DECODE_EMULATEX	(0x0fb00070, 0x06b00030, emulate_rd12rm0_noflags_nopc,
> +	DECODE_EMULATEX	(0x0fb00070, 0x06b00030, PROBES_REV,
>  						 REGS(0, NOPC, 0, 0, NOPC)),
>  
>  	/* ???			cccc 0110 0x00 xxxx xxxx xxxx xxx1 xxxx */
> @@ -466,12 +478,12 @@ static const union decode_item arm_cccc_0110_____xxx1_table[] = {
>  	/* 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 */
> -	DECODE_EMULATEX	(0x0f800010, 0x06000010, emulate_rd12rn16rm0_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0f800010, 0x06000010, PROBES_MMI,
>  						 REGS(NOPC, NOPC, 0, 0, NOPC)),
>  
>  	/* PKHBT		cccc 0110 1000 xxxx xxxx xxxx x001 xxxx */
>  	/* PKHTB		cccc 0110 1000 xxxx xxxx xxxx x101 xxxx */
> -	DECODE_EMULATEX	(0x0ff00030, 0x06800010, emulate_rd12rn16rm0_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0ff00030, 0x06800010, PROBES_PACK,
>  						 REGS(NOPC, NOPC, 0, 0, NOPC)),
>  
>  	/* ???			cccc 0110 1001 xxxx xxxx xxxx 0111 xxxx */
> @@ -484,7 +496,7 @@ static const union decode_item arm_cccc_0110_____xxx1_table[] = {
>  	/* UXTB16		cccc 0110 1100 1111 xxxx xxxx 0111 xxxx */
>  	/* UXTB			cccc 0110 1110 1111 xxxx xxxx 0111 xxxx */
>  	/* UXTH			cccc 0110 1111 1111 xxxx xxxx 0111 xxxx */
> -	DECODE_EMULATEX	(0x0f8f00f0, 0x068f0070, emulate_rd12rm0_noflags_nopc,
> +	DECODE_EMULATEX	(0x0f8f00f0, 0x068f0070, PROBES_EXTEND,
>  						 REGS(0, NOPC, 0, 0, NOPC)),
>  
>  	/* SXTAB16		cccc 0110 1000 xxxx xxxx xxxx 0111 xxxx */
> @@ -493,7 +505,7 @@ static const union decode_item arm_cccc_0110_____xxx1_table[] = {
>  	/* 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 */
> -	DECODE_EMULATEX	(0x0f8000f0, 0x06800070, emulate_rd12rn16rm0_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0f8000f0, 0x06800070, PROBES_EXTEND_ADD,
>  						 REGS(NOPCX, NOPC, 0, 0, NOPC)),
>  
>  	DECODE_END
> @@ -507,7 +519,7 @@ static const union decode_item arm_cccc_0111_____xxx1_table[] = {
>  
>  	/* SMLALD		cccc 0111 0100 xxxx xxxx xxxx 00x1 xxxx */
>  	/* SMLSLD		cccc 0111 0100 xxxx xxxx xxxx 01x1 xxxx */
> -	DECODE_EMULATEX	(0x0ff00090, 0x07400010, emulate_rdlo12rdhi16rn0rm8_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0ff00090, 0x07400010, PROBES_MUL_ADD_LONG,
>  						 REGS(NOPC, NOPC, NOPC, 0, NOPC)),
>  
>  	/* SMUAD		cccc 0111 0000 xxxx 1111 xxxx 00x1 xxxx */
> @@ -516,7 +528,7 @@ static const union decode_item arm_cccc_0111_____xxx1_table[] = {
>  	/* SMMUL		cccc 0111 0101 xxxx 1111 xxxx 00x1 xxxx */
>  	DECODE_OR	(0x0ff0f0d0, 0x0750f010),
>  	/* USAD8		cccc 0111 1000 xxxx 1111 xxxx 0001 xxxx */
> -	DECODE_EMULATEX	(0x0ff0f0f0, 0x0780f010, emulate_rd16rn12rm0rs8_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0ff0f0f0, 0x0780f010, PROBES_MUL_ADD,
>  						 REGS(NOPC, 0, NOPC, 0, NOPC)),
>  
>  	/* SMLAD		cccc 0111 0000 xxxx xxxx xxxx 00x1 xxxx */
> @@ -525,24 +537,24 @@ static const union decode_item arm_cccc_0111_____xxx1_table[] = {
>  	/* SMMLA		cccc 0111 0101 xxxx xxxx xxxx 00x1 xxxx */
>  	DECODE_OR	(0x0ff000d0, 0x07500010),
>  	/* USADA8		cccc 0111 1000 xxxx xxxx xxxx 0001 xxxx */
> -	DECODE_EMULATEX	(0x0ff000f0, 0x07800010, emulate_rd16rn12rm0rs8_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0ff000f0, 0x07800010, PROBES_MUL_ADD,
>  						 REGS(NOPC, NOPCX, NOPC, 0, NOPC)),
>  
>  	/* SMMLS		cccc 0111 0101 xxxx xxxx xxxx 11x1 xxxx */
> -	DECODE_EMULATEX	(0x0ff000d0, 0x075000d0, emulate_rd16rn12rm0rs8_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0ff000d0, 0x075000d0, PROBES_MUL_ADD,
>  						 REGS(NOPC, NOPC, NOPC, 0, NOPC)),
>  
>  	/* SBFX			cccc 0111 101x xxxx xxxx xxxx x101 xxxx */
>  	/* UBFX			cccc 0111 111x xxxx xxxx xxxx x101 xxxx */
> -	DECODE_EMULATEX	(0x0fa00070, 0x07a00050, emulate_rd12rm0_noflags_nopc,
> +	DECODE_EMULATEX	(0x0fa00070, 0x07a00050, PROBES_BITFIELD,
>  						 REGS(0, NOPC, 0, 0, NOPC)),
>  
>  	/* BFC			cccc 0111 110x xxxx xxxx xxxx x001 1111 */
> -	DECODE_EMULATEX	(0x0fe0007f, 0x07c0001f, emulate_rd12rm0_noflags_nopc,
> +	DECODE_EMULATEX	(0x0fe0007f, 0x07c0001f, PROBES_BITFIELD,
>  						 REGS(0, NOPC, 0, 0, 0)),
>  
>  	/* BFI			cccc 0111 110x xxxx xxxx xxxx x001 xxxx */
> -	DECODE_EMULATEX	(0x0fe00070, 0x07c00010, emulate_rd12rm0_noflags_nopc,
> +	DECODE_EMULATEX	(0x0fe00070, 0x07c00010, PROBES_BITFIELD,
>  						 REGS(0, NOPC, 0, 0, NOPCX)),
>  
>  	DECODE_END
> @@ -562,22 +574,22 @@ static const union decode_item arm_cccc_01xx_table[] = {
>  
>  	/* STR (immediate)	cccc 010x x0x0 xxxx xxxx xxxx xxxx xxxx */
>  	/* STRB (immediate)	cccc 010x x1x0 xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0x0e100000, 0x04000000, emulate_str,
> +	DECODE_EMULATEX	(0x0e100000, 0x04000000, PROBES_STORE,
>  						 REGS(NOPCWB, ANY, 0, 0, 0)),
>  
>  	/* LDR (immediate)	cccc 010x x0x1 xxxx xxxx xxxx xxxx xxxx */
>  	/* LDRB (immediate)	cccc 010x x1x1 xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0x0e100000, 0x04100000, emulate_ldr,
> +	DECODE_EMULATEX	(0x0e100000, 0x04100000, PROBES_LOAD,
>  						 REGS(NOPCWB, ANY, 0, 0, 0)),
>  
>  	/* STR (register)	cccc 011x x0x0 xxxx xxxx xxxx xxxx xxxx */
>  	/* STRB (register)	cccc 011x x1x0 xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0x0e100000, 0x06000000, emulate_str,
> +	DECODE_EMULATEX	(0x0e100000, 0x06000000, PROBES_STORE,
>  						 REGS(NOPCWB, ANY, 0, 0, NOPC)),
>  
>  	/* LDR (register)	cccc 011x x0x1 xxxx xxxx xxxx xxxx xxxx */
>  	/* LDRB (register)	cccc 011x x1x1 xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0x0e100000, 0x06100000, emulate_ldr,
> +	DECODE_EMULATEX	(0x0e100000, 0x06100000, PROBES_LOAD,
>  						 REGS(NOPCWB, ANY, 0, 0, NOPC)),
>  
>  	DECODE_END
> @@ -588,7 +600,7 @@ static const union decode_item arm_cccc_100x_table[] = {
>  
>  	/* LDM			cccc 100x x0x1 xxxx xxxx xxxx xxxx xxxx */
>  	/* STM			cccc 100x x0x0 xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_CUSTOM	(0x0e400000, 0x08000000, kprobe_decode_ldmstm),
> +	DECODE_CUSTOM	(0x0e400000, 0x08000000, PROBES_LDMSTM),
>  
>  	/* STM (user registers)	cccc 100x x1x0 xxxx xxxx xxxx xxxx xxxx */
>  	/* LDM (user registers)	cccc 100x x1x1 xxxx 0xxx xxxx xxxx xxxx */
> @@ -668,7 +680,7 @@ const union decode_item kprobe_decode_arm_table[] = {
>  
>  	/* B			cccc 1010 xxxx xxxx xxxx xxxx xxxx xxxx */
>  	/* BL			cccc 1011 xxxx xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_SIMULATE	(0x0e000000, 0x0a000000, simulate_bbl),
> +	DECODE_SIMULATE	(0x0e000000, 0x0a000000, PROBES_BRANCH),
>  
>  	/*
>  	 * Supervisor Call, and coprocessor instructions
> @@ -709,9 +721,11 @@ static void __kprobes arm_singlestep(struct kprobe *p, struct pt_regs *regs)
>   *   should also be very rare.
>   */
>  enum kprobe_insn __kprobes
> -arm_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi)
> +arm_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> +			bool usermode, const union decode_item *actions)
>  {
>  	asi->insn_singlestep = arm_singlestep;
>  	asi->insn_check_cc = kprobe_condition_checks[insn>>28];
> -	return kprobe_decode_insn(insn, asi, kprobe_decode_arm_table, false);
> +	return probes_decode_insn(insn, asi, kprobe_decode_arm_table, false,
> +				  usermode, actions);
>  }
> diff --git a/arch/arm/kernel/kprobes-common.c b/arch/arm/kernel/kprobes-common.c
> index b66e9f7..4e19498 100644
> --- a/arch/arm/kernel/kprobes-common.c
> +++ b/arch/arm/kernel/kprobes-common.c
> @@ -268,7 +268,8 @@ emulate_ldm_r3_15(struct kprobe *p, struct pt_regs *regs)
>  }
>  
>  enum kprobe_insn __kprobes
> -kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi)
> +kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> +		     void *d)
>  {
>  	kprobe_insn_handler_t *handler = 0;
>  	unsigned reglist = insn & 0xffff;
> diff --git a/arch/arm/kernel/kprobes-thumb.c b/arch/arm/kernel/kprobes-thumb.c
> index 173b2bc..f5d57a8 100644
> --- a/arch/arm/kernel/kprobes-thumb.c
> +++ b/arch/arm/kernel/kprobes-thumb.c
> @@ -13,6 +13,8 @@
>  #include <linux/module.h>
>  
>  #include "kprobes.h"
> +#include "probes.h"
> +#include "probes-thumb.h"
>  
> 
>  /*
> @@ -83,7 +85,8 @@ t32_simulate_cond_branch(struct kprobe *p, struct pt_regs *regs)
>  }
>  
>  static enum kprobe_insn __kprobes
> -t32_decode_cond_branch(kprobe_opcode_t insn, struct arch_specific_insn *asi)
> +t32_decode_cond_branch(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> +		       void *d)
>  {
>  	int cc = (insn >> 22) & 0xf;
>  	asi->insn_check_cc = kprobe_condition_checks[cc];
> @@ -158,9 +161,10 @@ t32_simulate_ldr_literal(struct kprobe *p, struct pt_regs *regs)
>  }
>  
>  static enum kprobe_insn __kprobes
> -t32_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi)
> +t32_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> +		  void *d)
>  {
> -	enum kprobe_insn ret = kprobe_decode_ldmstm(insn, asi);
> +	enum kprobe_insn ret = kprobe_decode_ldmstm(insn, asi, d);
>  
>  	/* Fixup modified instruction to have halfwords in correct order...*/
>  	insn = asi->insn[0];
> @@ -344,7 +348,7 @@ static const union decode_item t32_table_1110_100x_x0xx[] = {
>  	/* LDMIA		1110 1000 10x1 xxxx xxxx xxxx xxxx xxxx */
>  	/* STMDB		1110 1001 00x0 xxxx xxxx xxxx xxxx xxxx */
>  	/* LDMDB		1110 1001 00x1 xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_CUSTOM	(0xfe400000, 0xe8000000, t32_decode_ldmstm),
> +	DECODE_CUSTOM	(0xfe400000, 0xe8000000, PROBES_T32_LDMSTM),
>  
>  	DECODE_END
>  };
> @@ -357,12 +361,12 @@ static const union decode_item t32_table_1110_100x_x1xx[] = {
>  	DECODE_OR	(0xff600000, 0xe8600000),
>  	/* STRD (immediate)	1110 1001 x1x0 xxxx xxxx xxxx xxxx xxxx */
>  	/* LDRD (immediate)	1110 1001 x1x1 xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0xff400000, 0xe9400000, t32_emulate_ldrdstrd,
> +	DECODE_EMULATEX	(0xff400000, 0xe9400000, PROBES_T32_LDRDSTRD,
>  						 REGS(NOPCWB, NOSPPC, NOSPPC, 0, 0)),
>  
>  	/* TBB			1110 1000 1101 xxxx xxxx xxxx 0000 xxxx */
>  	/* TBH			1110 1000 1101 xxxx xxxx xxxx 0001 xxxx */
> -	DECODE_SIMULATEX(0xfff000e0, 0xe8d00000, t32_simulate_table_branch,
> +	DECODE_SIMULATEX(0xfff000e0, 0xe8d00000, PROBES_T32_TABLE_BRANCH,
>  						 REGS(NOSP, 0, 0, 0, NOSPPC)),
>  
>  	/* STREX		1110 1000 0100 xxxx xxxx xxxx xxxx xxxx */
> @@ -382,18 +386,18 @@ static const union decode_item t32_table_1110_101x[] = {
>  
>  	/* TST			1110 1010 0001 xxxx xxxx 1111 xxxx xxxx */
>  	/* TEQ			1110 1010 1001 xxxx xxxx 1111 xxxx xxxx */
> -	DECODE_EMULATEX	(0xff700f00, 0xea100f00, t32_emulate_rd8rn16rm0_rwflags,
> +	DECODE_EMULATEX	(0xff700f00, 0xea100f00, PROBES_T32_TST,
>  						 REGS(NOSPPC, 0, 0, 0, NOSPPC)),
>  
>  	/* CMN			1110 1011 0001 xxxx xxxx 1111 xxxx xxxx */
>  	DECODE_OR	(0xfff00f00, 0xeb100f00),
>  	/* CMP			1110 1011 1011 xxxx xxxx 1111 xxxx xxxx */
> -	DECODE_EMULATEX	(0xfff00f00, 0xebb00f00, t32_emulate_rd8rn16rm0_rwflags,
> +	DECODE_EMULATEX	(0xfff00f00, 0xebb00f00, PROBES_T32_TST,
>  						 REGS(NOPC, 0, 0, 0, NOSPPC)),
>  
>  	/* MOV			1110 1010 010x 1111 xxxx xxxx xxxx xxxx */
>  	/* MVN			1110 1010 011x 1111 xxxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0xffcf0000, 0xea4f0000, t32_emulate_rd8rn16rm0_rwflags,
> +	DECODE_EMULATEX	(0xffcf0000, 0xea4f0000, PROBES_T32_MOV,
>  						 REGS(0, 0, NOSPPC, 0, NOSPPC)),
>  
>  	/* ???			1110 1010 101x xxxx xxxx xxxx xxxx xxxx */
> @@ -408,7 +412,7 @@ static const union decode_item t32_table_1110_101x[] = {
>  
>  	/* ADD/SUB SP, SP, Rm, LSL #0..3				*/
>  	/*			1110 1011 x0xx 1101 x000 1101 xx00 xxxx */
> -	DECODE_EMULATEX	(0xff4f7f30, 0xeb0d0d00, t32_emulate_rd8rn16rm0_rwflags,
> +	DECODE_EMULATEX	(0xff4f7f30, 0xeb0d0d00, PROBES_T32_ADDSUB,
>  						 REGS(SP, 0, SP, 0, NOSPPC)),
>  
>  	/* ADD/SUB SP, SP, Rm, shift					*/
> @@ -417,7 +421,7 @@ static const union decode_item t32_table_1110_101x[] = {
>  
>  	/* ADD/SUB Rd, SP, Rm, shift					*/
>  	/*			1110 1011 x0xx 1101 xxxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0xff4f0000, 0xeb0d0000, t32_emulate_rd8rn16rm0_rwflags,
> +	DECODE_EMULATEX	(0xff4f0000, 0xeb0d0000, PROBES_T32_ADDSUB,
>  						 REGS(SP, 0, NOPC, 0, NOSPPC)),
>  
>  	/* AND			1110 1010 000x xxxx xxxx xxxx xxxx xxxx */
> @@ -431,7 +435,7 @@ static const union decode_item t32_table_1110_101x[] = {
>  	/* SBC			1110 1011 011x xxxx xxxx xxxx xxxx xxxx */
>  	/* SUB			1110 1011 101x xxxx xxxx xxxx xxxx xxxx */
>  	/* RSB			1110 1011 110x xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0xfe000000, 0xea000000, t32_emulate_rd8rn16rm0_rwflags,
> +	DECODE_EMULATEX	(0xfe000000, 0xea000000, PROBES_T32_LOGICAL,
>  						 REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)),
>  
>  	DECODE_END
> @@ -442,18 +446,18 @@ static const union decode_item t32_table_1111_0x0x___0[] = {
>  
>  	/* TST			1111 0x00 0001 xxxx 0xxx 1111 xxxx xxxx */
>  	/* TEQ			1111 0x00 1001 xxxx 0xxx 1111 xxxx xxxx */
> -	DECODE_EMULATEX	(0xfb708f00, 0xf0100f00, t32_emulate_rd8rn16rm0_rwflags,
> +	DECODE_EMULATEX	(0xfb708f00, 0xf0100f00, PROBES_T32_TST,
>  						 REGS(NOSPPC, 0, 0, 0, 0)),
>  
>  	/* CMN			1111 0x01 0001 xxxx 0xxx 1111 xxxx xxxx */
>  	DECODE_OR	(0xfbf08f00, 0xf1100f00),
>  	/* CMP			1111 0x01 1011 xxxx 0xxx 1111 xxxx xxxx */
> -	DECODE_EMULATEX	(0xfbf08f00, 0xf1b00f00, t32_emulate_rd8rn16rm0_rwflags,
> +	DECODE_EMULATEX	(0xfbf08f00, 0xf1b00f00, PROBES_T32_CMP,
>  						 REGS(NOPC, 0, 0, 0, 0)),
>  
>  	/* MOV			1111 0x00 010x 1111 0xxx xxxx xxxx xxxx */
>  	/* MVN			1111 0x00 011x 1111 0xxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0xfbcf8000, 0xf04f0000, t32_emulate_rd8rn16rm0_rwflags,
> +	DECODE_EMULATEX	(0xfbcf8000, 0xf04f0000, PROBES_T32_MOV,
>  						 REGS(0, 0, NOSPPC, 0, 0)),
>  
>  	/* ???			1111 0x00 101x xxxx 0xxx xxxx xxxx xxxx */
> @@ -470,7 +474,7 @@ static const union decode_item t32_table_1111_0x0x___0[] = {
>  
>  	/* ADD Rd, SP, #imm	1111 0x01 000x 1101 0xxx xxxx xxxx xxxx */
>  	/* SUB Rd, SP, #imm	1111 0x01 101x 1101 0xxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0xfb4f8000, 0xf10d0000, t32_emulate_rd8rn16rm0_rwflags,
> +	DECODE_EMULATEX	(0xfb4f8000, 0xf10d0000, PROBES_T32_ADDSUB,
>  						 REGS(SP, 0, NOPC, 0, 0)),
>  
>  	/* AND			1111 0x00 000x xxxx 0xxx xxxx xxxx xxxx */
> @@ -483,7 +487,7 @@ static const union decode_item t32_table_1111_0x0x___0[] = {
>  	/* SBC			1111 0x01 011x xxxx 0xxx xxxx xxxx xxxx */
>  	/* SUB			1111 0x01 101x xxxx 0xxx xxxx xxxx xxxx */
>  	/* RSB			1111 0x01 110x xxxx 0xxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0xfa008000, 0xf0000000, t32_emulate_rd8rn16rm0_rwflags,
> +	DECODE_EMULATEX	(0xfa008000, 0xf0000000, PROBES_T32_LOGICAL,
>  						 REGS(NOSPPC, 0, NOSPPC, 0, 0)),
>  
>  	DECODE_END
> @@ -495,44 +499,44 @@ static const union decode_item t32_table_1111_0x1x___0[] = {
>  	/* ADDW Rd, PC, #imm	1111 0x10 0000 1111 0xxx xxxx xxxx xxxx */
>  	DECODE_OR	(0xfbff8000, 0xf20f0000),
>  	/* SUBW	Rd, PC, #imm	1111 0x10 1010 1111 0xxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0xfbff8000, 0xf2af0000, t32_emulate_rd8pc16_noflags,
> +	DECODE_EMULATEX	(0xfbff8000, 0xf2af0000, PROBES_T32_ADDWSUBW_PC,
>  						 REGS(PC, 0, NOSPPC, 0, 0)),
>  
>  	/* ADDW SP, SP, #imm	1111 0x10 0000 1101 0xxx 1101 xxxx xxxx */
>  	DECODE_OR	(0xfbff8f00, 0xf20d0d00),
>  	/* SUBW	SP, SP, #imm	1111 0x10 1010 1101 0xxx 1101 xxxx xxxx */
> -	DECODE_EMULATEX	(0xfbff8f00, 0xf2ad0d00, t32_emulate_rd8rn16_noflags,
> +	DECODE_EMULATEX	(0xfbff8f00, 0xf2ad0d00, PROBES_T32_ADDWSUBW,
>  						 REGS(SP, 0, SP, 0, 0)),
>  
>  	/* ADDW			1111 0x10 0000 xxxx 0xxx xxxx xxxx xxxx */
>  	DECODE_OR	(0xfbf08000, 0xf2000000),
>  	/* SUBW			1111 0x10 1010 xxxx 0xxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0xfbf08000, 0xf2a00000, t32_emulate_rd8rn16_noflags,
> +	DECODE_EMULATEX	(0xfbf08000, 0xf2a00000, PROBES_T32_ADDWSUBW,
>  						 REGS(NOPCX, 0, NOSPPC, 0, 0)),
>  
>  	/* MOVW			1111 0x10 0100 xxxx 0xxx xxxx xxxx xxxx */
>  	/* MOVT			1111 0x10 1100 xxxx 0xxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0xfb708000, 0xf2400000, t32_emulate_rd8rn16_noflags,
> +	DECODE_EMULATEX	(0xfb708000, 0xf2400000, PROBES_T32_MOVW,
>  						 REGS(0, 0, NOSPPC, 0, 0)),
>  
>  	/* SSAT16		1111 0x11 0010 xxxx 0000 xxxx 00xx xxxx */
>  	/* SSAT			1111 0x11 00x0 xxxx 0xxx xxxx xxxx xxxx */
>  	/* USAT16		1111 0x11 1010 xxxx 0000 xxxx 00xx xxxx */
>  	/* USAT			1111 0x11 10x0 xxxx 0xxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0xfb508000, 0xf3000000, t32_emulate_rd8rn16rm0_rwflags,
> +	DECODE_EMULATEX	(0xfb508000, 0xf3000000, PROBES_T32_SAT,
>  						 REGS(NOSPPC, 0, NOSPPC, 0, 0)),
>  
>  	/* SFBX			1111 0x11 0100 xxxx 0xxx xxxx xxxx xxxx */
>  	/* UFBX			1111 0x11 1100 xxxx 0xxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0xfb708000, 0xf3400000, t32_emulate_rd8rn16_noflags,
> +	DECODE_EMULATEX	(0xfb708000, 0xf3400000, PROBES_T32_BITFIELD,
>  						 REGS(NOSPPC, 0, NOSPPC, 0, 0)),
>  
>  	/* BFC			1111 0x11 0110 1111 0xxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0xfbff8000, 0xf36f0000, t32_emulate_rd8rn16_noflags,
> +	DECODE_EMULATEX	(0xfbff8000, 0xf36f0000, PROBES_T32_BITFIELD,
>  						 REGS(0, 0, NOSPPC, 0, 0)),
>  
>  	/* BFI			1111 0x11 0110 xxxx 0xxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0xfbf08000, 0xf3600000, t32_emulate_rd8rn16_noflags,
> +	DECODE_EMULATEX	(0xfbf08000, 0xf3600000, PROBES_T32_BITFIELD,
>  						 REGS(NOSPPCX, 0, NOSPPC, 0, 0)),
>  
>  	DECODE_END
> @@ -544,14 +548,14 @@ static const union decode_item t32_table_1111_0xxx___1[] = {
>  	/* YIELD		1111 0011 1010 xxxx 10x0 x000 0000 0001 */
>  	DECODE_OR	(0xfff0d7ff, 0xf3a08001),
>  	/* SEV			1111 0011 1010 xxxx 10x0 x000 0000 0100 */
> -	DECODE_EMULATE	(0xfff0d7ff, 0xf3a08004, probes_emulate_none),
> +	DECODE_EMULATE	(0xfff0d7ff, 0xf3a08004, PROBES_T32_SEV),
>  	/* NOP			1111 0011 1010 xxxx 10x0 x000 0000 0000 */
>  	/* WFE			1111 0011 1010 xxxx 10x0 x000 0000 0010 */
>  	/* WFI			1111 0011 1010 xxxx 10x0 x000 0000 0011 */
> -	DECODE_SIMULATE	(0xfff0d7fc, 0xf3a08000, probes_simulate_nop),
> +	DECODE_SIMULATE	(0xfff0d7fc, 0xf3a08000, PROBES_T32_WFE),
>  
>  	/* MRS Rd, CPSR		1111 0011 1110 xxxx 10x0 xxxx xxxx xxxx */
> -	DECODE_SIMULATEX(0xfff0d000, 0xf3e08000, t32_simulate_mrs,
> +	DECODE_SIMULATEX(0xfff0d000, 0xf3e08000, PROBES_T32_MRS,
>  						 REGS(0, 0, NOSPPC, 0, 0)),
>  
>  	/*
> @@ -573,13 +577,13 @@ static const union decode_item t32_table_1111_0xxx___1[] = {
>  	DECODE_REJECT	(0xfb80d000, 0xf3808000),
>  
>  	/* Bcc			1111 0xxx xxxx xxxx 10x0 xxxx xxxx xxxx */
> -	DECODE_CUSTOM	(0xf800d000, 0xf0008000, t32_decode_cond_branch),
> +	DECODE_CUSTOM	(0xf800d000, 0xf0008000, PROBES_T32_BRANCH_COND),
>  
>  	/* BLX			1111 0xxx xxxx xxxx 11x0 xxxx xxxx xxx0 */
>  	DECODE_OR	(0xf800d001, 0xf000c000),
>  	/* B			1111 0xxx xxxx xxxx 10x1 xxxx xxxx xxxx */
>  	/* BL			1111 0xxx xxxx xxxx 11x1 xxxx xxxx xxxx */
> -	DECODE_SIMULATE	(0xf8009000, 0xf0009000, t32_simulate_branch),
> +	DECODE_SIMULATE	(0xf8009000, 0xf0009000, PROBES_T32_BRANCH),
>  
>  	DECODE_END
>  };
> @@ -589,7 +593,7 @@ static const union decode_item t32_table_1111_100x_x0x1__1111[] = {
>  
>  	/* PLD (literal)	1111 1000 x001 1111 1111 xxxx xxxx xxxx */
>  	/* PLI (literal)	1111 1001 x001 1111 1111 xxxx xxxx xxxx */
> -	DECODE_SIMULATE	(0xfe7ff000, 0xf81ff000, probes_simulate_nop),
> +	DECODE_SIMULATE	(0xfe7ff000, 0xf81ff000, PROBES_T32_PLDI),
>  
>  	/* PLD{W} (immediate)	1111 1000 10x1 xxxx 1111 xxxx xxxx xxxx */
>  	DECODE_OR	(0xffd0f000, 0xf890f000),
> @@ -598,13 +602,13 @@ static const union decode_item t32_table_1111_100x_x0x1__1111[] = {
>  	/* PLI (immediate)	1111 1001 1001 xxxx 1111 xxxx xxxx xxxx */
>  	DECODE_OR	(0xfff0f000, 0xf990f000),
>  	/* PLI (immediate)	1111 1001 0001 xxxx 1111 1100 xxxx xxxx */
> -	DECODE_SIMULATEX(0xfff0ff00, 0xf910fc00, probes_simulate_nop,
> +	DECODE_SIMULATEX(0xfff0ff00, 0xf910fc00, PROBES_T32_PLDI,
>  						 REGS(NOPCX, 0, 0, 0, 0)),
>  
>  	/* PLD{W} (register)	1111 1000 00x1 xxxx 1111 0000 00xx xxxx */
>  	DECODE_OR	(0xffd0ffc0, 0xf810f000),
>  	/* PLI (register)	1111 1001 0001 xxxx 1111 0000 00xx xxxx */
> -	DECODE_SIMULATEX(0xfff0ffc0, 0xf910f000, probes_simulate_nop,
> +	DECODE_SIMULATEX(0xfff0ffc0, 0xf910f000, PROBES_T32_PLDI,
>  						 REGS(NOPCX, 0, 0, 0, NOSPPC)),
>  
>  	/* Other unallocated instructions...				*/
> @@ -640,7 +644,7 @@ static const union decode_item t32_table_1111_100x[] = {
>  	DECODE_REJECT	(0xff10f000, 0xf800f000),
>  
>  	/* LDR (literal)	1111 1000 x101 1111 xxxx xxxx xxxx xxxx */
> -	DECODE_SIMULATEX(0xff7f0000, 0xf85f0000, t32_simulate_ldr_literal,
> +	DECODE_SIMULATEX(0xff7f0000, 0xf85f0000, PROBES_T32_LDR_LIT,
>  						 REGS(PC, ANY, 0, 0, 0)),
>  
>  	/* STR (immediate)	1111 1000 0100 xxxx xxxx 1xxx xxxx xxxx */
> @@ -648,19 +652,19 @@ static const union decode_item t32_table_1111_100x[] = {
>  	DECODE_OR	(0xffe00800, 0xf8400800),
>  	/* STR (immediate)	1111 1000 1100 xxxx xxxx xxxx xxxx xxxx */
>  	/* LDR (immediate)	1111 1000 1101 xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0xffe00000, 0xf8c00000, t32_emulate_ldrstr,
> +	DECODE_EMULATEX	(0xffe00000, 0xf8c00000, PROBES_T32_LDRSTR,
>  						 REGS(NOPCX, ANY, 0, 0, 0)),
>  
>  	/* STR (register)	1111 1000 0100 xxxx xxxx 0000 00xx xxxx */
>  	/* LDR (register)	1111 1000 0101 xxxx xxxx 0000 00xx xxxx */
> -	DECODE_EMULATEX	(0xffe00fc0, 0xf8400000, t32_emulate_ldrstr,
> +	DECODE_EMULATEX	(0xffe00fc0, 0xf8400000, PROBES_T32_LDRSTR,
>  						 REGS(NOPCX, ANY, 0, 0, NOSPPC)),
>  
>  	/* LDRB (literal)	1111 1000 x001 1111 xxxx xxxx xxxx xxxx */
>  	/* LDRSB (literal)	1111 1001 x001 1111 xxxx xxxx xxxx xxxx */
>  	/* LDRH (literal)	1111 1000 x011 1111 xxxx xxxx xxxx xxxx */
>  	/* LDRSH (literal)	1111 1001 x011 1111 xxxx xxxx xxxx xxxx */
> -	DECODE_SIMULATEX(0xfe5f0000, 0xf81f0000, t32_simulate_ldr_literal,
> +	DECODE_SIMULATEX(0xfe5f0000, 0xf81f0000, PROBES_T32_LDR_LIT,
>  						 REGS(PC, NOSPPCX, 0, 0, 0)),
>  
>  	/* STRB (immediate)	1111 1000 0000 xxxx xxxx 1xxx xxxx xxxx */
> @@ -676,7 +680,7 @@ static const union decode_item t32_table_1111_100x[] = {
>  	/* LDRSB (immediate)	1111 1001 1001 xxxx xxxx xxxx xxxx xxxx */
>  	/* LDRH (immediate)	1111 1000 1011 xxxx xxxx xxxx xxxx xxxx */
>  	/* LDRSH (immediate)	1111 1001 1011 xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0xfec00000, 0xf8800000, t32_emulate_ldrstr,
> +	DECODE_EMULATEX	(0xfec00000, 0xf8800000, PROBES_T32_LDRSTR,
>  						 REGS(NOPCX, NOSPPCX, 0, 0, 0)),
>  
>  	/* STRB (register)	1111 1000 0000 xxxx xxxx 0000 00xx xxxx */
> @@ -685,7 +689,7 @@ static const union decode_item t32_table_1111_100x[] = {
>  	/* LDRSB (register)	1111 1001 0001 xxxx xxxx 0000 00xx xxxx */
>  	/* LDRH (register)	1111 1000 0011 xxxx xxxx 0000 00xx xxxx */
>  	/* LDRSH (register)	1111 1001 0011 xxxx xxxx 0000 00xx xxxx */
> -	DECODE_EMULATEX	(0xfe800fc0, 0xf8000000, t32_emulate_ldrstr,
> +	DECODE_EMULATEX	(0xfe800fc0, 0xf8000000, PROBES_T32_LDRSTR,
>  						 REGS(NOPCX, NOSPPCX, 0, 0, NOSPPC)),
>  
>  	/* Other unallocated instructions...				*/
> @@ -704,7 +708,7 @@ static const union decode_item t32_table_1111_1010___1111[] = {
>  	/* UXTB16		1111 1010 0011 1111 1111 xxxx 1xxx xxxx */
>  	/* SXTB			1111 1010 0100 1111 1111 xxxx 1xxx xxxx */
>  	/* UXTB			1111 1010 0101 1111 1111 xxxx 1xxx xxxx */
> -	DECODE_EMULATEX	(0xff8ff080, 0xfa0ff080, t32_emulate_rd8rn16rm0_rwflags,
> +	DECODE_EMULATEX	(0xff8ff080, 0xfa0ff080, PROBES_T32_SIGN_EXTEND,
>  						 REGS(0, 0, NOSPPC, 0, NOSPPC)),
>  
> 
> @@ -777,7 +781,7 @@ static const union decode_item t32_table_1111_1010___1111[] = {
>  	/* LSR			1111 1010 001x xxxx 1111 xxxx 0000 xxxx */
>  	/* ASR			1111 1010 010x xxxx 1111 xxxx 0000 xxxx */
>  	/* ROR			1111 1010 011x xxxx 1111 xxxx 0000 xxxx */
> -	DECODE_EMULATEX	(0xff80f0f0, 0xfa00f000, t32_emulate_rd8rn16rm0_rwflags,
> +	DECODE_EMULATEX	(0xff80f0f0, 0xfa00f000, PROBES_T32_MEDIA,
>  						 REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)),
>  
>  	/* CLZ			1111 1010 1010 xxxx 1111 xxxx 1000 xxxx */
> @@ -787,7 +791,7 @@ static const union decode_item t32_table_1111_1010___1111[] = {
>  	/* REV16		1111 1010 1001 xxxx 1111 xxxx 1001 xxxx */
>  	/* RBIT			1111 1010 1001 xxxx 1111 xxxx 1010 xxxx */
>  	/* REVSH		1111 1010 1001 xxxx 1111 xxxx 1011 xxxx */
> -	DECODE_EMULATEX	(0xfff0f0c0, 0xfa90f080, t32_emulate_rd8rn16_noflags,
> +	DECODE_EMULATEX	(0xfff0f0c0, 0xfa90f080, PROBES_T32_REVERSE,
>  						 REGS(NOSPPC, 0, NOSPPC, 0, SAMEAS16)),
>  
>  	/* Other unallocated instructions...				*/
> @@ -810,7 +814,7 @@ static const union decode_item t32_table_1111_1011_0[] = {
>  	/* SMUSD{X}		1111 1011 0100 xxxx 1111 xxxx 000x xxxx */
>  	/* SMMUL{R}		1111 1011 0101 xxxx 1111 xxxx 000x xxxx */
>  	/* USAD8		1111 1011 0111 xxxx 1111 xxxx 0000 xxxx */
> -	DECODE_EMULATEX	(0xff80f0e0, 0xfb00f000, t32_emulate_rd8rn16rm0_rwflags,
> +	DECODE_EMULATEX	(0xff80f0e0, 0xfb00f000, PROBES_T32_MUL_ADD,
>  						 REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)),
>  
>  	/* ???			1111 1011 0111 xxxx xxxx xxxx 0001 xxxx */
> @@ -826,7 +830,7 @@ static const union decode_item t32_table_1111_1011_0[] = {
>  	/* SMMLA{R}		1111 1011 0101 xxxx xxxx xxxx 000x xxxx */
>  	/* SMMLS{R}		1111 1011 0110 xxxx xxxx xxxx 000x xxxx */
>  	/* USADA8		1111 1011 0111 xxxx xxxx xxxx 0000 xxxx */
> -	DECODE_EMULATEX	(0xff8000c0, 0xfb000000, t32_emulate_rd8rn16rm0ra12_noflags,
> +	DECODE_EMULATEX	(0xff8000c0, 0xfb000000,  PROBES_T32_MUL_ADD2,
>  						 REGS(NOSPPC, NOSPPCX, NOSPPC, 0, NOSPPC)),
>  
>  	/* Other unallocated instructions...				*/
> @@ -847,7 +851,7 @@ static const union decode_item t32_table_1111_1011_1[] = {
>  	/* UMULL		1111 1011 1010 xxxx xxxx xxxx 0000 xxxx */
>  	/* SMLAL		1111 1011 1100 xxxx xxxx xxxx 0000 xxxx */
>  	/* UMLAL		1111 1011 1110 xxxx xxxx xxxx 0000 xxxx */
> -	DECODE_EMULATEX	(0xff9000f0, 0xfb800000, t32_emulate_rdlo12rdhi8rn16rm0_noflags,
> +	DECODE_EMULATEX	(0xff9000f0, 0xfb800000, PROBES_T32_MUL_ADD_LONG,
>  						 REGS(NOSPPC, NOSPPC, NOSPPC, 0, NOSPPC)),
>  
>  	/* SDIV			1111 1011 1001 xxxx xxxx xxxx 1111 xxxx */
> @@ -1046,7 +1050,7 @@ t16_singlestep_it(struct kprobe *p, struct pt_regs *regs)
>  }
>  
>  static enum kprobe_insn __kprobes
> -t16_decode_it(kprobe_opcode_t insn, struct arch_specific_insn *asi)
> +t16_decode_it(kprobe_opcode_t insn, struct arch_specific_insn *asi, void *d)
>  {
>  	asi->insn_singlestep = t16_singlestep_it;
>  	return INSN_GOOD_NO_SLOT;
> @@ -1063,7 +1067,8 @@ t16_simulate_cond_branch(struct kprobe *p, struct pt_regs *regs)
>  }
>  
>  static enum kprobe_insn __kprobes
> -t16_decode_cond_branch(kprobe_opcode_t insn, struct arch_specific_insn *asi)
> +t16_decode_cond_branch(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> +		       void *d)
>  {
>  	int cc = (insn >> 8) & 0xf;
>  	asi->insn_check_cc = kprobe_condition_checks[cc];
> @@ -1149,7 +1154,7 @@ t16_emulate_hiregs(struct kprobe *p, struct pt_regs *regs)
>  }
>  
>  static enum kprobe_insn __kprobes
> -t16_decode_hiregs(kprobe_opcode_t insn, struct arch_specific_insn *asi)
> +t16_decode_hiregs(kprobe_opcode_t insn, struct arch_specific_insn *asi, void *d)
>  {
>  	insn &= ~0x00ff;
>  	insn |= 0x001; /* Set Rdn = R1 and Rm = R0 */
> @@ -1175,7 +1180,7 @@ t16_emulate_push(struct kprobe *p, struct pt_regs *regs)
>  }
>  
>  static enum kprobe_insn __kprobes
> -t16_decode_push(kprobe_opcode_t insn, struct arch_specific_insn *asi)
> +t16_decode_push(kprobe_opcode_t insn, struct arch_specific_insn *asi, void *d)
>  {
>  	/*
>  	 * To simulate a PUSH we use a Thumb-2 "STMDB R9!, {registers}"
> @@ -1225,7 +1230,7 @@ t16_emulate_pop_pc(struct kprobe *p, struct pt_regs *regs)
>  }
>  
>  static enum kprobe_insn __kprobes
> -t16_decode_pop(kprobe_opcode_t insn, struct arch_specific_insn *asi)
> +t16_decode_pop(kprobe_opcode_t insn, struct arch_specific_insn *asi, void *d)
>  {
>  	/*
>  	 * To simulate a POP we use a Thumb-2 "LDMDB R9!, {registers}"
> @@ -1244,11 +1249,11 @@ static const union decode_item t16_table_1011[] = {
>  
>  	/* ADD (SP plus immediate)	1011 0000 0xxx xxxx */
>  	/* SUB (SP minus immediate)	1011 0000 1xxx xxxx */
> -	DECODE_SIMULATE	(0xff00, 0xb000, t16_simulate_add_sp_imm),
> +	DECODE_SIMULATE	(0xff00, 0xb000, PROBES_T16_ADD_SP),
>  
>  	/* CBZ				1011 00x1 xxxx xxxx */
>  	/* CBNZ				1011 10x1 xxxx xxxx */
> -	DECODE_SIMULATE	(0xf500, 0xb100, t16_simulate_cbz),
> +	DECODE_SIMULATE	(0xf500, 0xb100, PROBES_T16_CBZ),
>  
>  	/* SXTH				1011 0010 00xx xxxx */
>  	/* SXTB				1011 0010 01xx xxxx */
> @@ -1259,12 +1264,12 @@ static const union decode_item t16_table_1011[] = {
>  	/* ???				1011 1010 10xx xxxx */
>  	/* REVSH			1011 1010 11xx xxxx */
>  	DECODE_REJECT	(0xffc0, 0xba80),
> -	DECODE_EMULATE	(0xf500, 0xb000, t16_emulate_loregs_rwflags),
> +	DECODE_EMULATE	(0xf500, 0xb000, PROBES_T16_SIGN_EXTEND),
>  
>  	/* PUSH				1011 010x xxxx xxxx */
> -	DECODE_CUSTOM	(0xfe00, 0xb400, t16_decode_push),
> +	DECODE_CUSTOM	(0xfe00, 0xb400, PROBES_T16_PUSH),
>  	/* POP				1011 110x xxxx xxxx */
> -	DECODE_CUSTOM	(0xfe00, 0xbc00, t16_decode_pop),
> +	DECODE_CUSTOM	(0xfe00, 0xbc00, PROBES_T16_POP),
>  
>  	/*
>  	 * If-Then, and hints
> @@ -1274,15 +1279,15 @@ static const union decode_item t16_table_1011[] = {
>  	/* YIELD			1011 1111 0001 0000 */
>  	DECODE_OR	(0xffff, 0xbf10),
>  	/* SEV				1011 1111 0100 0000 */
> -	DECODE_EMULATE	(0xffff, 0xbf40, probes_emulate_none),
> +	DECODE_EMULATE	(0xffff, 0xbf40, PROBES_T16_SEV),
>  	/* NOP				1011 1111 0000 0000 */
>  	/* WFE				1011 1111 0010 0000 */
>  	/* WFI				1011 1111 0011 0000 */
> -	DECODE_SIMULATE	(0xffcf, 0xbf00, probes_simulate_nop),
> +	DECODE_SIMULATE	(0xffcf, 0xbf00, PROBES_T16_WFE),
>  	/* Unassigned hints		1011 1111 xxxx 0000 */
>  	DECODE_REJECT	(0xff0f, 0xbf00),
>  	/* IT				1011 1111 xxxx xxxx */
> -	DECODE_CUSTOM	(0xff00, 0xbf00, t16_decode_it),
> +	DECODE_CUSTOM	(0xff00, 0xbf00, PROBES_T16_IT),
>  
>  	/* SETEND			1011 0110 010x xxxx */
>  	/* CPS				1011 0110 011x xxxx */
> @@ -1299,7 +1304,7 @@ const union decode_item kprobe_decode_thumb16_table[] = {
>  	 */
>  
>  	/* CMP (immediate)		0010 1xxx xxxx xxxx */
> -	DECODE_EMULATE	(0xf800, 0x2800, t16_emulate_loregs_rwflags),
> +	DECODE_EMULATE	(0xf800, 0x2800, PROBES_T16_CMP),
>  
>  	/* ADD (register)		0001 100x xxxx xxxx */
>  	/* SUB (register)		0001 101x xxxx xxxx */
> @@ -1311,7 +1316,7 @@ const union decode_item kprobe_decode_thumb16_table[] = {
>  	/* MOV (immediate)		0010 0xxx xxxx xxxx */
>  	/* ADD (immediate, Thumb)	0011 0xxx xxxx xxxx */
>  	/* SUB (immediate, Thumb)	0011 1xxx xxxx xxxx */
> -	DECODE_EMULATE	(0xc000, 0x0000, t16_emulate_loregs_noitrwflags),
> +	DECODE_EMULATE	(0xc000, 0x0000, PROBES_T16_ADDSUB),
>  
>  	/*
>  	 * 16-bit Thumb data-processing instructions
> @@ -1319,10 +1324,10 @@ const union decode_item kprobe_decode_thumb16_table[] = {
>  	 */
>  
>  	/* TST (register)		0100 0010 00xx xxxx */
> -	DECODE_EMULATE	(0xffc0, 0x4200, t16_emulate_loregs_rwflags),
> +	DECODE_EMULATE	(0xffc0, 0x4200, PROBES_T16_CMP),
>  	/* CMP (register)		0100 0010 10xx xxxx */
>  	/* CMN (register)		0100 0010 11xx xxxx */
> -	DECODE_EMULATE	(0xff80, 0x4280, t16_emulate_loregs_rwflags),
> +	DECODE_EMULATE	(0xff80, 0x4280, PROBES_T16_CMP),
>  	/* AND (register)		0100 0000 00xx xxxx */
>  	/* EOR (register)		0100 0000 01xx xxxx */
>  	/* LSL (register)		0100 0000 10xx xxxx */
> @@ -1336,7 +1341,7 @@ const union decode_item kprobe_decode_thumb16_table[] = {
>  	/* MUL				0100 0011 00xx xxxx */
>  	/* BIC (register)		0100 0011 10xx xxxx */
>  	/* MVN (register)		0100 0011 10xx xxxx */
> -	DECODE_EMULATE	(0xfc00, 0x4000, t16_emulate_loregs_noitrwflags),
> +	DECODE_EMULATE	(0xfc00, 0x4000, PROBES_T16_LOGICAL),
>  
>  	/*
>  	 * Special data instructions and branch and exchange
> @@ -1348,7 +1353,7 @@ const union decode_item kprobe_decode_thumb16_table[] = {
>  
>  	/* BX (register)		0100 0111 0xxx xxxx */
>  	/* BLX (register)		0100 0111 1xxx xxxx */
> -	DECODE_SIMULATE (0xff00, 0x4700, t16_simulate_bxblx),
> +	DECODE_SIMULATE (0xff00, 0x4700, PROBES_T16_BLX),
>  
>  	/* ADD pc, pc			0100 0100 1111 1111 */
>  	DECODE_REJECT	(0xffff, 0x44ff),
> @@ -1356,13 +1361,13 @@ const union decode_item kprobe_decode_thumb16_table[] = {
>  	/* ADD (register)		0100 0100 xxxx xxxx */
>  	/* CMP (register)		0100 0101 xxxx xxxx */
>  	/* MOV (register)		0100 0110 xxxx xxxx */
> -	DECODE_CUSTOM	(0xfc00, 0x4400, t16_decode_hiregs),
> +	DECODE_CUSTOM	(0xfc00, 0x4400, PROBES_T16_HIREGOPS),
>  
>  	/*
>  	 * Load from Literal Pool
>  	 * LDR (literal)		0100 1xxx xxxx xxxx
>  	 */
> -	DECODE_SIMULATE	(0xf800, 0x4800, t16_simulate_ldr_literal),
> +	DECODE_SIMULATE	(0xf800, 0x4800, PROBES_T16_LDR_LIT),
>  
>  	/*
>  	 * 16-bit Thumb Load/store instructions
> @@ -1383,20 +1388,20 @@ const union decode_item kprobe_decode_thumb16_table[] = {
>  	/* LDR (immediate, Thumb)	0110 1xxx xxxx xxxx */
>  	/* STRB (immediate, Thumb)	0111 0xxx xxxx xxxx */
>  	/* LDRB (immediate, Thumb)	0111 1xxx xxxx xxxx */
> -	DECODE_EMULATE	(0xc000, 0x4000, t16_emulate_loregs_rwflags),
> +	DECODE_EMULATE	(0xc000, 0x4000, PROBES_T16_LDRHSTRH),
>  	/* STRH (immediate, Thumb)	1000 0xxx xxxx xxxx */
>  	/* LDRH (immediate, Thumb)	1000 1xxx xxxx xxxx */
> -	DECODE_EMULATE	(0xf000, 0x8000, t16_emulate_loregs_rwflags),
> +	DECODE_EMULATE	(0xf000, 0x8000, PROBES_T16_LDRHSTRH),
>  	/* STR (immediate, Thumb)	1001 0xxx xxxx xxxx */
>  	/* LDR (immediate, Thumb)	1001 1xxx xxxx xxxx */
> -	DECODE_SIMULATE	(0xf000, 0x9000, t16_simulate_ldrstr_sp_relative),
> +	DECODE_SIMULATE	(0xf000, 0x9000, PROBES_T16_LDRSTR),
>  
>  	/*
>  	 * Generate PC-/SP-relative address
>  	 * ADR (literal)		1010 0xxx xxxx xxxx
>  	 * ADD (SP plus immediate)	1010 1xxx xxxx xxxx
>  	 */
> -	DECODE_SIMULATE	(0xf000, 0xa000, t16_simulate_reladr),
> +	DECODE_SIMULATE	(0xf000, 0xa000, PROBES_T16_ADR),
>  
>  	/*
>  	 * Miscellaneous 16-bit instructions
> @@ -1406,7 +1411,7 @@ const union decode_item kprobe_decode_thumb16_table[] = {
>  
>  	/* STM				1100 0xxx xxxx xxxx */
>  	/* LDM				1100 1xxx xxxx xxxx */
> -	DECODE_EMULATE	(0xf000, 0xc000, t16_emulate_loregs_rwflags),
> +	DECODE_EMULATE	(0xf000, 0xc000, PROBES_T16_LDMSTM),
>  
>  	/*
>  	 * Conditional branch, and Supervisor Call
> @@ -1417,16 +1422,70 @@ const union decode_item kprobe_decode_thumb16_table[] = {
>  	DECODE_REJECT	(0xfe00, 0xde00),
>  
>  	/* Conditional branch		1101 xxxx xxxx xxxx */
> -	DECODE_CUSTOM	(0xf000, 0xd000, t16_decode_cond_branch),
> +	DECODE_CUSTOM	(0xf000, 0xd000, PROBES_T16_BRANCH_COND),
>  
>  	/*
>  	 * Unconditional branch
>  	 * B				1110 0xxx xxxx xxxx
>  	 */
> -	DECODE_SIMULATE	(0xf800, 0xe000, t16_simulate_branch),
> +	DECODE_SIMULATE	(0xf800, 0xe000, PROBES_T16_BRANCH),
>  
>  	DECODE_END
>  };
> +
> +const union decode_item kprobes_t32_actions[] = {

I wonder if it would be a good idea to make sure these arrays are the
size we expect by adding an entry at the end of the relevant enumeration
and using that it set the size of the arrays. E.g. for this one

enum probes_t32_action {
	...
	...
	NUM_PROBES_T32_ACTIONS
};

and then use it like

const union decode_item kprobes_t32_actions[NUM_PROBES_T32_ACTIONS]

that way, we at least make any uninitialised entries are null (i
assume?) which is safer than accidentally indexing beyond the array.

> +	[PROBES_T32_LDMSTM] = {.decoder = t32_decode_ldmstm},
> +	[PROBES_T32_LDRDSTRD] = {.handler = t32_emulate_ldrdstrd},
> +	[PROBES_T32_TABLE_BRANCH] = {.handler = t32_simulate_table_branch},
> +	[PROBES_T32_TST] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
> +	[PROBES_T32_MOV] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
> +	[PROBES_T32_ADDSUB] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
> +	[PROBES_T32_LOGICAL] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
> +	[PROBES_T32_CMP] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
> +	[PROBES_T32_ADDWSUBW_PC] = {.handler = t32_emulate_rd8pc16_noflags,},
> +	[PROBES_T32_ADDWSUBW] = {.handler = t32_emulate_rd8rn16_noflags},
> +	[PROBES_T32_MOVW] = {.handler = t32_emulate_rd8rn16_noflags},
> +	[PROBES_T32_SAT] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
> +	[PROBES_T32_BITFIELD] = {.handler = t32_emulate_rd8rn16_noflags},
> +	[PROBES_T32_SEV] = {.handler = probes_emulate_none},
> +	[PROBES_T32_WFE] = {.handler = probes_simulate_nop},
> +	[PROBES_T32_MRS] = {.handler = t32_simulate_mrs},
> +	[PROBES_T32_BRANCH_COND] = {.decoder = t32_decode_cond_branch},
> +	[PROBES_T32_BRANCH] = {.handler = t32_simulate_branch},
> +	[PROBES_T32_PLDI] = {.handler = probes_simulate_nop},
> +	[PROBES_T32_LDR_LIT] = {.handler = t32_simulate_ldr_literal},
> +	[PROBES_T32_LDRSTR] = {.handler = t32_emulate_ldrstr},
> +	[PROBES_T32_SIGN_EXTEND] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
> +	[PROBES_T32_MEDIA] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
> +	[PROBES_T32_REVERSE] = {.handler = t32_emulate_rd8rn16_noflags},
> +	[PROBES_T32_MUL_ADD] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
> +	[PROBES_T32_MUL_ADD2] = {.handler = t32_emulate_rd8rn16rm0ra12_noflags},
> +	[PROBES_T32_MUL_ADD_LONG] = {.handler = t32_emulate_rdlo12rdhi8rn16rm0_noflags},
> +};
> +
> +const union decode_item kprobes_t16_actions[] = {
> +	[PROBES_T16_ADD_SP] = {.handler = t16_simulate_add_sp_imm},
> +	[PROBES_T16_CBZ] = {.handler = t16_simulate_cbz},
> +	[PROBES_T16_SIGN_EXTEND] = {.handler = t16_emulate_loregs_rwflags},
> +	[PROBES_T16_PUSH] = {.decoder = t16_decode_push},
> +	[PROBES_T16_POP] = {.decoder = t16_decode_pop},
> +	[PROBES_T16_SEV] = {.handler = probes_emulate_none},
> +	[PROBES_T16_WFE] = {.handler = probes_simulate_nop},
> +	[PROBES_T16_IT] = {.decoder = t16_decode_it},
> +	[PROBES_T16_CMP] = {.handler = t16_emulate_loregs_rwflags},
> +	[PROBES_T16_ADDSUB] = {.handler = t16_emulate_loregs_noitrwflags},
> +	[PROBES_T16_LOGICAL] = {.handler = t16_emulate_loregs_noitrwflags},
> +	[PROBES_T16_LDR_LIT] = {.handler = t16_simulate_ldr_literal},
> +	[PROBES_T16_BLX] = {.handler = t16_simulate_bxblx},
> +	[PROBES_T16_HIREGOPS] = {.decoder = t16_decode_hiregs},
> +	[PROBES_T16_LDRHSTRH] = {.handler = t16_emulate_loregs_rwflags},
> +	[PROBES_T16_LDRSTR] = {.handler = t16_simulate_ldrstr_sp_relative},
> +	[PROBES_T16_ADR] = {.handler = t16_simulate_reladr},
> +	[PROBES_T16_LDMSTM] = {.handler = t16_emulate_loregs_rwflags},
> +	[PROBES_T16_BRANCH_COND] = {.decoder = t16_decode_cond_branch},
> +	[PROBES_T16_BRANCH] = {.handler = t16_simulate_branch},
> +};
> +
>  #ifdef CONFIG_ARM_KPROBES_TEST_MODULE
>  EXPORT_SYMBOL_GPL(kprobe_decode_thumb16_table);
>  #endif
> @@ -1453,17 +1512,21 @@ static void __kprobes thumb32_singlestep(struct kprobe *p, struct pt_regs *regs)
>  }
>  
>  enum kprobe_insn __kprobes
> -thumb16_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi)
> +thumb16_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> +			   bool usermode, const union decode_item *actions)
>  {
>  	asi->insn_singlestep = thumb16_singlestep;
>  	asi->insn_check_cc = thumb_check_cc;
> -	return kprobe_decode_insn(insn, asi, kprobe_decode_thumb16_table, true);
> +	return probes_decode_insn(insn, asi, kprobe_decode_thumb16_table, true,
> +				  usermode, actions);
>  }
>  
>  enum kprobe_insn __kprobes
> -thumb32_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi)
> +thumb32_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> +			   bool usermode, const union decode_item *actions)
>  {
>  	asi->insn_singlestep = thumb32_singlestep;
>  	asi->insn_check_cc = thumb_check_cc;
> -	return kprobe_decode_insn(insn, asi, kprobe_decode_thumb32_table, true);
> +	return probes_decode_insn(insn, asi, kprobe_decode_thumb32_table, true,
> +				  usermode, actions);
>  }
> diff --git a/arch/arm/kernel/kprobes.c b/arch/arm/kernel/kprobes.c
> index 170e9f3..7b484d3 100644
> --- a/arch/arm/kernel/kprobes.c
> +++ b/arch/arm/kernel/kprobes.c
> @@ -29,6 +29,7 @@
>  #include <asm/cacheflush.h>
>  
>  #include "kprobes.h"
> +#include "probes.h"
>  #include "patch.h"
>  
>  #define MIN_STACK_SIZE(addr) 				\
> @@ -54,6 +55,7 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
>  	unsigned long addr = (unsigned long)p->addr;
>  	bool thumb;
>  	kprobe_decode_insn_t *decode_insn;
> +	const union decode_item *actions;
>  	int is;
>  
>  	if (in_exception_text(addr))
> @@ -67,20 +69,25 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
>  		insn <<= 16;
>  		insn |= ((u16 *)addr)[1];
>  		decode_insn = thumb32_kprobe_decode_insn;
> -	} else
> +		actions = kprobes_t32_actions;
> +	} else {
>  		decode_insn = thumb16_kprobe_decode_insn;
> +		actions = kprobes_t16_actions;
> +	}
>  #else /* !CONFIG_THUMB2_KERNEL */
>  	thumb = false;
>  	if (addr & 0x3)
>  		return -EINVAL;
>  	insn = *p->addr;
>  	decode_insn = arm_kprobe_decode_insn;
> +	actions = kprobes_probes_actions;
>  #endif
>  
>  	p->opcode = insn;
>  	p->ainsn.insn = tmp_insn;
>  
> -	switch ((*decode_insn)(insn, &p->ainsn)) {
> +	switch ((*decode_insn)(insn, &p->ainsn, false,
> +			       actions)) {
>  	case INSN_REJECTED:	/* not supported */
>  		return -EINVAL;
>  
> diff --git a/arch/arm/kernel/kprobes.h b/arch/arm/kernel/kprobes.h
> index 9aa2f15..6ca1cbe 100644
> --- a/arch/arm/kernel/kprobes.h
> +++ b/arch/arm/kernel/kprobes.h
> @@ -34,22 +34,6 @@ enum kprobe_insn {
>  	INSN_GOOD_NO_SLOT
>  };
>  
> -typedef enum kprobe_insn (kprobe_decode_insn_t)(kprobe_opcode_t,
> -						struct arch_specific_insn *);
> -
> -#ifdef CONFIG_THUMB2_KERNEL
> -
> -enum kprobe_insn thumb16_kprobe_decode_insn(kprobe_opcode_t,
> -						struct arch_specific_insn *);
> -enum kprobe_insn thumb32_kprobe_decode_insn(kprobe_opcode_t,
> -						struct arch_specific_insn *);
> -
> -#else /* !CONFIG_THUMB2_KERNEL */
> -
> -enum kprobe_insn arm_kprobe_decode_insn(kprobe_opcode_t,
> -					struct arch_specific_insn *);
> -#endif
> -
>  void __init arm_kprobe_decode_init(void);
>  
>  extern kprobe_check_cc * const kprobe_condition_checks[16];
> @@ -161,11 +145,9 @@ static inline void __kprobes alu_write_pc(long pcv, struct pt_regs *regs)
>  }
>  
> 
> -void __kprobes probes_simulate_nop(struct kprobe *p, struct pt_regs *regs);
> -void __kprobes probes_emulate_none(struct kprobe *p, struct pt_regs *regs);
> -
>  enum kprobe_insn __kprobes
> -kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi);
> +kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> +		     void *d);
>  
>  /*
>   * Test if load/store instructions writeback the address register.
> @@ -331,7 +313,9 @@ union decode_item {
>  	u32			bits;
>  	const union decode_item	*table;
>  	kprobe_insn_handler_t	*handler;
> -	kprobe_decode_insn_t	*decoder;
> +	enum kprobe_insn (*decoder)(kprobe_opcode_t,
> +				    struct arch_specific_insn *,
> +				    void *d);
>  };
>  
> 
> @@ -368,7 +352,7 @@ struct decode_custom {
>  
>  #define DECODE_CUSTOM(_mask, _value, _decoder)			\
>  	DECODE_HEADER(DECODE_TYPE_CUSTOM, _mask, _value, 0),	\
> -	{.decoder = (_decoder)}
> +	{.bits = (_decoder)}

'bits' looks a bit funny here. I've been trying to think of a way of
making it nicer but it's difficult. The actual value is one of three
different enums, so if we were to add another members to decode_item it
would just have to be "int action", at least that would read nicer in
these macros and where it gets read out in probes_decode_insn.

>  
> 
>  struct decode_simulate {
> @@ -378,7 +362,7 @@ struct decode_simulate {
>  
>  #define DECODE_SIMULATEX(_mask, _value, _handler, _regs)		\
>  	DECODE_HEADER(DECODE_TYPE_SIMULATE, _mask, _value, _regs),	\
> -	{.handler = (_handler)}
> +	{.bits = (_handler)}
>  
>  #define DECODE_SIMULATE(_mask, _value, _handler)	\
>  	DECODE_SIMULATEX(_mask, _value, _handler, 0)
> @@ -391,12 +375,11 @@ struct decode_emulate {
>  
>  #define DECODE_EMULATEX(_mask, _value, _handler, _regs)			\
>  	DECODE_HEADER(DECODE_TYPE_EMULATE, _mask, _value, _regs),	\
> -	{.handler = (_handler)}
> +	{.bits = (_handler)}
>  
>  #define DECODE_EMULATE(_mask, _value, _handler)		\
>  	DECODE_EMULATEX(_mask, _value, _handler, 0)
>  
> -
>  struct decode_or {
>  	struct decode_header	header;
>  };
> @@ -413,16 +396,39 @@ struct decode_reject {
>  	DECODE_HEADER(DECODE_TYPE_REJECT, _mask, _value, 0)
>  
> 
> +typedef enum kprobe_insn (kprobe_decode_insn_t)(kprobe_opcode_t,
> +						struct arch_specific_insn *,
> +						bool usermode,
> +						const union decode_item *actions);
> +
>  #ifdef CONFIG_THUMB2_KERNEL
> +
> +enum kprobe_insn thumb16_kprobe_decode_insn(kprobe_opcode_t,
> +					    struct arch_specific_insn *,
> +					    bool usermode,
> +					    const union decode_item *actions);
> +
> +enum kprobe_insn thumb32_kprobe_decode_insn(kprobe_opcode_t,
> +					    struct arch_specific_insn *,
> +					    bool usermode,
> +					    const union decode_item *actions);
> +
>  extern const union decode_item kprobe_decode_thumb16_table[];
>  extern const union decode_item kprobe_decode_thumb32_table[];
> -#else
> -extern const union decode_item kprobe_decode_arm_table[];
> -#endif
> +extern const union decode_item kprobes_t32_actions[];
> +extern const union decode_item kprobes_t16_actions[];
>  
> +#else
>  
>  int kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> -			const union decode_item *table, bool thumb16);
> +			const union decode_item *table, bool thumb16,
> +			bool usermode,
> +			const union decode_item *actions);
> +
> +extern const union decode_item kprobe_decode_arm_table[];
> +extern const union decode_item kprobes_probes_actions[];
> +
> +#endif
>  
> 
>  #endif /* _ARM_KERNEL_KPROBES_H */
> diff --git a/arch/arm/kernel/probes-arm.c b/arch/arm/kernel/probes-arm.c
> index e1b1a6e..e67bf3f 100644
> --- a/arch/arm/kernel/probes-arm.c
> +++ b/arch/arm/kernel/probes-arm.c
> @@ -18,8 +18,8 @@
>  #include <linux/kernel.h>
>  #include <linux/kprobes.h>
>  
> -#include "probes.h"
>  #include "kprobes.h"
> +#include "probes.h"

Minor nitpick, perhaps it would have been best to added the include in
the right place in the earlier patch rather than move it here.


>  #define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
>  
> diff --git a/arch/arm/kernel/probes-arm.h b/arch/arm/kernel/probes-arm.h
> new file mode 100644
> index 0000000..b1690878
> --- /dev/null
> +++ b/arch/arm/kernel/probes-arm.h
> @@ -0,0 +1,60 @@
> +#ifndef _ARM_KERNEL_PROBES_ARM_H
> +#define  _ARM_KERNEL_PROBES_ARM_H
> +
> +enum probes_action {
> +	PROBES_EMULATE_NONE,
> +	PROBES_SIMULATE_NOP,
> +	PROBES_PRELOAD_IMM,
> +	PROBES_PRELOAD_REG,
> +	PROBES_BRANCH_IMM,
> +	PROBES_BRANCH_REG,
> +	PROBES_MRS,
> +	PROBES_CLZ,
> +	PROBES_SATURATING_ARITHMETIC,
> +	PROBES_MUL1,
> +	PROBES_MUL2,
> +	PROBES_SWP,
> +	PROBES_LDRSTRD,
> +	PROBES_LOAD,
> +	PROBES_STORE,
> +	PROBES_LOAD_EXTRA,
> +	PROBES_STORE_EXTRA,
> +	PROBES_MOV_IP_SP,
> +	PROBES_DATA_PROCESSING_REG,
> +	PROBES_DATA_PROCESSING_IMM,
> +	PROBES_MOV_HALFWORD,
> +	PROBES_SEV,
> +	PROBES_WFE,
> +	PROBES_SATURATE,
> +	PROBES_REV,
> +	PROBES_MMI,
> +	PROBES_PACK,
> +	PROBES_EXTEND,
> +	PROBES_EXTEND_ADD,
> +	PROBES_MUL_ADD_LONG,
> +	PROBES_MUL_ADD,
> +	PROBES_BITFIELD,
> +	PROBES_BRANCH,
> +	PROBES_LDMSTM
> +};
> +
> +void __kprobes simulate_bbl(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes simulate_blx1(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes simulate_blx2bx(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes simulate_mrs(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes simulate_mov_ipsp(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes emulate_ldrdstrd(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes emulate_ldr(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes emulate_str(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes emulate_rd12rn16rm0rs8_rwflags(struct kprobe *p,
> +					      struct pt_regs *regs);
> +void __kprobes emulate_rd12rn16rm0_rwflags_nopc(struct kprobe *p,
> +						struct pt_regs *regs);
> +void __kprobes emulate_rd16rn12rm0rs8_rwflags_nopc(struct kprobe *p,
> +						   struct pt_regs *regs);
> +void __kprobes emulate_rd12rm0_noflags_nopc(struct kprobe *p,
> +					    struct pt_regs *regs);
> +void __kprobes emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(struct kprobe *p,
> +						       struct pt_regs *regs);
> +
> +#endif
> diff --git a/arch/arm/kernel/probes-thumb.h b/arch/arm/kernel/probes-thumb.h
> new file mode 100644
> index 0000000..4b9365c
> --- /dev/null
> +++ b/arch/arm/kernel/probes-thumb.h
> @@ -0,0 +1,59 @@
> +#ifndef _ARM_KERNEL_PROBES_THUMB_H
> +#define  _ARM_KERNEL_PROBES_THUMB_H
> +
> +enum probes_t32_action {
> +	PROBES_T32_EMULATE_NONE,
> +	PROBES_T32_SIMULATE_NOP,
> +	PROBES_T32_LDMSTM,
> +	PROBES_T32_LDRDSTRD,
> +	PROBES_T32_TABLE_BRANCH,
> +	PROBES_T32_TST,
> +	PROBES_T32_CMP,
> +	PROBES_T32_MOV,
> +	PROBES_T32_ADDSUB,
> +	PROBES_T32_LOGICAL,
> +	PROBES_T32_ADDWSUBW_PC,
> +	PROBES_T32_ADDWSUBW,
> +	PROBES_T32_MOVW,
> +	PROBES_T32_SAT,
> +	PROBES_T32_BITFIELD,
> +	PROBES_T32_SEV,
> +	PROBES_T32_WFE,
> +	PROBES_T32_MRS,
> +	PROBES_T32_BRANCH_COND,
> +	PROBES_T32_BRANCH,
> +	PROBES_T32_PLDI,
> +	PROBES_T32_LDR_LIT,
> +	PROBES_T32_LDRSTR,
> +	PROBES_T32_SIGN_EXTEND,
> +	PROBES_T32_MEDIA,
> +	PROBES_T32_REVERSE,
> +	PROBES_T32_MUL_ADD,
> +	PROBES_T32_MUL_ADD2,
> +	PROBES_T32_MUL_ADD_LONG
> +};
> +
> +enum probes_t16_action {
> +	PROBES_T16_ADD_SP,
> +	PROBES_T16_CBZ,
> +	PROBES_T16_SIGN_EXTEND,
> +	PROBES_T16_PUSH,
> +	PROBES_T16_POP,
> +	PROBES_T16_SEV,
> +	PROBES_T16_WFE,
> +	PROBES_T16_IT,
> +	PROBES_T16_CMP,
> +	PROBES_T16_ADDSUB,
> +	PROBES_T16_LOGICAL,
> +	PROBES_T16_BLX,
> +	PROBES_T16_HIREGOPS,
> +	PROBES_T16_LDR_LIT,
> +	PROBES_T16_LDRHSTRH,
> +	PROBES_T16_LDRSTR,
> +	PROBES_T16_ADR,
> +	PROBES_T16_LDMSTM,
> +	PROBES_T16_BRANCH_COND,
> +	PROBES_T16_BRANCH
> +};
> +
> +#endif
> diff --git a/arch/arm/kernel/probes.c b/arch/arm/kernel/probes.c
> index 86c63f3..8cff27a 100644
> --- a/arch/arm/kernel/probes.c
> +++ b/arch/arm/kernel/probes.c
> @@ -1,23 +1,8 @@
> -/*
> - * arch/arm/kernel/probes.c
> - *
> - * Some contents moved here from arch/arm/include/asm/kprobes-common.c
> - *
> - * Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
> - *
> - * Some contents moved here from arch/arm/include/asm/kprobes-arm.c which is
> - * 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.
> - */
> -

Any reason why this is deleted?

>  #include <linux/kernel.h>
>  #include <linux/kprobes.h>
>  
> -#include "probes.h"
>  #include "kprobes.h"
> +#include "probes.h"
>  
>  void __kprobes probes_simulate_nop(struct kprobe *p, struct pt_regs *regs)
>  {
> @@ -28,6 +13,47 @@ void __kprobes probes_emulate_none(struct kprobe *p, struct pt_regs *regs)
>  	p->ainsn.insn_fn();
>  }
>  
> +#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
> +
> +#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25)
> +
> +#if  __LINUX_ARM_ARCH__ >= 6
> +#define BLX(reg)	"blx	"reg"		\n\t"
> +#else
> +#define BLX(reg)	"mov	lr, pc		\n\t"	\
> +			"mov	pc, "reg"	\n\t"
> +#endif
> +
> +/*
> + * To avoid 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 performance.
> + *
> + * 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 perform register writeback)
> + * 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.
> + */
> +

The whole added block above looks spurious, probably a mistake when
re-factoring patches?

>  /*
>   * Prepare an instruction slot to receive an instruction for emulating.
>   * This is done by placing a subroutine return after the location where the
> @@ -97,7 +123,7 @@ set_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
>   * non-zero value, the corresponding nibble in pinsn is validated and modified
>   * according to the type.
>   */
> -static bool __kprobes decode_regs(kprobe_opcode_t *pinsn, u32 regs)
> +static bool __kprobes decode_regs(kprobe_opcode_t *pinsn, u32 regs, bool modify)
>  {
>  	kprobe_opcode_t insn = *pinsn;
>  	kprobe_opcode_t mask = 0xf; /* Start at least significant nibble */
> @@ -158,12 +184,16 @@ static bool __kprobes decode_regs(kprobe_opcode_t *pinsn, u32 regs)
>  			break;
>  		}
>  
> -		/* Replace value of nibble with new register number... */
> -		insn &= ~mask;
> -		insn |= new_bits & mask;
> +		if (modify) {
> +			/* Replace value of nibble with new register number */
> +			insn &= ~mask;
> +			insn |= new_bits & mask;
> +		}
>  	}
>  
> -	*pinsn = insn;
> +	if (modify)
> +		*pinsn = insn;
> +
>  	return true;
>  
>  reject:
> @@ -223,14 +253,17 @@ static const int decode_struct_sizes[NUM_DECODE_TYPES] = {
>   *
>   */
>  int __kprobes
> -kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> -				const union decode_item *table, bool thumb)
> +probes_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> +				const union decode_item *table, bool thumb,
> +				bool usermode,
> +				const union decode_item *actions)
>  {
>  	const struct decode_header *h = (struct decode_header *)table;
>  	const struct decode_header *next;
>  	bool matched = false;
>  
> -	insn = prepare_emulated_insn(insn, asi, thumb);
> +	if (!usermode)
> +		insn = prepare_emulated_insn(insn, asi, thumb);
>  
>  	for (;; h = next) {
>  		enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK;
> @@ -242,14 +275,14 @@ kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
>  		next = (struct decode_header *)
>  				((uintptr_t)h + decode_struct_sizes[type]);
>  
> -		if (!matched && (insn & h->mask.bits) != h->value.bits)
> +		if (!matched &&
> +		    (insn & h->mask.bits) != h->value.bits)
>  			continue;

Any reason for splitting the line?

> -		if (!decode_regs(&insn, regs))
> +		if (!decode_regs(&insn, regs, !usermode))
>  			return INSN_REJECTED;
>  
>  		switch (type) {struct decode_header
> -
>  		case DECODE_TYPE_TABLE: {
>  			struct decode_table *d = (struct decode_table *)h;
>  			next = (struct decode_header *)d->table.table;
> @@ -258,18 +291,24 @@ kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
>  
>  		case DECODE_TYPE_CUSTOM: {
>  			struct decode_custom *d = (struct decode_custom *)h;
> -			return (*d->decoder.decoder)(insn, asi);
> +
> +			return actions[d->decoder.bits].decoder(insn, asi, d);
>  		}
>  
>  		case DECODE_TYPE_SIMULATE: {
>  			struct decode_simulate *d = (struct decode_simulate *)h;
> -			asi->insn_handler = d->handler.handler;
> +			asi->insn_handler = actions[d->handler.bits].handler;
>  			return INSN_GOOD_NO_SLOT;
>  		}
>  
>  		case DECODE_TYPE_EMULATE: {
>  			struct decode_emulate *d = (struct decode_emulate *)h;
> -			asi->insn_handler = d->handler.handler;
> +
> +			if (usermode)
> +				return actions[d->handler.bits].decoder(insn,
> +									asi, d);
> +
> +			asi->insn_handler = actions[d->handler.bits].handler;

This change of behaviour depending on usermode seems to indicate that we
haven't quite got the abstractions right. But at the moment I can't think
about a possible alternative though.

>  			set_emulated_insn(insn, asi, thumb);
>  			return INSN_GOOD;
>  		}
> diff --git a/arch/arm/kernel/probes.h b/arch/arm/kernel/probes.h
> index 56eec12..bc03c690 100644
> --- a/arch/arm/kernel/probes.h
> +++ b/arch/arm/kernel/probes.h
> @@ -1,6 +1,18 @@
>  #ifndef _ARM_KERNEL_PROBES_H
>  #define  _ARM_KERNEL_PROBES_H
>  
> +int __kprobes
> +probes_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> +				const union decode_item *table, bool thumb,
> +				bool usermode,
> +				const union decode_item *actions);
> +enum kprobe_insn arm_kprobe_decode_insn(kprobe_opcode_t,
> +					struct arch_specific_insn *,
> +					bool usermode,
> +					const union decode_item *actions);
> +
> +void __kprobes probes_simulate_nop(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes probes_emulate_none(struct kprobe *p, struct pt_regs *regs);
>  void __kprobes simulate_bbl(struct kprobe *p, struct pt_regs *regs);
>  void __kprobes simulate_blx1(struct kprobe *p, struct pt_regs *regs);
>  void __kprobes simulate_blx2bx(struct kprobe *p, struct pt_regs *regs);

-- 
Tixy





^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH 8/9] ARM: Add "action" table for kprobes/uprobes instruction
@ 2013-08-29 14:09     ` Jon Medhurst (Tixy)
  0 siblings, 0 replies; 32+ messages in thread
From: Jon Medhurst (Tixy) @ 2013-08-29 14:09 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, 2013-08-01 at 19:45 -0400, David Long wrote:
> From: "David A. Long" <dave.long@linaro.org>
> 
> Add a table of functions used by the (previously) kprobes instruction
> parsing code so that it can also be used by uprobes. kprobes and
> (eventually) uprobes will each provide their own unique table of
> actions for each category of instruction recognized by the parser.
> 
> Signed-off-by: David A. Long <dave.long@linaro.org>
> ---

This is rather difficult to follow and I think it would be a lot easier
to understand if if was broken down into separate steps. E.g.

- A step which introduces the action enumeration to replace the function
  pointer (what the commit message to this patch implies it's doing).
- A step to add the extra 'void *d' argument to the decode functions, though
  I think this should be 'struct decode_header' not void as that's clearer
  and should avoid any user having to cast it.
- A step to add the 'usermode' to probes_decode_insn, and 'modify' 
  to decode_regs

More specific comments about this patch are inline below....



>  arch/arm/kernel/kprobes-arm.c    | 188 +++++++++++++++++----------------
>  arch/arm/kernel/kprobes-common.c |   3 +-
>  arch/arm/kernel/kprobes-thumb.c  | 217 +++++++++++++++++++++++++--------------
>  arch/arm/kernel/kprobes.c        |  11 +-
>  arch/arm/kernel/kprobes.h        |  64 ++++++------
>  arch/arm/kernel/probes-arm.c     |   2 +-
>  arch/arm/kernel/probes-arm.h     |  60 +++++++++++
>  arch/arm/kernel/probes-thumb.h   |  59 +++++++++++
>  arch/arm/kernel/probes.c         |  99 ++++++++++++------
>  arch/arm/kernel/probes.h         |  12 +++
>  10 files changed, 488 insertions(+), 227 deletions(-)
>  create mode 100644 arch/arm/kernel/probes-arm.h
>  create mode 100644 arch/arm/kernel/probes-thumb.h
> 
> diff --git a/arch/arm/kernel/kprobes-arm.c b/arch/arm/kernel/kprobes-arm.c
> index d6503cc..0620eba 100644
> --- a/arch/arm/kernel/kprobes-arm.c
> +++ b/arch/arm/kernel/kprobes-arm.c
> @@ -62,38 +62,50 @@
>  #include <linux/kprobes.h>
>  #include <linux/module.h>
>  
> -#include "probes.h"
>  #include "kprobes.h"
> -
> -/*
> - * To avoid 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 performance.
> - *
> - * 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 perform register writeback)
> - * 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.
> - */

Any reason to delete the above comment? I think it is still relevant and
has useful rational in.

> +#include "probes.h"
> +#include "probes-arm.h"
> +
> +const union decode_item kprobes_probes_actions[] = {

I think it's best if we don't reuse the decode_item type here, this is a
different sort of table so probably best to have it's own union. Also,
if we do that, then decode_item can be simplified as it won't need to
have function pointers in it, i.e. we could end up with...

typedef enum kprobe_insn (kprobe_custom_decode_t)(kprobe_opcode_t,
						  struct arch_specific_insn *,
						  void *);

union decode_action {
	kprobe_insn_handler_t	*handler;
	kprobe_custom_decode_t	*decoder;
};

union decode_item {
	u32			bits;
	const union decode_item	*table;
};



> +	[PROBES_EMULATE_NONE] = {.handler = probes_emulate_none},
> +	[PROBES_SIMULATE_NOP] = {.handler = probes_simulate_nop},
> +	[PROBES_PRELOAD_IMM] = {.handler = probes_simulate_nop},
> +	[PROBES_PRELOAD_REG] = {.handler = probes_simulate_nop},
> +	[PROBES_BRANCH_IMM] = {.handler = simulate_blx1},
> +	[PROBES_MRS] = {.handler = simulate_mrs},
> +	[PROBES_BRANCH_REG] = {.handler = simulate_blx2bx},
> +	[PROBES_CLZ] = {.handler = emulate_rd12rm0_noflags_nopc},
> +	[PROBES_SATURATING_ARITHMETIC] = {
> +		.handler = emulate_rd12rn16rm0_rwflags_nopc},
> +	[PROBES_MUL1] = {.handler = emulate_rdlo12rdhi16rn0rm8_rwflags_nopc},
> +	[PROBES_MUL2] = {.handler = emulate_rd16rn12rm0rs8_rwflags_nopc},
> +	[PROBES_SWP] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
> +	[PROBES_LDRSTRD] = {.handler = emulate_ldrdstrd},
> +	[PROBES_LOAD_EXTRA] = {.handler = emulate_ldr},
> +	[PROBES_LOAD] = {.handler = emulate_ldr},
> +	[PROBES_STORE_EXTRA] = {.handler = emulate_str},
> +	[PROBES_STORE] = {.handler = emulate_str},
> +	[PROBES_MOV_IP_SP] = {.handler = simulate_mov_ipsp},
> +	[PROBES_DATA_PROCESSING_REG] = {
> +		.handler = emulate_rd12rn16rm0rs8_rwflags},
> +	[PROBES_DATA_PROCESSING_IMM] = {
> +		.handler = emulate_rd12rn16rm0rs8_rwflags},
> +	[PROBES_MOV_HALFWORD] = {.handler = emulate_rd12rm0_noflags_nopc},
> +	[PROBES_SEV] = {.handler = probes_emulate_none},
> +	[PROBES_WFE] = {.handler = probes_simulate_nop},
> +	[PROBES_SATURATE] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
> +	[PROBES_REV] = {.handler = emulate_rd12rm0_noflags_nopc},
> +	[PROBES_MMI] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
> +	[PROBES_PACK] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
> +	[PROBES_EXTEND] = {.handler = emulate_rd12rm0_noflags_nopc},
> +	[PROBES_EXTEND_ADD] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
> +	[PROBES_MUL_ADD_LONG] = {
> +		.handler = emulate_rdlo12rdhi16rn0rm8_rwflags_nopc},
> +	[PROBES_MUL_ADD] = {.handler = emulate_rd16rn12rm0rs8_rwflags_nopc},
> +	[PROBES_BITFIELD] = {.handler = emulate_rd12rm0_noflags_nopc},
> +	[PROBES_BRANCH] = {.handler = simulate_bbl},
> +	[PROBES_LDMSTM] = {.decoder = kprobe_decode_ldmstm}
> +};
>  
>  /*
>   * For the instruction masking and comparisons in all the "space_*"
> @@ -112,16 +124,16 @@ static const union decode_item arm_1111_table[] = {
>  	/* PLDI (immediate)	1111 0100 x101 xxxx xxxx xxxx xxxx xxxx */
>  	/* PLDW (immediate)	1111 0101 x001 xxxx xxxx xxxx xxxx xxxx */
>  	/* PLD (immediate)	1111 0101 x101 xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_SIMULATE	(0xfe300000, 0xf4100000, probes_simulate_nop),
> +	DECODE_SIMULATE	(0xfe300000, 0xf4100000, PROBES_PRELOAD_IMM),
>  
>  	/* memory hint		1111 0110 x001 xxxx xxxx xxxx xxx0 xxxx */
>  	/* PLDI (register)	1111 0110 x101 xxxx xxxx xxxx xxx0 xxxx */
>  	/* PLDW (register)	1111 0111 x001 xxxx xxxx xxxx xxx0 xxxx */
>  	/* PLD (register)	1111 0111 x101 xxxx xxxx xxxx xxx0 xxxx */
> -	DECODE_SIMULATE	(0xfe300010, 0xf6100000, probes_simulate_nop),
> +	DECODE_SIMULATE	(0xfe300010, 0xf6100000, PROBES_PRELOAD_REG),
>  
>  	/* BLX (immediate)	1111 101x xxxx xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_SIMULATE	(0xfe000000, 0xfa000000, simulate_blx1),
> +	DECODE_SIMULATE	(0xfe000000, 0xfa000000, PROBES_BRANCH_IMM),
>  
>  	/* CPS			1111 0001 0000 xxx0 xxxx xxxx xx0x xxxx */
>  	/* SETEND		1111 0001 0000 0001 xxxx xxxx 0000 xxxx */
> @@ -145,25 +157,25 @@ static const union decode_item arm_cccc_0001_0xx0____0xxx_table[] = {
>  	/* Miscellaneous instructions					*/
>  
>  	/* MRS cpsr		cccc 0001 0000 xxxx xxxx xxxx 0000 xxxx */
> -	DECODE_SIMULATEX(0x0ff000f0, 0x01000000, simulate_mrs,
> +	DECODE_SIMULATEX(0x0ff000f0, 0x01000000, PROBES_MRS,
>  						 REGS(0, NOPC, 0, 0, 0)),
>  
>  	/* BX			cccc 0001 0010 xxxx xxxx xxxx 0001 xxxx */
> -	DECODE_SIMULATE	(0x0ff000f0, 0x01200010, simulate_blx2bx),
> +	DECODE_SIMULATE	(0x0ff000f0, 0x01200010, PROBES_BRANCH_REG),
>  
>  	/* BLX (register)	cccc 0001 0010 xxxx xxxx xxxx 0011 xxxx */
> -	DECODE_SIMULATEX(0x0ff000f0, 0x01200030, simulate_blx2bx,
> +	DECODE_SIMULATEX(0x0ff000f0, 0x01200030, PROBES_BRANCH_REG,
>  						 REGS(0, 0, 0, 0, NOPC)),
>  
>  	/* CLZ			cccc 0001 0110 xxxx xxxx xxxx 0001 xxxx */
> -	DECODE_EMULATEX	(0x0ff000f0, 0x01600010, emulate_rd12rm0_noflags_nopc,
> +	DECODE_EMULATEX	(0x0ff000f0, 0x01600010, PROBES_CLZ,
>  						 REGS(0, NOPC, 0, 0, NOPC)),
>  
>  	/* QADD			cccc 0001 0000 xxxx xxxx xxxx 0101 xxxx */
>  	/* QSUB			cccc 0001 0010 xxxx xxxx xxxx 0101 xxxx */
>  	/* QDADD		cccc 0001 0100 xxxx xxxx xxxx 0101 xxxx */
>  	/* QDSUB		cccc 0001 0110 xxxx xxxx xxxx 0101 xxxx */
> -	DECODE_EMULATEX	(0x0f9000f0, 0x01000050, emulate_rd12rn16rm0_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0f9000f0, 0x01000050, PROBES_SATURATING_ARITHMETIC,
>  						 REGS(NOPC, NOPC, 0, 0, NOPC)),
>  
>  	/* BXJ			cccc 0001 0010 xxxx xxxx xxxx 0010 xxxx */
> @@ -179,19 +191,19 @@ static const union decode_item arm_cccc_0001_0xx0____1xx0_table[] = {
>  	/* Halfword multiply and multiply-accumulate			*/
>  
>  	/* SMLALxy		cccc 0001 0100 xxxx xxxx xxxx 1xx0 xxxx */
> -	DECODE_EMULATEX	(0x0ff00090, 0x01400080, emulate_rdlo12rdhi16rn0rm8_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0ff00090, 0x01400080, PROBES_MUL1,
>  						 REGS(NOPC, NOPC, NOPC, 0, NOPC)),
>  
>  	/* SMULWy		cccc 0001 0010 xxxx xxxx xxxx 1x10 xxxx */
>  	DECODE_OR	(0x0ff000b0, 0x012000a0),
>  	/* SMULxy		cccc 0001 0110 xxxx xxxx xxxx 1xx0 xxxx */
> -	DECODE_EMULATEX	(0x0ff00090, 0x01600080, emulate_rd16rn12rm0rs8_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0ff00090, 0x01600080, PROBES_MUL2,
>  						 REGS(NOPC, 0, NOPC, 0, NOPC)),
>  
>  	/* SMLAxy		cccc 0001 0000 xxxx xxxx xxxx 1xx0 xxxx */
>  	DECODE_OR	(0x0ff00090, 0x01000080),
>  	/* SMLAWy		cccc 0001 0010 xxxx xxxx xxxx 1x00 xxxx */
> -	DECODE_EMULATEX	(0x0ff000b0, 0x01200080, emulate_rd16rn12rm0rs8_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0ff000b0, 0x01200080, PROBES_MUL2,
>  						 REGS(NOPC, NOPC, NOPC, 0, NOPC)),
>  
>  	DECODE_END
> @@ -202,14 +214,14 @@ static const union decode_item arm_cccc_0000_____1001_table[] = {
>  
>  	/* MUL			cccc 0000 0000 xxxx xxxx xxxx 1001 xxxx */
>  	/* MULS			cccc 0000 0001 xxxx xxxx xxxx 1001 xxxx */
> -	DECODE_EMULATEX	(0x0fe000f0, 0x00000090, emulate_rd16rn12rm0rs8_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0fe000f0, 0x00000090, PROBES_MUL2,
>  						 REGS(NOPC, 0, NOPC, 0, NOPC)),
>  
>  	/* MLA			cccc 0000 0010 xxxx xxxx xxxx 1001 xxxx */
>  	/* MLAS			cccc 0000 0011 xxxx xxxx xxxx 1001 xxxx */
>  	DECODE_OR	(0x0fe000f0, 0x00200090),
>  	/* MLS			cccc 0000 0110 xxxx xxxx xxxx 1001 xxxx */
> -	DECODE_EMULATEX	(0x0ff000f0, 0x00600090, emulate_rd16rn12rm0rs8_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0ff000f0, 0x00600090, PROBES_MUL2,
>  						 REGS(NOPC, NOPC, NOPC, 0, NOPC)),
>  
>  	/* UMAAL		cccc 0000 0100 xxxx xxxx xxxx 1001 xxxx */
> @@ -222,7 +234,7 @@ static const union decode_item arm_cccc_0000_____1001_table[] = {
>  	/* SMULLS		cccc 0000 1101 xxxx xxxx xxxx 1001 xxxx */
>  	/* SMLAL		cccc 0000 1110 xxxx xxxx xxxx 1001 xxxx */
>  	/* SMLALS		cccc 0000 1111 xxxx xxxx xxxx 1001 xxxx */
> -	DECODE_EMULATEX	(0x0f8000f0, 0x00800090, emulate_rdlo12rdhi16rn0rm8_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0f8000f0, 0x00800090, PROBES_MUL1,
>  						 REGS(NOPC, NOPC, NOPC, 0, NOPC)),
>  
>  	DECODE_END
> @@ -234,7 +246,7 @@ static const union decode_item arm_cccc_0001_____1001_table[] = {
>  #if __LINUX_ARM_ARCH__ < 6
>  	/* Deprecated on ARMv6 and may be UNDEFINED on v7		*/
>  	/* SMP/SWPB		cccc 0001 0x00 xxxx xxxx xxxx 1001 xxxx */
> -	DECODE_EMULATEX	(0x0fb000f0, 0x01000090, emulate_rd12rn16rm0_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0fb000f0, 0x01000090, PROBES_SWP,
>  						 REGS(NOPC, NOPC, 0, 0, NOPC)),
>  #endif
>  	/* LDREX/STREX{,D,B,H}	cccc 0001 1xxx xxxx xxxx xxxx 1001 xxxx */
> @@ -257,32 +269,32 @@ static const union decode_item arm_cccc_000x_____1xx1_table[] = {
>  
>  	/* LDRD (register)	cccc 000x x0x0 xxxx xxxx xxxx 1101 xxxx */
>  	/* STRD (register)	cccc 000x x0x0 xxxx xxxx xxxx 1111 xxxx */
> -	DECODE_EMULATEX	(0x0e5000d0, 0x000000d0, emulate_ldrdstrd,
> +	DECODE_EMULATEX	(0x0e5000d0, 0x000000d0, PROBES_LDRSTRD,
>  						 REGS(NOPCWB, NOPCX, 0, 0, NOPC)),
>  
>  	/* LDRD (immediate)	cccc 000x x1x0 xxxx xxxx xxxx 1101 xxxx */
>  	/* STRD (immediate)	cccc 000x x1x0 xxxx xxxx xxxx 1111 xxxx */
> -	DECODE_EMULATEX	(0x0e5000d0, 0x004000d0, emulate_ldrdstrd,
> +	DECODE_EMULATEX	(0x0e5000d0, 0x004000d0, PROBES_LDRSTRD,
>  						 REGS(NOPCWB, NOPCX, 0, 0, 0)),
>  
>  	/* STRH (register)	cccc 000x x0x0 xxxx xxxx xxxx 1011 xxxx */
> -	DECODE_EMULATEX	(0x0e5000f0, 0x000000b0, emulate_str,
> +	DECODE_EMULATEX	(0x0e5000f0, 0x000000b0, PROBES_STORE_EXTRA,
>  						 REGS(NOPCWB, NOPC, 0, 0, NOPC)),
>  
>  	/* LDRH (register)	cccc 000x x0x1 xxxx xxxx xxxx 1011 xxxx */
>  	/* LDRSB (register)	cccc 000x x0x1 xxxx xxxx xxxx 1101 xxxx */
>  	/* LDRSH (register)	cccc 000x x0x1 xxxx xxxx xxxx 1111 xxxx */
> -	DECODE_EMULATEX	(0x0e500090, 0x00100090, emulate_ldr,
> +	DECODE_EMULATEX	(0x0e500090, 0x00100090, PROBES_LOAD_EXTRA,
>  						 REGS(NOPCWB, NOPC, 0, 0, NOPC)),
>  
>  	/* STRH (immediate)	cccc 000x x1x0 xxxx xxxx xxxx 1011 xxxx */
> -	DECODE_EMULATEX	(0x0e5000f0, 0x004000b0, emulate_str,
> +	DECODE_EMULATEX	(0x0e5000f0, 0x004000b0, PROBES_STORE_EXTRA,
>  						 REGS(NOPCWB, NOPC, 0, 0, 0)),
>  
>  	/* LDRH (immediate)	cccc 000x x1x1 xxxx xxxx xxxx 1011 xxxx */
>  	/* LDRSB (immediate)	cccc 000x x1x1 xxxx xxxx xxxx 1101 xxxx */
>  	/* LDRSH (immediate)	cccc 000x x1x1 xxxx xxxx xxxx 1111 xxxx */
> -	DECODE_EMULATEX	(0x0e500090, 0x00500090, emulate_ldr,
> +	DECODE_EMULATEX	(0x0e500090, 0x00500090, PROBES_LOAD_EXTRA,
>  						 REGS(NOPCWB, NOPC, 0, 0, 0)),
>  
>  	DECODE_END
> @@ -295,18 +307,18 @@ static const union decode_item arm_cccc_000x_table[] = {
>  	DECODE_REJECT	(0x0e10f000, 0x0010f000),
>  
>  	/* MOV IP, SP		1110 0001 1010 0000 1100 0000 0000 1101 */
> -	DECODE_SIMULATE	(0xffffffff, 0xe1a0c00d, simulate_mov_ipsp),
> +	DECODE_SIMULATE	(0xffffffff, 0xe1a0c00d, PROBES_MOV_IP_SP),
>  
>  	/* TST (register)	cccc 0001 0001 xxxx xxxx xxxx xxx0 xxxx */
>  	/* TEQ (register)	cccc 0001 0011 xxxx xxxx xxxx xxx0 xxxx */
>  	/* CMP (register)	cccc 0001 0101 xxxx xxxx xxxx xxx0 xxxx */
>  	/* CMN (register)	cccc 0001 0111 xxxx xxxx xxxx xxx0 xxxx */
> -	DECODE_EMULATEX	(0x0f900010, 0x01100000, emulate_rd12rn16rm0rs8_rwflags,
> +	DECODE_EMULATEX	(0x0f900010, 0x01100000, PROBES_DATA_PROCESSING_REG,
>  						 REGS(ANY, 0, 0, 0, ANY)),
>  
>  	/* MOV (register)	cccc 0001 101x xxxx xxxx xxxx xxx0 xxxx */
>  	/* MVN (register)	cccc 0001 111x xxxx xxxx xxxx xxx0 xxxx */
> -	DECODE_EMULATEX	(0x0fa00010, 0x01a00000, emulate_rd12rn16rm0rs8_rwflags,
> +	DECODE_EMULATEX	(0x0fa00010, 0x01a00000, PROBES_DATA_PROCESSING_REG,
>  						 REGS(0, ANY, 0, 0, ANY)),
>  
>  	/* AND (register)	cccc 0000 000x xxxx xxxx xxxx xxx0 xxxx */
> @@ -319,19 +331,19 @@ static const union decode_item arm_cccc_000x_table[] = {
>  	/* RSC (register)	cccc 0000 111x xxxx xxxx xxxx xxx0 xxxx */
>  	/* ORR (register)	cccc 0001 100x xxxx xxxx xxxx xxx0 xxxx */
>  	/* BIC (register)	cccc 0001 110x xxxx xxxx xxxx xxx0 xxxx */
> -	DECODE_EMULATEX	(0x0e000010, 0x00000000, emulate_rd12rn16rm0rs8_rwflags,
> +	DECODE_EMULATEX	(0x0e000010, 0x00000000, PROBES_DATA_PROCESSING_REG,
>  						 REGS(ANY, ANY, 0, 0, ANY)),
>  
>  	/* TST (reg-shift reg)	cccc 0001 0001 xxxx xxxx xxxx 0xx1 xxxx */
>  	/* TEQ (reg-shift reg)	cccc 0001 0011 xxxx xxxx xxxx 0xx1 xxxx */
>  	/* CMP (reg-shift reg)	cccc 0001 0101 xxxx xxxx xxxx 0xx1 xxxx */
>  	/* CMN (reg-shift reg)	cccc 0001 0111 xxxx xxxx xxxx 0xx1 xxxx */
> -	DECODE_EMULATEX	(0x0f900090, 0x01100010, emulate_rd12rn16rm0rs8_rwflags,
> +	DECODE_EMULATEX	(0x0f900090, 0x01100010, PROBES_DATA_PROCESSING_REG,
>  						 REGS(ANY, 0, NOPC, 0, ANY)),
>  
>  	/* MOV (reg-shift reg)	cccc 0001 101x xxxx xxxx xxxx 0xx1 xxxx */
>  	/* MVN (reg-shift reg)	cccc 0001 111x xxxx xxxx xxxx 0xx1 xxxx */
> -	DECODE_EMULATEX	(0x0fa00090, 0x01a00010, emulate_rd12rn16rm0rs8_rwflags,
> +	DECODE_EMULATEX	(0x0fa00090, 0x01a00010, PROBES_DATA_PROCESSING_REG,
>  						 REGS(0, ANY, NOPC, 0, ANY)),
>  
>  	/* AND (reg-shift reg)	cccc 0000 000x xxxx xxxx xxxx 0xx1 xxxx */
> @@ -344,7 +356,7 @@ static const union decode_item arm_cccc_000x_table[] = {
>  	/* RSC (reg-shift reg)	cccc 0000 111x xxxx xxxx xxxx 0xx1 xxxx */
>  	/* ORR (reg-shift reg)	cccc 0001 100x xxxx xxxx xxxx 0xx1 xxxx */
>  	/* BIC (reg-shift reg)	cccc 0001 110x xxxx xxxx xxxx 0xx1 xxxx */
> -	DECODE_EMULATEX	(0x0e000090, 0x00000010, emulate_rd12rn16rm0rs8_rwflags,
> +	DECODE_EMULATEX	(0x0e000090, 0x00000010, PROBES_DATA_PROCESSING_REG,
>  						 REGS(ANY, ANY, NOPC, 0, ANY)),
>  
>  	DECODE_END
> @@ -355,17 +367,17 @@ static const union decode_item arm_cccc_001x_table[] = {
>  
>  	/* MOVW			cccc 0011 0000 xxxx xxxx xxxx xxxx xxxx */
>  	/* MOVT			cccc 0011 0100 xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0x0fb00000, 0x03000000, emulate_rd12rm0_noflags_nopc,
> +	DECODE_EMULATEX	(0x0fb00000, 0x03000000, PROBES_DATA_PROCESSING_IMM,
>  						 REGS(0, NOPC, 0, 0, 0)),
>  
>  	/* YIELD		cccc 0011 0010 0000 xxxx xxxx 0000 0001 */
>  	DECODE_OR	(0x0fff00ff, 0x03200001),
>  	/* SEV			cccc 0011 0010 0000 xxxx xxxx 0000 0100 */
> -	DECODE_EMULATE	(0x0fff00ff, 0x03200004, probes_emulate_none),
> +	DECODE_EMULATE	(0x0fff00ff, 0x03200004, PROBES_EMULATE_NONE),
>  	/* NOP			cccc 0011 0010 0000 xxxx xxxx 0000 0000 */
>  	/* WFE			cccc 0011 0010 0000 xxxx xxxx 0000 0010 */
>  	/* WFI			cccc 0011 0010 0000 xxxx xxxx 0000 0011 */
> -	DECODE_SIMULATE	(0x0fff00fc, 0x03200000, probes_simulate_nop),
> +	DECODE_SIMULATE	(0x0fff00fc, 0x03200000, PROBES_SIMULATE_NOP),
>  	/* DBG			cccc 0011 0010 0000 xxxx xxxx ffff xxxx */
>  	/* unallocated hints	cccc 0011 0010 0000 xxxx xxxx xxxx xxxx */
>  	/* MSR (immediate)	cccc 0011 0x10 xxxx xxxx xxxx xxxx xxxx */
> @@ -378,12 +390,12 @@ static const union decode_item arm_cccc_001x_table[] = {
>  	/* TEQ (immediate)	cccc 0011 0011 xxxx xxxx xxxx xxxx xxxx */
>  	/* CMP (immediate)	cccc 0011 0101 xxxx xxxx xxxx xxxx xxxx */
>  	/* CMN (immediate)	cccc 0011 0111 xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0x0f900000, 0x03100000, emulate_rd12rn16rm0rs8_rwflags,
> +	DECODE_EMULATEX	(0x0f900000, 0x03100000, PROBES_DATA_PROCESSING_IMM,
>  						 REGS(ANY, 0, 0, 0, 0)),
>  
>  	/* MOV (immediate)	cccc 0011 101x xxxx xxxx xxxx xxxx xxxx */
>  	/* MVN (immediate)	cccc 0011 111x xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0x0fa00000, 0x03a00000, emulate_rd12rn16rm0rs8_rwflags,
> +	DECODE_EMULATEX	(0x0fa00000, 0x03a00000, PROBES_DATA_PROCESSING_IMM,
>  						 REGS(0, ANY, 0, 0, 0)),
>  
>  	/* AND (immediate)	cccc 0010 000x xxxx xxxx xxxx xxxx xxxx */
> @@ -396,7 +408,7 @@ static const union decode_item arm_cccc_001x_table[] = {
>  	/* RSC (immediate)	cccc 0010 111x xxxx xxxx xxxx xxxx xxxx */
>  	/* ORR (immediate)	cccc 0011 100x xxxx xxxx xxxx xxxx xxxx */
>  	/* BIC (immediate)	cccc 0011 110x xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0x0e000000, 0x02000000, emulate_rd12rn16rm0rs8_rwflags,
> +	DECODE_EMULATEX	(0x0e000000, 0x02000000, PROBES_DATA_PROCESSING_IMM,
>  						 REGS(ANY, ANY, 0, 0, 0)),
>  
>  	DECODE_END
> @@ -406,7 +418,7 @@ static const union decode_item arm_cccc_0110_____xxx1_table[] = {
>  	/* Media instructions						*/
>  
>  	/* SEL			cccc 0110 1000 xxxx xxxx xxxx 1011 xxxx */
> -	DECODE_EMULATEX	(0x0ff000f0, 0x068000b0, emulate_rd12rn16rm0_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0ff000f0, 0x068000b0, PROBES_SATURATE,
>  						 REGS(NOPC, NOPC, 0, 0, NOPC)),
>  
>  	/* SSAT			cccc 0110 101x xxxx xxxx xxxx xx01 xxxx */
> @@ -414,14 +426,14 @@ static const union decode_item arm_cccc_0110_____xxx1_table[] = {
>  	DECODE_OR(0x0fa00030, 0x06a00010),
>  	/* SSAT16		cccc 0110 1010 xxxx xxxx xxxx 0011 xxxx */
>  	/* USAT16		cccc 0110 1110 xxxx xxxx xxxx 0011 xxxx */
> -	DECODE_EMULATEX	(0x0fb000f0, 0x06a00030, emulate_rd12rn16rm0_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0fb000f0, 0x06a00030, PROBES_SATURATE,
>  						 REGS(0, NOPC, 0, 0, NOPC)),
>  
>  	/* REV			cccc 0110 1011 xxxx xxxx xxxx 0011 xxxx */
>  	/* REV16		cccc 0110 1011 xxxx xxxx xxxx 1011 xxxx */
>  	/* RBIT			cccc 0110 1111 xxxx xxxx xxxx 0011 xxxx */
>  	/* REVSH		cccc 0110 1111 xxxx xxxx xxxx 1011 xxxx */
> -	DECODE_EMULATEX	(0x0fb00070, 0x06b00030, emulate_rd12rm0_noflags_nopc,
> +	DECODE_EMULATEX	(0x0fb00070, 0x06b00030, PROBES_REV,
>  						 REGS(0, NOPC, 0, 0, NOPC)),
>  
>  	/* ???			cccc 0110 0x00 xxxx xxxx xxxx xxx1 xxxx */
> @@ -466,12 +478,12 @@ static const union decode_item arm_cccc_0110_____xxx1_table[] = {
>  	/* 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 */
> -	DECODE_EMULATEX	(0x0f800010, 0x06000010, emulate_rd12rn16rm0_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0f800010, 0x06000010, PROBES_MMI,
>  						 REGS(NOPC, NOPC, 0, 0, NOPC)),
>  
>  	/* PKHBT		cccc 0110 1000 xxxx xxxx xxxx x001 xxxx */
>  	/* PKHTB		cccc 0110 1000 xxxx xxxx xxxx x101 xxxx */
> -	DECODE_EMULATEX	(0x0ff00030, 0x06800010, emulate_rd12rn16rm0_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0ff00030, 0x06800010, PROBES_PACK,
>  						 REGS(NOPC, NOPC, 0, 0, NOPC)),
>  
>  	/* ???			cccc 0110 1001 xxxx xxxx xxxx 0111 xxxx */
> @@ -484,7 +496,7 @@ static const union decode_item arm_cccc_0110_____xxx1_table[] = {
>  	/* UXTB16		cccc 0110 1100 1111 xxxx xxxx 0111 xxxx */
>  	/* UXTB			cccc 0110 1110 1111 xxxx xxxx 0111 xxxx */
>  	/* UXTH			cccc 0110 1111 1111 xxxx xxxx 0111 xxxx */
> -	DECODE_EMULATEX	(0x0f8f00f0, 0x068f0070, emulate_rd12rm0_noflags_nopc,
> +	DECODE_EMULATEX	(0x0f8f00f0, 0x068f0070, PROBES_EXTEND,
>  						 REGS(0, NOPC, 0, 0, NOPC)),
>  
>  	/* SXTAB16		cccc 0110 1000 xxxx xxxx xxxx 0111 xxxx */
> @@ -493,7 +505,7 @@ static const union decode_item arm_cccc_0110_____xxx1_table[] = {
>  	/* 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 */
> -	DECODE_EMULATEX	(0x0f8000f0, 0x06800070, emulate_rd12rn16rm0_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0f8000f0, 0x06800070, PROBES_EXTEND_ADD,
>  						 REGS(NOPCX, NOPC, 0, 0, NOPC)),
>  
>  	DECODE_END
> @@ -507,7 +519,7 @@ static const union decode_item arm_cccc_0111_____xxx1_table[] = {
>  
>  	/* SMLALD		cccc 0111 0100 xxxx xxxx xxxx 00x1 xxxx */
>  	/* SMLSLD		cccc 0111 0100 xxxx xxxx xxxx 01x1 xxxx */
> -	DECODE_EMULATEX	(0x0ff00090, 0x07400010, emulate_rdlo12rdhi16rn0rm8_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0ff00090, 0x07400010, PROBES_MUL_ADD_LONG,
>  						 REGS(NOPC, NOPC, NOPC, 0, NOPC)),
>  
>  	/* SMUAD		cccc 0111 0000 xxxx 1111 xxxx 00x1 xxxx */
> @@ -516,7 +528,7 @@ static const union decode_item arm_cccc_0111_____xxx1_table[] = {
>  	/* SMMUL		cccc 0111 0101 xxxx 1111 xxxx 00x1 xxxx */
>  	DECODE_OR	(0x0ff0f0d0, 0x0750f010),
>  	/* USAD8		cccc 0111 1000 xxxx 1111 xxxx 0001 xxxx */
> -	DECODE_EMULATEX	(0x0ff0f0f0, 0x0780f010, emulate_rd16rn12rm0rs8_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0ff0f0f0, 0x0780f010, PROBES_MUL_ADD,
>  						 REGS(NOPC, 0, NOPC, 0, NOPC)),
>  
>  	/* SMLAD		cccc 0111 0000 xxxx xxxx xxxx 00x1 xxxx */
> @@ -525,24 +537,24 @@ static const union decode_item arm_cccc_0111_____xxx1_table[] = {
>  	/* SMMLA		cccc 0111 0101 xxxx xxxx xxxx 00x1 xxxx */
>  	DECODE_OR	(0x0ff000d0, 0x07500010),
>  	/* USADA8		cccc 0111 1000 xxxx xxxx xxxx 0001 xxxx */
> -	DECODE_EMULATEX	(0x0ff000f0, 0x07800010, emulate_rd16rn12rm0rs8_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0ff000f0, 0x07800010, PROBES_MUL_ADD,
>  						 REGS(NOPC, NOPCX, NOPC, 0, NOPC)),
>  
>  	/* SMMLS		cccc 0111 0101 xxxx xxxx xxxx 11x1 xxxx */
> -	DECODE_EMULATEX	(0x0ff000d0, 0x075000d0, emulate_rd16rn12rm0rs8_rwflags_nopc,
> +	DECODE_EMULATEX	(0x0ff000d0, 0x075000d0, PROBES_MUL_ADD,
>  						 REGS(NOPC, NOPC, NOPC, 0, NOPC)),
>  
>  	/* SBFX			cccc 0111 101x xxxx xxxx xxxx x101 xxxx */
>  	/* UBFX			cccc 0111 111x xxxx xxxx xxxx x101 xxxx */
> -	DECODE_EMULATEX	(0x0fa00070, 0x07a00050, emulate_rd12rm0_noflags_nopc,
> +	DECODE_EMULATEX	(0x0fa00070, 0x07a00050, PROBES_BITFIELD,
>  						 REGS(0, NOPC, 0, 0, NOPC)),
>  
>  	/* BFC			cccc 0111 110x xxxx xxxx xxxx x001 1111 */
> -	DECODE_EMULATEX	(0x0fe0007f, 0x07c0001f, emulate_rd12rm0_noflags_nopc,
> +	DECODE_EMULATEX	(0x0fe0007f, 0x07c0001f, PROBES_BITFIELD,
>  						 REGS(0, NOPC, 0, 0, 0)),
>  
>  	/* BFI			cccc 0111 110x xxxx xxxx xxxx x001 xxxx */
> -	DECODE_EMULATEX	(0x0fe00070, 0x07c00010, emulate_rd12rm0_noflags_nopc,
> +	DECODE_EMULATEX	(0x0fe00070, 0x07c00010, PROBES_BITFIELD,
>  						 REGS(0, NOPC, 0, 0, NOPCX)),
>  
>  	DECODE_END
> @@ -562,22 +574,22 @@ static const union decode_item arm_cccc_01xx_table[] = {
>  
>  	/* STR (immediate)	cccc 010x x0x0 xxxx xxxx xxxx xxxx xxxx */
>  	/* STRB (immediate)	cccc 010x x1x0 xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0x0e100000, 0x04000000, emulate_str,
> +	DECODE_EMULATEX	(0x0e100000, 0x04000000, PROBES_STORE,
>  						 REGS(NOPCWB, ANY, 0, 0, 0)),
>  
>  	/* LDR (immediate)	cccc 010x x0x1 xxxx xxxx xxxx xxxx xxxx */
>  	/* LDRB (immediate)	cccc 010x x1x1 xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0x0e100000, 0x04100000, emulate_ldr,
> +	DECODE_EMULATEX	(0x0e100000, 0x04100000, PROBES_LOAD,
>  						 REGS(NOPCWB, ANY, 0, 0, 0)),
>  
>  	/* STR (register)	cccc 011x x0x0 xxxx xxxx xxxx xxxx xxxx */
>  	/* STRB (register)	cccc 011x x1x0 xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0x0e100000, 0x06000000, emulate_str,
> +	DECODE_EMULATEX	(0x0e100000, 0x06000000, PROBES_STORE,
>  						 REGS(NOPCWB, ANY, 0, 0, NOPC)),
>  
>  	/* LDR (register)	cccc 011x x0x1 xxxx xxxx xxxx xxxx xxxx */
>  	/* LDRB (register)	cccc 011x x1x1 xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0x0e100000, 0x06100000, emulate_ldr,
> +	DECODE_EMULATEX	(0x0e100000, 0x06100000, PROBES_LOAD,
>  						 REGS(NOPCWB, ANY, 0, 0, NOPC)),
>  
>  	DECODE_END
> @@ -588,7 +600,7 @@ static const union decode_item arm_cccc_100x_table[] = {
>  
>  	/* LDM			cccc 100x x0x1 xxxx xxxx xxxx xxxx xxxx */
>  	/* STM			cccc 100x x0x0 xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_CUSTOM	(0x0e400000, 0x08000000, kprobe_decode_ldmstm),
> +	DECODE_CUSTOM	(0x0e400000, 0x08000000, PROBES_LDMSTM),
>  
>  	/* STM (user registers)	cccc 100x x1x0 xxxx xxxx xxxx xxxx xxxx */
>  	/* LDM (user registers)	cccc 100x x1x1 xxxx 0xxx xxxx xxxx xxxx */
> @@ -668,7 +680,7 @@ const union decode_item kprobe_decode_arm_table[] = {
>  
>  	/* B			cccc 1010 xxxx xxxx xxxx xxxx xxxx xxxx */
>  	/* BL			cccc 1011 xxxx xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_SIMULATE	(0x0e000000, 0x0a000000, simulate_bbl),
> +	DECODE_SIMULATE	(0x0e000000, 0x0a000000, PROBES_BRANCH),
>  
>  	/*
>  	 * Supervisor Call, and coprocessor instructions
> @@ -709,9 +721,11 @@ static void __kprobes arm_singlestep(struct kprobe *p, struct pt_regs *regs)
>   *   should also be very rare.
>   */
>  enum kprobe_insn __kprobes
> -arm_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi)
> +arm_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> +			bool usermode, const union decode_item *actions)
>  {
>  	asi->insn_singlestep = arm_singlestep;
>  	asi->insn_check_cc = kprobe_condition_checks[insn>>28];
> -	return kprobe_decode_insn(insn, asi, kprobe_decode_arm_table, false);
> +	return probes_decode_insn(insn, asi, kprobe_decode_arm_table, false,
> +				  usermode, actions);
>  }
> diff --git a/arch/arm/kernel/kprobes-common.c b/arch/arm/kernel/kprobes-common.c
> index b66e9f7..4e19498 100644
> --- a/arch/arm/kernel/kprobes-common.c
> +++ b/arch/arm/kernel/kprobes-common.c
> @@ -268,7 +268,8 @@ emulate_ldm_r3_15(struct kprobe *p, struct pt_regs *regs)
>  }
>  
>  enum kprobe_insn __kprobes
> -kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi)
> +kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> +		     void *d)
>  {
>  	kprobe_insn_handler_t *handler = 0;
>  	unsigned reglist = insn & 0xffff;
> diff --git a/arch/arm/kernel/kprobes-thumb.c b/arch/arm/kernel/kprobes-thumb.c
> index 173b2bc..f5d57a8 100644
> --- a/arch/arm/kernel/kprobes-thumb.c
> +++ b/arch/arm/kernel/kprobes-thumb.c
> @@ -13,6 +13,8 @@
>  #include <linux/module.h>
>  
>  #include "kprobes.h"
> +#include "probes.h"
> +#include "probes-thumb.h"
>  
> 
>  /*
> @@ -83,7 +85,8 @@ t32_simulate_cond_branch(struct kprobe *p, struct pt_regs *regs)
>  }
>  
>  static enum kprobe_insn __kprobes
> -t32_decode_cond_branch(kprobe_opcode_t insn, struct arch_specific_insn *asi)
> +t32_decode_cond_branch(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> +		       void *d)
>  {
>  	int cc = (insn >> 22) & 0xf;
>  	asi->insn_check_cc = kprobe_condition_checks[cc];
> @@ -158,9 +161,10 @@ t32_simulate_ldr_literal(struct kprobe *p, struct pt_regs *regs)
>  }
>  
>  static enum kprobe_insn __kprobes
> -t32_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi)
> +t32_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> +		  void *d)
>  {
> -	enum kprobe_insn ret = kprobe_decode_ldmstm(insn, asi);
> +	enum kprobe_insn ret = kprobe_decode_ldmstm(insn, asi, d);
>  
>  	/* Fixup modified instruction to have halfwords in correct order...*/
>  	insn = asi->insn[0];
> @@ -344,7 +348,7 @@ static const union decode_item t32_table_1110_100x_x0xx[] = {
>  	/* LDMIA		1110 1000 10x1 xxxx xxxx xxxx xxxx xxxx */
>  	/* STMDB		1110 1001 00x0 xxxx xxxx xxxx xxxx xxxx */
>  	/* LDMDB		1110 1001 00x1 xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_CUSTOM	(0xfe400000, 0xe8000000, t32_decode_ldmstm),
> +	DECODE_CUSTOM	(0xfe400000, 0xe8000000, PROBES_T32_LDMSTM),
>  
>  	DECODE_END
>  };
> @@ -357,12 +361,12 @@ static const union decode_item t32_table_1110_100x_x1xx[] = {
>  	DECODE_OR	(0xff600000, 0xe8600000),
>  	/* STRD (immediate)	1110 1001 x1x0 xxxx xxxx xxxx xxxx xxxx */
>  	/* LDRD (immediate)	1110 1001 x1x1 xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0xff400000, 0xe9400000, t32_emulate_ldrdstrd,
> +	DECODE_EMULATEX	(0xff400000, 0xe9400000, PROBES_T32_LDRDSTRD,
>  						 REGS(NOPCWB, NOSPPC, NOSPPC, 0, 0)),
>  
>  	/* TBB			1110 1000 1101 xxxx xxxx xxxx 0000 xxxx */
>  	/* TBH			1110 1000 1101 xxxx xxxx xxxx 0001 xxxx */
> -	DECODE_SIMULATEX(0xfff000e0, 0xe8d00000, t32_simulate_table_branch,
> +	DECODE_SIMULATEX(0xfff000e0, 0xe8d00000, PROBES_T32_TABLE_BRANCH,
>  						 REGS(NOSP, 0, 0, 0, NOSPPC)),
>  
>  	/* STREX		1110 1000 0100 xxxx xxxx xxxx xxxx xxxx */
> @@ -382,18 +386,18 @@ static const union decode_item t32_table_1110_101x[] = {
>  
>  	/* TST			1110 1010 0001 xxxx xxxx 1111 xxxx xxxx */
>  	/* TEQ			1110 1010 1001 xxxx xxxx 1111 xxxx xxxx */
> -	DECODE_EMULATEX	(0xff700f00, 0xea100f00, t32_emulate_rd8rn16rm0_rwflags,
> +	DECODE_EMULATEX	(0xff700f00, 0xea100f00, PROBES_T32_TST,
>  						 REGS(NOSPPC, 0, 0, 0, NOSPPC)),
>  
>  	/* CMN			1110 1011 0001 xxxx xxxx 1111 xxxx xxxx */
>  	DECODE_OR	(0xfff00f00, 0xeb100f00),
>  	/* CMP			1110 1011 1011 xxxx xxxx 1111 xxxx xxxx */
> -	DECODE_EMULATEX	(0xfff00f00, 0xebb00f00, t32_emulate_rd8rn16rm0_rwflags,
> +	DECODE_EMULATEX	(0xfff00f00, 0xebb00f00, PROBES_T32_TST,
>  						 REGS(NOPC, 0, 0, 0, NOSPPC)),
>  
>  	/* MOV			1110 1010 010x 1111 xxxx xxxx xxxx xxxx */
>  	/* MVN			1110 1010 011x 1111 xxxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0xffcf0000, 0xea4f0000, t32_emulate_rd8rn16rm0_rwflags,
> +	DECODE_EMULATEX	(0xffcf0000, 0xea4f0000, PROBES_T32_MOV,
>  						 REGS(0, 0, NOSPPC, 0, NOSPPC)),
>  
>  	/* ???			1110 1010 101x xxxx xxxx xxxx xxxx xxxx */
> @@ -408,7 +412,7 @@ static const union decode_item t32_table_1110_101x[] = {
>  
>  	/* ADD/SUB SP, SP, Rm, LSL #0..3				*/
>  	/*			1110 1011 x0xx 1101 x000 1101 xx00 xxxx */
> -	DECODE_EMULATEX	(0xff4f7f30, 0xeb0d0d00, t32_emulate_rd8rn16rm0_rwflags,
> +	DECODE_EMULATEX	(0xff4f7f30, 0xeb0d0d00, PROBES_T32_ADDSUB,
>  						 REGS(SP, 0, SP, 0, NOSPPC)),
>  
>  	/* ADD/SUB SP, SP, Rm, shift					*/
> @@ -417,7 +421,7 @@ static const union decode_item t32_table_1110_101x[] = {
>  
>  	/* ADD/SUB Rd, SP, Rm, shift					*/
>  	/*			1110 1011 x0xx 1101 xxxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0xff4f0000, 0xeb0d0000, t32_emulate_rd8rn16rm0_rwflags,
> +	DECODE_EMULATEX	(0xff4f0000, 0xeb0d0000, PROBES_T32_ADDSUB,
>  						 REGS(SP, 0, NOPC, 0, NOSPPC)),
>  
>  	/* AND			1110 1010 000x xxxx xxxx xxxx xxxx xxxx */
> @@ -431,7 +435,7 @@ static const union decode_item t32_table_1110_101x[] = {
>  	/* SBC			1110 1011 011x xxxx xxxx xxxx xxxx xxxx */
>  	/* SUB			1110 1011 101x xxxx xxxx xxxx xxxx xxxx */
>  	/* RSB			1110 1011 110x xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0xfe000000, 0xea000000, t32_emulate_rd8rn16rm0_rwflags,
> +	DECODE_EMULATEX	(0xfe000000, 0xea000000, PROBES_T32_LOGICAL,
>  						 REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)),
>  
>  	DECODE_END
> @@ -442,18 +446,18 @@ static const union decode_item t32_table_1111_0x0x___0[] = {
>  
>  	/* TST			1111 0x00 0001 xxxx 0xxx 1111 xxxx xxxx */
>  	/* TEQ			1111 0x00 1001 xxxx 0xxx 1111 xxxx xxxx */
> -	DECODE_EMULATEX	(0xfb708f00, 0xf0100f00, t32_emulate_rd8rn16rm0_rwflags,
> +	DECODE_EMULATEX	(0xfb708f00, 0xf0100f00, PROBES_T32_TST,
>  						 REGS(NOSPPC, 0, 0, 0, 0)),
>  
>  	/* CMN			1111 0x01 0001 xxxx 0xxx 1111 xxxx xxxx */
>  	DECODE_OR	(0xfbf08f00, 0xf1100f00),
>  	/* CMP			1111 0x01 1011 xxxx 0xxx 1111 xxxx xxxx */
> -	DECODE_EMULATEX	(0xfbf08f00, 0xf1b00f00, t32_emulate_rd8rn16rm0_rwflags,
> +	DECODE_EMULATEX	(0xfbf08f00, 0xf1b00f00, PROBES_T32_CMP,
>  						 REGS(NOPC, 0, 0, 0, 0)),
>  
>  	/* MOV			1111 0x00 010x 1111 0xxx xxxx xxxx xxxx */
>  	/* MVN			1111 0x00 011x 1111 0xxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0xfbcf8000, 0xf04f0000, t32_emulate_rd8rn16rm0_rwflags,
> +	DECODE_EMULATEX	(0xfbcf8000, 0xf04f0000, PROBES_T32_MOV,
>  						 REGS(0, 0, NOSPPC, 0, 0)),
>  
>  	/* ???			1111 0x00 101x xxxx 0xxx xxxx xxxx xxxx */
> @@ -470,7 +474,7 @@ static const union decode_item t32_table_1111_0x0x___0[] = {
>  
>  	/* ADD Rd, SP, #imm	1111 0x01 000x 1101 0xxx xxxx xxxx xxxx */
>  	/* SUB Rd, SP, #imm	1111 0x01 101x 1101 0xxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0xfb4f8000, 0xf10d0000, t32_emulate_rd8rn16rm0_rwflags,
> +	DECODE_EMULATEX	(0xfb4f8000, 0xf10d0000, PROBES_T32_ADDSUB,
>  						 REGS(SP, 0, NOPC, 0, 0)),
>  
>  	/* AND			1111 0x00 000x xxxx 0xxx xxxx xxxx xxxx */
> @@ -483,7 +487,7 @@ static const union decode_item t32_table_1111_0x0x___0[] = {
>  	/* SBC			1111 0x01 011x xxxx 0xxx xxxx xxxx xxxx */
>  	/* SUB			1111 0x01 101x xxxx 0xxx xxxx xxxx xxxx */
>  	/* RSB			1111 0x01 110x xxxx 0xxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0xfa008000, 0xf0000000, t32_emulate_rd8rn16rm0_rwflags,
> +	DECODE_EMULATEX	(0xfa008000, 0xf0000000, PROBES_T32_LOGICAL,
>  						 REGS(NOSPPC, 0, NOSPPC, 0, 0)),
>  
>  	DECODE_END
> @@ -495,44 +499,44 @@ static const union decode_item t32_table_1111_0x1x___0[] = {
>  	/* ADDW Rd, PC, #imm	1111 0x10 0000 1111 0xxx xxxx xxxx xxxx */
>  	DECODE_OR	(0xfbff8000, 0xf20f0000),
>  	/* SUBW	Rd, PC, #imm	1111 0x10 1010 1111 0xxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0xfbff8000, 0xf2af0000, t32_emulate_rd8pc16_noflags,
> +	DECODE_EMULATEX	(0xfbff8000, 0xf2af0000, PROBES_T32_ADDWSUBW_PC,
>  						 REGS(PC, 0, NOSPPC, 0, 0)),
>  
>  	/* ADDW SP, SP, #imm	1111 0x10 0000 1101 0xxx 1101 xxxx xxxx */
>  	DECODE_OR	(0xfbff8f00, 0xf20d0d00),
>  	/* SUBW	SP, SP, #imm	1111 0x10 1010 1101 0xxx 1101 xxxx xxxx */
> -	DECODE_EMULATEX	(0xfbff8f00, 0xf2ad0d00, t32_emulate_rd8rn16_noflags,
> +	DECODE_EMULATEX	(0xfbff8f00, 0xf2ad0d00, PROBES_T32_ADDWSUBW,
>  						 REGS(SP, 0, SP, 0, 0)),
>  
>  	/* ADDW			1111 0x10 0000 xxxx 0xxx xxxx xxxx xxxx */
>  	DECODE_OR	(0xfbf08000, 0xf2000000),
>  	/* SUBW			1111 0x10 1010 xxxx 0xxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0xfbf08000, 0xf2a00000, t32_emulate_rd8rn16_noflags,
> +	DECODE_EMULATEX	(0xfbf08000, 0xf2a00000, PROBES_T32_ADDWSUBW,
>  						 REGS(NOPCX, 0, NOSPPC, 0, 0)),
>  
>  	/* MOVW			1111 0x10 0100 xxxx 0xxx xxxx xxxx xxxx */
>  	/* MOVT			1111 0x10 1100 xxxx 0xxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0xfb708000, 0xf2400000, t32_emulate_rd8rn16_noflags,
> +	DECODE_EMULATEX	(0xfb708000, 0xf2400000, PROBES_T32_MOVW,
>  						 REGS(0, 0, NOSPPC, 0, 0)),
>  
>  	/* SSAT16		1111 0x11 0010 xxxx 0000 xxxx 00xx xxxx */
>  	/* SSAT			1111 0x11 00x0 xxxx 0xxx xxxx xxxx xxxx */
>  	/* USAT16		1111 0x11 1010 xxxx 0000 xxxx 00xx xxxx */
>  	/* USAT			1111 0x11 10x0 xxxx 0xxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0xfb508000, 0xf3000000, t32_emulate_rd8rn16rm0_rwflags,
> +	DECODE_EMULATEX	(0xfb508000, 0xf3000000, PROBES_T32_SAT,
>  						 REGS(NOSPPC, 0, NOSPPC, 0, 0)),
>  
>  	/* SFBX			1111 0x11 0100 xxxx 0xxx xxxx xxxx xxxx */
>  	/* UFBX			1111 0x11 1100 xxxx 0xxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0xfb708000, 0xf3400000, t32_emulate_rd8rn16_noflags,
> +	DECODE_EMULATEX	(0xfb708000, 0xf3400000, PROBES_T32_BITFIELD,
>  						 REGS(NOSPPC, 0, NOSPPC, 0, 0)),
>  
>  	/* BFC			1111 0x11 0110 1111 0xxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0xfbff8000, 0xf36f0000, t32_emulate_rd8rn16_noflags,
> +	DECODE_EMULATEX	(0xfbff8000, 0xf36f0000, PROBES_T32_BITFIELD,
>  						 REGS(0, 0, NOSPPC, 0, 0)),
>  
>  	/* BFI			1111 0x11 0110 xxxx 0xxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0xfbf08000, 0xf3600000, t32_emulate_rd8rn16_noflags,
> +	DECODE_EMULATEX	(0xfbf08000, 0xf3600000, PROBES_T32_BITFIELD,
>  						 REGS(NOSPPCX, 0, NOSPPC, 0, 0)),
>  
>  	DECODE_END
> @@ -544,14 +548,14 @@ static const union decode_item t32_table_1111_0xxx___1[] = {
>  	/* YIELD		1111 0011 1010 xxxx 10x0 x000 0000 0001 */
>  	DECODE_OR	(0xfff0d7ff, 0xf3a08001),
>  	/* SEV			1111 0011 1010 xxxx 10x0 x000 0000 0100 */
> -	DECODE_EMULATE	(0xfff0d7ff, 0xf3a08004, probes_emulate_none),
> +	DECODE_EMULATE	(0xfff0d7ff, 0xf3a08004, PROBES_T32_SEV),
>  	/* NOP			1111 0011 1010 xxxx 10x0 x000 0000 0000 */
>  	/* WFE			1111 0011 1010 xxxx 10x0 x000 0000 0010 */
>  	/* WFI			1111 0011 1010 xxxx 10x0 x000 0000 0011 */
> -	DECODE_SIMULATE	(0xfff0d7fc, 0xf3a08000, probes_simulate_nop),
> +	DECODE_SIMULATE	(0xfff0d7fc, 0xf3a08000, PROBES_T32_WFE),
>  
>  	/* MRS Rd, CPSR		1111 0011 1110 xxxx 10x0 xxxx xxxx xxxx */
> -	DECODE_SIMULATEX(0xfff0d000, 0xf3e08000, t32_simulate_mrs,
> +	DECODE_SIMULATEX(0xfff0d000, 0xf3e08000, PROBES_T32_MRS,
>  						 REGS(0, 0, NOSPPC, 0, 0)),
>  
>  	/*
> @@ -573,13 +577,13 @@ static const union decode_item t32_table_1111_0xxx___1[] = {
>  	DECODE_REJECT	(0xfb80d000, 0xf3808000),
>  
>  	/* Bcc			1111 0xxx xxxx xxxx 10x0 xxxx xxxx xxxx */
> -	DECODE_CUSTOM	(0xf800d000, 0xf0008000, t32_decode_cond_branch),
> +	DECODE_CUSTOM	(0xf800d000, 0xf0008000, PROBES_T32_BRANCH_COND),
>  
>  	/* BLX			1111 0xxx xxxx xxxx 11x0 xxxx xxxx xxx0 */
>  	DECODE_OR	(0xf800d001, 0xf000c000),
>  	/* B			1111 0xxx xxxx xxxx 10x1 xxxx xxxx xxxx */
>  	/* BL			1111 0xxx xxxx xxxx 11x1 xxxx xxxx xxxx */
> -	DECODE_SIMULATE	(0xf8009000, 0xf0009000, t32_simulate_branch),
> +	DECODE_SIMULATE	(0xf8009000, 0xf0009000, PROBES_T32_BRANCH),
>  
>  	DECODE_END
>  };
> @@ -589,7 +593,7 @@ static const union decode_item t32_table_1111_100x_x0x1__1111[] = {
>  
>  	/* PLD (literal)	1111 1000 x001 1111 1111 xxxx xxxx xxxx */
>  	/* PLI (literal)	1111 1001 x001 1111 1111 xxxx xxxx xxxx */
> -	DECODE_SIMULATE	(0xfe7ff000, 0xf81ff000, probes_simulate_nop),
> +	DECODE_SIMULATE	(0xfe7ff000, 0xf81ff000, PROBES_T32_PLDI),
>  
>  	/* PLD{W} (immediate)	1111 1000 10x1 xxxx 1111 xxxx xxxx xxxx */
>  	DECODE_OR	(0xffd0f000, 0xf890f000),
> @@ -598,13 +602,13 @@ static const union decode_item t32_table_1111_100x_x0x1__1111[] = {
>  	/* PLI (immediate)	1111 1001 1001 xxxx 1111 xxxx xxxx xxxx */
>  	DECODE_OR	(0xfff0f000, 0xf990f000),
>  	/* PLI (immediate)	1111 1001 0001 xxxx 1111 1100 xxxx xxxx */
> -	DECODE_SIMULATEX(0xfff0ff00, 0xf910fc00, probes_simulate_nop,
> +	DECODE_SIMULATEX(0xfff0ff00, 0xf910fc00, PROBES_T32_PLDI,
>  						 REGS(NOPCX, 0, 0, 0, 0)),
>  
>  	/* PLD{W} (register)	1111 1000 00x1 xxxx 1111 0000 00xx xxxx */
>  	DECODE_OR	(0xffd0ffc0, 0xf810f000),
>  	/* PLI (register)	1111 1001 0001 xxxx 1111 0000 00xx xxxx */
> -	DECODE_SIMULATEX(0xfff0ffc0, 0xf910f000, probes_simulate_nop,
> +	DECODE_SIMULATEX(0xfff0ffc0, 0xf910f000, PROBES_T32_PLDI,
>  						 REGS(NOPCX, 0, 0, 0, NOSPPC)),
>  
>  	/* Other unallocated instructions...				*/
> @@ -640,7 +644,7 @@ static const union decode_item t32_table_1111_100x[] = {
>  	DECODE_REJECT	(0xff10f000, 0xf800f000),
>  
>  	/* LDR (literal)	1111 1000 x101 1111 xxxx xxxx xxxx xxxx */
> -	DECODE_SIMULATEX(0xff7f0000, 0xf85f0000, t32_simulate_ldr_literal,
> +	DECODE_SIMULATEX(0xff7f0000, 0xf85f0000, PROBES_T32_LDR_LIT,
>  						 REGS(PC, ANY, 0, 0, 0)),
>  
>  	/* STR (immediate)	1111 1000 0100 xxxx xxxx 1xxx xxxx xxxx */
> @@ -648,19 +652,19 @@ static const union decode_item t32_table_1111_100x[] = {
>  	DECODE_OR	(0xffe00800, 0xf8400800),
>  	/* STR (immediate)	1111 1000 1100 xxxx xxxx xxxx xxxx xxxx */
>  	/* LDR (immediate)	1111 1000 1101 xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0xffe00000, 0xf8c00000, t32_emulate_ldrstr,
> +	DECODE_EMULATEX	(0xffe00000, 0xf8c00000, PROBES_T32_LDRSTR,
>  						 REGS(NOPCX, ANY, 0, 0, 0)),
>  
>  	/* STR (register)	1111 1000 0100 xxxx xxxx 0000 00xx xxxx */
>  	/* LDR (register)	1111 1000 0101 xxxx xxxx 0000 00xx xxxx */
> -	DECODE_EMULATEX	(0xffe00fc0, 0xf8400000, t32_emulate_ldrstr,
> +	DECODE_EMULATEX	(0xffe00fc0, 0xf8400000, PROBES_T32_LDRSTR,
>  						 REGS(NOPCX, ANY, 0, 0, NOSPPC)),
>  
>  	/* LDRB (literal)	1111 1000 x001 1111 xxxx xxxx xxxx xxxx */
>  	/* LDRSB (literal)	1111 1001 x001 1111 xxxx xxxx xxxx xxxx */
>  	/* LDRH (literal)	1111 1000 x011 1111 xxxx xxxx xxxx xxxx */
>  	/* LDRSH (literal)	1111 1001 x011 1111 xxxx xxxx xxxx xxxx */
> -	DECODE_SIMULATEX(0xfe5f0000, 0xf81f0000, t32_simulate_ldr_literal,
> +	DECODE_SIMULATEX(0xfe5f0000, 0xf81f0000, PROBES_T32_LDR_LIT,
>  						 REGS(PC, NOSPPCX, 0, 0, 0)),
>  
>  	/* STRB (immediate)	1111 1000 0000 xxxx xxxx 1xxx xxxx xxxx */
> @@ -676,7 +680,7 @@ static const union decode_item t32_table_1111_100x[] = {
>  	/* LDRSB (immediate)	1111 1001 1001 xxxx xxxx xxxx xxxx xxxx */
>  	/* LDRH (immediate)	1111 1000 1011 xxxx xxxx xxxx xxxx xxxx */
>  	/* LDRSH (immediate)	1111 1001 1011 xxxx xxxx xxxx xxxx xxxx */
> -	DECODE_EMULATEX	(0xfec00000, 0xf8800000, t32_emulate_ldrstr,
> +	DECODE_EMULATEX	(0xfec00000, 0xf8800000, PROBES_T32_LDRSTR,
>  						 REGS(NOPCX, NOSPPCX, 0, 0, 0)),
>  
>  	/* STRB (register)	1111 1000 0000 xxxx xxxx 0000 00xx xxxx */
> @@ -685,7 +689,7 @@ static const union decode_item t32_table_1111_100x[] = {
>  	/* LDRSB (register)	1111 1001 0001 xxxx xxxx 0000 00xx xxxx */
>  	/* LDRH (register)	1111 1000 0011 xxxx xxxx 0000 00xx xxxx */
>  	/* LDRSH (register)	1111 1001 0011 xxxx xxxx 0000 00xx xxxx */
> -	DECODE_EMULATEX	(0xfe800fc0, 0xf8000000, t32_emulate_ldrstr,
> +	DECODE_EMULATEX	(0xfe800fc0, 0xf8000000, PROBES_T32_LDRSTR,
>  						 REGS(NOPCX, NOSPPCX, 0, 0, NOSPPC)),
>  
>  	/* Other unallocated instructions...				*/
> @@ -704,7 +708,7 @@ static const union decode_item t32_table_1111_1010___1111[] = {
>  	/* UXTB16		1111 1010 0011 1111 1111 xxxx 1xxx xxxx */
>  	/* SXTB			1111 1010 0100 1111 1111 xxxx 1xxx xxxx */
>  	/* UXTB			1111 1010 0101 1111 1111 xxxx 1xxx xxxx */
> -	DECODE_EMULATEX	(0xff8ff080, 0xfa0ff080, t32_emulate_rd8rn16rm0_rwflags,
> +	DECODE_EMULATEX	(0xff8ff080, 0xfa0ff080, PROBES_T32_SIGN_EXTEND,
>  						 REGS(0, 0, NOSPPC, 0, NOSPPC)),
>  
> 
> @@ -777,7 +781,7 @@ static const union decode_item t32_table_1111_1010___1111[] = {
>  	/* LSR			1111 1010 001x xxxx 1111 xxxx 0000 xxxx */
>  	/* ASR			1111 1010 010x xxxx 1111 xxxx 0000 xxxx */
>  	/* ROR			1111 1010 011x xxxx 1111 xxxx 0000 xxxx */
> -	DECODE_EMULATEX	(0xff80f0f0, 0xfa00f000, t32_emulate_rd8rn16rm0_rwflags,
> +	DECODE_EMULATEX	(0xff80f0f0, 0xfa00f000, PROBES_T32_MEDIA,
>  						 REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)),
>  
>  	/* CLZ			1111 1010 1010 xxxx 1111 xxxx 1000 xxxx */
> @@ -787,7 +791,7 @@ static const union decode_item t32_table_1111_1010___1111[] = {
>  	/* REV16		1111 1010 1001 xxxx 1111 xxxx 1001 xxxx */
>  	/* RBIT			1111 1010 1001 xxxx 1111 xxxx 1010 xxxx */
>  	/* REVSH		1111 1010 1001 xxxx 1111 xxxx 1011 xxxx */
> -	DECODE_EMULATEX	(0xfff0f0c0, 0xfa90f080, t32_emulate_rd8rn16_noflags,
> +	DECODE_EMULATEX	(0xfff0f0c0, 0xfa90f080, PROBES_T32_REVERSE,
>  						 REGS(NOSPPC, 0, NOSPPC, 0, SAMEAS16)),
>  
>  	/* Other unallocated instructions...				*/
> @@ -810,7 +814,7 @@ static const union decode_item t32_table_1111_1011_0[] = {
>  	/* SMUSD{X}		1111 1011 0100 xxxx 1111 xxxx 000x xxxx */
>  	/* SMMUL{R}		1111 1011 0101 xxxx 1111 xxxx 000x xxxx */
>  	/* USAD8		1111 1011 0111 xxxx 1111 xxxx 0000 xxxx */
> -	DECODE_EMULATEX	(0xff80f0e0, 0xfb00f000, t32_emulate_rd8rn16rm0_rwflags,
> +	DECODE_EMULATEX	(0xff80f0e0, 0xfb00f000, PROBES_T32_MUL_ADD,
>  						 REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)),
>  
>  	/* ???			1111 1011 0111 xxxx xxxx xxxx 0001 xxxx */
> @@ -826,7 +830,7 @@ static const union decode_item t32_table_1111_1011_0[] = {
>  	/* SMMLA{R}		1111 1011 0101 xxxx xxxx xxxx 000x xxxx */
>  	/* SMMLS{R}		1111 1011 0110 xxxx xxxx xxxx 000x xxxx */
>  	/* USADA8		1111 1011 0111 xxxx xxxx xxxx 0000 xxxx */
> -	DECODE_EMULATEX	(0xff8000c0, 0xfb000000, t32_emulate_rd8rn16rm0ra12_noflags,
> +	DECODE_EMULATEX	(0xff8000c0, 0xfb000000,  PROBES_T32_MUL_ADD2,
>  						 REGS(NOSPPC, NOSPPCX, NOSPPC, 0, NOSPPC)),
>  
>  	/* Other unallocated instructions...				*/
> @@ -847,7 +851,7 @@ static const union decode_item t32_table_1111_1011_1[] = {
>  	/* UMULL		1111 1011 1010 xxxx xxxx xxxx 0000 xxxx */
>  	/* SMLAL		1111 1011 1100 xxxx xxxx xxxx 0000 xxxx */
>  	/* UMLAL		1111 1011 1110 xxxx xxxx xxxx 0000 xxxx */
> -	DECODE_EMULATEX	(0xff9000f0, 0xfb800000, t32_emulate_rdlo12rdhi8rn16rm0_noflags,
> +	DECODE_EMULATEX	(0xff9000f0, 0xfb800000, PROBES_T32_MUL_ADD_LONG,
>  						 REGS(NOSPPC, NOSPPC, NOSPPC, 0, NOSPPC)),
>  
>  	/* SDIV			1111 1011 1001 xxxx xxxx xxxx 1111 xxxx */
> @@ -1046,7 +1050,7 @@ t16_singlestep_it(struct kprobe *p, struct pt_regs *regs)
>  }
>  
>  static enum kprobe_insn __kprobes
> -t16_decode_it(kprobe_opcode_t insn, struct arch_specific_insn *asi)
> +t16_decode_it(kprobe_opcode_t insn, struct arch_specific_insn *asi, void *d)
>  {
>  	asi->insn_singlestep = t16_singlestep_it;
>  	return INSN_GOOD_NO_SLOT;
> @@ -1063,7 +1067,8 @@ t16_simulate_cond_branch(struct kprobe *p, struct pt_regs *regs)
>  }
>  
>  static enum kprobe_insn __kprobes
> -t16_decode_cond_branch(kprobe_opcode_t insn, struct arch_specific_insn *asi)
> +t16_decode_cond_branch(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> +		       void *d)
>  {
>  	int cc = (insn >> 8) & 0xf;
>  	asi->insn_check_cc = kprobe_condition_checks[cc];
> @@ -1149,7 +1154,7 @@ t16_emulate_hiregs(struct kprobe *p, struct pt_regs *regs)
>  }
>  
>  static enum kprobe_insn __kprobes
> -t16_decode_hiregs(kprobe_opcode_t insn, struct arch_specific_insn *asi)
> +t16_decode_hiregs(kprobe_opcode_t insn, struct arch_specific_insn *asi, void *d)
>  {
>  	insn &= ~0x00ff;
>  	insn |= 0x001; /* Set Rdn = R1 and Rm = R0 */
> @@ -1175,7 +1180,7 @@ t16_emulate_push(struct kprobe *p, struct pt_regs *regs)
>  }
>  
>  static enum kprobe_insn __kprobes
> -t16_decode_push(kprobe_opcode_t insn, struct arch_specific_insn *asi)
> +t16_decode_push(kprobe_opcode_t insn, struct arch_specific_insn *asi, void *d)
>  {
>  	/*
>  	 * To simulate a PUSH we use a Thumb-2 "STMDB R9!, {registers}"
> @@ -1225,7 +1230,7 @@ t16_emulate_pop_pc(struct kprobe *p, struct pt_regs *regs)
>  }
>  
>  static enum kprobe_insn __kprobes
> -t16_decode_pop(kprobe_opcode_t insn, struct arch_specific_insn *asi)
> +t16_decode_pop(kprobe_opcode_t insn, struct arch_specific_insn *asi, void *d)
>  {
>  	/*
>  	 * To simulate a POP we use a Thumb-2 "LDMDB R9!, {registers}"
> @@ -1244,11 +1249,11 @@ static const union decode_item t16_table_1011[] = {
>  
>  	/* ADD (SP plus immediate)	1011 0000 0xxx xxxx */
>  	/* SUB (SP minus immediate)	1011 0000 1xxx xxxx */
> -	DECODE_SIMULATE	(0xff00, 0xb000, t16_simulate_add_sp_imm),
> +	DECODE_SIMULATE	(0xff00, 0xb000, PROBES_T16_ADD_SP),
>  
>  	/* CBZ				1011 00x1 xxxx xxxx */
>  	/* CBNZ				1011 10x1 xxxx xxxx */
> -	DECODE_SIMULATE	(0xf500, 0xb100, t16_simulate_cbz),
> +	DECODE_SIMULATE	(0xf500, 0xb100, PROBES_T16_CBZ),
>  
>  	/* SXTH				1011 0010 00xx xxxx */
>  	/* SXTB				1011 0010 01xx xxxx */
> @@ -1259,12 +1264,12 @@ static const union decode_item t16_table_1011[] = {
>  	/* ???				1011 1010 10xx xxxx */
>  	/* REVSH			1011 1010 11xx xxxx */
>  	DECODE_REJECT	(0xffc0, 0xba80),
> -	DECODE_EMULATE	(0xf500, 0xb000, t16_emulate_loregs_rwflags),
> +	DECODE_EMULATE	(0xf500, 0xb000, PROBES_T16_SIGN_EXTEND),
>  
>  	/* PUSH				1011 010x xxxx xxxx */
> -	DECODE_CUSTOM	(0xfe00, 0xb400, t16_decode_push),
> +	DECODE_CUSTOM	(0xfe00, 0xb400, PROBES_T16_PUSH),
>  	/* POP				1011 110x xxxx xxxx */
> -	DECODE_CUSTOM	(0xfe00, 0xbc00, t16_decode_pop),
> +	DECODE_CUSTOM	(0xfe00, 0xbc00, PROBES_T16_POP),
>  
>  	/*
>  	 * If-Then, and hints
> @@ -1274,15 +1279,15 @@ static const union decode_item t16_table_1011[] = {
>  	/* YIELD			1011 1111 0001 0000 */
>  	DECODE_OR	(0xffff, 0xbf10),
>  	/* SEV				1011 1111 0100 0000 */
> -	DECODE_EMULATE	(0xffff, 0xbf40, probes_emulate_none),
> +	DECODE_EMULATE	(0xffff, 0xbf40, PROBES_T16_SEV),
>  	/* NOP				1011 1111 0000 0000 */
>  	/* WFE				1011 1111 0010 0000 */
>  	/* WFI				1011 1111 0011 0000 */
> -	DECODE_SIMULATE	(0xffcf, 0xbf00, probes_simulate_nop),
> +	DECODE_SIMULATE	(0xffcf, 0xbf00, PROBES_T16_WFE),
>  	/* Unassigned hints		1011 1111 xxxx 0000 */
>  	DECODE_REJECT	(0xff0f, 0xbf00),
>  	/* IT				1011 1111 xxxx xxxx */
> -	DECODE_CUSTOM	(0xff00, 0xbf00, t16_decode_it),
> +	DECODE_CUSTOM	(0xff00, 0xbf00, PROBES_T16_IT),
>  
>  	/* SETEND			1011 0110 010x xxxx */
>  	/* CPS				1011 0110 011x xxxx */
> @@ -1299,7 +1304,7 @@ const union decode_item kprobe_decode_thumb16_table[] = {
>  	 */
>  
>  	/* CMP (immediate)		0010 1xxx xxxx xxxx */
> -	DECODE_EMULATE	(0xf800, 0x2800, t16_emulate_loregs_rwflags),
> +	DECODE_EMULATE	(0xf800, 0x2800, PROBES_T16_CMP),
>  
>  	/* ADD (register)		0001 100x xxxx xxxx */
>  	/* SUB (register)		0001 101x xxxx xxxx */
> @@ -1311,7 +1316,7 @@ const union decode_item kprobe_decode_thumb16_table[] = {
>  	/* MOV (immediate)		0010 0xxx xxxx xxxx */
>  	/* ADD (immediate, Thumb)	0011 0xxx xxxx xxxx */
>  	/* SUB (immediate, Thumb)	0011 1xxx xxxx xxxx */
> -	DECODE_EMULATE	(0xc000, 0x0000, t16_emulate_loregs_noitrwflags),
> +	DECODE_EMULATE	(0xc000, 0x0000, PROBES_T16_ADDSUB),
>  
>  	/*
>  	 * 16-bit Thumb data-processing instructions
> @@ -1319,10 +1324,10 @@ const union decode_item kprobe_decode_thumb16_table[] = {
>  	 */
>  
>  	/* TST (register)		0100 0010 00xx xxxx */
> -	DECODE_EMULATE	(0xffc0, 0x4200, t16_emulate_loregs_rwflags),
> +	DECODE_EMULATE	(0xffc0, 0x4200, PROBES_T16_CMP),
>  	/* CMP (register)		0100 0010 10xx xxxx */
>  	/* CMN (register)		0100 0010 11xx xxxx */
> -	DECODE_EMULATE	(0xff80, 0x4280, t16_emulate_loregs_rwflags),
> +	DECODE_EMULATE	(0xff80, 0x4280, PROBES_T16_CMP),
>  	/* AND (register)		0100 0000 00xx xxxx */
>  	/* EOR (register)		0100 0000 01xx xxxx */
>  	/* LSL (register)		0100 0000 10xx xxxx */
> @@ -1336,7 +1341,7 @@ const union decode_item kprobe_decode_thumb16_table[] = {
>  	/* MUL				0100 0011 00xx xxxx */
>  	/* BIC (register)		0100 0011 10xx xxxx */
>  	/* MVN (register)		0100 0011 10xx xxxx */
> -	DECODE_EMULATE	(0xfc00, 0x4000, t16_emulate_loregs_noitrwflags),
> +	DECODE_EMULATE	(0xfc00, 0x4000, PROBES_T16_LOGICAL),
>  
>  	/*
>  	 * Special data instructions and branch and exchange
> @@ -1348,7 +1353,7 @@ const union decode_item kprobe_decode_thumb16_table[] = {
>  
>  	/* BX (register)		0100 0111 0xxx xxxx */
>  	/* BLX (register)		0100 0111 1xxx xxxx */
> -	DECODE_SIMULATE (0xff00, 0x4700, t16_simulate_bxblx),
> +	DECODE_SIMULATE (0xff00, 0x4700, PROBES_T16_BLX),
>  
>  	/* ADD pc, pc			0100 0100 1111 1111 */
>  	DECODE_REJECT	(0xffff, 0x44ff),
> @@ -1356,13 +1361,13 @@ const union decode_item kprobe_decode_thumb16_table[] = {
>  	/* ADD (register)		0100 0100 xxxx xxxx */
>  	/* CMP (register)		0100 0101 xxxx xxxx */
>  	/* MOV (register)		0100 0110 xxxx xxxx */
> -	DECODE_CUSTOM	(0xfc00, 0x4400, t16_decode_hiregs),
> +	DECODE_CUSTOM	(0xfc00, 0x4400, PROBES_T16_HIREGOPS),
>  
>  	/*
>  	 * Load from Literal Pool
>  	 * LDR (literal)		0100 1xxx xxxx xxxx
>  	 */
> -	DECODE_SIMULATE	(0xf800, 0x4800, t16_simulate_ldr_literal),
> +	DECODE_SIMULATE	(0xf800, 0x4800, PROBES_T16_LDR_LIT),
>  
>  	/*
>  	 * 16-bit Thumb Load/store instructions
> @@ -1383,20 +1388,20 @@ const union decode_item kprobe_decode_thumb16_table[] = {
>  	/* LDR (immediate, Thumb)	0110 1xxx xxxx xxxx */
>  	/* STRB (immediate, Thumb)	0111 0xxx xxxx xxxx */
>  	/* LDRB (immediate, Thumb)	0111 1xxx xxxx xxxx */
> -	DECODE_EMULATE	(0xc000, 0x4000, t16_emulate_loregs_rwflags),
> +	DECODE_EMULATE	(0xc000, 0x4000, PROBES_T16_LDRHSTRH),
>  	/* STRH (immediate, Thumb)	1000 0xxx xxxx xxxx */
>  	/* LDRH (immediate, Thumb)	1000 1xxx xxxx xxxx */
> -	DECODE_EMULATE	(0xf000, 0x8000, t16_emulate_loregs_rwflags),
> +	DECODE_EMULATE	(0xf000, 0x8000, PROBES_T16_LDRHSTRH),
>  	/* STR (immediate, Thumb)	1001 0xxx xxxx xxxx */
>  	/* LDR (immediate, Thumb)	1001 1xxx xxxx xxxx */
> -	DECODE_SIMULATE	(0xf000, 0x9000, t16_simulate_ldrstr_sp_relative),
> +	DECODE_SIMULATE	(0xf000, 0x9000, PROBES_T16_LDRSTR),
>  
>  	/*
>  	 * Generate PC-/SP-relative address
>  	 * ADR (literal)		1010 0xxx xxxx xxxx
>  	 * ADD (SP plus immediate)	1010 1xxx xxxx xxxx
>  	 */
> -	DECODE_SIMULATE	(0xf000, 0xa000, t16_simulate_reladr),
> +	DECODE_SIMULATE	(0xf000, 0xa000, PROBES_T16_ADR),
>  
>  	/*
>  	 * Miscellaneous 16-bit instructions
> @@ -1406,7 +1411,7 @@ const union decode_item kprobe_decode_thumb16_table[] = {
>  
>  	/* STM				1100 0xxx xxxx xxxx */
>  	/* LDM				1100 1xxx xxxx xxxx */
> -	DECODE_EMULATE	(0xf000, 0xc000, t16_emulate_loregs_rwflags),
> +	DECODE_EMULATE	(0xf000, 0xc000, PROBES_T16_LDMSTM),
>  
>  	/*
>  	 * Conditional branch, and Supervisor Call
> @@ -1417,16 +1422,70 @@ const union decode_item kprobe_decode_thumb16_table[] = {
>  	DECODE_REJECT	(0xfe00, 0xde00),
>  
>  	/* Conditional branch		1101 xxxx xxxx xxxx */
> -	DECODE_CUSTOM	(0xf000, 0xd000, t16_decode_cond_branch),
> +	DECODE_CUSTOM	(0xf000, 0xd000, PROBES_T16_BRANCH_COND),
>  
>  	/*
>  	 * Unconditional branch
>  	 * B				1110 0xxx xxxx xxxx
>  	 */
> -	DECODE_SIMULATE	(0xf800, 0xe000, t16_simulate_branch),
> +	DECODE_SIMULATE	(0xf800, 0xe000, PROBES_T16_BRANCH),
>  
>  	DECODE_END
>  };
> +
> +const union decode_item kprobes_t32_actions[] = {

I wonder if it would be a good idea to make sure these arrays are the
size we expect by adding an entry at the end of the relevant enumeration
and using that it set the size of the arrays. E.g. for this one

enum probes_t32_action {
	...
	...
	NUM_PROBES_T32_ACTIONS
};

and then use it like

const union decode_item kprobes_t32_actions[NUM_PROBES_T32_ACTIONS]

that way, we at least make any uninitialised entries are null (i
assume?) which is safer than accidentally indexing beyond the array.

> +	[PROBES_T32_LDMSTM] = {.decoder = t32_decode_ldmstm},
> +	[PROBES_T32_LDRDSTRD] = {.handler = t32_emulate_ldrdstrd},
> +	[PROBES_T32_TABLE_BRANCH] = {.handler = t32_simulate_table_branch},
> +	[PROBES_T32_TST] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
> +	[PROBES_T32_MOV] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
> +	[PROBES_T32_ADDSUB] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
> +	[PROBES_T32_LOGICAL] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
> +	[PROBES_T32_CMP] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
> +	[PROBES_T32_ADDWSUBW_PC] = {.handler = t32_emulate_rd8pc16_noflags,},
> +	[PROBES_T32_ADDWSUBW] = {.handler = t32_emulate_rd8rn16_noflags},
> +	[PROBES_T32_MOVW] = {.handler = t32_emulate_rd8rn16_noflags},
> +	[PROBES_T32_SAT] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
> +	[PROBES_T32_BITFIELD] = {.handler = t32_emulate_rd8rn16_noflags},
> +	[PROBES_T32_SEV] = {.handler = probes_emulate_none},
> +	[PROBES_T32_WFE] = {.handler = probes_simulate_nop},
> +	[PROBES_T32_MRS] = {.handler = t32_simulate_mrs},
> +	[PROBES_T32_BRANCH_COND] = {.decoder = t32_decode_cond_branch},
> +	[PROBES_T32_BRANCH] = {.handler = t32_simulate_branch},
> +	[PROBES_T32_PLDI] = {.handler = probes_simulate_nop},
> +	[PROBES_T32_LDR_LIT] = {.handler = t32_simulate_ldr_literal},
> +	[PROBES_T32_LDRSTR] = {.handler = t32_emulate_ldrstr},
> +	[PROBES_T32_SIGN_EXTEND] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
> +	[PROBES_T32_MEDIA] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
> +	[PROBES_T32_REVERSE] = {.handler = t32_emulate_rd8rn16_noflags},
> +	[PROBES_T32_MUL_ADD] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
> +	[PROBES_T32_MUL_ADD2] = {.handler = t32_emulate_rd8rn16rm0ra12_noflags},
> +	[PROBES_T32_MUL_ADD_LONG] = {.handler = t32_emulate_rdlo12rdhi8rn16rm0_noflags},
> +};
> +
> +const union decode_item kprobes_t16_actions[] = {
> +	[PROBES_T16_ADD_SP] = {.handler = t16_simulate_add_sp_imm},
> +	[PROBES_T16_CBZ] = {.handler = t16_simulate_cbz},
> +	[PROBES_T16_SIGN_EXTEND] = {.handler = t16_emulate_loregs_rwflags},
> +	[PROBES_T16_PUSH] = {.decoder = t16_decode_push},
> +	[PROBES_T16_POP] = {.decoder = t16_decode_pop},
> +	[PROBES_T16_SEV] = {.handler = probes_emulate_none},
> +	[PROBES_T16_WFE] = {.handler = probes_simulate_nop},
> +	[PROBES_T16_IT] = {.decoder = t16_decode_it},
> +	[PROBES_T16_CMP] = {.handler = t16_emulate_loregs_rwflags},
> +	[PROBES_T16_ADDSUB] = {.handler = t16_emulate_loregs_noitrwflags},
> +	[PROBES_T16_LOGICAL] = {.handler = t16_emulate_loregs_noitrwflags},
> +	[PROBES_T16_LDR_LIT] = {.handler = t16_simulate_ldr_literal},
> +	[PROBES_T16_BLX] = {.handler = t16_simulate_bxblx},
> +	[PROBES_T16_HIREGOPS] = {.decoder = t16_decode_hiregs},
> +	[PROBES_T16_LDRHSTRH] = {.handler = t16_emulate_loregs_rwflags},
> +	[PROBES_T16_LDRSTR] = {.handler = t16_simulate_ldrstr_sp_relative},
> +	[PROBES_T16_ADR] = {.handler = t16_simulate_reladr},
> +	[PROBES_T16_LDMSTM] = {.handler = t16_emulate_loregs_rwflags},
> +	[PROBES_T16_BRANCH_COND] = {.decoder = t16_decode_cond_branch},
> +	[PROBES_T16_BRANCH] = {.handler = t16_simulate_branch},
> +};
> +
>  #ifdef CONFIG_ARM_KPROBES_TEST_MODULE
>  EXPORT_SYMBOL_GPL(kprobe_decode_thumb16_table);
>  #endif
> @@ -1453,17 +1512,21 @@ static void __kprobes thumb32_singlestep(struct kprobe *p, struct pt_regs *regs)
>  }
>  
>  enum kprobe_insn __kprobes
> -thumb16_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi)
> +thumb16_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> +			   bool usermode, const union decode_item *actions)
>  {
>  	asi->insn_singlestep = thumb16_singlestep;
>  	asi->insn_check_cc = thumb_check_cc;
> -	return kprobe_decode_insn(insn, asi, kprobe_decode_thumb16_table, true);
> +	return probes_decode_insn(insn, asi, kprobe_decode_thumb16_table, true,
> +				  usermode, actions);
>  }
>  
>  enum kprobe_insn __kprobes
> -thumb32_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi)
> +thumb32_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> +			   bool usermode, const union decode_item *actions)
>  {
>  	asi->insn_singlestep = thumb32_singlestep;
>  	asi->insn_check_cc = thumb_check_cc;
> -	return kprobe_decode_insn(insn, asi, kprobe_decode_thumb32_table, true);
> +	return probes_decode_insn(insn, asi, kprobe_decode_thumb32_table, true,
> +				  usermode, actions);
>  }
> diff --git a/arch/arm/kernel/kprobes.c b/arch/arm/kernel/kprobes.c
> index 170e9f3..7b484d3 100644
> --- a/arch/arm/kernel/kprobes.c
> +++ b/arch/arm/kernel/kprobes.c
> @@ -29,6 +29,7 @@
>  #include <asm/cacheflush.h>
>  
>  #include "kprobes.h"
> +#include "probes.h"
>  #include "patch.h"
>  
>  #define MIN_STACK_SIZE(addr) 				\
> @@ -54,6 +55,7 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
>  	unsigned long addr = (unsigned long)p->addr;
>  	bool thumb;
>  	kprobe_decode_insn_t *decode_insn;
> +	const union decode_item *actions;
>  	int is;
>  
>  	if (in_exception_text(addr))
> @@ -67,20 +69,25 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
>  		insn <<= 16;
>  		insn |= ((u16 *)addr)[1];
>  		decode_insn = thumb32_kprobe_decode_insn;
> -	} else
> +		actions = kprobes_t32_actions;
> +	} else {
>  		decode_insn = thumb16_kprobe_decode_insn;
> +		actions = kprobes_t16_actions;
> +	}
>  #else /* !CONFIG_THUMB2_KERNEL */
>  	thumb = false;
>  	if (addr & 0x3)
>  		return -EINVAL;
>  	insn = *p->addr;
>  	decode_insn = arm_kprobe_decode_insn;
> +	actions = kprobes_probes_actions;
>  #endif
>  
>  	p->opcode = insn;
>  	p->ainsn.insn = tmp_insn;
>  
> -	switch ((*decode_insn)(insn, &p->ainsn)) {
> +	switch ((*decode_insn)(insn, &p->ainsn, false,
> +			       actions)) {
>  	case INSN_REJECTED:	/* not supported */
>  		return -EINVAL;
>  
> diff --git a/arch/arm/kernel/kprobes.h b/arch/arm/kernel/kprobes.h
> index 9aa2f15..6ca1cbe 100644
> --- a/arch/arm/kernel/kprobes.h
> +++ b/arch/arm/kernel/kprobes.h
> @@ -34,22 +34,6 @@ enum kprobe_insn {
>  	INSN_GOOD_NO_SLOT
>  };
>  
> -typedef enum kprobe_insn (kprobe_decode_insn_t)(kprobe_opcode_t,
> -						struct arch_specific_insn *);
> -
> -#ifdef CONFIG_THUMB2_KERNEL
> -
> -enum kprobe_insn thumb16_kprobe_decode_insn(kprobe_opcode_t,
> -						struct arch_specific_insn *);
> -enum kprobe_insn thumb32_kprobe_decode_insn(kprobe_opcode_t,
> -						struct arch_specific_insn *);
> -
> -#else /* !CONFIG_THUMB2_KERNEL */
> -
> -enum kprobe_insn arm_kprobe_decode_insn(kprobe_opcode_t,
> -					struct arch_specific_insn *);
> -#endif
> -
>  void __init arm_kprobe_decode_init(void);
>  
>  extern kprobe_check_cc * const kprobe_condition_checks[16];
> @@ -161,11 +145,9 @@ static inline void __kprobes alu_write_pc(long pcv, struct pt_regs *regs)
>  }
>  
> 
> -void __kprobes probes_simulate_nop(struct kprobe *p, struct pt_regs *regs);
> -void __kprobes probes_emulate_none(struct kprobe *p, struct pt_regs *regs);
> -
>  enum kprobe_insn __kprobes
> -kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi);
> +kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> +		     void *d);
>  
>  /*
>   * Test if load/store instructions writeback the address register.
> @@ -331,7 +313,9 @@ union decode_item {
>  	u32			bits;
>  	const union decode_item	*table;
>  	kprobe_insn_handler_t	*handler;
> -	kprobe_decode_insn_t	*decoder;
> +	enum kprobe_insn (*decoder)(kprobe_opcode_t,
> +				    struct arch_specific_insn *,
> +				    void *d);
>  };
>  
> 
> @@ -368,7 +352,7 @@ struct decode_custom {
>  
>  #define DECODE_CUSTOM(_mask, _value, _decoder)			\
>  	DECODE_HEADER(DECODE_TYPE_CUSTOM, _mask, _value, 0),	\
> -	{.decoder = (_decoder)}
> +	{.bits = (_decoder)}

'bits' looks a bit funny here. I've been trying to think of a way of
making it nicer but it's difficult. The actual value is one of three
different enums, so if we were to add another members to decode_item it
would just have to be "int action", at least that would read nicer in
these macros and where it gets read out in probes_decode_insn.

>  
> 
>  struct decode_simulate {
> @@ -378,7 +362,7 @@ struct decode_simulate {
>  
>  #define DECODE_SIMULATEX(_mask, _value, _handler, _regs)		\
>  	DECODE_HEADER(DECODE_TYPE_SIMULATE, _mask, _value, _regs),	\
> -	{.handler = (_handler)}
> +	{.bits = (_handler)}
>  
>  #define DECODE_SIMULATE(_mask, _value, _handler)	\
>  	DECODE_SIMULATEX(_mask, _value, _handler, 0)
> @@ -391,12 +375,11 @@ struct decode_emulate {
>  
>  #define DECODE_EMULATEX(_mask, _value, _handler, _regs)			\
>  	DECODE_HEADER(DECODE_TYPE_EMULATE, _mask, _value, _regs),	\
> -	{.handler = (_handler)}
> +	{.bits = (_handler)}
>  
>  #define DECODE_EMULATE(_mask, _value, _handler)		\
>  	DECODE_EMULATEX(_mask, _value, _handler, 0)
>  
> -
>  struct decode_or {
>  	struct decode_header	header;
>  };
> @@ -413,16 +396,39 @@ struct decode_reject {
>  	DECODE_HEADER(DECODE_TYPE_REJECT, _mask, _value, 0)
>  
> 
> +typedef enum kprobe_insn (kprobe_decode_insn_t)(kprobe_opcode_t,
> +						struct arch_specific_insn *,
> +						bool usermode,
> +						const union decode_item *actions);
> +
>  #ifdef CONFIG_THUMB2_KERNEL
> +
> +enum kprobe_insn thumb16_kprobe_decode_insn(kprobe_opcode_t,
> +					    struct arch_specific_insn *,
> +					    bool usermode,
> +					    const union decode_item *actions);
> +
> +enum kprobe_insn thumb32_kprobe_decode_insn(kprobe_opcode_t,
> +					    struct arch_specific_insn *,
> +					    bool usermode,
> +					    const union decode_item *actions);
> +
>  extern const union decode_item kprobe_decode_thumb16_table[];
>  extern const union decode_item kprobe_decode_thumb32_table[];
> -#else
> -extern const union decode_item kprobe_decode_arm_table[];
> -#endif
> +extern const union decode_item kprobes_t32_actions[];
> +extern const union decode_item kprobes_t16_actions[];
>  
> +#else
>  
>  int kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> -			const union decode_item *table, bool thumb16);
> +			const union decode_item *table, bool thumb16,
> +			bool usermode,
> +			const union decode_item *actions);
> +
> +extern const union decode_item kprobe_decode_arm_table[];
> +extern const union decode_item kprobes_probes_actions[];
> +
> +#endif
>  
> 
>  #endif /* _ARM_KERNEL_KPROBES_H */
> diff --git a/arch/arm/kernel/probes-arm.c b/arch/arm/kernel/probes-arm.c
> index e1b1a6e..e67bf3f 100644
> --- a/arch/arm/kernel/probes-arm.c
> +++ b/arch/arm/kernel/probes-arm.c
> @@ -18,8 +18,8 @@
>  #include <linux/kernel.h>
>  #include <linux/kprobes.h>
>  
> -#include "probes.h"
>  #include "kprobes.h"
> +#include "probes.h"

Minor nitpick, perhaps it would have been best to added the include in
the right place in the earlier patch rather than move it here.


>  #define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
>  
> diff --git a/arch/arm/kernel/probes-arm.h b/arch/arm/kernel/probes-arm.h
> new file mode 100644
> index 0000000..b1690878
> --- /dev/null
> +++ b/arch/arm/kernel/probes-arm.h
> @@ -0,0 +1,60 @@
> +#ifndef _ARM_KERNEL_PROBES_ARM_H
> +#define  _ARM_KERNEL_PROBES_ARM_H
> +
> +enum probes_action {
> +	PROBES_EMULATE_NONE,
> +	PROBES_SIMULATE_NOP,
> +	PROBES_PRELOAD_IMM,
> +	PROBES_PRELOAD_REG,
> +	PROBES_BRANCH_IMM,
> +	PROBES_BRANCH_REG,
> +	PROBES_MRS,
> +	PROBES_CLZ,
> +	PROBES_SATURATING_ARITHMETIC,
> +	PROBES_MUL1,
> +	PROBES_MUL2,
> +	PROBES_SWP,
> +	PROBES_LDRSTRD,
> +	PROBES_LOAD,
> +	PROBES_STORE,
> +	PROBES_LOAD_EXTRA,
> +	PROBES_STORE_EXTRA,
> +	PROBES_MOV_IP_SP,
> +	PROBES_DATA_PROCESSING_REG,
> +	PROBES_DATA_PROCESSING_IMM,
> +	PROBES_MOV_HALFWORD,
> +	PROBES_SEV,
> +	PROBES_WFE,
> +	PROBES_SATURATE,
> +	PROBES_REV,
> +	PROBES_MMI,
> +	PROBES_PACK,
> +	PROBES_EXTEND,
> +	PROBES_EXTEND_ADD,
> +	PROBES_MUL_ADD_LONG,
> +	PROBES_MUL_ADD,
> +	PROBES_BITFIELD,
> +	PROBES_BRANCH,
> +	PROBES_LDMSTM
> +};
> +
> +void __kprobes simulate_bbl(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes simulate_blx1(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes simulate_blx2bx(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes simulate_mrs(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes simulate_mov_ipsp(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes emulate_ldrdstrd(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes emulate_ldr(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes emulate_str(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes emulate_rd12rn16rm0rs8_rwflags(struct kprobe *p,
> +					      struct pt_regs *regs);
> +void __kprobes emulate_rd12rn16rm0_rwflags_nopc(struct kprobe *p,
> +						struct pt_regs *regs);
> +void __kprobes emulate_rd16rn12rm0rs8_rwflags_nopc(struct kprobe *p,
> +						   struct pt_regs *regs);
> +void __kprobes emulate_rd12rm0_noflags_nopc(struct kprobe *p,
> +					    struct pt_regs *regs);
> +void __kprobes emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(struct kprobe *p,
> +						       struct pt_regs *regs);
> +
> +#endif
> diff --git a/arch/arm/kernel/probes-thumb.h b/arch/arm/kernel/probes-thumb.h
> new file mode 100644
> index 0000000..4b9365c
> --- /dev/null
> +++ b/arch/arm/kernel/probes-thumb.h
> @@ -0,0 +1,59 @@
> +#ifndef _ARM_KERNEL_PROBES_THUMB_H
> +#define  _ARM_KERNEL_PROBES_THUMB_H
> +
> +enum probes_t32_action {
> +	PROBES_T32_EMULATE_NONE,
> +	PROBES_T32_SIMULATE_NOP,
> +	PROBES_T32_LDMSTM,
> +	PROBES_T32_LDRDSTRD,
> +	PROBES_T32_TABLE_BRANCH,
> +	PROBES_T32_TST,
> +	PROBES_T32_CMP,
> +	PROBES_T32_MOV,
> +	PROBES_T32_ADDSUB,
> +	PROBES_T32_LOGICAL,
> +	PROBES_T32_ADDWSUBW_PC,
> +	PROBES_T32_ADDWSUBW,
> +	PROBES_T32_MOVW,
> +	PROBES_T32_SAT,
> +	PROBES_T32_BITFIELD,
> +	PROBES_T32_SEV,
> +	PROBES_T32_WFE,
> +	PROBES_T32_MRS,
> +	PROBES_T32_BRANCH_COND,
> +	PROBES_T32_BRANCH,
> +	PROBES_T32_PLDI,
> +	PROBES_T32_LDR_LIT,
> +	PROBES_T32_LDRSTR,
> +	PROBES_T32_SIGN_EXTEND,
> +	PROBES_T32_MEDIA,
> +	PROBES_T32_REVERSE,
> +	PROBES_T32_MUL_ADD,
> +	PROBES_T32_MUL_ADD2,
> +	PROBES_T32_MUL_ADD_LONG
> +};
> +
> +enum probes_t16_action {
> +	PROBES_T16_ADD_SP,
> +	PROBES_T16_CBZ,
> +	PROBES_T16_SIGN_EXTEND,
> +	PROBES_T16_PUSH,
> +	PROBES_T16_POP,
> +	PROBES_T16_SEV,
> +	PROBES_T16_WFE,
> +	PROBES_T16_IT,
> +	PROBES_T16_CMP,
> +	PROBES_T16_ADDSUB,
> +	PROBES_T16_LOGICAL,
> +	PROBES_T16_BLX,
> +	PROBES_T16_HIREGOPS,
> +	PROBES_T16_LDR_LIT,
> +	PROBES_T16_LDRHSTRH,
> +	PROBES_T16_LDRSTR,
> +	PROBES_T16_ADR,
> +	PROBES_T16_LDMSTM,
> +	PROBES_T16_BRANCH_COND,
> +	PROBES_T16_BRANCH
> +};
> +
> +#endif
> diff --git a/arch/arm/kernel/probes.c b/arch/arm/kernel/probes.c
> index 86c63f3..8cff27a 100644
> --- a/arch/arm/kernel/probes.c
> +++ b/arch/arm/kernel/probes.c
> @@ -1,23 +1,8 @@
> -/*
> - * arch/arm/kernel/probes.c
> - *
> - * Some contents moved here from arch/arm/include/asm/kprobes-common.c
> - *
> - * Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
> - *
> - * Some contents moved here from arch/arm/include/asm/kprobes-arm.c which is
> - * 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.
> - */
> -

Any reason why this is deleted?

>  #include <linux/kernel.h>
>  #include <linux/kprobes.h>
>  
> -#include "probes.h"
>  #include "kprobes.h"
> +#include "probes.h"
>  
>  void __kprobes probes_simulate_nop(struct kprobe *p, struct pt_regs *regs)
>  {
> @@ -28,6 +13,47 @@ void __kprobes probes_emulate_none(struct kprobe *p, struct pt_regs *regs)
>  	p->ainsn.insn_fn();
>  }
>  
> +#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
> +
> +#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25)
> +
> +#if  __LINUX_ARM_ARCH__ >= 6
> +#define BLX(reg)	"blx	"reg"		\n\t"
> +#else
> +#define BLX(reg)	"mov	lr, pc		\n\t"	\
> +			"mov	pc, "reg"	\n\t"
> +#endif
> +
> +/*
> + * To avoid 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 performance.
> + *
> + * 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 perform register writeback)
> + * 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.
> + */
> +

The whole added block above looks spurious, probably a mistake when
re-factoring patches?

>  /*
>   * Prepare an instruction slot to receive an instruction for emulating.
>   * This is done by placing a subroutine return after the location where the
> @@ -97,7 +123,7 @@ set_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
>   * non-zero value, the corresponding nibble in pinsn is validated and modified
>   * according to the type.
>   */
> -static bool __kprobes decode_regs(kprobe_opcode_t *pinsn, u32 regs)
> +static bool __kprobes decode_regs(kprobe_opcode_t *pinsn, u32 regs, bool modify)
>  {
>  	kprobe_opcode_t insn = *pinsn;
>  	kprobe_opcode_t mask = 0xf; /* Start at least significant nibble */
> @@ -158,12 +184,16 @@ static bool __kprobes decode_regs(kprobe_opcode_t *pinsn, u32 regs)
>  			break;
>  		}
>  
> -		/* Replace value of nibble with new register number... */
> -		insn &= ~mask;
> -		insn |= new_bits & mask;
> +		if (modify) {
> +			/* Replace value of nibble with new register number */
> +			insn &= ~mask;
> +			insn |= new_bits & mask;
> +		}
>  	}
>  
> -	*pinsn = insn;
> +	if (modify)
> +		*pinsn = insn;
> +
>  	return true;
>  
>  reject:
> @@ -223,14 +253,17 @@ static const int decode_struct_sizes[NUM_DECODE_TYPES] = {
>   *
>   */
>  int __kprobes
> -kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> -				const union decode_item *table, bool thumb)
> +probes_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> +				const union decode_item *table, bool thumb,
> +				bool usermode,
> +				const union decode_item *actions)
>  {
>  	const struct decode_header *h = (struct decode_header *)table;
>  	const struct decode_header *next;
>  	bool matched = false;
>  
> -	insn = prepare_emulated_insn(insn, asi, thumb);
> +	if (!usermode)
> +		insn = prepare_emulated_insn(insn, asi, thumb);
>  
>  	for (;; h = next) {
>  		enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK;
> @@ -242,14 +275,14 @@ kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
>  		next = (struct decode_header *)
>  				((uintptr_t)h + decode_struct_sizes[type]);
>  
> -		if (!matched && (insn & h->mask.bits) != h->value.bits)
> +		if (!matched &&
> +		    (insn & h->mask.bits) != h->value.bits)
>  			continue;

Any reason for splitting the line?

> -		if (!decode_regs(&insn, regs))
> +		if (!decode_regs(&insn, regs, !usermode))
>  			return INSN_REJECTED;
>  
>  		switch (type) {struct decode_header
> -
>  		case DECODE_TYPE_TABLE: {
>  			struct decode_table *d = (struct decode_table *)h;
>  			next = (struct decode_header *)d->table.table;
> @@ -258,18 +291,24 @@ kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
>  
>  		case DECODE_TYPE_CUSTOM: {
>  			struct decode_custom *d = (struct decode_custom *)h;
> -			return (*d->decoder.decoder)(insn, asi);
> +
> +			return actions[d->decoder.bits].decoder(insn, asi, d);
>  		}
>  
>  		case DECODE_TYPE_SIMULATE: {
>  			struct decode_simulate *d = (struct decode_simulate *)h;
> -			asi->insn_handler = d->handler.handler;
> +			asi->insn_handler = actions[d->handler.bits].handler;
>  			return INSN_GOOD_NO_SLOT;
>  		}
>  
>  		case DECODE_TYPE_EMULATE: {
>  			struct decode_emulate *d = (struct decode_emulate *)h;
> -			asi->insn_handler = d->handler.handler;
> +
> +			if (usermode)
> +				return actions[d->handler.bits].decoder(insn,
> +									asi, d);
> +
> +			asi->insn_handler = actions[d->handler.bits].handler;

This change of behaviour depending on usermode seems to indicate that we
haven't quite got the abstractions right. But at the moment I can't think
about a possible alternative though.

>  			set_emulated_insn(insn, asi, thumb);
>  			return INSN_GOOD;
>  		}
> diff --git a/arch/arm/kernel/probes.h b/arch/arm/kernel/probes.h
> index 56eec12..bc03c690 100644
> --- a/arch/arm/kernel/probes.h
> +++ b/arch/arm/kernel/probes.h
> @@ -1,6 +1,18 @@
>  #ifndef _ARM_KERNEL_PROBES_H
>  #define  _ARM_KERNEL_PROBES_H
>  
> +int __kprobes
> +probes_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> +				const union decode_item *table, bool thumb,
> +				bool usermode,
> +				const union decode_item *actions);
> +enum kprobe_insn arm_kprobe_decode_insn(kprobe_opcode_t,
> +					struct arch_specific_insn *,
> +					bool usermode,
> +					const union decode_item *actions);
> +
> +void __kprobes probes_simulate_nop(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes probes_emulate_none(struct kprobe *p, struct pt_regs *regs);
>  void __kprobes simulate_bbl(struct kprobe *p, struct pt_regs *regs);
>  void __kprobes simulate_blx1(struct kprobe *p, struct pt_regs *regs);
>  void __kprobes simulate_blx2bx(struct kprobe *p, struct pt_regs *regs);

-- 
Tixy

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH 9/9] ARM: add uprobes support
  2013-08-01 23:45   ` David Long
@ 2013-08-29 14:54     ` Jon Medhurst (Tixy)
  -1 siblings, 0 replies; 32+ messages in thread
From: Jon Medhurst (Tixy) @ 2013-08-29 14:54 UTC (permalink / raw)
  To: David Long; +Cc: linux-arm-kernel, Rabin Vincent, linux-kernel

On Thu, 2013-08-01 at 19:45 -0400, David Long wrote:
> From: "David A. Long" <dave.long@linaro.org>
> 
> Using Rabin Vincent's ARM uprobes patches as a base, enable uprobes
> support on ARM.
> 
> Caveats:
> 
>  - Thumb is not supported

Do you know if there any plans for this to be worked on? I believe most
distros targeting modern ARM chips build user side for Thumb.

>  - XOL abort/trap handling is not implemented

What are the consequences of this, e.g. is it possible for a probe to
get stuck in an infinite loop of faulting? I hope there are no integrity
issues for the kernel itself.

I've not reviewed this patch properly because I'm not very familiar with
how the uprobes work, but I did notice a rogue change at the very
end....

> 
> Signed-off-by: David A. Long <dave.long@linaro.org>
> ---
>  arch/arm/Kconfig                   |   4 +
>  arch/arm/include/asm/ptrace.h      |   6 +
>  arch/arm/include/asm/thread_info.h |   5 +-
>  arch/arm/include/asm/uprobes.h     |  34 ++++++
>  arch/arm/kernel/Makefile           |   1 +
>  arch/arm/kernel/signal.c           |   4 +
>  arch/arm/kernel/uprobes-arm.c      | 221 +++++++++++++++++++++++++++++++++++++
>  arch/arm/kernel/uprobes.c          | 203 ++++++++++++++++++++++++++++++++++
>  arch/arm/kernel/uprobes.h          |  25 +++++
>  arch/powerpc/include/asm/uprobes.h |   1 -
>  include/linux/uprobes.h            |   3 +
>  kernel/events/uprobes.c            |   1 +
>  12 files changed, 506 insertions(+), 2 deletions(-)
>  create mode 100644 arch/arm/include/asm/uprobes.h
>  create mode 100644 arch/arm/kernel/uprobes-arm.c
>  create mode 100644 arch/arm/kernel/uprobes.c
>  create mode 100644 arch/arm/kernel/uprobes.h
> 
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index 37c0f4e..06efe09 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -197,6 +197,10 @@ config ZONE_DMA
>  config NEED_DMA_MAP_STATE
>         def_bool y
>  
> +config ARCH_SUPPORTS_UPROBES
> +	depends on KPROBES
> +	def_bool y
> +

Why do we need kprobes for uprobes?

>  config ARCH_HAS_DMA_SET_COHERENT_MASK
>  	bool
>  
> diff --git a/arch/arm/include/asm/ptrace.h b/arch/arm/include/asm/ptrace.h
> index 04c99f3..ee688b0a 100644
> --- a/arch/arm/include/asm/ptrace.h
> +++ b/arch/arm/include/asm/ptrace.h
> @@ -80,6 +80,12 @@ static inline long regs_return_value(struct pt_regs *regs)
>  
>  #define instruction_pointer(regs)	(regs)->ARM_pc
>  
> +static inline void instruction_pointer_set(struct pt_regs *regs,
> +					   unsigned long val)
> +{
> +	instruction_pointer(regs) = val;
> +}
> +
>  #ifdef CONFIG_SMP
>  extern unsigned long profile_pc(struct pt_regs *regs);
>  #else
> diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h
> index 214d415..c219f8d 100644
> --- a/arch/arm/include/asm/thread_info.h
> +++ b/arch/arm/include/asm/thread_info.h
> @@ -148,6 +148,7 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *,
>  #define TIF_SIGPENDING		0
>  #define TIF_NEED_RESCHED	1
>  #define TIF_NOTIFY_RESUME	2	/* callback before returning to user */
> +#define TIF_UPROBE		7
>  #define TIF_SYSCALL_TRACE	8
>  #define TIF_SYSCALL_AUDIT	9
>  #define TIF_SYSCALL_TRACEPOINT	10
> @@ -161,6 +162,7 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *,
>  #define _TIF_SIGPENDING		(1 << TIF_SIGPENDING)
>  #define _TIF_NEED_RESCHED	(1 << TIF_NEED_RESCHED)
>  #define _TIF_NOTIFY_RESUME	(1 << TIF_NOTIFY_RESUME)
> +#define _TIF_UPROBE		(1 << TIF_UPROBE)
>  #define _TIF_SYSCALL_TRACE	(1 << TIF_SYSCALL_TRACE)
>  #define _TIF_SYSCALL_AUDIT	(1 << TIF_SYSCALL_AUDIT)
>  #define _TIF_SYSCALL_TRACEPOINT	(1 << TIF_SYSCALL_TRACEPOINT)
> @@ -174,7 +176,8 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *,
>  /*
>   * Change these and you break ASM code in entry-common.S
>   */
> -#define _TIF_WORK_MASK		(_TIF_NEED_RESCHED | _TIF_SIGPENDING | _TIF_NOTIFY_RESUME)
> +#define _TIF_WORK_MASK		(_TIF_NEED_RESCHED | _TIF_SIGPENDING | \
> +				 _TIF_NOTIFY_RESUME | _TIF_UPROBE)
>  
>  #endif /* __KERNEL__ */
>  #endif /* __ASM_ARM_THREAD_INFO_H */
> diff --git a/arch/arm/include/asm/uprobes.h b/arch/arm/include/asm/uprobes.h
> new file mode 100644
> index 0000000..fa4b81e
> --- /dev/null
> +++ b/arch/arm/include/asm/uprobes.h
> @@ -0,0 +1,34 @@
> +#ifndef _ASM_UPROBES_H
> +#define _ASM_UPROBES_H
> +
> +#include <asm/probes.h>
> +
> +typedef u32 uprobe_opcode_t;
> +
> +#define MAX_UINSN_BYTES		4
> +#define UPROBE_XOL_SLOT_BYTES	64
> +
> +#define UPROBE_SWBP_INSN	0x07f001f9
> +#define UPROBE_SS_INSN		0x07f001fa
> +#define UPROBE_SWBP_INSN_SIZE	4
> +
> +struct arch_uprobe_task {
> +	u32 backup;
> +};
> +
> +struct arch_uprobe {
> +	u8 insn[MAX_UINSN_BYTES];
> +	uprobe_opcode_t modinsn;
> +	uprobe_opcode_t bpinsn;
> +	bool simulate;
> +	u32 pcreg;
> +	void (*prehandler)(struct arch_uprobe *auprobe,
> +			   struct arch_uprobe_task *autask,
> +			   struct pt_regs *regs);
> +	void (*posthandler)(struct arch_uprobe *auprobe,
> +			    struct arch_uprobe_task *autask,
> +			    struct pt_regs *regs);
> +	struct arch_specific_insn asi;
> +};
> +
> +#endif
> diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
> index 3292023..853f6f02 100644
> --- a/arch/arm/kernel/Makefile
> +++ b/arch/arm/kernel/Makefile
> @@ -49,6 +49,7 @@ obj-$(CONFIG_DYNAMIC_FTRACE)	+= ftrace.o insn.o
>  obj-$(CONFIG_FUNCTION_GRAPH_TRACER)	+= ftrace.o insn.o
>  obj-$(CONFIG_JUMP_LABEL)	+= jump_label.o insn.o patch.o
>  obj-$(CONFIG_KEXEC)		+= machine_kexec.o relocate_kernel.o
> +obj-$(CONFIG_UPROBES)		+= probes.o uprobes.o uprobes-arm.o kprobes-arm.o
>  obj-$(CONFIG_KPROBES)		+= probes.o probes-arm.o kprobes.o kprobes-common.o patch.o
>  ifdef CONFIG_THUMB2_KERNEL
>  obj-$(CONFIG_KPROBES)		+= kprobes-thumb.o
> diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c
> index 1c16c35..20e4e61 100644
> --- a/arch/arm/kernel/signal.c
> +++ b/arch/arm/kernel/signal.c
> @@ -12,6 +12,7 @@
>  #include <linux/personality.h>
>  #include <linux/uaccess.h>
>  #include <linux/tracehook.h>
> +#include <linux/uprobes.h>
>  
>  #include <asm/elf.h>
>  #include <asm/cacheflush.h>
> @@ -598,6 +599,9 @@ do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall)
>  					return restart;
>  				}
>  				syscall = 0;
> +			} else if (thread_flags & _TIF_UPROBE) {
> +				clear_thread_flag(TIF_UPROBE);
> +				uprobe_notify_resume(regs);
>  			} else {
>  				clear_thread_flag(TIF_NOTIFY_RESUME);
>  				tracehook_notify_resume(regs);
> diff --git a/arch/arm/kernel/uprobes-arm.c b/arch/arm/kernel/uprobes-arm.c
> new file mode 100644
> index 0000000..dc298ec
> --- /dev/null
> +++ b/arch/arm/kernel/uprobes-arm.c
> @@ -0,0 +1,221 @@
> +#include <linux/kernel.h>
> +#include <linux/kprobes.h>
> +#include <linux/uprobes.h>
> +#include <linux/module.h>
> +
> +#include "kprobes.h"
> +#include "probes.h"
> +#include "probes-arm.h"
> +#include "uprobes.h"
> +
> +static int uprobes_substitute_pc(kprobe_opcode_t *pinsn, u32 oregs)
> +{
> +	kprobe_opcode_t insn = *pinsn;
> +	kprobe_opcode_t temp;
> +	kprobe_opcode_t mask;
> +	int freereg;
> +	u32 free = 0xffff;
> +	u32 regs;
> +
> +	for (regs = oregs; regs; regs >>= 4, insn >>= 4) {
> +		if ((regs & 0xf) == REG_TYPE_NONE)
> +			continue;
> +
> +		free &= ~(1 << (insn & 0xf));
> +	}
> +
> +	/* No PC, no problem */
> +	if (free & (1 << 15))
> +		return 15;
> +
> +	if (!free)
> +		return -1;
> +
> +	/*
> +	 * fls instead of ffs ensures that for "ldrd r0, r1, [pc]" we would
> +	 * pick LR instead of R1.
> +	 */
> +	freereg = free = fls(free) - 1;
> +
> +	temp = *pinsn;
> +	insn = *pinsn;
> +	regs = oregs;
> +	mask = 0xf;
> +
> +	for (; regs; regs >>= 4, mask <<= 4, free <<= 4, temp >>= 4) {
> +		if ((regs & 0xf) == REG_TYPE_NONE)
> +			continue;
> +
> +		if ((temp & 0xf) != 15)
> +			continue;
> +
> +		insn &= ~mask;
> +		insn |= free & mask;
> +	}
> +
> +	*pinsn = insn;
> +	return freereg;
> +}
> +
> +static void uprobe_set_pc(struct arch_uprobe *auprobe,
> +			  struct arch_uprobe_task *autask,
> +			  struct pt_regs *regs)
> +{
> +	u32 pcreg = auprobe->pcreg;
> +
> +	autask->backup = regs->uregs[pcreg];
> +	regs->uregs[pcreg] = regs->ARM_pc + 8;
> +}
> +
> +static void uprobe_unset_pc(struct arch_uprobe *auprobe,
> +			    struct arch_uprobe_task *autask,
> +			    struct pt_regs *regs)
> +{
> +	/* PC will be taken care of by common code */
> +	regs->uregs[auprobe->pcreg] = autask->backup;
> +}
> +
> +static void uprobe_aluwrite_pc(struct arch_uprobe *auprobe,
> +			       struct arch_uprobe_task *autask,
> +			       struct pt_regs *regs)
> +{
> +	u32 pcreg = auprobe->pcreg;
> +
> +	alu_write_pc(regs->uregs[pcreg], regs);
> +	regs->uregs[pcreg] = autask->backup;
> +}
> +
> +static void uprobe_write_pc(struct arch_uprobe *auprobe,
> +			    struct arch_uprobe_task *autask,
> +			    struct pt_regs *regs)
> +{
> +	u32 pcreg = auprobe->pcreg;
> +
> +	load_write_pc(regs->uregs[pcreg], regs);
> +	regs->uregs[pcreg] = autask->backup;
> +}
> +
> +enum kprobe_insn
> +decode_pc_ro(kprobe_opcode_t insn, struct arch_specific_insn *asi, void *d)
> +{
> +	struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe,
> +						   asi);
> +	struct decode_emulate *decode = d;
> +	u32 regs = decode->header.type_regs.bits >> DECODE_TYPE_BITS;
> +	int reg;
> +
> +	reg = uprobes_substitute_pc(&auprobe->modinsn, regs);
> +	if (reg == 15)
> +		return INSN_GOOD;
> +
> +	if (reg == -1)
> +		return INSN_REJECTED;
> +
> +	auprobe->pcreg = reg;
> +	auprobe->prehandler = uprobe_set_pc;
> +	auprobe->posthandler = uprobe_unset_pc;
> +
> +	return INSN_GOOD;
> +}
> +
> +enum kprobe_insn
> +decode_wb_pc(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> +	     void *d, bool alu)
> +{
> +	struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe,
> +						   asi);
> +	enum kprobe_insn ret = decode_pc_ro(insn, asi, d);
> +
> +	if (((insn >> 12) & 0xf) == 15)
> +		auprobe->posthandler = alu ? uprobe_aluwrite_pc
> +					   : uprobe_write_pc;
> +
> +	return ret;
> +}
> +
> +enum kprobe_insn
> +decode_rd12rn16rm0rs8_rwflags(kprobe_opcode_t insn,
> +			      struct arch_specific_insn *asi, void *d)
> +{
> +	return decode_wb_pc(insn, asi, d, true);
> +}
> +
> +enum kprobe_insn
> +decode_ldr(kprobe_opcode_t insn, struct arch_specific_insn *asi, void *d)
> +{
> +	return decode_wb_pc(insn, asi, d, false);
> +}
> +
> +enum kprobe_insn
> +uprobe_decode_ldmstm(kprobe_opcode_t insn,
> +		     struct arch_specific_insn *asi, void *d)
> +{
> +	struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe,
> +						   asi);
> +	unsigned reglist = insn & 0xffff;
> +	int rn = (insn >> 16) & 0xf;
> +	int lbit = insn & (1 << 20);
> +	unsigned used = reglist | (1 << rn);
> +
> +	if (rn == 15)
> +		return INSN_REJECTED;
> +
> +	if (!(used & (1 << 15)))
> +		return INSN_GOOD;
> +
> +	if (used & (1 << 14))
> +		return INSN_REJECTED;
> +
> +	/* Use LR instead of PC */
> +	insn ^= 0xc000;
> +
> +	auprobe->pcreg = 14;
> +	auprobe->modinsn = insn;
> +
> +	auprobe->prehandler = uprobe_set_pc;
> +	if (lbit)
> +		auprobe->posthandler = uprobe_write_pc;
> +	else
> +		auprobe->posthandler = uprobe_unset_pc;
> +
> +	return INSN_GOOD;
> +}
> +
> +const union decode_item uprobes_probes_actions[] = {
> +	[PROBES_EMULATE_NONE] {.handler = probes_simulate_nop},
> +	[PROBES_SIMULATE_NOP] = {.handler = probes_simulate_nop},
> +	[PROBES_PRELOAD_IMM] = {.handler = probes_simulate_nop},
> +	[PROBES_PRELOAD_REG] = {.handler = probes_simulate_nop},
> +	[PROBES_BRANCH_IMM] = {.handler = simulate_blx1},
> +	[PROBES_MRS] = {.handler = simulate_mrs},
> +	[PROBES_BRANCH_REG] = {.handler = simulate_blx2bx},
> +	[PROBES_CLZ] = {.handler = probes_simulate_nop},
> +	[PROBES_SATURATING_ARITHMETIC] = {.handler = probes_simulate_nop},
> +	[PROBES_MUL1] = {.handler = probes_simulate_nop},
> +	[PROBES_MUL2] = {.handler = probes_simulate_nop},
> +	[PROBES_SWP] = {.handler = probes_simulate_nop},
> +	[PROBES_LDRSTRD] = {.decoder = decode_pc_ro},
> +	[PROBES_LOAD_EXTRA] = {.decoder = decode_pc_ro},
> +	[PROBES_LOAD] = {.decoder = decode_ldr},
> +	[PROBES_STORE_EXTRA] = {.decoder = decode_pc_ro},
> +	[PROBES_STORE] = {.decoder = decode_pc_ro},
> +	[PROBES_MOV_IP_SP] = {.handler = simulate_mov_ipsp},
> +	[PROBES_DATA_PROCESSING_REG] = {
> +		.decoder = decode_rd12rn16rm0rs8_rwflags},
> +	[PROBES_DATA_PROCESSING_IMM] = {
> +		.decoder = decode_rd12rn16rm0rs8_rwflags},
> +	[PROBES_MOV_HALFWORD] = {.handler = probes_simulate_nop},
> +	[PROBES_SEV] = {.handler = probes_simulate_nop},
> +	[PROBES_WFE] = {.handler = probes_simulate_nop},
> +	[PROBES_SATURATE] = {.handler = probes_simulate_nop},
> +	[PROBES_REV] = {.handler = probes_simulate_nop},
> +	[PROBES_MMI] = {.handler = probes_simulate_nop},
> +	[PROBES_PACK] = {.handler = probes_simulate_nop},
> +	[PROBES_EXTEND] = {.handler = probes_simulate_nop},
> +	[PROBES_EXTEND_ADD] = {.handler = probes_simulate_nop},
> +	[PROBES_MUL_ADD_LONG] = {.handler = probes_simulate_nop},
> +	[PROBES_MUL_ADD] = {.handler = probes_simulate_nop},
> +	[PROBES_BITFIELD] = {.handler = probes_simulate_nop},
> +	[PROBES_BRANCH] = {.handler = simulate_bbl},
> +	[PROBES_LDMSTM] = {.decoder = uprobe_decode_ldmstm}
> +};
> diff --git a/arch/arm/kernel/uprobes.c b/arch/arm/kernel/uprobes.c
> new file mode 100644
> index 0000000..90a32d2
> --- /dev/null
> +++ b/arch/arm/kernel/uprobes.c
> @@ -0,0 +1,203 @@
> +/*
> + * Copyright (C) 2012 Rabin Vincent <rabin at rab.in>
> + *
> + * 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.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/sched.h>
> +#include <linux/uprobes.h>
> +#include <linux/notifier.h>
> +#include <linux/kprobes.h>
> +
> +#include <asm/opcodes.h>
> +#include <asm/traps.h>
> +
> +#include "kprobes.h"
> +#include "probes.h"
> +#include "probes-arm.h"
> +#include "uprobes.h"
> +
> +bool is_swbp_insn(uprobe_opcode_t *insn)
> +{
> +	return (__mem_to_opcode_arm(*insn) & 0x0fffffff) == UPROBE_SWBP_INSN;
> +}
> +
> +bool arch_uprobe_ignore(struct arch_uprobe *auprobe, struct pt_regs *regs)
> +{
> +	if (!auprobe->asi.insn_check_cc(regs->ARM_cpsr)) {
> +		regs->ARM_pc += 4;
> +		return true;
> +	}
> +
> +	return false;
> +}
> +
> +bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
> +{
> +	struct kprobe kp;
> +
> +	if (!auprobe->simulate)
> +		return false;
> +
> +	kp.addr = (void *) regs->ARM_pc;
> +	kp.opcode = __mem_to_opcode_arm(*(unsigned int *) auprobe->insn);
> +	kp.ainsn.insn_handler = auprobe->asi.insn_handler;
> +
> +	auprobe->asi.insn_singlestep(&kp, regs);
> +
> +	return true;
> +}
> +
> +unsigned long
> +arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr,
> +				  struct pt_regs *regs)
> +{
> +	regs->ARM_pc = trampoline_vaddr;
> +	return trampoline_vaddr;
> +}
> +
> +int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm,
> +			     unsigned long addr)
> +{
> +	unsigned int insn;
> +	unsigned int bpinsn;
> +	enum kprobe_insn ret;
> +
> +	/* Thumb not yet support */
> +	if (addr & 0x3)
> +		return -EINVAL;
> +
> +	insn = __mem_to_opcode_arm(*(unsigned int *)auprobe->insn);
> +	auprobe->modinsn = insn;
> +
> +	ret = arm_kprobe_decode_insn(insn, &auprobe->asi, true,
> +				     uprobes_probes_actions);
> +	switch (ret) {
> +	case INSN_REJECTED:
> +		return -EINVAL;
> +
> +	case INSN_GOOD_NO_SLOT:
> +		auprobe->simulate = true;
> +		break;
> +
> +	case INSN_GOOD:
> +	default:
> +		break;
> +	}
> +
> +	bpinsn = UPROBE_SWBP_INSN;
> +	if (insn >= 0xe0000000)
> +		bpinsn |= 0xe0000000;  /* Unconditional instruction */
> +	else
> +		bpinsn |= insn & 0xf0000000;  /* Copy condition from insn */
> +
> +	auprobe->bpinsn = bpinsn;
> +
> +	return 0;
> +}
> +
> +void arch_uprobe_write_opcode(struct arch_uprobe *auprobe, void *vaddr,
> +			      uprobe_opcode_t opcode)
> +{
> +	unsigned long *addr = vaddr;
> +
> +	if (opcode == UPROBE_SWBP_INSN)
> +		opcode = __opcode_to_mem_arm(auprobe->bpinsn);
> +
> +	*addr = opcode;
> +}
> +
> +void arch_uprobe_xol_copy(struct arch_uprobe *auprobe, void *vaddr)
> +{
> +	unsigned long *addr = vaddr;
> +
> +	addr[0] = __opcode_to_mem_arm(auprobe->modinsn);
> +	addr[1] = __opcode_to_mem_arm(0xe0000000 | UPROBE_SS_INSN);
> +}
> +
> +int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
> +{
> +	struct uprobe_task *utask = current->utask;
> +
> +	if (auprobe->prehandler)
> +		auprobe->prehandler(auprobe, &utask->autask, regs);
> +
> +	regs->ARM_pc = utask->xol_vaddr;
> +
> +	return 0;
> +}
> +
> +int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
> +{
> +	struct uprobe_task *utask = current->utask;
> +
> +	regs->ARM_pc = utask->vaddr + 4;
> +
> +	if (auprobe->posthandler)
> +		auprobe->posthandler(auprobe, &utask->autask, regs);
> +
> +	return 0;
> +}
> +
> +bool arch_uprobe_xol_was_trapped(struct task_struct *t)
> +{
> +	/* TODO: implement */
> +	return false;
> +}
> +
> +void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
> +{
> +	/* TODO: implement */
> +}
> +
> +int arch_uprobe_exception_notify(struct notifier_block *self,
> +				 unsigned long val, void *data)
> +{
> +	return NOTIFY_DONE;
> +}
> +
> +static int uprobe_trap_handler(struct pt_regs *regs, unsigned int instr)
> +{
> +	unsigned long flags;
> +
> +	local_irq_save(flags);
> +	if ((instr & 0x0fffffff) == UPROBE_SWBP_INSN)
> +		uprobe_pre_sstep_notifier(regs);
> +	else
> +		uprobe_post_sstep_notifier(regs);
> +	local_irq_restore(flags);
> +
> +	return 0;
> +}
> +
> +unsigned long uprobe_get_swbp_addr(struct pt_regs *regs)
> +{
> +	return instruction_pointer(regs);
> +}
> +
> +static struct undef_hook uprobes_arm_break_hook = {
> +	.instr_mask	= 0x0fffffff,
> +	.instr_val	= UPROBE_SWBP_INSN,
> +	.cpsr_mask	= MODE_MASK,
> +	.cpsr_val	= USR_MODE,
> +	.fn		= uprobe_trap_handler,
> +};
> +
> +static struct undef_hook uprobes_arm_ss_hook = {
> +	.instr_mask	= 0x0fffffff,
> +	.instr_val	= UPROBE_SS_INSN,
> +	.cpsr_mask	= MODE_MASK,
> +	.cpsr_val	= USR_MODE,
> +	.fn		= uprobe_trap_handler,
> +};
> +
> +int arch_uprobes_init(void)
> +{
> +	register_undef_hook(&uprobes_arm_break_hook);
> +	register_undef_hook(&uprobes_arm_ss_hook);
> +
> +	return 0;
> +}
> diff --git a/arch/arm/kernel/uprobes.h b/arch/arm/kernel/uprobes.h
> new file mode 100644
> index 0000000..3887c19
> --- /dev/null
> +++ b/arch/arm/kernel/uprobes.h
> @@ -0,0 +1,25 @@
> +#ifndef __ARM_KERNEL_UPROBES_H
> +#define __ARM_KERNEL_UPROBES_H
> +
> +enum kprobe_insn uprobe_decode_ldmstm(kprobe_opcode_t insn,
> +				      struct arch_specific_insn *asi,
> +				      void *d);
> +
> +enum kprobe_insn decode_ldr(kprobe_opcode_t insn,
> +			    struct arch_specific_insn *asi,
> +			    void *d);
> +
> +enum kprobe_insn
> +decode_rd12rn16rm0rs8_rwflags(kprobe_opcode_t insn,
> +			      struct arch_specific_insn *asi, void *d);
> +
> +enum kprobe_insn
> +decode_wb_pc(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> +	     void *d, bool alu);
> +
> +enum kprobe_insn
> +decode_pc_ro(kprobe_opcode_t insn, struct arch_specific_insn *asi, void *d);
> +
> +extern const union decode_item uprobes_probes_actions[];
> +
> +#endif
> diff --git a/arch/powerpc/include/asm/uprobes.h b/arch/powerpc/include/asm/uprobes.h
> index 2301602..b532060 100644
> --- a/arch/powerpc/include/asm/uprobes.h
> +++ b/arch/powerpc/include/asm/uprobes.h
> @@ -51,5 +51,4 @@ extern int  arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs);
>  extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk);
>  extern int  arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data);
>  extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs);
> -extern unsigned long arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr, struct pt_regs *regs);
>  #endif	/* _ASM_UPROBES_H */
> diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h
> index 9cd3b25..41341e9 100644
> --- a/include/linux/uprobes.h
> +++ b/include/linux/uprobes.h
> @@ -139,6 +139,9 @@ extern int __weak arch_uprobes_init(void);
>  extern void __weak arch_uprobe_write_opcode(struct arch_uprobe *auprobe,
>  					    void *vaddr,
>  					    uprobe_opcode_t opcode);
> +extern
> +unsigned long arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr,
> +						       struct pt_regs *regs);
>  #else /* !CONFIG_UPROBES */
>  struct uprobes_state {
>  };
> diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
> index 6a60eec..3e9d596 100644
> --- a/kernel/events/uprobes.c
> +++ b/kernel/events/uprobes.c
> @@ -271,6 +271,7 @@ static int write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm,
>  {
>  	struct page *old_page, *new_page;
>  	struct vm_area_struct *vma;
> +	void *vaddr_new;

This looks like it should be in patch 5 where vaddr_new is used but not
defined ;-)

>  	int ret;
>  
>  retry:






^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH 9/9] ARM: add uprobes support
@ 2013-08-29 14:54     ` Jon Medhurst (Tixy)
  0 siblings, 0 replies; 32+ messages in thread
From: Jon Medhurst (Tixy) @ 2013-08-29 14:54 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, 2013-08-01 at 19:45 -0400, David Long wrote:
> From: "David A. Long" <dave.long@linaro.org>
> 
> Using Rabin Vincent's ARM uprobes patches as a base, enable uprobes
> support on ARM.
> 
> Caveats:
> 
>  - Thumb is not supported

Do you know if there any plans for this to be worked on? I believe most
distros targeting modern ARM chips build user side for Thumb.

>  - XOL abort/trap handling is not implemented

What are the consequences of this, e.g. is it possible for a probe to
get stuck in an infinite loop of faulting? I hope there are no integrity
issues for the kernel itself.

I've not reviewed this patch properly because I'm not very familiar with
how the uprobes work, but I did notice a rogue change at the very
end....

> 
> Signed-off-by: David A. Long <dave.long@linaro.org>
> ---
>  arch/arm/Kconfig                   |   4 +
>  arch/arm/include/asm/ptrace.h      |   6 +
>  arch/arm/include/asm/thread_info.h |   5 +-
>  arch/arm/include/asm/uprobes.h     |  34 ++++++
>  arch/arm/kernel/Makefile           |   1 +
>  arch/arm/kernel/signal.c           |   4 +
>  arch/arm/kernel/uprobes-arm.c      | 221 +++++++++++++++++++++++++++++++++++++
>  arch/arm/kernel/uprobes.c          | 203 ++++++++++++++++++++++++++++++++++
>  arch/arm/kernel/uprobes.h          |  25 +++++
>  arch/powerpc/include/asm/uprobes.h |   1 -
>  include/linux/uprobes.h            |   3 +
>  kernel/events/uprobes.c            |   1 +
>  12 files changed, 506 insertions(+), 2 deletions(-)
>  create mode 100644 arch/arm/include/asm/uprobes.h
>  create mode 100644 arch/arm/kernel/uprobes-arm.c
>  create mode 100644 arch/arm/kernel/uprobes.c
>  create mode 100644 arch/arm/kernel/uprobes.h
> 
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index 37c0f4e..06efe09 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -197,6 +197,10 @@ config ZONE_DMA
>  config NEED_DMA_MAP_STATE
>         def_bool y
>  
> +config ARCH_SUPPORTS_UPROBES
> +	depends on KPROBES
> +	def_bool y
> +

Why do we need kprobes for uprobes?

>  config ARCH_HAS_DMA_SET_COHERENT_MASK
>  	bool
>  
> diff --git a/arch/arm/include/asm/ptrace.h b/arch/arm/include/asm/ptrace.h
> index 04c99f3..ee688b0a 100644
> --- a/arch/arm/include/asm/ptrace.h
> +++ b/arch/arm/include/asm/ptrace.h
> @@ -80,6 +80,12 @@ static inline long regs_return_value(struct pt_regs *regs)
>  
>  #define instruction_pointer(regs)	(regs)->ARM_pc
>  
> +static inline void instruction_pointer_set(struct pt_regs *regs,
> +					   unsigned long val)
> +{
> +	instruction_pointer(regs) = val;
> +}
> +
>  #ifdef CONFIG_SMP
>  extern unsigned long profile_pc(struct pt_regs *regs);
>  #else
> diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h
> index 214d415..c219f8d 100644
> --- a/arch/arm/include/asm/thread_info.h
> +++ b/arch/arm/include/asm/thread_info.h
> @@ -148,6 +148,7 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *,
>  #define TIF_SIGPENDING		0
>  #define TIF_NEED_RESCHED	1
>  #define TIF_NOTIFY_RESUME	2	/* callback before returning to user */
> +#define TIF_UPROBE		7
>  #define TIF_SYSCALL_TRACE	8
>  #define TIF_SYSCALL_AUDIT	9
>  #define TIF_SYSCALL_TRACEPOINT	10
> @@ -161,6 +162,7 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *,
>  #define _TIF_SIGPENDING		(1 << TIF_SIGPENDING)
>  #define _TIF_NEED_RESCHED	(1 << TIF_NEED_RESCHED)
>  #define _TIF_NOTIFY_RESUME	(1 << TIF_NOTIFY_RESUME)
> +#define _TIF_UPROBE		(1 << TIF_UPROBE)
>  #define _TIF_SYSCALL_TRACE	(1 << TIF_SYSCALL_TRACE)
>  #define _TIF_SYSCALL_AUDIT	(1 << TIF_SYSCALL_AUDIT)
>  #define _TIF_SYSCALL_TRACEPOINT	(1 << TIF_SYSCALL_TRACEPOINT)
> @@ -174,7 +176,8 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *,
>  /*
>   * Change these and you break ASM code in entry-common.S
>   */
> -#define _TIF_WORK_MASK		(_TIF_NEED_RESCHED | _TIF_SIGPENDING | _TIF_NOTIFY_RESUME)
> +#define _TIF_WORK_MASK		(_TIF_NEED_RESCHED | _TIF_SIGPENDING | \
> +				 _TIF_NOTIFY_RESUME | _TIF_UPROBE)
>  
>  #endif /* __KERNEL__ */
>  #endif /* __ASM_ARM_THREAD_INFO_H */
> diff --git a/arch/arm/include/asm/uprobes.h b/arch/arm/include/asm/uprobes.h
> new file mode 100644
> index 0000000..fa4b81e
> --- /dev/null
> +++ b/arch/arm/include/asm/uprobes.h
> @@ -0,0 +1,34 @@
> +#ifndef _ASM_UPROBES_H
> +#define _ASM_UPROBES_H
> +
> +#include <asm/probes.h>
> +
> +typedef u32 uprobe_opcode_t;
> +
> +#define MAX_UINSN_BYTES		4
> +#define UPROBE_XOL_SLOT_BYTES	64
> +
> +#define UPROBE_SWBP_INSN	0x07f001f9
> +#define UPROBE_SS_INSN		0x07f001fa
> +#define UPROBE_SWBP_INSN_SIZE	4
> +
> +struct arch_uprobe_task {
> +	u32 backup;
> +};
> +
> +struct arch_uprobe {
> +	u8 insn[MAX_UINSN_BYTES];
> +	uprobe_opcode_t modinsn;
> +	uprobe_opcode_t bpinsn;
> +	bool simulate;
> +	u32 pcreg;
> +	void (*prehandler)(struct arch_uprobe *auprobe,
> +			   struct arch_uprobe_task *autask,
> +			   struct pt_regs *regs);
> +	void (*posthandler)(struct arch_uprobe *auprobe,
> +			    struct arch_uprobe_task *autask,
> +			    struct pt_regs *regs);
> +	struct arch_specific_insn asi;
> +};
> +
> +#endif
> diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
> index 3292023..853f6f02 100644
> --- a/arch/arm/kernel/Makefile
> +++ b/arch/arm/kernel/Makefile
> @@ -49,6 +49,7 @@ obj-$(CONFIG_DYNAMIC_FTRACE)	+= ftrace.o insn.o
>  obj-$(CONFIG_FUNCTION_GRAPH_TRACER)	+= ftrace.o insn.o
>  obj-$(CONFIG_JUMP_LABEL)	+= jump_label.o insn.o patch.o
>  obj-$(CONFIG_KEXEC)		+= machine_kexec.o relocate_kernel.o
> +obj-$(CONFIG_UPROBES)		+= probes.o uprobes.o uprobes-arm.o kprobes-arm.o
>  obj-$(CONFIG_KPROBES)		+= probes.o probes-arm.o kprobes.o kprobes-common.o patch.o
>  ifdef CONFIG_THUMB2_KERNEL
>  obj-$(CONFIG_KPROBES)		+= kprobes-thumb.o
> diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c
> index 1c16c35..20e4e61 100644
> --- a/arch/arm/kernel/signal.c
> +++ b/arch/arm/kernel/signal.c
> @@ -12,6 +12,7 @@
>  #include <linux/personality.h>
>  #include <linux/uaccess.h>
>  #include <linux/tracehook.h>
> +#include <linux/uprobes.h>
>  
>  #include <asm/elf.h>
>  #include <asm/cacheflush.h>
> @@ -598,6 +599,9 @@ do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall)
>  					return restart;
>  				}
>  				syscall = 0;
> +			} else if (thread_flags & _TIF_UPROBE) {
> +				clear_thread_flag(TIF_UPROBE);
> +				uprobe_notify_resume(regs);
>  			} else {
>  				clear_thread_flag(TIF_NOTIFY_RESUME);
>  				tracehook_notify_resume(regs);
> diff --git a/arch/arm/kernel/uprobes-arm.c b/arch/arm/kernel/uprobes-arm.c
> new file mode 100644
> index 0000000..dc298ec
> --- /dev/null
> +++ b/arch/arm/kernel/uprobes-arm.c
> @@ -0,0 +1,221 @@
> +#include <linux/kernel.h>
> +#include <linux/kprobes.h>
> +#include <linux/uprobes.h>
> +#include <linux/module.h>
> +
> +#include "kprobes.h"
> +#include "probes.h"
> +#include "probes-arm.h"
> +#include "uprobes.h"
> +
> +static int uprobes_substitute_pc(kprobe_opcode_t *pinsn, u32 oregs)
> +{
> +	kprobe_opcode_t insn = *pinsn;
> +	kprobe_opcode_t temp;
> +	kprobe_opcode_t mask;
> +	int freereg;
> +	u32 free = 0xffff;
> +	u32 regs;
> +
> +	for (regs = oregs; regs; regs >>= 4, insn >>= 4) {
> +		if ((regs & 0xf) == REG_TYPE_NONE)
> +			continue;
> +
> +		free &= ~(1 << (insn & 0xf));
> +	}
> +
> +	/* No PC, no problem */
> +	if (free & (1 << 15))
> +		return 15;
> +
> +	if (!free)
> +		return -1;
> +
> +	/*
> +	 * fls instead of ffs ensures that for "ldrd r0, r1, [pc]" we would
> +	 * pick LR instead of R1.
> +	 */
> +	freereg = free = fls(free) - 1;
> +
> +	temp = *pinsn;
> +	insn = *pinsn;
> +	regs = oregs;
> +	mask = 0xf;
> +
> +	for (; regs; regs >>= 4, mask <<= 4, free <<= 4, temp >>= 4) {
> +		if ((regs & 0xf) == REG_TYPE_NONE)
> +			continue;
> +
> +		if ((temp & 0xf) != 15)
> +			continue;
> +
> +		insn &= ~mask;
> +		insn |= free & mask;
> +	}
> +
> +	*pinsn = insn;
> +	return freereg;
> +}
> +
> +static void uprobe_set_pc(struct arch_uprobe *auprobe,
> +			  struct arch_uprobe_task *autask,
> +			  struct pt_regs *regs)
> +{
> +	u32 pcreg = auprobe->pcreg;
> +
> +	autask->backup = regs->uregs[pcreg];
> +	regs->uregs[pcreg] = regs->ARM_pc + 8;
> +}
> +
> +static void uprobe_unset_pc(struct arch_uprobe *auprobe,
> +			    struct arch_uprobe_task *autask,
> +			    struct pt_regs *regs)
> +{
> +	/* PC will be taken care of by common code */
> +	regs->uregs[auprobe->pcreg] = autask->backup;
> +}
> +
> +static void uprobe_aluwrite_pc(struct arch_uprobe *auprobe,
> +			       struct arch_uprobe_task *autask,
> +			       struct pt_regs *regs)
> +{
> +	u32 pcreg = auprobe->pcreg;
> +
> +	alu_write_pc(regs->uregs[pcreg], regs);
> +	regs->uregs[pcreg] = autask->backup;
> +}
> +
> +static void uprobe_write_pc(struct arch_uprobe *auprobe,
> +			    struct arch_uprobe_task *autask,
> +			    struct pt_regs *regs)
> +{
> +	u32 pcreg = auprobe->pcreg;
> +
> +	load_write_pc(regs->uregs[pcreg], regs);
> +	regs->uregs[pcreg] = autask->backup;
> +}
> +
> +enum kprobe_insn
> +decode_pc_ro(kprobe_opcode_t insn, struct arch_specific_insn *asi, void *d)
> +{
> +	struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe,
> +						   asi);
> +	struct decode_emulate *decode = d;
> +	u32 regs = decode->header.type_regs.bits >> DECODE_TYPE_BITS;
> +	int reg;
> +
> +	reg = uprobes_substitute_pc(&auprobe->modinsn, regs);
> +	if (reg == 15)
> +		return INSN_GOOD;
> +
> +	if (reg == -1)
> +		return INSN_REJECTED;
> +
> +	auprobe->pcreg = reg;
> +	auprobe->prehandler = uprobe_set_pc;
> +	auprobe->posthandler = uprobe_unset_pc;
> +
> +	return INSN_GOOD;
> +}
> +
> +enum kprobe_insn
> +decode_wb_pc(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> +	     void *d, bool alu)
> +{
> +	struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe,
> +						   asi);
> +	enum kprobe_insn ret = decode_pc_ro(insn, asi, d);
> +
> +	if (((insn >> 12) & 0xf) == 15)
> +		auprobe->posthandler = alu ? uprobe_aluwrite_pc
> +					   : uprobe_write_pc;
> +
> +	return ret;
> +}
> +
> +enum kprobe_insn
> +decode_rd12rn16rm0rs8_rwflags(kprobe_opcode_t insn,
> +			      struct arch_specific_insn *asi, void *d)
> +{
> +	return decode_wb_pc(insn, asi, d, true);
> +}
> +
> +enum kprobe_insn
> +decode_ldr(kprobe_opcode_t insn, struct arch_specific_insn *asi, void *d)
> +{
> +	return decode_wb_pc(insn, asi, d, false);
> +}
> +
> +enum kprobe_insn
> +uprobe_decode_ldmstm(kprobe_opcode_t insn,
> +		     struct arch_specific_insn *asi, void *d)
> +{
> +	struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe,
> +						   asi);
> +	unsigned reglist = insn & 0xffff;
> +	int rn = (insn >> 16) & 0xf;
> +	int lbit = insn & (1 << 20);
> +	unsigned used = reglist | (1 << rn);
> +
> +	if (rn == 15)
> +		return INSN_REJECTED;
> +
> +	if (!(used & (1 << 15)))
> +		return INSN_GOOD;
> +
> +	if (used & (1 << 14))
> +		return INSN_REJECTED;
> +
> +	/* Use LR instead of PC */
> +	insn ^= 0xc000;
> +
> +	auprobe->pcreg = 14;
> +	auprobe->modinsn = insn;
> +
> +	auprobe->prehandler = uprobe_set_pc;
> +	if (lbit)
> +		auprobe->posthandler = uprobe_write_pc;
> +	else
> +		auprobe->posthandler = uprobe_unset_pc;
> +
> +	return INSN_GOOD;
> +}
> +
> +const union decode_item uprobes_probes_actions[] = {
> +	[PROBES_EMULATE_NONE] {.handler = probes_simulate_nop},
> +	[PROBES_SIMULATE_NOP] = {.handler = probes_simulate_nop},
> +	[PROBES_PRELOAD_IMM] = {.handler = probes_simulate_nop},
> +	[PROBES_PRELOAD_REG] = {.handler = probes_simulate_nop},
> +	[PROBES_BRANCH_IMM] = {.handler = simulate_blx1},
> +	[PROBES_MRS] = {.handler = simulate_mrs},
> +	[PROBES_BRANCH_REG] = {.handler = simulate_blx2bx},
> +	[PROBES_CLZ] = {.handler = probes_simulate_nop},
> +	[PROBES_SATURATING_ARITHMETIC] = {.handler = probes_simulate_nop},
> +	[PROBES_MUL1] = {.handler = probes_simulate_nop},
> +	[PROBES_MUL2] = {.handler = probes_simulate_nop},
> +	[PROBES_SWP] = {.handler = probes_simulate_nop},
> +	[PROBES_LDRSTRD] = {.decoder = decode_pc_ro},
> +	[PROBES_LOAD_EXTRA] = {.decoder = decode_pc_ro},
> +	[PROBES_LOAD] = {.decoder = decode_ldr},
> +	[PROBES_STORE_EXTRA] = {.decoder = decode_pc_ro},
> +	[PROBES_STORE] = {.decoder = decode_pc_ro},
> +	[PROBES_MOV_IP_SP] = {.handler = simulate_mov_ipsp},
> +	[PROBES_DATA_PROCESSING_REG] = {
> +		.decoder = decode_rd12rn16rm0rs8_rwflags},
> +	[PROBES_DATA_PROCESSING_IMM] = {
> +		.decoder = decode_rd12rn16rm0rs8_rwflags},
> +	[PROBES_MOV_HALFWORD] = {.handler = probes_simulate_nop},
> +	[PROBES_SEV] = {.handler = probes_simulate_nop},
> +	[PROBES_WFE] = {.handler = probes_simulate_nop},
> +	[PROBES_SATURATE] = {.handler = probes_simulate_nop},
> +	[PROBES_REV] = {.handler = probes_simulate_nop},
> +	[PROBES_MMI] = {.handler = probes_simulate_nop},
> +	[PROBES_PACK] = {.handler = probes_simulate_nop},
> +	[PROBES_EXTEND] = {.handler = probes_simulate_nop},
> +	[PROBES_EXTEND_ADD] = {.handler = probes_simulate_nop},
> +	[PROBES_MUL_ADD_LONG] = {.handler = probes_simulate_nop},
> +	[PROBES_MUL_ADD] = {.handler = probes_simulate_nop},
> +	[PROBES_BITFIELD] = {.handler = probes_simulate_nop},
> +	[PROBES_BRANCH] = {.handler = simulate_bbl},
> +	[PROBES_LDMSTM] = {.decoder = uprobe_decode_ldmstm}
> +};
> diff --git a/arch/arm/kernel/uprobes.c b/arch/arm/kernel/uprobes.c
> new file mode 100644
> index 0000000..90a32d2
> --- /dev/null
> +++ b/arch/arm/kernel/uprobes.c
> @@ -0,0 +1,203 @@
> +/*
> + * Copyright (C) 2012 Rabin Vincent <rabin@rab.in>
> + *
> + * 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.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/sched.h>
> +#include <linux/uprobes.h>
> +#include <linux/notifier.h>
> +#include <linux/kprobes.h>
> +
> +#include <asm/opcodes.h>
> +#include <asm/traps.h>
> +
> +#include "kprobes.h"
> +#include "probes.h"
> +#include "probes-arm.h"
> +#include "uprobes.h"
> +
> +bool is_swbp_insn(uprobe_opcode_t *insn)
> +{
> +	return (__mem_to_opcode_arm(*insn) & 0x0fffffff) == UPROBE_SWBP_INSN;
> +}
> +
> +bool arch_uprobe_ignore(struct arch_uprobe *auprobe, struct pt_regs *regs)
> +{
> +	if (!auprobe->asi.insn_check_cc(regs->ARM_cpsr)) {
> +		regs->ARM_pc += 4;
> +		return true;
> +	}
> +
> +	return false;
> +}
> +
> +bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
> +{
> +	struct kprobe kp;
> +
> +	if (!auprobe->simulate)
> +		return false;
> +
> +	kp.addr = (void *) regs->ARM_pc;
> +	kp.opcode = __mem_to_opcode_arm(*(unsigned int *) auprobe->insn);
> +	kp.ainsn.insn_handler = auprobe->asi.insn_handler;
> +
> +	auprobe->asi.insn_singlestep(&kp, regs);
> +
> +	return true;
> +}
> +
> +unsigned long
> +arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr,
> +				  struct pt_regs *regs)
> +{
> +	regs->ARM_pc = trampoline_vaddr;
> +	return trampoline_vaddr;
> +}
> +
> +int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm,
> +			     unsigned long addr)
> +{
> +	unsigned int insn;
> +	unsigned int bpinsn;
> +	enum kprobe_insn ret;
> +
> +	/* Thumb not yet support */
> +	if (addr & 0x3)
> +		return -EINVAL;
> +
> +	insn = __mem_to_opcode_arm(*(unsigned int *)auprobe->insn);
> +	auprobe->modinsn = insn;
> +
> +	ret = arm_kprobe_decode_insn(insn, &auprobe->asi, true,
> +				     uprobes_probes_actions);
> +	switch (ret) {
> +	case INSN_REJECTED:
> +		return -EINVAL;
> +
> +	case INSN_GOOD_NO_SLOT:
> +		auprobe->simulate = true;
> +		break;
> +
> +	case INSN_GOOD:
> +	default:
> +		break;
> +	}
> +
> +	bpinsn = UPROBE_SWBP_INSN;
> +	if (insn >= 0xe0000000)
> +		bpinsn |= 0xe0000000;  /* Unconditional instruction */
> +	else
> +		bpinsn |= insn & 0xf0000000;  /* Copy condition from insn */
> +
> +	auprobe->bpinsn = bpinsn;
> +
> +	return 0;
> +}
> +
> +void arch_uprobe_write_opcode(struct arch_uprobe *auprobe, void *vaddr,
> +			      uprobe_opcode_t opcode)
> +{
> +	unsigned long *addr = vaddr;
> +
> +	if (opcode == UPROBE_SWBP_INSN)
> +		opcode = __opcode_to_mem_arm(auprobe->bpinsn);
> +
> +	*addr = opcode;
> +}
> +
> +void arch_uprobe_xol_copy(struct arch_uprobe *auprobe, void *vaddr)
> +{
> +	unsigned long *addr = vaddr;
> +
> +	addr[0] = __opcode_to_mem_arm(auprobe->modinsn);
> +	addr[1] = __opcode_to_mem_arm(0xe0000000 | UPROBE_SS_INSN);
> +}
> +
> +int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
> +{
> +	struct uprobe_task *utask = current->utask;
> +
> +	if (auprobe->prehandler)
> +		auprobe->prehandler(auprobe, &utask->autask, regs);
> +
> +	regs->ARM_pc = utask->xol_vaddr;
> +
> +	return 0;
> +}
> +
> +int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
> +{
> +	struct uprobe_task *utask = current->utask;
> +
> +	regs->ARM_pc = utask->vaddr + 4;
> +
> +	if (auprobe->posthandler)
> +		auprobe->posthandler(auprobe, &utask->autask, regs);
> +
> +	return 0;
> +}
> +
> +bool arch_uprobe_xol_was_trapped(struct task_struct *t)
> +{
> +	/* TODO: implement */
> +	return false;
> +}
> +
> +void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
> +{
> +	/* TODO: implement */
> +}
> +
> +int arch_uprobe_exception_notify(struct notifier_block *self,
> +				 unsigned long val, void *data)
> +{
> +	return NOTIFY_DONE;
> +}
> +
> +static int uprobe_trap_handler(struct pt_regs *regs, unsigned int instr)
> +{
> +	unsigned long flags;
> +
> +	local_irq_save(flags);
> +	if ((instr & 0x0fffffff) == UPROBE_SWBP_INSN)
> +		uprobe_pre_sstep_notifier(regs);
> +	else
> +		uprobe_post_sstep_notifier(regs);
> +	local_irq_restore(flags);
> +
> +	return 0;
> +}
> +
> +unsigned long uprobe_get_swbp_addr(struct pt_regs *regs)
> +{
> +	return instruction_pointer(regs);
> +}
> +
> +static struct undef_hook uprobes_arm_break_hook = {
> +	.instr_mask	= 0x0fffffff,
> +	.instr_val	= UPROBE_SWBP_INSN,
> +	.cpsr_mask	= MODE_MASK,
> +	.cpsr_val	= USR_MODE,
> +	.fn		= uprobe_trap_handler,
> +};
> +
> +static struct undef_hook uprobes_arm_ss_hook = {
> +	.instr_mask	= 0x0fffffff,
> +	.instr_val	= UPROBE_SS_INSN,
> +	.cpsr_mask	= MODE_MASK,
> +	.cpsr_val	= USR_MODE,
> +	.fn		= uprobe_trap_handler,
> +};
> +
> +int arch_uprobes_init(void)
> +{
> +	register_undef_hook(&uprobes_arm_break_hook);
> +	register_undef_hook(&uprobes_arm_ss_hook);
> +
> +	return 0;
> +}
> diff --git a/arch/arm/kernel/uprobes.h b/arch/arm/kernel/uprobes.h
> new file mode 100644
> index 0000000..3887c19
> --- /dev/null
> +++ b/arch/arm/kernel/uprobes.h
> @@ -0,0 +1,25 @@
> +#ifndef __ARM_KERNEL_UPROBES_H
> +#define __ARM_KERNEL_UPROBES_H
> +
> +enum kprobe_insn uprobe_decode_ldmstm(kprobe_opcode_t insn,
> +				      struct arch_specific_insn *asi,
> +				      void *d);
> +
> +enum kprobe_insn decode_ldr(kprobe_opcode_t insn,
> +			    struct arch_specific_insn *asi,
> +			    void *d);
> +
> +enum kprobe_insn
> +decode_rd12rn16rm0rs8_rwflags(kprobe_opcode_t insn,
> +			      struct arch_specific_insn *asi, void *d);
> +
> +enum kprobe_insn
> +decode_wb_pc(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> +	     void *d, bool alu);
> +
> +enum kprobe_insn
> +decode_pc_ro(kprobe_opcode_t insn, struct arch_specific_insn *asi, void *d);
> +
> +extern const union decode_item uprobes_probes_actions[];
> +
> +#endif
> diff --git a/arch/powerpc/include/asm/uprobes.h b/arch/powerpc/include/asm/uprobes.h
> index 2301602..b532060 100644
> --- a/arch/powerpc/include/asm/uprobes.h
> +++ b/arch/powerpc/include/asm/uprobes.h
> @@ -51,5 +51,4 @@ extern int  arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs);
>  extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk);
>  extern int  arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data);
>  extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs);
> -extern unsigned long arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr, struct pt_regs *regs);
>  #endif	/* _ASM_UPROBES_H */
> diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h
> index 9cd3b25..41341e9 100644
> --- a/include/linux/uprobes.h
> +++ b/include/linux/uprobes.h
> @@ -139,6 +139,9 @@ extern int __weak arch_uprobes_init(void);
>  extern void __weak arch_uprobe_write_opcode(struct arch_uprobe *auprobe,
>  					    void *vaddr,
>  					    uprobe_opcode_t opcode);
> +extern
> +unsigned long arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr,
> +						       struct pt_regs *regs);
>  #else /* !CONFIG_UPROBES */
>  struct uprobes_state {
>  };
> diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
> index 6a60eec..3e9d596 100644
> --- a/kernel/events/uprobes.c
> +++ b/kernel/events/uprobes.c
> @@ -271,6 +271,7 @@ static int write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm,
>  {
>  	struct page *old_page, *new_page;
>  	struct vm_area_struct *vma;
> +	void *vaddr_new;

This looks like it should be in patch 5 where vaddr_new is used but not
defined ;-)

>  	int ret;
>  
>  retry:

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH 1/9] uprobes: move function declarations out of arch
  2012-10-15 17:19   ` Srikar Dronamraju
@ 2012-10-16 20:30     ` Rabin Vincent
  -1 siblings, 0 replies; 32+ messages in thread
From: Rabin Vincent @ 2012-10-16 20:30 UTC (permalink / raw)
  To: Srikar Dronamraju; +Cc: linux-kernel, linux-arm-kernel, Peter Zijlstra, oleg

2012/10/15 Srikar Dronamraju <srikar@linux.vnet.ibm.com>:
> You need to take care of the powerpc port
> http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=commitdiff;h=8b7b80b9ebb46dd88fbb94e918297295cf312b59
>
> Also as Oleg pointed out, He has already posted changes on top of -tip.
> So it probably makes sense to redo these patches on top of those
> patches.

OK, will look into it.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH 1/9] uprobes: move function declarations out of arch
@ 2012-10-16 20:30     ` Rabin Vincent
  0 siblings, 0 replies; 32+ messages in thread
From: Rabin Vincent @ 2012-10-16 20:30 UTC (permalink / raw)
  To: linux-arm-kernel

2012/10/15 Srikar Dronamraju <srikar@linux.vnet.ibm.com>:
> You need to take care of the powerpc port
> http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=commitdiff;h=8b7b80b9ebb46dd88fbb94e918297295cf312b59
>
> Also as Oleg pointed out, He has already posted changes on top of -tip.
> So it probably makes sense to redo these patches on top of those
> patches.

OK, will look into it.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH 1/9] uprobes: move function declarations out of arch
  2012-10-14 19:23 ` Rabin Vincent
@ 2012-10-15 17:19   ` Srikar Dronamraju
  -1 siblings, 0 replies; 32+ messages in thread
From: Srikar Dronamraju @ 2012-10-15 17:19 UTC (permalink / raw)
  To: Rabin Vincent; +Cc: linux-kernel, linux-arm-kernel, Peter Zijlstra, oleg

* Rabin Vincent <rabin@rab.in> [2012-10-14 21:23:05]:

> It seems odd to keep the function declarations in the arch header where
> they will need to be copy/pasted verbatim across arches.  Move them to
> the common header.
> 



> Signed-off-by: Rabin Vincent <rabin@rab.in>
> ---
>  arch/x86/include/asm/uprobes.h |    6 ------
>  include/linux/uprobes.h        |    8 ++++++++
>  2 files changed, 8 insertions(+), 6 deletions(-)
> 

You need to take care of the powerpc port
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=commitdiff;h=8b7b80b9ebb46dd88fbb94e918297295cf312b59

Also as Oleg pointed out, He has already posted changes on top of -tip.
So it probably makes sense to redo these patches on top of those
patches.

-- 
Thanks and Regards
Srikar


^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH 1/9] uprobes: move function declarations out of arch
@ 2012-10-15 17:19   ` Srikar Dronamraju
  0 siblings, 0 replies; 32+ messages in thread
From: Srikar Dronamraju @ 2012-10-15 17:19 UTC (permalink / raw)
  To: linux-arm-kernel

* Rabin Vincent <rabin@rab.in> [2012-10-14 21:23:05]:

> It seems odd to keep the function declarations in the arch header where
> they will need to be copy/pasted verbatim across arches.  Move them to
> the common header.
> 



> Signed-off-by: Rabin Vincent <rabin@rab.in>
> ---
>  arch/x86/include/asm/uprobes.h |    6 ------
>  include/linux/uprobes.h        |    8 ++++++++
>  2 files changed, 8 insertions(+), 6 deletions(-)
> 

You need to take care of the powerpc port
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=commitdiff;h=8b7b80b9ebb46dd88fbb94e918297295cf312b59

Also as Oleg pointed out, He has already posted changes on top of -tip.
So it probably makes sense to redo these patches on top of those
patches.

-- 
Thanks and Regards
Srikar

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH 1/9] uprobes: move function declarations out of arch
@ 2012-10-14 19:23 ` Rabin Vincent
  0 siblings, 0 replies; 32+ messages in thread
From: Rabin Vincent @ 2012-10-14 19:23 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel
  Cc: Peter Zijlstra, Srikar Dronamraju, oleg, Rabin Vincent

It seems odd to keep the function declarations in the arch header where
they will need to be copy/pasted verbatim across arches.  Move them to
the common header.

Signed-off-by: Rabin Vincent <rabin@rab.in>
---
 arch/x86/include/asm/uprobes.h |    6 ------
 include/linux/uprobes.h        |    8 ++++++++
 2 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/arch/x86/include/asm/uprobes.h b/arch/x86/include/asm/uprobes.h
index 8ff8be7..b20b4d6 100644
--- a/arch/x86/include/asm/uprobes.h
+++ b/arch/x86/include/asm/uprobes.h
@@ -49,10 +49,4 @@ struct arch_uprobe_task {
 	unsigned int			saved_tf;
 };
 
-extern int  arch_uprobe_analyze_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long addr);
-extern int  arch_uprobe_pre_xol(struct arch_uprobe *aup, struct pt_regs *regs);
-extern int  arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs);
-extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk);
-extern int  arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data);
-extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs);
 #endif	/* _ASM_UPROBES_H */
diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h
index e6f0331..ac90704 100644
--- a/include/linux/uprobes.h
+++ b/include/linux/uprobes.h
@@ -30,6 +30,7 @@
 struct vm_area_struct;
 struct mm_struct;
 struct inode;
+struct notifier_block;
 
 #ifdef CONFIG_ARCH_SUPPORTS_UPROBES
 # include <asm/uprobes.h>
@@ -120,6 +121,13 @@ extern void uprobe_notify_resume(struct pt_regs *regs);
 extern bool uprobe_deny_signal(void);
 extern bool __weak arch_uprobe_skip_sstep(struct arch_uprobe *aup, struct pt_regs *regs);
 extern void uprobe_clear_state(struct mm_struct *mm);
+extern void uprobe_reset_state(struct mm_struct *mm);
+extern int  arch_uprobe_analyze_insn(struct arch_uprobe *aup, struct mm_struct *mm,unsigned long addr);
+extern int  arch_uprobe_pre_xol(struct arch_uprobe *aup, struct pt_regs *regs);
+extern int  arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs);
+extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk);
+extern int  arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data);
+extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs);
 #else /* !CONFIG_UPROBES */
 struct uprobes_state {
 };
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 1/9] uprobes: move function declarations out of arch
@ 2012-10-14 19:23 ` Rabin Vincent
  0 siblings, 0 replies; 32+ messages in thread
From: Rabin Vincent @ 2012-10-14 19:23 UTC (permalink / raw)
  To: linux-arm-kernel

It seems odd to keep the function declarations in the arch header where
they will need to be copy/pasted verbatim across arches.  Move them to
the common header.

Signed-off-by: Rabin Vincent <rabin@rab.in>
---
 arch/x86/include/asm/uprobes.h |    6 ------
 include/linux/uprobes.h        |    8 ++++++++
 2 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/arch/x86/include/asm/uprobes.h b/arch/x86/include/asm/uprobes.h
index 8ff8be7..b20b4d6 100644
--- a/arch/x86/include/asm/uprobes.h
+++ b/arch/x86/include/asm/uprobes.h
@@ -49,10 +49,4 @@ struct arch_uprobe_task {
 	unsigned int			saved_tf;
 };
 
-extern int  arch_uprobe_analyze_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long addr);
-extern int  arch_uprobe_pre_xol(struct arch_uprobe *aup, struct pt_regs *regs);
-extern int  arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs);
-extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk);
-extern int  arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data);
-extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs);
 #endif	/* _ASM_UPROBES_H */
diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h
index e6f0331..ac90704 100644
--- a/include/linux/uprobes.h
+++ b/include/linux/uprobes.h
@@ -30,6 +30,7 @@
 struct vm_area_struct;
 struct mm_struct;
 struct inode;
+struct notifier_block;
 
 #ifdef CONFIG_ARCH_SUPPORTS_UPROBES
 # include <asm/uprobes.h>
@@ -120,6 +121,13 @@ extern void uprobe_notify_resume(struct pt_regs *regs);
 extern bool uprobe_deny_signal(void);
 extern bool __weak arch_uprobe_skip_sstep(struct arch_uprobe *aup, struct pt_regs *regs);
 extern void uprobe_clear_state(struct mm_struct *mm);
+extern void uprobe_reset_state(struct mm_struct *mm);
+extern int  arch_uprobe_analyze_insn(struct arch_uprobe *aup, struct mm_struct *mm,unsigned long addr);
+extern int  arch_uprobe_pre_xol(struct arch_uprobe *aup, struct pt_regs *regs);
+extern int  arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs);
+extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk);
+extern int  arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data);
+extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs);
 #else /* !CONFIG_UPROBES */
 struct uprobes_state {
 };
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 32+ messages in thread

end of thread, other threads:[~2013-08-29 14:54 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-08-01 23:45 [PATCH 0/9] uprobes: Add uprobes support for ARM David Long
2013-08-01 23:45 ` David Long
2013-08-01 23:45 ` [PATCH 1/9] uprobes: move function declarations out of arch David Long
2013-08-01 23:45   ` David Long
2013-08-01 23:45 ` [PATCH 2/9] uprobes: allow ignoring of probe hits David Long
2013-08-01 23:45   ` David Long
2013-08-01 23:45 ` [PATCH 3/9] uprobes: allow arch access to xol slot David Long
2013-08-01 23:45   ` David Long
2013-08-01 23:45 ` [PATCH 4/9] uprobes: allow arch-specific initialization David Long
2013-08-01 23:45   ` David Long
2013-08-01 23:45 ` [PATCH 5/9] uprobes: add arch write opcode hook David Long
2013-08-01 23:45   ` David Long
2013-08-01 23:45 ` [PATCH 6/9] ARM: move shared uprobe/kprobe definitions into new include file David Long
2013-08-01 23:45   ` David Long
2013-08-01 23:45 ` [PATCH 7/9] ARM: Move uprobes/kprobes shared functions to common file David Long
2013-08-01 23:45   ` David Long
2013-08-29 14:08   ` Jon Medhurst (Tixy)
2013-08-29 14:08     ` Jon Medhurst (Tixy)
2013-08-01 23:45 ` [PATCH 8/9] ARM: Add "action" table for kprobes/uprobes instruction David Long
2013-08-01 23:45   ` David Long
2013-08-29 14:09   ` Jon Medhurst (Tixy)
2013-08-29 14:09     ` Jon Medhurst (Tixy)
2013-08-01 23:45 ` [PATCH 9/9] ARM: add uprobes support David Long
2013-08-01 23:45   ` David Long
2013-08-29 14:54   ` Jon Medhurst (Tixy)
2013-08-29 14:54     ` Jon Medhurst (Tixy)
  -- strict thread matches above, loose matches on Subject: below --
2012-10-14 19:23 [PATCH 1/9] uprobes: move function declarations out of arch Rabin Vincent
2012-10-14 19:23 ` Rabin Vincent
2012-10-15 17:19 ` Srikar Dronamraju
2012-10-15 17:19   ` Srikar Dronamraju
2012-10-16 20:30   ` Rabin Vincent
2012-10-16 20:30     ` Rabin Vincent

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.