All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] Kprobes support for MIPS architecture.
@ 2010-06-07 16:33 Himanshu Chauhan
  2010-06-07 16:34 ` [PATCH] MIPS: KProbes support v0.1 Himanshu Chauhan
  0 siblings, 1 reply; 5+ messages in thread
From: Himanshu Chauhan @ 2010-06-07 16:33 UTC (permalink / raw)
  To: ralf; +Cc: linux-kernel, linux-mips, Himanshu Chauhan

This is an initial draft of Kprobes support for MIPS architecture.
It is based on a patch series submitted on CELinux mailing list
from Sony for 2.6.16 series. Since then many things changed including
APIs and file paths. So this is essentially a patch replayed on
latest series.

Please review and provide your feedback.

Signed-off-by: Himanshu Chauhan <hschauhan@nulltrace.org>
---
Himanshu Chauhan (1):
  MIPS: KProbes support v0.1

 arch/mips/Kconfig               |   13 ++
 arch/mips/include/asm/kdebug.h  |    5 +
 arch/mips/include/asm/kprobes.h |   85 +++++++++
 arch/mips/kernel/Makefile       |    2 +
 arch/mips/kernel/genex.S        |    6 +
 arch/mips/kernel/kprobes.c      |  380 +++++++++++++++++++++++++++++++++++++++
 arch/mips/kernel/traps.c        |   40 ++++-
 arch/mips/mm/fault.c            |   11 +-
 8 files changed, 539 insertions(+), 3 deletions(-)
 create mode 100644 arch/mips/include/asm/kprobes.h
 create mode 100644 arch/mips/kernel/kprobes.c


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

* [PATCH] MIPS: KProbes support v0.1
  2010-06-07 16:33 [PATCH] Kprobes support for MIPS architecture Himanshu Chauhan
@ 2010-06-07 16:34 ` Himanshu Chauhan
  2010-06-07 19:41   ` David Daney
  0 siblings, 1 reply; 5+ messages in thread
From: Himanshu Chauhan @ 2010-06-07 16:34 UTC (permalink / raw)
  To: ralf; +Cc: linux-kernel, linux-mips, Himanshu Chauhan

Signed-off-by: Himanshu Chauhan <hschauhan@nulltrace.org>

---
 arch/mips/Kconfig               |   13 ++
 arch/mips/include/asm/kdebug.h  |    5 +
 arch/mips/include/asm/kprobes.h |   85 +++++++++
 arch/mips/kernel/Makefile       |    2 +
 arch/mips/kernel/genex.S        |    6 +
 arch/mips/kernel/kprobes.c      |  380 +++++++++++++++++++++++++++++++++++++++
 arch/mips/kernel/traps.c        |   40 ++++-
 arch/mips/mm/fault.c            |   11 +-
 8 files changed, 539 insertions(+), 3 deletions(-)
 create mode 100644 arch/mips/include/asm/kprobes.h
 create mode 100644 arch/mips/kernel/kprobes.c

diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index cdaae94..ba7cc87 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -2243,6 +2243,19 @@ source "kernel/power/Kconfig"
 
 endmenu
 
+menu "Instrumentation Support"
+
+config KPROBES
+       bool "MIPS Kprobes Support (Experimental)"
+       depends on EXPERIMENTAL && MODULES
+       help
+	  Kprobes allow you to trap at almost any kernel address
+	  and execute a callback function. Kprobes is useful for
+	  kernel debugging, non-intrusive instrumetation and testing.
+	  If in doubt, say N.
+
+endmenu
+
 source "arch/mips/kernel/cpufreq/Kconfig"
 
 source "net/Kconfig"
diff --git a/arch/mips/include/asm/kdebug.h b/arch/mips/include/asm/kdebug.h
index 5bf62aa..52818ac 100644
--- a/arch/mips/include/asm/kdebug.h
+++ b/arch/mips/include/asm/kdebug.h
@@ -8,6 +8,11 @@ enum die_val {
 	DIE_FP,
 	DIE_TRAP,
 	DIE_RI,
+#ifdef CONFIG_KPROBES
+	DIE_PAGE_FAULT,
+	DIE_BREAK,
+	DIE_SSTEPBP,
+#endif
 };
 
 #endif /* _ASM_MIPS_KDEBUG_H */
diff --git a/arch/mips/include/asm/kprobes.h b/arch/mips/include/asm/kprobes.h
new file mode 100644
index 0000000..0f647bf
--- /dev/null
+++ b/arch/mips/include/asm/kprobes.h
@@ -0,0 +1,85 @@
+/*
+ *  Kernel Probes (KProbes)
+ *  include/asm-mips/kprobes.h
+ *
+ *  Copyright 2006 Sony Corp.
+ *
+ *  Himanshu Chauhan <hschauhan@nulltrace.org>
+ *  for >2.6.35 kernels.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  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.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _ASM_KPROBES_H
+#define _ASM_KPROBES_H
+
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <asm/inst.h>
+
+#define  __ARCH_WANT_KPROBES_INSN_SLOT
+
+struct kprobe;
+struct pt_regs;
+
+typedef union mips_instruction kprobe_opcode_t;
+
+#define BREAKPOINT_INSTRUCTION		0x0000000d
+
+/* 
+ * We do not have hardware single-stepping on MIPS.
+ * So we implement software single-stepping with breakpoint
+ * trap 'break 5'.
+ */
+#define BREAKPOINT_INSTRUCTION_2	0x0000014d
+#define MAX_INSN_SIZE 			2
+
+#define flush_insn_slot(p)		do { \
+        /* invalidate I-cache */             \
+        asm volatile("cache 0, 0($0)");      \
+        /* invalidate D-cache */             \
+        asm volatile("cache 9, 0($0)");      \
+        } while(0);
+
+#define kretprobe_blacklist_size	0
+
+void arch_remove_kprobe(struct kprobe *p);
+
+/* Architecture specific copy of original instruction*/
+struct arch_specific_insn {
+	/* copy of the original instruction */
+	kprobe_opcode_t *insn;
+};
+
+struct prev_kprobe {
+	struct kprobe *kp;
+	unsigned long status;
+	unsigned long old_SR;
+	unsigned long saved_SR;
+	unsigned long saved_epc;
+};
+
+/* per-cpu kprobe control block */
+struct kprobe_ctlblk {
+	unsigned long kprobe_status;
+	unsigned long kprobe_old_SR;
+	unsigned long kprobe_saved_SR;
+	unsigned long kprobe_saved_epc;
+	struct prev_kprobe prev_kprobe;
+};
+
+extern int kprobe_exceptions_notify(struct notifier_block *self,
+				    unsigned long val, void *data);
+
+#endif				/* _ASM_KPROBES_H */
diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile
index 7a6ac50..714f3c1 100644
--- a/arch/mips/kernel/Makefile
+++ b/arch/mips/kernel/Makefile
@@ -83,6 +83,7 @@ obj-$(CONFIG_MIPS32_N32)	+= binfmt_elfn32.o scall64-n32.o signal_n32.o
 obj-$(CONFIG_MIPS32_O32)	+= binfmt_elfo32.o scall64-o32.o
 
 obj-$(CONFIG_KGDB)		+= kgdb.o
+obj-$(CONFIG_KPROBES)		+= kprobes.o
 obj-$(CONFIG_PROC_FS)		+= proc.o
 
 obj-$(CONFIG_64BIT)		+= cpu-bugs64.o
@@ -95,6 +96,7 @@ obj-$(CONFIG_KEXEC)		+= machine_kexec.o relocate_kernel.o
 obj-$(CONFIG_EARLY_PRINTK)	+= early_printk.o
 obj-$(CONFIG_SPINLOCK_TEST)	+= spinlock_test.o
 
+
 CFLAGS_cpu-bugs64.o	= $(shell if $(CC) $(KBUILD_CFLAGS) -Wa,-mdaddi -c -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-DHAVE_AS_SET_DADDI"; fi)
 
 obj-$(CONFIG_HAVE_STD_PC_SERIAL_PORT)	+= 8250-platform.o
diff --git a/arch/mips/kernel/genex.S b/arch/mips/kernel/genex.S
index 8882e57..e53ac80 100644
--- a/arch/mips/kernel/genex.S
+++ b/arch/mips/kernel/genex.S
@@ -450,7 +450,13 @@ NESTED(nmi_handler, PT_SIZE, sp)
 	BUILD_HANDLER ades ade ade silent		/* #5  */
 	BUILD_HANDLER ibe be cli silent			/* #6  */
 	BUILD_HANDLER dbe be cli silent			/* #7  */
+#ifndef CONFIG_KPROBES
+	/* call do_bp if bp hit and kprobes not configured */
 	BUILD_HANDLER bp bp sti silent			/* #9  */
+#else
+	/* call do_break if bp hit and kprobes are configured */
+	BUILD_HANDLER bp break sti silent		/* #9  */
+#endif
 	BUILD_HANDLER ri ri sti silent			/* #10 */
 	BUILD_HANDLER cpu cpu sti silent		/* #11 */
 	BUILD_HANDLER ov ov sti silent			/* #12 */
diff --git a/arch/mips/kernel/kprobes.c b/arch/mips/kernel/kprobes.c
new file mode 100644
index 0000000..0493791
--- /dev/null
+++ b/arch/mips/kernel/kprobes.c
@@ -0,0 +1,380 @@
+/*
+ *  Kernel Probes (KProbes)
+ *  arch/mips/kernel/kprobes.c
+ *
+ *  Copyright 2006 Sony Corp.
+ *
+ *  Himanshu Chauhan <hschauhan@nulltrace.org> for >2.6.35 kernels.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  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.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/semaphore.h>
+#include <linux/kdebug.h>
+#include <linux/kprobes.h>
+#include <linux/preempt.h>
+#include <asm/cacheflush.h>
+#include <asm/inst.h>
+#include <asm/ptrace.h>
+
+DECLARE_MUTEX(kprobe_mutex);
+DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL;
+DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
+
+int __kprobes arch_prepare_kprobe(struct kprobe *p)
+{
+	union mips_instruction insn;
+	int ret = 0;
+
+	insn = *p->addr;
+
+	switch (insn.i_format.opcode) {
+		/*
+		 * This group contains:
+		 * jr and jalr are in r_format format.
+		 */
+	case spec_op:
+
+		/*
+		 * This group contains:
+		 * bltz_op, bgez_op, bltzl_op, bgezl_op,
+		 * bltzal_op, bgezal_op, bltzall_op, bgezall_op.
+		 */
+	case bcond_op:
+
+		/*
+		 * These are unconditional and in j_format.
+		 */
+	case jal_op:
+	case j_op:
+
+		/*
+		 * These are conditional and in i_format.
+		 */
+	case beq_op:
+	case beql_op:
+	case bne_op:
+	case bnel_op:
+	case blez_op:
+	case blezl_op:
+	case bgtz_op:
+	case bgtzl_op:
+		/*
+		 * These are the FPA/cp1 branch instructions.
+		 */
+	case cop1_op:
+		printk("Kprobes for branch and jump instructions "
+		       "is not supported\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* insn: must be on special executable page on MIPS. */
+	p->ainsn.insn = get_insn_slot();
+	if (!p->ainsn.insn) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	/*
+	 * In the kprobe->ainsn.insn[] array we store the original
+	 * instruction at index zero and a break trap instruction at
+	 * index one.
+	 */
+
+	memcpy(&p->ainsn.insn[0], p->addr, sizeof(kprobe_opcode_t));
+	p->ainsn.insn[1].word = BREAKPOINT_INSTRUCTION_2;
+	p->opcode = *p->addr;
+
+out:
+	return ret;
+}
+
+void __kprobes arch_arm_kprobe(struct kprobe *p)
+{
+	p->addr->word = BREAKPOINT_INSTRUCTION;
+	flush_icache_range((unsigned long)p->addr,
+			   (unsigned long)p->addr +
+			   (MAX_INSN_SIZE * sizeof(kprobe_opcode_t)));
+}
+
+void __kprobes arch_disarm_kprobe(struct kprobe *p)
+{
+	*p->addr = p->opcode;
+	flush_icache_range((unsigned long)p->addr,
+			   (unsigned long)p->addr +
+			   (MAX_INSN_SIZE * sizeof(kprobe_opcode_t)));
+}
+
+void __kprobes arch_remove_kprobe(struct kprobe *p)
+{
+	down(&kprobe_mutex);
+	free_insn_slot(p->ainsn.insn, 0);
+	up(&kprobe_mutex);
+}
+
+static inline void save_previous_kprobe(struct kprobe_ctlblk *kcb)
+{
+	kcb->prev_kprobe.kp = kprobe_running();
+	kcb->prev_kprobe.status = kcb->kprobe_status;
+	kcb->prev_kprobe.old_SR = kcb->kprobe_old_SR;
+	kcb->prev_kprobe.saved_SR = kcb->kprobe_saved_SR;
+	kcb->prev_kprobe.saved_epc = kcb->kprobe_saved_epc;
+}
+
+static inline void restore_previous_kprobe(struct kprobe_ctlblk *kcb)
+{
+	__get_cpu_var(current_kprobe) = kcb->prev_kprobe.kp;
+	kcb->kprobe_status = kcb->prev_kprobe.status;
+	kcb->kprobe_old_SR = kcb->prev_kprobe.old_SR;
+	kcb->kprobe_saved_SR = kcb->prev_kprobe.saved_SR;
+	kcb->kprobe_saved_epc = kcb->prev_kprobe.saved_epc;
+}
+
+static inline void set_current_kprobe(struct kprobe *p, struct pt_regs *regs,
+				      struct kprobe_ctlblk *kcb)
+{
+	__get_cpu_var(current_kprobe) = p;
+	kcb->kprobe_saved_SR = kcb->kprobe_old_SR = (regs->cp0_status & ST0_IE);
+	kcb->kprobe_saved_epc = regs->cp0_epc;
+}
+
+static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
+{
+	regs->cp0_status &= ~ST0_IE;
+
+	/* single step inline if the instruction is an break 0 */
+	if ((MIPSInst_OPCODE(p->opcode.word) == BREAKPOINT_INSTRUCTION)
+	    || (MIPSInst_OPCODE(p->opcode.word) == BREAKPOINT_INSTRUCTION_2))
+		regs->cp0_epc = (unsigned long)p->addr;
+	else
+		regs->cp0_epc = (unsigned long)&p->ainsn.insn[0];
+}
+
+static int __kprobes kprobe_handler(struct pt_regs *regs)
+{
+	struct kprobe *p;
+	int ret = 0;
+	kprobe_opcode_t *addr = NULL;
+	struct kprobe_ctlblk *kcb;
+
+	addr = (kprobe_opcode_t *) regs->cp0_epc;
+
+	/*
+	 * We don't want to be preempted for the entire
+	 * duration of kprobe processing
+	 */
+	preempt_disable();
+	kcb = get_kprobe_ctlblk();
+
+	/* Check we're not actually recursing */
+	if (kprobe_running()) {
+		p = get_kprobe(addr);
+		if (p) {
+			if (kcb->kprobe_status == KPROBE_HIT_SS &&
+			    p->ainsn.insn->word == BREAKPOINT_INSTRUCTION) {
+				regs->cp0_status &= ~ST0_IE;
+				regs->cp0_status |= kcb->kprobe_saved_SR;
+				goto no_kprobe;
+			}
+			/* We have reentered the kprobe_handler(), since
+			 * another probe was hit while within the handler.
+			 * We here save the original kprobes variables and
+			 * just single step on the instruction of the new probe
+			 * without calling any user handlers.
+			 */
+			save_previous_kprobe(kcb);
+			set_current_kprobe(p, regs, kcb);
+			kprobes_inc_nmissed_count(p);
+			prepare_singlestep(p, regs);
+			kcb->kprobe_status = KPROBE_REENTER;
+			return 1;
+		} else {
+			if (addr->word != BREAKPOINT_INSTRUCTION) {
+				/* The breakpoint instruction was removed by
+				 * another cpu right after we hit, no further
+				 * handling of this interrupt is appropriate
+				 */
+				ret = 1;
+				goto no_kprobe;
+			}
+			p = __get_cpu_var(current_kprobe);
+			if (p->break_handler && p->break_handler(p, regs)) {
+				goto ss_probe;
+			}
+		}
+		goto no_kprobe;
+	}
+
+	p = get_kprobe(addr);
+	if (!p) {
+		if (addr->word != BREAKPOINT_INSTRUCTION) {
+			/*
+			 * The breakpoint instruction was removed right
+			 * after we hit it.  Another cpu has removed
+			 * either a probepoint or a debugger breakpoint
+			 * at this address.  In either case, no further
+			 * handling of this interrupt is appropriate.
+			 */
+			ret = 1;
+		}
+		/* Not one of ours: let kernel handle it */
+		goto no_kprobe;
+	}
+
+	set_current_kprobe(p, regs, kcb);
+	kcb->kprobe_status = KPROBE_HIT_ACTIVE;
+
+	if (p->pre_handler && p->pre_handler(p, regs))
+		/* handler has already set things up, so skip ss setup */
+		return 1;
+
+ss_probe:
+	prepare_singlestep(p, regs);
+	kcb->kprobe_status = KPROBE_HIT_SS;
+	return 1;
+
+no_kprobe:
+	preempt_enable_no_resched();
+	return ret;
+}
+
+/*
+ * Called after single-stepping.  p->addr is the address of the
+ * instruction whose first byte has been replaced by the "break 0"
+ * instruction.  To avoid the SMP problems that can occur when we
+ * temporarily put back the original opcode to single-step, we
+ * single-stepped a copy of the instruction.  The address of this
+ * copy is p->ainsn.insn.
+ *
+ * This function prepares to return from the post-single-step
+ * breakpoint trap.
+ */
+static void __kprobes resume_execution(struct kprobe *p,
+				       struct pt_regs *regs,
+				       struct kprobe_ctlblk *kcb)
+{
+	unsigned long orig_epc = kcb->kprobe_saved_epc;
+	regs->cp0_epc = orig_epc + 4;
+}
+
+static inline int post_kprobe_handler(struct pt_regs *regs)
+{
+	struct kprobe *cur = kprobe_running();
+	struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+
+	if (!cur)
+		return 0;
+
+	if ((kcb->kprobe_status != KPROBE_REENTER) && cur->post_handler) {
+		kcb->kprobe_status = KPROBE_HIT_SSDONE;
+		cur->post_handler(cur, regs, 0);
+	}
+
+	resume_execution(cur, regs, kcb);
+
+	regs->cp0_status |= kcb->kprobe_saved_SR;
+
+	/* Restore back the original saved kprobes variables and continue. */
+	if (kcb->kprobe_status == KPROBE_REENTER) {
+		restore_previous_kprobe(kcb);
+		goto out;
+	}
+	reset_current_kprobe();
+out:
+	preempt_enable_no_resched();
+
+	return 1;
+}
+
+static inline int kprobe_fault_handler(struct pt_regs *regs, int trapnr)
+{
+	struct kprobe *cur = kprobe_running();
+	struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+
+	if (cur->fault_handler && cur->fault_handler(cur, regs, trapnr))
+		return 1;
+
+	if (kcb->kprobe_status & KPROBE_HIT_SS) {
+		resume_execution(cur, regs, kcb);
+		regs->cp0_status |= kcb->kprobe_old_SR;
+
+		reset_current_kprobe();
+		preempt_enable_no_resched();
+	}
+	return 0;
+}
+
+/*
+ * Wrapper routine 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 = (struct pt_regs *)args->regs;
+	int ret = NOTIFY_DONE;
+	int trapnr = 0;
+
+	switch (val) {
+	case DIE_BREAK:
+                ret = kprobe_handler(regs);
+		if (ret)
+			ret = NOTIFY_STOP;
+		break;
+	case DIE_SSTEPBP:
+		if (post_kprobe_handler(args->regs))
+			ret = NOTIFY_STOP;
+		break;
+
+	case DIE_PAGE_FAULT:
+		/* kprobe_running() needs smp_processor_id() */
+		preempt_disable();
+
+		/*
+		 * trapnr is the architecture-specific trap
+		 * number associated with the fault while handling
+		 * the Kprobe (e.g. on mips, 9 for a break or 13 for
+		 * a trap).
+		 * current die_args structure does not have trapnr.
+		 */
+		if (kprobe_running()
+		    && kprobe_fault_handler(args->regs, trapnr))
+			ret = NOTIFY_STOP;
+		preempt_enable();
+		break;
+	default:
+		break;
+	}
+	return ret;
+}
+
+int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
+{
+	return 0;
+}
+
+void __kprobes jprobe_return(void)
+{
+}
+
+int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
+{
+	return 0;
+}
+
+int __init arch_init_kprobes(void)
+{
+	return 0;
+}
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index 8bdd6a6..f6b4b41 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -27,6 +27,7 @@
 #include <linux/kdebug.h>
 #include <linux/notifier.h>
 #include <linux/kdb.h>
+#include <linux/kprobes.h>
 
 #include <asm/bootinfo.h>
 #include <asm/branch.h>
@@ -334,7 +335,7 @@ void show_regs(struct pt_regs *regs)
 	__show_regs((struct pt_regs *)regs);
 }
 
-void show_registers(const struct pt_regs *regs)
+void show_registers(struct pt_regs *regs)
 {
 	const int field = 2 * sizeof(unsigned long);
 
@@ -790,6 +791,43 @@ out_sigsegv:
 	force_sig(SIGSEGV, current);
 }
 
+#ifdef CONFIG_KPROBES
+asmlinkage void __kprobes do_break (struct pt_regs *regs)
+{
+	unsigned int opcode, bcode;
+
+	opcode = *(unsigned long *)(regs->cp0_epc);
+
+	bcode = ((opcode >> 6) & ((1 << 20) - 1));
+	if (bcode < (1 << 10))
+		bcode <<= 10;
+
+	/*
+	 * notify the kprobe handlers,if instruction is break 0 or break 5
+	 */
+	switch (bcode) {
+	case BRK_USERBP << 10:
+		if (notify_die(DIE_BREAK, "debug", regs, bcode, 0, 0) == NOTIFY_STOP)
+			return;
+		else
+			break;
+	case BRK_SSTEPBP << 10:
+		if (notify_die(DIE_SSTEPBP, "single_step", regs, bcode, 0, 0) == NOTIFY_STOP)
+			return;
+		else
+			break;
+	default:
+		break;
+	}
+
+	/*
+	 * If the bcode is other than 0 and 5, then call the normal
+	 * break handler do_bp()
+	 */
+	do_bp(regs);
+}
+#endif
+
 asmlinkage void do_tr(struct pt_regs *regs)
 {
 	unsigned int opcode, tcode = 0;
diff --git a/arch/mips/mm/fault.c b/arch/mips/mm/fault.c
index b78f7d9..86e2d27 100644
--- a/arch/mips/mm/fault.c
+++ b/arch/mips/mm/fault.c
@@ -18,6 +18,8 @@
 #include <linux/smp.h>
 #include <linux/vt_kern.h>		/* For unblank_screen() */
 #include <linux/module.h>
+#include <linux/kprobes.h>
+#include <linux/kdebug.h>		/* notify_die and asm/kdebug.h */
 
 #include <asm/branch.h>
 #include <asm/mmu_context.h>
@@ -31,8 +33,8 @@
  * and the problem, and then passes it off to one of the appropriate
  * routines.
  */
-asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long write,
-			      unsigned long address)
+asmlinkage void __kprobes do_page_fault(struct pt_regs *regs, unsigned long write,
+                                        unsigned long address)
 {
 	struct vm_area_struct * vma = NULL;
 	struct task_struct *tsk = current;
@@ -47,6 +49,11 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long write,
 	       field, regs->cp0_epc);
 #endif
 
+	/* Notify kprobes fault handler. */
+        if (notify_die(DIE_PAGE_FAULT, "page fault",
+                       regs, -1, SEGV_MAPERR, SEGV_MAPERR) == NOTIFY_STOP)
+                return;
+
 	info.si_code = SEGV_MAPERR;
 
 	/*
-- 
1.7.0.4


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

* Re: [PATCH] MIPS: KProbes support v0.1
  2010-06-07 16:34 ` [PATCH] MIPS: KProbes support v0.1 Himanshu Chauhan
