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/arm/kernel/ptrace.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/arm/kernel/ptrace.c')
-rw-r--r-- | arch/arm/kernel/ptrace.c | 861 |
1 files changed, 861 insertions, 0 deletions
diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c new file mode 100644 index 000000000000..efd7a341614b --- /dev/null +++ b/arch/arm/kernel/ptrace.c | |||
@@ -0,0 +1,861 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/kernel/ptrace.c | ||
3 | * | ||
4 | * By Ross Biro 1/23/92 | ||
5 | * edited by Linus Torvalds | ||
6 | * ARM modifications Copyright (C) 2000 Russell King | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | #include <linux/config.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/mm.h> | ||
16 | #include <linux/smp.h> | ||
17 | #include <linux/smp_lock.h> | ||
18 | #include <linux/ptrace.h> | ||
19 | #include <linux/user.h> | ||
20 | #include <linux/security.h> | ||
21 | #include <linux/init.h> | ||
22 | |||
23 | #include <asm/uaccess.h> | ||
24 | #include <asm/pgtable.h> | ||
25 | #include <asm/system.h> | ||
26 | #include <asm/traps.h> | ||
27 | |||
28 | #include "ptrace.h" | ||
29 | |||
30 | #define REG_PC 15 | ||
31 | #define REG_PSR 16 | ||
32 | /* | ||
33 | * does not yet catch signals sent when the child dies. | ||
34 | * in exit.c or in signal.c. | ||
35 | */ | ||
36 | |||
37 | #if 0 | ||
38 | /* | ||
39 | * Breakpoint SWI instruction: SWI &9F0001 | ||
40 | */ | ||
41 | #define BREAKINST_ARM 0xef9f0001 | ||
42 | #define BREAKINST_THUMB 0xdf00 /* fill this in later */ | ||
43 | #else | ||
44 | /* | ||
45 | * New breakpoints - use an undefined instruction. The ARM architecture | ||
46 | * reference manual guarantees that the following instruction space | ||
47 | * will produce an undefined instruction exception on all CPUs: | ||
48 | * | ||
49 | * ARM: xxxx 0111 1111 xxxx xxxx xxxx 1111 xxxx | ||
50 | * Thumb: 1101 1110 xxxx xxxx | ||
51 | */ | ||
52 | #define BREAKINST_ARM 0xe7f001f0 | ||
53 | #define BREAKINST_THUMB 0xde01 | ||
54 | #endif | ||
55 | |||
56 | /* | ||
57 | * Get the address of the live pt_regs for the specified task. | ||
58 | * These are saved onto the top kernel stack when the process | ||
59 | * is not running. | ||
60 | * | ||
61 | * Note: if a user thread is execve'd from kernel space, the | ||
62 | * kernel stack will not be empty on entry to the kernel, so | ||
63 | * ptracing these tasks will fail. | ||
64 | */ | ||
65 | static inline struct pt_regs * | ||
66 | get_user_regs(struct task_struct *task) | ||
67 | { | ||
68 | return (struct pt_regs *) | ||
69 | ((unsigned long)task->thread_info + THREAD_SIZE - | ||
70 | 8 - sizeof(struct pt_regs)); | ||
71 | } | ||
72 | |||
73 | /* | ||
74 | * this routine will get a word off of the processes privileged stack. | ||
75 | * the offset is how far from the base addr as stored in the THREAD. | ||
76 | * this routine assumes that all the privileged stacks are in our | ||
77 | * data space. | ||
78 | */ | ||
79 | static inline long get_user_reg(struct task_struct *task, int offset) | ||
80 | { | ||
81 | return get_user_regs(task)->uregs[offset]; | ||
82 | } | ||
83 | |||
84 | /* | ||
85 | * this routine will put a word on the processes privileged stack. | ||
86 | * the offset is how far from the base addr as stored in the THREAD. | ||
87 | * this routine assumes that all the privileged stacks are in our | ||
88 | * data space. | ||
89 | */ | ||
90 | static inline int | ||
91 | put_user_reg(struct task_struct *task, int offset, long data) | ||
92 | { | ||
93 | struct pt_regs newregs, *regs = get_user_regs(task); | ||
94 | int ret = -EINVAL; | ||
95 | |||
96 | newregs = *regs; | ||
97 | newregs.uregs[offset] = data; | ||
98 | |||
99 | if (valid_user_regs(&newregs)) { | ||
100 | regs->uregs[offset] = data; | ||
101 | ret = 0; | ||
102 | } | ||
103 | |||
104 | return ret; | ||
105 | } | ||
106 | |||
107 | static inline int | ||
108 | read_u32(struct task_struct *task, unsigned long addr, u32 *res) | ||
109 | { | ||
110 | int ret; | ||
111 | |||
112 | ret = access_process_vm(task, addr, res, sizeof(*res), 0); | ||
113 | |||
114 | return ret == sizeof(*res) ? 0 : -EIO; | ||
115 | } | ||
116 | |||
117 | static inline int | ||
118 | read_instr(struct task_struct *task, unsigned long addr, u32 *res) | ||
119 | { | ||
120 | int ret; | ||
121 | |||
122 | if (addr & 1) { | ||
123 | u16 val; | ||
124 | ret = access_process_vm(task, addr & ~1, &val, sizeof(val), 0); | ||
125 | ret = ret == sizeof(val) ? 0 : -EIO; | ||
126 | *res = val; | ||
127 | } else { | ||
128 | u32 val; | ||
129 | ret = access_process_vm(task, addr & ~3, &val, sizeof(val), 0); | ||
130 | ret = ret == sizeof(val) ? 0 : -EIO; | ||
131 | *res = val; | ||
132 | } | ||
133 | return ret; | ||
134 | } | ||
135 | |||
136 | /* | ||
137 | * Get value of register `rn' (in the instruction) | ||
138 | */ | ||
139 | static unsigned long | ||
140 | ptrace_getrn(struct task_struct *child, unsigned long insn) | ||
141 | { | ||
142 | unsigned int reg = (insn >> 16) & 15; | ||
143 | unsigned long val; | ||
144 | |||
145 | val = get_user_reg(child, reg); | ||
146 | if (reg == 15) | ||
147 | val = pc_pointer(val + 8); | ||
148 | |||
149 | return val; | ||
150 | } | ||
151 | |||
152 | /* | ||
153 | * Get value of operand 2 (in an ALU instruction) | ||
154 | */ | ||
155 | static unsigned long | ||
156 | ptrace_getaluop2(struct task_struct *child, unsigned long insn) | ||
157 | { | ||
158 | unsigned long val; | ||
159 | int shift; | ||
160 | int type; | ||
161 | |||
162 | if (insn & 1 << 25) { | ||
163 | val = insn & 255; | ||
164 | shift = (insn >> 8) & 15; | ||
165 | type = 3; | ||
166 | } else { | ||
167 | val = get_user_reg (child, insn & 15); | ||
168 | |||
169 | if (insn & (1 << 4)) | ||
170 | shift = (int)get_user_reg (child, (insn >> 8) & 15); | ||
171 | else | ||
172 | shift = (insn >> 7) & 31; | ||
173 | |||
174 | type = (insn >> 5) & 3; | ||
175 | } | ||
176 | |||
177 | switch (type) { | ||
178 | case 0: val <<= shift; break; | ||
179 | case 1: val >>= shift; break; | ||
180 | case 2: | ||
181 | val = (((signed long)val) >> shift); | ||
182 | break; | ||
183 | case 3: | ||
184 | val = (val >> shift) | (val << (32 - shift)); | ||
185 | break; | ||
186 | } | ||
187 | return val; | ||
188 | } | ||
189 | |||
190 | /* | ||
191 | * Get value of operand 2 (in a LDR instruction) | ||
192 | */ | ||
193 | static unsigned long | ||
194 | ptrace_getldrop2(struct task_struct *child, unsigned long insn) | ||
195 | { | ||
196 | unsigned long val; | ||
197 | int shift; | ||
198 | int type; | ||
199 | |||
200 | val = get_user_reg(child, insn & 15); | ||
201 | shift = (insn >> 7) & 31; | ||
202 | type = (insn >> 5) & 3; | ||
203 | |||
204 | switch (type) { | ||
205 | case 0: val <<= shift; break; | ||
206 | case 1: val >>= shift; break; | ||
207 | case 2: | ||
208 | val = (((signed long)val) >> shift); | ||
209 | break; | ||
210 | case 3: | ||
211 | val = (val >> shift) | (val << (32 - shift)); | ||
212 | break; | ||
213 | } | ||
214 | return val; | ||
215 | } | ||
216 | |||
217 | #define OP_MASK 0x01e00000 | ||
218 | #define OP_AND 0x00000000 | ||
219 | #define OP_EOR 0x00200000 | ||
220 | #define OP_SUB 0x00400000 | ||
221 | #define OP_RSB 0x00600000 | ||
222 | #define OP_ADD 0x00800000 | ||
223 | #define OP_ADC 0x00a00000 | ||
224 | #define OP_SBC 0x00c00000 | ||
225 | #define OP_RSC 0x00e00000 | ||
226 | #define OP_ORR 0x01800000 | ||
227 | #define OP_MOV 0x01a00000 | ||
228 | #define OP_BIC 0x01c00000 | ||
229 | #define OP_MVN 0x01e00000 | ||
230 | |||
231 | static unsigned long | ||
232 | get_branch_address(struct task_struct *child, unsigned long pc, unsigned long insn) | ||
233 | { | ||
234 | u32 alt = 0; | ||
235 | |||
236 | switch (insn & 0x0e000000) { | ||
237 | case 0x00000000: | ||
238 | case 0x02000000: { | ||
239 | /* | ||
240 | * data processing | ||
241 | */ | ||
242 | long aluop1, aluop2, ccbit; | ||
243 | |||
244 | if ((insn & 0xf000) != 0xf000) | ||
245 | break; | ||
246 | |||
247 | aluop1 = ptrace_getrn(child, insn); | ||
248 | aluop2 = ptrace_getaluop2(child, insn); | ||
249 | ccbit = get_user_reg(child, REG_PSR) & PSR_C_BIT ? 1 : 0; | ||
250 | |||
251 | switch (insn & OP_MASK) { | ||
252 | case OP_AND: alt = aluop1 & aluop2; break; | ||
253 | case OP_EOR: alt = aluop1 ^ aluop2; break; | ||
254 | case OP_SUB: alt = aluop1 - aluop2; break; | ||
255 | case OP_RSB: alt = aluop2 - aluop1; break; | ||
256 | case OP_ADD: alt = aluop1 + aluop2; break; | ||
257 | case OP_ADC: alt = aluop1 + aluop2 + ccbit; break; | ||
258 | case OP_SBC: alt = aluop1 - aluop2 + ccbit; break; | ||
259 | case OP_RSC: alt = aluop2 - aluop1 + ccbit; break; | ||
260 | case OP_ORR: alt = aluop1 | aluop2; break; | ||
261 | case OP_MOV: alt = aluop2; break; | ||
262 | case OP_BIC: alt = aluop1 & ~aluop2; break; | ||
263 | case OP_MVN: alt = ~aluop2; break; | ||
264 | } | ||
265 | break; | ||
266 | } | ||
267 | |||
268 | case 0x04000000: | ||
269 | case 0x06000000: | ||
270 | /* | ||
271 | * ldr | ||
272 | */ | ||
273 | if ((insn & 0x0010f000) == 0x0010f000) { | ||
274 | unsigned long base; | ||
275 | |||
276 | base = ptrace_getrn(child, insn); | ||
277 | if (insn & 1 << 24) { | ||
278 | long aluop2; | ||
279 | |||
280 | if (insn & 0x02000000) | ||
281 | aluop2 = ptrace_getldrop2(child, insn); | ||
282 | else | ||
283 | aluop2 = insn & 0xfff; | ||
284 | |||
285 | if (insn & 1 << 23) | ||
286 | base += aluop2; | ||
287 | else | ||
288 | base -= aluop2; | ||
289 | } | ||
290 | if (read_u32(child, base, &alt) == 0) | ||
291 | alt = pc_pointer(alt); | ||
292 | } | ||
293 | break; | ||
294 | |||
295 | case 0x08000000: | ||
296 | /* | ||
297 | * ldm | ||
298 | */ | ||
299 | if ((insn & 0x00108000) == 0x00108000) { | ||
300 | unsigned long base; | ||
301 | unsigned int nr_regs; | ||
302 | |||
303 | if (insn & (1 << 23)) { | ||
304 | nr_regs = hweight16(insn & 65535) << 2; | ||
305 | |||
306 | if (!(insn & (1 << 24))) | ||
307 | nr_regs -= 4; | ||
308 | } else { | ||
309 | if (insn & (1 << 24)) | ||
310 | nr_regs = -4; | ||
311 | else | ||
312 | nr_regs = 0; | ||
313 | } | ||
314 | |||
315 | base = ptrace_getrn(child, insn); | ||
316 | |||
317 | if (read_u32(child, base + nr_regs, &alt) == 0) | ||
318 | alt = pc_pointer(alt); | ||
319 | break; | ||
320 | } | ||
321 | break; | ||
322 | |||
323 | case 0x0a000000: { | ||
324 | /* | ||
325 | * bl or b | ||
326 | */ | ||
327 | signed long displ; | ||
328 | /* It's a branch/branch link: instead of trying to | ||
329 | * figure out whether the branch will be taken or not, | ||
330 | * we'll put a breakpoint at both locations. This is | ||
331 | * simpler, more reliable, and probably not a whole lot | ||
332 | * slower than the alternative approach of emulating the | ||
333 | * branch. | ||
334 | */ | ||
335 | displ = (insn & 0x00ffffff) << 8; | ||
336 | displ = (displ >> 6) + 8; | ||
337 | if (displ != 0 && displ != 4) | ||
338 | alt = pc + displ; | ||
339 | } | ||
340 | break; | ||
341 | } | ||
342 | |||
343 | return alt; | ||
344 | } | ||
345 | |||
346 | static int | ||
347 | swap_insn(struct task_struct *task, unsigned long addr, | ||
348 | void *old_insn, void *new_insn, int size) | ||
349 | { | ||
350 | int ret; | ||
351 | |||
352 | ret = access_process_vm(task, addr, old_insn, size, 0); | ||
353 | if (ret == size) | ||
354 | ret = access_process_vm(task, addr, new_insn, size, 1); | ||
355 | return ret; | ||
356 | } | ||
357 | |||
358 | static void | ||
359 | add_breakpoint(struct task_struct *task, struct debug_info *dbg, unsigned long addr) | ||
360 | { | ||
361 | int nr = dbg->nsaved; | ||
362 | |||
363 | if (nr < 2) { | ||
364 | u32 new_insn = BREAKINST_ARM; | ||
365 | int res; | ||
366 | |||
367 | res = swap_insn(task, addr, &dbg->bp[nr].insn, &new_insn, 4); | ||
368 | |||
369 | if (res == 4) { | ||
370 | dbg->bp[nr].address = addr; | ||
371 | dbg->nsaved += 1; | ||
372 | } | ||
373 | } else | ||
374 | printk(KERN_ERR "ptrace: too many breakpoints\n"); | ||
375 | } | ||
376 | |||
377 | /* | ||
378 | * Clear one breakpoint in the user program. We copy what the hardware | ||
379 | * does and use bit 0 of the address to indicate whether this is a Thumb | ||
380 | * breakpoint or an ARM breakpoint. | ||
381 | */ | ||
382 | static void clear_breakpoint(struct task_struct *task, struct debug_entry *bp) | ||
383 | { | ||
384 | unsigned long addr = bp->address; | ||
385 | union debug_insn old_insn; | ||
386 | int ret; | ||
387 | |||
388 | if (addr & 1) { | ||
389 | ret = swap_insn(task, addr & ~1, &old_insn.thumb, | ||
390 | &bp->insn.thumb, 2); | ||
391 | |||
392 | if (ret != 2 || old_insn.thumb != BREAKINST_THUMB) | ||
393 | printk(KERN_ERR "%s:%d: corrupted Thumb breakpoint at " | ||
394 | "0x%08lx (0x%04x)\n", task->comm, task->pid, | ||
395 | addr, old_insn.thumb); | ||
396 | } else { | ||
397 | ret = swap_insn(task, addr & ~3, &old_insn.arm, | ||
398 | &bp->insn.arm, 4); | ||
399 | |||
400 | if (ret != 4 || old_insn.arm != BREAKINST_ARM) | ||
401 | printk(KERN_ERR "%s:%d: corrupted ARM breakpoint at " | ||
402 | "0x%08lx (0x%08x)\n", task->comm, task->pid, | ||
403 | addr, old_insn.arm); | ||
404 | } | ||
405 | } | ||
406 | |||
407 | void ptrace_set_bpt(struct task_struct *child) | ||
408 | { | ||
409 | struct pt_regs *regs; | ||
410 | unsigned long pc; | ||
411 | u32 insn; | ||
412 | int res; | ||
413 | |||
414 | regs = get_user_regs(child); | ||
415 | pc = instruction_pointer(regs); | ||
416 | |||
417 | if (thumb_mode(regs)) { | ||
418 | printk(KERN_WARNING "ptrace: can't handle thumb mode\n"); | ||
419 | return; | ||
420 | } | ||
421 | |||
422 | res = read_instr(child, pc, &insn); | ||
423 | if (!res) { | ||
424 | struct debug_info *dbg = &child->thread.debug; | ||
425 | unsigned long alt; | ||
426 | |||
427 | dbg->nsaved = 0; | ||
428 | |||
429 | alt = get_branch_address(child, pc, insn); | ||
430 | if (alt) | ||
431 | add_breakpoint(child, dbg, alt); | ||
432 | |||
433 | /* | ||
434 | * Note that we ignore the result of setting the above | ||
435 | * breakpoint since it may fail. When it does, this is | ||
436 | * not so much an error, but a forewarning that we may | ||
437 | * be receiving a prefetch abort shortly. | ||
438 | * | ||
439 | * If we don't set this breakpoint here, then we can | ||
440 | * lose control of the thread during single stepping. | ||
441 | */ | ||
442 | if (!alt || predicate(insn) != PREDICATE_ALWAYS) | ||
443 | add_breakpoint(child, dbg, pc + 4); | ||
444 | } | ||
445 | } | ||
446 | |||
447 | /* | ||
448 | * Ensure no single-step breakpoint is pending. Returns non-zero | ||
449 | * value if child was being single-stepped. | ||
450 | */ | ||
451 | void ptrace_cancel_bpt(struct task_struct *child) | ||
452 | { | ||
453 | int i, nsaved = child->thread.debug.nsaved; | ||
454 | |||
455 | child->thread.debug.nsaved = 0; | ||
456 | |||
457 | if (nsaved > 2) { | ||
458 | printk("ptrace_cancel_bpt: bogus nsaved: %d!\n", nsaved); | ||
459 | nsaved = 2; | ||
460 | } | ||
461 | |||
462 | for (i = 0; i < nsaved; i++) | ||
463 | clear_breakpoint(child, &child->thread.debug.bp[i]); | ||
464 | } | ||
465 | |||
466 | /* | ||
467 | * Called by kernel/ptrace.c when detaching.. | ||
468 | * | ||
469 | * Make sure the single step bit is not set. | ||
470 | */ | ||
471 | void ptrace_disable(struct task_struct *child) | ||
472 | { | ||
473 | child->ptrace &= ~PT_SINGLESTEP; | ||
474 | ptrace_cancel_bpt(child); | ||
475 | } | ||
476 | |||
477 | /* | ||
478 | * Handle hitting a breakpoint. | ||
479 | */ | ||
480 | void ptrace_break(struct task_struct *tsk, struct pt_regs *regs) | ||
481 | { | ||
482 | siginfo_t info; | ||
483 | |||
484 | ptrace_cancel_bpt(tsk); | ||
485 | |||
486 | info.si_signo = SIGTRAP; | ||
487 | info.si_errno = 0; | ||
488 | info.si_code = TRAP_BRKPT; | ||
489 | info.si_addr = (void __user *)instruction_pointer(regs); | ||
490 | |||
491 | force_sig_info(SIGTRAP, &info, tsk); | ||
492 | } | ||
493 | |||
494 | static int break_trap(struct pt_regs *regs, unsigned int instr) | ||
495 | { | ||
496 | ptrace_break(current, regs); | ||
497 | return 0; | ||
498 | } | ||
499 | |||
500 | static struct undef_hook arm_break_hook = { | ||
501 | .instr_mask = 0x0fffffff, | ||
502 | .instr_val = 0x07f001f0, | ||
503 | .cpsr_mask = PSR_T_BIT, | ||
504 | .cpsr_val = 0, | ||
505 | .fn = break_trap, | ||
506 | }; | ||
507 | |||
508 | static struct undef_hook thumb_break_hook = { | ||
509 | .instr_mask = 0xffff, | ||
510 | .instr_val = 0xde01, | ||
511 | .cpsr_mask = PSR_T_BIT, | ||
512 | .cpsr_val = PSR_T_BIT, | ||
513 | .fn = break_trap, | ||
514 | }; | ||
515 | |||
516 | static int __init ptrace_break_init(void) | ||
517 | { | ||
518 | register_undef_hook(&arm_break_hook); | ||
519 | register_undef_hook(&thumb_break_hook); | ||
520 | return 0; | ||
521 | } | ||
522 | |||
523 | core_initcall(ptrace_break_init); | ||
524 | |||
525 | /* | ||
526 | * Read the word at offset "off" into the "struct user". We | ||
527 | * actually access the pt_regs stored on the kernel stack. | ||
528 | */ | ||
529 | static int ptrace_read_user(struct task_struct *tsk, unsigned long off, | ||
530 | unsigned long __user *ret) | ||
531 | { | ||
532 | unsigned long tmp; | ||
533 | |||
534 | if (off & 3 || off >= sizeof(struct user)) | ||
535 | return -EIO; | ||
536 | |||
537 | tmp = 0; | ||
538 | if (off < sizeof(struct pt_regs)) | ||
539 | tmp = get_user_reg(tsk, off >> 2); | ||
540 | |||
541 | return put_user(tmp, ret); | ||
542 | } | ||
543 | |||
544 | /* | ||
545 | * Write the word at offset "off" into "struct user". We | ||
546 | * actually access the pt_regs stored on the kernel stack. | ||
547 | */ | ||
548 | static int ptrace_write_user(struct task_struct *tsk, unsigned long off, | ||
549 | unsigned long val) | ||
550 | { | ||
551 | if (off & 3 || off >= sizeof(struct user)) | ||
552 | return -EIO; | ||
553 | |||
554 | if (off >= sizeof(struct pt_regs)) | ||
555 | return 0; | ||
556 | |||
557 | return put_user_reg(tsk, off >> 2, val); | ||
558 | } | ||
559 | |||
560 | /* | ||
561 | * Get all user integer registers. | ||
562 | */ | ||
563 | static int ptrace_getregs(struct task_struct *tsk, void __user *uregs) | ||
564 | { | ||
565 | struct pt_regs *regs = get_user_regs(tsk); | ||
566 | |||
567 | return copy_to_user(uregs, regs, sizeof(struct pt_regs)) ? -EFAULT : 0; | ||
568 | } | ||
569 | |||
570 | /* | ||
571 | * Set all user integer registers. | ||
572 | */ | ||
573 | static int ptrace_setregs(struct task_struct *tsk, void __user *uregs) | ||
574 | { | ||
575 | struct pt_regs newregs; | ||
576 | int ret; | ||
577 | |||
578 | ret = -EFAULT; | ||
579 | if (copy_from_user(&newregs, uregs, sizeof(struct pt_regs)) == 0) { | ||
580 | struct pt_regs *regs = get_user_regs(tsk); | ||
581 | |||
582 | ret = -EINVAL; | ||
583 | if (valid_user_regs(&newregs)) { | ||
584 | *regs = newregs; | ||
585 | ret = 0; | ||
586 | } | ||
587 | } | ||
588 | |||
589 | return ret; | ||
590 | } | ||
591 | |||
592 | /* | ||
593 | * Get the child FPU state. | ||
594 | */ | ||
595 | static int ptrace_getfpregs(struct task_struct *tsk, void __user *ufp) | ||
596 | { | ||
597 | return copy_to_user(ufp, &tsk->thread_info->fpstate, | ||
598 | sizeof(struct user_fp)) ? -EFAULT : 0; | ||
599 | } | ||
600 | |||
601 | /* | ||
602 | * Set the child FPU state. | ||
603 | */ | ||
604 | static int ptrace_setfpregs(struct task_struct *tsk, void __user *ufp) | ||
605 | { | ||
606 | struct thread_info *thread = tsk->thread_info; | ||
607 | thread->used_cp[1] = thread->used_cp[2] = 1; | ||
608 | return copy_from_user(&thread->fpstate, ufp, | ||
609 | sizeof(struct user_fp)) ? -EFAULT : 0; | ||
610 | } | ||
611 | |||
612 | #ifdef CONFIG_IWMMXT | ||
613 | |||
614 | /* | ||
615 | * Get the child iWMMXt state. | ||
616 | */ | ||
617 | static int ptrace_getwmmxregs(struct task_struct *tsk, void __user *ufp) | ||
618 | { | ||
619 | struct thread_info *thread = tsk->thread_info; | ||
620 | void *ptr = &thread->fpstate; | ||
621 | |||
622 | if (!test_ti_thread_flag(thread, TIF_USING_IWMMXT)) | ||
623 | return -ENODATA; | ||
624 | iwmmxt_task_disable(thread); /* force it to ram */ | ||
625 | /* The iWMMXt state is stored doubleword-aligned. */ | ||
626 | if (((long) ptr) & 4) | ||
627 | ptr += 4; | ||
628 | return copy_to_user(ufp, ptr, 0x98) ? -EFAULT : 0; | ||
629 | } | ||
630 | |||
631 | /* | ||
632 | * Set the child iWMMXt state. | ||
633 | */ | ||
634 | static int ptrace_setwmmxregs(struct task_struct *tsk, void __user *ufp) | ||
635 | { | ||
636 | struct thread_info *thread = tsk->thread_info; | ||
637 | void *ptr = &thread->fpstate; | ||
638 | |||
639 | if (!test_ti_thread_flag(thread, TIF_USING_IWMMXT)) | ||
640 | return -EACCES; | ||
641 | iwmmxt_task_release(thread); /* force a reload */ | ||
642 | /* The iWMMXt state is stored doubleword-aligned. */ | ||
643 | if (((long) ptr) & 4) | ||
644 | ptr += 4; | ||
645 | return copy_from_user(ptr, ufp, 0x98) ? -EFAULT : 0; | ||
646 | } | ||
647 | |||
648 | #endif | ||
649 | |||
650 | static int do_ptrace(int request, struct task_struct *child, long addr, long data) | ||
651 | { | ||
652 | unsigned long tmp; | ||
653 | int ret; | ||
654 | |||
655 | switch (request) { | ||
656 | /* | ||
657 | * read word at location "addr" in the child process. | ||
658 | */ | ||
659 | case PTRACE_PEEKTEXT: | ||
660 | case PTRACE_PEEKDATA: | ||
661 | ret = access_process_vm(child, addr, &tmp, | ||
662 | sizeof(unsigned long), 0); | ||
663 | if (ret == sizeof(unsigned long)) | ||
664 | ret = put_user(tmp, (unsigned long __user *) data); | ||
665 | else | ||
666 | ret = -EIO; | ||
667 | break; | ||
668 | |||
669 | case PTRACE_PEEKUSR: | ||
670 | ret = ptrace_read_user(child, addr, (unsigned long __user *)data); | ||
671 | break; | ||
672 | |||
673 | /* | ||
674 | * write the word at location addr. | ||
675 | */ | ||
676 | case PTRACE_POKETEXT: | ||
677 | case PTRACE_POKEDATA: | ||
678 | ret = access_process_vm(child, addr, &data, | ||
679 | sizeof(unsigned long), 1); | ||
680 | if (ret == sizeof(unsigned long)) | ||
681 | ret = 0; | ||
682 | else | ||
683 | ret = -EIO; | ||
684 | break; | ||
685 | |||
686 | case PTRACE_POKEUSR: | ||
687 | ret = ptrace_write_user(child, addr, data); | ||
688 | break; | ||
689 | |||
690 | /* | ||
691 | * continue/restart and stop at next (return from) syscall | ||
692 | */ | ||
693 | case PTRACE_SYSCALL: | ||
694 | case PTRACE_CONT: | ||
695 | ret = -EIO; | ||
696 | if ((unsigned long) data > _NSIG) | ||
697 | break; | ||
698 | if (request == PTRACE_SYSCALL) | ||
699 | set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | ||
700 | else | ||
701 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | ||
702 | child->exit_code = data; | ||
703 | /* make sure single-step breakpoint is gone. */ | ||
704 | child->ptrace &= ~PT_SINGLESTEP; | ||
705 | ptrace_cancel_bpt(child); | ||
706 | wake_up_process(child); | ||
707 | ret = 0; | ||
708 | break; | ||
709 | |||
710 | /* | ||
711 | * make the child exit. Best I can do is send it a sigkill. | ||
712 | * perhaps it should be put in the status that it wants to | ||
713 | * exit. | ||
714 | */ | ||
715 | case PTRACE_KILL: | ||
716 | /* make sure single-step breakpoint is gone. */ | ||
717 | child->ptrace &= ~PT_SINGLESTEP; | ||
718 | ptrace_cancel_bpt(child); | ||
719 | if (child->exit_state != EXIT_ZOMBIE) { | ||
720 | child->exit_code = SIGKILL; | ||
721 | wake_up_process(child); | ||
722 | } | ||
723 | ret = 0; | ||
724 | break; | ||
725 | |||
726 | /* | ||
727 | * execute single instruction. | ||
728 | */ | ||
729 | case PTRACE_SINGLESTEP: | ||
730 | ret = -EIO; | ||
731 | if ((unsigned long) data > _NSIG) | ||
732 | break; | ||
733 | child->ptrace |= PT_SINGLESTEP; | ||
734 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | ||
735 | child->exit_code = data; | ||
736 | /* give it a chance to run. */ | ||
737 | wake_up_process(child); | ||
738 | ret = 0; | ||
739 | break; | ||
740 | |||
741 | case PTRACE_DETACH: | ||
742 | ret = ptrace_detach(child, data); | ||
743 | break; | ||
744 | |||
745 | case PTRACE_GETREGS: | ||
746 | ret = ptrace_getregs(child, (void __user *)data); | ||
747 | break; | ||
748 | |||
749 | case PTRACE_SETREGS: | ||
750 | ret = ptrace_setregs(child, (void __user *)data); | ||
751 | break; | ||
752 | |||
753 | case PTRACE_GETFPREGS: | ||
754 | ret = ptrace_getfpregs(child, (void __user *)data); | ||
755 | break; | ||
756 | |||
757 | case PTRACE_SETFPREGS: | ||
758 | ret = ptrace_setfpregs(child, (void __user *)data); | ||
759 | break; | ||
760 | |||
761 | #ifdef CONFIG_IWMMXT | ||
762 | case PTRACE_GETWMMXREGS: | ||
763 | ret = ptrace_getwmmxregs(child, (void __user *)data); | ||
764 | break; | ||
765 | |||
766 | case PTRACE_SETWMMXREGS: | ||
767 | ret = ptrace_setwmmxregs(child, (void __user *)data); | ||
768 | break; | ||
769 | #endif | ||
770 | |||
771 | case PTRACE_GET_THREAD_AREA: | ||
772 | ret = put_user(child->thread_info->tp_value, | ||
773 | (unsigned long __user *) data); | ||
774 | break; | ||
775 | |||
776 | default: | ||
777 | ret = ptrace_request(child, request, addr, data); | ||
778 | break; | ||
779 | } | ||
780 | |||
781 | return ret; | ||
782 | } | ||
783 | |||
784 | asmlinkage int sys_ptrace(long request, long pid, long addr, long data) | ||
785 | { | ||
786 | struct task_struct *child; | ||
787 | int ret; | ||
788 | |||
789 | lock_kernel(); | ||
790 | ret = -EPERM; | ||
791 | if (request == PTRACE_TRACEME) { | ||
792 | /* are we already being traced? */ | ||
793 | if (current->ptrace & PT_PTRACED) | ||
794 | goto out; | ||
795 | ret = security_ptrace(current->parent, current); | ||
796 | if (ret) | ||
797 | goto out; | ||
798 | /* set the ptrace bit in the process flags. */ | ||
799 | current->ptrace |= PT_PTRACED; | ||
800 | ret = 0; | ||
801 | goto out; | ||
802 | } | ||
803 | ret = -ESRCH; | ||
804 | read_lock(&tasklist_lock); | ||
805 | child = find_task_by_pid(pid); | ||
806 | if (child) | ||
807 | get_task_struct(child); | ||
808 | read_unlock(&tasklist_lock); | ||
809 | if (!child) | ||
810 | goto out; | ||
811 | |||
812 | ret = -EPERM; | ||
813 | if (pid == 1) /* you may not mess with init */ | ||
814 | goto out_tsk; | ||
815 | |||
816 | if (request == PTRACE_ATTACH) { | ||
817 | ret = ptrace_attach(child); | ||
818 | goto out_tsk; | ||
819 | } | ||
820 | ret = ptrace_check_attach(child, request == PTRACE_KILL); | ||
821 | if (ret == 0) | ||
822 | ret = do_ptrace(request, child, addr, data); | ||
823 | |||
824 | out_tsk: | ||
825 | put_task_struct(child); | ||
826 | out: | ||
827 | unlock_kernel(); | ||
828 | return ret; | ||
829 | } | ||
830 | |||
831 | asmlinkage void syscall_trace(int why, struct pt_regs *regs) | ||
832 | { | ||
833 | unsigned long ip; | ||
834 | |||
835 | if (!test_thread_flag(TIF_SYSCALL_TRACE)) | ||
836 | return; | ||
837 | if (!(current->ptrace & PT_PTRACED)) | ||
838 | return; | ||
839 | |||
840 | /* | ||
841 | * Save IP. IP is used to denote syscall entry/exit: | ||
842 | * IP = 0 -> entry, = 1 -> exit | ||
843 | */ | ||
844 | ip = regs->ARM_ip; | ||
845 | regs->ARM_ip = why; | ||
846 | |||
847 | /* the 0x80 provides a way for the tracing parent to distinguish | ||
848 | between a syscall stop and SIGTRAP delivery */ | ||
849 | ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) | ||
850 | ? 0x80 : 0)); | ||
851 | /* | ||
852 | * this isn't the same as continuing with a signal, but it will do | ||
853 | * for normal use. strace only continues with a signal if the | ||
854 | * stopping signal is not SIGTRAP. -brl | ||
855 | */ | ||
856 | if (current->exit_code) { | ||
857 | send_sig(current->exit_code, current, 1); | ||
858 | current->exit_code = 0; | ||
859 | } | ||
860 | regs->ARM_ip = ip; | ||
861 | } | ||