1
2
3
4
5
6
7
8
9
10
11
12#include <linux/spinlock.h>
13#include <linux/hardirq.h>
14#include <linux/uaccess.h>
15#include <linux/ftrace.h>
16#include <linux/percpu.h>
17#include <linux/init.h>
18#include <linux/list.h>
19
20#include <asm/ftrace.h>
21#include <asm/nops.h>
22
23
24static unsigned char ftrace_nop[MCOUNT_INSN_SIZE];
25
26union ftrace_code_union {
27 char code[MCOUNT_INSN_SIZE];
28 struct {
29 char e8;
30 int offset;
31 } __attribute__((packed));
32};
33
34
35static int ftrace_calc_offset(long ip, long addr)
36{
37 return (int)(addr - ip);
38}
39
40unsigned char *ftrace_nop_replace(void)
41{
42 return ftrace_nop;
43}
44
45unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
46{
47 static union ftrace_code_union calc;
48
49 calc.e8 = 0xe8;
50 calc.offset = ftrace_calc_offset(ip + MCOUNT_INSN_SIZE, addr);
51
52
53
54
55
56 return calc.code;
57}
58
59int
60ftrace_modify_code(unsigned long ip, unsigned char *old_code,
61 unsigned char *new_code)
62{
63 unsigned char replaced[MCOUNT_INSN_SIZE];
64
65
66
67
68
69
70
71
72
73
74
75
76 if (probe_kernel_read(replaced, (void *)ip, MCOUNT_INSN_SIZE))
77 return -EFAULT;
78
79
80 if (memcmp(replaced, old_code, MCOUNT_INSN_SIZE) != 0)
81 return -EINVAL;
82
83
84 if (probe_kernel_write((void *)ip, new_code, MCOUNT_INSN_SIZE))
85 return -EPERM;
86
87 sync_core();
88
89 return 0;
90}
91
92int ftrace_update_ftrace_func(ftrace_func_t func)
93{
94 unsigned long ip = (unsigned long)(&ftrace_call);
95 unsigned char old[MCOUNT_INSN_SIZE], *new;
96 int ret;
97
98 memcpy(old, &ftrace_call, MCOUNT_INSN_SIZE);
99 new = ftrace_call_replace(ip, (unsigned long)func);
100 ret = ftrace_modify_code(ip, old, new);
101
102 return ret;
103}
104
105int __init ftrace_dyn_arch_init(void *data)
106{
107 extern const unsigned char ftrace_test_p6nop[];
108 extern const unsigned char ftrace_test_nop5[];
109 extern const unsigned char ftrace_test_jmp[];
110 int faulted = 0;
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126 asm volatile (
127 "ftrace_test_jmp:"
128 "jmp ftrace_test_p6nop\n"
129 "nop\n"
130 "nop\n"
131 "nop\n"
132 "ftrace_test_p6nop:"
133 P6_NOP5
134 "jmp 1f\n"
135 "ftrace_test_nop5:"
136 ".byte 0x66,0x66,0x66,0x66,0x90\n"
137 "1:"
138 ".section .fixup, \"ax\"\n"
139 "2: movl $1, %0\n"
140 " jmp ftrace_test_nop5\n"
141 "3: movl $2, %0\n"
142 " jmp 1b\n"
143 ".previous\n"
144 _ASM_EXTABLE(ftrace_test_p6nop, 2b)
145 _ASM_EXTABLE(ftrace_test_nop5, 3b)
146 : "=r"(faulted) : "0" (faulted));
147
148 switch (faulted) {
149 case 0:
150 pr_info("ftrace: converting mcount calls to 0f 1f 44 00 00\n");
151 memcpy(ftrace_nop, ftrace_test_p6nop, MCOUNT_INSN_SIZE);
152 break;
153 case 1:
154 pr_info("ftrace: converting mcount calls to 66 66 66 66 90\n");
155 memcpy(ftrace_nop, ftrace_test_nop5, MCOUNT_INSN_SIZE);
156 break;
157 case 2:
158 pr_info("ftrace: converting mcount calls to jmp . + 5\n");
159 memcpy(ftrace_nop, ftrace_test_jmp, MCOUNT_INSN_SIZE);
160 break;
161 }
162
163
164 *(unsigned long *)data = 0;
165
166 return 0;
167}
168