blob: 24c023a5cae8d5311a06545d0b69804bc918a3cd [file] [log] [blame]
Steven Rostedt4e491d12008-05-14 23:49:44 -04001/*
2 * Code for replacing ftrace calls with jumps.
3 *
4 * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com>
5 *
6 * Thanks goes out to P.A. Semi, Inc for supplying me with a PPC64 box.
7 *
8 */
9
10#include <linux/spinlock.h>
11#include <linux/hardirq.h>
12#include <linux/ftrace.h>
13#include <linux/percpu.h>
14#include <linux/init.h>
15#include <linux/list.h>
16
17#include <asm/cacheflush.h>
Abhishek Sagar395a59d2008-06-21 23:47:27 +053018#include <asm/ftrace.h>
Steven Rostedt4e491d12008-05-14 23:49:44 -040019
Steven Rostedt4e491d12008-05-14 23:49:44 -040020
21static unsigned int ftrace_nop = 0x60000000;
22
23#ifdef CONFIG_PPC32
24# define GET_ADDR(addr) addr
25#else
26/* PowerPC64's functions are data that points to the functions */
27# define GET_ADDR(addr) *(unsigned long *)addr
28#endif
29
Abhishek Sagar395a59d2008-06-21 23:47:27 +053030
Steven Rostedt15adc042008-10-23 09:33:08 -040031static unsigned int ftrace_calc_offset(long ip, long addr)
Steven Rostedt4e491d12008-05-14 23:49:44 -040032{
Abhishek Sagar395a59d2008-06-21 23:47:27 +053033 return (int)(addr - ip);
Steven Rostedt4e491d12008-05-14 23:49:44 -040034}
35
Steven Rostedt8fd6e5a2008-11-14 16:21:19 -080036static unsigned char *ftrace_nop_replace(void)
Steven Rostedt4e491d12008-05-14 23:49:44 -040037{
38 return (char *)&ftrace_nop;
39}
40
Steven Rostedt8fd6e5a2008-11-14 16:21:19 -080041static unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
Steven Rostedt4e491d12008-05-14 23:49:44 -040042{
43 static unsigned int op;
44
Steven Rostedtccbfac22008-05-22 14:31:07 -040045 /*
46 * It would be nice to just use create_function_call, but that will
47 * update the code itself. Here we need to just return the
48 * instruction that is going to be modified, without modifying the
49 * code.
50 */
Steven Rostedt4e491d12008-05-14 23:49:44 -040051 addr = GET_ADDR(addr);
52
53 /* Set to "bl addr" */
Steven Rostedtccbfac22008-05-22 14:31:07 -040054 op = 0x48000001 | (ftrace_calc_offset(ip, addr) & 0x03fffffc);
Steven Rostedt4e491d12008-05-14 23:49:44 -040055
56 /*
57 * No locking needed, this must be called via kstop_machine
58 * which in essence is like running on a uniprocessor machine.
59 */
60 return (unsigned char *)&op;
61}
62
63#ifdef CONFIG_PPC64
64# define _ASM_ALIGN " .align 3 "
65# define _ASM_PTR " .llong "
66#else
67# define _ASM_ALIGN " .align 2 "
68# define _ASM_PTR " .long "
69#endif
70
Steven Rostedt8fd6e5a2008-11-14 16:21:19 -080071static int
Steven Rostedt4e491d12008-05-14 23:49:44 -040072ftrace_modify_code(unsigned long ip, unsigned char *old_code,
73 unsigned char *new_code)
74{
75 unsigned replaced;
76 unsigned old = *(unsigned *)old_code;
77 unsigned new = *(unsigned *)new_code;
78 int faulted = 0;
79
Steven Rostedt4e491d12008-05-14 23:49:44 -040080 /*
81 * Note: Due to modules and __init, code can
82 * disappear and change, we need to protect against faulting
83 * as well as code changing.
84 *
85 * No real locking needed, this code is run through
86 * kstop_machine.
87 */
88 asm volatile (
89 "1: lwz %1, 0(%2)\n"
90 " cmpw %1, %5\n"
91 " bne 2f\n"
92 " stwu %3, 0(%2)\n"
93 "2:\n"
94 ".section .fixup, \"ax\"\n"
95 "3: li %0, 1\n"
96 " b 2b\n"
97 ".previous\n"
98 ".section __ex_table,\"a\"\n"
99 _ASM_ALIGN "\n"
100 _ASM_PTR "1b, 3b\n"
101 ".previous"
102 : "=r"(faulted), "=r"(replaced)
103 : "r"(ip), "r"(new),
104 "0"(faulted), "r"(old)
105 : "memory");
106
107 if (replaced != old && replaced != new)
108 faulted = 2;
109
110 if (!faulted)
111 flush_icache_range(ip, ip + 8);
112
113 return faulted;
114}
115
Steven Rostedt8fd6e5a2008-11-14 16:21:19 -0800116static int test_24bit_addr(unsigned long ip, unsigned long addr)
117{
118 long diff;
119
120 /*
121 * Can we get to addr from ip in 24 bits?
122 * (26 really, since we mulitply by 4 for 4 byte alignment)
123 */
124 diff = addr - ip;
125
126 /*
127 * Return true if diff is less than 1 << 25
128 * and greater than -1 << 26.
129 */
130 return (diff < (1 << 25)) && (diff > (-1 << 26));
131}
132
133int ftrace_make_nop(struct module *mod,
134 struct dyn_ftrace *rec, unsigned long addr)
135{
136 unsigned char *old, *new;
137
138 /*
139 * If the calling address is more that 24 bits away,
140 * then we had to use a trampoline to make the call.
141 * Otherwise just update the call site.
142 */
143 if (test_24bit_addr(rec->ip, addr)) {
144 /* within range */
145 old = ftrace_call_replace(rec->ip, addr);
146 new = ftrace_nop_replace();
147 return ftrace_modify_code(rec->ip, old, new);
148 }
149
150 return 0;
151}
152
153int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
154{
155 unsigned char *old, *new;
156
157 /*
158 * If the calling address is more that 24 bits away,
159 * then we had to use a trampoline to make the call.
160 * Otherwise just update the call site.
161 */
162 if (test_24bit_addr(rec->ip, addr)) {
163 /* within range */
164 old = ftrace_nop_replace();
165 new = ftrace_call_replace(rec->ip, addr);
166 return ftrace_modify_code(rec->ip, old, new);
167 }
168
169 return 0;
170}
171
Steven Rostedt15adc042008-10-23 09:33:08 -0400172int ftrace_update_ftrace_func(ftrace_func_t func)
Steven Rostedt4e491d12008-05-14 23:49:44 -0400173{
174 unsigned long ip = (unsigned long)(&ftrace_call);
Abhishek Sagar395a59d2008-06-21 23:47:27 +0530175 unsigned char old[MCOUNT_INSN_SIZE], *new;
Steven Rostedt4e491d12008-05-14 23:49:44 -0400176 int ret;
177
Abhishek Sagar395a59d2008-06-21 23:47:27 +0530178 memcpy(old, &ftrace_call, MCOUNT_INSN_SIZE);
Steven Rostedt4e491d12008-05-14 23:49:44 -0400179 new = ftrace_call_replace(ip, (unsigned long)func);
180 ret = ftrace_modify_code(ip, old, new);
181
182 return ret;
183}
184
Steven Rostedt4e491d12008-05-14 23:49:44 -0400185int __init ftrace_dyn_arch_init(void *data)
186{
Steven Rostedt8fd6e5a2008-11-14 16:21:19 -0800187 /* caller expects data to be zero */
188 unsigned long *p = data;
Steven Rostedt4e491d12008-05-14 23:49:44 -0400189
Steven Rostedt8fd6e5a2008-11-14 16:21:19 -0800190 *p = 0;
Steven Rostedt4e491d12008-05-14 23:49:44 -0400191
192 return 0;
193}
194