aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/kernel/entry-common.S12
-rw-r--r--arch/arm/kernel/ftrace.c69
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)
183ftrace_call\suffix: 183ftrace_call\suffix:
184 bl ftrace_stub 184 bl ftrace_stub
185 185
186#ifdef CONFIG_FUNCTION_GRAPH_TRACER
187 .globl ftrace_graph_call\suffix
188ftrace_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
65static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr) 64static 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
92static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr) 94static 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
118static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr)
119{
120 return ftrace_gen_branch(pc, addr, true);
121}
122
111static int ftrace_modify_code(unsigned long pc, unsigned long old, 123static 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
243extern unsigned long ftrace_graph_call;
244extern unsigned long ftrace_graph_call_old;
245extern void ftrace_graph_caller_old(void);
246
247static 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
260static 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
278int ftrace_enable_ftrace_graph_caller(void)
279{
280 return ftrace_modify_graph_caller(true);
281}
282
283int 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 */