diff options
Diffstat (limited to 'arch/arm/kernel')
| -rw-r--r-- | arch/arm/kernel/entry-common.S | 124 | ||||
| -rw-r--r-- | arch/arm/kernel/etm.c | 4 | ||||
| -rw-r--r-- | arch/arm/kernel/module.c | 34 |
3 files changed, 117 insertions, 45 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 | ||
diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c index 827753966301..56418f98cd01 100644 --- a/arch/arm/kernel/etm.c +++ b/arch/arm/kernel/etm.c | |||
| @@ -543,7 +543,9 @@ static int __init etm_probe(struct amba_device *dev, struct amba_id *id) | |||
| 543 | t->etm_portsz = 1; | 543 | t->etm_portsz = 1; |
| 544 | 544 | ||
| 545 | etm_unlock(t); | 545 | etm_unlock(t); |
| 546 | ret = etm_readl(t, CSCR_PRSR); | 546 | (void)etm_readl(t, ETMMR_PDSR); |
| 547 | /* dummy first read */ | ||
| 548 | (void)etm_readl(&tracer, ETMMR_OSSRR); | ||
| 547 | 549 | ||
| 548 | t->ncmppairs = etm_readl(t, ETMR_CONFCODE) & 0xf; | 550 | t->ncmppairs = etm_readl(t, ETMR_CONFCODE) & 0xf; |
| 549 | etm_writel(t, 0x440, ETMR_CTRL); | 551 | etm_writel(t, 0x440, ETMR_CTRL); |
diff --git a/arch/arm/kernel/module.c b/arch/arm/kernel/module.c index c628bdf6c430..6b4605893f1e 100644 --- a/arch/arm/kernel/module.c +++ b/arch/arm/kernel/module.c | |||
| @@ -102,7 +102,9 @@ apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex, | |||
| 102 | unsigned long loc; | 102 | unsigned long loc; |
| 103 | Elf32_Sym *sym; | 103 | Elf32_Sym *sym; |
| 104 | s32 offset; | 104 | s32 offset; |
| 105 | #ifdef CONFIG_THUMB2_KERNEL | ||
| 105 | u32 upper, lower, sign, j1, j2; | 106 | u32 upper, lower, sign, j1, j2; |
| 107 | #endif | ||
| 106 | 108 | ||
| 107 | offset = ELF32_R_SYM(rel->r_info); | 109 | offset = ELF32_R_SYM(rel->r_info); |
| 108 | if (offset < 0 || offset > (symsec->sh_size / sizeof(Elf32_Sym))) { | 110 | if (offset < 0 || offset > (symsec->sh_size / sizeof(Elf32_Sym))) { |
| @@ -185,6 +187,7 @@ apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex, | |||
| 185 | (offset & 0x0fff); | 187 | (offset & 0x0fff); |
| 186 | break; | 188 | break; |
| 187 | 189 | ||
| 190 | #ifdef CONFIG_THUMB2_KERNEL | ||
| 188 | case R_ARM_THM_CALL: | 191 | case R_ARM_THM_CALL: |
| 189 | case R_ARM_THM_JUMP24: | 192 | case R_ARM_THM_JUMP24: |
| 190 | upper = *(u16 *)loc; | 193 | upper = *(u16 *)loc; |
| @@ -233,9 +236,40 @@ apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex, | |||
| 233 | *(u16 *)(loc + 2) = (u16)((lower & 0xd000) | | 236 | *(u16 *)(loc + 2) = (u16)((lower & 0xd000) | |
| 234 | (j1 << 13) | (j2 << 11) | | 237 | (j1 << 13) | (j2 << 11) | |
| 235 | ((offset >> 1) & 0x07ff)); | 238 | ((offset >> 1) & 0x07ff)); |
| 239 | break; | ||
| 240 | |||
| 241 | case R_ARM_THM_MOVW_ABS_NC: | ||
| 242 | case R_ARM_THM_MOVT_ABS: | ||
| 236 | upper = *(u16 *)loc; | 243 | upper = *(u16 *)loc; |
| 237 | lower = *(u16 *)(loc + 2); | 244 | lower = *(u16 *)(loc + 2); |
| 245 | |||
| 246 | /* | ||
| 247 | * MOVT/MOVW instructions encoding in Thumb-2: | ||
| 248 | * | ||
| 249 | * i = upper[10] | ||
| 250 | * imm4 = upper[3:0] | ||
| 251 | * imm3 = lower[14:12] | ||
| 252 | * imm8 = lower[7:0] | ||
| 253 | * | ||
| 254 | * imm16 = imm4:i:imm3:imm8 | ||
| 255 | */ | ||
| 256 | offset = ((upper & 0x000f) << 12) | | ||
| 257 | ((upper & 0x0400) << 1) | | ||
| 258 | ((lower & 0x7000) >> 4) | (lower & 0x00ff); | ||
| 259 | offset = (offset ^ 0x8000) - 0x8000; | ||
| 260 | offset += sym->st_value; | ||
| 261 | |||
| 262 | if (ELF32_R_TYPE(rel->r_info) == R_ARM_THM_MOVT_ABS) | ||
| 263 | offset >>= 16; | ||
| 264 | |||
| 265 | *(u16 *)loc = (u16)((upper & 0xfbf0) | | ||
| 266 | ((offset & 0xf000) >> 12) | | ||
| 267 | ((offset & 0x0800) >> 1)); | ||
| 268 | *(u16 *)(loc + 2) = (u16)((lower & 0x8f00) | | ||
| 269 | ((offset & 0x0700) << 4) | | ||
| 270 | (offset & 0x00ff)); | ||
| 238 | break; | 271 | break; |
| 272 | #endif | ||
| 239 | 273 | ||
| 240 | default: | 274 | default: |
| 241 | printk(KERN_ERR "%s: unknown relocation: %u\n", | 275 | printk(KERN_ERR "%s: unknown relocation: %u\n", |
