diff options
Diffstat (limited to 'arch/x86/kernel/ftrace.c')
-rw-r--r-- | arch/x86/kernel/ftrace.c | 56 |
1 files changed, 8 insertions, 48 deletions
diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c index 52819e816f87..cbc4a91b131e 100644 --- a/arch/x86/kernel/ftrace.c +++ b/arch/x86/kernel/ftrace.c | |||
@@ -297,16 +297,7 @@ int ftrace_int3_handler(struct pt_regs *regs) | |||
297 | 297 | ||
298 | static int ftrace_write(unsigned long ip, const char *val, int size) | 298 | static int ftrace_write(unsigned long ip, const char *val, int size) |
299 | { | 299 | { |
300 | /* | 300 | ip = text_ip_addr(ip); |
301 | * On x86_64, kernel text mappings are mapped read-only with | ||
302 | * CONFIG_DEBUG_RODATA. So we use the kernel identity mapping instead | ||
303 | * of the kernel text mapping to modify the kernel text. | ||
304 | * | ||
305 | * For 32bit kernels, these mappings are same and we can use | ||
306 | * kernel identity mapping to modify code. | ||
307 | */ | ||
308 | if (within(ip, (unsigned long)_text, (unsigned long)_etext)) | ||
309 | ip = (unsigned long)__va(__pa_symbol(ip)); | ||
310 | 301 | ||
311 | if (probe_kernel_write((void *)ip, val, size)) | 302 | if (probe_kernel_write((void *)ip, val, size)) |
312 | return -EPERM; | 303 | return -EPERM; |
@@ -349,40 +340,14 @@ static int add_brk_on_nop(struct dyn_ftrace *rec) | |||
349 | return add_break(rec->ip, old); | 340 | return add_break(rec->ip, old); |
350 | } | 341 | } |
351 | 342 | ||
352 | /* | ||
353 | * If the record has the FTRACE_FL_REGS set, that means that it | ||
354 | * wants to convert to a callback that saves all regs. If FTRACE_FL_REGS | ||
355 | * is not not set, then it wants to convert to the normal callback. | ||
356 | */ | ||
357 | static unsigned long get_ftrace_addr(struct dyn_ftrace *rec) | ||
358 | { | ||
359 | if (rec->flags & FTRACE_FL_REGS) | ||
360 | return (unsigned long)FTRACE_REGS_ADDR; | ||
361 | else | ||
362 | return (unsigned long)FTRACE_ADDR; | ||
363 | } | ||
364 | |||
365 | /* | ||
366 | * The FTRACE_FL_REGS_EN is set when the record already points to | ||
367 | * a function that saves all the regs. Basically the '_EN' version | ||
368 | * represents the current state of the function. | ||
369 | */ | ||
370 | static unsigned long get_ftrace_old_addr(struct dyn_ftrace *rec) | ||
371 | { | ||
372 | if (rec->flags & FTRACE_FL_REGS_EN) | ||
373 | return (unsigned long)FTRACE_REGS_ADDR; | ||
374 | else | ||
375 | return (unsigned long)FTRACE_ADDR; | ||
376 | } | ||
377 | |||
378 | static int add_breakpoints(struct dyn_ftrace *rec, int enable) | 343 | static int add_breakpoints(struct dyn_ftrace *rec, int enable) |
379 | { | 344 | { |
380 | unsigned long ftrace_addr; | 345 | unsigned long ftrace_addr; |
381 | int ret; | 346 | int ret; |
382 | 347 | ||
383 | ret = ftrace_test_record(rec, enable); | 348 | ftrace_addr = ftrace_get_addr_curr(rec); |
384 | 349 | ||
385 | ftrace_addr = get_ftrace_addr(rec); | 350 | ret = ftrace_test_record(rec, enable); |
386 | 351 | ||
387 | switch (ret) { | 352 | switch (ret) { |
388 | case FTRACE_UPDATE_IGNORE: | 353 | case FTRACE_UPDATE_IGNORE: |
@@ -392,10 +357,7 @@ static int add_breakpoints(struct dyn_ftrace *rec, int enable) | |||
392 | /* converting nop to call */ | 357 | /* converting nop to call */ |
393 | return add_brk_on_nop(rec); | 358 | return add_brk_on_nop(rec); |
394 | 359 | ||
395 | case FTRACE_UPDATE_MODIFY_CALL_REGS: | ||
396 | case FTRACE_UPDATE_MODIFY_CALL: | 360 | case FTRACE_UPDATE_MODIFY_CALL: |
397 | ftrace_addr = get_ftrace_old_addr(rec); | ||
398 | /* fall through */ | ||
399 | case FTRACE_UPDATE_MAKE_NOP: | 361 | case FTRACE_UPDATE_MAKE_NOP: |
400 | /* converting a call to a nop */ | 362 | /* converting a call to a nop */ |
401 | return add_brk_on_call(rec, ftrace_addr); | 363 | return add_brk_on_call(rec, ftrace_addr); |
@@ -440,14 +402,14 @@ static int remove_breakpoint(struct dyn_ftrace *rec) | |||
440 | * If not, don't touch the breakpoint, we make just create | 402 | * If not, don't touch the breakpoint, we make just create |
441 | * a disaster. | 403 | * a disaster. |
442 | */ | 404 | */ |
443 | ftrace_addr = get_ftrace_addr(rec); | 405 | ftrace_addr = ftrace_get_addr_new(rec); |
444 | nop = ftrace_call_replace(ip, ftrace_addr); | 406 | nop = ftrace_call_replace(ip, ftrace_addr); |
445 | 407 | ||
446 | if (memcmp(&ins[1], &nop[1], MCOUNT_INSN_SIZE - 1) == 0) | 408 | if (memcmp(&ins[1], &nop[1], MCOUNT_INSN_SIZE - 1) == 0) |
447 | goto update; | 409 | goto update; |
448 | 410 | ||
449 | /* Check both ftrace_addr and ftrace_old_addr */ | 411 | /* Check both ftrace_addr and ftrace_old_addr */ |
450 | ftrace_addr = get_ftrace_old_addr(rec); | 412 | ftrace_addr = ftrace_get_addr_curr(rec); |
451 | nop = ftrace_call_replace(ip, ftrace_addr); | 413 | nop = ftrace_call_replace(ip, ftrace_addr); |
452 | 414 | ||
453 | if (memcmp(&ins[1], &nop[1], MCOUNT_INSN_SIZE - 1) != 0) | 415 | if (memcmp(&ins[1], &nop[1], MCOUNT_INSN_SIZE - 1) != 0) |
@@ -491,13 +453,12 @@ static int add_update(struct dyn_ftrace *rec, int enable) | |||
491 | 453 | ||
492 | ret = ftrace_test_record(rec, enable); | 454 | ret = ftrace_test_record(rec, enable); |
493 | 455 | ||
494 | ftrace_addr = get_ftrace_addr(rec); | 456 | ftrace_addr = ftrace_get_addr_new(rec); |
495 | 457 | ||
496 | switch (ret) { | 458 | switch (ret) { |
497 | case FTRACE_UPDATE_IGNORE: | 459 | case FTRACE_UPDATE_IGNORE: |
498 | return 0; | 460 | return 0; |
499 | 461 | ||
500 | case FTRACE_UPDATE_MODIFY_CALL_REGS: | ||
501 | case FTRACE_UPDATE_MODIFY_CALL: | 462 | case FTRACE_UPDATE_MODIFY_CALL: |
502 | case FTRACE_UPDATE_MAKE_CALL: | 463 | case FTRACE_UPDATE_MAKE_CALL: |
503 | /* converting nop to call */ | 464 | /* converting nop to call */ |
@@ -538,13 +499,12 @@ static int finish_update(struct dyn_ftrace *rec, int enable) | |||
538 | 499 | ||
539 | ret = ftrace_update_record(rec, enable); | 500 | ret = ftrace_update_record(rec, enable); |
540 | 501 | ||
541 | ftrace_addr = get_ftrace_addr(rec); | 502 | ftrace_addr = ftrace_get_addr_new(rec); |
542 | 503 | ||
543 | switch (ret) { | 504 | switch (ret) { |
544 | case FTRACE_UPDATE_IGNORE: | 505 | case FTRACE_UPDATE_IGNORE: |
545 | return 0; | 506 | return 0; |
546 | 507 | ||
547 | case FTRACE_UPDATE_MODIFY_CALL_REGS: | ||
548 | case FTRACE_UPDATE_MODIFY_CALL: | 508 | case FTRACE_UPDATE_MODIFY_CALL: |
549 | case FTRACE_UPDATE_MAKE_CALL: | 509 | case FTRACE_UPDATE_MAKE_CALL: |
550 | /* converting nop to call */ | 510 | /* converting nop to call */ |
@@ -621,8 +581,8 @@ void ftrace_replace_code(int enable) | |||
621 | return; | 581 | return; |
622 | 582 | ||
623 | remove_breakpoints: | 583 | remove_breakpoints: |
584 | pr_warn("Failed on %s (%d):\n", report, count); | ||
624 | ftrace_bug(ret, rec ? rec->ip : 0); | 585 | ftrace_bug(ret, rec ? rec->ip : 0); |
625 | printk(KERN_WARNING "Failed on %s (%d):\n", report, count); | ||
626 | for_ftrace_rec_iter(iter) { | 586 | for_ftrace_rec_iter(iter) { |
627 | rec = ftrace_rec_iter_record(iter); | 587 | rec = ftrace_rec_iter_record(iter); |
628 | /* | 588 | /* |