diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/ppc64/kernel/kprobes.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/ppc64/kernel/kprobes.c')
-rw-r--r-- | arch/ppc64/kernel/kprobes.c | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/arch/ppc64/kernel/kprobes.c b/arch/ppc64/kernel/kprobes.c new file mode 100644 index 000000000000..103daaf73573 --- /dev/null +++ b/arch/ppc64/kernel/kprobes.c | |||
@@ -0,0 +1,290 @@ | |||
1 | /* | ||
2 | * Kernel Probes (KProbes) | ||
3 | * arch/ppc64/kernel/kprobes.c | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
18 | * | ||
19 | * Copyright (C) IBM Corporation, 2002, 2004 | ||
20 | * | ||
21 | * 2002-Oct Created by Vamsi Krishna S <vamsi_krishna@in.ibm.com> Kernel | ||
22 | * Probes initial implementation ( includes contributions from | ||
23 | * Rusty Russell). | ||
24 | * 2004-July Suparna Bhattacharya <suparna@in.ibm.com> added jumper probes | ||
25 | * interface to access function arguments. | ||
26 | * 2004-Nov Ananth N Mavinakayanahalli <ananth@in.ibm.com> kprobes port | ||
27 | * for PPC64 | ||
28 | */ | ||
29 | |||
30 | #include <linux/config.h> | ||
31 | #include <linux/kprobes.h> | ||
32 | #include <linux/ptrace.h> | ||
33 | #include <linux/spinlock.h> | ||
34 | #include <linux/preempt.h> | ||
35 | #include <asm/kdebug.h> | ||
36 | #include <asm/sstep.h> | ||
37 | |||
38 | /* kprobe_status settings */ | ||
39 | #define KPROBE_HIT_ACTIVE 0x00000001 | ||
40 | #define KPROBE_HIT_SS 0x00000002 | ||
41 | |||
42 | static struct kprobe *current_kprobe; | ||
43 | static unsigned long kprobe_status, kprobe_saved_msr; | ||
44 | static struct pt_regs jprobe_saved_regs; | ||
45 | |||
46 | int arch_prepare_kprobe(struct kprobe *p) | ||
47 | { | ||
48 | kprobe_opcode_t insn = *p->addr; | ||
49 | |||
50 | if (IS_MTMSRD(insn) || IS_RFID(insn)) | ||
51 | /* cannot put bp on RFID/MTMSRD */ | ||
52 | return 1; | ||
53 | return 0; | ||
54 | } | ||
55 | |||
56 | void arch_copy_kprobe(struct kprobe *p) | ||
57 | { | ||
58 | memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t)); | ||
59 | } | ||
60 | |||
61 | void arch_remove_kprobe(struct kprobe *p) | ||
62 | { | ||
63 | } | ||
64 | |||
65 | static inline void disarm_kprobe(struct kprobe *p, struct pt_regs *regs) | ||
66 | { | ||
67 | *p->addr = p->opcode; | ||
68 | regs->nip = (unsigned long)p->addr; | ||
69 | } | ||
70 | |||
71 | static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) | ||
72 | { | ||
73 | regs->msr |= MSR_SE; | ||
74 | /*single step inline if it a breakpoint instruction*/ | ||
75 | if (p->opcode == BREAKPOINT_INSTRUCTION) | ||
76 | regs->nip = (unsigned long)p->addr; | ||
77 | else | ||
78 | regs->nip = (unsigned long)&p->ainsn.insn; | ||
79 | } | ||
80 | |||
81 | static inline int kprobe_handler(struct pt_regs *regs) | ||
82 | { | ||
83 | struct kprobe *p; | ||
84 | int ret = 0; | ||
85 | unsigned int *addr = (unsigned int *)regs->nip; | ||
86 | |||
87 | /* Check we're not actually recursing */ | ||
88 | if (kprobe_running()) { | ||
89 | /* We *are* holding lock here, so this is safe. | ||
90 | Disarm the probe we just hit, and ignore it. */ | ||
91 | p = get_kprobe(addr); | ||
92 | if (p) { | ||
93 | if (kprobe_status == KPROBE_HIT_SS) { | ||
94 | regs->msr &= ~MSR_SE; | ||
95 | regs->msr |= kprobe_saved_msr; | ||
96 | unlock_kprobes(); | ||
97 | goto no_kprobe; | ||
98 | } | ||
99 | disarm_kprobe(p, regs); | ||
100 | ret = 1; | ||
101 | } else { | ||
102 | p = current_kprobe; | ||
103 | if (p->break_handler && p->break_handler(p, regs)) { | ||
104 | goto ss_probe; | ||
105 | } | ||
106 | } | ||
107 | /* If it's not ours, can't be delete race, (we hold lock). */ | ||
108 | goto no_kprobe; | ||
109 | } | ||
110 | |||
111 | lock_kprobes(); | ||
112 | p = get_kprobe(addr); | ||
113 | if (!p) { | ||
114 | unlock_kprobes(); | ||
115 | if (*addr != BREAKPOINT_INSTRUCTION) { | ||
116 | /* | ||
117 | * PowerPC has multiple variants of the "trap" | ||
118 | * instruction. If the current instruction is a | ||
119 | * trap variant, it could belong to someone else | ||
120 | */ | ||
121 | kprobe_opcode_t cur_insn = *addr; | ||
122 | if (IS_TW(cur_insn) || IS_TD(cur_insn) || | ||
123 | IS_TWI(cur_insn) || IS_TDI(cur_insn)) | ||
124 | goto no_kprobe; | ||
125 | /* | ||
126 | * The breakpoint instruction was removed right | ||
127 | * after we hit it. Another cpu has removed | ||
128 | * either a probepoint or a debugger breakpoint | ||
129 | * at this address. In either case, no further | ||
130 | * handling of this interrupt is appropriate. | ||
131 | */ | ||
132 | ret = 1; | ||
133 | } | ||
134 | /* Not one of ours: let kernel handle it */ | ||
135 | goto no_kprobe; | ||
136 | } | ||
137 | |||
138 | kprobe_status = KPROBE_HIT_ACTIVE; | ||
139 | current_kprobe = p; | ||
140 | kprobe_saved_msr = regs->msr; | ||
141 | if (p->pre_handler && p->pre_handler(p, regs)) | ||
142 | /* handler has already set things up, so skip ss setup */ | ||
143 | return 1; | ||
144 | |||
145 | ss_probe: | ||
146 | prepare_singlestep(p, regs); | ||
147 | kprobe_status = KPROBE_HIT_SS; | ||
148 | /* | ||
149 | * This preempt_disable() matches the preempt_enable_no_resched() | ||
150 | * in post_kprobe_handler(). | ||
151 | */ | ||
152 | preempt_disable(); | ||
153 | return 1; | ||
154 | |||
155 | no_kprobe: | ||
156 | return ret; | ||
157 | } | ||
158 | |||
159 | /* | ||
160 | * Called after single-stepping. p->addr is the address of the | ||
161 | * instruction whose first byte has been replaced by the "breakpoint" | ||
162 | * instruction. To avoid the SMP problems that can occur when we | ||
163 | * temporarily put back the original opcode to single-step, we | ||
164 | * single-stepped a copy of the instruction. The address of this | ||
165 | * copy is p->ainsn.insn. | ||
166 | */ | ||
167 | static void resume_execution(struct kprobe *p, struct pt_regs *regs) | ||
168 | { | ||
169 | int ret; | ||
170 | |||
171 | regs->nip = (unsigned long)p->addr; | ||
172 | ret = emulate_step(regs, p->ainsn.insn[0]); | ||
173 | if (ret == 0) | ||
174 | regs->nip = (unsigned long)p->addr + 4; | ||
175 | |||
176 | regs->msr &= ~MSR_SE; | ||
177 | } | ||
178 | |||
179 | static inline int post_kprobe_handler(struct pt_regs *regs) | ||
180 | { | ||
181 | if (!kprobe_running()) | ||
182 | return 0; | ||
183 | |||
184 | if (current_kprobe->post_handler) | ||
185 | current_kprobe->post_handler(current_kprobe, regs, 0); | ||
186 | |||
187 | resume_execution(current_kprobe, regs); | ||
188 | regs->msr |= kprobe_saved_msr; | ||
189 | |||
190 | unlock_kprobes(); | ||
191 | preempt_enable_no_resched(); | ||
192 | |||
193 | /* | ||
194 | * if somebody else is singlestepping across a probe point, msr | ||
195 | * will have SE set, in which case, continue the remaining processing | ||
196 | * of do_debug, as if this is not a probe hit. | ||
197 | */ | ||
198 | if (regs->msr & MSR_SE) | ||
199 | return 0; | ||
200 | |||
201 | return 1; | ||
202 | } | ||
203 | |||
204 | /* Interrupts disabled, kprobe_lock held. */ | ||
205 | static inline int kprobe_fault_handler(struct pt_regs *regs, int trapnr) | ||
206 | { | ||
207 | if (current_kprobe->fault_handler | ||
208 | && current_kprobe->fault_handler(current_kprobe, regs, trapnr)) | ||
209 | return 1; | ||
210 | |||
211 | if (kprobe_status & KPROBE_HIT_SS) { | ||
212 | resume_execution(current_kprobe, regs); | ||
213 | regs->msr |= kprobe_saved_msr; | ||
214 | |||
215 | unlock_kprobes(); | ||
216 | preempt_enable_no_resched(); | ||
217 | } | ||
218 | return 0; | ||
219 | } | ||
220 | |||
221 | /* | ||
222 | * Wrapper routine to for handling exceptions. | ||
223 | */ | ||
224 | int kprobe_exceptions_notify(struct notifier_block *self, unsigned long val, | ||
225 | void *data) | ||
226 | { | ||
227 | struct die_args *args = (struct die_args *)data; | ||
228 | int ret = NOTIFY_DONE; | ||
229 | |||
230 | /* | ||
231 | * Interrupts are not disabled here. We need to disable | ||
232 | * preemption, because kprobe_running() uses smp_processor_id(). | ||
233 | */ | ||
234 | preempt_disable(); | ||
235 | switch (val) { | ||
236 | case DIE_IABR_MATCH: | ||
237 | case DIE_DABR_MATCH: | ||
238 | case DIE_BPT: | ||
239 | if (kprobe_handler(args->regs)) | ||
240 | ret = NOTIFY_STOP; | ||
241 | break; | ||
242 | case DIE_SSTEP: | ||
243 | if (post_kprobe_handler(args->regs)) | ||
244 | ret = NOTIFY_STOP; | ||
245 | break; | ||
246 | case DIE_GPF: | ||
247 | case DIE_PAGE_FAULT: | ||
248 | if (kprobe_running() && | ||
249 | kprobe_fault_handler(args->regs, args->trapnr)) | ||
250 | ret = NOTIFY_STOP; | ||
251 | break; | ||
252 | default: | ||
253 | break; | ||
254 | } | ||
255 | preempt_enable(); | ||
256 | return ret; | ||
257 | } | ||
258 | |||
259 | int setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) | ||
260 | { | ||
261 | struct jprobe *jp = container_of(p, struct jprobe, kp); | ||
262 | |||
263 | memcpy(&jprobe_saved_regs, regs, sizeof(struct pt_regs)); | ||
264 | |||
265 | /* setup return addr to the jprobe handler routine */ | ||
266 | regs->nip = (unsigned long)(((func_descr_t *)jp->entry)->entry); | ||
267 | regs->gpr[2] = (unsigned long)(((func_descr_t *)jp->entry)->toc); | ||
268 | |||
269 | return 1; | ||
270 | } | ||
271 | |||
272 | void jprobe_return(void) | ||
273 | { | ||
274 | asm volatile("trap" ::: "memory"); | ||
275 | } | ||
276 | |||
277 | void jprobe_return_end(void) | ||
278 | { | ||
279 | }; | ||
280 | |||
281 | int longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) | ||
282 | { | ||
283 | /* | ||
284 | * FIXME - we should ideally be validating that we got here 'cos | ||
285 | * of the "trap" in jprobe_return() above, before restoring the | ||
286 | * saved regs... | ||
287 | */ | ||
288 | memcpy(regs, &jprobe_saved_regs, sizeof(struct pt_regs)); | ||
289 | return 1; | ||
290 | } | ||