|
@@ -6,9 +6,109 @@
|
|
|
*/
|
|
|
|
|
|
#include <linux/ftrace.h>
|
|
|
+#include <linux/uaccess.h>
|
|
|
+#include <asm/cacheflush.h>
|
|
|
|
|
|
+#ifdef CONFIG_DYNAMIC_FTRACE
|
|
|
+static int ftrace_check_current_call(unsigned long hook_pos,
|
|
|
+ unsigned int *expected)
|
|
|
+{
|
|
|
+ unsigned int replaced[2];
|
|
|
+ unsigned int nops[2] = {NOP4, NOP4};
|
|
|
+
|
|
|
+ /* we expect nops at the hook position */
|
|
|
+ if (!expected)
|
|
|
+ expected = nops;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Read the text we want to modify;
|
|
|
+ * return must be -EFAULT on read error
|
|
|
+ */
|
|
|
+ if (probe_kernel_read(replaced, (void *)hook_pos, MCOUNT_INSN_SIZE))
|
|
|
+ return -EFAULT;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Make sure it is what we expect it to be;
|
|
|
+ * return must be -EINVAL on failed comparison
|
|
|
+ */
|
|
|
+ if (memcmp(expected, replaced, sizeof(replaced))) {
|
|
|
+ pr_err("%p: expected (%08x %08x) but get (%08x %08x)",
|
|
|
+ (void *)hook_pos, expected[0], expected[1], replaced[0],
|
|
|
+ replaced[1]);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int __ftrace_modify_call(unsigned long hook_pos, unsigned long target,
|
|
|
+ bool enable)
|
|
|
+{
|
|
|
+ unsigned int call[2];
|
|
|
+ unsigned int nops[2] = {NOP4, NOP4};
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ make_call(hook_pos, target, call);
|
|
|
+
|
|
|
+ /* replace the auipc-jalr pair at once */
|
|
|
+ ret = probe_kernel_write((void *)hook_pos, enable ? call : nops,
|
|
|
+ MCOUNT_INSN_SIZE);
|
|
|
+ /* return must be -EPERM on write error */
|
|
|
+ if (ret)
|
|
|
+ return -EPERM;
|
|
|
+
|
|
|
+ smp_mb();
|
|
|
+ flush_icache_range((void *)hook_pos, (void *)hook_pos + MCOUNT_INSN_SIZE);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
|
|
|
+{
|
|
|
+ int ret = ftrace_check_current_call(rec->ip, NULL);
|
|
|
+
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ return __ftrace_modify_call(rec->ip, addr, true);
|
|
|
+}
|
|
|
+
|
|
|
+int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
|
|
|
+ unsigned long addr)
|
|
|
+{
|
|
|
+ unsigned int call[2];
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ make_call(rec->ip, addr, call);
|
|
|
+ ret = ftrace_check_current_call(rec->ip, call);
|
|
|
+
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ return __ftrace_modify_call(rec->ip, addr, false);
|
|
|
+}
|
|
|
+
|
|
|
+int ftrace_update_ftrace_func(ftrace_func_t func)
|
|
|
+{
|
|
|
+ int ret = __ftrace_modify_call((unsigned long)&ftrace_call,
|
|
|
+ (unsigned long)func, true);
|
|
|
+ if (!ret) {
|
|
|
+ ret = __ftrace_modify_call((unsigned long)&ftrace_regs_call,
|
|
|
+ (unsigned long)func, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+int __init ftrace_dyn_arch_init(void)
|
|
|
+{
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
|
|
/*
|
|
|
- * Most of this file is copied from arm64.
|
|
|
+ * Most of this function is copied from arm64.
|
|
|
*/
|
|
|
void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
|
|
|
unsigned long frame_pointer)
|
|
@@ -39,3 +139,4 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
|
|
|
return;
|
|
|
*parent = return_hooker;
|
|
|
}
|
|
|
+#endif
|