diff options
-rw-r--r-- | arch/arm/kernel/entry-common.S | 12 | ||||
-rw-r--r-- | arch/arm/kernel/ftrace.c | 69 |
2 files changed, 76 insertions, 5 deletions
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S index 9f1766211668..aae802ee12f8 100644 --- a/arch/arm/kernel/entry-common.S +++ b/arch/arm/kernel/entry-common.S | |||
@@ -183,12 +183,24 @@ ENDPROC(ret_from_fork) | |||
183 | ftrace_call\suffix: | 183 | ftrace_call\suffix: |
184 | bl ftrace_stub | 184 | bl ftrace_stub |
185 | 185 | ||
186 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
187 | .globl ftrace_graph_call\suffix | ||
188 | ftrace_graph_call\suffix: | ||
189 | mov r0, r0 | ||
190 | #endif | ||
191 | |||
186 | mcount_exit | 192 | mcount_exit |
187 | .endm | 193 | .endm |
188 | 194 | ||
189 | .macro __ftrace_graph_caller | 195 | .macro __ftrace_graph_caller |
190 | sub r0, fp, #4 @ &lr of instrumented routine (&parent) | 196 | sub r0, fp, #4 @ &lr of instrumented routine (&parent) |
197 | #ifdef CONFIG_DYNAMIC_FTRACE | ||
198 | @ called from __ftrace_caller, saved in mcount_enter | ||
199 | ldr r1, [sp, #16] @ instrumented routine (func) | ||
200 | #else | ||
201 | @ called from __mcount, untouched in lr | ||
191 | mov r1, lr @ instrumented routine (func) | 202 | mov r1, lr @ instrumented routine (func) |
203 | #endif | ||
192 | sub r1, r1, #MCOUNT_INSN_SIZE | 204 | sub r1, r1, #MCOUNT_INSN_SIZE |
193 | mov r2, fp @ frame pointer | 205 | mov r2, fp @ frame pointer |
194 | bl prepare_ftrace_return | 206 | bl prepare_ftrace_return |
diff --git a/arch/arm/kernel/ftrace.c b/arch/arm/kernel/ftrace.c index 7a702a502871..c0062ad1e847 100644 --- a/arch/arm/kernel/ftrace.c +++ b/arch/arm/kernel/ftrace.c | |||
@@ -60,9 +60,9 @@ static unsigned long adjust_address(struct dyn_ftrace *rec, unsigned long addr) | |||
60 | } | 60 | } |
61 | #endif | 61 | #endif |
62 | 62 | ||
63 | /* construct a branch (BL) instruction to addr */ | ||
64 | #ifdef CONFIG_THUMB2_KERNEL | 63 | #ifdef CONFIG_THUMB2_KERNEL |
65 | static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr) | 64 | static unsigned long ftrace_gen_branch(unsigned long pc, unsigned long addr, |
65 | bool link) | ||
66 | { | 66 | { |
67 | unsigned long s, j1, j2, i1, i2, imm10, imm11; | 67 | unsigned long s, j1, j2, i1, i2, imm10, imm11; |
68 | unsigned long first, second; | 68 | unsigned long first, second; |
@@ -84,15 +84,22 @@ static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr) | |||
84 | j2 = (!i2) ^ s; | 84 | j2 = (!i2) ^ s; |
85 | 85 | ||
86 | first = 0xf000 | (s << 10) | imm10; | 86 | first = 0xf000 | (s << 10) | imm10; |
87 | second = 0xd000 | (j1 << 13) | (j2 << 11) | imm11; | 87 | second = 0x9000 | (j1 << 13) | (j2 << 11) | imm11; |
88 | if (link) | ||
89 | second |= 1 << 14; | ||
88 | 90 | ||
89 | return (second << 16) | first; | 91 | return (second << 16) | first; |
90 | } | 92 | } |
91 | #else | 93 | #else |
92 | static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr) | 94 | static unsigned long ftrace_gen_branch(unsigned long pc, unsigned long addr, |
95 | bool link) | ||
93 | { | 96 | { |
97 | unsigned long opcode = 0xea000000; | ||
94 | long offset; | 98 | long offset; |
95 | 99 | ||
100 | if (link) | ||
101 | opcode |= 1 << 24; | ||
102 | |||
96 | offset = (long)addr - (long)(pc + 8); | 103 | offset = (long)addr - (long)(pc + 8); |
97 | if (unlikely(offset < -33554432 || offset > 33554428)) { | 104 | if (unlikely(offset < -33554432 || offset > 33554428)) { |
98 | /* Can't generate branches that far (from ARM ARM). Ftrace | 105 | /* Can't generate branches that far (from ARM ARM). Ftrace |
@@ -104,10 +111,15 @@ static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr) | |||
104 | 111 | ||
105 | offset = (offset >> 2) & 0x00ffffff; | 112 | offset = (offset >> 2) & 0x00ffffff; |
106 | 113 | ||
107 | return 0xeb000000 | offset; | 114 | return opcode | offset; |
108 | } | 115 | } |
109 | #endif | 116 | #endif |
110 | 117 | ||
118 | static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr) | ||
119 | { | ||
120 | return ftrace_gen_branch(pc, addr, true); | ||
121 | } | ||
122 | |||
111 | static int ftrace_modify_code(unsigned long pc, unsigned long old, | 123 | static int ftrace_modify_code(unsigned long pc, unsigned long old, |
112 | unsigned long new) | 124 | unsigned long new) |
113 | { | 125 | { |
@@ -226,4 +238,51 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, | |||
226 | *parent = old; | 238 | *parent = old; |
227 | } | 239 | } |
228 | } | 240 | } |
241 | |||
242 | #ifdef CONFIG_DYNAMIC_FTRACE | ||
243 | extern unsigned long ftrace_graph_call; | ||
244 | extern unsigned long ftrace_graph_call_old; | ||
245 | extern void ftrace_graph_caller_old(void); | ||
246 | |||
247 | static int __ftrace_modify_caller(unsigned long *callsite, | ||
248 | void (*func) (void), bool enable) | ||
249 | { | ||
250 | unsigned long caller_fn = (unsigned long) func; | ||
251 | unsigned long pc = (unsigned long) callsite; | ||
252 | unsigned long branch = ftrace_gen_branch(pc, caller_fn, false); | ||
253 | unsigned long nop = 0xe1a00000; /* mov r0, r0 */ | ||
254 | unsigned long old = enable ? nop : branch; | ||
255 | unsigned long new = enable ? branch : nop; | ||
256 | |||
257 | return ftrace_modify_code(pc, old, new); | ||
258 | } | ||
259 | |||
260 | static int ftrace_modify_graph_caller(bool enable) | ||
261 | { | ||
262 | int ret; | ||
263 | |||
264 | ret = __ftrace_modify_caller(&ftrace_graph_call, | ||
265 | ftrace_graph_caller, | ||
266 | enable); | ||
267 | |||
268 | #ifdef CONFIG_OLD_MCOUNT | ||
269 | if (!ret) | ||
270 | ret = __ftrace_modify_caller(&ftrace_graph_call_old, | ||
271 | ftrace_graph_caller_old, | ||
272 | enable); | ||
273 | #endif | ||
274 | |||
275 | return ret; | ||
276 | } | ||
277 | |||
278 | int ftrace_enable_ftrace_graph_caller(void) | ||
279 | { | ||
280 | return ftrace_modify_graph_caller(true); | ||
281 | } | ||
282 | |||
283 | int ftrace_disable_ftrace_graph_caller(void) | ||
284 | { | ||
285 | return ftrace_modify_graph_caller(false); | ||
286 | } | ||
287 | #endif /* CONFIG_DYNAMIC_FTRACE */ | ||
229 | #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ | 288 | #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ |