This is the mail archive of the systemtap@sourceware.org mailing list for the systemtap project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[RFC][PATCH] Kprobes for ARM


This is a fully functional Kprobes implementation for ARM as a
patch set against the 2.6.20.4 kernel.

This implementation includes kprobes, jprobes, and kretprobes.
Abhishek Sagar contributed the initialization, undef handling, fault
handling, jprobes, and kretprobes code.  The blame for the remaining
core kprobes code falls to me.

This code has no known problems and has received some basic testing
through systemtap testsuite.  It should, however, be considered
raw prototype quality.  I'm sure it has a considerable quantity of
undiscovered bugs.

This mail exceeds Linus' recommended size of 40KB for a patch
mailing.  It is ~70KB in size.  However, I have noticed that others
on this mailing list have exceeded the limit before.  If this size
is an imposition, please let me know.  I'll be setting this project
up on a remote site, but haven't yet.  This will probably be my
only patch mailing of this size.

I'll provide more detailed information in some following mails.

Quentin



Index: linux-2.6.20.4/include/asm-arm/kdebug.h
===================================================================
--- linux-2.6.20.4/include/asm-arm/kdebug.h	(.../vendor/usr/src)	(revision 0)
+++ linux-2.6.20.4/include/asm-arm/kdebug.h	(.../branches/kprobes/usr/src)	(revision 129)
@@ -0,0 +1,34 @@
+#ifndef _ARM_KDEBUG_H
+#define _ARM_KDEBUG_H
+
+#include <linux/notifier.h>
+
+struct pt_regs;
+
+struct die_args {
+	struct pt_regs *regs;
+	const char *str;
+	unsigned int fsr;
+};
+
+extern int register_page_fault_notifier(struct notifier_block *);
+extern int unregister_page_fault_notifier(struct notifier_block *);
+
+extern void notify_die(const char *str, struct pt_regs *regs,
+			struct siginfo *info, unsigned long err,
+			unsigned long trap);
+
+enum die_val {
+	DIE_TRANS_FAULT = 1,
+	DIE_PAGE_FAULT
+};
+
+
+/* Die notifier kernel feature only used by kprobes.  We don't use it
+ * for kprobes on ARM.  Disable it with this dummy function. */
+static inline int register_die_notifier(struct notifier_block *nb)
+{
+	return 0;
+}
+
+#endif /* _ARM_KDEBUG_H */
Index: linux-2.6.20.4/include/asm-arm/kprobes.h
===================================================================
--- linux-2.6.20.4/include/asm-arm/kprobes.h	(.../vendor/usr/src)	(revision 0)
+++ linux-2.6.20.4/include/asm-arm/kprobes.h	(.../branches/kprobes/usr/src)	(revision 129)
@@ -0,0 +1,84 @@
+#ifndef _ARM_KPROBES_H
+#define _ARM_KPROBES_H
+
+/* include/asm-arm/kprobes.h
+ *
+ * Copyright (C) 2006, 2007 Motorola Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/percpu.h>
+#include <linux/ptrace.h>
+#include <asm/cacheflush.h>
+
+#define ARCH_SUPPORTS_KRETPROBES
+#define __ARCH_WANT_KPROBES_INSN_SLOT
+#define MAX_INSN_SIZE		   2
+#define ARCH_INACTIVE_KPROBE_COUNT 0
+
+#define flush_insn_slot(p)	do { } while (0)
+
+#define MAX_STACK_SIZE          64 /* Probably 32 should suffice */
+#define MIN_STACK_SIZE(addr) \
+			min( (unsigned long)MAX_STACK_SIZE, \
+			     (unsigned long)current_thread_info() + \
+			     THREAD_START_SP - (addr) )
+
+/* This undefined instruction must be unique and
+ * reserved solely for kprobes' use. */
+#define KPROBE_BREAKPOINT_INSTRUCTION	0xe7f001f8
+
+#define KPROBE_RETURN_INSTRUCTION	0xe1a0f00e    /* mov pc, lr */
+
+#define JPROBE_ENTRY(pentry)		((kprobe_opcode_t *)(pentry))
+
+enum kprobe_insn {
+	INSN_REJECTED,
+	INSN_SIMULATED,
+	INSN_EMULATED
+};
+
+typedef u32 kprobe_opcode_t;
+
+struct kprobe;
+typedef void (kprobe_insn_handler_t)(struct kprobe *, struct pt_regs *);
+
+/* Architecture specific copy of original instruction. */
+struct arch_specific_insn {
+	kprobe_opcode_t		*insn;
+	kprobe_insn_handler_t	*insn_handler;
+};
+
+struct prev_kprobe {
+	struct kprobe *kp;
+	unsigned int status;
+};
+
+
+/* per-cpu kprobe control block */
+struct kprobe_ctlblk {
+	unsigned int kprobe_status;
+	struct prev_kprobe prev_kprobe;
+	struct pt_regs jprobe_saved_regs;
+	char jprobes_stack[MAX_STACK_SIZE];
+};
+
+extern void arch_flush_insn_slot(struct kprobe *);
+extern int kprobe_exceptions_notify(struct notifier_block *self,
+                    unsigned long val, void *data);
+extern void arch_remove_kprobe(struct kprobe *);
+extern enum kprobe_insn kprobe_decode_insn(kprobe_opcode_t,
+						struct arch_specific_insn *);
+extern int kprobe_handler(struct pt_regs *regs);
+
+#endif /* _ARM_KPROBES_H */
Index: linux-2.6.20.4/include/asm-arm/ptrace.h
===================================================================
--- linux-2.6.20.4/include/asm-arm/ptrace.h	(.../vendor/usr/src)	(revision 129)
+++ linux-2.6.20.4/include/asm-arm/ptrace.h	(.../branches/kprobes/usr/src)	(revision 129)
@@ -144,6 +144,8 @@ static inline int valid_user_regs(struct
#define instruction_pointer(regs) \
	(pc_pointer((regs)->ARM_pc))

+#define regs_return_value(regs) ((regs)->ARM_r0)
+
#ifdef CONFIG_SMP
extern unsigned long profile_pc(struct pt_regs *regs);
#else
Index: linux-2.6.20.4/Documentation/kprobes.txt
===================================================================
--- linux-2.6.20.4/Documentation/kprobes.txt	(.../vendor/usr/src)	(revision 129)
+++ linux-2.6.20.4/Documentation/kprobes.txt	(.../branches/kprobes/usr/src)	(revision 129)
@@ -140,6 +140,7 @@ architectures:
- ppc64
- ia64 (Does not support probes on instruction slot1.)
- sparc64 (Return probes not yet implemented.)
+- arm

3. Configuring Kprobes

Index: linux-2.6.20.4/arch/arm/Kconfig
===================================================================
--- linux-2.6.20.4/arch/arm/Kconfig	(.../vendor/usr/src)	(revision 129)
+++ linux-2.6.20.4/arch/arm/Kconfig	(.../branches/kprobes/usr/src)	(revision 129)
@@ -966,8 +966,15 @@ endmenu

source "fs/Kconfig"

+menu "Instrumentation Support"
+	depends on EXPERIMENTAL
+
source "arch/arm/oprofile/Kconfig"

+source "arch/arm/kernel/Kconfig"
+
+endmenu
+
source "arch/arm/Kconfig.debug"

source "security/Kconfig"
Index: linux-2.6.20.4/arch/arm/kernel/Kconfig
===================================================================
--- linux-2.6.20.4/arch/arm/kernel/Kconfig (.../vendor/usr/src) (revision 0)
+++ linux-2.6.20.4/arch/arm/kernel/Kconfig (.../branches/kprobes/usr/src) (revision 129)
@@ -0,0 +1,13 @@
+menu "Kernel Probes(Kprobes)"
+ depends on KALLSYMS && EXPERIMENTAL && MODULES
+
+config KPROBES
+ bool "Kernel Probes (EXPERIMENTAL)"
+ help
+ Kprobes allows you to trap at almost any kernel address and
+ execute a callback function. register_kprobe() establishes
+ a probepoint and specifies the callback. Kprobes is useful
+ for kernel debugging, non-intrusive instrumentation and testing.
+ If in doubt, say "N".
+
+endmenu
Index: linux-2.6.20.4/arch/arm/kernel/kprobes.c
===================================================================
--- linux-2.6.20.4/arch/arm/kernel/kprobes.c (.../vendor/usr/src) (revision 0)
+++ linux-2.6.20.4/arch/arm/kernel/kprobes.c (.../branches/kprobes/usr/src) (revision 129)
@@ -0,0 +1,601 @@
+/* arch/arm/kernel/kprobes.c
+ *
+ * Kprobes on ARM
+ *
+ * Abhishek Sagar <sagar.abhishek@gmail.com>
+ *
+ * Copyright (C) 2006, 2007 Motorola Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/kprobes.h>
+#include <linux/module.h>
+#include <asm/kdebug.h>
+#include <asm/traps.h>
+#include <asm/signal.h>
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
+#include <asm/uaccess.h>
+
+#define flush_insns(addr, cnt) \
+ flush_icache_range((unsigned long)(addr), \
+ (unsigned long)(addr) + \
+ sizeof(kprobe_opcode_t) * (cnt))
+
+/* Used as a marker in ARM_pc to note when we're in a jprobe. */
+#define JPROBE_MAGIC_ADDR 0xffffffff
+
+void jprobe_return_end(void);
+void kretprobe_trampoline(void);
+
+DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL;
+DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
+
+int kprobes_str_pc_offset;
+
+
+int __kprobes arch_prepare_kprobe(struct kprobe *p)
+{
+ kprobe_opcode_t insn;
+ kprobe_opcode_t tmp_insn[MAX_INSN_SIZE];
+ int is;
+
+ if ((unsigned long)p->addr & 0x3)
+ return -EINVAL;
+
+ insn = *p->addr;
+
+ p->opcode = insn;
+ p->ainsn.insn = tmp_insn;
+
+ switch (kprobe_decode_insn(insn, &p->ainsn)) {
+ case INSN_REJECTED: /* not supported */
+ return -EINVAL;
+
+ case INSN_SIMULATED: /* software only - no slot */
+ p->ainsn.insn = 0;
+ break;
+
+ case INSN_EMULATED: /* emulated using slot */
+ p->ainsn.insn = get_insn_slot();
+ if (!p->ainsn.insn)
+ return -ENOMEM;
+
+ for (is = 0; is < MAX_INSN_SIZE; ++is)
+ p->ainsn.insn[is] = tmp_insn[is];
+
+ flush_insns(&p->ainsn.insn, MAX_INSN_SIZE);
+ break;
+ }
+
+ return 0;
+}
+
+
+void __kprobes arch_arm_kprobe(struct kprobe *p)
+{
+ *p->addr = KPROBE_BREAKPOINT_INSTRUCTION;
+ flush_insns(p->addr, 1);
+}
+
+
+void __kprobes arch_disarm_kprobe(struct kprobe *p)
+{
+ *p->addr = p->opcode;
+ flush_insns(p->addr, 1);
+}
+
+
+void __kprobes arch_remove_kprobe(struct kprobe *p)
+{
+ if (p->ainsn.insn) {
+ mutex_lock(&kprobe_mutex);
+ free_insn_slot(p->ainsn.insn, 0);
+ mutex_unlock(&kprobe_mutex);
+ p->ainsn.insn = 0;
+ }
+}
+
+static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb)
+{
+ kcb->prev_kprobe.kp = kprobe_running();
+ kcb->prev_kprobe.status = kcb->kprobe_status;
+}
+
+
+static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb)
+{
+ __get_cpu_var(current_kprobe) = kcb->prev_kprobe.kp;
+ kcb->kprobe_status = kcb->prev_kprobe.status;
+}
+
+
+static void __kprobes set_current_kprobe(struct kprobe *p)
+{
+ __get_cpu_var(current_kprobe) = p;
+}
+
+
+/* Called with kretprobe_lock held. */
+void __kprobes arch_prepare_kretprobe(struct kretprobe *rp,
+ struct pt_regs *regs)
+{
+ struct kretprobe_instance *ri;
+
+ if ((ri = get_free_rp_inst(rp)) != NULL) {
+ ri->rp = rp;
+ ri->task = current;
+ ri->ret_addr = (kprobe_opcode_t *)regs->ARM_lr;
+
+ /* replace the return addr with trampoline addr */
+ regs->ARM_lr = (unsigned long)&kretprobe_trampoline;
+ add_rp_inst(ri);
+ } else {
+ rp->nmissed++;
+ }
+}
+
+
+/* Instruction condition field values. */
+#define INST_EQ 0x0
+#define INST_NE 0x1
+#define INST_CS 0x2
+#define INST_CC 0x3
+#define INST_MI 0x4
+#define INST_PL 0x5
+#define INST_VS 0x6
+#define INST_VC 0x7
+#define INST_HI 0x8
+#define INST_LS 0x9
+#define INST_GE 0xa
+#define INST_LT 0xb
+#define INST_GT 0xc
+#define INST_LE 0xd
+#define INST_AL 0xe
+#define INST_NV 0xf
+
+#define FLAG_N 0x80000000
+#define FLAG_Z 0x40000000
+#define FLAG_C 0x20000000
+#define FLAG_V 0x10000000
+
+/* Borrowed directly from gdb-6.5 arm-tdep.c. */
+static int __kprobes condition_true (unsigned long cond,
+ unsigned long status_reg)
+{
+ if (cond == INST_AL || cond == INST_NV)
+ return 1;
+
+ switch (cond) {
+ case INST_EQ:
+ return ((status_reg & FLAG_Z) != 0);
+ case INST_NE:
+ return ((status_reg & FLAG_Z) == 0);
+ case INST_CS:
+ return ((status_reg & FLAG_C) != 0);
+ case INST_CC:
+ return ((status_reg & FLAG_C) == 0);
+ case INST_MI:
+ return ((status_reg & FLAG_N) != 0);
+ case INST_PL:
+ return ((status_reg & FLAG_N) == 0);
+ case INST_VS:
+ return ((status_reg & FLAG_V) != 0);
+ case INST_VC:
+ return ((status_reg & FLAG_V) == 0);
+ case INST_HI:
+ return ((status_reg & (FLAG_C | FLAG_Z)) == FLAG_C);
+ case INST_LS:
+ return ((status_reg & (FLAG_C | FLAG_Z)) != FLAG_C);
+ case INST_GE:
+ return (((status_reg & FLAG_N) == 0) ==
+ ((status_reg & FLAG_V) == 0));
+ case INST_LT:
+ return (((status_reg & FLAG_N) == 0) !=
+ ((status_reg & FLAG_V) == 0));
+ case INST_GT:
+ return (((status_reg & FLAG_Z) == 0) &&
+ (((status_reg & FLAG_N) == 0) ==
+ ((status_reg & FLAG_V) == 0)));
+ case INST_LE:
+ return (((status_reg & FLAG_Z) != 0) ||
+ (((status_reg & FLAG_N) == 0) !=
+ ((status_reg & FLAG_V) == 0)));
+ }
+ return 1;
+}
+
+
+static void __kprobes singlestep(struct kprobe *p, struct pt_regs *regs,
+ struct kprobe_ctlblk *kcb)
+{
+ int insn_cc = (p->opcode >> 28) & 0xf;
+
+ regs->ARM_pc += 4;
+
+ if (condition_true(insn_cc, regs->ARM_cpsr)) {
+ p->ainsn.insn_handler(p, regs);
+ }
+
+ if ((kcb->kprobe_status != KPROBE_REENTER) && p->post_handler) {
+ kcb->kprobe_status = KPROBE_HIT_SSDONE;
+ p->post_handler(p, regs, 0);
+ }
+
+ if (kcb->kprobe_status == KPROBE_REENTER) {
+ restore_previous_kprobe(kcb);
+ } else {
+ reset_current_kprobe();
+ }
+}
+
+
+/* IRQs were automatically disabled when executing the kprobe
+ * instruction. IRQs must remain disabled from that point all the
+ * way until processing this kprobe is complete. The current kprobes
+ * implementation cannot process more than one nested level of kprobe,
+ * and that level is reserved for user kprobe handlers, so we can't
+ * risk encountering a new kprobe in an interrupt handler.
+ */
+
+int __kprobes kprobe_handler(struct pt_regs *regs)
+{
+ struct kprobe *p, *cur;
+ struct kprobe_ctlblk *kcb;
+ kprobe_opcode_t *addr = (kprobe_opcode_t *)regs->ARM_pc;
+
+ kcb = get_kprobe_ctlblk();
+ cur = kprobe_running();
+ p = get_kprobe(addr);
+
+ if (p) {
+ if (cur) { /* Kprobe is pending, so we're recursing. */
+ switch (kcb->kprobe_status) {
+ case KPROBE_HIT_ACTIVE:
+ case KPROBE_HIT_SSDONE:
+ /* A pre- or post-handler probe got us here. */
+ kprobes_inc_nmissed_count(p);
+ save_previous_kprobe(kcb);
+ set_current_kprobe(p);
+ kcb->kprobe_status = KPROBE_REENTER;
+ singlestep(p, regs, kcb);
+ break;
+ default:
+ /* impossible cases */
+ BUG();
+ }
+ } else {
+ set_current_kprobe(p);
+ kcb->kprobe_status = KPROBE_HIT_ACTIVE;
+
+ /* If we have no pre-handler or it returned 0, we
+ * continue with normal processing. If we have a
+ * pre-handler and it returned non-zero, it prepped
+ * for calling the break_handler below on re-entry,
+ * so get out doing nothing more here.
+ */
+
+ if (!p->pre_handler || !p->pre_handler(p, regs)) {
+ kcb->kprobe_status = KPROBE_HIT_SS;
+ singlestep(p, regs, kcb);
+ }
+ }
+ } else {
+ /* Two ways we could have gotten here. Either the probe
+ * was removed and a race is in progress or we hit a
+ * jprobe.
+ *
+ * If it's a race, nothing we can do about it. Restart
+ * the instruction. By the time we can restart, the
+ * real instruction will be there. If we're in a jprobe
+ * call its break handler.
+ */
+
+ if (cur) {
+ if ( (addr == (kprobe_opcode_t *)JPROBE_MAGIC_ADDR) &&
+ cur->break_handler && + cur->break_handler(cur, regs) ) {
+ kcb->kprobe_status = KPROBE_HIT_SS;
+ singlestep(cur, regs, kcb);
+ } else {
+ reset_current_kprobe();
+ }
+ }
+ }
+
+ return 1;
+}
+
+
+static int __kprobes kprobe_fault_handler(struct pt_regs *regs,
+ unsigned int fsr)
+{
+ struct kprobe *cur = kprobe_running();
+ struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+
+ switch (kcb->kprobe_status) {
+ case KPROBE_HIT_SS:
+ case KPROBE_REENTER:
+ /*
+ * We are here because the instruction being single
+ * stepped caused a page fault. We reset the current
+ * kprobe and the PC to point back to the probe address
+ * and allow the page fault handler to continue as a
+ * normal page fault.
+ */
+ regs->ARM_pc = (long)cur->addr;
+ if (kcb->kprobe_status == KPROBE_REENTER) {
+ restore_previous_kprobe(kcb);
+ } else {
+ reset_current_kprobe();
+ }
+ break;
+
+ case KPROBE_HIT_ACTIVE:
+ case KPROBE_HIT_SSDONE:
+ /*
+ * We increment the nmissed count for accounting,
+ * we can also use npre/npostfault count for accounting
+ * these specific fault cases.
+ */
+ kprobes_inc_nmissed_count(cur);
+
+ /*
+ * We come here because instructions in the pre/post
+ * handler caused the page_fault, this could happen
+ * if handler tries to access user space by
+ * copy_from_user(), get_user() etc. Let the
+ * user-specified handler try to fix it first.
+ */
+ if (cur->fault_handler && cur->fault_handler(cur, regs, fsr))
+ return 1;
+
+ /*
+ * In case the user-specified fault handler returned
+ * zero, try to fix up.
+ */
+
+ if (fixup_exception(regs))
+ return 1;
+
+ /*
+ * fixup_exception() could not handle it,
+ * Let do_page_fault() fix it.
+ */
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+
+/*
+ * When a retprobed function returns, trampoline_handler() is called,
+ * calling the kretprobe's handler. We contruct a struct pt_regs to
+ * give a view of registers r0-r11 to the user return-handler.
+ */
+void __attribute__((naked)) __kprobes kretprobe_trampoline_holder(void)
+{
+ __asm__ volatile (
+ ".global kretprobe_trampoline \n\t"
+ "kretprobe_trampoline: \n\t"
+ "stmdb sp!, {r0-r11} \n\t"
+ "mov r0, sp \n\t"
+ "bl trampoline_handler \n\t"
+ "mov lr, r0 \n\t"
+ "ldmia sp!, {r0-r11} \n\t"
+ "mov pc, lr \n\t"
+ :
+ :
+ :"memory");
+}
+
+
+/*
+ * Called from kretprobe_trampoline_holder
+ */
+void * __kprobes trampoline_handler(struct pt_regs *regs)
+{
+ struct kretprobe_instance *ri = NULL;
+ struct hlist_head *head, empty_rp;
+ struct hlist_node *node, *tmp;
+ unsigned long flags, orig_ret_address = 0;
+ unsigned long trampoline_address =(unsigned long)&kretprobe_trampoline;
+
+ INIT_HLIST_HEAD(&empty_rp);
+ spin_lock_irqsave(&kretprobe_lock, flags);
+ head = kretprobe_inst_table_head(current);
+
+ /*
+ * It is possible to have multiple instances associated with a given
+ * task either because an multiple functions in the call path
+ * have a return probe installed on them, and/or more then one return
+ * return probe was registered for a target function.
+ *
+ * We can handle this because:
+ * - instances are always inserted at the head of the list
+ * - when multiple return probes are registered for the same
+ * function, the first instance's ret_addr will point to the
+ * real return address, and all the rest will point to
+ * kretprobe_trampoline
+ */
+ hlist_for_each_entry_safe(ri, node, tmp, head, hlist) {
+ if (ri->task != current)
+ /* another task is sharing our hash bucket */
+ continue;
+
+ if (ri->rp && ri->rp->handler) {
+ __get_cpu_var(current_kprobe) = &ri->rp->kp;
+ ri->rp->handler(ri, regs);
+ __get_cpu_var(current_kprobe) = NULL;
+ }
+
+ orig_ret_address = (unsigned long)ri->ret_addr;
+ recycle_rp_inst(ri, &empty_rp);
+
+ if (orig_ret_address != trampoline_address)
+ /*
+ * This is the real return address. Any other
+ * instances associated with this task are for
+ * other calls deeper on the call stack
+ */
+ break;
+ }
+
+ BUG_ON(!orig_ret_address || (orig_ret_address == trampoline_address));
+
+ spin_unlock_irqrestore(&kretprobe_lock, flags);
+
+ hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) {
+ hlist_del(&ri->hlist);
+ kfree(ri);
+ }
+ return (void *)orig_ret_address;
+}
+
+
+/*
+ * Wrapper routine to for handling exceptions.
+ */
+int __kprobes kprobe_exceptions_notify(struct notifier_block *self,
+ unsigned long val, void *data)
+{
+ struct die_args *args = (struct die_args *)data;
+ struct pt_regs *regs = args->regs;
+ int ret = NOTIFY_DONE;
+
+ switch (val) {
+ case DIE_TRANS_FAULT:
+ case DIE_PAGE_FAULT:
+ /* To be potentially processing a kprobe fault and to
+ * trust the result from kprobe_running(), we have
+ * be non-preemptible. */
+ if (!preemptible() &&
+ regs && !user_mode(regs) &&
+ kprobe_running() &&
+ kprobe_fault_handler(regs, args->fsr)) {
+ ret = NOTIFY_STOP;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ struct jprobe *jp = container_of(p, struct jprobe, kp);
+ struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+ long sp_addr = regs->ARM_sp;
+
+ kcb->jprobe_saved_regs = *regs;
+ memcpy(kcb->jprobes_stack, (void *)sp_addr, MIN_STACK_SIZE(sp_addr));
+ regs->ARM_pc = (long)jp->entry;
+ regs->ARM_cpsr |= PSR_I_BIT;
+ preempt_disable();
+ return 1;
+}
+
+
+#define str(s) #s
+#define xstr(s) str(s)
+
+void __kprobes jprobe_return(void)
+{
+ struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+
+ __asm__ volatile(
+ /* Setup an empty pt_regs. Fill SP and PC fields as
+ * they're needed by longjmp_break_handler.
+ */
+ "sub sp, %0, %1 \n\t"
+ "ldr r0, =" xstr(JPROBE_MAGIC_ADDR) "\n\t"
+ "str r0, [sp, %3] \n\t"
+ "str %0, [sp, %2] \n\t"
+
+ "mov r0, sp \n\t"
+ "bl kprobe_handler \n\t"
+
+ /* Return to the context saved by setjmp_pre_handler
+ * and restored by longjmp_break_handler. */
+ "ldr r0, [sp, %4] \n\t"
+ "msr cpsr_cxsf, r0 \n\t"
+ "ldmia sp, {r0-pc} \n\t"
+ :
+ : "r" (kcb->jprobe_saved_regs.ARM_sp),
+ "I"(sizeof(struct pt_regs)),
+ "J"(offsetof(struct pt_regs, ARM_sp)),
+ "J"(offsetof(struct pt_regs, ARM_pc)),
+ "J"(offsetof(struct pt_regs, ARM_cpsr))
+ : "memory", "cc");
+}
+
+int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+ long stack_addr = kcb->jprobe_saved_regs.ARM_sp;
+ long orig_sp = regs->ARM_sp;
+ struct jprobe *jp = container_of(p, struct jprobe, kp);
+
+ if (orig_sp != stack_addr) {
+ struct pt_regs *saved_regs =
+ (struct pt_regs *)kcb->jprobe_saved_regs.ARM_sp;
+ printk("current sp %lx does not match saved sp %lx\n",
+ orig_sp, stack_addr);
+ printk("Saved registers for jprobe %p\n", jp);
+ show_regs(saved_regs);
+ printk("Current registers\n");
+ show_regs(regs);
+ BUG();
+ }
+
+ *regs = kcb->jprobe_saved_regs;
+ memcpy((void *)stack_addr, kcb->jprobes_stack,
+ MIN_STACK_SIZE(stack_addr));
+ preempt_enable_no_resched();
+
+ return 1;
+}
+
+
+/*
+ * For STR and STM instructions, an ARM core may choose to use either
+ * a +8 or a +12 displacement from the current instruction's address.
+ * Whichever value is chosen for a given core, it must be the same for
+ * both instructions and may not change. This function measures it.
+ */
+
+static int __init find_str_pc_offset(void)
+{
+ int addr;
+ int scratch;
+ int ret;
+
+ __asm__("sub %[ret], pc, #4 \n\t"
+ "str pc, %[addr] \n\t"
+ "ldr %[scr], %[addr] \n\t"
+ "sub %[ret], %[scr], %[ret] \n\t"
+ : [ret] "=r" (ret), [scr] "=r" (scratch), [addr] "+m" (addr) );
+
+ return ret;
+}
+
+int __init arch_init_kprobes()
+{
+ kprobes_str_pc_offset = find_str_pc_offset();
+ return 0;
+}
Index: linux-2.6.20.4/arch/arm/kernel/entry-armv.S
===================================================================
--- linux-2.6.20.4/arch/arm/kernel/entry-armv.S (.../vendor/usr/src) (revision 129)
+++ linux-2.6.20.4/arch/arm/kernel/entry-armv.S (.../branches/kprobes/usr/src) (revision 129)
@@ -11,8 +11,8 @@
*
* Low-level vector interface routines
*
- * Note: there is a StrongARM bug in the STMIA rn, {regs}^ instruction that causes
- * it to save wrong values... Be aware!
+ * Note: there is a StrongARM bug in the STMIA rn, {regs}^ instruction
+ * that causes it to save wrong values... Be aware!
*/


#include <asm/memory.h>
@@ -57,6 +57,8 @@

.endm

+	.section	".kprobes.text"
+
/*
 * Invalid mode handlers
 */
@@ -242,7 +244,16 @@ svc_preempt:

	.align	5
__und_svc:
+#ifdef CONFIG_KPROBES
+	@ Give kprobe'd instruction stack space to finish in case of STM.
+	sub	sp, sp, #64
+#endif
	svc_entry
+#ifdef CONFIG_KPROBES
+	@ Adjust saved SP back to its original value.
+	add	r0, r0, #64
+	str	r0, [sp, #S_SP]
+#endif

	@
	@ call emulation code, which returns using r9 if it has emulated
@@ -548,7 +559,7 @@ do_fpe:
	.data
ENTRY(fp_enter)
	.word	no_fp
-	.text
+	.previous

no_fp: mov pc, lr

Index: linux-2.6.20.4/arch/arm/kernel/Makefile
===================================================================
--- linux-2.6.20.4/arch/arm/kernel/Makefile	(.../vendor/usr/src)	(revision 129)
+++ linux-2.6.20.4/arch/arm/kernel/Makefile	(.../branches/kprobes/usr/src)	(revision 129)
@@ -19,6 +19,7 @@ obj-$(CONFIG_ARTHUR)		+= arthur.o
obj-$(CONFIG_ISA_DMA)		+= dma-isa.o
obj-$(CONFIG_PCI)		+= bios32.o isa.o
obj-$(CONFIG_SMP)		+= smp.o
+obj-$(CONFIG_KPROBES)		+= kprobes.o kprobes-decode.o
obj-$(CONFIG_OABI_COMPAT)	+= sys_oabi-compat.o

obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o
Index: linux-2.6.20.4/arch/arm/kernel/vmlinux.lds.S
===================================================================
--- linux-2.6.20.4/arch/arm/kernel/vmlinux.lds.S (.../vendor/usr/src) (revision 129)
+++ linux-2.6.20.4/arch/arm/kernel/vmlinux.lds.S (.../branches/kprobes/usr/src) (revision 129)
@@ -84,6 +84,7 @@ SECTIONS
*(.text)
SCHED_TEXT
LOCK_TEXT
+ KPROBES_TEXT
#ifdef CONFIG_MMU
*(.fixup)
#endif
Index: linux-2.6.20.4/arch/arm/kernel/kprobes-decode.c
===================================================================
--- linux-2.6.20.4/arch/arm/kernel/kprobes-decode.c (.../vendor/usr/src) (revision 0)
+++ linux-2.6.20.4/arch/arm/kernel/kprobes-decode.c (.../branches/kprobes/usr/src) (revision 129)
@@ -0,0 +1,1348 @@
+/* arch/arm/kernel/kprobes-decode.c
+ *
+ * Copyright (C) 2006, 2007 Motorola Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+/* We do not have hardware single-stepping on ARM, This
+ * effort is further complicated by the ARM not having a
+ * "next PC" register. Instructions that change the PC
+ * can't be safely single-stepped in a MP environment, so
+ * we have a lot of work to do:
+ *
+ * In the prepare phase:
+ * *) If it is an instruction that does anything
+ * with the CPU mode, we reject it for a kprobe.
+ * (This is out of laziness rather than need. The
+ * instructions could be simulated.)
+ *
+ * *) Otherwise, decode the instruction rewriting its
+ * registers to take fixed, ordered registers and
+ * setting a handler for it to run the instruction.
+ *
+ * In the execution phase by an instruction's handler:
+ *
+ * *) If the PC is written to by the instruction, the
+ * instruction must be fully simulated in software.
+ *
+ * *) Otherwise, a modified form of the instruction can
+ * directly executed. Its handler calls the
+ * instruction in insn[0]. In insn[1] is a "bx lr"
+ * to get us back.
+ *
+ * Before calling, load up the reordered registers
+ * from the original instruction's registers. If one
+ * of the original input registers is the PC, compute
+ * and adjust the appropriate input register.
+ *
+ * After call completes, copy the output registers to
+ * the original instruction's original registers.
+ *
+ * We don't use a real breakpoint instruction since that
+ * would have us in the kernel go from SVC mode to SVC
+ * mode losing the link register. Instead we use an
+ * undefined instruction. To simply processing, the
+ * undefined instruction used for kprobes must be reserved
+ * exclusively for kprobes use.
+ */
+
+/* TODO: ifdef instruction decoding based on architecture.
+ */
+
+#include <linux/kernel.h>
+#include <linux/kprobes.h>
+#include <asm/kdebug.h>
+
+#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
+
+#define array_cnt(x) (sizeof((x))/sizeof(*(x)))
+
+#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25)
+
+#define PSR_fs (PSR_f|PSR_s)
+
+typedef long (insn_0arg_fn_t)(void);
+typedef long (insn_1arg_fn_t)(long);
+typedef long (insn_2arg_fn_t)(long,long);
+typedef long (insn_3arg_fn_t)(long,long,long);
+typedef long (insn_4arg_fn_t)(long,long,long,long);
+typedef long long (insn_llret_3arg_fn_t)(long,long,long);
+
+typedef union {
+ long long dr;
+#ifdef __LITTLE_ENDIAN
+ struct { long r0, r1; };
+#else
+ struct { long r1, r0; };
+#endif
+} reg_pair;
+
+extern int kprobes_str_pc_offset;
+
+
+/* The emulate_?arg_[rw]flags() functions below are to keep the
+ * msr -> *fn -> mrs instruction sequences indivisible so that
+ * the state of the CPSR flags aren't inadvertently modified
+ * just before or just after the call. By having these implemented
+ * as functions separated from the calling functions and having the
+ * asm directives just prior and after the fn dereference should do
+ * the trick due to ISO C sequence points forcing the updates.
+ */
+
+static long __kprobes emulate_2arg_rflags(long r0, long r1, long cpsr,
+ insn_2arg_fn_t *fn)
+{
+ __asm__("msr cpsr_fs, %0" : : "r" (cpsr) : "cc");
+ return fn(r0, r1);
+}
+
+
+static long __kprobes emulate_1arg_wflags(long r0, long *cpsr,
+ insn_1arg_fn_t *fn)
+{
+ long ret;
+ ret = fn(r0);
+ __asm__("mrs %0, cpsr" : "=r" (*cpsr));
+ return ret;
+}
+
+
+static long __kprobes emulate_2arg_wflags(long r0, long r1, long *cpsr,
+ insn_2arg_fn_t *fn)
+{
+ long ret;
+ ret = fn(r0, r1);
+ __asm__("mrs %0, cpsr" : "=r" (*cpsr));
+ return ret;
+}
+
+
+static long __kprobes emulate_3arg_wflags(long r0, long r1, long r2, long *cpsr,
+ insn_3arg_fn_t *fn)
+{
+ long ret;
+ ret = fn(r0, r1, r2);
+ __asm__("mrs %0, cpsr" : "=r" (*cpsr));
+ return ret;
+}
+
+
+static long __kprobes emulate_4arg_wflags(long r0, long r1, long r2, long r3,
+ long *cpsr,
+ insn_4arg_fn_t *fn)
+{
+ long ret;
+ ret = fn(r0, r1, r2, r3);
+ __asm__("mrs %0, cpsr" : "=r" (*cpsr));
+ return ret;
+}
+
+
+static long __kprobes emulate_1arg_rwflags(long r0, long *cpsr,
+ insn_1arg_fn_t *fn)
+{
+ long ret;
+ __asm__("msr cpsr_fs, %0" : : "r" (*cpsr) : "cc");
+ ret = fn(r0);
+ __asm__("mrs %0, cpsr" : "=r" (*cpsr));
+ return ret;
+}
+
+
+static long __kprobes emulate_3arg_rwflags(long r0, long r1, long r2,
+ long *cpsr,
+ insn_3arg_fn_t *fn)
+{
+ long ret;
+ __asm__("msr cpsr_fs, %0" : : "r" (*cpsr) : "cc");
+ ret = fn(r0, r1, r2);
+ __asm__("mrs %0, cpsr" : "=r" (*cpsr));
+ return ret;
+}
+
+
+/*
+ * To avoid having to the complications of mimicing single-stepping
+ * on a processor without a Next-PC or a single-step mode, and to
+ * avoid having to deal with the side-effects of boosting, we
+ * simulate or emulate (almost) all ARM instructions.
+ *
+ * "Simulation" is where the instruction's behavior is completely
+ * duplicated in C code. "Emulation" is where the original instruction
+ * is rewritten and executed, often by changing its registers.
+ *
+ * By having all behavior of the kprobe'd instruction completed before
+ * returning from the kprobe_handler(), all locks (scheduler and
+ * interrupt) can safely be released. There is no need for secondary
+ * breakpoints, no race with MP or preemptable kernels, nor having to
+ * clean up resources counts at a later time impacting overall system
+ * performance. By rewriting the instruction, only the minimum registers
+ * need to be loaded and saved back optimizing perfoamance.
+ *
+ * The {emulate|simulate}_* functions assume that ARM conditional
+ * exec flags have been checked against the CPSR prior to calling and
+ * that the PC has been incremented to point to the instruction
+ * following the kprobe'd instruction.
+ *
+ * Calling the emulate_*_flags version of a function doesn't hurt
+ * anything even when the CPSR flags aren't updated by the
+ * instruction. It's just a little slower in return for saving
+ * a little space by not having a duplicate function that doesn't
+ * update the flags. (The same optimization can be said for
+ * instructions that do or don't update the writeback register.)
+ * Also, instructions can either read the flags, only write the
+ * flags, or read and write the flags. To save combinations
+ * rather than for sheer performance, flag functions just assume
+ * read and write of flags.
+ */
+
+static void __kprobes simulate_bbl(struct kprobe *p, struct pt_regs *regs)
+{
+ 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 = (regs->ARM_cpsr & ~PSR_T_BIT) | ((rmv & 0x1) << 5);
+}
+
+
+static void __kprobes simulate_ldm1stm1(struct kprobe *p, struct pt_regs *regs)
+{
+ kprobe_opcode_t insn = p->opcode;
+ int rn = (insn >> 16) & 0xf;
+ int lbit = (insn >> 20) & 0x1;
+ int wbit = (insn >> 21) & 0x1;
+ int ubit = (insn >> 23) & 0x1;
+ int pbit = (insn >> 24) & 0x1;
+ long *addr = (long *)regs->uregs[rn];
+ int reg_bit_vector;
+ int reg_count;
+
+ reg_count = 0;
+ reg_bit_vector = insn & 0xffff;
+
+ while (reg_bit_vector) {
+ reg_bit_vector &= ~(1 << __ffs(reg_bit_vector));
+ ++reg_count;
+ }
+
+ if (!ubit) addr -= reg_count;
+ if (!(pbit ^ ubit)) addr += 1;
+
+ reg_bit_vector = insn & 0xffff;
+
+ while (reg_bit_vector) {
+ int reg = __ffs(reg_bit_vector);
+ reg_bit_vector &= ~(1 << reg);
+ if (lbit) {
+ regs->uregs[reg] = *addr++;
+ } else {
+ *addr++ = regs->uregs[reg];
+ }
+ }
+
+ if (wbit) {
+ if (!ubit) addr -= reg_count;
+ if (!(pbit ^ ubit)) addr -= 1;
+ regs->uregs[rn] = (long)addr;
+ }
+}
+
+
+static void __kprobes simulate_stm1_pc(struct kprobe *p, struct pt_regs *regs)
+{
+ regs->ARM_pc = (long)p->addr + kprobes_str_pc_offset;
+ simulate_ldm1stm1(p,regs);
+ regs->ARM_pc = (long)p->addr + 4;
+}
+
+
+static void __kprobes emulate_ldcstc(struct kprobe *p, struct pt_regs *regs)
+{
+ insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
+ kprobe_opcode_t insn = p->opcode;
+ int rn = (insn >> 16) & 0xf;
+ long rnv = regs->uregs[rn];
+
+ regs->uregs[rn] = i_fn(rnv); /* Save Rn in case of writeback. */
+}
+
+
+static void __kprobes emulate_ldrd(struct kprobe *p, struct pt_regs *regs)
+{
+ insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0];
+ kprobe_opcode_t insn = p->opcode;
+ int rd = (insn >> 12) & 0xf;
+ int rn = (insn >> 16) & 0xf;
+ int rm = insn & 0xf;
+ long rnv = regs->uregs[rn];
+ long rmv = regs->uregs[rm]; /* rm/rmv may be invalid, don't care. */
+
+ /* Not following the C calling convention here, so need asm(). */
+ __asm__("mov r0, %3 \n\t"
+ "mov r1, %4 \n\t"
+#if __LINUX_ARM_ARCH__ >= 5
+ "blx %5 \n\t"
+#else
+ "mov lr, pc \n\t"
+ "bx %5 \n\t"
+#endif
+ "str r0, %0 \n\t" /* Rn in case of writeback. */
+ "str r2, %1 \n\t" /* Rd */
+ "str r3, %2 \n\t" /* R(d+1) */
+ : "=m" (regs->uregs[rn]),
+ "=m" (regs->uregs[rd]),
+ "=m" (regs->uregs[rd+1])
+ : "r" (rnv), "r" (rmv), "r" (i_fn)
+ : "r0", "r1", "r2", "r3", "lr"
+ );
+}
+
+
+static void __kprobes emulate_strd(struct kprobe *p, struct pt_regs *regs)
+{
+ insn_4arg_fn_t *i_fn = (insn_4arg_fn_t *)&p->ainsn.insn[0];
+ kprobe_opcode_t insn = p->opcode;
+ int rd = (insn >> 12) & 0xf;
+ int rn = (insn >> 16) & 0xf;
+ int rm = insn & 0xf;
+ long rnv = regs->uregs[rn];
+ long rmv = regs->uregs[rm]; /* rm/rmv may be invalid, don't care. */
+
+ regs->uregs[rn] = i_fn(rnv, rmv, regs->uregs[rd], regs->uregs[rd+1]);
+}
+
+
+static void __kprobes emulate_ldr(struct kprobe *p, struct pt_regs *regs)
+{
+ insn_llret_3arg_fn_t *i_fn = (insn_llret_3arg_fn_t *)&p->ainsn.insn[0];
+ kprobe_opcode_t insn = p->opcode;
+ reg_pair fnr;
+ int rd = (insn >> 12) & 0xf;
+ int rn = (insn >> 16) & 0xf;
+ int rm = insn & 0xf;
+ long rdv;
+ long rnv = regs->uregs[rn];
+ long rmv = regs->uregs[rm]; /* rm/rmv may be invalid, don't care. */
+
+ fnr.dr = i_fn(rnv, 0, rmv);
+ regs->uregs[rn] = fnr.r0; /* Save Rn in case of writeback. */
+ rdv = fnr.r1;
+
+ if (rd == 15) {
+#if __LINUX_ARM_ARCH__ >= 5
+ rdv &= ~0x1;
+ regs->ARM_cpsr = (regs->ARM_cpsr & ~PSR_T_BIT) |
+ ((rdv & 0x1) << 5);
+#else
+ rdv &= ~0x2;
+#endif
+ }
+
+ regs->uregs[rd] = rdv;
+}
+
+
+static void __kprobes emulate_str(struct kprobe *p, struct pt_regs *regs)
+{
+ insn_3arg_fn_t *i_fn = (insn_3arg_fn_t *)&p->ainsn.insn[0];
+ kprobe_opcode_t insn = p->opcode;
+ long iaddr = (long)p->addr;
+ int rd = (insn >> 12) & 0xf;
+ int rn = (insn >> 16) & 0xf;
+ int rm = insn & 0xf;
+ long rdv = (rd == 15) ? iaddr + kprobes_str_pc_offset :
+ regs->uregs[rd];
+ long rnv = (rn == 15) ? iaddr + 8 : regs->uregs[rn];
+ long rmv = regs->uregs[rm]; /* rm/rmv may be invalid, don't care. */
+
+ /* Save Rn in case of writeback. */
+ regs->uregs[rn] = i_fn(rnv, rdv, rmv);
+}
+
+
+static void __kprobes emulate_mrrc(struct kprobe *p, struct pt_regs *regs)
+{
+ insn_0arg_fn_t *i_fn = (insn_0arg_fn_t *)&p->ainsn.insn[0];
+ kprobe_opcode_t insn = p->opcode;
+ reg_pair fnr;
+ int rd = (insn >> 12) & 0xf;
+ int rn = (insn >> 16) & 0xf;
+
+ fnr.dr = i_fn();
+ regs->uregs[rn] = fnr.r0;
+ regs->uregs[rd] = fnr.r1;
+}
+
+
+static void __kprobes emulate_mcrr(struct kprobe *p, struct pt_regs *regs)
+{
+ insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0];
+ kprobe_opcode_t insn = p->opcode;
+ int rd = (insn >> 12) & 0xf;
+ int rn = (insn >> 16) & 0xf;
+ long rnv = regs->uregs[rn];
+ long rdv = regs->uregs[rd];
+
+ i_fn(rnv, rdv);
+}
+
+
+static void __kprobes emulate_sat(struct kprobe *p, struct pt_regs *regs)
+{
+ insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
+ kprobe_opcode_t insn = p->opcode;
+ int rd = (insn >> 12) & 0xf;
+ int rm = insn & 0xf;
+ long rmv = regs->uregs[rm];
+ long newcpsr;
+
+ /* Writes Q flag */
+ regs->uregs[rd] = emulate_1arg_wflags(rmv, &newcpsr, i_fn);
+
+ regs->ARM_cpsr = (regs->ARM_cpsr & ~PSR_fs) | (newcpsr & PSR_fs);
+}
+
+
+static void __kprobes emulate_sel(struct kprobe *p, struct pt_regs *regs)
+{
+ insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0];
+ kprobe_opcode_t insn = p->opcode;
+ int rd = (insn >> 12) & 0xf;
+ int rn = (insn >> 16) & 0xf;
+ int rm = insn & 0xf;
+ long rnv = regs->uregs[rn];
+ long rmv = regs->uregs[rm];
+
+ /* Reads GE bits */
+ regs->uregs[rd] = emulate_2arg_rflags(rnv, rmv, regs->ARM_cpsr, i_fn);
+}
+
+
+static void __kprobes emulate_none(struct kprobe *p, struct pt_regs *regs)
+{
+ insn_0arg_fn_t *i_fn = (insn_0arg_fn_t *)&p->ainsn.insn[0];
+
+ i_fn();
+}
+
+
+static void __kprobes emulate_rd12(struct kprobe *p, struct pt_regs *regs)
+{
+ insn_0arg_fn_t *i_fn = (insn_0arg_fn_t *)&p->ainsn.insn[0];
+ kprobe_opcode_t insn = p->opcode;
+ int rd = (insn >> 12) & 0xf;
+
+ regs->uregs[rd] = i_fn();
+}
+
+
+static void __kprobes emulate_ird12(struct kprobe *p, struct pt_regs *regs)
+{
+ insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
+ kprobe_opcode_t insn = p->opcode;
+ int ird = (insn >> 12) & 0xf;
+
+ i_fn(regs->uregs[ird]);
+}
+
+
+static void __kprobes emulate_rn16(struct kprobe *p, struct pt_regs *regs)
+{
+ insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
+ kprobe_opcode_t insn = p->opcode;
+ int rn = (insn >> 16) & 0xf;
+ long rnv = regs->uregs[rn];
+
+ i_fn(rnv);
+}
+
+
+static void __kprobes emulate_rd12rm0(struct kprobe *p, struct pt_regs *regs)
+{
+ insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
+ kprobe_opcode_t insn = p->opcode;
+ int rd = (insn >> 12) & 0xf;
+ int rm = insn & 0xf;
+ long rmv = regs->uregs[rm];
+
+ regs->uregs[rd] = i_fn(rmv);
+}
+
+
+static void __kprobes emulate_rd12rn16rm0_wflags(struct kprobe *p,
+ struct pt_regs *regs)
+{
+ insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0];
+ kprobe_opcode_t insn = p->opcode;
+ int rd = (insn >> 12) & 0xf;
+ int rn = (insn >> 16) & 0xf;
+ int rm = insn & 0xf;
+ long rnv = regs->uregs[rn];
+ long rmv = regs->uregs[rm];
+ long newcpsr;
+
+ regs->uregs[rd] = emulate_2arg_wflags(rnv, rmv, &newcpsr, i_fn);
+
+ regs->ARM_cpsr = (regs->ARM_cpsr & ~PSR_fs) | (newcpsr & PSR_fs);
+}
+
+
+static void __kprobes emulate_rd16rn12rs8rm0_wflags(struct kprobe *p,
+ struct pt_regs *regs)
+{
+ insn_3arg_fn_t *i_fn = (insn_3arg_fn_t *)&p->ainsn.insn[0];
+ kprobe_opcode_t insn = p->opcode;
+ int rd = (insn >> 16) & 0xf;
+ int rn = (insn >> 12) & 0xf;
+ int rs = (insn >> 8) & 0xf;
+ int rm = insn & 0xf;
+ long rnv = regs->uregs[rn];
+ long rsv = regs->uregs[rs];
+ long rmv = regs->uregs[rm];
+ long newcpsr;
+
+ regs->uregs[rd] = emulate_3arg_wflags(rnv, rsv, rmv, &newcpsr, i_fn);
+
+ regs->ARM_cpsr = (regs->ARM_cpsr & ~PSR_fs) | (newcpsr & PSR_fs);
+}
+
+
+static void __kprobes emulate_rd16rs8rm0_wflags(struct kprobe *p,
+ struct pt_regs *regs)
+{
+ insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0];
+ kprobe_opcode_t insn = p->opcode;
+ int rd = (insn >> 16) & 0xf;
+ int rs = (insn >> 8) & 0xf;
+ int rm = insn & 0xf;
+ long rsv = regs->uregs[rs];
+ long rmv = regs->uregs[rm];
+ long newcpsr;
+
+ regs->uregs[rd] = emulate_2arg_wflags(rsv, rmv, &newcpsr, i_fn);
+
+ regs->ARM_cpsr = (regs->ARM_cpsr & ~PSR_fs) | (newcpsr & PSR_fs);
+}
+
+
+static void __kprobes emulate_rdhi16rdlo12rs8rm0_wflags(struct kprobe *p,
+ struct pt_regs *regs)
+{
+ insn_4arg_fn_t *i_fn = (insn_4arg_fn_t *)&p->ainsn.insn[0];
+ kprobe_opcode_t insn = p->opcode;
+ reg_pair fnr;
+ int rdhi = (insn >> 16) & 0xf;
+ int rdlo = (insn >> 12) & 0xf;
+ int rs = (insn >> 8) & 0xf;
+ int rm = insn & 0xf;
+ long rsv = regs->uregs[rs];
+ long rmv = regs->uregs[rm];
+ long newcpsr;
+
+ fnr.dr = emulate_4arg_wflags(regs->uregs[rdhi], regs->uregs[rdlo],
+ rsv, rmv, &newcpsr, i_fn);
+ regs->uregs[rdhi] = fnr.r0;
+ regs->uregs[rdlo] = fnr.r1;
+
+ regs->ARM_cpsr = (regs->ARM_cpsr & ~PSR_fs) | (newcpsr & PSR_fs);
+}
+
+
+static void __kprobes emulate_alu_imm_rwflags(struct kprobe *p,
+ struct pt_regs *regs)
+{
+ insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
+ kprobe_opcode_t insn = p->opcode;
+ int rd = (insn >> 12) & 0xf;
+ int rn = (insn >> 16) & 0xf;
+ long rnv = (rn == 15) ? (long)p->addr + 8 : regs->uregs[rn];
+ long oldcpsr = regs->ARM_cpsr;
+ long newcpsr = oldcpsr;
+
+ regs->uregs[rd] = emulate_1arg_rwflags(rnv, &newcpsr, i_fn);
+
+ if (insn & (1 << 20)) /* S bit */
+ regs->ARM_cpsr = (oldcpsr & ~PSR_fs) | (newcpsr & PSR_fs);
+}
+
+
+static void __kprobes emulate_alu_rwflags(struct kprobe *p,
+ struct pt_regs *regs)
+{
+ insn_3arg_fn_t *i_fn = (insn_3arg_fn_t *)&p->ainsn.insn[0];
+ kprobe_opcode_t insn = p->opcode;
+ long ppc = (long)p->addr + 8;
+ int rd = (insn >> 12) & 0xf;
+ int rn = (insn >> 16) & 0xf; /* rn/rnv/rs/rsv may be */
+ int rs = (insn >> 8) & 0xf; /* invalid, don't care. */
+ int rm = insn & 0xf;
+ long rnv = (rn == 15) ? ppc : regs->uregs[rn];
+ long rmv = (rm == 15) ? ppc : regs->uregs[rm];
+ long rsv = regs->uregs[rs];
+ long oldcpsr = regs->ARM_cpsr;
+ long newcpsr = oldcpsr;
+
+ regs->uregs[rd] = emulate_3arg_rwflags(rnv, rmv, rsv, &newcpsr, i_fn);
+
+ if (insn & (1 << 20)) /* S bit */
+ regs->ARM_cpsr = (oldcpsr & ~PSR_fs) | (newcpsr & PSR_fs);
+}
+
+
+
+static enum kprobe_insn __kprobes prep_emulate_ldr_str(kprobe_opcode_t insn,
+ struct arch_specific_insn *asi)
+{
+ int ibit = (insn & (1 << 26)) ? 25 : 22;
+
+ insn &= 0xfff00fff;
+ insn |= 0x00001000; /* Rn = r0, Rd = r1 */
+ if (insn & (1 << ibit)) {
+ insn &= ~0xf;
+ insn |= 2; /* Rm = r2 */
+ }
+ asi->insn[0] = insn;
+ asi->insn_handler = (insn & (1 << 20)) ? emulate_ldr : emulate_str;
+ return INSN_EMULATED;
+}
+
+static enum kprobe_insn __kprobes prep_emulate_rd12rm0(kprobe_opcode_t insn,
+ struct arch_specific_insn *asi)
+{
+ insn &= 0xffff0ff0; /* Rd = r0, Rm = r0 */
+ asi->insn[0] = insn;
+ asi->insn_handler = emulate_rd12rm0;
+ return INSN_EMULATED;
+}
+
+static enum kprobe_insn __kprobes prep_emulate_rd12(kprobe_opcode_t insn,
+ struct arch_specific_insn *asi)
+{
+ insn &= 0xffff0fff; /* Rd = r0 */
+ asi->insn[0] = insn;
+ asi->insn_handler = emulate_rd12;
+ return INSN_EMULATED;
+}
+
+static enum kprobe_insn __kprobes
+ prep_emulate_rd12rn16rm0_wflags(kprobe_opcode_t insn,
+ struct arch_specific_insn *asi)
+{
+ insn &= 0xfff00ff0; /* Rd = r0, Rn = r0 */
+ insn |= 0x00000001; /* Rm = r1 */
+ asi->insn[0] = insn;
+ asi->insn_handler = emulate_rd12rn16rm0_wflags;
+ return INSN_EMULATED;
+}
+
+static enum kprobe_insn __kprobes
+ prep_emulate_rd16rs8rm0_wflags(kprobe_opcode_t insn,
+ struct arch_specific_insn *asi)
+{
+ insn &= 0xfff0f0f0; /* Rd = r0, Rs = r0 */
+ insn |= 0x00000001; /* Rm = r1 */
+ asi->insn[0] = insn;
+ asi->insn_handler = emulate_rd16rs8rm0_wflags;
+ return INSN_EMULATED;
+}
+
+static enum kprobe_insn __kprobes
+ prep_emulate_rd16rn12rs8rm0_wflags(kprobe_opcode_t insn,
+ struct arch_specific_insn *asi)
+{
+ insn &= 0xfff000f0; /* Rd = r0, Rn = r0 */
+ insn |= 0x00000102; /* Rs = r1, Rm = r2 */
+ asi->insn[0] = insn;
+ asi->insn_handler = emulate_rd16rn12rs8rm0_wflags;
+ return INSN_EMULATED;
+}
+
+static enum kprobe_insn __kprobes
+ prep_emulate_rdhi16rdlo12rs8rm0_wflags(kprobe_opcode_t insn,
+ struct arch_specific_insn *asi)
+{
+ insn &= 0xfff000f0; /* RdHi = r0, RdLo = r1 */
+ insn |= 0x00001203; /* Rs = r2, Rm = r3 */
+ asi->insn[0] = insn;
+ asi->insn_handler = emulate_rdhi16rdlo12rs8rm0_wflags;
+ return INSN_EMULATED;
+}
+
+
+/*
+ * For the instruction masking and comparisons in all the "space_*"
+ * functions below, Do _not_ rearrange the order of tests unless
+ * you're very, very sure of what you are doing. For the sake of
+ * efficiency, the masks for some tests sometimes assume other test
+ * have been done prior to them so the number of patterns to test
+ * for an instruction set can be as broad as possible to reduce the
+ * number of tests needed.
+ */
+
+static enum kprobe_insn __kprobes space_1111(kprobe_opcode_t insn,
+ struct arch_specific_insn *asi)
+{
+ /* CPS mmod == 1 : 1111 0001 0000 xx10 xxxx xxxx xx0x xxxx
+ * RFE : 1111 100x x0x1 xxxx xxxx 1010 xxxx xxxx
+ * SRS : 1111 100x x1x0 1101 xxxx 0101 xxxx xxxx
+ */
+ if ( (insn & 0xfff30020) == 0xf1020000 ||
+ (insn & 0xfe500f00) == 0xf8100a00 ||
+ (insn & 0xfe5f0f00) == 0xf84d0500 )
+ return INSN_REJECTED;
+
+ /* PLD : 1111 01x1 x101 xxxx xxxx xxxx xxxx xxxx : */
+ if ( (insn & 0xfd700000) == 0xf4500000 ) {
+ insn &= 0xfff0ffff; /* Rn = r0 */
+
+ asi->insn[0] = insn;
+ asi->insn_handler = emulate_rn16;
+ return INSN_EMULATED;
+ }
+
+ /* BLX(1) : 1111 101x xxxx xxxx xxxx xxxx xxxx xxxx : */
+ if ( (insn & 0xfe000000) == 0xfa000000 ) {
+ asi->insn_handler = simulate_blx1;
+ return INSN_SIMULATED;
+ }
+
+ /* SETEND : 1111 0001 0000 0001 xxxx xxxx 0000 xxxx :
+ * CDP2 : 1111 1110 xxxx xxxx xxxx xxxx xxx0 xxxx :
+ */
+ if ( (insn & 0xffff00f0) == 0xf1010000 ||
+ (insn & 0xff000010) == 0xfe000000 ) {
+ asi->insn[0] = insn;
+ asi->insn_handler = emulate_none;
+ return INSN_EMULATED;
+ }
+
+ /* MCRR2 : 1111 1100 0100 xxxx xxxx xxxx xxxx xxxx : (Rd != Rn)
+ * MRRC2 : 1111 1100 0101 xxxx xxxx xxxx xxxx xxxx : (Rd != Rn)
+ */
+ if ( (insn & 0xffe00000) == 0xfc400000 ) {
+ insn &= 0xfff00fff; /* Rn = r0 */
+ insn |= 0x00001000; /* Rd = r1 */
+
+ asi->insn[0] = insn;
+ asi->insn_handler = (insn & (1 << 20)) ?
+ emulate_mrrc : emulate_mcrr; + return INSN_EMULATED;
+ }
+
+ /* LDC2 : 1111 110x xxx1 xxxx xxxx xxxx xxxx xxxx :
+ * STC2 : 1111 110x xxx0 xxxx xxxx xxxx xxxx xxxx :
+ */
+ if ( (insn & 0xfe000000) == 0xfc000000 ) {
+ insn &= 0xfff0ffff; /* Rn = r0 */
+
+ asi->insn[0] = insn;
+ asi->insn_handler = emulate_ldcstc;
+ return INSN_EMULATED;
+ }
+
+ /* MCR2 : 1111 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx :
+ * MRC2 : 1111 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx :
+ */
+ insn &= 0xffff0fff; /* Rd = r0 */
+
+ asi->insn[0] = insn;
+ asi->insn_handler = (insn & (1 << 20)) ? emulate_rd12 : emulate_ird12;
+ return INSN_EMULATED;
+}
+
+
+static enum kprobe_insn __kprobes space_cccc_000x(kprobe_opcode_t insn,
+ struct arch_specific_insn *asi)
+{
+ if ( (insn & 0x0f900010) == 0x01000000 ) {
+ /* cccc 0001 0xx0 xxxx xxxx xxxx xxxx xxx0 xxxx */
+
+ /* BXJ : cccc 0001 0010 xxxx xxxx xxxx 0010 xxxx
+ * MSR : cccc 0001 0x10 xxxx xxxx xxxx 0000 xxxx
+ */
+ if ( (insn & 0x0ff000f0) == 0x01200020 ||
+ (insn & 0x0fb000f0) == 0x01200000 )
+ return INSN_REJECTED;
+
+ /* MRS : cccc 0001 0x00 xxxx xxxx xxxx 0000 xxxx : */
+ if ( (insn & 0x0fb00010) == 0x01000000 )
+ return prep_emulate_rd12(insn, asi);
+
+ /* SMLALxy : cccc 0001 0100 xxxx xxxx xxxx 1xx0 xxxx : */
+ if ( (insn & 0x0ff00090) == 0x01400080 )
+ return prep_emulate_rdhi16rdlo12rs8rm0_wflags(insn, asi);
+
+ /* SMULWy : cccc 0001 0010 xxxx xxxx xxxx 1x10 xxxx :
+ * SMULxy : cccc 0001 0110 xxxx xxxx xxxx 1xx0 xxxx :
+ */
+ if ( (insn & 0x0ff000b0) == 0x012000a0 ||
+ (insn & 0x0ff00090) == 0x01600080 )
+ return prep_emulate_rd16rs8rm0_wflags(insn, asi);
+
+ /* SMLAxy : cccc 0001 0000 xxxx xxxx xxxx 1xx0 xxxx :Q
+ * SMLAWy : cccc 0001 0010 xxxx xxxx xxxx 0x00 xxxx :Q
+ */
+ return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi);
+
+ } else if ( (insn & 0x0f900090) == 0x01000010 ) {
+ /* cccc 0001 0xx0 xxxx xxxx xxxx xxxx 0xx1 xxxx */
+
+ /* BKPT : 1110 0001 0010 xxxx xxxx xxxx 0111 xxxx */
+ if ( (insn & 0xfff000f0) == 0xe1200070 )
+ return INSN_REJECTED;
+
+ /* BLX(2) : cccc 0001 0010 xxxx xxxx xxxx 0011 xxxx :
+ * BX : cccc 0001 0010 xxxx xxxx xxxx 0001 xxxx :
+ */
+
+ if ( (insn & 0x0ff000d0) == 0x01200010 ) {
+ asi->insn_handler = simulate_blx2bx;
+ return INSN_SIMULATED;
+ }
+
+ /* CLZ : cccc 0001 0110 xxxx xxxx xxxx 0001 xxxx : */
+ if ( (insn & 0x0ff000f0) == 0x01600010 )
+ return prep_emulate_rd12rm0(insn, asi);
+
+ /* QADD : cccc 0001 0000 xxxx xxxx xxxx 0101 xxxx :Q
+ * QSUB : cccc 0001 0010 xxxx xxxx xxxx 0101 xxxx :Q
+ * QDADD : cccc 0001 0100 xxxx xxxx xxxx 0101 xxxx :Q
+ * QDSUB : cccc 0001 0110 xxxx xxxx xxxx 0101 xxxx :Q
+ */
+ return prep_emulate_rd12rn16rm0_wflags(insn, asi);
+
+ } else if ( (insn & 0x0f000090) == 0x00000090 ) {
+ /* cccc 0000 xxxx xxxx xxxx xxxx xxxx 1001 xxxx */
+
+ /* MUL : cccc 0000 0000 xxxx xxxx xxxx 1001 xxxx :
+ * MULS : cccc 0000 0001 xxxx xxxx xxxx 1001 xxxx :cc
+ * MLA : cccc 0000 0010 xxxx xxxx xxxx 1001 xxxx :
+ * MLAS : cccc 0000 0011 xxxx xxxx xxxx 1001 xxxx :cc
+ * UMAAL : cccc 0000 0100 xxxx xxxx xxxx 1001 xxxx :
+ * UMULL : cccc 0000 1000 xxxx xxxx xxxx 1001 xxxx :
+ * UMULLS : cccc 0000 1001 xxxx xxxx xxxx 1001 xxxx :cc
+ * UMLAL : cccc 0000 1010 xxxx xxxx xxxx 1001 xxxx :
+ * UMLALS : cccc 0000 1011 xxxx xxxx xxxx 1001 xxxx :cc
+ * SMULL : cccc 0000 1100 xxxx xxxx xxxx 1001 xxxx :
+ * SMULLS : cccc 0000 1101 xxxx xxxx xxxx 1001 xxxx :cc
+ * SMLAL : cccc 0000 1110 xxxx xxxx xxxx 1001 xxxx :
+ * SMLALS : cccc 0000 1111 xxxx xxxx xxxx 1001 xxxx :cc
+ */
+
+ if ( (insn & 0x0fe000f0) == 0x00000090 ) {
+ return prep_emulate_rd16rs8rm0_wflags(insn, asi);
+ } else if ( (insn & 0x0fe000f0) == 0x00200090 ) {
+ return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi);
+ } else {
+ return prep_emulate_rdhi16rdlo12rs8rm0_wflags(insn, asi);
+ }
+
+ } else if ( (insn & 0x0e000090) == 0x00000090 ) {
+ /* cccc 000x xxxx xxxx xxxx xxxx xxxx 1xx1 xxxx */
+
+ /* SWP : cccc 0001 0000 xxxx xxxx xxxx 1001 xxxx :
+ * SWPB : cccc 0001 0100 xxxx xxxx xxxx 1001 xxxx :
+ * LDRD : cccc 000x xxx0 xxxx xxxx xxxx 1101 xxxx :
+ * STRD : cccc 000x xxx0 xxxx xxxx xxxx 1111 xxxx :
+ * STREX : cccc 0001 1000 xxxx xxxx xxxx 1001 xxxx :
+ * LDREX : cccc 0001 1001 xxxx xxxx xxxx 1001 xxxx :
+ * LDRH : cccc 000x xxx1 xxxx xxxx xxxx 1011 xxxx :
+ * STRH : cccc 000x xxx0 xxxx xxxx xxxx 1011 xxxx :
+ * LDRSB : cccc 000x xxx1 xxxx xxxx xxxx 1101 xxxx :
+ * LDRSH : cccc 000x xxx1 xxxx xxxx xxxx 1111 xxxx :
+ */
+
+ if ( (insn & 0x0fb000f0) == 0x01000090 ) { /* SWP/SWPB */
+ return prep_emulate_rd12rn16rm0_wflags(insn, asi);
+ } else if ( (insn & 0x0e1000d0) == 0x00000d0 ) { /* STRD/LDRD */
+ insn &= 0xfff00fff;
+ insn |= 0x00002000; /* Rn = r0, Rd = r2 */
+
+ if (insn & (1 << 22)) { /* I bit */
+ insn &= ~0xf;
+ insn |= 1; /* Rm = r1 */
+ }
+ asi->insn[0] = insn;
+ asi->insn_handler = (insn & (1 << 5)) ?
+ emulate_strd :
+ emulate_ldrd;
+ return INSN_EMULATED;
+ }
+
+ return prep_emulate_ldr_str(insn, asi);
+ }
+
+ /* cccc 000x xxxx xxxx xxxx xxxx xxxx xxxx xxxx */
+
+ /* ALU op with S bit and Rd == 15 :
+ * cccc 000x xxx1 xxxx 1111 xxxx xxxx xxxx
+ */
+ if ( (insn & 0x0e10f000) == 0x0010f000 )
+ return INSN_REJECTED;
+
+ /* Data processing: Immediate-shift / Register-shift
+ * ALU op : cccc 000x xxxx xxxx xxxx xxxx xxxx xxxx
+ * CPY : cccc 0001 1010 xxxx xxxx 0000 0000 xxxx
+ * MOV : cccc 0001 101x xxxx xxxx xxxx xxxx xxxx
+ * *S (bit 20) updates condition codes
+ * ADC/SBC/RSC reads the C flag
+ */
+
+ insn &= 0xfff00ff0; /* Rn = r0, Rd = r0 */
+ insn |= 0x00000001; /* Rm = r1 */
+
+ if (insn & 0x010) {
+ insn &= 0xfffff0ff; /* register shift */
+ insn |= 0x00000200; /* Rs = r2 */
+ }
+
+ asi->insn[0] = insn;
+ asi->insn_handler = emulate_alu_rwflags;
+ return INSN_EMULATED;
+}
+
+
+static enum kprobe_insn __kprobes space_cccc_001x(kprobe_opcode_t insn,
+ struct arch_specific_insn *asi)
+{
+ /* MSR : cccc 0011 0x10 xxxx xxxx xxxx xxxx xxxx
+ * Undef : cccc 0011 0x00 xxxx xxxx xxxx xxxx xxxx
+ * ALU op with S bit and Rd == 15 :
+ * cccc 001x xxx1 xxxx 1111 xxxx xxxx xxxx
+ */
+
+ if ( (insn & 0x0f900000) == 0x03200000 || /* MSR & Undef */
+ (insn & 0x0e10f000) == 0x0210f000 ) /* ALU s-bit, R15 */
+ return INSN_REJECTED;
+
+ /* Data processing: 32-bit Immediate
+ * ALU op : cccc 001x xxxx xxxx xxxx xxxx xxxx xxxx
+ * MOV : cccc 0011 101x xxxx xxxx xxxx xxxx xxxx
+ * *S (bit 20) updates condition codes
+ * ADC/SBC/RSC reads the C flag
+ */
+
+ insn &= 0xfff00ff0; /* Rn = r0, Rd = r0 */
+ asi->insn[0] = insn;
+ asi->insn_handler = emulate_alu_imm_rwflags;
+ return INSN_EMULATED;
+}
+
+
+static enum kprobe_insn __kprobes space_cccc_0110__1(kprobe_opcode_t insn,
+ struct arch_specific_insn *asi)
+{
+ /* SEL : cccc 0110 1000 xxxx xxxx xxxx 1011 xxxx GE: !!! */
+
+ if ( (insn & 0x0ff000f0) == 0x068000b0 ) {
+ insn &= 0xfff00ff0; /* Rd = r0, Rn = r0 */
+ insn |= 0x00000001; /* Rm = r1 */
+
+ asi->insn[0] = insn;
+ asi->insn_handler = emulate_sel;
+ return INSN_EMULATED;
+ }
+
+ /* SSAT : cccc 0110 101x xxxx xxxx xxxx xx01 xxxx :Q
+ * USAT : cccc 0110 111x xxxx xxxx xxxx xx01 xxxx :Q
+ * SSAT16 : cccc 0110 1010 xxxx xxxx xxxx 0011 xxxx :Q
+ * USAT16 : cccc 0110 1110 xxxx xxxx xxxx 0011 xxxx :Q
+ */
+
+ if ( (insn & 0x0fa00030) == 0x06a00010 ||
+ (insn & 0x0fb000f0) == 0x06a00030 ) {
+ insn &= 0xffff0ff0; /* Rd = r0, Rm = r0 */
+
+ asi->insn[0] = insn;
+ asi->insn_handler = emulate_sat;
+ return INSN_EMULATED;
+ }
+
+ /*
+ * REV : cccc 0110 1011 xxxx xxxx xxxx 0011 xxxx :
+ * REV16 : cccc 0110 1011 xxxx xxxx xxxx 1011 xxxx :
+ * REVSH : cccc 0110 1111 xxxx xxxx xxxx 1011 xxxx :
+ */
+
+ if ( (insn & 0x0ff00070) == 0x06b00030 ||
+ (insn & 0x0ff000f0) == 0x06f000b0 )
+ return prep_emulate_rd12rm0(insn, asi);
+
+ /* SADD16 : cccc 0110 0001 xxxx xxxx xxxx 0001 xxxx :GE
+ * SADDSUBX : cccc 0110 0001 xxxx xxxx xxxx 0011 xxxx :GE
+ * SSUBADDX : cccc 0110 0001 xxxx xxxx xxxx 0101 xxxx :GE
+ * SSUB16 : cccc 0110 0001 xxxx xxxx xxxx 0111 xxxx :GE
+ * SADD8 : cccc 0110 0001 xxxx xxxx xxxx 1001 xxxx :GE
+ * SSUB8 : cccc 0110 0001 xxxx xxxx xxxx 1111 xxxx :GE
+ * QADD16 : cccc 0110 0010 xxxx xxxx xxxx 0001 xxxx :
+ * QADDSUBX : cccc 0110 0010 xxxx xxxx xxxx 0011 xxxx :
+ * QSUBADDX : cccc 0110 0010 xxxx xxxx xxxx 0101 xxxx :
+ * QSUB16 : cccc 0110 0010 xxxx xxxx xxxx 0111 xxxx :
+ * QADD8 : cccc 0110 0010 xxxx xxxx xxxx 1001 xxxx :
+ * QSUB8 : cccc 0110 0010 xxxx xxxx xxxx 1111 xxxx :
+ * SHADD16 : cccc 0110 0011 xxxx xxxx xxxx 0001 xxxx :
+ * SHADDSUBX : cccc 0110 0011 xxxx xxxx xxxx 0011 xxxx :
+ * SHSUBADDX : cccc 0110 0011 xxxx xxxx xxxx 0101 xxxx :
+ * SHSUB16 : cccc 0110 0011 xxxx xxxx xxxx 0111 xxxx :
+ * SHADD8 : cccc 0110 0011 xxxx xxxx xxxx 1001 xxxx :
+ * SHSUB8 : cccc 0110 0011 xxxx xxxx xxxx 1111 xxxx :
+ * UADD16 : cccc 0110 0101 xxxx xxxx xxxx 0001 xxxx :GE
+ * UADDSUBX : cccc 0110 0101 xxxx xxxx xxxx 0011 xxxx :GE
+ * USUBADDX : cccc 0110 0101 xxxx xxxx xxxx 0101 xxxx :GE
+ * USUB16 : cccc 0110 0101 xxxx xxxx xxxx 0111 xxxx :GE
+ * UADD8 : cccc 0110 0101 xxxx xxxx xxxx 1001 xxxx :GE
+ * USUB8 : cccc 0110 0101 xxxx xxxx xxxx 1111 xxxx :GE
+ * UQADD16 : cccc 0110 0110 xxxx xxxx xxxx 0001 xxxx :
+ * UQADDSUBX : cccc 0110 0110 xxxx xxxx xxxx 0011 xxxx :
+ * UQSUBADDX : cccc 0110 0110 xxxx xxxx xxxx 0101 xxxx :
+ * UQSUB16 : cccc 0110 0110 xxxx xxxx xxxx 0111 xxxx :
+ * UQADD8 : cccc 0110 0110 xxxx xxxx xxxx 1001 xxxx :
+ * UQSUB8 : cccc 0110 0110 xxxx xxxx xxxx 1111 xxxx :
+ * UHADD16 : cccc 0110 0111 xxxx xxxx xxxx 0001 xxxx :
+ * UHADDSUBX : cccc 0110 0111 xxxx xxxx xxxx 0011 xxxx :
+ * UHSUBADDX : cccc 0110 0111 xxxx xxxx xxxx 0101 xxxx :
+ * UHSUB16 : cccc 0110 0111 xxxx xxxx xxxx 0111 xxxx :
+ * UHADD8 : cccc 0110 0111 xxxx xxxx xxxx 1001 xxxx :
+ * UHSUB8 : cccc 0110 0111 xxxx xxxx xxxx 1111 xxxx :
+ * PKHBT : cccc 0110 1000 xxxx xxxx xxxx x001 xxxx :
+ * PKHTB : cccc 0110 1000 xxxx xxxx xxxx x101 xxxx :
+ * SXTAB16 : cccc 0110 1000 xxxx xxxx xxxx 0111 xxxx :
+ * SXTB : cccc 0110 1010 xxxx xxxx xxxx 0111 xxxx :
+ * SXTAB : cccc 0110 1010 xxxx xxxx xxxx 0111 xxxx :
+ * SXTAH : cccc 0110 1011 xxxx xxxx xxxx 0111 xxxx :
+ * UXTAB16 : cccc 0110 1100 xxxx xxxx xxxx 0111 xxxx :
+ * UXTAB : cccc 0110 1110 xxxx xxxx xxxx 0111 xxxx :
+ * UXTAH : cccc 0110 1111 xxxx xxxx xxxx 0111 xxxx :
+ */
+
+ return prep_emulate_rd12rn16rm0_wflags(insn, asi);
+}
+
+static enum kprobe_insn __kprobes space_cccc_0111__1(kprobe_opcode_t insn,
+ struct arch_specific_insn *asi)
+{
+ /* Undef : cccc 0111 1111 xxxx xxxx xxxx 1111 xxxx */
+ if ( (insn & 0x0ff000f0) == 0x03f000f0 )
+ return INSN_REJECTED;
+
+ /* USADA8 : cccc 0111 1000 xxxx xxxx xxxx 0001 xxxx :
+ * USAD8 : cccc 0111 1000 xxxx 1111 xxxx 0001 xxxx :
+ */
+
+ if ( (insn & 0x0ff000f0) == 0x07800010 )
+ return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi);
+
+ /* SMLALD : cccc 0111 0100 xxxx xxxx xxxx 00x1 xxxx :
+ * SMLSLD : cccc 0111 0100 xxxx xxxx xxxx 01x1 xxxx :
+ */
+
+ if ( (insn & 0x0ff00090) == 0x07400010 )
+ return prep_emulate_rdhi16rdlo12rs8rm0_wflags(insn, asi);
+
+ /* SMLAD : cccc 0111 0000 xxxx xxxx xxxx 00x1 xxxx :Q
+ * SMLSD : cccc 0111 0000 xxxx xxxx xxxx 01x1 xxxx :Q
+ * SMMLA : cccc 0111 0101 xxxx xxxx xxxx 00x1 xxxx :
+ * SMMLS : cccc 0111 0101 xxxx xxxx xxxx 11x1 xxxx :
+ */
+
+ if ( (insn & 0x0ff00090) == 0x07000010 ||
+ (insn & 0x0ff000d0) == 0x07500010 ||
+ (insn & 0x0ff000d0) == 0x075000d0 )
+ return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi);
+
+ /* SMUSD : cccc 0111 0000 xxxx xxxx xxxx 01x1 xxxx :
+ * SMUAD : cccc 0111 0000 xxxx 1111 xxxx 00x1 xxxx :Q
+ * SMMUL : cccc 0111 0101 xxxx 1111 xxxx 00x1 xxxx :
+ */
+
+ return prep_emulate_rd16rs8rm0_wflags(insn, asi);
+}
+
+
+static enum kprobe_insn __kprobes space_cccc_01xx(kprobe_opcode_t insn,
+ struct arch_specific_insn *asi)
+{
+ /* LDR : cccc 01xx x0x1 xxxx xxxx xxxx xxxx xxxx :
+ * LDRB : cccc 01xx x1x1 xxxx xxxx xxxx xxxx xxxx :
+ * LDRBT : cccc 01x0 x111 xxxx xxxx xxxx xxxx xxxx :
+ * LDRT : cccc 01x0 x011 xxxx xxxx xxxx xxxx xxxx :
+ * STR : cccc 01xx x0x0 xxxx xxxx xxxx xxxx xxxx :
+ * STRB : cccc 01xx x1x0 xxxx xxxx xxxx xxxx xxxx :
+ * STRBT : cccc 01x0 x110 xxxx xxxx xxxx xxxx xxxx :
+ * STRT : cccc 01x0 x010 xxxx xxxx xxxx xxxx xxxx :
+ */
+
+ return prep_emulate_ldr_str(insn, asi);
+}
+
+
+static enum kprobe_insn __kprobes space_cccc_100x(kprobe_opcode_t insn,
+ struct arch_specific_insn *asi)
+{
+ /* LDM(2) : cccc 100x x101 xxxx 0xxx xxxx xxxx xxxx
+ * LDM(3) : cccc 100x x1x1 xxxx 1xxx xxxx xxxx xxxx
+ */
+
+ if ( (insn & 0x0e708000) == 0x85000000 ||
+ (insn & 0x0e508000) == 0x85010000 )
+ return INSN_REJECTED;
+
+ /* LDM(1) : cccc 100x x0x1 xxxx xxxx xxxx xxxx xxxx :
+ * STM(1) : cccc 100x x0x0 xxxx xxxx xxxx xxxx xxxx :
+ */
+
+ asi->insn_handler = ((insn & 0x108000) == 0x008000) ? /* STM & R15 */
+ simulate_stm1_pc : simulate_ldm1stm1;
+
+ return INSN_SIMULATED;
+}
+
+
+static enum kprobe_insn __kprobes space_cccc_101x(kprobe_opcode_t insn,
+ struct arch_specific_insn *asi)
+{
+ /* B : cccc 1010 xxxx xxxx xxxx xxxx xxxx xxxx :
+ * BL : cccc 1011 xxxx xxxx xxxx xxxx xxxx xxxx :
+ */
+
+ asi->insn_handler = simulate_bbl;
+
+ return INSN_SIMULATED;
+}
+
+
+static enum kprobe_insn __kprobes space_cccc_1100_010x(kprobe_opcode_t insn,
+ struct arch_specific_insn *asi)
+{
+ /* MCRR : cccc 1100 0100 xxxx xxxx xxxx xxxx xxxx : (Rd!=Rn)
+ * MRRC : cccc 1100 0101 xxxx xxxx xxxx xxxx xxxx : (Rd!=Rn)
+ */
+ insn &= 0xfff00fff;
+ insn |= 0x00001000; /* Rn = r0, Rd = r1 */
+ asi->insn[0] = insn;
+ asi->insn_handler = (insn & (1 << 20)) ? emulate_mrrc : emulate_mcrr; + return INSN_EMULATED;
+}
+
+
+static enum kprobe_insn __kprobes space_cccc_110x(kprobe_opcode_t insn,
+ struct arch_specific_insn *asi)
+{
+ /* LDC : cccc 110x xxx1 xxxx xxxx xxxx xxxx xxxx :
+ * STC : cccc 110x xxx0 xxxx xxxx xxxx xxxx xxxx :
+ */
+ insn &= 0xfff0ffff; /* Rn = r0 */
+ asi->insn[0] = insn;
+ asi->insn_handler = emulate_ldcstc;
+
+ return INSN_EMULATED;
+}
+
+
+static enum kprobe_insn __kprobes space_cccc_111x(kprobe_opcode_t insn,
+ struct arch_specific_insn *asi)
+{
+ /* BKPT : 1110 0001 0010 xxxx xxxx xxxx 0111 xxxx
+ * SWI : cccc 1111 xxxx xxxx xxxx xxxx xxxx xxxx
+ */
+ if ( (insn & 0xfff000f0) == 0xe1200070 ||
+ (insn & 0x0f000000) == 0x0f000000 )
+ return INSN_REJECTED;
+
+ /* CDP : cccc 1110 xxxx xxxx xxxx xxxx xxx0 xxxx : */
+ if ( (insn & 0x0f000010) == 0x0e000000 ) {
+ asi->insn[0] = insn;
+ asi->insn_handler = emulate_none;
+ return INSN_EMULATED;
+ }
+
+ /* MCR : cccc 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx :
+ * MRC : cccc 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx :
+ */
+ insn &= 0xffff0fff; /* Rd = r0 */
+ asi->insn[0] = insn;
+ asi->insn_handler = (insn & (1 << 20)) ? emulate_rd12 : emulate_ird12;
+ return INSN_EMULATED;
+}
+
+
+/* Return:
+ * INSN_REJECTED If instruction is one not allowed to kprobe,
+ * INSN_SIMULATED If insn slots go unneeded (instruction will be simulated
+ * in software only by its handler), and
+ * INSN_EMULATED if instruction is emulated and uses its insn slots.
+ *
+ * For instructions we don't want to kprobe (INSN_REJECTED return result):
+ * These are generally ones that modify the processor state making
+ * them "hard" to simulate such as switches processor modes or
+ * make accesses in alternate modes. Any of these could be simulated
+ * if the work was put into it, but low return considering they
+ * should also be very rare.
+ */
+
+enum kprobe_insn __kprobes kprobe_decode_insn(kprobe_opcode_t insn,
+ struct arch_specific_insn *asi)
+{
+ asi->insn[1] = KPROBE_RETURN_INSTRUCTION;
+
+ if ( (insn & 0xf0000000) == 0xf0000000 ) {
+
+ return space_1111(insn, asi);
+
+ } else if ( (insn & 0x0e000000) == 0x00000000 ) {
+
+ return space_cccc_000x(insn, asi);
+
+ } else if ( (insn & 0x0e000000) == 0x02000000 ) {
+
+ return space_cccc_001x(insn, asi);
+
+ } else if ( (insn & 0x0f000010) == 0x06000010 ) {
+
+ return space_cccc_0110__1(insn, asi);
+
+ } else if ( (insn & 0x0f000010) == 0x07000010 ) {
+
+ return space_cccc_0111__1(insn, asi);
+
+ } else if ( (insn & 0x0c000000) == 0x04000000 ) {
+
+ return space_cccc_01xx(insn, asi);
+
+ } else if ( (insn & 0x0e000000) == 0x08000000 ) {
+
+ return space_cccc_100x(insn, asi);
+
+ } else if ( (insn & 0x0e000000) == 0x0a000000 ) {
+
+ return space_cccc_101x(insn, asi);
+
+ } else if ( (insn & 0x0fe00000) == 0x0c400000 ) {
+
+ return space_cccc_1100_010x(insn, asi);
+
+ } else if ( (insn & 0x0e000000) == 0x0c400000 ) {
+
+ return space_cccc_110x(insn, asi);
+
+ }
+
+ return space_cccc_111x(insn, asi);
+}
+
+
+/*
+ * All ARM instructions listed below.
+ *
+ * Instructions and their general purpose registers are given.
+ * If a particular register may not use R15, it it prefixed with a "!".
+ * If it is parked with a "*" means the value returned by readering R15
+ * is implementation defined.
+ *
+ * ADC/ADD/AND/BIC/CMN/CMP/EOR/MOV/MVN/ORR/RSB/RSC/SBC/SUB/TEQ
+ * TST: Rd, Rn, Rm, !Rs
+ * BX: Rm
+ * BLX(2): !Rm
+ * BX: Rm (R15 legal, but discouraged)
+ * BXJ: !Rm,
+ * CLZ: !Rd, !Rm
+ * CPY: Rd, Rm
+ * LDC/2,STC/2 immediate offset & unindex: Rn
+ * LDC/2,STC/2 immediate pre/post-indexed: !Rn
+ * LDM(1/3): !Rn, register_list
+ * LDM(2): !Rn, !register_list
+ * LDR,STR,PLD immediate offset: Rd, Rn
+ * LDR,STR,PLD register offset: Rd, Rn, !Rm
+ * LDR,STR,PLD scaled register offset: Rd, !Rn, !Rm
+ * LDR,STR immediate pre/post-indexed: Rd, !Rn
+ * LDR,STR register pre/post-indexed: Rd, !Rn, !Rm
+ * LDR,STR scaled register pre/post-indexed: Rd, !Rn, !Rm
+ * LDRB,STRB immediate offset: !Rd, Rn
+ * LDRB,STRB register offset: !Rd, Rn, !Rm
+ * LDRB,STRB scaled register offset: !Rd, !Rn, !Rm
+ * LDRB,STRB immediate pre/post-indexed: !Rd, !Rn
+ * LDRB,STRB register pre/post-indexed: !Rd, !Rn, !Rm
+ * LDRB,STRB scaled register pre/post-indexed: !Rd, !Rn, !Rm
+ * LDRT,LDRBT,STRBT immediate pre/post-indexed: !Rd, !Rn
+ * LDRT,LDRBT,STRBT register pre/post-indexed: !Rd, !Rn, !Rm
+ * LDRT,LDRBT,STRBT scaled register pre/post-indexed: !Rd, !Rn, !Rm
+ * LDRH/SH/SB/D,STRH/SH/SB/D immediate offset: !Rd, Rn
+ * LDRH/SH/SB/D,STRH/SH/SB/D register offset: !Rd, Rn, !Rm
+ * LDRH/SH/SB/D,STRH/SH/SB/D immediate pre/post-indexed: !Rd, !Rn
+ * LDRH/SH/SB/D,STRH/SH/SB/D register pre/post-indexed: !Rd, !Rn, !Rm
+ * LDREX: !Rd, !Rn
+ * MCR/2: !Rd
+ * MCRR/2,MRRC/2: !Rd, !Rn
+ * MLA: !Rd, !Rn, !Rm, !Rs
+ * MOV: Rd
+ * MRC/2: !Rd (if Rd==15, only changes cond codes, not the register)
+ * MRS,MSR: !Rd
+ * MUL: !Rd, !Rm, !Rs
+ * PKH{BT,TB}: !Rd, !Rn, !Rm
+ * QDADD,[U]QADD/16/8/SUBX: !Rd, !Rm, !Rn
+ * QDSUB,[U]QSUB/16/8/ADDX: !Rd, !Rm, !Rn
+ * REV/16/SH: !Rd, !Rm
+ * RFE: !Rn
+ * {S,U}[H]ADD{16,8,SUBX},{S,U}[H]SUB{16,8,ADDX}: !Rd, !Rn, !Rm
+ * SEL: !Rd, !Rn, !Rm
+ * SMLA<x><y>,SMLA{D,W<y>},SMLSD,SMML{A,S}: !Rd, !Rn, !Rm, !Rs
+ * SMLAL<x><y>,SMLA{D,LD},SMLSLD,SMMULL,SMULW<y>: !RdHi, !RdLo, !Rm, !Rs
+ * SMMUL,SMUAD,SMUL<x><y>,SMUSD: !Rd, !Rm, !Rs
+ * SSAT/16: !Rd, !Rm
+ * STM(1/2): !Rn, register_list* (R15 in reg list not recommended)
+ * STRT immediate pre/post-indexed: Rd*, !Rn
+ * STRT register pre/post-indexed: Rd*, !Rn, !Rm
+ * STRT scaled register pre/post-indexed: Rd*, !Rn, !Rm
+ * STREX: !Rd, !Rn, !Rm
+ * SWP/B: !Rd, !Rn, !Rm
+ * {S,U}XTA{B,B16,H}: !Rd, !Rn, !Rm
+ * {S,U}XT{B,B16,H}: !Rd, !Rm
+ * UM{AA,LA,UL}L: !RdHi, !RdLo, !Rm, !Rs
+ * USA{D8,A8,T,T16}: !Rd, !Rm, !Rs
+ *
+ * May transfer control by writing R15 (possible mode changes or alternate
+ * mode accesses marked by "*"):
+ * ALU op (* with s-bit), B, BL, BKPT, BLX(1/2), BX, BXJ, CPS*, CPY,
+ * LDM(1), LDM(2/3)*, LDR, MOV, RFE*, SWI*
+ *
+ * Instructions that do not take general registers, nor transfer control:
+ * CDP/2, SETEND, SRS*
+ */
Index: linux-2.6.20.4/arch/arm/kernel/traps.c
===================================================================
--- linux-2.6.20.4/arch/arm/kernel/traps.c (.../vendor/usr/src) (revision 129)
+++ linux-2.6.20.4/arch/arm/kernel/traps.c (.../branches/kprobes/usr/src) (revision 129)
@@ -20,6 +20,7 @@
#include <linux/kallsyms.h>
#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/kprobes.h>


