aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/include/asm/ftrace.h3
-rw-r--r--arch/x86/kernel/ftrace.c342
-rw-r--r--arch/x86/kernel/traps.c8
-rw-r--r--include/linux/ftrace.h6
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__
36extern void mcount(void); 36extern void mcount(void);
37extern int modifying_ftrace_code;
37 38
38static inline unsigned long ftrace_call_adjust(unsigned long addr) 39static 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
54int 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
338int 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 */
347int 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
360static 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
376static 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
394static 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
405static 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
414static 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 */
446static 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
485static 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
495static 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
504static 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
513static 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
538static 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
551static 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
563static 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
588static void do_sync_core(void *data)
589{
590 sync_core();
591}
592
593static 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
605static 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
659void 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
337int __init ftrace_dyn_arch_init(void *data) 679int __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. */
306dotraplinkage void __kprobes do_int3(struct pt_regs *regs, long error_code) 307dotraplinkage 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);
286struct ftrace_rec_iter *ftrace_rec_iter_next(struct ftrace_rec_iter *iter); 286struct ftrace_rec_iter *ftrace_rec_iter_next(struct ftrace_rec_iter *iter);
287struct dyn_ftrace *ftrace_rec_iter_record(struct ftrace_rec_iter *iter); 287struct 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
289int ftrace_update_record(struct dyn_ftrace *rec, int enable); 295int ftrace_update_record(struct dyn_ftrace *rec, int enable);
290int ftrace_test_record(struct dyn_ftrace *rec, int enable); 296int ftrace_test_record(struct dyn_ftrace *rec, int enable);
291void ftrace_run_stop_machine(int command); 297void ftrace_run_stop_machine(int command);