@ 2010-06-07 19:41   ` David Daney
  2010-06-08 17:51     ` Himanshu Chauhan
  0 siblings, 1 reply; 5+ messages in thread
From: David Daney @ 2010-06-07 19:41 UTC (permalink / raw)
  To: Himanshu Chauhan; +Cc: ralf, linux-kernel, linux-mips

I have a few questions and comments below.  Many of them  may be due to 
my lack of understanding about the internals of KProbes.

David Daney.


On 06/07/2010 09:34 AM, Himanshu Chauhan wrote:
[...]
> diff --git a/arch/mips/include/asm/kdebug.h b/arch/mips/include/asm/kdebug.h
> index 5bf62aa..52818ac 100644
> --- a/arch/mips/include/asm/kdebug.h
> +++ b/arch/mips/include/asm/kdebug.h
> @@ -8,6 +8,11 @@ enum die_val {
>   	DIE_FP,
>   	DIE_TRAP,
>   	DIE_RI,
> +#ifdef CONFIG_KPROBES
> +	DIE_PAGE_FAULT,
> +	DIE_BREAK,
> +	DIE_SSTEPBP,
> +#endif
>   };
>

It might be cleaner without the #ifdef.  These are enum value 
definitions, so it doesn't affect code size.


Can you also explain how the die notifier chain interacts with KProbes 
and why it cannot be a seperate notifier chain?

>   #endif /* _ASM_MIPS_KDEBUG_H */
> diff --git a/arch/mips/include/asm/kprobes.h b/arch/mips/include/asm/kprobes.h
> new file mode 100644
> index 0000000..0f647bf
> --- /dev/null
> +++ b/arch/mips/include/asm/kprobes.h
> @@ -0,0 +1,85 @@
[...]
> +
> +#define BREAKPOINT_INSTRUCTION		0x0000000d
> +
> +/*
> + * We do not have hardware single-stepping on MIPS.
> + * So we implement software single-stepping with breakpoint
> + * trap 'break 5'.
> + */
> +#define BREAKPOINT_INSTRUCTION_2	0x0000014d

The BREAK codes are defined in asm/break.h  This should be added there 
instead.

Why do you use codes (0 and 5) that are already kind of reserved for 
user space debuggers?

> +#define MAX_INSN_SIZE 			2
> +
> +#define flush_insn_slot(p)		do { \
> +        /* invalidate I-cache */             \
> +        asm volatile("cache 0, 0($0)");      \
> +        /* invalidate D-cache */             \
> +        asm volatile("cache 9, 0($0)");      \
> +        } while(0);
> +

You have to call a function in arch/mips/mm/c-* to do this, you cannot 
open code with CACHE instructions as you need to handle CPU quirks and 
SMP.  It is possible that flush_icache_range() or flush_cache_sigtramp() 
would work.  Or we might need something new.

