Commit c1bf207d authored by David Daney's avatar David Daney Committed by Ralf Baechle

MIPS: kprobe: Add support.

This patch is based on previous work by Sony and Himanshu Chauhan.

I have done some cleanup and implemented JProbes and KRETPROBES.  The
KRETPROBES part is pretty much copied verbatim from powerpc.  A possible
future enhance might be to factor out the common code.
Signed-off-by: default avatarDavid Daney <ddaney@caviumnetworks.com>
Cc: Himanshu Chauhan <hschauhan@nulltrace.org>
To: linux-mips@linux-mips.org
To: ananth@in.ibm.com,
To: anil.s.keshavamurthy@intel.com
To: davem@davemloft.net
To: masami.hiramatsu.pt@hitachi.com
Cc: linux-kernel@vger.kernel.org
Patchwork: https://patchwork.linux-mips.org/patch/1525/
Patchwork: https://patchwork.linux-mips.org/patch/1530/Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent 2ea6399f
...@@ -10,6 +10,8 @@ config MIPS ...@@ -10,6 +10,8 @@ config MIPS
select HAVE_DYNAMIC_FTRACE select HAVE_DYNAMIC_FTRACE
select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FTRACE_MCOUNT_RECORD
select HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_GRAPH_TRACER
select HAVE_KPROBES
select HAVE_KRETPROBES
select RTC_LIB if !MACH_LOONGSON select RTC_LIB if !MACH_LOONGSON
mainmenu "Linux/MIPS Kernel Configuration" mainmenu "Linux/MIPS Kernel Configuration"
......
...@@ -259,6 +259,9 @@ endif ...@@ -259,6 +259,9 @@ endif
vmlinux.32: vmlinux vmlinux.32: vmlinux
$(OBJCOPY) -O $(32bit-bfd) $(OBJCOPYFLAGS) $< $@ $(OBJCOPY) -O $(32bit-bfd) $(OBJCOPYFLAGS) $< $@
#obj-$(CONFIG_KPROBES) += kprobes.o
# #
# The 64-bit ELF tools are pretty broken so at this time we generate 64-bit # The 64-bit ELF tools are pretty broken so at this time we generate 64-bit
# ELF files from 32-bit files by conversion. # ELF files from 32-bit files by conversion.
......
...@@ -30,6 +30,8 @@ ...@@ -30,6 +30,8 @@
#define BRK_BUG 512 /* Used by BUG() */ #define BRK_BUG 512 /* Used by BUG() */
#define BRK_KDB 513 /* Used in KDB_ENTER() */ #define BRK_KDB 513 /* Used in KDB_ENTER() */
#define BRK_MEMU 514 /* Used by FPU emulator */ #define BRK_MEMU 514 /* Used by FPU emulator */
#define BRK_KPROBE_BP 515 /* Kprobe break */
#define BRK_KPROBE_SSTEPBP 516 /* Kprobe single step software implementation */
#define BRK_MULOVF 1023 /* Multiply overflow */ #define BRK_MULOVF 1023 /* Multiply overflow */
#endif /* __ASM_BREAK_H */ #endif /* __ASM_BREAK_H */
...@@ -8,6 +8,9 @@ enum die_val { ...@@ -8,6 +8,9 @@ enum die_val {
DIE_FP, DIE_FP,
DIE_TRAP, DIE_TRAP,
DIE_RI, DIE_RI,
DIE_PAGE_FAULT,
DIE_BREAK,
DIE_SSTEPBP
}; };
#endif /* _ASM_MIPS_KDEBUG_H */ #endif /* _ASM_MIPS_KDEBUG_H */
/*
* Kernel Probes (KProbes)
* include/asm-mips/kprobes.h
*
* Copyright 2006 Sony Corp.
* Copyright 2010 Cavium Networks
*
* 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/ptrace.h>
#include <linux/types.h>
#include <asm/cacheflush.h>
#include <asm/kdebug.h>
#include <asm/inst.h>
#define __ARCH_WANT_KPROBES_INSN_SLOT
struct kprobe;
struct pt_regs;
typedef union mips_instruction kprobe_opcode_t;
#define MAX_INSN_SIZE 2
#define flush_insn_slot(p) \
do { \
flush_icache_range((unsigned long)p->addr, \
(unsigned long)p->addr + \
(MAX_INSN_SIZE * sizeof(kprobe_opcode_t))); \
} 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;
};
#define MAX_JPROBES_STACK_SIZE 128
#define MAX_JPROBES_STACK_ADDR \
(((unsigned long)current_thread_info()) + THREAD_SIZE - 32 - sizeof(struct pt_regs))
#define MIN_JPROBES_STACK_SIZE(ADDR) \
((((ADDR) + MAX_JPROBES_STACK_SIZE) > MAX_JPROBES_STACK_ADDR) \
? MAX_JPROBES_STACK_ADDR - (ADDR) \
: MAX_JPROBES_STACK_SIZE)
/* 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;
unsigned long jprobe_saved_sp;
struct pt_regs jprobe_saved_regs;
u8 jprobes_stack[MAX_JPROBES_STACK_SIZE];
struct prev_kprobe prev_kprobe;
};
extern int kprobe_exceptions_notify(struct notifier_block *self,
unsigned long val, void *data);
#endif /* _ASM_KPROBES_H */
...@@ -76,6 +76,7 @@ obj-$(CONFIG_IRQ_TXX9) += irq_txx9.o ...@@ -76,6 +76,7 @@ obj-$(CONFIG_IRQ_TXX9) += irq_txx9.o
obj-$(CONFIG_IRQ_GT641XX) += irq-gt641xx.o obj-$(CONFIG_IRQ_GT641XX) += irq-gt641xx.o
obj-$(CONFIG_IRQ_GIC) += irq-gic.o obj-$(CONFIG_IRQ_GIC) += irq-gic.o
obj-$(CONFIG_KPROBES) += kprobes.o
obj-$(CONFIG_32BIT) += scall32-o32.o obj-$(CONFIG_32BIT) += scall32-o32.o
obj-$(CONFIG_64BIT) += scall64-64.o obj-$(CONFIG_64BIT) += scall64-64.o
obj-$(CONFIG_MIPS32_COMPAT) += linux32.o ptrace32.o signal32.o obj-$(CONFIG_MIPS32_COMPAT) += linux32.o ptrace32.o signal32.o
......
This diff is collapsed.
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <linux/ptrace.h> #include <linux/ptrace.h>
#include <linux/kgdb.h> #include <linux/kgdb.h>
#include <linux/kdebug.h> #include <linux/kdebug.h>
#include <linux/kprobes.h>
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/kdb.h> #include <linux/kdb.h>
...@@ -334,7 +335,7 @@ void show_regs(struct pt_regs *regs) ...@@ -334,7 +335,7 @@ void show_regs(struct pt_regs *regs)
__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); const int field = 2 * sizeof(unsigned long);
...@@ -783,6 +784,25 @@ asmlinkage void do_bp(struct pt_regs *regs) ...@@ -783,6 +784,25 @@ asmlinkage void do_bp(struct pt_regs *regs)
if (bcode >= (1 << 10)) if (bcode >= (1 << 10))
bcode >>= 10; bcode >>= 10;
/*
* notify the kprobe handlers, if instruction is likely to
* pertain to them.
*/
switch (bcode) {
case BRK_KPROBE_BP:
if (notify_die(DIE_BREAK, "debug", regs, bcode, 0, 0) == NOTIFY_STOP)
return;
else
break;
case BRK_KPROBE_SSTEPBP:
if (notify_die(DIE_SSTEPBP, "single_step", regs, bcode, 0, 0) == NOTIFY_STOP)
return;
else
break;
default:
break;
}
do_trap_or_bp(regs, bcode, "Break"); do_trap_or_bp(regs, bcode, "Break");
return; return;
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/smp.h> #include <linux/smp.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/kprobes.h>
#include <asm/branch.h> #include <asm/branch.h>
#include <asm/mmu_context.h> #include <asm/mmu_context.h>
...@@ -24,13 +25,14 @@ ...@@ -24,13 +25,14 @@
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/ptrace.h> #include <asm/ptrace.h>
#include <asm/highmem.h> /* For VMALLOC_END */ #include <asm/highmem.h> /* For VMALLOC_END */
#include <linux/kdebug.h>
/* /*
* This routine handles page faults. It determines the address, * This routine handles page faults. It determines the address,
* and the problem, and then passes it off to one of the appropriate * and the problem, and then passes it off to one of the appropriate
* routines. * routines.
*/ */
asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long write, asmlinkage void __kprobes do_page_fault(struct pt_regs *regs, unsigned long write,
unsigned long address) unsigned long address)
{ {
struct vm_area_struct * vma = NULL; struct vm_area_struct * vma = NULL;
...@@ -46,6 +48,17 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long write, ...@@ -46,6 +48,17 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long write,
field, regs->cp0_epc); field, regs->cp0_epc);
#endif #endif
#ifdef CONFIG_KPROBES
/*
* This is to notify the fault handler of the kprobes. The
* exception code is redundant as it is also carried in REGS,
* but we pass it anyhow.
*/
if (notify_die(DIE_PAGE_FAULT, "page fault", regs, -1,
(regs->cp0_cause >> 2) & 0x1f, SIGSEGV) == NOTIFY_STOP)
return;
#endif
info.si_code = SEGV_MAPERR; info.si_code = SEGV_MAPERR;
/* /*
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment