diff options
author | Heiko Carstens <heiko.carstens@de.ibm.com> | 2009-06-12 04:26:44 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2009-06-12 04:27:38 -0400 |
commit | dfd9f7abc0fb67b5781f340d982384cea53b2884 (patch) | |
tree | 850baae88167699f45ba1dea1765b9156373db72 /arch/s390/kernel/ftrace.c | |
parent | a2b53673fae14601bb520acf6a6f154463994565 (diff) |
[S390] ftrace: add dynamic ftrace support
Dynamic ftrace support for s390.
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch/s390/kernel/ftrace.c')
-rw-r--r-- | arch/s390/kernel/ftrace.c | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/arch/s390/kernel/ftrace.c b/arch/s390/kernel/ftrace.c new file mode 100644 index 000000000000..0b81a784e039 --- /dev/null +++ b/arch/s390/kernel/ftrace.c | |||
@@ -0,0 +1,132 @@ | |||
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 | } | ||