aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnanth N Mavinakayanahalli <ananth@in.ibm.com>2012-08-23 17:31:32 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2012-09-05 01:35:19 -0400
commit8b7b80b9ebb46dd88fbb94e918297295cf312b59 (patch)
tree9904b4440bbe1adc7c9dc3848814d15bf80b518d
parent41ab5266c3622354353433618edb92ab278025fa (diff)
powerpc: Uprobes port to powerpc
This is the port of uprobes to powerpc. Usage is similar to x86. [root@xxxx ~]# ./bin/perf probe -x /lib64/libc.so.6 malloc Added new event: probe_libc:malloc (on 0xb4860) You can now use it in all perf tools, such as: perf record -e probe_libc:malloc -aR sleep 1 [root@xxxx ~]# ./bin/perf record -e probe_libc:malloc -aR sleep 20 [ perf record: Woken up 22 times to write data ] [ perf record: Captured and wrote 5.843 MB perf.data (~255302 samples) ] [root@xxxx ~]# ./bin/perf report --stdio ... 69.05% tar libc-2.12.so [.] malloc 28.57% rm libc-2.12.so [.] malloc 1.32% avahi-daemon libc-2.12.so [.] malloc 0.58% bash libc-2.12.so [.] malloc 0.28% sshd libc-2.12.so [.] malloc 0.08% irqbalance libc-2.12.so [.] malloc 0.05% bzip2 libc-2.12.so [.] malloc 0.04% sleep libc-2.12.so [.] malloc 0.03% multipathd libc-2.12.so [.] malloc 0.01% sendmail libc-2.12.so [.] malloc 0.01% automount libc-2.12.so [.] malloc The trap_nr addition patch is a prereq. Signed-off-by: Ananth N Mavinakayanahalli <ananth@in.ibm.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
-rw-r--r--arch/powerpc/Kconfig3
-rw-r--r--arch/powerpc/include/asm/thread_info.h4
-rw-r--r--arch/powerpc/include/asm/uprobes.h54
-rw-r--r--arch/powerpc/kernel/Makefile1
-rw-r--r--arch/powerpc/kernel/signal.c6
-rw-r--r--arch/powerpc/kernel/uprobes.c184
6 files changed, 251 insertions, 1 deletions
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 352f416269ce..98e513b62709 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -239,6 +239,9 @@ config PPC_OF_PLATFORM_PCI
239config ARCH_SUPPORTS_DEBUG_PAGEALLOC 239config ARCH_SUPPORTS_DEBUG_PAGEALLOC
240 def_bool y 240 def_bool y
241 241
242config ARCH_SUPPORTS_UPROBES
243 def_bool y
244
242config PPC_ADV_DEBUG_REGS 245config PPC_ADV_DEBUG_REGS
243 bool 246 bool
244 depends on 40x || BOOKE 247 depends on 40x || BOOKE
diff --git a/arch/powerpc/include/asm/thread_info.h b/arch/powerpc/include/asm/thread_info.h
index faf93529cbf0..e942203cd4a8 100644
--- a/arch/powerpc/include/asm/thread_info.h
+++ b/arch/powerpc/include/asm/thread_info.h
@@ -102,6 +102,7 @@ static inline struct thread_info *current_thread_info(void)
102#define TIF_RESTOREALL 11 /* Restore all regs (implies NOERROR) */ 102#define TIF_RESTOREALL 11 /* Restore all regs (implies NOERROR) */
103#define TIF_NOERROR 12 /* Force successful syscall return */ 103#define TIF_NOERROR 12 /* Force successful syscall return */
104#define TIF_NOTIFY_RESUME 13 /* callback before returning to user */ 104#define TIF_NOTIFY_RESUME 13 /* callback before returning to user */
105#define TIF_UPROBE 14 /* breakpointed or single-stepping */
105#define TIF_SYSCALL_TRACEPOINT 15 /* syscall tracepoint instrumentation */ 106#define TIF_SYSCALL_TRACEPOINT 15 /* syscall tracepoint instrumentation */
106 107
107/* as above, but as bit values */ 108/* as above, but as bit values */
@@ -118,12 +119,13 @@ static inline struct thread_info *current_thread_info(void)
118#define _TIF_RESTOREALL (1<<TIF_RESTOREALL) 119#define _TIF_RESTOREALL (1<<TIF_RESTOREALL)
119#define _TIF_NOERROR (1<<TIF_NOERROR) 120#define _TIF_NOERROR (1<<TIF_NOERROR)
120#define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME) 121#define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME)
122#define _TIF_UPROBE (1<<TIF_UPROBE)
121#define _TIF_SYSCALL_TRACEPOINT (1<<TIF_SYSCALL_TRACEPOINT) 123#define _TIF_SYSCALL_TRACEPOINT (1<<TIF_SYSCALL_TRACEPOINT)
122#define _TIF_SYSCALL_T_OR_A (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | \ 124#define _TIF_SYSCALL_T_OR_A (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | \
123 _TIF_SECCOMP | _TIF_SYSCALL_TRACEPOINT) 125 _TIF_SECCOMP | _TIF_SYSCALL_TRACEPOINT)
124 126
125#define _TIF_USER_WORK_MASK (_TIF_SIGPENDING | _TIF_NEED_RESCHED | \ 127#define _TIF_USER_WORK_MASK (_TIF_SIGPENDING | _TIF_NEED_RESCHED | \
126 _TIF_NOTIFY_RESUME) 128 _TIF_NOTIFY_RESUME | _TIF_UPROBE)
127#define _TIF_PERSYSCALL_MASK (_TIF_RESTOREALL|_TIF_NOERROR) 129#define _TIF_PERSYSCALL_MASK (_TIF_RESTOREALL|_TIF_NOERROR)
128 130
129/* Bits in local_flags */ 131/* Bits in local_flags */
diff --git a/arch/powerpc/include/asm/uprobes.h b/arch/powerpc/include/asm/uprobes.h
new file mode 100644
index 000000000000..13ba5077b59b
--- /dev/null
+++ b/arch/powerpc/include/asm/uprobes.h
@@ -0,0 +1,54 @@
1#ifndef _ASM_UPROBES_H
2#define _ASM_UPROBES_H
3/*
4 * User-space Probes (UProbes) for powerpc
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 *
20 * Copyright IBM Corporation, 2007-2012
21 *
22 * Adapted from the x86 port by Ananth N Mavinakayanahalli <ananth@in.ibm.com>
23 */
24
25#include <linux/notifier.h>
26#include <asm/probes.h>
27
28typedef opcode_t uprobe_opcode_t;
29
30#define MAX_UINSN_BYTES 4
31#define UPROBE_XOL_SLOT_BYTES (MAX_UINSN_BYTES)
32
33/* The following alias is needed for reference from arch-agnostic code */
34#define UPROBE_SWBP_INSN BREAKPOINT_INSTRUCTION
35#define UPROBE_SWBP_INSN_SIZE 4 /* swbp insn size in bytes */
36
37struct arch_uprobe {
38 union {
39 u8 insn[MAX_UINSN_BYTES];
40 u32 ainsn;
41 };
42};
43
44struct arch_uprobe_task {
45 unsigned long saved_trap_nr;
46};
47
48extern int arch_uprobe_analyze_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long addr);
49extern int arch_uprobe_pre_xol(struct arch_uprobe *aup, struct pt_regs *regs);
50extern int arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs);
51extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk);
52extern int arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data);
53extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs);
54#endif /* _ASM_UPROBES_H */
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index bb282dd81612..cde12f8a4ebc 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -96,6 +96,7 @@ obj-$(CONFIG_MODULES) += ppc_ksyms.o
96obj-$(CONFIG_BOOTX_TEXT) += btext.o 96obj-$(CONFIG_BOOTX_TEXT) += btext.o
97obj-$(CONFIG_SMP) += smp.o 97obj-$(CONFIG_SMP) += smp.o
98obj-$(CONFIG_KPROBES) += kprobes.o 98obj-$(CONFIG_KPROBES) += kprobes.o
99obj-$(CONFIG_UPROBES) += uprobes.o
99obj-$(CONFIG_PPC_UDBG_16550) += legacy_serial.o udbg_16550.o 100obj-$(CONFIG_PPC_UDBG_16550) += legacy_serial.o udbg_16550.o
100obj-$(CONFIG_STACKTRACE) += stacktrace.o 101obj-$(CONFIG_STACKTRACE) += stacktrace.o
101obj-$(CONFIG_SWIOTLB) += dma-swiotlb.o 102obj-$(CONFIG_SWIOTLB) += dma-swiotlb.o
diff --git a/arch/powerpc/kernel/signal.c b/arch/powerpc/kernel/signal.c
index 5c023c9cf16e..29be2712e560 100644
--- a/arch/powerpc/kernel/signal.c
+++ b/arch/powerpc/kernel/signal.c
@@ -11,6 +11,7 @@
11 11
12#include <linux/tracehook.h> 12#include <linux/tracehook.h>
13#include <linux/signal.h> 13#include <linux/signal.h>
14#include <linux/uprobes.h>
14#include <linux/key.h> 15#include <linux/key.h>
15#include <asm/hw_breakpoint.h> 16#include <asm/hw_breakpoint.h>
16#include <asm/uaccess.h> 17#include <asm/uaccess.h>
@@ -157,6 +158,11 @@ static int do_signal(struct pt_regs *regs)
157 158
158void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags) 159void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags)
159{ 160{
161 if (thread_info_flags & _TIF_UPROBE) {
162 clear_thread_flag(TIF_UPROBE);
163 uprobe_notify_resume(regs);
164 }
165
160 if (thread_info_flags & _TIF_SIGPENDING) 166 if (thread_info_flags & _TIF_SIGPENDING)
161 do_signal(regs); 167 do_signal(regs);
162 168
diff --git a/arch/powerpc/kernel/uprobes.c b/arch/powerpc/kernel/uprobes.c
new file mode 100644
index 000000000000..d2d46d1014f8
--- /dev/null
+++ b/arch/powerpc/kernel/uprobes.c
@@ -0,0 +1,184 @@
1/*
2 * User-space Probes (UProbes) for powerpc
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 *
18 * Copyright IBM Corporation, 2007-2012
19 *
20 * Adapted from the x86 port by Ananth N Mavinakayanahalli <ananth@in.ibm.com>
21 */
22#include <linux/kernel.h>
23#include <linux/sched.h>
24#include <linux/ptrace.h>
25#include <linux/uprobes.h>
26#include <linux/uaccess.h>
27#include <linux/kdebug.h>
28
29#include <asm/sstep.h>
30
31#define UPROBE_TRAP_NR UINT_MAX
32
33/**
34 * arch_uprobe_analyze_insn
35 * @mm: the probed address space.
36 * @arch_uprobe: the probepoint information.
37 * @addr: vaddr to probe.
38 * Return 0 on success or a -ve number on error.
39 */
40int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe,
41 struct mm_struct *mm, unsigned long addr)
42{
43 if (addr & 0x03)
44 return -EINVAL;
45
46 /*
47 * We currently don't support a uprobe on an already
48 * existing breakpoint instruction underneath
49 */
50 if (is_trap(auprobe->ainsn))
51 return -ENOTSUPP;
52 return 0;
53}
54
55/*
56 * arch_uprobe_pre_xol - prepare to execute out of line.
57 * @auprobe: the probepoint information.
58 * @regs: reflects the saved user state of current task.
59 */
60int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
61{
62 struct arch_uprobe_task *autask = &current->utask->autask;
63
64 autask->saved_trap_nr = current->thread.trap_nr;
65 current->thread.trap_nr = UPROBE_TRAP_NR;
66 regs->nip = current->utask->xol_vaddr;
67 return 0;
68}
69
70/**
71 * uprobe_get_swbp_addr - compute address of swbp given post-swbp regs
72 * @regs: Reflects the saved state of the task after it has hit a breakpoint
73 * instruction.
74 * Return the address of the breakpoint instruction.
75 */
76unsigned long uprobe_get_swbp_addr(struct pt_regs *regs)
77{
78 return instruction_pointer(regs);
79}
80
81/*
82 * If xol insn itself traps and generates a signal (SIGILL/SIGSEGV/etc),
83 * then detect the case where a singlestepped instruction jumps back to its
84 * own address. It is assumed that anything like do_page_fault/do_trap/etc
85 * sets thread.trap_nr != UINT_MAX.
86 *
87 * arch_uprobe_pre_xol/arch_uprobe_post_xol save/restore thread.trap_nr,
88 * arch_uprobe_xol_was_trapped() simply checks that ->trap_nr is not equal to
89 * UPROBE_TRAP_NR == UINT_MAX set by arch_uprobe_pre_xol().
90 */
91bool arch_uprobe_xol_was_trapped(struct task_struct *t)
92{
93 if (t->thread.trap_nr != UPROBE_TRAP_NR)
94 return true;
95
96 return false;
97}
98
99/*
100 * Called after single-stepping. To avoid the SMP problems that can
101 * occur when we temporarily put back the original opcode to
102 * single-step, we single-stepped a copy of the instruction.
103 *
104 * This function prepares to resume execution after the single-step.
105 */
106int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
107{
108 struct uprobe_task *utask = current->utask;
109
110 WARN_ON_ONCE(current->thread.trap_nr != UPROBE_TRAP_NR);
111
112 current->thread.trap_nr = utask->autask.saved_trap_nr;
113
114 /*
115 * On powerpc, except for loads and stores, most instructions
116 * including ones that alter code flow (branches, calls, returns)
117 * are emulated in the kernel. We get here only if the emulation
118 * support doesn't exist and have to fix-up the next instruction
119 * to be executed.
120 */
121 regs->nip = utask->vaddr + MAX_UINSN_BYTES;
122 return 0;
123}
124
125/* callback routine for handling exceptions. */
126int arch_uprobe_exception_notify(struct notifier_block *self,
127 unsigned long val, void *data)
128{
129 struct die_args *args = data;
130 struct pt_regs *regs = args->regs;
131
132 /* regs == NULL is a kernel bug */
133 if (WARN_ON(!regs))
134 return NOTIFY_DONE;
135
136 /* We are only interested in userspace traps */
137 if (!user_mode(regs))
138 return NOTIFY_DONE;
139
140 switch (val) {
141 case DIE_BPT:
142 if (uprobe_pre_sstep_notifier(regs))
143 return NOTIFY_STOP;
144 break;
145 case DIE_SSTEP:
146 if (uprobe_post_sstep_notifier(regs))
147 return NOTIFY_STOP;
148 default:
149 break;
150 }
151 return NOTIFY_DONE;
152}
153
154/*
155 * This function gets called when XOL instruction either gets trapped or
156 * the thread has a fatal signal, so reset the instruction pointer to its
157 * probed address.
158 */
159void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
160{
161 struct uprobe_task *utask = current->utask;
162
163 current->thread.trap_nr = utask->autask.saved_trap_nr;
164 instruction_pointer_set(regs, utask->vaddr);
165}
166
167/*
168 * See if the instruction can be emulated.
169 * Returns true if instruction was emulated, false otherwise.
170 */
171bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
172{
173 int ret;
174
175 /*
176 * emulate_step() returns 1 if the insn was successfully emulated.
177 * For all other cases, we need to single-step in hardware.
178 */
179 ret = emulate_step(regs, auprobe->ainsn);
180 if (ret > 0)
181 return true;
182
183 return false;
184}