diff options
Diffstat (limited to 'arch/ia64/kernel/kprobes.c')
-rw-r--r-- | arch/ia64/kernel/kprobes.c | 128 |
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 | ||
38 | extern void jprobe_inst_return(void); | 39 | extern 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 */ | ||
268 | static 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 | |||
266 | static int valid_kprobe_addr(int template, int slot, unsigned long addr) | 274 | static 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 | ||
314 | static 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 | */ | ||
326 | int 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 | |||
384 | void 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 | |||
293 | int arch_prepare_kprobe(struct kprobe *p) | 402 | int 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 | |||
712 | static struct kprobe trampoline_p = { | ||
713 | .pre_handler = trampoline_probe_handler | ||
714 | }; | ||
715 | |||
716 | int __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 | } | ||