12 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
19 #include <linux/sched.h>
21 #include <linux/list.h>
22 #include <linux/module.h>
26 #include <asm/cacheflush.h>
27 #include <asm/kprobes.h>
28 #include <asm/ftrace.h>
31 #ifdef CONFIG_DYNAMIC_FTRACE
33 int ftrace_arch_code_modify_prepare(
void)
36 set_all_modules_text_rw();
40 int ftrace_arch_code_modify_post_process(
void)
42 set_all_modules_text_ro();
47 union ftrace_code_union {
55 static int ftrace_calc_offset(
long ip,
long addr)
57 return (
int)(addr -
ip);
60 static unsigned char *ftrace_call_replace(
unsigned long ip,
unsigned long addr)
62 static union ftrace_code_union calc;
75 within(
unsigned long addr,
unsigned long start,
unsigned long end)
77 return addr >= start && addr <
end;
81 do_ftrace_mod_code(
unsigned long ip,
const void *new_code)
91 if (within(ip, (
unsigned long)_text, (
unsigned long)_etext))
97 static const unsigned char *ftrace_nop_replace(
void)
103 ftrace_modify_code_direct(
unsigned long ip,
unsigned const char *old_code,
104 unsigned const char *new_code)
127 if (do_ftrace_mod_code(ip, new_code))
136 struct dyn_ftrace *rec,
unsigned long addr)
138 unsigned const char *
new, *old;
139 unsigned long ip = rec->ip;
141 old = ftrace_call_replace(ip, addr);
142 new = ftrace_nop_replace();
153 return ftrace_modify_code_direct(rec->ip, old,
new);
156 WARN_ONCE(1,
"invalid use of ftrace_make_nop");
162 unsigned const char *
new, *old;
163 unsigned long ip = rec->ip;
165 old = ftrace_nop_replace();
166 new = ftrace_call_replace(ip, addr);
169 return ftrace_modify_code_direct(rec->ip, old,
new);
206 ftrace_modify_code(
unsigned long ip,
unsigned const char *old_code,
207 unsigned const char *new_code);
217 int ftrace_modify_call(
struct dyn_ftrace *rec,
unsigned long old_addr,
226 unsigned long ip = (
unsigned long)(&ftrace_call);
231 new = ftrace_call_replace(ip, (
unsigned long)func);
236 ret = ftrace_modify_code(ip, old,
new);
240 ip = (
unsigned long)(&ftrace_regs_call);
242 new = ftrace_call_replace(ip, (
unsigned long)func);
243 ret = ftrace_modify_code(ip, old,
new);
263 if (!ftrace_location(regs->ip - 1))
271 static int ftrace_write(
unsigned long ip,
const char *
val,
int size)
281 if (within(ip, (
unsigned long)_text, (
unsigned long)_etext))
287 static int add_break(
unsigned long ip,
const char *old)
299 if (ftrace_write(ip, &brk, 1))
305 static int add_brk_on_call(
struct dyn_ftrace *rec,
unsigned long addr)
307 unsigned const char *old;
308 unsigned long ip = rec->ip;
310 old = ftrace_call_replace(ip, addr);
312 return add_break(rec->ip, old);
316 static int add_brk_on_nop(
struct dyn_ftrace *rec)
318 unsigned const char *old;
320 old = ftrace_nop_replace();
322 return add_break(rec->ip, old);
330 static unsigned long get_ftrace_addr(
struct dyn_ftrace *rec)
332 if (rec->flags & FTRACE_FL_REGS)
333 return (
unsigned long)FTRACE_REGS_ADDR;
335 return (
unsigned long)FTRACE_ADDR;
343 static unsigned long get_ftrace_old_addr(
struct dyn_ftrace *rec)
345 if (rec->flags & FTRACE_FL_REGS_EN)
346 return (
unsigned long)FTRACE_REGS_ADDR;
348 return (
unsigned long)FTRACE_ADDR;
351 static int add_breakpoints(
struct dyn_ftrace *rec,
int enable)
353 unsigned long ftrace_addr;
356 ret = ftrace_test_record(rec, enable);
358 ftrace_addr = get_ftrace_addr(rec);
361 case FTRACE_UPDATE_IGNORE:
364 case FTRACE_UPDATE_MAKE_CALL:
366 return add_brk_on_nop(rec);
368 case FTRACE_UPDATE_MODIFY_CALL_REGS:
369 case FTRACE_UPDATE_MODIFY_CALL:
370 ftrace_addr = get_ftrace_old_addr(rec);
372 case FTRACE_UPDATE_MAKE_NOP:
374 return add_brk_on_call(rec, ftrace_addr);
387 static int remove_breakpoint(
struct dyn_ftrace *rec)
391 const unsigned char *
nop;
392 unsigned long ftrace_addr;
393 unsigned long ip = rec->ip;
403 nop = ftrace_nop_replace();
416 ftrace_addr = get_ftrace_addr(rec);
417 nop = ftrace_call_replace(ip, ftrace_addr);
423 ftrace_addr = get_ftrace_old_addr(rec);
424 nop = ftrace_call_replace(ip, ftrace_addr);
434 static int add_update_code(
unsigned long ip,
unsigned const char *
new)
444 static int add_update_call(
struct dyn_ftrace *rec,
unsigned long addr)
446 unsigned long ip = rec->ip;
447 unsigned const char *
new;
449 new = ftrace_call_replace(ip, addr);
450 return add_update_code(ip,
new);
453 static int add_update_nop(
struct dyn_ftrace *rec)
455 unsigned long ip = rec->ip;
456 unsigned const char *
new;
458 new = ftrace_nop_replace();
459 return add_update_code(ip,
new);
462 static int add_update(
struct dyn_ftrace *rec,
int enable)
464 unsigned long ftrace_addr;
467 ret = ftrace_test_record(rec, enable);
469 ftrace_addr = get_ftrace_addr(rec);
472 case FTRACE_UPDATE_IGNORE:
475 case FTRACE_UPDATE_MODIFY_CALL_REGS:
476 case FTRACE_UPDATE_MODIFY_CALL:
477 case FTRACE_UPDATE_MAKE_CALL:
479 return add_update_call(rec, ftrace_addr);
481 case FTRACE_UPDATE_MAKE_NOP:
483 return add_update_nop(rec);
489 static int finish_update_call(
struct dyn_ftrace *rec,
unsigned long addr)
491 unsigned long ip = rec->ip;
492 unsigned const char *
new;
494 new = ftrace_call_replace(ip, addr);
496 if (ftrace_write(ip,
new, 1))
502 static int finish_update_nop(
struct dyn_ftrace *rec)
504 unsigned long ip = rec->ip;
505 unsigned const char *
new;
507 new = ftrace_nop_replace();
509 if (ftrace_write(ip,
new, 1))
514 static int finish_update(
struct dyn_ftrace *rec,
int enable)
516 unsigned long ftrace_addr;
519 ret = ftrace_update_record(rec, enable);
521 ftrace_addr = get_ftrace_addr(rec);
524 case FTRACE_UPDATE_IGNORE:
527 case FTRACE_UPDATE_MODIFY_CALL_REGS:
528 case FTRACE_UPDATE_MODIFY_CALL:
529 case FTRACE_UPDATE_MAKE_CALL:
531 return finish_update_call(rec, ftrace_addr);
533 case FTRACE_UPDATE_MAKE_NOP:
535 return finish_update_nop(rec);
541 static void do_sync_core(
void *
data)
546 static void run_sync(
void)
558 void ftrace_replace_code(
int enable)
560 struct ftrace_rec_iter *iter;
561 struct dyn_ftrace *rec;
562 const char *
report =
"adding breakpoints";
566 for_ftrace_rec_iter(iter) {
567 rec = ftrace_rec_iter_record(iter);
569 ret = add_breakpoints(rec, enable);
571 goto remove_breakpoints;
577 report =
"updating code";
579 for_ftrace_rec_iter(iter) {
580 rec = ftrace_rec_iter_record(iter);
582 ret = add_update(rec, enable);
584 goto remove_breakpoints;
589 report =
"removing breakpoints";
591 for_ftrace_rec_iter(iter) {
592 rec = ftrace_rec_iter_record(iter);
594 ret = finish_update(rec, enable);
596 goto remove_breakpoints;
604 ftrace_bug(ret, rec ? rec->ip : 0);
606 for_ftrace_rec_iter(iter) {
607 rec = ftrace_rec_iter_record(iter);
608 remove_breakpoint(rec);
613 ftrace_modify_code(
unsigned long ip,
unsigned const char *old_code,
614 unsigned const char *new_code)
618 ret = add_break(ip, old_code);
624 ret = add_update_code(ip, new_code);
630 ret = ftrace_write(ip, new_code, 1);
644 void arch_ftrace_update_code(
int command)
649 ftrace_modify_all_code(command);
657 *(
unsigned long *)data = 0;
663 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
665 #ifdef CONFIG_DYNAMIC_FTRACE
666 extern void ftrace_graph_call(
void);
668 static int ftrace_mod_jmp(
unsigned long ip,
669 int old_offset,
int new_offset)
676 if (code[0] != 0xe9 || old_offset != *(
int *)(&code[1]))
679 *(
int *)(&code[1]) = new_offset;
681 if (do_ftrace_mod_code(ip, &code))
687 int ftrace_enable_ftrace_graph_caller(
void)
689 unsigned long ip = (
unsigned long)(&ftrace_graph_call);
690 int old_offset, new_offset;
695 return ftrace_mod_jmp(ip, old_offset, new_offset);
698 int ftrace_disable_ftrace_graph_caller(
void)
700 unsigned long ip = (
unsigned long)(&ftrace_graph_call);
701 int old_offset, new_offset;
706 return ftrace_mod_jmp(ip, old_offset, new_offset);
715 void prepare_ftrace_return(
unsigned long *parent,
unsigned long self_addr,
716 unsigned long frame_pointer)
721 unsigned long return_hooker = (
unsigned long)
733 "1: " _ASM_MOV " (%[parent]), %[old]\n"
734 "2: " _ASM_MOV " %[return_hooker], (%[parent])\n"
735 " movl $0, %[faulted]\n"
738 ".section .fixup, \"ax\"\n"
739 "4: movl $1, %[faulted]\n"
746 : [old] "=&
r" (old), [faulted] "=
r" (faulted)
747 : [parent] "
r" (parent), [return_hooker] "
r" (return_hooker)
757 trace.func = self_addr;
761 if (!ftrace_graph_entry(&
trace)) {
767 frame_pointer) == -
EBUSY) {