I see you use flush_icache_range() below, why have this definition, it 
looks unused?


[...]
> diff --git a/arch/mips/kernel/genex.S b/arch/mips/kernel/genex.S
> index 8882e57..e53ac80 100644
> --- a/arch/mips/kernel/genex.S
> +++ b/arch/mips/kernel/genex.S
> @@ -450,7 +450,13 @@ NESTED(nmi_handler, PT_SIZE, sp)
>   	BUILD_HANDLER ades ade ade silent		/* #5  */
>   	BUILD_HANDLER ibe be cli silent			/* #6  */
>   	BUILD_HANDLER dbe be cli silent			/* #7  */
> +#ifndef CONFIG_KPROBES
> +	/* call do_bp if bp hit and kprobes not configured */
>   	BUILD_HANDLER bp bp sti silent			/* #9  */
> +#else
> +	/* call do_break if bp hit and kprobes are configured */
> +	BUILD_HANDLER bp break sti silent		/* #9  */
> +#endif

Why this ugliness?  Can't you handle it in do_bp() or  do_trap_or_bp()?


>   	BUILD_HANDLER ri ri sti silent			/* #10 */
>   	BUILD_HANDLER cpu cpu sti silent		/* #11 */
>   	BUILD_HANDLER ov ov sti silent			/* #12 */
> diff --git a/arch/mips/kernel/kprobes.c b/arch/mips/kernel/kprobes.c
> new file mode 100644
> index 0000000..0493791
> --- /dev/null
> +++ b/arch/mips/kernel/kprobes.c
> @@ -0,0 +1,380 @@
[...]
> +
> +int __kprobes arch_prepare_kprobe(struct kprobe *p)
> +{
> +	union mips_instruction insn;
> +	int ret = 0;
> +
> +	insn = *p->addr;
> +
> +	switch (insn.i_format.opcode) {
> +		/*
> +		 * This group contains:
> +		 * jr and jalr are in r_format format.
> +		 */
> +	case spec_op:
> +
> +		/*
> +		 * This group contains:
> +		 * bltz_op, bgez_op, bltzl_op, bgezl_op,
> +		 * bltzal_op, bgezal_op, bltzall_op, bgezall_op.
> +		 */
> +	case bcond_op:
> +
> +		/*
> +		 * These are unconditional and in j_format.
> +		 */
> +	case jal_op:
> +	case j_op:
> +
> +		/*
> +		 * These are conditional and in i_format.
> +		 */
> +	case beq_op:
> +	case beql_op:
> +	case bne_op:
> +	case bnel_op:
> +	case blez_op:
> +	case blezl_op:
> +	case bgtz_op:
> +	case bgtzl_op:

Need to add or otherwise handle:


#ifdef CONFIG_CPU_CAVIUM_OCTEON
	case lwc2_op: /* This is bbit0 on Octeon */
	case ldc2_op: /* This is bbit032 on Octeon */
	case swc2_op: /* This is bbit1 on Octeon */
	case sdc2_op: /* This is bbit132 on Octeon */
#endif


[...]

> diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
> index 8bdd6a6..f6b4b41 100644
> --- a/arch/mips/kernel/traps.c
> +++ b/arch/mips/kernel/traps.c
> @@ -27,6 +27,7 @@
>   #include<linux/kdebug.h>
>   #include<linux/notifier.h>
>   #include<linux/kdb.h>
> +#include<linux/kprobes.h>
>
>   #include<asm/bootinfo.h>
>   #include<asm/branch.h>
> @@ -334,7 +335,7 @@ void show_regs(struct pt_regs *regs)
>   	__show_regs((struct pt_regs *)regs);
>   }
>
> -void show_registers(const struct pt_regs *regs)
> +void show_registers(struct pt_regs *regs)
>   {
>   	const int field = 2 * sizeof(unsigned long);
>
> @@ -790,6 +791,43 @@ out_sigsegv:
>   	force_sig(SIGSEGV, current);
>   }
>
> +#ifdef CONFIG_KPROBES
> +asmlinkage void __kprobes do_break (struct pt_regs *regs)
> +{
> +	unsigned int opcode, bcode;
> +
> +	opcode = *(unsigned long *)(regs->cp0_epc);
> +
> +	bcode = ((opcode>>  6)&  ((1<<  20) - 1));
> +	if (bcode<  (1<<  10))
> +		bcode<<= 10;
> +
> +	/*
> +	 * notify the kprobe handlers,if instruction is break 0 or break 5
> +	 */
> +	switch (bcode) {
> +	case BRK_USERBP<<  10:
> +		if (notify_die(DIE_BREAK, "debug", regs, bcode, 0, 0) == NOTIFY_STOP)
> +			return;
> +		else
> +			break;
> +	case BRK_SSTEPBP<<  10:
> +		if (notify_die(DIE_SSTEPBP, "single_step", regs, bcode, 0, 0) == NOTIFY_STOP)
> +			return;
> +		else
> +			break;
> +	default:
> +		break;
> +	}
> +


This should be folded into do_bp().



> +	/*
> +	 * If the bcode is other than 0 and 5, then call the normal
> +	 * break handler do_bp()
> +	 */
> +	do_bp(regs);
> +}
> +#endif
> +
>   asmlinkage void do_tr(struct pt_regs *regs)
>   {
>   	unsigned int opcode, tcode = 0;
> diff --git a/arch/mips/mm/fault.c b/arch/mips/mm/fault.c
> index b78f7d9..86e2d27 100644
> --- a/arch/mips/mm/fault.c
> +++ b/arch/mips/mm/fault.c
> @@ -18,6 +18,8 @@
>   #include<linux/smp.h>
>   #include<linux/vt_kern.h>		/* For unblank_screen() */
>   #include<linux/module.h>
> +#include<linux/kprobes.h>
> +#include<linux/kdebug.h>		/* notify_die and asm/kdebug.h */
>
>   #include<asm/branch.h>
>   #include<asm/mmu_context.h>
> @@ -31,8 +33,8 @@
>    * and the problem, and then passes it off to one of the appropriate
>    * routines.
>    */
> -asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long write,
> -			      unsigned long address)
> +asmlinkage void __kprobes do_page_fault(struct pt_regs *regs, unsigned long write,
> +                                        unsigned long address)
>   {
>   	struct vm_area_struct * vma = NULL;
>   	struct task_struct *tsk = current;
> @@ -47,6 +49,11 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long write,
>   	       field, regs->cp0_epc);
>   #endif
>
> +	/* Notify kprobes fault handler. */
> +        if (notify_die(DIE_PAGE_FAULT, "page fault",
> +                       regs, -1, SEGV_MAPERR, SEGV_MAPERR) == NOTIFY_STOP)
> +                return;
> +
>   	info.si_code = SEGV_MAPERR;
>
>   	/*


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

* Re: [PATCH] MIPS: KProbes support v0.1
  2010-06-07 19:41   ` David Daney
@ 2010-06-08 17:51     ` Himanshu Chauhan
  2010-06-11  0:12       ` David Daney
  0 siblings, 1 reply; 5+ messages in thread
From: Himanshu Chauhan @ 2010-06-08 17:51 UTC (permalink / raw)
  To: David Daney; +Cc: ralf, linux-kernel, linux-mips

Hi David,

Thanks for taking a look.

> >+#ifdef CONFIG_KPROBES
> >+	DIE_PAGE_FAULT,
> >+	DIE_BREAK,
> >+	DIE_SSTEPBP,
> >+#endif
> >  };
> >
> 
> It might be cleaner without the #ifdef.  These are enum value
> definitions, so it doesn't affect code size.
>
Hmm.. I will remove this.
 
> 
> Can you also explain how the die notifier chain interacts with
> KProbes and why it cannot be a seperate notifier chain?

Actually, looking at x86 and other code, this is not the proper way
to do it. I will try to comeup with common approach.

> 
> >  #endif /* _ASM_MIPS_KDEBUG_H */
> >diff --git a/arch/mips/include/asm/kprobes.h b/arch/mips/include/asm/kprobes.h
> >new file mode 100644
> >index 0000000..0f647bf
> >--- /dev/null
> >+++ b/arch/mips/include/asm/kprobes.h
> >@@ -0,0 +1,85 @@
> [...]
> >+
> >+#define BREAKPOINT_INSTRUCTION		0x0000000d
> >+
> >+/*
> >+ * We do not have hardware single-stepping on MIPS.
> >+ * So we implement software single-stepping with breakpoint
> >+ * trap 'break 5'.
> >+ */
> >+#define BREAKPOINT_INSTRUCTION_2	0x0000014d
> 
> The BREAK codes are defined in asm/break.h  This should be added
> there instead.
> 
> Why do you use codes (0 and 5) that are already kind of reserved for
> user space debuggers?

As said ealier, this patch was based on some very older patch of 2.6.16 from
Sony Corp, I didn't make much changes like this. But anyways, I wan't aware of
this either. What would be the best code then?

> 
> >+#define MAX_INSN_SIZE 			2
> >+
> >+#define flush_insn_slot(p)		do { \
> >+        /* invalidate I-cache */             \
> >+        asm volatile("cache 0, 0($0)");      \
> >+        /* invalidate D-cache */             \
> >+        asm volatile("cache 9, 0($0)");      \
> >+        } while(0);
> >+
> 
> You have to call a function in arch/mips/mm/c-* to do this, you
> cannot open code with CACHE instructions as you need to handle CPU
> quirks and SMP.  It is possible that flush_icache_range() or
> flush_cache_sigtramp() would work.  Or we might need something new.
> 
> I see you use flush_icache_range() below, why have this definition,
> it looks unused?

