diff options
author | Matt Fleming <matt@console-pimps.org> | 2009-06-10 17:07:53 -0400 |
---|---|---|
committer | Matt Fleming <matt@console-pimps.org> | 2009-06-11 14:00:58 -0400 |
commit | 9e28c46b7dd116a607ffb054c5545c468c77d779 (patch) | |
tree | 348ea5654f1eb17ecd8b0e3256615e96fb3c6dc9 /arch/sh/kernel/ftrace.c | |
parent | 7780b6a2990e2fbb697bb900e01ca7361943c7da (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/ftrace.c')
-rw-r--r-- | arch/sh/kernel/ftrace.c | 58 |
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 | ||
22 | static unsigned char ftrace_nop[] = { | ||
23 | 0x09, 0x00, /* nop */ | ||
24 | 0x09, 0x00, /* nop */ | ||
25 | }; | ||
26 | |||
27 | static unsigned char ftrace_replaced_code[MCOUNT_INSN_SIZE]; | 22 | static unsigned char ftrace_replaced_code[MCOUNT_INSN_SIZE]; |
28 | 23 | ||
29 | unsigned char *ftrace_nop_replace(void) | 24 | static 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 | */ | ||
43 | static 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 | ||
34 | static int is_sh_nop(unsigned char *ip) | 49 | static unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr) |
35 | { | ||
36 | return strncmp(ip, ftrace_nop, sizeof(ftrace_nop)); | ||
37 | } | ||
38 | |||
39 | unsigned 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 | ||
54 | int ftrace_modify_code(unsigned long ip, unsigned char *old_code, | 61 | static 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 | ||
93 | int ftrace_update_ftrace_func(ftrace_func_t func) | 93 | int 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 | ||
104 | int ftrace_make_nop(struct module *mod, | 104 | int 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); |