aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sh/kernel
diff options
context:
space:
mode:
authorMatt Fleming <matt@console-pimps.org>2009-06-10 17:07:53 -0400
committerMatt Fleming <matt@console-pimps.org>2009-06-11 14:00:58 -0400
commit9e28c46b7dd116a607ffb054c5545c468c77d779 (patch)
tree348ea5654f1eb17ecd8b0e3256615e96fb3c6dc9 /arch/sh/kernel
parent7780b6a2990e2fbb697bb900e01ca7361943c7da (diff)
sh: Fix dynamic ftrace's NOP action.
Ftrace on sh handles nop'ing out trace function calls differently than other architectures. Instead of inserting NOP instructions in place of the call to the function tracer we branch over the call instructions and continue executing the main body of the function. This patch fixes a bug in the implementation of ftrace_modify_code() where we check that the old value of the code we're about to replace is an expected one. In the ftrace_make_call() code path ftrace_modify_code() was comparing the old instruction value with NOP instructions. The compare was failing because we never actually insert NOP instructions. It makes sense to just get rid of the NOP instructions in ftrace_nop and compare the old code with the address of the function body if we're expecting ftrace to have nop'd out the function trace call. Signed-off-by: Matt Fleming <matt@console-pimps.org>
Diffstat (limited to 'arch/sh/kernel')
-rw-r--r--arch/sh/kernel/ftrace.c58
1 files changed, 29 insertions, 29 deletions
diff --git a/arch/sh/kernel/ftrace.c b/arch/sh/kernel/ftrace.c
index 040cdc6a67ed..066f37dc32a9 100644
--- a/arch/sh/kernel/ftrace.c
+++ b/arch/sh/kernel/ftrace.c
@@ -19,30 +19,37 @@
19#include <asm/ftrace.h> 19#include <asm/ftrace.h>
20#include <asm/cacheflush.h> 20#include <asm/cacheflush.h>
21 21
22static unsigned char ftrace_nop[] = {
23 0x09, 0x00, /* nop */
24 0x09, 0x00, /* nop */
25};
26
27static unsigned char ftrace_replaced_code[MCOUNT_INSN_SIZE]; 22static unsigned char ftrace_replaced_code[MCOUNT_INSN_SIZE];
28 23
29unsigned char *ftrace_nop_replace(void) 24static unsigned char ftrace_nop[4];
25/*
26 * If we're trying to nop out a call to a function, we instead
27 * place a call to the address after the memory table.
28 *
29 * 8c011060 <a>:
30 * 8c011060: 02 d1 mov.l 8c01106c <a+0xc>,r1
31 * 8c011062: 22 4f sts.l pr,@-r15
32 * 8c011064: 02 c7 mova 8c011070 <a+0x10>,r0
33 * 8c011066: 2b 41 jmp @r1
34 * 8c011068: 2a 40 lds r0,pr
35 * 8c01106a: 09 00 nop
36 * 8c01106c: 68 24 .word 0x2468 <--- ip
37 * 8c01106e: 1d 8c .word 0x8c1d
38 * 8c011070: 26 4f lds.l @r15+,pr <--- ip + MCOUNT_INSN_SIZE
39 *
40 * We write 0x8c011070 to 0x8c01106c so that on entry to a() we branch
41 * past the _mcount call and continue executing code like normal.
42 */
43static unsigned char *ftrace_nop_replace(unsigned long ip)
30{ 44{
45 __raw_writel(ip + MCOUNT_INSN_SIZE, ftrace_nop);
31 return ftrace_nop; 46 return ftrace_nop;
32} 47}
33 48
34static int is_sh_nop(unsigned char *ip) 49static unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
35{
36 return strncmp(ip, ftrace_nop, sizeof(ftrace_nop));
37}
38
39unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
40{ 50{
41 /* Place the address in the memory table. */ 51 /* Place the address in the memory table. */
42 if (addr == CALLER_ADDR) 52 __raw_writel(addr, ftrace_replaced_code);
43 __raw_writel(addr + MCOUNT_INSN_OFFSET, ftrace_replaced_code);
44 else
45 __raw_writel(addr, ftrace_replaced_code);
46 53
47 /* 54 /*
48 * No locking needed, this must be called via kstop_machine 55 * No locking needed, this must be called via kstop_machine
@@ -51,7 +58,7 @@ unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
51 return ftrace_replaced_code; 58 return ftrace_replaced_code;
52} 59}
53 60
54int ftrace_modify_code(unsigned long ip, unsigned char *old_code, 61static int ftrace_modify_code(unsigned long ip, unsigned char *old_code,
55 unsigned char *new_code) 62 unsigned char *new_code)
56{ 63{
57 unsigned char replaced[MCOUNT_INSN_SIZE]; 64 unsigned char replaced[MCOUNT_INSN_SIZE];
@@ -66,13 +73,6 @@ int ftrace_modify_code(unsigned long ip, unsigned char *old_code,
66 * kstop_machine, or before SMP starts. 73 * kstop_machine, or before SMP starts.
67 */ 74 */
68 75
69 /*
70 * If we're trying to nop out a call to a function, we instead
71 * place a call to the address after the memory table.
72 */
73 if (is_sh_nop(new_code) == 0)
74 __raw_writel(ip + MCOUNT_INSN_SIZE, (unsigned long)new_code);
75
76 /* read the text we want to modify */ 76 /* read the text we want to modify */
77 if (probe_kernel_read(replaced, (void *)ip, MCOUNT_INSN_SIZE)) 77 if (probe_kernel_read(replaced, (void *)ip, MCOUNT_INSN_SIZE))
78 return -EFAULT; 78 return -EFAULT;
@@ -92,13 +92,13 @@ int ftrace_modify_code(unsigned long ip, unsigned char *old_code,
92 92
93int ftrace_update_ftrace_func(ftrace_func_t func) 93int ftrace_update_ftrace_func(ftrace_func_t func)
94{ 94{
95 unsigned long ip = (unsigned long)(&ftrace_call); 95 unsigned long ip = (unsigned long)(&ftrace_call) + MCOUNT_INSN_OFFSET;
96 unsigned char old[MCOUNT_INSN_SIZE], *new; 96 unsigned char old[MCOUNT_INSN_SIZE], *new;
97 97
98 memcpy(old, (unsigned char *)(ip + MCOUNT_INSN_OFFSET), MCOUNT_INSN_SIZE); 98 memcpy(old, (unsigned char *)ip, MCOUNT_INSN_SIZE);
99 new = ftrace_call_replace(ip, (unsigned long)func); 99 new = ftrace_call_replace(ip, (unsigned long)func);
100 100
101 return ftrace_modify_code(ip + MCOUNT_INSN_OFFSET, old, new); 101 return ftrace_modify_code(ip, old, new);
102} 102}
103 103
104int ftrace_make_nop(struct module *mod, 104int ftrace_make_nop(struct module *mod,
@@ -108,7 +108,7 @@ int ftrace_make_nop(struct module *mod,
108 unsigned long ip = rec->ip; 108 unsigned long ip = rec->ip;
109 109
110 old = ftrace_call_replace(ip, addr); 110 old = ftrace_call_replace(ip, addr);
111 new = ftrace_nop_replace(); 111 new = ftrace_nop_replace(ip);
112 112
113 return ftrace_modify_code(rec->ip, old, new); 113 return ftrace_modify_code(rec->ip, old, new);
114} 114}
@@ -118,7 +118,7 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
118 unsigned char *new, *old; 118 unsigned char *new, *old;
119 unsigned long ip = rec->ip; 119 unsigned long ip = rec->ip;
120 120
121 old = ftrace_nop_replace(); 121 old = ftrace_nop_replace(ip);
122 new = ftrace_call_replace(ip, addr); 122 new = ftrace_call_replace(ip, addr);
123 123
124 return ftrace_modify_code(rec->ip, old, new); 124 return ftrace_modify_code(rec->ip, old, new);