aboutsummaryrefslogtreecommitdiffstats
path: root/arch/ppc64/kernel
diff options
context:
space:
mode:
authorRusty Lynch <rusty.lynch@intel.com>2005-06-27 18:17:15 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-06-27 18:23:53 -0400
commit97f7943d70ff0e1e92ea627c44cfacfdae65dbc4 (patch)
treee2a042a3bb5014ee98551bd5d42dc3b21a42e1ec /arch/ppc64/kernel
parent9508dbfe39112813612085c00d55bacd398eddc6 (diff)
[PATCH] Return probe redesign: ppc64 specific implementation
The following is a patch provided by Ananth Mavinakayanahalli that implements the new PPC64 specific parts of the new function return probe design. NOTE: Since getting Ananth's patch, I changed trampoline_probe_handler() to consume each of the outstanding return probem instances (feedback on my original RFC after Ananth cut a patch), and also added the arch_init() function (adding arch specific initialization.) I have cross compiled but have not testing this on a PPC64 machine. Changes include: * Addition of kretprobe_trampoline to act as a dummy function for instrumented functions to return to, and for the return probe infrastructure to place a kprobe on on, gaining control so that the return probe handler can be called, and so that the instruction pointer can be moved back to the original return address. * Addition of arch_init(), allowing a kprobe to be registered on kretprobe_trampoline * Addition of trampoline_probe_handler() which is used as the pre_handler for the kprobe inserted on kretprobe_implementation. This is the function that handles the details for calling the return probe handler function and returning control back at the original return address * Addition of arch_prepare_kretprobe() which is setup as the pre_handler for a kprobe registered at the beginning of the target function by kernel/kprobes.c so that a return probe instance can be setup when a caller enters the target function. (A return probe instance contains all the needed information for trampoline_probe_handler to do it's job.) * Hooks added to the exit path of a task so that we can cleanup any left-over return probe instances (i.e. if a task dies while inside a targeted function then the return probe instance was reserved at the beginning of the function but the function never returns so we need to mark the instance as unused.) Signed-off-by: Rusty Lynch <rusty.lynch@intel.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch/ppc64/kernel')
-rw-r--r--arch/ppc64/kernel/kprobes.c99
-rw-r--r--arch/ppc64/kernel/process.c4
2 files changed, 103 insertions, 0 deletions
diff --git a/arch/ppc64/kernel/kprobes.c b/arch/ppc64/kernel/kprobes.c
index 86cc5496db9f..1d2ff6d6b0b3 100644
--- a/arch/ppc64/kernel/kprobes.c
+++ b/arch/ppc64/kernel/kprobes.c
@@ -122,6 +122,23 @@ static inline void restore_previous_kprobe(void)
122 kprobe_saved_msr = kprobe_saved_msr_prev; 122 kprobe_saved_msr = kprobe_saved_msr_prev;
123} 123}
124 124
125void arch_prepare_kretprobe(struct kretprobe *rp, struct pt_regs *regs)
126{
127 struct kretprobe_instance *ri;
128
129 if ((ri = get_free_rp_inst(rp)) != NULL) {
130 ri->rp = rp;
131 ri->task = current;
132 ri->ret_addr = (kprobe_opcode_t *)regs->link;
133
134 /* Replace the return addr with trampoline addr */
135 regs->link = (unsigned long)kretprobe_trampoline;
136 add_rp_inst(ri);
137 } else {
138 rp->nmissed++;
139 }
140}
141
125static inline int kprobe_handler(struct pt_regs *regs) 142static inline int kprobe_handler(struct pt_regs *regs)
126{ 143{
127 struct kprobe *p; 144 struct kprobe *p;
@@ -212,6 +229,78 @@ no_kprobe:
212} 229}
213 230
214/* 231/*
232 * Function return probe trampoline:
233 * - init_kprobes() establishes a probepoint here
234 * - When the probed function returns, this probe
235 * causes the handlers to fire
236 */
237void kretprobe_trampoline_holder(void)
238{
239 asm volatile(".global kretprobe_trampoline\n"
240 "kretprobe_trampoline:\n"
241 "nop\n");
242}
243
244/*
245 * Called when the probe at kretprobe trampoline is hit
246 */
247int trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
248{
249 struct kretprobe_instance *ri = NULL;
250 struct hlist_head *head;
251 struct hlist_node *node, *tmp;
252 unsigned long orig_ret_address = 0;
253 unsigned long trampoline_address =(unsigned long)&kretprobe_trampoline;
254
255 head = kretprobe_inst_table_head(current);
256
257 /*
258 * It is possible to have multiple instances associated with a given
259 * task either because an multiple functions in the call path
260 * have a return probe installed on them, and/or more then one return
261 * return probe was registered for a target function.
262 *
263 * We can handle this because:
264 * - instances are always inserted at the head of the list
265 * - when multiple return probes are registered for the same
266 * function, the first instance's ret_addr will point to the
267 * real return address, and all the rest will point to
268 * kretprobe_trampoline
269 */
270 hlist_for_each_entry_safe(ri, node, tmp, head, hlist) {
271 if (ri->task != current)
272 /* another task is sharing our hash bucket */
273 continue;
274
275 if (ri->rp && ri->rp->handler)
276 ri->rp->handler(ri, regs);
277
278 orig_ret_address = (unsigned long)ri->ret_addr;
279 recycle_rp_inst(ri);
280
281 if (orig_ret_address != trampoline_address)
282 /*
283 * This is the real return address. Any other
284 * instances associated with this task are for
285 * other calls deeper on the call stack
286 */
287 break;
288 }
289
290 BUG_ON(!orig_ret_address || (orig_ret_address == trampoline_address));
291 regs->nip = orig_ret_address;
292
293 unlock_kprobes();
294
295 /*
296 * By returning a non-zero value, we are telling
297 * kprobe_handler() that we have handled unlocking
298 * and re-enabling preemption.
299 */
300 return 1;
301}
302
303/*
215 * Called after single-stepping. p->addr is the address of the 304 * Called after single-stepping. p->addr is the address of the
216 * instruction whose first byte has been replaced by the "breakpoint" 305 * instruction whose first byte has been replaced by the "breakpoint"
217 * instruction. To avoid the SMP problems that can occur when we 306 * instruction. To avoid the SMP problems that can occur when we
@@ -349,3 +438,13 @@ int longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
349 memcpy(regs, &jprobe_saved_regs, sizeof(struct pt_regs)); 438 memcpy(regs, &jprobe_saved_regs, sizeof(struct pt_regs));
350 return 1; 439 return 1;
351} 440}
441
442static struct kprobe trampoline_p = {
443 .addr = (kprobe_opcode_t *) &kretprobe_trampoline,
444 .pre_handler = trampoline_probe_handler
445};
446
447int __init arch_init(void)
448{
449 return register_kprobe(&trampoline_p);
450}
diff --git a/arch/ppc64/kernel/process.c b/arch/ppc64/kernel/process.c
index aba89554d89d..f7cae05e40fb 100644
--- a/arch/ppc64/kernel/process.c
+++ b/arch/ppc64/kernel/process.c
@@ -36,6 +36,7 @@
36#include <linux/kallsyms.h> 36#include <linux/kallsyms.h>
37#include <linux/interrupt.h> 37#include <linux/interrupt.h>
38#include <linux/utsname.h> 38#include <linux/utsname.h>
39#include <linux/kprobes.h>
39 40
40#include <asm/pgtable.h> 41#include <asm/pgtable.h>
41#include <asm/uaccess.h> 42#include <asm/uaccess.h>
@@ -307,6 +308,8 @@ void show_regs(struct pt_regs * regs)
307 308
308void exit_thread(void) 309void exit_thread(void)
309{ 310{
311 kprobe_flush_task(current);
312
310#ifndef CONFIG_SMP 313#ifndef CONFIG_SMP
311 if (last_task_used_math == current) 314 if (last_task_used_math == current)
312 last_task_used_math = NULL; 315 last_task_used_math = NULL;
@@ -321,6 +324,7 @@ void flush_thread(void)
321{ 324{
322 struct thread_info *t = current_thread_info(); 325 struct thread_info *t = current_thread_info();
323 326
327 kprobe_flush_task(current);
324 if (t->flags & _TIF_ABI_PENDING) 328 if (t->flags & _TIF_ABI_PENDING)
325 t->flags ^= (_TIF_ABI_PENDING | _TIF_32BIT); 329 t->flags ^= (_TIF_ABI_PENDING | _TIF_32BIT);
326 330