]> pilppa.org Git - linux-2.6-omap-h63xx.git/blob - arch/powerpc/kernel/ftrace.c
ftrace: powerpc clean ups
[linux-2.6-omap-h63xx.git] / arch / powerpc / kernel / ftrace.c
1 /*
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
21 static 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
30 notrace 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
40 static unsigned int notrace ftrace_calc_offset(long ip, long addr)
41 {
42         return (int)((addr + CALL_BACK) - ip);
43 }
44
45 notrace unsigned char *ftrace_nop_replace(void)
46 {
47         return (char *)&ftrace_nop;
48 }
49
50 notrace unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
51 {
52         static unsigned int op;
53
54         /*
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          */
60         addr = GET_ADDR(addr);
61
62         /* Set to "bl addr" */
63         op = 0x48000001 | (ftrace_calc_offset(ip, addr) & 0x03fffffc);
64
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
80 notrace int
81 ftrace_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
128 notrace 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
143 notrace 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
163 int __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