|
|
@@ -29,6 +29,7 @@
|
|
|
#include <asm/kprobes.h>
|
|
|
#include <asm/ftrace.h>
|
|
|
#include <asm/nops.h>
|
|
|
+#include <asm/text-patching.h>
|
|
|
|
|
|
#ifdef CONFIG_DYNAMIC_FTRACE
|
|
|
|
|
|
@@ -228,6 +229,7 @@ int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
|
|
|
}
|
|
|
|
|
|
static unsigned long ftrace_update_func;
|
|
|
+static unsigned long ftrace_update_func_call;
|
|
|
|
|
|
static int update_ftrace_func(unsigned long ip, void *new)
|
|
|
{
|
|
|
@@ -256,6 +258,8 @@ int ftrace_update_ftrace_func(ftrace_func_t func)
|
|
|
unsigned char *new;
|
|
|
int ret;
|
|
|
|
|
|
+ ftrace_update_func_call = (unsigned long)func;
|
|
|
+
|
|
|
new = ftrace_call_replace(ip, (unsigned long)func);
|
|
|
ret = update_ftrace_func(ip, new);
|
|
|
|
|
|
@@ -291,13 +295,28 @@ int ftrace_int3_handler(struct pt_regs *regs)
|
|
|
if (WARN_ON_ONCE(!regs))
|
|
|
return 0;
|
|
|
|
|
|
- ip = regs->ip - 1;
|
|
|
- if (!ftrace_location(ip) && !is_ftrace_caller(ip))
|
|
|
- return 0;
|
|
|
+ ip = regs->ip - INT3_INSN_SIZE;
|
|
|
|
|
|
- regs->ip += MCOUNT_INSN_SIZE - 1;
|
|
|
+#ifdef CONFIG_X86_64
|
|
|
+ if (ftrace_location(ip)) {
|
|
|
+ int3_emulate_call(regs, (unsigned long)ftrace_regs_caller);
|
|
|
+ return 1;
|
|
|
+ } else if (is_ftrace_caller(ip)) {
|
|
|
+ if (!ftrace_update_func_call) {
|
|
|
+ int3_emulate_jmp(regs, ip + CALL_INSN_SIZE);
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ int3_emulate_call(regs, ftrace_update_func_call);
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+#else
|
|
|
+ if (ftrace_location(ip) || is_ftrace_caller(ip)) {
|
|
|
+ int3_emulate_jmp(regs, ip + CALL_INSN_SIZE);
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+#endif
|
|
|
|
|
|
- return 1;
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
static int ftrace_write(unsigned long ip, const char *val, int size)
|
|
|
@@ -868,6 +887,8 @@ void arch_ftrace_update_trampoline(struct ftrace_ops *ops)
|
|
|
|
|
|
func = ftrace_ops_get_func(ops);
|
|
|
|
|
|
+ ftrace_update_func_call = (unsigned long)func;
|
|
|
+
|
|
|
/* Do a safe modify in case the trampoline is executing */
|
|
|
new = ftrace_call_replace(ip, (unsigned long)func);
|
|
|
ret = update_ftrace_func(ip, new);
|
|
|
@@ -964,6 +985,7 @@ static int ftrace_mod_jmp(unsigned long ip, void *func)
|
|
|
{
|
|
|
unsigned char *new;
|
|
|
|
|
|
+ ftrace_update_func_call = 0UL;
|
|
|
new = ftrace_jmp_replace(ip, (unsigned long)func);
|
|
|
|
|
|
return update_ftrace_func(ip, new);
|