blob: 69ed412234688464cbc49c0d71aa8d319285f431 [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>
18
19#define CALL_BACK 4
20
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
30notrace int ftrace_ip_converted(unsigned long ip)
31{
32 unsigned int save;
33
34 ip -= CALL_BACK;
35 save = *(unsigned int *)ip;
36
37 return save == ftrace_nop;
38}
39
40static unsigned int notrace ftrace_calc_offset(long ip, long addr)
41{
42 return (int)((addr + CALL_BACK) - ip);
43}
44
45notrace unsigned char *ftrace_nop_replace(void)
46{
47 return (char *)&ftrace_nop;
48}
49
50notrace unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
51{
52 static unsigned int op;
53
Steven Rostedtccbfac22008-05-22 14:31:07 -040054 /*
55 * It would be nice to just use create_function_call, but that will
56 * update the code itself. Here we need to just return the
57 * instruction that is going to be modified, without modifying the
58 * code.
59 */
Steven Rostedt4e491d12008-05-14 23:49:44 -040060 addr = GET_ADDR(addr);
61
62 /* Set to "bl addr" */
Steven Rostedtccbfac22008-05-22 14:31:07 -040063 op = 0x48000001 | (ftrace_calc_offset(ip, addr) & 0x03fffffc);
Steven Rostedt4e491d12008-05-14 23:49:44 -040064
65 /*
66 * No locking needed, this must be called via kstop_machine
67 * which in essence is like running on a uniprocessor machine.
68 */
69 return (unsigned char *)&op;
70}
71
72#ifdef CONFIG_PPC64
73# define _ASM_ALIGN " .align 3 "
74# define _ASM_PTR " .llong "
75#else
76# define _ASM_ALIGN " .align 2 "
77# define _ASM_PTR " .long "
78#endif
79
80notrace int
81ftrace_modify_code(unsigned long ip, unsigned char *old_code,
82 unsigned char *new_code)
83{
84 unsigned replaced;
85 unsigned old = *(unsigned *)old_code;
86 unsigned new = *(unsigned *)new_code;
87 int faulted = 0;
88
89 /* move the IP back to the start of the call */
90 ip -= CALL_BACK;
91
92 /*
93 * Note: Due to modules and __init, code can
94 * disappear and change, we need to protect against faulting
95 * as well as code changing.
96 *
97 * No real locking needed, this code is run through
98 * kstop_machine.
99 */
100 asm volatile (
101 "1: lwz %1, 0(%2)\n"
102 " cmpw %1, %5\n"
103 " bne 2f\n"
104 " stwu %3, 0(%2)\n"
105 "2:\n"
106 ".section .fixup, \"ax\"\n"
107 "3: li %0, 1\n"
108 " b 2b\n"
109 ".previous\n"
110 ".section __ex_table,\"a\"\n"
111 _ASM_ALIGN "\n"
112 _ASM_PTR "1b, 3b\n"
113 ".previous"
114 : "=r"(faulted), "=r"(replaced)
115 : "r"(ip), "r"(new),
116 "0"(faulted), "r"(old)
117 : "memory");
118
119 if (replaced != old && replaced != new)
120 faulted = 2;
121
122 if (!faulted)
123 flush_icache_range(ip, ip + 8);
124
125 return faulted;
126}
127
128notrace int ftrace_update_ftrace_func(ftrace_func_t func)
129{
130 unsigned long ip = (unsigned long)(&ftrace_call);
131 unsigned char old[4], *new;
132 int ret;
133
134 ip += CALL_BACK;
135
136 memcpy(old, &ftrace_call, 4);
137 new = ftrace_call_replace(ip, (unsigned long)func);
138 ret = ftrace_modify_code(ip, old, new);
139
140 return ret;
141}
142
143notrace int ftrace_mcount_set(unsigned long *data)
144{
145 unsigned long ip = (long)(&mcount_call);
146 unsigned long *addr = data;
147 unsigned char old[4], *new;
148
149 /* ip is at the location, but modify code will subtact this */
150 ip += CALL_BACK;
151
152 /*
153 * Replace the mcount stub with a pointer to the
154 * ip recorder function.
155 */
156 memcpy(old, &mcount_call, 4);
157 new = ftrace_call_replace(ip, *addr);
158 *addr = ftrace_modify_code(ip, old, new);
159
160 return 0;
161}
162
163int __init ftrace_dyn_arch_init(void *data)
164{
165 /* This is running in kstop_machine */
166
167 ftrace_mcount_set(data);
168
169 return 0;
170}
171