Framework needs you to define this.

> 
> Why this ugliness?  Can't you handle it in do_bp() or  do_trap_or_bp()?

Let me see what I can do about this.

> Need to add or otherwise handle:
> 
> 
> #ifdef CONFIG_CPU_CAVIUM_OCTEON
> 	case lwc2_op: /* This is bbit0 on Octeon */
> 	case ldc2_op: /* This is bbit032 on Octeon */
> 	case swc2_op: /* This is bbit1 on Octeon */
> 	case sdc2_op: /* This is bbit132 on Octeon */
> #endif

Though I have worked on octeon before but I don't remember nitty-gritties.
And I have no clue about these ops :) No way to test either!

With all that, I will soon send an updated patch.
Thanks for the review!

Regards
-Himanshu

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

* Re: [PATCH] MIPS: KProbes support v0.1
  2010-06-08 17:51     ` Himanshu Chauhan
@ 2010-06-11  0:12       ` David Daney
  0 siblings, 0 replies; 5+ messages in thread
From: David Daney @ 2010-06-11  0:12 UTC (permalink / raw)
  To: Himanshu Chauhan; +Cc: ralf, linux-kernel, linux-mips

On 06/08/2010 10:51 AM, Himanshu Chauhan wrote:
> Hi David,
>
> Thanks for taking a look.
>
[...]
>>> +
>>> +#define BREAKPOINT_INSTRUCTION		0x0000000d
>>> +
>>> +/*
>>> + * We do not have hardware single-stepping on MIPS.
>>> + * So we implement software single-stepping with breakpoint
>>> + * trap 'break 5'.
>>> + */
>>> +#define BREAKPOINT_INSTRUCTION_2	0x0000014d
>>
>> The BREAK codes are defined in asm/break.h  This should be added
>> there instead.
>>
>> Why do you use codes (0 and 5) that are already kind of reserved for
>> user space debuggers?
>
> As said ealier, this patch was based on some very older patch of 2.6.16 from
> Sony Corp, I didn't make much changes like this. But anyways, I wan't aware of
> this either. What would be the best code then?
>

How about allocating them after BRK_MEMU?  Say 515 and 516 or something 
like that.

David Daney

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

end of thread, other threads:[~2010-06-11  0:13 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-06-07 16:33 [PATCH] Kprobes support for MIPS architecture Himanshu Chauhan
2010-06-07 16:34 ` [PATCH] MIPS: KProbes support v0.1 Himanshu Chauhan
2010-06-07 19:41   ` David Daney
2010-06-08 17:51     ` Himanshu Chauhan
2010-06-11  0:12       ` David Daney

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.