diff options
-rw-r--r-- | arch/x86/include/asm/ftrace.h | 3 | ||||
-rw-r--r-- | arch/x86/kernel/ftrace.c | 342 | ||||
-rw-r--r-- | arch/x86/kernel/traps.c | 8 | ||||
-rw-r--r-- | include/linux/ftrace.h | 6 |
4 files changed, 358 insertions, 1 deletions
diff --git a/arch/x86/include/asm/ftrace.h b/arch/x86/include/asm/ftrace.h index 268c783ab1c0..18d9005d9e4f 100644 --- a/arch/x86/include/asm/ftrace.h +++ b/arch/x86/include/asm/ftrace.h | |||
@@ -34,6 +34,7 @@ | |||
34 | 34 | ||
35 | #ifndef __ASSEMBLY__ | 35 | #ifndef __ASSEMBLY__ |
36 | extern void mcount(void); | 36 | extern void mcount(void); |
37 | extern int modifying_ftrace_code; | ||
37 | 38 | ||
38 | static inline unsigned long ftrace_call_adjust(unsigned long addr) | 39 | static inline unsigned long ftrace_call_adjust(unsigned long addr) |
39 | { | 40 | { |
@@ -50,6 +51,8 @@ struct dyn_arch_ftrace { | |||
50 | /* No extra data needed for x86 */ | 51 | /* No extra data needed for x86 */ |
51 | }; | 52 | }; |
52 | 53 | ||
54 | int ftrace_int3_handler(struct pt_regs *regs); | ||
55 | |||
53 | #endif /* CONFIG_DYNAMIC_FTRACE */ | 56 | #endif /* CONFIG_DYNAMIC_FTRACE */ |
54 | #endif /* __ASSEMBLY__ */ | 57 | #endif /* __ASSEMBLY__ */ |
55 | #endif /* CONFIG_FUNCTION_TRACER */ | 58 | #endif /* CONFIG_FUNCTION_TRACER */ |
diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c index c9a281f272fd..80af34739a9a 100644 --- a/arch/x86/kernel/ftrace.c +++ b/arch/x86/kernel/ftrace.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/init.h> | 20 | #include <linux/init.h> |
21 | #include <linux/list.h> | 21 | #include <linux/list.h> |
22 | #include <linux/module.h> | 22 | #include <linux/module.h> |
23 | #include <linux/kprobes.h> | ||
23 | 24 | ||
24 | #include <trace/syscall.h> | 25 | #include <trace/syscall.h> |
25 | 26 | ||
@@ -334,6 +335,347 @@ int ftrace_update_ftrace_func(ftrace_func_t func) | |||
334 | return ret; | 335 | return ret; |
335 | } | 336 | } |
336 | 337 | ||
338 | int modifying_ftrace_code __read_mostly; | ||
339 | |||
340 | /* | ||
341 | * A breakpoint was added to the code address we are about to | ||
342 | * modify, and this is the handle that will just skip over it. | ||
343 | * We are either changing a nop into a trace call, or a trace | ||
344 | * call to a nop. While the change is taking place, we treat | ||
345 | * it just like it was a nop. | ||
346 | */ | ||
347 | int ftrace_int3_handler(struct pt_regs *regs) | ||
348 | { | ||
349 | if (WARN_ON_ONCE(!regs)) | ||
350 | return 0; | ||
351 | |||
352 | if (!ftrace_location(regs->ip - 1)) | ||
353 | return 0; | ||
354 | |||
355 | regs->ip += MCOUNT_INSN_SIZE - 1; | ||
356 | |||
357 | return 1; | ||
358 | } | ||
359 | |||
360 | static int ftrace_write(unsigned long ip, const char *val, int size) | ||
361 | { | ||
362 | /* | ||
363 | * On x86_64, kernel text mappings are mapped read-only with | ||
364 | * CONFIG_DEBUG_RODATA. So we use the kernel identity mapping instead | ||
365 | * of the kernel text mapping to modify the kernel text. | ||
366 | * | ||
367 | * For 32bit kernels, these mappings are same and we can use | ||
368 | * kernel identity mapping to modify code. | ||
369 | */ | ||
370 | if (within(ip, (unsigned long)_text, (unsigned long)_etext)) | ||
371 | ip = (unsigned long)__va(__pa(ip)); | ||
372 | |||
373 | return probe_kernel_write((void *)ip, val, size); | ||
374 | } | ||
375 | |||
376 | static int add_break(unsigned long ip, const char *old) | ||
377 | { | ||
378 | unsigned char replaced[MCOUNT_INSN_SIZE]; | ||
379 | unsigned char brk = BREAKPOINT_INSTRUCTION; | ||
380 | |||
381 | if (probe_kernel_read(replaced, (void *)ip, MCOUNT_INSN_SIZE)) | ||
382 | return -EFAULT; | ||
383 | |||
384 | /* Make sure it is what we expect it to be */ | ||
385 | if (memcmp(replaced, old, MCOUNT_INSN_SIZE) != 0) | ||
386 | return -EINVAL; | ||
387 | |||
388 | if (ftrace_write(ip, &brk, 1)) | ||
389 | return -EPERM; | ||
390 | |||
391 | return 0; | ||
392 | } | ||
393 | |||
394 | static int add_brk_on_call(struct dyn_ftrace *rec, unsigned long addr) | ||
395 | { | ||
396 | unsigned const char *old; | ||
397 | unsigned long ip = rec->ip; | ||
398 | |||
399 | old = ftrace_call_replace(ip, addr); | ||
400 | |||
401 | return add_break(rec->ip, old); | ||
402 | } | ||
403 | |||
404 | |||
405 | static int add_brk_on_nop(struct dyn_ftrace *rec) | ||
406 | { | ||
407 | unsigned const char *old; | ||
408 | |||
409 | old = ftrace_nop_replace(); | ||
410 | |||
411 | return add_break(rec->ip, old); | ||
412 | } | ||
413 | |||
414 | static int add_breakpoints(struct dyn_ftrace *rec, int enable) | ||
415 | { | ||
416 | unsigned long ftrace_addr; | ||
417 | int ret; | ||
418 | |||
419 | ret = ftrace_test_record(rec, enable); | ||
420 | |||
421 | ftrace_addr = (unsigned long)FTRACE_ADDR; | ||
422 | |||
423 | switch (ret) { | ||
424 | case FTRACE_UPDATE_IGNORE: | ||
425 | return 0; | ||
426 | |||
427 | case FTRACE_UPDATE_MAKE_CALL: | ||
428 | /* converting nop to call */ | ||
429 | return add_brk_on_nop(rec); | ||
430 | |||
431 | case FTRACE_UPDATE_MAKE_NOP: | ||
432 | /* converting a call to a nop */ | ||
433 | return add_brk_on_call(rec, ftrace_addr); | ||
434 | } | ||
435 | return 0; | ||
436 | } | ||
437 | |||
438 | /* | ||
439 | * On error, we need to remove breakpoints. This needs to | ||
440 | * be done caefully. If the address does not currently have a | ||
441 | * breakpoint, we know we are done. Otherwise, we look at the | ||
442 | * remaining 4 bytes of the instruction. If it matches a nop | ||
443 | * we replace the breakpoint with the nop. Otherwise we replace | ||
444 | * it with the call instruction. | ||
445 | */ | ||
446 | static int remove_breakpoint(struct dyn_ftrace *rec) | ||
447 | { | ||
448 | unsigned char ins[MCOUNT_INSN_SIZE]; | ||
449 | unsigned char brk = BREAKPOINT_INSTRUCTION; | ||
450 | const unsigned char *nop; | ||
451 | unsigned long ftrace_addr; | ||
452 | unsigned long ip = rec->ip; | ||
453 | |||
454 | /* If we fail the read, just give up */ | ||
455 | if (probe_kernel_read(ins, (void *)ip, MCOUNT_INSN_SIZE)) | ||
456 | return -EFAULT; | ||
457 | |||
458 | /* If this does not have a breakpoint, we are done */ | ||
459 | if (ins[0] != brk) | ||
460 | return -1; | ||
461 | |||
462 | nop = ftrace_nop_replace(); | ||
463 | |||
464 | /* | ||
465 | * If the last 4 bytes of the instruction do not match | ||
466 | * a nop, then we assume that this is a call to ftrace_addr. | ||
467 | */ | ||
468 | if (memcmp(&ins[1], &nop[1], MCOUNT_INSN_SIZE - 1) != 0) { | ||
469 | /* | ||
470 | * For extra paranoidism, we check if the breakpoint is on | ||
471 | * a call that would actually jump to the ftrace_addr. | ||
472 | * If not, don't touch the breakpoint, we make just create | ||
473 | * a disaster. | ||
474 | */ | ||
475 | ftrace_addr = (unsigned long)FTRACE_ADDR; | ||
476 | nop = ftrace_call_replace(ip, ftrace_addr); | ||
477 | |||
478 | if (memcmp(&ins[1], &nop[1], MCOUNT_INSN_SIZE - 1) != 0) | ||
479 | return -EINVAL; | ||
480 | } | ||
481 | |||
482 | return probe_kernel_write((void *)ip, &nop[0], 1); | ||
483 | } | ||
484 | |||
485 | static int add_update_code(unsigned long ip, unsigned const char *new) | ||
486 | { | ||
487 | /* skip breakpoint */ | ||
488 | ip++; | ||
489 | new++; | ||
490 | if (ftrace_write(ip, new, MCOUNT_INSN_SIZE - 1)) | ||
491 | return -EPERM; | ||
492 | return 0; | ||
493 | } | ||
494 | |||
495 | static int add_update_call(struct dyn_ftrace *rec, unsigned long addr) | ||
496 | { | ||
497 | unsigned long ip = rec->ip; | ||
498 | unsigned const char *new; | ||
499 | |||
500 | new = ftrace_call_replace(ip, addr); | ||
501 | return add_update_code(ip, new); | ||
502 | } | ||
503 | |||
504 | static int add_update_nop(struct dyn_ftrace *rec) | ||
505 | { | ||
506 | unsigned long ip = rec->ip; | ||
507 | unsigned const char *new; | ||
508 | |||
509 | new = ftrace_nop_replace(); | ||
510 | return add_update_code(ip, new); | ||
511 | } | ||
512 | |||
513 | static int add_update(struct dyn_ftrace *rec, int enable) | ||
514 | { | ||
515 | unsigned long ftrace_addr; | ||
516 | int ret; | ||
517 | |||
518 | ret = ftrace_test_record(rec, enable); | ||
519 | |||
520 | ftrace_addr = (unsigned long)FTRACE_ADDR; | ||
521 | |||
522 | switch (ret) { | ||
523 | case FTRACE_UPDATE_IGNORE: | ||
524 | return 0; | ||
525 | |||
526 | case FTRACE_UPDATE_MAKE_CALL: | ||
527 | /* converting nop to call */ | ||
528 | return add_update_call(rec, ftrace_addr); | ||
529 | |||
530 | case FTRACE_UPDATE_MAKE_NOP: | ||
531 | /* converting a call to a nop */ | ||
532 | return add_update_nop(rec); | ||
533 | } | ||
534 | |||
535 | return 0; | ||
536 | } | ||
537 | |||
538 | static int finish_update_call(struct dyn_ftrace *rec, unsigned long addr) | ||
539 | { | ||
540 | unsigned long ip = rec->ip; | ||
541 | unsigned const char *new; | ||
542 | |||
543 | new = ftrace_call_replace(ip, addr); | ||
544 | |||
545 | if (ftrace_write(ip, new, 1)) | ||
546 | return -EPERM; | ||
547 | |||
548 | return 0; | ||
549 | } | ||
550 | |||
551 | static int finish_update_nop(struct dyn_ftrace *rec) | ||
552 | { | ||
553 | unsigned long ip = rec->ip; | ||
554 | unsigned const char *new; | ||
555 | |||
556 | new = ftrace_nop_replace(); | ||
557 | |||
558 | if (ftrace_write(ip, new, 1)) | ||
559 | return -EPERM; | ||
560 | return 0; | ||
561 | } | ||
562 | |||
563 | static int finish_update(struct dyn_ftrace *rec, int enable) | ||
564 | { | ||
565 | unsigned long ftrace_addr; | ||
566 | int ret; | ||
567 | |||
568 | ret = ftrace_update_record(rec, enable); | ||
569 | |||
570 | ftrace_addr = (unsigned long)FTRACE_ADDR; | ||
571 | |||
572 | switch (ret) { | ||
573 | case FTRACE_UPDATE_IGNORE: | ||
574 | return 0; | ||
575 | |||
576 | case FTRACE_UPDATE_MAKE_CALL: | ||
577 | /* converting nop to call */ | ||
578 | return finish_update_call(rec, ftrace_addr); | ||
579 | |||
580 | case FTRACE_UPDATE_MAKE_NOP: | ||
581 | /* converting a call to a nop */ | ||
582 | return finish_update_nop(rec); | ||
583 | } | ||
584 | |||
585 | return 0; | ||
586 | } | ||
587 | |||
588 | static void do_sync_core(void *data) | ||
589 | { | ||
590 | sync_core(); | ||
591 | } | ||
592 | |||
593 | static void run_sync(void) | ||
594 | { | ||
595 | int enable_irqs = irqs_disabled(); | ||
596 | |||
597 | /* We may be called with interrupts disbled (on bootup). */ | ||
598 | if (enable_irqs) | ||
599 | local_irq_enable(); | ||
600 | on_each_cpu(do_sync_core, NULL, 1); | ||
601 | if (enable_irqs) | ||
602 | local_irq_disable(); | ||
603 | } | ||
604 | |||
605 | static void ftrace_replace_code(int enable) | ||
606 | { | ||
607 | struct ftrace_rec_iter *iter; | ||
608 | struct dyn_ftrace *rec; | ||
609 | const char *report = "adding breakpoints"; | ||
610 | int count = 0; | ||
611 | int ret; | ||
612 | |||
613 | for_ftrace_rec_iter(iter) { | ||
614 | rec = ftrace_rec_iter_record(iter); | ||
615 | |||
616 | ret = add_breakpoints(rec, enable); | ||
617 | if (ret) | ||
618 | goto remove_breakpoints; | ||
619 | count++; | ||
620 | } | ||
621 | |||
622 | run_sync(); | ||
623 | |||
624 | report = "updating code"; | ||
625 | |||
626 | for_ftrace_rec_iter(iter) { | ||
627 | rec = ftrace_rec_iter_record(iter); | ||
628 | |||
629 | ret = add_update(rec, enable); | ||
630 | if (ret) | ||
631 | goto remove_breakpoints; | ||
632 | } | ||
633 | |||
634 | run_sync(); | ||
635 | |||
636 | report = "removing breakpoints"; | ||
637 | |||
638 | for_ftrace_rec_iter(iter) { | ||
639 | rec = ftrace_rec_iter_record(iter); | ||
640 | |||
641 | ret = finish_update(rec, enable); | ||
642 | if (ret) | ||
643 | goto remove_breakpoints; | ||
644 | } | ||
645 | |||
646 | run_sync(); | ||
647 | |||
648 | return; | ||
649 | |||
650 | remove_breakpoints: | ||
651 | ftrace_bug(ret, rec ? rec->ip : 0); | ||
652 | printk(KERN_WARNING "Failed on %s (%d):\n", report, count); | ||
653 | for_ftrace_rec_iter(iter) { | ||
654 | rec = ftrace_rec_iter_record(iter); | ||
655 | remove_breakpoint(rec); | ||
656 | } | ||
657 | } | ||
658 | |||
659 | void arch_ftrace_update_code(int command) | ||
660 | { | ||
661 | modifying_ftrace_code++; | ||
662 | |||
663 | if (command & FTRACE_UPDATE_CALLS) | ||
664 | ftrace_replace_code(1); | ||
665 | else if (command & FTRACE_DISABLE_CALLS) | ||
666 | ftrace_replace_code(0); | ||
667 | |||
668 | if (command & FTRACE_UPDATE_TRACE_FUNC) | ||
669 | ftrace_update_ftrace_func(ftrace_trace_function); | ||
670 | |||
671 | if (command & FTRACE_START_FUNC_RET) | ||
672 | ftrace_enable_ftrace_graph_caller(); | ||
673 | else if (command & FTRACE_STOP_FUNC_RET) | ||
674 | ftrace_disable_ftrace_graph_caller(); | ||
675 | |||
676 | modifying_ftrace_code--; | ||
677 | } | ||
678 | |||
337 | int __init ftrace_dyn_arch_init(void *data) | 679 | int __init ftrace_dyn_arch_init(void *data) |
338 | { | 680 | { |
339 | /* The return code is retured via data */ | 681 | /* The return code is retured via data */ |
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index ff9281f16029..92d5756d85fc 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c | |||
@@ -50,6 +50,7 @@ | |||
50 | #include <asm/processor.h> | 50 | #include <asm/processor.h> |
51 | #include <asm/debugreg.h> | 51 | #include <asm/debugreg.h> |
52 | #include <linux/atomic.h> | 52 | #include <linux/atomic.h> |
53 | #include <asm/ftrace.h> | ||
53 | #include <asm/traps.h> | 54 | #include <asm/traps.h> |
54 | #include <asm/desc.h> | 55 | #include <asm/desc.h> |
55 | #include <asm/i387.h> | 56 | #include <asm/i387.h> |
@@ -303,8 +304,13 @@ gp_in_kernel: | |||
303 | } | 304 | } |
304 | 305 | ||
305 | /* May run on IST stack. */ | 306 | /* May run on IST stack. */ |
306 | dotraplinkage void __kprobes do_int3(struct pt_regs *regs, long error_code) | 307 | dotraplinkage void __kprobes notrace do_int3(struct pt_regs *regs, long error_code) |
307 | { | 308 | { |
309 | #ifdef CONFIG_DYNAMIC_FTRACE | ||
310 | /* ftrace must be first, everything else may cause a recursive crash */ | ||
311 | if (unlikely(modifying_ftrace_code) && ftrace_int3_handler(regs)) | ||
312 | return; | ||
313 | #endif | ||
308 | #ifdef CONFIG_KGDB_LOW_LEVEL_TRAP | 314 | #ifdef CONFIG_KGDB_LOW_LEVEL_TRAP |
309 | if (kgdb_ll_trap(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP, | 315 | if (kgdb_ll_trap(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP, |
310 | SIGTRAP) == NOTIFY_STOP) | 316 | SIGTRAP) == NOTIFY_STOP) |
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 72a6cabb4d5b..0b5590330bca 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h | |||
@@ -286,6 +286,12 @@ struct ftrace_rec_iter *ftrace_rec_iter_start(void); | |||
286 | struct ftrace_rec_iter *ftrace_rec_iter_next(struct ftrace_rec_iter *iter); | 286 | struct ftrace_rec_iter *ftrace_rec_iter_next(struct ftrace_rec_iter *iter); |
287 | struct dyn_ftrace *ftrace_rec_iter_record(struct ftrace_rec_iter *iter); | 287 | struct dyn_ftrace *ftrace_rec_iter_record(struct ftrace_rec_iter *iter); |
288 | 288 | ||
289 | #define for_ftrace_rec_iter(iter) \ | ||
290 | for (iter = ftrace_rec_iter_start(); \ | ||
291 | iter; \ | ||
292 | iter = ftrace_rec_iter_next(iter)) | ||
293 | |||
294 | |||
289 | int ftrace_update_record(struct dyn_ftrace *rec, int enable); | 295 | int ftrace_update_record(struct dyn_ftrace *rec, int enable); |
290 | int ftrace_test_record(struct dyn_ftrace *rec, int enable); | 296 | int ftrace_test_record(struct dyn_ftrace *rec, int enable); |
291 | void ftrace_run_stop_machine(int command); | 297 | void ftrace_run_stop_machine(int command); |