diff options
Diffstat (limited to 'arch/powerpc/kernel/ftrace.c')
-rw-r--r-- | arch/powerpc/kernel/ftrace.c | 137 |
1 files changed, 39 insertions, 98 deletions
diff --git a/arch/powerpc/kernel/ftrace.c b/arch/powerpc/kernel/ftrace.c index 6a014c763cc7..f202d0731b06 100644 --- a/arch/powerpc/kernel/ftrace.c +++ b/arch/powerpc/kernel/ftrace.c | |||
@@ -105,11 +105,9 @@ __ftrace_make_nop(struct module *mod, | |||
105 | struct dyn_ftrace *rec, unsigned long addr) | 105 | struct dyn_ftrace *rec, unsigned long addr) |
106 | { | 106 | { |
107 | unsigned int op; | 107 | unsigned int op; |
108 | unsigned int jmp[5]; | ||
109 | unsigned long ptr; | 108 | unsigned long ptr; |
110 | unsigned long ip = rec->ip; | 109 | unsigned long ip = rec->ip; |
111 | unsigned long tramp; | 110 | void *tramp; |
112 | int offset; | ||
113 | 111 | ||
114 | /* read where this goes */ | 112 | /* read where this goes */ |
115 | if (probe_kernel_read(&op, (void *)ip, sizeof(int))) | 113 | if (probe_kernel_read(&op, (void *)ip, sizeof(int))) |
@@ -122,96 +120,41 @@ __ftrace_make_nop(struct module *mod, | |||
122 | } | 120 | } |
123 | 121 | ||
124 | /* lets find where the pointer goes */ | 122 | /* lets find where the pointer goes */ |
125 | tramp = find_bl_target(ip, op); | 123 | tramp = (void *)find_bl_target(ip, op); |
126 | |||
127 | /* | ||
128 | * On PPC64 the trampoline looks like: | ||
129 | * 0x3d, 0x82, 0x00, 0x00, addis r12,r2, <high> | ||
130 | * 0x39, 0x8c, 0x00, 0x00, addi r12,r12, <low> | ||
131 | * Where the bytes 2,3,6 and 7 make up the 32bit offset | ||
132 | * to the TOC that holds the pointer. | ||
133 | * to jump to. | ||
134 | * 0xf8, 0x41, 0x00, 0x28, std r2,40(r1) | ||
135 | * 0xe9, 0x6c, 0x00, 0x20, ld r11,32(r12) | ||
136 | * The actually address is 32 bytes from the offset | ||
137 | * into the TOC. | ||
138 | * 0xe8, 0x4c, 0x00, 0x28, ld r2,40(r12) | ||
139 | */ | ||
140 | |||
141 | pr_devel("ip:%lx jumps to %lx r2: %lx", ip, tramp, mod->arch.toc); | ||
142 | |||
143 | /* Find where the trampoline jumps to */ | ||
144 | if (probe_kernel_read(jmp, (void *)tramp, sizeof(jmp))) { | ||
145 | printk(KERN_ERR "Failed to read %lx\n", tramp); | ||
146 | return -EFAULT; | ||
147 | } | ||
148 | 124 | ||
149 | pr_devel(" %08x %08x", jmp[0], jmp[1]); | 125 | pr_devel("ip:%lx jumps to %p", ip, tramp); |
150 | 126 | ||
151 | /* verify that this is what we expect it to be */ | 127 | if (!is_module_trampoline(tramp)) { |
152 | if (((jmp[0] & 0xffff0000) != 0x3d820000) || | ||
153 | ((jmp[1] & 0xffff0000) != 0x398c0000) || | ||
154 | (jmp[2] != 0xf8410028) || | ||
155 | (jmp[3] != 0xe96c0020) || | ||
156 | (jmp[4] != 0xe84c0028)) { | ||
157 | printk(KERN_ERR "Not a trampoline\n"); | 128 | printk(KERN_ERR "Not a trampoline\n"); |
158 | return -EINVAL; | 129 | return -EINVAL; |
159 | } | 130 | } |
160 | 131 | ||
161 | /* The bottom half is signed extended */ | 132 | if (module_trampoline_target(mod, tramp, &ptr)) { |
162 | offset = ((unsigned)((unsigned short)jmp[0]) << 16) + | 133 | printk(KERN_ERR "Failed to get trampoline target\n"); |
163 | (int)((short)jmp[1]); | ||
164 | |||
165 | pr_devel(" %x ", offset); | ||
166 | |||
167 | /* get the address this jumps too */ | ||
168 | tramp = mod->arch.toc + offset + 32; | ||
169 | pr_devel("toc: %lx", tramp); | ||
170 | |||
171 | if (probe_kernel_read(jmp, (void *)tramp, 8)) { | ||
172 | printk(KERN_ERR "Failed to read %lx\n", tramp); | ||
173 | return -EFAULT; | 134 | return -EFAULT; |
174 | } | 135 | } |
175 | 136 | ||
176 | pr_devel(" %08x %08x\n", jmp[0], jmp[1]); | 137 | pr_devel("trampoline target %lx", ptr); |
177 | |||
178 | #ifdef __LITTLE_ENDIAN__ | ||
179 | ptr = ((unsigned long)jmp[1] << 32) + jmp[0]; | ||
180 | #else | ||
181 | ptr = ((unsigned long)jmp[0] << 32) + jmp[1]; | ||
182 | #endif | ||
183 | 138 | ||
184 | /* This should match what was called */ | 139 | /* This should match what was called */ |
185 | if (ptr != ppc_function_entry((void *)addr)) { | 140 | if (ptr != ppc_function_entry((void *)addr)) { |
186 | printk(KERN_ERR "addr does not match %lx\n", ptr); | 141 | printk(KERN_ERR "addr %lx does not match expected %lx\n", |
142 | ptr, ppc_function_entry((void *)addr)); | ||
187 | return -EINVAL; | 143 | return -EINVAL; |
188 | } | 144 | } |
189 | 145 | ||
190 | /* | 146 | /* |
191 | * We want to nop the line, but the next line is | 147 | * Our original call site looks like: |
192 | * 0xe8, 0x41, 0x00, 0x28 ld r2,40(r1) | 148 | * |
193 | * This needs to be turned to a nop too. | 149 | * bl <tramp> |
194 | */ | 150 | * ld r2,XX(r1) |
195 | if (probe_kernel_read(&op, (void *)(ip+4), MCOUNT_INSN_SIZE)) | 151 | * |
196 | return -EFAULT; | 152 | * Milton Miller pointed out that we can not simply nop the branch. |
197 | 153 | * If a task was preempted when calling a trace function, the nops | |
198 | if (op != 0xe8410028) { | 154 | * will remove the way to restore the TOC in r2 and the r2 TOC will |
199 | printk(KERN_ERR "Next line is not ld! (%08x)\n", op); | 155 | * get corrupted. |
200 | return -EINVAL; | 156 | * |
201 | } | 157 | * Use a b +8 to jump over the load. |
202 | |||
203 | /* | ||
204 | * Milton Miller pointed out that we can not blindly do nops. | ||
205 | * If a task was preempted when calling a trace function, | ||
206 | * the nops will remove the way to restore the TOC in r2 | ||
207 | * and the r2 TOC will get corrupted. | ||
208 | */ | ||
209 | |||
210 | /* | ||
211 | * Replace: | ||
212 | * bl <tramp> <==== will be replaced with "b 1f" | ||
213 | * ld r2,40(r1) | ||
214 | * 1: | ||
215 | */ | 158 | */ |
216 | op = 0x48000008; /* b +8 */ | 159 | op = 0x48000008; /* b +8 */ |
217 | 160 | ||
@@ -349,19 +292,24 @@ static int | |||
349 | __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) | 292 | __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) |
350 | { | 293 | { |
351 | unsigned int op[2]; | 294 | unsigned int op[2]; |
352 | unsigned long ip = rec->ip; | 295 | void *ip = (void *)rec->ip; |
353 | 296 | ||
354 | /* read where this goes */ | 297 | /* read where this goes */ |
355 | if (probe_kernel_read(op, (void *)ip, MCOUNT_INSN_SIZE * 2)) | 298 | if (probe_kernel_read(op, ip, sizeof(op))) |
356 | return -EFAULT; | 299 | return -EFAULT; |
357 | 300 | ||
358 | /* | 301 | /* |
359 | * It should be pointing to two nops or | 302 | * We expect to see: |
360 | * b +8; ld r2,40(r1) | 303 | * |
304 | * b +8 | ||
305 | * ld r2,XX(r1) | ||
306 | * | ||
307 | * The load offset is different depending on the ABI. For simplicity | ||
308 | * just mask it out when doing the compare. | ||
361 | */ | 309 | */ |
362 | if (((op[0] != 0x48000008) || (op[1] != 0xe8410028)) && | 310 | if ((op[0] != 0x48000008) || ((op[1] & 0xffff00000) != 0xe8410000)) { |
363 | ((op[0] != PPC_INST_NOP) || (op[1] != PPC_INST_NOP))) { | 311 | printk(KERN_ERR "Unexpected call sequence: %x %x\n", |
364 | printk(KERN_ERR "Expected NOPs but have %x %x\n", op[0], op[1]); | 312 | op[0], op[1]); |
365 | return -EINVAL; | 313 | return -EINVAL; |
366 | } | 314 | } |
367 | 315 | ||
@@ -371,23 +319,16 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) | |||
371 | return -EINVAL; | 319 | return -EINVAL; |
372 | } | 320 | } |
373 | 321 | ||
374 | /* create the branch to the trampoline */ | 322 | /* Ensure branch is within 24 bits */ |
375 | op[0] = create_branch((unsigned int *)ip, | 323 | if (create_branch(ip, rec->arch.mod->arch.tramp, BRANCH_SET_LINK)) { |
376 | rec->arch.mod->arch.tramp, BRANCH_SET_LINK); | 324 | printk(KERN_ERR "Branch out of range"); |
377 | if (!op[0]) { | ||
378 | printk(KERN_ERR "REL24 out of range!\n"); | ||
379 | return -EINVAL; | 325 | return -EINVAL; |
380 | } | 326 | } |
381 | 327 | ||
382 | /* ld r2,40(r1) */ | 328 | if (patch_branch(ip, rec->arch.mod->arch.tramp, BRANCH_SET_LINK)) { |
383 | op[1] = 0xe8410028; | 329 | printk(KERN_ERR "REL24 out of range!\n"); |
384 | 330 | return -EINVAL; | |
385 | pr_devel("write to %lx\n", rec->ip); | 331 | } |
386 | |||
387 | if (probe_kernel_write((void *)ip, op, MCOUNT_INSN_SIZE * 2)) | ||
388 | return -EPERM; | ||
389 | |||
390 | flush_icache_range(ip, ip + 8); | ||
391 | 332 | ||
392 | return 0; | 333 | return 0; |
393 | } | 334 | } |