diff options
-rw-r--r-- | arch/arm/Kconfig | 1 | ||||
-rw-r--r-- | arch/arm/include/asm/ftrace.h | 4 | ||||
-rw-r--r-- | arch/arm/kernel/entry-ftrace.S | 100 | ||||
-rw-r--r-- | arch/arm/kernel/ftrace.c | 37 |
4 files changed, 142 insertions, 0 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 4c1a35f15838..730d456e2843 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig | |||
@@ -56,6 +56,7 @@ config ARM | |||
56 | select HAVE_DMA_API_DEBUG | 56 | select HAVE_DMA_API_DEBUG |
57 | select HAVE_DMA_CONTIGUOUS if MMU | 57 | select HAVE_DMA_CONTIGUOUS if MMU |
58 | select HAVE_DYNAMIC_FTRACE if (!XIP_KERNEL) && !CPU_ENDIAN_BE32 && MMU | 58 | select HAVE_DYNAMIC_FTRACE if (!XIP_KERNEL) && !CPU_ENDIAN_BE32 && MMU |
59 | select HAVE_DYNAMIC_FTRACE_WITH_REGS if HAVE_DYNAMIC_FTRACE | ||
59 | select HAVE_EFFICIENT_UNALIGNED_ACCESS if (CPU_V6 || CPU_V6K || CPU_V7) && MMU | 60 | select HAVE_EFFICIENT_UNALIGNED_ACCESS if (CPU_V6 || CPU_V6K || CPU_V7) && MMU |
60 | select HAVE_EXIT_THREAD | 61 | select HAVE_EXIT_THREAD |
61 | select HAVE_FTRACE_MCOUNT_RECORD if (!XIP_KERNEL) | 62 | select HAVE_FTRACE_MCOUNT_RECORD if (!XIP_KERNEL) |
diff --git a/arch/arm/include/asm/ftrace.h b/arch/arm/include/asm/ftrace.h index 22b73112b75f..f379881d5cc3 100644 --- a/arch/arm/include/asm/ftrace.h +++ b/arch/arm/include/asm/ftrace.h | |||
@@ -1,6 +1,10 @@ | |||
1 | #ifndef _ASM_ARM_FTRACE | 1 | #ifndef _ASM_ARM_FTRACE |
2 | #define _ASM_ARM_FTRACE | 2 | #define _ASM_ARM_FTRACE |
3 | 3 | ||
4 | #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS | ||
5 | #define ARCH_SUPPORTS_FTRACE_OPS 1 | ||
6 | #endif | ||
7 | |||
4 | #ifdef CONFIG_FUNCTION_TRACER | 8 | #ifdef CONFIG_FUNCTION_TRACER |
5 | #define MCOUNT_ADDR ((unsigned long)(__gnu_mcount_nc)) | 9 | #define MCOUNT_ADDR ((unsigned long)(__gnu_mcount_nc)) |
6 | #define MCOUNT_INSN_SIZE 4 /* sizeof mcount call */ | 10 | #define MCOUNT_INSN_SIZE 4 /* sizeof mcount call */ |
diff --git a/arch/arm/kernel/entry-ftrace.S b/arch/arm/kernel/entry-ftrace.S index c73c4030ca5d..efcd9f25a14b 100644 --- a/arch/arm/kernel/entry-ftrace.S +++ b/arch/arm/kernel/entry-ftrace.S | |||
@@ -92,12 +92,95 @@ | |||
92 | 2: mcount_exit | 92 | 2: mcount_exit |
93 | .endm | 93 | .endm |
94 | 94 | ||
95 | #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS | ||
96 | |||
97 | .macro __ftrace_regs_caller | ||
98 | |||
99 | sub sp, sp, #8 @ space for PC and CPSR OLD_R0, | ||
100 | @ OLD_R0 will overwrite previous LR | ||
101 | |||
102 | add ip, sp, #12 @ move in IP the value of SP as it was | ||
103 | @ before the push {lr} of the mcount mechanism | ||
104 | |||
105 | str lr, [sp, #0] @ store LR instead of PC | ||
106 | |||
107 | ldr lr, [sp, #8] @ get previous LR | ||
108 | |||
109 | str r0, [sp, #8] @ write r0 as OLD_R0 over previous LR | ||
110 | |||
111 | stmdb sp!, {ip, lr} | ||
112 | stmdb sp!, {r0-r11, lr} | ||
113 | |||
114 | @ stack content at this point: | ||
115 | @ 0 4 48 52 56 60 64 68 72 | ||
116 | @ R0 | R1 | ... | LR | SP + 4 | previous LR | LR | PSR | OLD_R0 | | ||
117 | |||
118 | mov r3, sp @ struct pt_regs* | ||
119 | |||
120 | ldr r2, =function_trace_op | ||
121 | ldr r2, [r2] @ pointer to the current | ||
122 | @ function tracing op | ||
123 | |||
124 | ldr r1, [sp, #S_LR] @ lr of instrumented func | ||
125 | |||
126 | ldr lr, [sp, #S_PC] @ get LR | ||
127 | |||
128 | mcount_adjust_addr r0, lr @ instrumented function | ||
129 | |||
130 | .globl ftrace_regs_call | ||
131 | ftrace_regs_call: | ||
132 | bl ftrace_stub | ||
133 | |||
134 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
135 | .globl ftrace_graph_regs_call | ||
136 | ftrace_graph_regs_call: | ||
137 | mov r0, r0 | ||
138 | #endif | ||
139 | |||
140 | @ pop saved regs | ||
141 | ldmia sp!, {r0-r12} @ restore r0 through r12 | ||
142 | ldr ip, [sp, #8] @ restore PC | ||
143 | ldr lr, [sp, #4] @ restore LR | ||
144 | ldr sp, [sp, #0] @ restore SP | ||
145 | mov pc, ip @ return | ||
146 | .endm | ||
147 | |||
148 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
149 | .macro __ftrace_graph_regs_caller | ||
150 | |||
151 | sub r0, fp, #4 @ lr of instrumented routine (parent) | ||
152 | |||
153 | @ called from __ftrace_regs_caller | ||
154 | ldr r1, [sp, #S_PC] @ instrumented routine (func) | ||
155 | mcount_adjust_addr r1, r1 | ||
156 | |||
157 | mov r2, fp @ frame pointer | ||
158 | bl prepare_ftrace_return | ||
159 | |||
160 | @ pop registers saved in ftrace_regs_caller | ||
161 | ldmia sp!, {r0-r12} @ restore r0 through r12 | ||
162 | ldr ip, [sp, #8] @ restore PC | ||
163 | ldr lr, [sp, #4] @ restore LR | ||
164 | ldr sp, [sp, #0] @ restore SP | ||
165 | mov pc, ip @ return | ||
166 | |||
167 | .endm | ||
168 | #endif | ||
169 | #endif | ||
170 | |||
95 | .macro __ftrace_caller suffix | 171 | .macro __ftrace_caller suffix |
96 | mcount_enter | 172 | mcount_enter |
97 | 173 | ||
98 | mcount_get_lr r1 @ lr of instrumented func | 174 | mcount_get_lr r1 @ lr of instrumented func |
99 | mcount_adjust_addr r0, lr @ instrumented function | 175 | mcount_adjust_addr r0, lr @ instrumented function |
100 | 176 | ||
177 | #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS | ||
178 | ldr r2, =function_trace_op | ||
179 | ldr r2, [r2] @ pointer to the current | ||
180 | @ function tracing op | ||
181 | mov r3, #0 @ regs is NULL | ||
182 | #endif | ||
183 | |||
101 | .globl ftrace_call\suffix | 184 | .globl ftrace_call\suffix |
102 | ftrace_call\suffix: | 185 | ftrace_call\suffix: |
103 | bl ftrace_stub | 186 | bl ftrace_stub |
@@ -212,6 +295,15 @@ UNWIND(.fnstart) | |||
212 | __ftrace_caller | 295 | __ftrace_caller |
213 | UNWIND(.fnend) | 296 | UNWIND(.fnend) |
214 | ENDPROC(ftrace_caller) | 297 | ENDPROC(ftrace_caller) |
298 | |||
299 | #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS | ||
300 | ENTRY(ftrace_regs_caller) | ||
301 | UNWIND(.fnstart) | ||
302 | __ftrace_regs_caller | ||
303 | UNWIND(.fnend) | ||
304 | ENDPROC(ftrace_regs_caller) | ||
305 | #endif | ||
306 | |||
215 | #endif | 307 | #endif |
216 | 308 | ||
217 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | 309 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER |
@@ -220,6 +312,14 @@ UNWIND(.fnstart) | |||
220 | __ftrace_graph_caller | 312 | __ftrace_graph_caller |
221 | UNWIND(.fnend) | 313 | UNWIND(.fnend) |
222 | ENDPROC(ftrace_graph_caller) | 314 | ENDPROC(ftrace_graph_caller) |
315 | |||
316 | #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS | ||
317 | ENTRY(ftrace_graph_regs_caller) | ||
318 | UNWIND(.fnstart) | ||
319 | __ftrace_graph_regs_caller | ||
320 | UNWIND(.fnend) | ||
321 | ENDPROC(ftrace_graph_regs_caller) | ||
322 | #endif | ||
223 | #endif | 323 | #endif |
224 | 324 | ||
225 | .purgem mcount_enter | 325 | .purgem mcount_enter |
diff --git a/arch/arm/kernel/ftrace.c b/arch/arm/kernel/ftrace.c index 833c991075a1..5617932a83df 100644 --- a/arch/arm/kernel/ftrace.c +++ b/arch/arm/kernel/ftrace.c | |||
@@ -141,6 +141,15 @@ int ftrace_update_ftrace_func(ftrace_func_t func) | |||
141 | 141 | ||
142 | ret = ftrace_modify_code(pc, 0, new, false); | 142 | ret = ftrace_modify_code(pc, 0, new, false); |
143 | 143 | ||
144 | #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS | ||
145 | if (!ret) { | ||
146 | pc = (unsigned long)&ftrace_regs_call; | ||
147 | new = ftrace_call_replace(pc, (unsigned long)func); | ||
148 | |||
149 | ret = ftrace_modify_code(pc, 0, new, false); | ||
150 | } | ||
151 | #endif | ||
152 | |||
144 | #ifdef CONFIG_OLD_MCOUNT | 153 | #ifdef CONFIG_OLD_MCOUNT |
145 | if (!ret) { | 154 | if (!ret) { |
146 | pc = (unsigned long)&ftrace_call_old; | 155 | pc = (unsigned long)&ftrace_call_old; |
@@ -159,11 +168,29 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) | |||
159 | unsigned long ip = rec->ip; | 168 | unsigned long ip = rec->ip; |
160 | 169 | ||
161 | old = ftrace_nop_replace(rec); | 170 | old = ftrace_nop_replace(rec); |
171 | |||
172 | new = ftrace_call_replace(ip, adjust_address(rec, addr)); | ||
173 | |||
174 | return ftrace_modify_code(rec->ip, old, new, true); | ||
175 | } | ||
176 | |||
177 | #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS | ||
178 | |||
179 | int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr, | ||
180 | unsigned long addr) | ||
181 | { | ||
182 | unsigned long new, old; | ||
183 | unsigned long ip = rec->ip; | ||
184 | |||
185 | old = ftrace_call_replace(ip, adjust_address(rec, old_addr)); | ||
186 | |||
162 | new = ftrace_call_replace(ip, adjust_address(rec, addr)); | 187 | new = ftrace_call_replace(ip, adjust_address(rec, addr)); |
163 | 188 | ||
164 | return ftrace_modify_code(rec->ip, old, new, true); | 189 | return ftrace_modify_code(rec->ip, old, new, true); |
165 | } | 190 | } |
166 | 191 | ||
192 | #endif | ||
193 | |||
167 | int ftrace_make_nop(struct module *mod, | 194 | int ftrace_make_nop(struct module *mod, |
168 | struct dyn_ftrace *rec, unsigned long addr) | 195 | struct dyn_ftrace *rec, unsigned long addr) |
169 | { | 196 | { |
@@ -231,6 +258,8 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, | |||
231 | extern unsigned long ftrace_graph_call; | 258 | extern unsigned long ftrace_graph_call; |
232 | extern unsigned long ftrace_graph_call_old; | 259 | extern unsigned long ftrace_graph_call_old; |
233 | extern void ftrace_graph_caller_old(void); | 260 | extern void ftrace_graph_caller_old(void); |
261 | extern unsigned long ftrace_graph_regs_call; | ||
262 | extern void ftrace_graph_regs_caller(void); | ||
234 | 263 | ||
235 | static int __ftrace_modify_caller(unsigned long *callsite, | 264 | static int __ftrace_modify_caller(unsigned long *callsite, |
236 | void (*func) (void), bool enable) | 265 | void (*func) (void), bool enable) |
@@ -253,6 +282,14 @@ static int ftrace_modify_graph_caller(bool enable) | |||
253 | ftrace_graph_caller, | 282 | ftrace_graph_caller, |
254 | enable); | 283 | enable); |
255 | 284 | ||
285 | #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS | ||
286 | if (!ret) | ||
287 | ret = __ftrace_modify_caller(&ftrace_graph_regs_call, | ||
288 | ftrace_graph_regs_caller, | ||
289 | enable); | ||
290 | #endif | ||
291 | |||
292 | |||
256 | #ifdef CONFIG_OLD_MCOUNT | 293 | #ifdef CONFIG_OLD_MCOUNT |
257 | if (!ret) | 294 | if (!ret) |
258 | ret = __ftrace_modify_caller(&ftrace_graph_call_old, | 295 | ret = __ftrace_modify_caller(&ftrace_graph_call_old, |