diff options
Diffstat (limited to 'arch/powerpc/kernel/ftrace.c')
-rw-r--r-- | arch/powerpc/kernel/ftrace.c | 461 |
1 files changed, 421 insertions, 40 deletions
diff --git a/arch/powerpc/kernel/ftrace.c b/arch/powerpc/kernel/ftrace.c index f4b006ed0ab1..5355244c99ff 100644 --- a/arch/powerpc/kernel/ftrace.c +++ b/arch/powerpc/kernel/ftrace.c | |||
@@ -9,22 +9,30 @@ | |||
9 | 9 | ||
10 | #include <linux/spinlock.h> | 10 | #include <linux/spinlock.h> |
11 | #include <linux/hardirq.h> | 11 | #include <linux/hardirq.h> |
12 | #include <linux/uaccess.h> | ||
13 | #include <linux/module.h> | ||
12 | #include <linux/ftrace.h> | 14 | #include <linux/ftrace.h> |
13 | #include <linux/percpu.h> | 15 | #include <linux/percpu.h> |
14 | #include <linux/init.h> | 16 | #include <linux/init.h> |
15 | #include <linux/list.h> | 17 | #include <linux/list.h> |
16 | 18 | ||
17 | #include <asm/cacheflush.h> | 19 | #include <asm/cacheflush.h> |
20 | #include <asm/code-patching.h> | ||
18 | #include <asm/ftrace.h> | 21 | #include <asm/ftrace.h> |
19 | 22 | ||
23 | #if 0 | ||
24 | #define DEBUGP printk | ||
25 | #else | ||
26 | #define DEBUGP(fmt , ...) do { } while (0) | ||
27 | #endif | ||
20 | 28 | ||
21 | static unsigned int ftrace_nop = 0x60000000; | 29 | static unsigned int ftrace_nop = PPC_NOP_INSTR; |
22 | 30 | ||
23 | #ifdef CONFIG_PPC32 | 31 | #ifdef CONFIG_PPC32 |
24 | # define GET_ADDR(addr) addr | 32 | # define GET_ADDR(addr) addr |
25 | #else | 33 | #else |
26 | /* PowerPC64's functions are data that points to the functions */ | 34 | /* PowerPC64's functions are data that points to the functions */ |
27 | # define GET_ADDR(addr) *(unsigned long *)addr | 35 | # define GET_ADDR(addr) (*(unsigned long *)addr) |
28 | #endif | 36 | #endif |
29 | 37 | ||
30 | 38 | ||
@@ -33,12 +41,12 @@ static unsigned int ftrace_calc_offset(long ip, long addr) | |||
33 | return (int)(addr - ip); | 41 | return (int)(addr - ip); |
34 | } | 42 | } |
35 | 43 | ||
36 | unsigned char *ftrace_nop_replace(void) | 44 | static unsigned char *ftrace_nop_replace(void) |
37 | { | 45 | { |
38 | return (char *)&ftrace_nop; | 46 | return (char *)&ftrace_nop; |
39 | } | 47 | } |
40 | 48 | ||
41 | unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr) | 49 | static unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr) |
42 | { | 50 | { |
43 | static unsigned int op; | 51 | static unsigned int op; |
44 | 52 | ||
@@ -68,49 +76,422 @@ unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr) | |||
68 | # define _ASM_PTR " .long " | 76 | # define _ASM_PTR " .long " |
69 | #endif | 77 | #endif |
70 | 78 | ||
71 | int | 79 | static int |
72 | ftrace_modify_code(unsigned long ip, unsigned char *old_code, | 80 | ftrace_modify_code(unsigned long ip, unsigned char *old_code, |
73 | unsigned char *new_code) | 81 | unsigned char *new_code) |
74 | { | 82 | { |
75 | unsigned replaced; | 83 | unsigned char replaced[MCOUNT_INSN_SIZE]; |
76 | unsigned old = *(unsigned *)old_code; | ||
77 | unsigned new = *(unsigned *)new_code; | ||
78 | int faulted = 0; | ||
79 | 84 | ||
80 | /* | 85 | /* |
81 | * Note: Due to modules and __init, code can | 86 | * Note: Due to modules and __init, code can |
82 | * disappear and change, we need to protect against faulting | 87 | * disappear and change, we need to protect against faulting |
83 | * as well as code changing. | 88 | * as well as code changing. We do this by using the |
89 | * probe_kernel_* functions. | ||
84 | * | 90 | * |
85 | * No real locking needed, this code is run through | 91 | * No real locking needed, this code is run through |
86 | * kstop_machine. | 92 | * kstop_machine, or before SMP starts. |
87 | */ | 93 | */ |
88 | asm volatile ( | 94 | |
89 | "1: lwz %1, 0(%2)\n" | 95 | /* read the text we want to modify */ |
90 | " cmpw %1, %5\n" | 96 | if (probe_kernel_read(replaced, (void *)ip, MCOUNT_INSN_SIZE)) |
91 | " bne 2f\n" | 97 | return -EFAULT; |
92 | " stwu %3, 0(%2)\n" | 98 | |
93 | "2:\n" | 99 | /* Make sure it is what we expect it to be */ |
94 | ".section .fixup, \"ax\"\n" | 100 | if (memcmp(replaced, old_code, MCOUNT_INSN_SIZE) != 0) |
95 | "3: li %0, 1\n" | 101 | return -EINVAL; |
96 | " b 2b\n" | 102 | |
97 | ".previous\n" | 103 | /* replace the text with the new text */ |
98 | ".section __ex_table,\"a\"\n" | 104 | if (probe_kernel_write((void *)ip, new_code, MCOUNT_INSN_SIZE)) |
99 | _ASM_ALIGN "\n" | 105 | return -EPERM; |
100 | _ASM_PTR "1b, 3b\n" | 106 | |
101 | ".previous" | 107 | flush_icache_range(ip, ip + 8); |
102 | : "=r"(faulted), "=r"(replaced) | 108 | |
103 | : "r"(ip), "r"(new), | 109 | return 0; |
104 | "0"(faulted), "r"(old) | 110 | } |
105 | : "memory"); | 111 | |
106 | 112 | /* | |
107 | if (replaced != old && replaced != new) | 113 | * Helper functions that are the same for both PPC64 and PPC32. |
108 | faulted = 2; | 114 | */ |
109 | 115 | static int test_24bit_addr(unsigned long ip, unsigned long addr) | |
110 | if (!faulted) | 116 | { |
111 | flush_icache_range(ip, ip + 8); | 117 | |
112 | 118 | /* use the create_branch to verify that this offset can be branched */ | |
113 | return faulted; | 119 | return create_branch((unsigned int *)ip, addr, 0); |
120 | } | ||
121 | |||
122 | static int is_bl_op(unsigned int op) | ||
123 | { | ||
124 | return (op & 0xfc000003) == 0x48000001; | ||
125 | } | ||
126 | |||
127 | static unsigned long find_bl_target(unsigned long ip, unsigned int op) | ||
128 | { | ||
129 | static int offset; | ||
130 | |||
131 | offset = (op & 0x03fffffc); | ||
132 | /* make it signed */ | ||
133 | if (offset & 0x02000000) | ||
134 | offset |= 0xfe000000; | ||
135 | |||
136 | return ip + (long)offset; | ||
137 | } | ||
138 | |||
139 | #ifdef CONFIG_PPC64 | ||
140 | static int | ||
141 | __ftrace_make_nop(struct module *mod, | ||
142 | struct dyn_ftrace *rec, unsigned long addr) | ||
143 | { | ||
144 | unsigned int op; | ||
145 | unsigned int jmp[5]; | ||
146 | unsigned long ptr; | ||
147 | unsigned long ip = rec->ip; | ||
148 | unsigned long tramp; | ||
149 | int offset; | ||
150 | |||
151 | /* read where this goes */ | ||
152 | if (probe_kernel_read(&op, (void *)ip, sizeof(int))) | ||
153 | return -EFAULT; | ||
154 | |||
155 | /* Make sure that that this is still a 24bit jump */ | ||
156 | if (!is_bl_op(op)) { | ||
157 | printk(KERN_ERR "Not expected bl: opcode is %x\n", op); | ||
158 | return -EINVAL; | ||
159 | } | ||
160 | |||
161 | /* lets find where the pointer goes */ | ||
162 | tramp = find_bl_target(ip, op); | ||
163 | |||
164 | /* | ||
165 | * On PPC64 the trampoline looks like: | ||
166 | * 0x3d, 0x82, 0x00, 0x00, addis r12,r2, <high> | ||
167 | * 0x39, 0x8c, 0x00, 0x00, addi r12,r12, <low> | ||
168 | * Where the bytes 2,3,6 and 7 make up the 32bit offset | ||
169 | * to the TOC that holds the pointer. | ||
170 | * to jump to. | ||
171 | * 0xf8, 0x41, 0x00, 0x28, std r2,40(r1) | ||
172 | * 0xe9, 0x6c, 0x00, 0x20, ld r11,32(r12) | ||
173 | * The actually address is 32 bytes from the offset | ||
174 | * into the TOC. | ||
175 | * 0xe8, 0x4c, 0x00, 0x28, ld r2,40(r12) | ||
176 | */ | ||
177 | |||
178 | DEBUGP("ip:%lx jumps to %lx r2: %lx", ip, tramp, mod->arch.toc); | ||
179 | |||
180 | /* Find where the trampoline jumps to */ | ||
181 | if (probe_kernel_read(jmp, (void *)tramp, sizeof(jmp))) { | ||
182 | printk(KERN_ERR "Failed to read %lx\n", tramp); | ||
183 | return -EFAULT; | ||
184 | } | ||
185 | |||
186 | DEBUGP(" %08x %08x", jmp[0], jmp[1]); | ||
187 | |||
188 | /* verify that this is what we expect it to be */ | ||
189 | if (((jmp[0] & 0xffff0000) != 0x3d820000) || | ||
190 | ((jmp[1] & 0xffff0000) != 0x398c0000) || | ||
191 | (jmp[2] != 0xf8410028) || | ||
192 | (jmp[3] != 0xe96c0020) || | ||
193 | (jmp[4] != 0xe84c0028)) { | ||
194 | printk(KERN_ERR "Not a trampoline\n"); | ||
195 | return -EINVAL; | ||
196 | } | ||
197 | |||
198 | offset = (unsigned)((unsigned short)jmp[0]) << 16 | | ||
199 | (unsigned)((unsigned short)jmp[1]); | ||
200 | |||
201 | DEBUGP(" %x ", offset); | ||
202 | |||
203 | /* get the address this jumps too */ | ||
204 | tramp = mod->arch.toc + offset + 32; | ||
205 | DEBUGP("toc: %lx", tramp); | ||
206 | |||
207 | if (probe_kernel_read(jmp, (void *)tramp, 8)) { | ||
208 | printk(KERN_ERR "Failed to read %lx\n", tramp); | ||
209 | return -EFAULT; | ||
210 | } | ||
211 | |||
212 | DEBUGP(" %08x %08x\n", jmp[0], jmp[1]); | ||
213 | |||
214 | ptr = ((unsigned long)jmp[0] << 32) + jmp[1]; | ||
215 | |||
216 | /* This should match what was called */ | ||
217 | if (ptr != GET_ADDR(addr)) { | ||
218 | printk(KERN_ERR "addr does not match %lx\n", ptr); | ||
219 | return -EINVAL; | ||
220 | } | ||
221 | |||
222 | /* | ||
223 | * We want to nop the line, but the next line is | ||
224 | * 0xe8, 0x41, 0x00, 0x28 ld r2,40(r1) | ||
225 | * This needs to be turned to a nop too. | ||
226 | */ | ||
227 | if (probe_kernel_read(&op, (void *)(ip+4), MCOUNT_INSN_SIZE)) | ||
228 | return -EFAULT; | ||
229 | |||
230 | if (op != 0xe8410028) { | ||
231 | printk(KERN_ERR "Next line is not ld! (%08x)\n", op); | ||
232 | return -EINVAL; | ||
233 | } | ||
234 | |||
235 | /* | ||
236 | * Milton Miller pointed out that we can not blindly do nops. | ||
237 | * If a task was preempted when calling a trace function, | ||
238 | * the nops will remove the way to restore the TOC in r2 | ||
239 | * and the r2 TOC will get corrupted. | ||
240 | */ | ||
241 | |||
242 | /* | ||
243 | * Replace: | ||
244 | * bl <tramp> <==== will be replaced with "b 1f" | ||
245 | * ld r2,40(r1) | ||
246 | * 1: | ||
247 | */ | ||
248 | op = 0x48000008; /* b +8 */ | ||
249 | |||
250 | if (probe_kernel_write((void *)ip, &op, MCOUNT_INSN_SIZE)) | ||
251 | return -EPERM; | ||
252 | |||
253 | |||
254 | flush_icache_range(ip, ip + 8); | ||
255 | |||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | #else /* !PPC64 */ | ||
260 | static int | ||
261 | __ftrace_make_nop(struct module *mod, | ||
262 | struct dyn_ftrace *rec, unsigned long addr) | ||
263 | { | ||
264 | unsigned int op; | ||
265 | unsigned int jmp[4]; | ||
266 | unsigned long ip = rec->ip; | ||
267 | unsigned long tramp; | ||
268 | |||
269 | if (probe_kernel_read(&op, (void *)ip, MCOUNT_INSN_SIZE)) | ||
270 | return -EFAULT; | ||
271 | |||
272 | /* Make sure that that this is still a 24bit jump */ | ||
273 | if (!is_bl_op(op)) { | ||
274 | printk(KERN_ERR "Not expected bl: opcode is %x\n", op); | ||
275 | return -EINVAL; | ||
276 | } | ||
277 | |||
278 | /* lets find where the pointer goes */ | ||
279 | tramp = find_bl_target(ip, op); | ||
280 | |||
281 | /* | ||
282 | * On PPC32 the trampoline looks like: | ||
283 | * 0x3d, 0x60, 0x00, 0x00 lis r11,sym@ha | ||
284 | * 0x39, 0x6b, 0x00, 0x00 addi r11,r11,sym@l | ||
285 | * 0x7d, 0x69, 0x03, 0xa6 mtctr r11 | ||
286 | * 0x4e, 0x80, 0x04, 0x20 bctr | ||
287 | */ | ||
288 | |||
289 | DEBUGP("ip:%lx jumps to %lx", ip, tramp); | ||
290 | |||
291 | /* Find where the trampoline jumps to */ | ||
292 | if (probe_kernel_read(jmp, (void *)tramp, sizeof(jmp))) { | ||
293 | printk(KERN_ERR "Failed to read %lx\n", tramp); | ||
294 | return -EFAULT; | ||
295 | } | ||
296 | |||
297 | DEBUGP(" %08x %08x ", jmp[0], jmp[1]); | ||
298 | |||
299 | /* verify that this is what we expect it to be */ | ||
300 | if (((jmp[0] & 0xffff0000) != 0x3d600000) || | ||
301 | ((jmp[1] & 0xffff0000) != 0x396b0000) || | ||
302 | (jmp[2] != 0x7d6903a6) || | ||
303 | (jmp[3] != 0x4e800420)) { | ||
304 | printk(KERN_ERR "Not a trampoline\n"); | ||
305 | return -EINVAL; | ||
306 | } | ||
307 | |||
308 | tramp = (jmp[1] & 0xffff) | | ||
309 | ((jmp[0] & 0xffff) << 16); | ||
310 | if (tramp & 0x8000) | ||
311 | tramp -= 0x10000; | ||
312 | |||
313 | DEBUGP(" %x ", tramp); | ||
314 | |||
315 | if (tramp != addr) { | ||
316 | printk(KERN_ERR | ||
317 | "Trampoline location %08lx does not match addr\n", | ||
318 | tramp); | ||
319 | return -EINVAL; | ||
320 | } | ||
321 | |||
322 | op = PPC_NOP_INSTR; | ||
323 | |||
324 | if (probe_kernel_write((void *)ip, &op, MCOUNT_INSN_SIZE)) | ||
325 | return -EPERM; | ||
326 | |||
327 | flush_icache_range(ip, ip + 8); | ||
328 | |||
329 | return 0; | ||
330 | } | ||
331 | #endif /* PPC64 */ | ||
332 | |||
333 | int ftrace_make_nop(struct module *mod, | ||
334 | struct dyn_ftrace *rec, unsigned long addr) | ||
335 | { | ||
336 | unsigned char *old, *new; | ||
337 | unsigned long ip = rec->ip; | ||
338 | |||
339 | /* | ||
340 | * If the calling address is more that 24 bits away, | ||
341 | * then we had to use a trampoline to make the call. | ||
342 | * Otherwise just update the call site. | ||
343 | */ | ||
344 | if (test_24bit_addr(ip, addr)) { | ||
345 | /* within range */ | ||
346 | old = ftrace_call_replace(ip, addr); | ||
347 | new = ftrace_nop_replace(); | ||
348 | return ftrace_modify_code(ip, old, new); | ||
349 | } | ||
350 | |||
351 | /* | ||
352 | * Out of range jumps are called from modules. | ||
353 | * We should either already have a pointer to the module | ||
354 | * or it has been passed in. | ||
355 | */ | ||
356 | if (!rec->arch.mod) { | ||
357 | if (!mod) { | ||
358 | printk(KERN_ERR "No module loaded addr=%lx\n", | ||
359 | addr); | ||
360 | return -EFAULT; | ||
361 | } | ||
362 | rec->arch.mod = mod; | ||
363 | } else if (mod) { | ||
364 | if (mod != rec->arch.mod) { | ||
365 | printk(KERN_ERR | ||
366 | "Record mod %p not equal to passed in mod %p\n", | ||
367 | rec->arch.mod, mod); | ||
368 | return -EINVAL; | ||
369 | } | ||
370 | /* nothing to do if mod == rec->arch.mod */ | ||
371 | } else | ||
372 | mod = rec->arch.mod; | ||
373 | |||
374 | return __ftrace_make_nop(mod, rec, addr); | ||
375 | |||
376 | } | ||
377 | |||
378 | #ifdef CONFIG_PPC64 | ||
379 | static int | ||
380 | __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) | ||
381 | { | ||
382 | unsigned int op[2]; | ||
383 | unsigned long ip = rec->ip; | ||
384 | |||
385 | /* read where this goes */ | ||
386 | if (probe_kernel_read(op, (void *)ip, MCOUNT_INSN_SIZE * 2)) | ||
387 | return -EFAULT; | ||
388 | |||
389 | /* | ||
390 | * It should be pointing to two nops or | ||
391 | * b +8; ld r2,40(r1) | ||
392 | */ | ||
393 | if (((op[0] != 0x48000008) || (op[1] != 0xe8410028)) && | ||
394 | ((op[0] != PPC_NOP_INSTR) || (op[1] != PPC_NOP_INSTR))) { | ||
395 | printk(KERN_ERR "Expected NOPs but have %x %x\n", op[0], op[1]); | ||
396 | return -EINVAL; | ||
397 | } | ||
398 | |||
399 | /* If we never set up a trampoline to ftrace_caller, then bail */ | ||
400 | if (!rec->arch.mod->arch.tramp) { | ||
401 | printk(KERN_ERR "No ftrace trampoline\n"); | ||
402 | return -EINVAL; | ||
403 | } | ||
404 | |||
405 | /* create the branch to the trampoline */ | ||
406 | op[0] = create_branch((unsigned int *)ip, | ||
407 | rec->arch.mod->arch.tramp, BRANCH_SET_LINK); | ||
408 | if (!op[0]) { | ||
409 | printk(KERN_ERR "REL24 out of range!\n"); | ||
410 | return -EINVAL; | ||
411 | } | ||
412 | |||
413 | /* ld r2,40(r1) */ | ||
414 | op[1] = 0xe8410028; | ||
415 | |||
416 | DEBUGP("write to %lx\n", rec->ip); | ||
417 | |||
418 | if (probe_kernel_write((void *)ip, op, MCOUNT_INSN_SIZE * 2)) | ||
419 | return -EPERM; | ||
420 | |||
421 | flush_icache_range(ip, ip + 8); | ||
422 | |||
423 | return 0; | ||
424 | } | ||
425 | #else | ||
426 | static int | ||
427 | __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) | ||
428 | { | ||
429 | unsigned int op; | ||
430 | unsigned long ip = rec->ip; | ||
431 | |||
432 | /* read where this goes */ | ||
433 | if (probe_kernel_read(&op, (void *)ip, MCOUNT_INSN_SIZE)) | ||
434 | return -EFAULT; | ||
435 | |||
436 | /* It should be pointing to a nop */ | ||
437 | if (op != PPC_NOP_INSTR) { | ||
438 | printk(KERN_ERR "Expected NOP but have %x\n", op); | ||
439 | return -EINVAL; | ||
440 | } | ||
441 | |||
442 | /* If we never set up a trampoline to ftrace_caller, then bail */ | ||
443 | if (!rec->arch.mod->arch.tramp) { | ||
444 | printk(KERN_ERR "No ftrace trampoline\n"); | ||
445 | return -EINVAL; | ||
446 | } | ||
447 | |||
448 | /* create the branch to the trampoline */ | ||
449 | op = create_branch((unsigned int *)ip, | ||
450 | rec->arch.mod->arch.tramp, BRANCH_SET_LINK); | ||
451 | if (!op) { | ||
452 | printk(KERN_ERR "REL24 out of range!\n"); | ||
453 | return -EINVAL; | ||
454 | } | ||
455 | |||
456 | DEBUGP("write to %lx\n", rec->ip); | ||
457 | |||
458 | if (probe_kernel_write((void *)ip, &op, MCOUNT_INSN_SIZE)) | ||
459 | return -EPERM; | ||
460 | |||
461 | flush_icache_range(ip, ip + 8); | ||
462 | |||
463 | return 0; | ||
464 | } | ||
465 | #endif /* CONFIG_PPC64 */ | ||
466 | |||
467 | int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) | ||
468 | { | ||
469 | unsigned char *old, *new; | ||
470 | unsigned long ip = rec->ip; | ||
471 | |||
472 | /* | ||
473 | * If the calling address is more that 24 bits away, | ||
474 | * then we had to use a trampoline to make the call. | ||
475 | * Otherwise just update the call site. | ||
476 | */ | ||
477 | if (test_24bit_addr(ip, addr)) { | ||
478 | /* within range */ | ||
479 | old = ftrace_nop_replace(); | ||
480 | new = ftrace_call_replace(ip, addr); | ||
481 | return ftrace_modify_code(ip, old, new); | ||
482 | } | ||
483 | |||
484 | /* | ||
485 | * Out of range jumps are called from modules. | ||
486 | * Being that we are converting from nop, it had better | ||
487 | * already have a module defined. | ||
488 | */ | ||
489 | if (!rec->arch.mod) { | ||
490 | printk(KERN_ERR "No module loaded\n"); | ||
491 | return -EINVAL; | ||
492 | } | ||
493 | |||
494 | return __ftrace_make_call(rec, addr); | ||
114 | } | 495 | } |
115 | 496 | ||
116 | int ftrace_update_ftrace_func(ftrace_func_t func) | 497 | int ftrace_update_ftrace_func(ftrace_func_t func) |
@@ -128,10 +509,10 @@ int ftrace_update_ftrace_func(ftrace_func_t func) | |||
128 | 509 | ||
129 | int __init ftrace_dyn_arch_init(void *data) | 510 | int __init ftrace_dyn_arch_init(void *data) |
130 | { | 511 | { |
131 | /* This is running in kstop_machine */ | 512 | /* caller expects data to be zero */ |
513 | unsigned long *p = data; | ||
132 | 514 | ||
133 | ftrace_mcount_set(data); | 515 | *p = 0; |
134 | 516 | ||
135 | return 0; | 517 | return 0; |
136 | } | 518 | } |
137 | |||