aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kernel/ftrace.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/kernel/ftrace.c')
-rw-r--r--arch/powerpc/kernel/ftrace.c182
1 files changed, 85 insertions, 97 deletions
diff --git a/arch/powerpc/kernel/ftrace.c b/arch/powerpc/kernel/ftrace.c
index 3271cd698e4c..5355244c99ff 100644
--- a/arch/powerpc/kernel/ftrace.c
+++ b/arch/powerpc/kernel/ftrace.c
@@ -114,19 +114,9 @@ ftrace_modify_code(unsigned long ip, unsigned char *old_code,
114 */ 114 */
115static int test_24bit_addr(unsigned long ip, unsigned long addr) 115static int test_24bit_addr(unsigned long ip, unsigned long addr)
116{ 116{
117 long diff;
118 117
119 /* 118 /* use the create_branch to verify that this offset can be branched */
120 * Can we get to addr from ip in 24 bits? 119 return create_branch((unsigned int *)ip, addr, 0);
121 * (26 really, since we mulitply by 4 for 4 byte alignment)
122 */
123 diff = addr - ip;
124
125 /*
126 * Return true if diff is less than 1 << 25
127 * and greater than -1 << 26.
128 */
129 return (diff < (1 << 25)) && (diff > (-1 << 26));
130} 120}
131 121
132static int is_bl_op(unsigned int op) 122static int is_bl_op(unsigned int op)
@@ -134,11 +124,6 @@ static int is_bl_op(unsigned int op)
134 return (op & 0xfc000003) == 0x48000001; 124 return (op & 0xfc000003) == 0x48000001;
135} 125}
136 126
137static int test_offset(unsigned long offset)
138{
139 return (offset + 0x2000000 > 0x3ffffff) || ((offset & 3) != 0);
140}
141
142static unsigned long find_bl_target(unsigned long ip, unsigned int op) 127static unsigned long find_bl_target(unsigned long ip, unsigned int op)
143{ 128{
144 static int offset; 129 static int offset;
@@ -151,37 +136,30 @@ static unsigned long find_bl_target(unsigned long ip, unsigned int op)
151 return ip + (long)offset; 136 return ip + (long)offset;
152} 137}
153 138
154static unsigned int branch_offset(unsigned long offset)
155{
156 /* return "bl ip+offset" */
157 return 0x48000001 | (offset & 0x03fffffc);
158}
159
160#ifdef CONFIG_PPC64 139#ifdef CONFIG_PPC64
161static int 140static int
162__ftrace_make_nop(struct module *mod, 141__ftrace_make_nop(struct module *mod,
163 struct dyn_ftrace *rec, unsigned long addr) 142 struct dyn_ftrace *rec, unsigned long addr)
164{ 143{
165 unsigned char replaced[MCOUNT_INSN_SIZE * 2]; 144 unsigned int op;
166 unsigned int *op = (unsigned *)&replaced; 145 unsigned int jmp[5];
167 unsigned char jmp[8]; 146 unsigned long ptr;
168 unsigned long *ptr = (unsigned long *)&jmp;
169 unsigned long ip = rec->ip; 147 unsigned long ip = rec->ip;
170 unsigned long tramp; 148 unsigned long tramp;
171 int offset; 149 int offset;
172 150
173 /* read where this goes */ 151 /* read where this goes */
174 if (probe_kernel_read(replaced, (void *)ip, MCOUNT_INSN_SIZE)) 152 if (probe_kernel_read(&op, (void *)ip, sizeof(int)))
175 return -EFAULT; 153 return -EFAULT;
176 154
177 /* Make sure that that this is still a 24bit jump */ 155 /* Make sure that that this is still a 24bit jump */
178 if (!is_bl_op(*op)) { 156 if (!is_bl_op(op)) {
179 printk(KERN_ERR "Not expected bl: opcode is %x\n", *op); 157 printk(KERN_ERR "Not expected bl: opcode is %x\n", op);
180 return -EINVAL; 158 return -EINVAL;
181 } 159 }
182 160
183 /* lets find where the pointer goes */ 161 /* lets find where the pointer goes */
184 tramp = find_bl_target(ip, *op); 162 tramp = find_bl_target(ip, op);
185 163
186 /* 164 /*
187 * On PPC64 the trampoline looks like: 165 * On PPC64 the trampoline looks like:
@@ -200,19 +178,25 @@ __ftrace_make_nop(struct module *mod,
200 DEBUGP("ip:%lx jumps to %lx r2: %lx", ip, tramp, mod->arch.toc); 178 DEBUGP("ip:%lx jumps to %lx r2: %lx", ip, tramp, mod->arch.toc);
201 179
202 /* Find where the trampoline jumps to */ 180 /* Find where the trampoline jumps to */
203 if (probe_kernel_read(jmp, (void *)tramp, 8)) { 181 if (probe_kernel_read(jmp, (void *)tramp, sizeof(jmp))) {
204 printk(KERN_ERR "Failed to read %lx\n", tramp); 182 printk(KERN_ERR "Failed to read %lx\n", tramp);
205 return -EFAULT; 183 return -EFAULT;
206 } 184 }
207 185
208 DEBUGP(" %08x %08x", 186 DEBUGP(" %08x %08x", jmp[0], jmp[1]);
209 (unsigned)(*ptr >> 32), 187
210 (unsigned)*ptr); 188 /* verify that this is what we expect it to be */
189 if (((jmp[0] & 0xffff0000) != 0x3d820000) ||
190 ((jmp[1] & 0xffff0000) != 0x398c0000) ||
191 (jmp[2] != 0xf8410028) ||
192 (jmp[3] != 0xe96c0020) ||
193 (jmp[4] != 0xe84c0028)) {
194 printk(KERN_ERR "Not a trampoline\n");
195 return -EINVAL;
196 }
211 197
212 offset = (unsigned)jmp[2] << 24 | 198 offset = (unsigned)((unsigned short)jmp[0]) << 16 |
213 (unsigned)jmp[3] << 16 | 199 (unsigned)((unsigned short)jmp[1]);
214 (unsigned)jmp[6] << 8 |
215 (unsigned)jmp[7];
216 200
217 DEBUGP(" %x ", offset); 201 DEBUGP(" %x ", offset);
218 202
@@ -225,13 +209,13 @@ __ftrace_make_nop(struct module *mod,
225 return -EFAULT; 209 return -EFAULT;
226 } 210 }
227 211
228 DEBUGP(" %08x %08x\n", 212 DEBUGP(" %08x %08x\n", jmp[0], jmp[1]);
229 (unsigned)(*ptr >> 32), 213
230 (unsigned)*ptr); 214 ptr = ((unsigned long)jmp[0] << 32) + jmp[1];
231 215
232 /* This should match what was called */ 216 /* This should match what was called */
233 if (*ptr != GET_ADDR(addr)) { 217 if (ptr != GET_ADDR(addr)) {
234 printk(KERN_ERR "addr does not match %lx\n", *ptr); 218 printk(KERN_ERR "addr does not match %lx\n", ptr);
235 return -EINVAL; 219 return -EINVAL;
236 } 220 }
237 221
@@ -240,11 +224,11 @@ __ftrace_make_nop(struct module *mod,
240 * 0xe8, 0x41, 0x00, 0x28 ld r2,40(r1) 224 * 0xe8, 0x41, 0x00, 0x28 ld r2,40(r1)
241 * This needs to be turned to a nop too. 225 * This needs to be turned to a nop too.
242 */ 226 */
243 if (probe_kernel_read(replaced, (void *)(ip+4), MCOUNT_INSN_SIZE)) 227 if (probe_kernel_read(&op, (void *)(ip+4), MCOUNT_INSN_SIZE))
244 return -EFAULT; 228 return -EFAULT;
245 229
246 if (*op != 0xe8410028) { 230 if (op != 0xe8410028) {
247 printk(KERN_ERR "Next line is not ld! (%08x)\n", *op); 231 printk(KERN_ERR "Next line is not ld! (%08x)\n", op);
248 return -EINVAL; 232 return -EINVAL;
249 } 233 }
250 234
@@ -261,11 +245,14 @@ __ftrace_make_nop(struct module *mod,
261 * ld r2,40(r1) 245 * ld r2,40(r1)
262 * 1: 246 * 1:
263 */ 247 */
264 op[0] = 0x48000008; /* b +8 */ 248 op = 0x48000008; /* b +8 */
265 249
266 if (probe_kernel_write((void *)ip, replaced, MCOUNT_INSN_SIZE)) 250 if (probe_kernel_write((void *)ip, &op, MCOUNT_INSN_SIZE))
267 return -EPERM; 251 return -EPERM;
268 252
253
254 flush_icache_range(ip, ip + 8);
255
269 return 0; 256 return 0;
270} 257}
271 258
@@ -274,46 +261,52 @@ static int
274__ftrace_make_nop(struct module *mod, 261__ftrace_make_nop(struct module *mod,
275 struct dyn_ftrace *rec, unsigned long addr) 262 struct dyn_ftrace *rec, unsigned long addr)
276{ 263{
277 unsigned char replaced[MCOUNT_INSN_SIZE]; 264 unsigned int op;
278 unsigned int *op = (unsigned *)&replaced; 265 unsigned int jmp[4];
279 unsigned char jmp[8];
280 unsigned int *ptr = (unsigned int *)&jmp;
281 unsigned long ip = rec->ip; 266 unsigned long ip = rec->ip;
282 unsigned long tramp; 267 unsigned long tramp;
283 int offset;
284 268
285 if (probe_kernel_read(replaced, (void *)ip, MCOUNT_INSN_SIZE)) 269 if (probe_kernel_read(&op, (void *)ip, MCOUNT_INSN_SIZE))
286 return -EFAULT; 270 return -EFAULT;
287 271
288 /* Make sure that that this is still a 24bit jump */ 272 /* Make sure that that this is still a 24bit jump */
289 if (!is_bl_op(*op)) { 273 if (!is_bl_op(op)) {
290 printk(KERN_ERR "Not expected bl: opcode is %x\n", *op); 274 printk(KERN_ERR "Not expected bl: opcode is %x\n", op);
291 return -EINVAL; 275 return -EINVAL;
292 } 276 }
293 277
294 /* lets find where the pointer goes */ 278 /* lets find where the pointer goes */
295 tramp = find_bl_target(ip, *op); 279 tramp = find_bl_target(ip, op);
296 280
297 /* 281 /*
298 * On PPC32 the trampoline looks like: 282 * On PPC32 the trampoline looks like:
299 * lis r11,sym@ha 283 * 0x3d, 0x60, 0x00, 0x00 lis r11,sym@ha
300 * addi r11,r11,sym@l 284 * 0x39, 0x6b, 0x00, 0x00 addi r11,r11,sym@l
301 * mtctr r11 285 * 0x7d, 0x69, 0x03, 0xa6 mtctr r11
302 * bctr 286 * 0x4e, 0x80, 0x04, 0x20 bctr
303 */ 287 */
304 288
305 DEBUGP("ip:%lx jumps to %lx", ip, tramp); 289 DEBUGP("ip:%lx jumps to %lx", ip, tramp);
306 290
307 /* Find where the trampoline jumps to */ 291 /* Find where the trampoline jumps to */
308 if (probe_kernel_read(jmp, (void *)tramp, 8)) { 292 if (probe_kernel_read(jmp, (void *)tramp, sizeof(jmp))) {
309 printk(KERN_ERR "Failed to read %lx\n", tramp); 293 printk(KERN_ERR "Failed to read %lx\n", tramp);
310 return -EFAULT; 294 return -EFAULT;
311 } 295 }
312 296
313 DEBUGP(" %08x %08x ", ptr[0], ptr[1]); 297 DEBUGP(" %08x %08x ", jmp[0], jmp[1]);
298
299 /* verify that this is what we expect it to be */
300 if (((jmp[0] & 0xffff0000) != 0x3d600000) ||
301 ((jmp[1] & 0xffff0000) != 0x396b0000) ||
302 (jmp[2] != 0x7d6903a6) ||
303 (jmp[3] != 0x4e800420)) {
304 printk(KERN_ERR "Not a trampoline\n");
305 return -EINVAL;
306 }
314 307
315 tramp = (ptr[1] & 0xffff) | 308 tramp = (jmp[1] & 0xffff) |
316 ((ptr[0] & 0xffff) << 16); 309 ((jmp[0] & 0xffff) << 16);
317 if (tramp & 0x8000) 310 if (tramp & 0x8000)
318 tramp -= 0x10000; 311 tramp -= 0x10000;
319 312
@@ -326,11 +319,13 @@ __ftrace_make_nop(struct module *mod,
326 return -EINVAL; 319 return -EINVAL;
327 } 320 }
328 321
329 op[0] = PPC_NOP_INSTR; 322 op = PPC_NOP_INSTR;
330 323
331 if (probe_kernel_write((void *)ip, replaced, MCOUNT_INSN_SIZE)) 324 if (probe_kernel_write((void *)ip, &op, MCOUNT_INSN_SIZE))
332 return -EPERM; 325 return -EPERM;
333 326
327 flush_icache_range(ip, ip + 8);
328
334 return 0; 329 return 0;
335} 330}
336#endif /* PPC64 */ 331#endif /* PPC64 */
@@ -384,13 +379,11 @@ int ftrace_make_nop(struct module *mod,
384static int 379static int
385__ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) 380__ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
386{ 381{
387 unsigned char replaced[MCOUNT_INSN_SIZE * 2]; 382 unsigned int op[2];
388 unsigned int *op = (unsigned *)&replaced;
389 unsigned long ip = rec->ip; 383 unsigned long ip = rec->ip;
390 unsigned long offset;
391 384
392 /* read where this goes */ 385 /* read where this goes */
393 if (probe_kernel_read(replaced, (void *)ip, MCOUNT_INSN_SIZE * 2)) 386 if (probe_kernel_read(op, (void *)ip, MCOUNT_INSN_SIZE * 2))
394 return -EFAULT; 387 return -EFAULT;
395 388
396 /* 389 /*
@@ -409,43 +402,40 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
409 return -EINVAL; 402 return -EINVAL;
410 } 403 }
411 404
412 /* now calculate a jump to the ftrace caller trampoline */ 405 /* create the branch to the trampoline */
413 offset = rec->arch.mod->arch.tramp - ip; 406 op[0] = create_branch((unsigned int *)ip,
414 407 rec->arch.mod->arch.tramp, BRANCH_SET_LINK);
415 if (test_offset(offset)) { 408 if (!op[0]) {
416 printk(KERN_ERR "REL24 %li out of range!\n", 409 printk(KERN_ERR "REL24 out of range!\n");
417 (long int)offset);
418 return -EINVAL; 410 return -EINVAL;
419 } 411 }
420 412
421 /* Set to "bl addr" */
422 op[0] = branch_offset(offset);
423 /* ld r2,40(r1) */ 413 /* ld r2,40(r1) */
424 op[1] = 0xe8410028; 414 op[1] = 0xe8410028;
425 415
426 DEBUGP("write to %lx\n", rec->ip); 416 DEBUGP("write to %lx\n", rec->ip);
427 417
428 if (probe_kernel_write((void *)ip, replaced, MCOUNT_INSN_SIZE * 2)) 418 if (probe_kernel_write((void *)ip, op, MCOUNT_INSN_SIZE * 2))
429 return -EPERM; 419 return -EPERM;
430 420
421 flush_icache_range(ip, ip + 8);
422
431 return 0; 423 return 0;
432} 424}
433#else 425#else
434static int 426static int
435__ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) 427__ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
436{ 428{
437 unsigned char replaced[MCOUNT_INSN_SIZE]; 429 unsigned int op;
438 unsigned int *op = (unsigned *)&replaced;
439 unsigned long ip = rec->ip; 430 unsigned long ip = rec->ip;
440 unsigned long offset;
441 431
442 /* read where this goes */ 432 /* read where this goes */
443 if (probe_kernel_read(replaced, (void *)ip, MCOUNT_INSN_SIZE)) 433 if (probe_kernel_read(&op, (void *)ip, MCOUNT_INSN_SIZE))
444 return -EFAULT; 434 return -EFAULT;
445 435
446 /* It should be pointing to a nop */ 436 /* It should be pointing to a nop */
447 if (op[0] != PPC_NOP_INSTR) { 437 if (op != PPC_NOP_INSTR) {
448 printk(KERN_ERR "Expected NOP but have %x\n", op[0]); 438 printk(KERN_ERR "Expected NOP but have %x\n", op);
449 return -EINVAL; 439 return -EINVAL;
450 } 440 }
451 441
@@ -455,23 +445,21 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
455 return -EINVAL; 445 return -EINVAL;
456 } 446 }
457 447
458 /* now calculate a jump to the ftrace caller trampoline */ 448 /* create the branch to the trampoline */
459 offset = rec->arch.mod->arch.tramp - ip; 449 op = create_branch((unsigned int *)ip,
460 450 rec->arch.mod->arch.tramp, BRANCH_SET_LINK);
461 if (test_offset(offset)) { 451 if (!op) {
462 printk(KERN_ERR "REL24 %li out of range!\n", 452 printk(KERN_ERR "REL24 out of range!\n");
463 (long int)offset);
464 return -EINVAL; 453 return -EINVAL;
465 } 454 }
466 455
467 /* Set to "bl addr" */
468 op[0] = branch_offset(offset);
469
470 DEBUGP("write to %lx\n", rec->ip); 456 DEBUGP("write to %lx\n", rec->ip);
471 457
472 if (probe_kernel_write((void *)ip, replaced, MCOUNT_INSN_SIZE)) 458 if (probe_kernel_write((void *)ip, &op, MCOUNT_INSN_SIZE))
473 return -EPERM; 459 return -EPERM;
474 460
461 flush_icache_range(ip, ip + 8);
462
475 return 0; 463 return 0;
476} 464}
477#endif /* CONFIG_PPC64 */ 465#endif /* CONFIG_PPC64 */