aboutsummaryrefslogtreecommitdiffstats
path: root/arch/ia64/kernel/kprobes.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/ia64/kernel/kprobes.c')
-rw-r--r--arch/ia64/kernel/kprobes.c128
1 files changed, 124 insertions, 4 deletions
diff --git a/arch/ia64/kernel/kprobes.c b/arch/ia64/kernel/kprobes.c
index 5978823d5c63..3aa3167edbec 100644
--- a/arch/ia64/kernel/kprobes.c
+++ b/arch/ia64/kernel/kprobes.c
@@ -34,6 +34,7 @@
34 34
35#include <asm/pgtable.h> 35#include <asm/pgtable.h>
36#include <asm/kdebug.h> 36#include <asm/kdebug.h>
37#include <asm/sections.h>
37 38
38extern void jprobe_inst_return(void); 39extern void jprobe_inst_return(void);
39 40
@@ -263,13 +264,33 @@ static inline void get_kprobe_inst(bundle_t *bundle, uint slot,
263 } 264 }
264} 265}
265 266
267/* Returns non-zero if the addr is in the Interrupt Vector Table */
268static inline int in_ivt_functions(unsigned long addr)
269{
270 return (addr >= (unsigned long)__start_ivt_text
271 && addr < (unsigned long)__end_ivt_text);
272}
273
266static int valid_kprobe_addr(int template, int slot, unsigned long addr) 274static int valid_kprobe_addr(int template, int slot, unsigned long addr)
267{ 275{
268 if ((slot > 2) || ((bundle_encoding[template][1] == L) && slot > 1)) { 276 if ((slot > 2) || ((bundle_encoding[template][1] == L) && slot > 1)) {
269 printk(KERN_WARNING "Attempting to insert unaligned kprobe at 0x%lx\n", 277 printk(KERN_WARNING "Attempting to insert unaligned kprobe "
270 addr); 278 "at 0x%lx\n", addr);
271 return -EINVAL; 279 return -EINVAL;
272 } 280 }
281
282 if (in_ivt_functions(addr)) {
283 printk(KERN_WARNING "Kprobes can't be inserted inside "
284 "IVT functions at 0x%lx\n", addr);
285 return -EINVAL;
286 }
287
288 if (slot == 1 && bundle_encoding[template][1] != L) {
289 printk(KERN_WARNING "Inserting kprobes on slot #1 "
290 "is not supported\n");
291 return -EINVAL;
292 }
293
273 return 0; 294 return 0;
274} 295}
275 296
@@ -290,6 +311,94 @@ static inline void set_current_kprobe(struct kprobe *p)
290 current_kprobe = p; 311 current_kprobe = p;
291} 312}
292 313
314static void kretprobe_trampoline(void)
315{
316}
317
318/*
319 * At this point the target function has been tricked into
320 * returning into our trampoline. Lookup the associated instance
321 * and then:
322 * - call the handler function
323 * - cleanup by marking the instance as unused
324 * - long jump back to the original return address
325 */
326int trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
327{
328 struct kretprobe_instance *ri = NULL;
329 struct hlist_head *head;
330 struct hlist_node *node, *tmp;
331 unsigned long orig_ret_address = 0;
332 unsigned long trampoline_address =
333 ((struct fnptr *)kretprobe_trampoline)->ip;
334
335 head = kretprobe_inst_table_head(current);
336
337 /*
338 * It is possible to have multiple instances associated with a given
339 * task either because an multiple functions in the call path
340 * have a return probe installed on them, and/or more then one return
341 * return probe was registered for a target function.
342 *
343 * We can handle this because:
344 * - instances are always inserted at the head of the list
345 * - when multiple return probes are registered for the same
346 * function, the first instance's ret_addr will point to the
347 * real return address, and all the rest will point to
348 * kretprobe_trampoline
349 */
350 hlist_for_each_entry_safe(ri, node, tmp, head, hlist) {
351 if (ri->task != current)
352 /* another task is sharing our hash bucket */
353 continue;
354
355 if (ri->rp && ri->rp->handler)
356 ri->rp->handler(ri, regs);
357
358 orig_ret_address = (unsigned long)ri->ret_addr;
359 recycle_rp_inst(ri);
360
361 if (orig_ret_address != trampoline_address)
362 /*
363 * This is the real return address. Any other
364 * instances associated with this task are for
365 * other calls deeper on the call stack
366 */
367 break;
368 }
369
370 BUG_ON(!orig_ret_address || (orig_ret_address == trampoline_address));
371 regs->cr_iip = orig_ret_address;
372
373 unlock_kprobes();
374 preempt_enable_no_resched();
375
376 /*
377 * By returning a non-zero value, we are telling
378 * kprobe_handler() that we have handled unlocking
379 * and re-enabling preemption.
380 */
381 return 1;
382}
383
384void arch_prepare_kretprobe(struct kretprobe *rp, struct pt_regs *regs)
385{
386 struct kretprobe_instance *ri;
387
388 if ((ri = get_free_rp_inst(rp)) != NULL) {
389 ri->rp = rp;
390 ri->task = current;
391 ri->ret_addr = (kprobe_opcode_t *)regs->b0;
392
393 /* Replace the return addr with trampoline addr */
394 regs->b0 = ((struct fnptr *)kretprobe_trampoline)->ip;
395
396 add_rp_inst(ri);
397 } else {
398 rp->nmissed++;
399 }
400}
401
293int arch_prepare_kprobe(struct kprobe *p) 402int arch_prepare_kprobe(struct kprobe *p)
294{ 403{
295 unsigned long addr = (unsigned long) p->addr; 404 unsigned long addr = (unsigned long) p->addr;
@@ -492,8 +601,8 @@ static int pre_kprobes_handler(struct die_args *args)
492 if (p->pre_handler && p->pre_handler(p, regs)) 601 if (p->pre_handler && p->pre_handler(p, regs))
493 /* 602 /*
494 * Our pre-handler is specifically requesting that we just 603 * Our pre-handler is specifically requesting that we just
495 * do a return. This is handling the case where the 604 * do a return. This is used for both the jprobe pre-handler
496 * pre-handler is really our special jprobe pre-handler. 605 * and the kretprobe trampoline
497 */ 606 */
498 return 1; 607 return 1;
499 608
@@ -599,3 +708,14 @@ int longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
599 *regs = jprobe_saved_regs; 708 *regs = jprobe_saved_regs;
600 return 1; 709 return 1;
601} 710}
711
712static struct kprobe trampoline_p = {
713 .pre_handler = trampoline_probe_handler
714};
715
716int __init arch_init(void)
717{
718 trampoline_p.addr =
719 (kprobe_opcode_t *)((struct fnptr *)kretprobe_trampoline)->ip;
720 return register_kprobe(&trampoline_p);
721}