0001
0002 #include <linux/spinlock.h>
0003 #include <linux/hardirq.h>
0004 #include <linux/ftrace.h>
0005 #include <linux/percpu.h>
0006 #include <linux/init.h>
0007 #include <linux/list.h>
0008 #include <trace/syscall.h>
0009
0010 #include <asm/ftrace.h>
0011
0012 #ifdef CONFIG_DYNAMIC_FTRACE
0013 static const u32 ftrace_nop = 0x01000000;
0014
0015 static u32 ftrace_call_replace(unsigned long ip, unsigned long addr)
0016 {
0017 u32 call;
0018 s32 off;
0019
0020 off = ((s32)addr - (s32)ip);
0021 call = 0x40000000 | ((u32)off >> 2);
0022
0023 return call;
0024 }
0025
0026 static int ftrace_modify_code(unsigned long ip, u32 old, u32 new)
0027 {
0028 u32 replaced;
0029 int faulted;
0030
0031 __asm__ __volatile__(
0032 "1: cas [%[ip]], %[old], %[new]\n"
0033 " flush %[ip]\n"
0034 " mov 0, %[faulted]\n"
0035 "2:\n"
0036 " .section .fixup,#alloc,#execinstr\n"
0037 " .align 4\n"
0038 "3: sethi %%hi(2b), %[faulted]\n"
0039 " jmpl %[faulted] + %%lo(2b), %%g0\n"
0040 " mov 1, %[faulted]\n"
0041 " .previous\n"
0042 " .section __ex_table,\"a\"\n"
0043 " .align 4\n"
0044 " .word 1b, 3b\n"
0045 " .previous\n"
0046 : "=r" (replaced), [faulted] "=r" (faulted)
0047 : [new] "0" (new), [old] "r" (old), [ip] "r" (ip)
0048 : "memory");
0049
0050 if (replaced != old && replaced != new)
0051 faulted = 2;
0052
0053 return faulted;
0054 }
0055
0056 int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, unsigned long addr)
0057 {
0058 unsigned long ip = rec->ip;
0059 u32 old, new;
0060
0061 old = ftrace_call_replace(ip, addr);
0062 new = ftrace_nop;
0063 return ftrace_modify_code(ip, old, new);
0064 }
0065
0066 int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
0067 {
0068 unsigned long ip = rec->ip;
0069 u32 old, new;
0070
0071 old = ftrace_nop;
0072 new = ftrace_call_replace(ip, addr);
0073 return ftrace_modify_code(ip, old, new);
0074 }
0075
0076 int ftrace_update_ftrace_func(ftrace_func_t func)
0077 {
0078 unsigned long ip = (unsigned long)(&ftrace_call);
0079 u32 old, new;
0080
0081 old = *(u32 *) &ftrace_call;
0082 new = ftrace_call_replace(ip, (unsigned long)func);
0083 return ftrace_modify_code(ip, old, new);
0084 }
0085 #endif
0086
0087 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
0088
0089 #ifdef CONFIG_DYNAMIC_FTRACE
0090 extern void ftrace_graph_call(void);
0091
0092 int ftrace_enable_ftrace_graph_caller(void)
0093 {
0094 unsigned long ip = (unsigned long)(&ftrace_graph_call);
0095 u32 old, new;
0096
0097 old = *(u32 *) &ftrace_graph_call;
0098 new = ftrace_call_replace(ip, (unsigned long) &ftrace_graph_caller);
0099 return ftrace_modify_code(ip, old, new);
0100 }
0101
0102 int ftrace_disable_ftrace_graph_caller(void)
0103 {
0104 unsigned long ip = (unsigned long)(&ftrace_graph_call);
0105 u32 old, new;
0106
0107 old = *(u32 *) &ftrace_graph_call;
0108 new = ftrace_call_replace(ip, (unsigned long) &ftrace_stub);
0109
0110 return ftrace_modify_code(ip, old, new);
0111 }
0112
0113 #endif
0114
0115
0116
0117
0118
0119 unsigned long prepare_ftrace_return(unsigned long parent,
0120 unsigned long self_addr,
0121 unsigned long frame_pointer)
0122 {
0123 unsigned long return_hooker = (unsigned long) &return_to_handler;
0124
0125 if (unlikely(atomic_read(¤t->tracing_graph_pause)))
0126 return parent + 8UL;
0127
0128 if (function_graph_enter(parent, self_addr, frame_pointer, NULL))
0129 return parent + 8UL;
0130
0131 return return_hooker;
0132 }
0133 #endif