#include <asm/atomic.h>
#include <asm/cacheflush.h>
@@ -273,7 +274,7 @@ void unregister_undef_hook(struct undef_
	spin_unlock_irqrestore(&undef_lock, flags);
}

-asmlinkage void do_undefinstr(struct pt_regs *regs)
+asmlinkage void __kprobes do_undefinstr(struct pt_regs *regs)
{
	unsigned int correction = thumb_mode(regs) ? 2 : 4;
	unsigned int instr;
@@ -288,6 +289,19 @@ asmlinkage void do_undefinstr(struct pt_
	 */
	regs->ARM_pc -= correction;

+#ifdef CONFIG_KPROBES
+        /*
+         * Redirect kernel mode undef exceptions to kprobes.
+         */
+	if (!user_mode(regs) && !thumb_mode(regs)) {
+		if ( (*(unsigned long *)regs->ARM_pc ==
+			KPROBE_BREAKPOINT_INSTRUCTION) &&
+			kprobe_handler(regs) ) {
+				return;
+		}
+	}
+#endif /* CONFIG_KPROBES */
+
	pc = (void __user *)instruction_pointer(regs);
	if (thumb_mode(regs)) {
		get_user(instr, (u16 __user *)pc);
Index: linux-2.6.20.4/arch/arm/mm/fault.c
===================================================================
--- linux-2.6.20.4/arch/arm/mm/fault.c	(.../vendor/usr/src)	(revision 129)
+++ linux-2.6.20.4/arch/arm/mm/fault.c	(.../branches/kprobes/usr/src)	(revision 129)
@@ -13,11 +13,13 @@
#include <linux/ptrace.h>
#include <linux/mm.h>
#include <linux/init.h>
+#include <linux/kprobes.h>

#include <asm/system.h>
#include <asm/pgtable.h>
#include <asm/tlbflush.h>
#include <asm/uaccess.h>
+#include <asm/kdebug.h>

#include "fault.h"

@@ -73,6 +75,43 @@ void show_pte(struct mm_struct *mm, unsi
	printk("\n");
}

+#ifdef CONFIG_KPROBES
+ATOMIC_NOTIFIER_HEAD(notify_page_fault_chain);
+
+/* Hook to register for page fault notifications */
+int register_page_fault_notifier(struct notifier_block *nb)
+{
+	return atomic_notifier_chain_register(&notify_page_fault_chain, nb);
+}
+
+int unregister_page_fault_notifier(struct notifier_block *nb)
+{
+	return atomic_notifier_chain_unregister(&notify_page_fault_chain, nb);
+}
+
+static inline int notify_page_fault(enum die_val val, const char *str,
+					unsigned long addr,
+					struct pt_regs *regs,
+					unsigned int fsr)
+{
+	struct die_args args = {
+		.regs = regs,
+		.str = str,
+		.fsr = fsr
+	};
+
+	return atomic_notifier_call_chain(&notify_page_fault_chain, val, &args);
+}
+#else
+static inline int notify_page_fault(enum die_val val, const char *str,
+					unsigned long addr,
+					struct pt_regs *regs,
+					unsigned int fsr)
+{
+	return NOTIFY_DONE;
+}
+#endif
+
/*
 * Oops.  The kernel tried to access some page that wasn't present.
 */
@@ -226,6 +265,10 @@ do_page_fault(unsigned long addr, unsign
	tsk = current;
	mm  = tsk->mm;

+	if (notify_page_fault(DIE_PAGE_FAULT, "page fault", addr, regs,
+				fsr) == NOTIFY_STOP)
+		return 0;
+
	/*
	 * If we're in an interrupt or have no user
	 * context, we must not take the fault..
@@ -327,6 +370,10 @@ do_translation_fault(unsigned long addr,
	if (addr < TASK_SIZE)
		return do_page_fault(addr, fsr, regs);

+	if (notify_page_fault(DIE_TRANS_FAULT, "translation fault",
+				addr, regs, fsr) == NOTIFY_STOP)
+		return 0;
+
	index = pgd_index(addr);

/*


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]