diff options
Diffstat (limited to 'arch/x86/kernel/ftrace.c')
| -rw-r--r-- | arch/x86/kernel/ftrace.c | 50 |
1 files changed, 21 insertions, 29 deletions
diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c index d073d981a730..50ea0ac8c9bf 100644 --- a/arch/x86/kernel/ftrace.c +++ b/arch/x86/kernel/ftrace.c | |||
| @@ -21,8 +21,7 @@ | |||
| 21 | #include <asm/nops.h> | 21 | #include <asm/nops.h> |
| 22 | 22 | ||
| 23 | 23 | ||
| 24 | /* Long is fine, even if it is only 4 bytes ;-) */ | 24 | static unsigned char ftrace_nop[MCOUNT_INSN_SIZE]; |
| 25 | static unsigned long *ftrace_nop; | ||
| 26 | 25 | ||
| 27 | union ftrace_code_union { | 26 | union ftrace_code_union { |
| 28 | char code[MCOUNT_INSN_SIZE]; | 27 | char code[MCOUNT_INSN_SIZE]; |
| @@ -33,17 +32,17 @@ union ftrace_code_union { | |||
| 33 | }; | 32 | }; |
| 34 | 33 | ||
| 35 | 34 | ||
| 36 | static int notrace ftrace_calc_offset(long ip, long addr) | 35 | static int ftrace_calc_offset(long ip, long addr) |
| 37 | { | 36 | { |
| 38 | return (int)(addr - ip); | 37 | return (int)(addr - ip); |
| 39 | } | 38 | } |
| 40 | 39 | ||
| 41 | notrace unsigned char *ftrace_nop_replace(void) | 40 | unsigned char *ftrace_nop_replace(void) |
| 42 | { | 41 | { |
| 43 | return (char *)ftrace_nop; | 42 | return ftrace_nop; |
| 44 | } | 43 | } |
| 45 | 44 | ||
| 46 | notrace unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr) | 45 | unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr) |
| 47 | { | 46 | { |
| 48 | static union ftrace_code_union calc; | 47 | static union ftrace_code_union calc; |
| 49 | 48 | ||
| @@ -57,7 +56,7 @@ notrace unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr) | |||
| 57 | return calc.code; | 56 | return calc.code; |
| 58 | } | 57 | } |
| 59 | 58 | ||
| 60 | notrace int | 59 | int |
| 61 | ftrace_modify_code(unsigned long ip, unsigned char *old_code, | 60 | ftrace_modify_code(unsigned long ip, unsigned char *old_code, |
| 62 | unsigned char *new_code) | 61 | unsigned char *new_code) |
| 63 | { | 62 | { |
| @@ -66,26 +65,31 @@ ftrace_modify_code(unsigned long ip, unsigned char *old_code, | |||
| 66 | /* | 65 | /* |
| 67 | * Note: Due to modules and __init, code can | 66 | * Note: Due to modules and __init, code can |
| 68 | * disappear and change, we need to protect against faulting | 67 | * disappear and change, we need to protect against faulting |
| 69 | * as well as code changing. | 68 | * as well as code changing. We do this by using the |
| 69 | * probe_kernel_* functions. | ||
| 70 | * | 70 | * |
| 71 | * No real locking needed, this code is run through | 71 | * No real locking needed, this code is run through |
| 72 | * kstop_machine, or before SMP starts. | 72 | * kstop_machine, or before SMP starts. |
| 73 | */ | 73 | */ |
| 74 | if (__copy_from_user_inatomic(replaced, (char __user *)ip, MCOUNT_INSN_SIZE)) | ||
| 75 | return 1; | ||
| 76 | 74 | ||
| 75 | /* read the text we want to modify */ | ||
| 76 | if (probe_kernel_read(replaced, (void *)ip, MCOUNT_INSN_SIZE)) | ||
| 77 | return -EFAULT; | ||
| 78 | |||
| 79 | /* Make sure it is what we expect it to be */ | ||
| 77 | if (memcmp(replaced, old_code, MCOUNT_INSN_SIZE) != 0) | 80 | if (memcmp(replaced, old_code, MCOUNT_INSN_SIZE) != 0) |
| 78 | return 2; | 81 | return -EINVAL; |
| 79 | 82 | ||
| 80 | WARN_ON_ONCE(__copy_to_user_inatomic((char __user *)ip, new_code, | 83 | /* replace the text with the new text */ |
| 81 | MCOUNT_INSN_SIZE)); | 84 | if (probe_kernel_write((void *)ip, new_code, MCOUNT_INSN_SIZE)) |
| 85 | return -EPERM; | ||
| 82 | 86 | ||
| 83 | sync_core(); | 87 | sync_core(); |
| 84 | 88 | ||
| 85 | return 0; | 89 | return 0; |
| 86 | } | 90 | } |
| 87 | 91 | ||
| 88 | notrace int ftrace_update_ftrace_func(ftrace_func_t func) | 92 | int ftrace_update_ftrace_func(ftrace_func_t func) |
| 89 | { | 93 | { |
| 90 | unsigned long ip = (unsigned long)(&ftrace_call); | 94 | unsigned long ip = (unsigned long)(&ftrace_call); |
| 91 | unsigned char old[MCOUNT_INSN_SIZE], *new; | 95 | unsigned char old[MCOUNT_INSN_SIZE], *new; |
| @@ -98,13 +102,6 @@ notrace int ftrace_update_ftrace_func(ftrace_func_t func) | |||
| 98 | return ret; | 102 | return ret; |
| 99 | } | 103 | } |
| 100 | 104 | ||
| 101 | notrace int ftrace_mcount_set(unsigned long *data) | ||
| 102 | { | ||
| 103 | /* mcount is initialized as a nop */ | ||
| 104 | *data = 0; | ||
| 105 | return 0; | ||
| 106 | } | ||
| 107 | |||
| 108 | int __init ftrace_dyn_arch_init(void *data) | 105 | int __init ftrace_dyn_arch_init(void *data) |
| 109 | { | 106 | { |
| 110 | extern const unsigned char ftrace_test_p6nop[]; | 107 | extern const unsigned char ftrace_test_p6nop[]; |
| @@ -127,9 +124,6 @@ int __init ftrace_dyn_arch_init(void *data) | |||
| 127 | * TODO: check the cpuid to determine the best nop. | 124 | * TODO: check the cpuid to determine the best nop. |
| 128 | */ | 125 | */ |
| 129 | asm volatile ( | 126 | asm volatile ( |
| 130 | "jmp ftrace_test_jmp\n" | ||
| 131 | /* This code needs to stay around */ | ||
| 132 | ".section .text, \"ax\"\n" | ||
| 133 | "ftrace_test_jmp:" | 127 | "ftrace_test_jmp:" |
| 134 | "jmp ftrace_test_p6nop\n" | 128 | "jmp ftrace_test_p6nop\n" |
| 135 | "nop\n" | 129 | "nop\n" |
| @@ -140,8 +134,6 @@ int __init ftrace_dyn_arch_init(void *data) | |||
| 140 | "jmp 1f\n" | 134 | "jmp 1f\n" |
| 141 | "ftrace_test_nop5:" | 135 | "ftrace_test_nop5:" |
| 142 | ".byte 0x66,0x66,0x66,0x66,0x90\n" | 136 | ".byte 0x66,0x66,0x66,0x66,0x90\n" |
| 143 | "jmp 1f\n" | ||
| 144 | ".previous\n" | ||
| 145 | "1:" | 137 | "1:" |
| 146 | ".section .fixup, \"ax\"\n" | 138 | ".section .fixup, \"ax\"\n" |
| 147 | "2: movl $1, %0\n" | 139 | "2: movl $1, %0\n" |
| @@ -156,15 +148,15 @@ int __init ftrace_dyn_arch_init(void *data) | |||
| 156 | switch (faulted) { | 148 | switch (faulted) { |
| 157 | case 0: | 149 | case 0: |
| 158 | pr_info("ftrace: converting mcount calls to 0f 1f 44 00 00\n"); | 150 | pr_info("ftrace: converting mcount calls to 0f 1f 44 00 00\n"); |
| 159 | ftrace_nop = (unsigned long *)ftrace_test_p6nop; | 151 | memcpy(ftrace_nop, ftrace_test_p6nop, MCOUNT_INSN_SIZE); |
| 160 | break; | 152 | break; |
| 161 | case 1: | 153 | case 1: |
| 162 | pr_info("ftrace: converting mcount calls to 66 66 66 66 90\n"); | 154 | pr_info("ftrace: converting mcount calls to 66 66 66 66 90\n"); |
| 163 | ftrace_nop = (unsigned long *)ftrace_test_nop5; | 155 | memcpy(ftrace_nop, ftrace_test_nop5, MCOUNT_INSN_SIZE); |
| 164 | break; | 156 | break; |
| 165 | case 2: | 157 | case 2: |
| 166 | pr_info("ftrace: converting mcount calls to jmp . + 5\n"); | 158 | pr_info("ftrace: converting mcount calls to jmp . + 5\n"); |
| 167 | ftrace_nop = (unsigned long *)ftrace_test_jmp; | 159 | memcpy(ftrace_nop, ftrace_test_jmp, MCOUNT_INSN_SIZE); |
| 168 | break; | 160 | break; |
| 169 | } | 161 | } |
| 170 | 162 | ||
