diff options
| -rw-r--r-- | arch/ia64/kernel/jprobes.S | 27 | ||||
| -rw-r--r-- | arch/ia64/kernel/kprobes.c | 57 | ||||
| -rw-r--r-- | include/asm-ia64/kprobes.h | 6 |
3 files changed, 90 insertions, 0 deletions
diff --git a/arch/ia64/kernel/jprobes.S b/arch/ia64/kernel/jprobes.S index 2323377e3695..5cd6226f44f2 100644 --- a/arch/ia64/kernel/jprobes.S +++ b/arch/ia64/kernel/jprobes.S | |||
| @@ -60,3 +60,30 @@ END(jprobe_break) | |||
| 60 | GLOBAL_ENTRY(jprobe_inst_return) | 60 | GLOBAL_ENTRY(jprobe_inst_return) |
| 61 | br.call.sptk.many b0=jprobe_break | 61 | br.call.sptk.many b0=jprobe_break |
| 62 | END(jprobe_inst_return) | 62 | END(jprobe_inst_return) |
| 63 | |||
| 64 | GLOBAL_ENTRY(invalidate_stacked_regs) | ||
| 65 | movl r16=invalidate_restore_cfm | ||
| 66 | ;; | ||
| 67 | mov b6=r16 | ||
| 68 | ;; | ||
| 69 | br.ret.sptk.many b6 | ||
| 70 | ;; | ||
| 71 | invalidate_restore_cfm: | ||
| 72 | mov r16=ar.rsc | ||
| 73 | ;; | ||
| 74 | mov ar.rsc=r0 | ||
| 75 | ;; | ||
| 76 | loadrs | ||
| 77 | ;; | ||
| 78 | mov ar.rsc=r16 | ||
| 79 | ;; | ||
| 80 | br.cond.sptk.many rp | ||
| 81 | END(invalidate_stacked_regs) | ||
| 82 | |||
| 83 | GLOBAL_ENTRY(flush_register_stack) | ||
| 84 | // flush dirty regs to backing store (must be first in insn group) | ||
| 85 | flushrs | ||
| 86 | ;; | ||
| 87 | br.ret.sptk.many rp | ||
| 88 | END(flush_register_stack) | ||
| 89 | |||
diff --git a/arch/ia64/kernel/kprobes.c b/arch/ia64/kernel/kprobes.c index 346fedf9ea47..50ae8c7d453d 100644 --- a/arch/ia64/kernel/kprobes.c +++ b/arch/ia64/kernel/kprobes.c | |||
| @@ -766,11 +766,56 @@ int __kprobes kprobe_exceptions_notify(struct notifier_block *self, | |||
| 766 | return ret; | 766 | return ret; |
| 767 | } | 767 | } |
| 768 | 768 | ||
| 769 | struct param_bsp_cfm { | ||
| 770 | unsigned long ip; | ||
| 771 | unsigned long *bsp; | ||
| 772 | unsigned long cfm; | ||
| 773 | }; | ||
| 774 | |||
| 775 | static void ia64_get_bsp_cfm(struct unw_frame_info *info, void *arg) | ||
| 776 | { | ||
| 777 | unsigned long ip; | ||
| 778 | struct param_bsp_cfm *lp = arg; | ||
| 779 | |||
| 780 | do { | ||
| 781 | unw_get_ip(info, &ip); | ||
| 782 | if (ip == 0) | ||
| 783 | break; | ||
| 784 | if (ip == lp->ip) { | ||
| 785 | unw_get_bsp(info, (unsigned long*)&lp->bsp); | ||
| 786 | unw_get_cfm(info, (unsigned long*)&lp->cfm); | ||
| 787 | return; | ||
| 788 | } | ||
| 789 | } while (unw_unwind(info) >= 0); | ||
| 790 | lp->bsp = 0; | ||
| 791 | lp->cfm = 0; | ||
| 792 | return; | ||
| 793 | } | ||
| 794 | |||
| 769 | int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) | 795 | int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) |
| 770 | { | 796 | { |
| 771 | struct jprobe *jp = container_of(p, struct jprobe, kp); | 797 | struct jprobe *jp = container_of(p, struct jprobe, kp); |
| 772 | unsigned long addr = ((struct fnptr *)(jp->entry))->ip; | 798 | unsigned long addr = ((struct fnptr *)(jp->entry))->ip; |
| 773 | struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); | 799 | struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); |
| 800 | struct param_bsp_cfm pa; | ||
| 801 | int bytes; | ||
| 802 | |||
| 803 | /* | ||
| 804 | * Callee owns the argument space and could overwrite it, eg | ||
| 805 | * tail call optimization. So to be absolutely safe | ||
| 806 | * we save the argument space before transfering the control | ||
| 807 | * to instrumented jprobe function which runs in | ||
| 808 | * the process context | ||
| 809 | */ | ||
| 810 | pa.ip = regs->cr_iip; | ||
| 811 | unw_init_running(ia64_get_bsp_cfm, &pa); | ||
| 812 | bytes = (char *)ia64_rse_skip_regs(pa.bsp, pa.cfm & 0x3f) | ||
| 813 | - (char *)pa.bsp; | ||
| 814 | memcpy( kcb->jprobes_saved_stacked_regs, | ||
| 815 | pa.bsp, | ||
| 816 | bytes ); | ||
| 817 | kcb->bsp = pa.bsp; | ||
| 818 | kcb->cfm = pa.cfm; | ||
| 774 | 819 | ||
| 775 | /* save architectural state */ | 820 | /* save architectural state */ |
| 776 | kcb->jprobe_saved_regs = *regs; | 821 | kcb->jprobe_saved_regs = *regs; |
| @@ -792,8 +837,20 @@ int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) | |||
| 792 | int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) | 837 | int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) |
| 793 | { | 838 | { |
| 794 | struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); | 839 | struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); |
| 840 | int bytes; | ||
| 795 | 841 | ||
| 842 | /* restoring architectural state */ | ||
| 796 | *regs = kcb->jprobe_saved_regs; | 843 | *regs = kcb->jprobe_saved_regs; |
| 844 | |||
| 845 | /* restoring the original argument space */ | ||
| 846 | flush_register_stack(); | ||
| 847 | bytes = (char *)ia64_rse_skip_regs(kcb->bsp, kcb->cfm & 0x3f) | ||
| 848 | - (char *)kcb->bsp; | ||
| 849 | memcpy( kcb->bsp, | ||
| 850 | kcb->jprobes_saved_stacked_regs, | ||
| 851 | bytes ); | ||
| 852 | invalidate_stacked_regs(); | ||
| 853 | |||
| 797 | preempt_enable_no_resched(); | 854 | preempt_enable_no_resched(); |
| 798 | return 1; | 855 | return 1; |
| 799 | } | 856 | } |
diff --git a/include/asm-ia64/kprobes.h b/include/asm-ia64/kprobes.h index a74b68104559..8c0fc227f0fb 100644 --- a/include/asm-ia64/kprobes.h +++ b/include/asm-ia64/kprobes.h | |||
| @@ -68,10 +68,14 @@ struct prev_kprobe { | |||
| 68 | unsigned long status; | 68 | unsigned long status; |
| 69 | }; | 69 | }; |
| 70 | 70 | ||
| 71 | #define MAX_PARAM_RSE_SIZE (0x60+0x60/0x3f) | ||
| 71 | /* per-cpu kprobe control block */ | 72 | /* per-cpu kprobe control block */ |
| 72 | struct kprobe_ctlblk { | 73 | struct kprobe_ctlblk { |
| 73 | unsigned long kprobe_status; | 74 | unsigned long kprobe_status; |
| 74 | struct pt_regs jprobe_saved_regs; | 75 | struct pt_regs jprobe_saved_regs; |
| 76 | unsigned long jprobes_saved_stacked_regs[MAX_PARAM_RSE_SIZE]; | ||
| 77 | unsigned long *bsp; | ||
| 78 | unsigned long cfm; | ||
| 75 | struct prev_kprobe prev_kprobe; | 79 | struct prev_kprobe prev_kprobe; |
| 76 | }; | 80 | }; |
| 77 | 81 | ||
| @@ -118,5 +122,7 @@ extern int kprobe_exceptions_notify(struct notifier_block *self, | |||
| 118 | static inline void jprobe_return(void) | 122 | static inline void jprobe_return(void) |
| 119 | { | 123 | { |
| 120 | } | 124 | } |
| 125 | extern void invalidate_stacked_regs(void); | ||
| 126 | extern void flush_register_stack(void); | ||
| 121 | 127 | ||
| 122 | #endif /* _ASM_KPROBES_H */ | 128 | #endif /* _ASM_KPROBES_H */ |
