[S390] ftrace: add dynamic ftrace support
[linux-2.6.git] / arch / s390 / kernel / ftrace.c
1 /*
2  * Dynamic function tracer architecture backend.
3  *
4  * Copyright IBM Corp. 2009
5  *
6  *   Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>,
7  *
8  */
9
10 #include <linux/uaccess.h>
11 #include <linux/ftrace.h>
12 #include <linux/kernel.h>
13 #include <linux/types.h>
14 #include <asm/lowcore.h>
15
16 void ftrace_disable_code(void);
17 void ftrace_call_code(void);
18 void ftrace_nop_code(void);
19
20 #define FTRACE_INSN_SIZE 4
21
22 #ifdef CONFIG_64BIT
23
24 asm(
25         "       .align  4\n"
26         "ftrace_disable_code:\n"
27         "       j       0f\n"
28         "       .word   0x0024\n"
29         "       lg      %r1,"__stringify(__LC_FTRACE_FUNC)"\n"
30         "       basr    %r14,%r1\n"
31         "       lg      %r14,8(15)\n"
32         "       lgr     %r0,%r0\n"
33         "0:\n");
34
35 asm(
36         "       .align  4\n"
37         "ftrace_nop_code:\n"
38         "       j       .+"__stringify(MCOUNT_INSN_SIZE)"\n");
39
40 asm(
41         "       .align  4\n"
42         "ftrace_call_code:\n"
43         "       stg     %r14,8(%r15)\n");
44
45 #else /* CONFIG_64BIT */
46
47 asm(
48         "       .align  4\n"
49         "ftrace_disable_code:\n"
50         "       j       0f\n"
51         "       l       %r1,"__stringify(__LC_FTRACE_FUNC)"\n"
52         "       basr    %r14,%r1\n"
53         "       l       %r14,4(%r15)\n"
54         "       j       0f\n"
55         "       bcr     0,%r7\n"
56         "       bcr     0,%r7\n"
57         "       bcr     0,%r7\n"
58         "       bcr     0,%r7\n"
59         "       bcr     0,%r7\n"
60         "       bcr     0,%r7\n"
61         "0:\n");
62
63 asm(
64         "       .align  4\n"
65         "ftrace_nop_code:\n"
66         "       j       .+"__stringify(MCOUNT_INSN_SIZE)"\n");
67
68 asm(
69         "       .align  4\n"
70         "ftrace_call_code:\n"
71         "       st      %r14,4(%r15)\n");
72
73 #endif /* CONFIG_64BIT */
74
75 static int ftrace_modify_code(unsigned long ip,
76                               void *old_code, int old_size,
77                               void *new_code, int new_size)
78 {
79         unsigned char replaced[MCOUNT_INSN_SIZE];
80
81         /*
82          * Note: Due to modules code can disappear and change.
83          *  We need to protect against faulting as well as code
84          *  changing. We do this by using the probe_kernel_*
85          *  functions.
86          *  This however is just a simple sanity check.
87          */
88         if (probe_kernel_read(replaced, (void *)ip, old_size))
89                 return -EFAULT;
90         if (memcmp(replaced, old_code, old_size) != 0)
91                 return -EINVAL;
92         if (probe_kernel_write((void *)ip, new_code, new_size))
93                 return -EPERM;
94         return 0;
95 }
96
97 static int ftrace_make_initial_nop(struct module *mod, struct dyn_ftrace *rec,
98                                    unsigned long addr)
99 {
100         return ftrace_modify_code(rec->ip,
101                                   ftrace_call_code, FTRACE_INSN_SIZE,
102                                   ftrace_disable_code, MCOUNT_INSN_SIZE);
103 }
104
105 int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
106                     unsigned long addr)
107 {
108         if (addr == MCOUNT_ADDR)
109                 return ftrace_make_initial_nop(mod, rec, addr);
110         return ftrace_modify_code(rec->ip,
111                                   ftrace_call_code, FTRACE_INSN_SIZE,
112                                   ftrace_nop_code, FTRACE_INSN_SIZE);
113 }
114
115 int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
116 {
117         return ftrace_modify_code(rec->ip,
118                                   ftrace_nop_code, FTRACE_INSN_SIZE,
119                                   ftrace_call_code, FTRACE_INSN_SIZE);
120 }
121
122 int ftrace_update_ftrace_func(ftrace_func_t func)
123 {
124         ftrace_dyn_func = (unsigned long)func;
125         return 0;
126 }
127
128 int __init ftrace_dyn_arch_init(void *data)
129 {
130         *(unsigned long *)data = 0;
131         return 0;
132 }