diff options
Diffstat (limited to 'arch/x86/kernel/ftrace.c')
-rw-r--r-- | arch/x86/kernel/ftrace.c | 41 |
1 files changed, 22 insertions, 19 deletions
diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c index 7ee8067cbf45..8257a59704ae 100644 --- a/arch/x86/kernel/ftrace.c +++ b/arch/x86/kernel/ftrace.c | |||
@@ -733,18 +733,20 @@ union ftrace_op_code_union { | |||
733 | } __attribute__((packed)); | 733 | } __attribute__((packed)); |
734 | }; | 734 | }; |
735 | 735 | ||
736 | #define RET_SIZE 1 | ||
737 | |||
736 | static unsigned long | 738 | static unsigned long |
737 | create_trampoline(struct ftrace_ops *ops, unsigned int *tramp_size) | 739 | create_trampoline(struct ftrace_ops *ops, unsigned int *tramp_size) |
738 | { | 740 | { |
739 | unsigned const char *jmp; | ||
740 | unsigned long start_offset; | 741 | unsigned long start_offset; |
741 | unsigned long end_offset; | 742 | unsigned long end_offset; |
742 | unsigned long op_offset; | 743 | unsigned long op_offset; |
743 | unsigned long offset; | 744 | unsigned long offset; |
744 | unsigned long size; | 745 | unsigned long size; |
745 | unsigned long ip; | 746 | unsigned long retq; |
746 | unsigned long *ptr; | 747 | unsigned long *ptr; |
747 | void *trampoline; | 748 | void *trampoline; |
749 | void *ip; | ||
748 | /* 48 8b 15 <offset> is movq <offset>(%rip), %rdx */ | 750 | /* 48 8b 15 <offset> is movq <offset>(%rip), %rdx */ |
749 | unsigned const char op_ref[] = { 0x48, 0x8b, 0x15 }; | 751 | unsigned const char op_ref[] = { 0x48, 0x8b, 0x15 }; |
750 | union ftrace_op_code_union op_ptr; | 752 | union ftrace_op_code_union op_ptr; |
@@ -764,27 +766,27 @@ create_trampoline(struct ftrace_ops *ops, unsigned int *tramp_size) | |||
764 | 766 | ||
765 | /* | 767 | /* |
766 | * Allocate enough size to store the ftrace_caller code, | 768 | * Allocate enough size to store the ftrace_caller code, |
767 | * the jmp to ftrace_epilogue, as well as the address of | 769 | * the iret , as well as the address of the ftrace_ops this |
768 | * the ftrace_ops this trampoline is used for. | 770 | * trampoline is used for. |
769 | */ | 771 | */ |
770 | trampoline = alloc_tramp(size + MCOUNT_INSN_SIZE + sizeof(void *)); | 772 | trampoline = alloc_tramp(size + RET_SIZE + sizeof(void *)); |
771 | if (!trampoline) | 773 | if (!trampoline) |
772 | return 0; | 774 | return 0; |
773 | 775 | ||
774 | *tramp_size = size + MCOUNT_INSN_SIZE + sizeof(void *); | 776 | *tramp_size = size + RET_SIZE + sizeof(void *); |
775 | 777 | ||
776 | /* Copy ftrace_caller onto the trampoline memory */ | 778 | /* Copy ftrace_caller onto the trampoline memory */ |
777 | ret = probe_kernel_read(trampoline, (void *)start_offset, size); | 779 | ret = probe_kernel_read(trampoline, (void *)start_offset, size); |
778 | if (WARN_ON(ret < 0)) { | 780 | if (WARN_ON(ret < 0)) |
779 | tramp_free(trampoline, *tramp_size); | 781 | goto fail; |
780 | return 0; | ||
781 | } | ||
782 | 782 | ||
783 | ip = (unsigned long)trampoline + size; | 783 | ip = trampoline + size; |
784 | 784 | ||
785 | /* The trampoline ends with a jmp to ftrace_epilogue */ | 785 | /* The trampoline ends with ret(q) */ |
786 | jmp = ftrace_jmp_replace(ip, (unsigned long)ftrace_epilogue); | 786 | retq = (unsigned long)ftrace_stub; |
787 | memcpy(trampoline + size, jmp, MCOUNT_INSN_SIZE); | 787 | ret = probe_kernel_read(ip, (void *)retq, RET_SIZE); |
788 | if (WARN_ON(ret < 0)) | ||
789 | goto fail; | ||
788 | 790 | ||
789 | /* | 791 | /* |
790 | * The address of the ftrace_ops that is used for this trampoline | 792 | * The address of the ftrace_ops that is used for this trampoline |
@@ -794,17 +796,15 @@ create_trampoline(struct ftrace_ops *ops, unsigned int *tramp_size) | |||
794 | * the global function_trace_op variable. | 796 | * the global function_trace_op variable. |
795 | */ | 797 | */ |
796 | 798 | ||
797 | ptr = (unsigned long *)(trampoline + size + MCOUNT_INSN_SIZE); | 799 | ptr = (unsigned long *)(trampoline + size + RET_SIZE); |
798 | *ptr = (unsigned long)ops; | 800 | *ptr = (unsigned long)ops; |
799 | 801 | ||
800 | op_offset -= start_offset; | 802 | op_offset -= start_offset; |
801 | memcpy(&op_ptr, trampoline + op_offset, OP_REF_SIZE); | 803 | memcpy(&op_ptr, trampoline + op_offset, OP_REF_SIZE); |
802 | 804 | ||
803 | /* Are we pointing to the reference? */ | 805 | /* Are we pointing to the reference? */ |
804 | if (WARN_ON(memcmp(op_ptr.op, op_ref, 3) != 0)) { | 806 | if (WARN_ON(memcmp(op_ptr.op, op_ref, 3) != 0)) |
805 | tramp_free(trampoline, *tramp_size); | 807 | goto fail; |
806 | return 0; | ||
807 | } | ||
808 | 808 | ||
809 | /* Load the contents of ptr into the callback parameter */ | 809 | /* Load the contents of ptr into the callback parameter */ |
810 | offset = (unsigned long)ptr; | 810 | offset = (unsigned long)ptr; |
@@ -819,6 +819,9 @@ create_trampoline(struct ftrace_ops *ops, unsigned int *tramp_size) | |||
819 | ops->flags |= FTRACE_OPS_FL_ALLOC_TRAMP; | 819 | ops->flags |= FTRACE_OPS_FL_ALLOC_TRAMP; |
820 | 820 | ||
821 | return (unsigned long)trampoline; | 821 | return (unsigned long)trampoline; |
822 | fail: | ||
823 | tramp_free(trampoline, *tramp_size); | ||
824 | return 0; | ||
822 | } | 825 | } |
823 | 826 | ||
824 | static unsigned long calc_trampoline_call_offset(bool save_regs) | 827 | static unsigned long calc_trampoline_call_offset(bool save_regs) |