diff options
Diffstat (limited to 'arch/arm/kernel/ftrace.c')
-rw-r--r-- | arch/arm/kernel/ftrace.c | 103 |
1 files changed, 98 insertions, 5 deletions
diff --git a/arch/arm/kernel/ftrace.c b/arch/arm/kernel/ftrace.c index 971ac8c36ea7..c0062ad1e847 100644 --- a/arch/arm/kernel/ftrace.c +++ b/arch/arm/kernel/ftrace.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #define NOP 0xe8bd4000 /* pop {lr} */ | 24 | #define NOP 0xe8bd4000 /* pop {lr} */ |
25 | #endif | 25 | #endif |
26 | 26 | ||
27 | #ifdef CONFIG_DYNAMIC_FTRACE | ||
27 | #ifdef CONFIG_OLD_MCOUNT | 28 | #ifdef CONFIG_OLD_MCOUNT |
28 | #define OLD_MCOUNT_ADDR ((unsigned long) mcount) | 29 | #define OLD_MCOUNT_ADDR ((unsigned long) mcount) |
29 | #define OLD_FTRACE_ADDR ((unsigned long) ftrace_caller_old) | 30 | #define OLD_FTRACE_ADDR ((unsigned long) ftrace_caller_old) |
@@ -59,9 +60,9 @@ static unsigned long adjust_address(struct dyn_ftrace *rec, unsigned long addr) | |||
59 | } | 60 | } |
60 | #endif | 61 | #endif |
61 | 62 | ||
62 | /* construct a branch (BL) instruction to addr */ | ||
63 | #ifdef CONFIG_THUMB2_KERNEL | 63 | #ifdef CONFIG_THUMB2_KERNEL |
64 | 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) | ||
65 | { | 66 | { |
66 | unsigned long s, j1, j2, i1, i2, imm10, imm11; | 67 | unsigned long s, j1, j2, i1, i2, imm10, imm11; |
67 | unsigned long first, second; | 68 | unsigned long first, second; |
@@ -83,15 +84,22 @@ static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr) | |||
83 | j2 = (!i2) ^ s; | 84 | j2 = (!i2) ^ s; |
84 | 85 | ||
85 | first = 0xf000 | (s << 10) | imm10; | 86 | first = 0xf000 | (s << 10) | imm10; |
86 | second = 0xd000 | (j1 << 13) | (j2 << 11) | imm11; | 87 | second = 0x9000 | (j1 << 13) | (j2 << 11) | imm11; |
88 | if (link) | ||
89 | second |= 1 << 14; | ||
87 | 90 | ||
88 | return (second << 16) | first; | 91 | return (second << 16) | first; |
89 | } | 92 | } |
90 | #else | 93 | #else |
91 | 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) | ||
92 | { | 96 | { |
97 | unsigned long opcode = 0xea000000; | ||
93 | long offset; | 98 | long offset; |
94 | 99 | ||
100 | if (link) | ||
101 | opcode |= 1 << 24; | ||
102 | |||
95 | offset = (long)addr - (long)(pc + 8); | 103 | offset = (long)addr - (long)(pc + 8); |
96 | if (unlikely(offset < -33554432 || offset > 33554428)) { | 104 | if (unlikely(offset < -33554432 || offset > 33554428)) { |
97 | /* Can't generate branches that far (from ARM ARM). Ftrace | 105 | /* Can't generate branches that far (from ARM ARM). Ftrace |
@@ -103,10 +111,15 @@ static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr) | |||
103 | 111 | ||
104 | offset = (offset >> 2) & 0x00ffffff; | 112 | offset = (offset >> 2) & 0x00ffffff; |
105 | 113 | ||
106 | return 0xeb000000 | offset; | 114 | return opcode | offset; |
107 | } | 115 | } |
108 | #endif | 116 | #endif |
109 | 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 | |||
110 | static int ftrace_modify_code(unsigned long pc, unsigned long old, | 123 | static int ftrace_modify_code(unsigned long pc, unsigned long old, |
111 | unsigned long new) | 124 | unsigned long new) |
112 | { | 125 | { |
@@ -193,3 +206,83 @@ int __init ftrace_dyn_arch_init(void *data) | |||
193 | 206 | ||
194 | return 0; | 207 | return 0; |
195 | } | 208 | } |
209 | #endif /* CONFIG_DYNAMIC_FTRACE */ | ||
210 | |||
211 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
212 | void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, | ||
213 | unsigned long frame_pointer) | ||
214 | { | ||
215 | unsigned long return_hooker = (unsigned long) &return_to_handler; | ||
216 | struct ftrace_graph_ent trace; | ||
217 | unsigned long old; | ||
218 | int err; | ||
219 | |||
220 | if (unlikely(atomic_read(¤t->tracing_graph_pause))) | ||
221 | return; | ||
222 | |||
223 | old = *parent; | ||
224 | *parent = return_hooker; | ||
225 | |||
226 | err = ftrace_push_return_trace(old, self_addr, &trace.depth, | ||
227 | frame_pointer); | ||
228 | if (err == -EBUSY) { | ||
229 | *parent = old; | ||
230 | return; | ||
231 | } | ||
232 | |||
233 | trace.func = self_addr; | ||
234 | |||
235 | /* Only trace if the calling function expects to */ | ||
236 | if (!ftrace_graph_entry(&trace)) { | ||
237 | current->curr_ret_stack--; | ||
238 | *parent = old; | ||
239 | } | ||
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 */ | ||
288 | #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ | ||