diff options
Diffstat (limited to 'arch/arm/kernel/entry-common.S')
| -rw-r--r-- | arch/arm/kernel/entry-common.S | 124 |
1 files changed, 80 insertions, 44 deletions
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S index 2c1db77d7848..f05a35a59694 100644 --- a/arch/arm/kernel/entry-common.S +++ b/arch/arm/kernel/entry-common.S | |||
| @@ -92,75 +92,111 @@ ENDPROC(ret_from_fork) | |||
| 92 | #define CALL(x) .long x | 92 | #define CALL(x) .long x |
| 93 | 93 | ||
| 94 | #ifdef CONFIG_FUNCTION_TRACER | 94 | #ifdef CONFIG_FUNCTION_TRACER |
| 95 | /* | ||
| 96 | * When compiling with -pg, gcc inserts a call to the mcount routine at the | ||
| 97 | * start of every function. In mcount, apart from the function's address (in | ||
| 98 | * lr), we need to get hold of the function's caller's address. | ||
| 99 | * | ||
| 100 | * Older GCCs (pre-4.4) inserted a call to a routine called mcount like this: | ||
| 101 | * | ||
| 102 | * bl mcount | ||
| 103 | * | ||
| 104 | * These versions have the limitation that in order for the mcount routine to | ||
| 105 | * be able to determine the function's caller's address, an APCS-style frame | ||
| 106 | * pointer (which is set up with something like the code below) is required. | ||
| 107 | * | ||
| 108 | * mov ip, sp | ||
| 109 | * push {fp, ip, lr, pc} | ||
| 110 | * sub fp, ip, #4 | ||
| 111 | * | ||
| 112 | * With EABI, these frame pointers are not available unless -mapcs-frame is | ||
| 113 | * specified, and if building as Thumb-2, not even then. | ||
| 114 | * | ||
| 115 | * Newer GCCs (4.4+) solve this problem by introducing a new version of mcount, | ||
| 116 | * with call sites like: | ||
| 117 | * | ||
| 118 | * push {lr} | ||
| 119 | * bl __gnu_mcount_nc | ||
| 120 | * | ||
| 121 | * With these compilers, frame pointers are not necessary. | ||
| 122 | * | ||
| 123 | * mcount can be thought of as a function called in the middle of a subroutine | ||
| 124 | * call. As such, it needs to be transparent for both the caller and the | ||
| 125 | * callee: the original lr needs to be restored when leaving mcount, and no | ||
| 126 | * registers should be clobbered. (In the __gnu_mcount_nc implementation, we | ||
| 127 | * clobber the ip register. This is OK because the ARM calling convention | ||
| 128 | * allows it to be clobbered in subroutines and doesn't use it to hold | ||
| 129 | * parameters.) | ||
| 130 | */ | ||
| 95 | #ifdef CONFIG_DYNAMIC_FTRACE | 131 | #ifdef CONFIG_DYNAMIC_FTRACE |
| 96 | ENTRY(mcount) | 132 | ENTRY(mcount) |
| 97 | stmdb sp!, {r0-r3, lr} | 133 | stmdb sp!, {r0-r3, lr} |
| 98 | mov r0, lr | 134 | mov r0, lr |
| 99 | sub r0, r0, #MCOUNT_INSN_SIZE | 135 | sub r0, r0, #MCOUNT_INSN_SIZE |
| 100 | 136 | ||
| 101 | .globl mcount_call | 137 | .globl mcount_call |
| 102 | mcount_call: | 138 | mcount_call: |
| 103 | bl ftrace_stub | 139 | bl ftrace_stub |
| 104 | ldr lr, [fp, #-4] @ restore lr | 140 | ldr lr, [fp, #-4] @ restore lr |
| 105 | ldmia sp!, {r0-r3, pc} | 141 | ldmia sp!, {r0-r3, pc} |
| 106 | 142 | ||
| 107 | ENTRY(ftrace_caller) | 143 | ENTRY(ftrace_caller) |
| 108 | stmdb sp!, {r0-r3, lr} | 144 | stmdb sp!, {r0-r3, lr} |
| 109 | ldr r1, [fp, #-4] | 145 | ldr r1, [fp, #-4] |
| 110 | mov r0, lr | 146 | mov r0, lr |
| 111 | sub r0, r0, #MCOUNT_INSN_SIZE | 147 | sub r0, r0, #MCOUNT_INSN_SIZE |
| 112 | 148 | ||
| 113 | .globl ftrace_call | 149 | .globl ftrace_call |
| 114 | ftrace_call: | 150 | ftrace_call: |
| 115 | bl ftrace_stub | 151 | bl ftrace_stub |
| 116 | ldr lr, [fp, #-4] @ restore lr | 152 | ldr lr, [fp, #-4] @ restore lr |
| 117 | ldmia sp!, {r0-r3, pc} | 153 | ldmia sp!, {r0-r3, pc} |
| 118 | 154 | ||
| 119 | #else | 155 | #else |
| 120 | 156 | ||
| 121 | ENTRY(__gnu_mcount_nc) | 157 | ENTRY(__gnu_mcount_nc) |
| 122 | stmdb sp!, {r0-r3, lr} | 158 | stmdb sp!, {r0-r3, lr} |
| 123 | ldr r0, =ftrace_trace_function | 159 | ldr r0, =ftrace_trace_function |
| 124 | ldr r2, [r0] | 160 | ldr r2, [r0] |
| 125 | adr r0, ftrace_stub | 161 | adr r0, ftrace_stub |
| 126 | cmp r0, r2 | 162 | cmp r0, r2 |
| 127 | bne gnu_trace | 163 | bne gnu_trace |
| 128 | ldmia sp!, {r0-r3, ip, lr} | 164 | ldmia sp!, {r0-r3, ip, lr} |
| 129 | mov pc, ip | 165 | mov pc, ip |
| 130 | 166 | ||
| 131 | gnu_trace: | 167 | gnu_trace: |
| 132 | ldr r1, [sp, #20] @ lr of instrumented routine | 168 | ldr r1, [sp, #20] @ lr of instrumented routine |
| 133 | mov r0, lr | 169 | mov r0, lr |
| 134 | sub r0, r0, #MCOUNT_INSN_SIZE | 170 | sub r0, r0, #MCOUNT_INSN_SIZE |
| 135 | mov lr, pc | 171 | mov lr, pc |
| 136 | mov pc, r2 | 172 | mov pc, r2 |
| 137 | ldmia sp!, {r0-r3, ip, lr} | 173 | ldmia sp!, {r0-r3, ip, lr} |
| 138 | mov pc, ip | 174 | mov pc, ip |
| 139 | 175 | ||
| 140 | ENTRY(mcount) | 176 | ENTRY(mcount) |
| 141 | stmdb sp!, {r0-r3, lr} | 177 | stmdb sp!, {r0-r3, lr} |
| 142 | ldr r0, =ftrace_trace_function | 178 | ldr r0, =ftrace_trace_function |
| 143 | ldr r2, [r0] | 179 | ldr r2, [r0] |
| 144 | adr r0, ftrace_stub | 180 | adr r0, ftrace_stub |
| 145 | cmp r0, r2 | 181 | cmp r0, r2 |
| 146 | bne trace | 182 | bne trace |
| 147 | ldr lr, [fp, #-4] @ restore lr | 183 | ldr lr, [fp, #-4] @ restore lr |
| 148 | ldmia sp!, {r0-r3, pc} | 184 | ldmia sp!, {r0-r3, pc} |
| 149 | 185 | ||
| 150 | trace: | 186 | trace: |
| 151 | ldr r1, [fp, #-4] @ lr of instrumented routine | 187 | ldr r1, [fp, #-4] @ lr of instrumented routine |
| 152 | mov r0, lr | 188 | mov r0, lr |
| 153 | sub r0, r0, #MCOUNT_INSN_SIZE | 189 | sub r0, r0, #MCOUNT_INSN_SIZE |
| 154 | mov lr, pc | 190 | mov lr, pc |
| 155 | mov pc, r2 | 191 | mov pc, r2 |
| 156 | ldr lr, [fp, #-4] @ restore lr | 192 | ldr lr, [fp, #-4] @ restore lr |
| 157 | ldmia sp!, {r0-r3, pc} | 193 | ldmia sp!, {r0-r3, pc} |
| 158 | 194 | ||
| 159 | #endif /* CONFIG_DYNAMIC_FTRACE */ | 195 | #endif /* CONFIG_DYNAMIC_FTRACE */ |
| 160 | 196 | ||
| 161 | .globl ftrace_stub | 197 | .globl ftrace_stub |
| 162 | ftrace_stub: | 198 | ftrace_stub: |
| 163 | mov pc, lr | 199 | mov pc, lr |
| 164 | 200 | ||
| 165 | #endif /* CONFIG_FUNCTION_TRACER */ | 201 | #endif /* CONFIG_FUNCTION_TRACER */ |
| 166 | 202 | ||
