diff options
Diffstat (limited to 'arch/arm/kernel/ftrace.c')
-rw-r--r-- | arch/arm/kernel/ftrace.c | 100 |
1 files changed, 28 insertions, 72 deletions
diff --git a/arch/arm/kernel/ftrace.c b/arch/arm/kernel/ftrace.c index c0062ad1e847..df0bf0c8cb79 100644 --- a/arch/arm/kernel/ftrace.c +++ b/arch/arm/kernel/ftrace.c | |||
@@ -16,10 +16,13 @@ | |||
16 | #include <linux/uaccess.h> | 16 | #include <linux/uaccess.h> |
17 | 17 | ||
18 | #include <asm/cacheflush.h> | 18 | #include <asm/cacheflush.h> |
19 | #include <asm/opcodes.h> | ||
19 | #include <asm/ftrace.h> | 20 | #include <asm/ftrace.h> |
20 | 21 | ||
22 | #include "insn.h" | ||
23 | |||
21 | #ifdef CONFIG_THUMB2_KERNEL | 24 | #ifdef CONFIG_THUMB2_KERNEL |
22 | #define NOP 0xeb04f85d /* pop.w {lr} */ | 25 | #define NOP 0xf85deb04 /* pop.w {lr} */ |
23 | #else | 26 | #else |
24 | #define NOP 0xe8bd4000 /* pop {lr} */ | 27 | #define NOP 0xe8bd4000 /* pop {lr} */ |
25 | #endif | 28 | #endif |
@@ -60,76 +63,31 @@ static unsigned long adjust_address(struct dyn_ftrace *rec, unsigned long addr) | |||
60 | } | 63 | } |
61 | #endif | 64 | #endif |
62 | 65 | ||
63 | #ifdef CONFIG_THUMB2_KERNEL | ||
64 | static unsigned long ftrace_gen_branch(unsigned long pc, unsigned long addr, | ||
65 | bool link) | ||
66 | { | ||
67 | unsigned long s, j1, j2, i1, i2, imm10, imm11; | ||
68 | unsigned long first, second; | ||
69 | long offset; | ||
70 | |||
71 | offset = (long)addr - (long)(pc + 4); | ||
72 | if (offset < -16777216 || offset > 16777214) { | ||
73 | WARN_ON_ONCE(1); | ||
74 | return 0; | ||
75 | } | ||
76 | |||
77 | s = (offset >> 24) & 0x1; | ||
78 | i1 = (offset >> 23) & 0x1; | ||
79 | i2 = (offset >> 22) & 0x1; | ||
80 | imm10 = (offset >> 12) & 0x3ff; | ||
81 | imm11 = (offset >> 1) & 0x7ff; | ||
82 | |||
83 | j1 = (!i1) ^ s; | ||
84 | j2 = (!i2) ^ s; | ||
85 | |||
86 | first = 0xf000 | (s << 10) | imm10; | ||
87 | second = 0x9000 | (j1 << 13) | (j2 << 11) | imm11; | ||
88 | if (link) | ||
89 | second |= 1 << 14; | ||
90 | |||
91 | return (second << 16) | first; | ||
92 | } | ||
93 | #else | ||
94 | static unsigned long ftrace_gen_branch(unsigned long pc, unsigned long addr, | ||
95 | bool link) | ||
96 | { | ||
97 | unsigned long opcode = 0xea000000; | ||
98 | long offset; | ||
99 | |||
100 | if (link) | ||
101 | opcode |= 1 << 24; | ||
102 | |||
103 | offset = (long)addr - (long)(pc + 8); | ||
104 | if (unlikely(offset < -33554432 || offset > 33554428)) { | ||
105 | /* Can't generate branches that far (from ARM ARM). Ftrace | ||
106 | * doesn't generate branches outside of kernel text. | ||
107 | */ | ||
108 | WARN_ON_ONCE(1); | ||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | offset = (offset >> 2) & 0x00ffffff; | ||
113 | |||
114 | return opcode | offset; | ||
115 | } | ||
116 | #endif | ||
117 | |||
118 | static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr) | 66 | static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr) |
119 | { | 67 | { |
120 | return ftrace_gen_branch(pc, addr, true); | 68 | return arm_gen_branch_link(pc, addr); |
121 | } | 69 | } |
122 | 70 | ||
123 | static int ftrace_modify_code(unsigned long pc, unsigned long old, | 71 | static int ftrace_modify_code(unsigned long pc, unsigned long old, |
124 | unsigned long new) | 72 | unsigned long new, bool validate) |
125 | { | 73 | { |
126 | unsigned long replaced; | 74 | unsigned long replaced; |
127 | 75 | ||
128 | if (probe_kernel_read(&replaced, (void *)pc, MCOUNT_INSN_SIZE)) | 76 | if (IS_ENABLED(CONFIG_THUMB2_KERNEL)) { |
129 | return -EFAULT; | 77 | old = __opcode_to_mem_thumb32(old); |
78 | new = __opcode_to_mem_thumb32(new); | ||
79 | } else { | ||
80 | old = __opcode_to_mem_arm(old); | ||
81 | new = __opcode_to_mem_arm(new); | ||
82 | } | ||
130 | 83 | ||
131 | if (replaced != old) | 84 | if (validate) { |
132 | return -EINVAL; | 85 | if (probe_kernel_read(&replaced, (void *)pc, MCOUNT_INSN_SIZE)) |
86 | return -EFAULT; | ||
87 | |||
88 | if (replaced != old) | ||
89 | return -EINVAL; | ||
90 | } | ||
133 | 91 | ||
134 | if (probe_kernel_write((void *)pc, &new, MCOUNT_INSN_SIZE)) | 92 | if (probe_kernel_write((void *)pc, &new, MCOUNT_INSN_SIZE)) |
135 | return -EPERM; | 93 | return -EPERM; |
@@ -141,23 +99,21 @@ static int ftrace_modify_code(unsigned long pc, unsigned long old, | |||
141 | 99 | ||
142 | int ftrace_update_ftrace_func(ftrace_func_t func) | 100 | int ftrace_update_ftrace_func(ftrace_func_t func) |
143 | { | 101 | { |
144 | unsigned long pc, old; | 102 | unsigned long pc; |
145 | unsigned long new; | 103 | unsigned long new; |
146 | int ret; | 104 | int ret; |
147 | 105 | ||
148 | pc = (unsigned long)&ftrace_call; | 106 | pc = (unsigned long)&ftrace_call; |
149 | memcpy(&old, &ftrace_call, MCOUNT_INSN_SIZE); | ||
150 | new = ftrace_call_replace(pc, (unsigned long)func); | 107 | new = ftrace_call_replace(pc, (unsigned long)func); |
151 | 108 | ||
152 | ret = ftrace_modify_code(pc, old, new); | 109 | ret = ftrace_modify_code(pc, 0, new, false); |
153 | 110 | ||
154 | #ifdef CONFIG_OLD_MCOUNT | 111 | #ifdef CONFIG_OLD_MCOUNT |
155 | if (!ret) { | 112 | if (!ret) { |
156 | pc = (unsigned long)&ftrace_call_old; | 113 | pc = (unsigned long)&ftrace_call_old; |
157 | memcpy(&old, &ftrace_call_old, MCOUNT_INSN_SIZE); | ||
158 | new = ftrace_call_replace(pc, (unsigned long)func); | 114 | new = ftrace_call_replace(pc, (unsigned long)func); |
159 | 115 | ||
160 | ret = ftrace_modify_code(pc, old, new); | 116 | ret = ftrace_modify_code(pc, 0, new, false); |
161 | } | 117 | } |
162 | #endif | 118 | #endif |
163 | 119 | ||
@@ -172,7 +128,7 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) | |||
172 | old = ftrace_nop_replace(rec); | 128 | old = ftrace_nop_replace(rec); |
173 | new = ftrace_call_replace(ip, adjust_address(rec, addr)); | 129 | new = ftrace_call_replace(ip, adjust_address(rec, addr)); |
174 | 130 | ||
175 | return ftrace_modify_code(rec->ip, old, new); | 131 | return ftrace_modify_code(rec->ip, old, new, true); |
176 | } | 132 | } |
177 | 133 | ||
178 | int ftrace_make_nop(struct module *mod, | 134 | int ftrace_make_nop(struct module *mod, |
@@ -185,7 +141,7 @@ int ftrace_make_nop(struct module *mod, | |||
185 | 141 | ||
186 | old = ftrace_call_replace(ip, adjust_address(rec, addr)); | 142 | old = ftrace_call_replace(ip, adjust_address(rec, addr)); |
187 | new = ftrace_nop_replace(rec); | 143 | new = ftrace_nop_replace(rec); |
188 | ret = ftrace_modify_code(ip, old, new); | 144 | ret = ftrace_modify_code(ip, old, new, true); |
189 | 145 | ||
190 | #ifdef CONFIG_OLD_MCOUNT | 146 | #ifdef CONFIG_OLD_MCOUNT |
191 | if (ret == -EINVAL && addr == MCOUNT_ADDR) { | 147 | if (ret == -EINVAL && addr == MCOUNT_ADDR) { |
@@ -193,7 +149,7 @@ int ftrace_make_nop(struct module *mod, | |||
193 | 149 | ||
194 | old = ftrace_call_replace(ip, adjust_address(rec, addr)); | 150 | old = ftrace_call_replace(ip, adjust_address(rec, addr)); |
195 | new = ftrace_nop_replace(rec); | 151 | new = ftrace_nop_replace(rec); |
196 | ret = ftrace_modify_code(ip, old, new); | 152 | ret = ftrace_modify_code(ip, old, new, true); |
197 | } | 153 | } |
198 | #endif | 154 | #endif |
199 | 155 | ||
@@ -249,12 +205,12 @@ static int __ftrace_modify_caller(unsigned long *callsite, | |||
249 | { | 205 | { |
250 | unsigned long caller_fn = (unsigned long) func; | 206 | unsigned long caller_fn = (unsigned long) func; |
251 | unsigned long pc = (unsigned long) callsite; | 207 | unsigned long pc = (unsigned long) callsite; |
252 | unsigned long branch = ftrace_gen_branch(pc, caller_fn, false); | 208 | unsigned long branch = arm_gen_branch(pc, caller_fn); |
253 | unsigned long nop = 0xe1a00000; /* mov r0, r0 */ | 209 | unsigned long nop = 0xe1a00000; /* mov r0, r0 */ |
254 | unsigned long old = enable ? nop : branch; | 210 | unsigned long old = enable ? nop : branch; |
255 | unsigned long new = enable ? branch : nop; | 211 | unsigned long new = enable ? branch : nop; |
256 | 212 | ||
257 | return ftrace_modify_code(pc, old, new); | 213 | return ftrace_modify_code(pc, old, new, true); |
258 | } | 214 | } |
259 | 215 | ||
260 | static int ftrace_modify_graph_caller(bool enable) | 216 | static int ftrace_modify_graph_caller(bool enable) |