blob: c1e7e6ae7c6f5642527228fd3a5222b8bfcbd5a1 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/* ptrace.c: Sparc process tracing support.
2 *
David S. Miller8e3fe802008-02-06 21:00:44 -08003 * Copyright (C) 1996, 2008 David S. Miller (davem@davemloft.net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 *
5 * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson,
6 * and David Mosberger.
7 *
Joe Perches5b2afff2007-12-20 13:55:45 -08008 * Added Linux support -miguel (weird, eh?, the original code was meant
Linus Torvalds1da177e2005-04-16 15:20:36 -07009 * to emulate SunOS).
10 */
11
12#include <linux/kernel.h>
13#include <linux/sched.h>
14#include <linux/mm.h>
15#include <linux/errno.h>
16#include <linux/ptrace.h>
17#include <linux/user.h>
18#include <linux/smp.h>
19#include <linux/smp_lock.h>
20#include <linux/security.h>
Jesper Juhl7ed20e12005-05-01 08:59:14 -070021#include <linux/signal.h>
David S. Miller8e3fe802008-02-06 21:00:44 -080022#include <linux/regset.h>
23#include <linux/elf.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070024
25#include <asm/pgtable.h>
26#include <asm/system.h>
27#include <asm/uaccess.h>
28
29#define MAGIC_CONSTANT 0x80000000
30
31
32/* Returning from ptrace is a bit tricky because the syscall return
33 * low level code assumes any value returned which is negative and
34 * is a valid errno will mean setting the condition codes to indicate
35 * an error return. This doesn't work, so we have this hook.
36 */
37static inline void pt_error_return(struct pt_regs *regs, unsigned long error)
38{
39 regs->u_regs[UREG_I0] = error;
40 regs->psr |= PSR_C;
41 regs->pc = regs->npc;
42 regs->npc += 4;
43}
44
45static inline void pt_succ_return(struct pt_regs *regs, unsigned long value)
46{
47 regs->u_regs[UREG_I0] = value;
48 regs->psr &= ~PSR_C;
49 regs->pc = regs->npc;
50 regs->npc += 4;
51}
52
53static void
54pt_succ_return_linux(struct pt_regs *regs, unsigned long value, long __user *addr)
55{
56 if (put_user(value, addr)) {
57 pt_error_return(regs, EFAULT);
58 return;
59 }
60 regs->u_regs[UREG_I0] = 0;
61 regs->psr &= ~PSR_C;
62 regs->pc = regs->npc;
63 regs->npc += 4;
64}
65
66static void
67pt_os_succ_return (struct pt_regs *regs, unsigned long val, long __user *addr)
68{
69 if (current->personality == PER_SUNOS)
70 pt_succ_return (regs, val);
71 else
72 pt_succ_return_linux (regs, val, addr);
73}
74
75/* Fuck me gently with a chainsaw... */
76static inline void read_sunos_user(struct pt_regs *regs, unsigned long offset,
77 struct task_struct *tsk, long __user *addr)
78{
79 struct pt_regs *cregs = tsk->thread.kregs;
Al Virod562ef62006-01-12 01:05:46 -080080 struct thread_info *t = task_thread_info(tsk);
Linus Torvalds1da177e2005-04-16 15:20:36 -070081 int v;
82
83 if(offset >= 1024)
84 offset -= 1024; /* whee... */
85 if(offset & ((sizeof(unsigned long) - 1))) {
86 pt_error_return(regs, EIO);
87 return;
88 }
89 if(offset >= 16 && offset < 784) {
90 offset -= 16; offset >>= 2;
91 pt_os_succ_return(regs, *(((unsigned long *)(&t->reg_window[0]))+offset), addr);
92 return;
93 }
94 if(offset >= 784 && offset < 832) {
95 offset -= 784; offset >>= 2;
96 pt_os_succ_return(regs, *(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset), addr);
97 return;
98 }
99 switch(offset) {
100 case 0:
101 v = t->ksp;
102 break;
103 case 4:
104 v = t->kpc;
105 break;
106 case 8:
107 v = t->kpsr;
108 break;
109 case 12:
110 v = t->uwinmask;
111 break;
112 case 832:
113 v = t->w_saved;
114 break;
115 case 896:
116 v = cregs->u_regs[UREG_I0];
117 break;
118 case 900:
119 v = cregs->u_regs[UREG_I1];
120 break;
121 case 904:
122 v = cregs->u_regs[UREG_I2];
123 break;
124 case 908:
125 v = cregs->u_regs[UREG_I3];
126 break;
127 case 912:
128 v = cregs->u_regs[UREG_I4];
129 break;
130 case 916:
131 v = cregs->u_regs[UREG_I5];
132 break;
133 case 920:
134 v = cregs->u_regs[UREG_I6];
135 break;
136 case 924:
137 if(tsk->thread.flags & MAGIC_CONSTANT)
138 v = cregs->u_regs[UREG_G1];
139 else
140 v = 0;
141 break;
142 case 940:
143 v = cregs->u_regs[UREG_I0];
144 break;
145 case 944:
146 v = cregs->u_regs[UREG_I1];
147 break;
148
149 case 948:
150 /* Isn't binary compatibility _fun_??? */
151 if(cregs->psr & PSR_C)
152 v = cregs->u_regs[UREG_I0] << 24;
153 else
154 v = 0;
155 break;
156
157 /* Rest of them are completely unsupported. */
158 default:
159 printk("%s [%d]: Wants to read user offset %ld\n",
Alexey Dobriyan19c58702007-10-18 23:40:41 -0700160 current->comm, task_pid_nr(current), offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 pt_error_return(regs, EIO);
162 return;
163 }
164 if (current->personality == PER_SUNOS)
165 pt_succ_return (regs, v);
166 else
167 pt_succ_return_linux (regs, v, addr);
168 return;
169}
170
171static inline void write_sunos_user(struct pt_regs *regs, unsigned long offset,
172 struct task_struct *tsk)
173{
174 struct pt_regs *cregs = tsk->thread.kregs;
Al Virod562ef62006-01-12 01:05:46 -0800175 struct thread_info *t = task_thread_info(tsk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176 unsigned long value = regs->u_regs[UREG_I3];
177
178 if(offset >= 1024)
179 offset -= 1024; /* whee... */
180 if(offset & ((sizeof(unsigned long) - 1)))
181 goto failure;
182 if(offset >= 16 && offset < 784) {
183 offset -= 16; offset >>= 2;
184 *(((unsigned long *)(&t->reg_window[0]))+offset) = value;
185 goto success;
186 }
187 if(offset >= 784 && offset < 832) {
188 offset -= 784; offset >>= 2;
189 *(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset) = value;
190 goto success;
191 }
192 switch(offset) {
193 case 896:
194 cregs->u_regs[UREG_I0] = value;
195 break;
196 case 900:
197 cregs->u_regs[UREG_I1] = value;
198 break;
199 case 904:
200 cregs->u_regs[UREG_I2] = value;
201 break;
202 case 908:
203 cregs->u_regs[UREG_I3] = value;
204 break;
205 case 912:
206 cregs->u_regs[UREG_I4] = value;
207 break;
208 case 916:
209 cregs->u_regs[UREG_I5] = value;
210 break;
211 case 920:
212 cregs->u_regs[UREG_I6] = value;
213 break;
214 case 924:
215 cregs->u_regs[UREG_I7] = value;
216 break;
217 case 940:
218 cregs->u_regs[UREG_I0] = value;
219 break;
220 case 944:
221 cregs->u_regs[UREG_I1] = value;
222 break;
223
224 /* Rest of them are completely unsupported or "no-touch". */
225 default:
226 printk("%s [%d]: Wants to write user offset %ld\n",
Alexey Dobriyan19c58702007-10-18 23:40:41 -0700227 current->comm, task_pid_nr(current), offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228 goto failure;
229 }
230success:
231 pt_succ_return(regs, 0);
232 return;
233failure:
234 pt_error_return(regs, EIO);
235 return;
236}
237
238/* #define ALLOW_INIT_TRACING */
239/* #define DEBUG_PTRACE */
240
241#ifdef DEBUG_PTRACE
242char *pt_rq [] = {
243 /* 0 */ "TRACEME", "PEEKTEXT", "PEEKDATA", "PEEKUSR",
244 /* 4 */ "POKETEXT", "POKEDATA", "POKEUSR", "CONT",
245 /* 8 */ "KILL", "SINGLESTEP", "SUNATTACH", "SUNDETACH",
246 /* 12 */ "GETREGS", "SETREGS", "GETFPREGS", "SETFPREGS",
247 /* 16 */ "READDATA", "WRITEDATA", "READTEXT", "WRITETEXT",
248 /* 20 */ "GETFPAREGS", "SETFPAREGS", "unknown", "unknown",
249 /* 24 */ "SYSCALL", ""
250};
251#endif
252
253/*
254 * Called by kernel/ptrace.c when detaching..
255 *
256 * Make sure single step bits etc are not set.
257 */
258void ptrace_disable(struct task_struct *child)
259{
260 /* nothing to do */
261}
262
David S. Miller8e3fe802008-02-06 21:00:44 -0800263enum sparc_regset {
264 REGSET_GENERAL,
265 REGSET_FP,
266};
267
268static int genregs32_get(struct task_struct *target,
269 const struct user_regset *regset,
270 unsigned int pos, unsigned int count,
271 void *kbuf, void __user *ubuf)
272{
273 const struct pt_regs *regs = target->thread.kregs;
274 unsigned long __user *reg_window;
275 unsigned long *k = kbuf;
276 unsigned long __user *u = ubuf;
277 unsigned long reg;
278
279 if (target == current)
280 flush_user_windows();
281
282 pos /= sizeof(reg);
283 count /= sizeof(reg);
284
285 if (kbuf) {
286 for (; count > 0 && pos < 16; count--)
287 *k++ = regs->u_regs[pos++];
288
289 reg_window = (unsigned long __user *) regs->u_regs[UREG_I6];
290 for (; count > 0 && pos < 32; count--) {
291 if (get_user(*k++, &reg_window[pos++]))
292 return -EFAULT;
293 }
294 } else {
295 for (; count > 0 && pos < 16; count--) {
296 if (put_user(regs->u_regs[pos++], u++))
297 return -EFAULT;
298 }
299
300 reg_window = (unsigned long __user *) regs->u_regs[UREG_I6];
301 for (; count > 0 && pos < 32; count--) {
302 if (get_user(reg, &reg_window[pos++]) ||
303 put_user(reg, u++))
304 return -EFAULT;
305 }
306 }
307 while (count > 0) {
308 switch (pos) {
309 case 32: /* PSR */
310 reg = regs->psr;
311 break;
312 case 33: /* PC */
313 reg = regs->pc;
314 break;
315 case 34: /* NPC */
316 reg = regs->npc;
317 break;
318 case 35: /* Y */
319 reg = regs->y;
320 break;
321 case 36: /* WIM */
322 case 37: /* TBR */
323 reg = 0;
324 break;
325 default:
326 goto finish;
327 }
328
329 if (kbuf)
330 *k++ = reg;
331 else if (put_user(reg, u++))
332 return -EFAULT;
333 pos++;
334 count--;
335 }
336finish:
337 pos *= sizeof(reg);
338 count *= sizeof(reg);
339
340 return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
341 38 * sizeof(reg), -1);
342}
343
344static int genregs32_set(struct task_struct *target,
345 const struct user_regset *regset,
346 unsigned int pos, unsigned int count,
347 const void *kbuf, const void __user *ubuf)
348{
349 struct pt_regs *regs = target->thread.kregs;
350 unsigned long __user *reg_window;
351 const unsigned long *k = kbuf;
352 const unsigned long __user *u = ubuf;
353 unsigned long reg;
354
355 if (target == current)
356 flush_user_windows();
357
358 pos /= sizeof(reg);
359 count /= sizeof(reg);
360
361 if (kbuf) {
362 for (; count > 0 && pos < 16; count--)
363 regs->u_regs[pos++] = *k++;
364
365 reg_window = (unsigned long __user *) regs->u_regs[UREG_I6];
366 for (; count > 0 && pos < 32; count--) {
367 if (put_user(*k++, &reg_window[pos++]))
368 return -EFAULT;
369 }
370 } else {
371 for (; count > 0 && pos < 16; count--) {
372 if (get_user(reg, u++))
373 return -EFAULT;
374 regs->u_regs[pos++] = reg;
375 }
376
377 reg_window = (unsigned long __user *) regs->u_regs[UREG_I6];
378 for (; count > 0 && pos < 32; count--) {
379 if (get_user(reg, u++) ||
380 put_user(reg, &reg_window[pos++]))
381 return -EFAULT;
382 }
383 }
384 while (count > 0) {
385 unsigned long psr;
386
387 if (kbuf)
388 reg = *k++;
389 else if (get_user(reg, u++))
390 return -EFAULT;
391
392 switch (pos) {
393 case 32: /* PSR */
394 psr = regs->psr;
395 psr &= ~PSR_ICC;
396 psr |= (reg & PSR_ICC);
397 regs->psr = psr;
398 break;
399 case 33: /* PC */
400 regs->pc = reg;
401 break;
402 case 34: /* NPC */
403 regs->npc = reg;
404 break;
405 case 35: /* Y */
406 regs->y = reg;
407 break;
408 case 36: /* WIM */
409 case 37: /* TBR */
410 break;
411 default:
412 goto finish;
413 }
414
415 pos++;
416 count--;
417 }
418finish:
419 pos *= sizeof(reg);
420 count *= sizeof(reg);
421
422 return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
423 38 * sizeof(reg), -1);
424}
425
426static int fpregs32_get(struct task_struct *target,
427 const struct user_regset *regset,
428 unsigned int pos, unsigned int count,
429 void *kbuf, void __user *ubuf)
430{
431 const unsigned long *fpregs = target->thread.float_regs;
432 int ret = 0;
433
434#if 0
435 if (target == current)
436 save_and_clear_fpu();
437#endif
438
439 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
440 fpregs,
441 0, 32 * sizeof(u32));
442
443 if (!ret)
444 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
445 32 * sizeof(u32),
446 33 * sizeof(u32));
447 if (!ret)
448 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
449 &target->thread.fsr,
450 33 * sizeof(u32),
451 34 * sizeof(u32));
452
453 if (!ret) {
454 unsigned long val;
455
456 val = (1 << 8) | (8 << 16);
457 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
458 &val,
459 34 * sizeof(u32),
460 35 * sizeof(u32));
461 }
462
463 if (!ret)
464 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
465 35 * sizeof(u32), -1);
466
467 return ret;
468}
469
470static int fpregs32_set(struct task_struct *target,
471 const struct user_regset *regset,
472 unsigned int pos, unsigned int count,
473 const void *kbuf, const void __user *ubuf)
474{
475 unsigned long *fpregs = target->thread.float_regs;
476 int ret;
477
478#if 0
479 if (target == current)
480 save_and_clear_fpu();
481#endif
482 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
483 fpregs,
484 0, 32 * sizeof(u32));
485 if (!ret)
486 user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
487 32 * sizeof(u32),
488 33 * sizeof(u32));
489 if (!ret && count > 0) {
490 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
491 &target->thread.fsr,
492 33 * sizeof(u32),
493 34 * sizeof(u32));
494 }
495
496 if (!ret)
497 ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
498 34 * sizeof(u32), -1);
499 return ret;
500}
501
502static const struct user_regset sparc32_regsets[] = {
503 /* Format is:
504 * G0 --> G7
505 * O0 --> O7
506 * L0 --> L7
507 * I0 --> I7
508 * PSR, PC, nPC, Y, WIM, TBR
509 */
510 [REGSET_GENERAL] = {
511 .core_note_type = NT_PRSTATUS,
512 .n = 38 * sizeof(u32),
513 .size = sizeof(u32), .align = sizeof(u32),
514 .get = genregs32_get, .set = genregs32_set
515 },
516 /* Format is:
517 * F0 --> F31
518 * empty 32-bit word
519 * FSR (32--bit word)
520 * FPU QUEUE COUNT (8-bit char)
521 * FPU QUEUE ENTRYSIZE (8-bit char)
522 * FPU ENABLED (8-bit char)
523 * empty 8-bit char
524 * FPU QUEUE (64 32-bit ints)
525 */
526 [REGSET_FP] = {
527 .core_note_type = NT_PRFPREG,
528 .n = 99 * sizeof(u32),
529 .size = sizeof(u32), .align = sizeof(u32),
530 .get = fpregs32_get, .set = fpregs32_set
531 },
532};
533
534static const struct user_regset_view user_sparc32_view = {
535 .name = "sparc", .e_machine = EM_SPARC,
536 .regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets)
537};
538
539const struct user_regset_view *task_user_regset_view(struct task_struct *task)
540{
541 return &user_sparc32_view;
542}
543
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544asmlinkage void do_ptrace(struct pt_regs *regs)
545{
546 unsigned long request = regs->u_regs[UREG_I0];
547 unsigned long pid = regs->u_regs[UREG_I1];
548 unsigned long addr = regs->u_regs[UREG_I2];
549 unsigned long data = regs->u_regs[UREG_I3];
550 unsigned long addr2 = regs->u_regs[UREG_I4];
551 struct task_struct *child;
552 int ret;
553
554 lock_kernel();
555#ifdef DEBUG_PTRACE
556 {
557 char *s;
558
559 if ((request >= 0) && (request <= 24))
560 s = pt_rq [request];
561 else
562 s = "unknown";
563
564 if (request == PTRACE_POKEDATA && data == 0x91d02001){
565 printk ("do_ptrace: breakpoint pid=%d, addr=%08lx addr2=%08lx\n",
566 pid, addr, addr2);
567 } else
568 printk("do_ptrace: rq=%s(%d) pid=%d addr=%08lx data=%08lx addr2=%08lx\n",
569 s, (int) request, (int) pid, addr, data, addr2);
570 }
571#endif
Christoph Hellwig6b9c7ed2006-01-08 01:02:33 -0800572
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 if (request == PTRACE_TRACEME) {
Christoph Hellwig6b9c7ed2006-01-08 01:02:33 -0800574 ret = ptrace_traceme();
Alexey Dobriyan35bca362006-12-01 20:18:40 -0800575 if (ret < 0)
576 pt_error_return(regs, -ret);
577 else
578 pt_succ_return(regs, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 goto out;
580 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581
Christoph Hellwig6b9c7ed2006-01-08 01:02:33 -0800582 child = ptrace_get_task_struct(pid);
583 if (IS_ERR(child)) {
584 ret = PTR_ERR(child);
585 pt_error_return(regs, -ret);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586 goto out;
587 }
588
589 if ((current->personality == PER_SUNOS && request == PTRACE_SUNATTACH)
590 || (current->personality != PER_SUNOS && request == PTRACE_ATTACH)) {
591 if (ptrace_attach(child)) {
592 pt_error_return(regs, EPERM);
593 goto out_tsk;
594 }
595 pt_succ_return(regs, 0);
596 goto out_tsk;
597 }
598
599 ret = ptrace_check_attach(child, request == PTRACE_KILL);
600 if (ret < 0) {
601 pt_error_return(regs, -ret);
602 goto out_tsk;
603 }
604
605 switch(request) {
606 case PTRACE_PEEKTEXT: /* read word at location addr. */
607 case PTRACE_PEEKDATA: {
608 unsigned long tmp;
609
610 if (access_process_vm(child, addr,
611 &tmp, sizeof(tmp), 0) == sizeof(tmp))
612 pt_os_succ_return(regs, tmp, (long __user *)data);
613 else
614 pt_error_return(regs, EIO);
615 goto out_tsk;
616 }
617
618 case PTRACE_PEEKUSR:
619 read_sunos_user(regs, addr, child, (long __user *) data);
620 goto out_tsk;
621
622 case PTRACE_POKEUSR:
623 write_sunos_user(regs, addr, child);
624 goto out_tsk;
625
626 case PTRACE_POKETEXT: /* write the word at location addr. */
627 case PTRACE_POKEDATA: {
628 if (access_process_vm(child, addr,
629 &data, sizeof(data), 1) == sizeof(data))
630 pt_succ_return(regs, 0);
631 else
632 pt_error_return(regs, EIO);
633 goto out_tsk;
634 }
635
636 case PTRACE_GETREGS: {
637 struct pt_regs __user *pregs = (struct pt_regs __user *) addr;
638 struct pt_regs *cregs = child->thread.kregs;
639 int rval;
640
641 if (!access_ok(VERIFY_WRITE, pregs, sizeof(struct pt_regs))) {
642 rval = -EFAULT;
643 pt_error_return(regs, -rval);
644 goto out_tsk;
645 }
646 __put_user(cregs->psr, (&pregs->psr));
647 __put_user(cregs->pc, (&pregs->pc));
648 __put_user(cregs->npc, (&pregs->npc));
649 __put_user(cregs->y, (&pregs->y));
650 for(rval = 1; rval < 16; rval++)
651 __put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]));
652 pt_succ_return(regs, 0);
653#ifdef DEBUG_PTRACE
654 printk ("PC=%x nPC=%x o7=%x\n", cregs->pc, cregs->npc, cregs->u_regs [15]);
655#endif
656 goto out_tsk;
657 }
658
659 case PTRACE_SETREGS: {
660 struct pt_regs __user *pregs = (struct pt_regs __user *) addr;
661 struct pt_regs *cregs = child->thread.kregs;
662 unsigned long psr, pc, npc, y;
663 int i;
664
665 /* Must be careful, tracing process can only set certain
666 * bits in the psr.
667 */
668 if (!access_ok(VERIFY_READ, pregs, sizeof(struct pt_regs))) {
669 pt_error_return(regs, EFAULT);
670 goto out_tsk;
671 }
672 __get_user(psr, (&pregs->psr));
673 __get_user(pc, (&pregs->pc));
674 __get_user(npc, (&pregs->npc));
675 __get_user(y, (&pregs->y));
676 psr &= PSR_ICC;
677 cregs->psr &= ~PSR_ICC;
678 cregs->psr |= psr;
679 if (!((pc | npc) & 3)) {
680 cregs->pc = pc;
681 cregs->npc =npc;
682 }
683 cregs->y = y;
684 for(i = 1; i < 16; i++)
685 __get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]));
686 pt_succ_return(regs, 0);
687 goto out_tsk;
688 }
689
690 case PTRACE_GETFPREGS: {
691 struct fps {
692 unsigned long regs[32];
693 unsigned long fsr;
694 unsigned long flags;
695 unsigned long extra;
696 unsigned long fpqd;
697 struct fq {
698 unsigned long *insnaddr;
699 unsigned long insn;
700 } fpq[16];
701 };
702 struct fps __user *fps = (struct fps __user *) addr;
703 int i;
704
705 if (!access_ok(VERIFY_WRITE, fps, sizeof(struct fps))) {
706 i = -EFAULT;
707 pt_error_return(regs, -i);
708 goto out_tsk;
709 }
710 for(i = 0; i < 32; i++)
711 __put_user(child->thread.float_regs[i], (&fps->regs[i]));
712 __put_user(child->thread.fsr, (&fps->fsr));
713 __put_user(child->thread.fpqdepth, (&fps->fpqd));
714 __put_user(0, (&fps->flags));
715 __put_user(0, (&fps->extra));
716 for(i = 0; i < 16; i++) {
717 __put_user(child->thread.fpqueue[i].insn_addr,
718 (&fps->fpq[i].insnaddr));
719 __put_user(child->thread.fpqueue[i].insn, (&fps->fpq[i].insn));
720 }
721 pt_succ_return(regs, 0);
722 goto out_tsk;
723 }
724
725 case PTRACE_SETFPREGS: {
726 struct fps {
727 unsigned long regs[32];
728 unsigned long fsr;
729 unsigned long flags;
730 unsigned long extra;
731 unsigned long fpqd;
732 struct fq {
733 unsigned long *insnaddr;
734 unsigned long insn;
735 } fpq[16];
736 };
737 struct fps __user *fps = (struct fps __user *) addr;
738 int i;
739
740 if (!access_ok(VERIFY_READ, fps, sizeof(struct fps))) {
741 i = -EFAULT;
742 pt_error_return(regs, -i);
743 goto out_tsk;
744 }
745 copy_from_user(&child->thread.float_regs[0], &fps->regs[0], (32 * sizeof(unsigned long)));
746 __get_user(child->thread.fsr, (&fps->fsr));
747 __get_user(child->thread.fpqdepth, (&fps->fpqd));
748 for(i = 0; i < 16; i++) {
749 __get_user(child->thread.fpqueue[i].insn_addr,
750 (&fps->fpq[i].insnaddr));
751 __get_user(child->thread.fpqueue[i].insn, (&fps->fpq[i].insn));
752 }
753 pt_succ_return(regs, 0);
754 goto out_tsk;
755 }
756
757 case PTRACE_READTEXT:
758 case PTRACE_READDATA: {
759 int res = ptrace_readdata(child, addr,
760 (void __user *) addr2, data);
761
762 if (res == data) {
763 pt_succ_return(regs, 0);
764 goto out_tsk;
765 }
766 /* Partial read is an IO failure */
767 if (res >= 0)
768 res = -EIO;
769 pt_error_return(regs, -res);
770 goto out_tsk;
771 }
772
773 case PTRACE_WRITETEXT:
774 case PTRACE_WRITEDATA: {
775 int res = ptrace_writedata(child, (void __user *) addr2,
776 addr, data);
777
778 if (res == data) {
779 pt_succ_return(regs, 0);
780 goto out_tsk;
781 }
782 /* Partial write is an IO failure */
783 if (res >= 0)
784 res = -EIO;
785 pt_error_return(regs, -res);
786 goto out_tsk;
787 }
788
789 case PTRACE_SYSCALL: /* continue and stop at (return from) syscall */
790 addr = 1;
791
792 case PTRACE_CONT: { /* restart after signal. */
Jesper Juhl7ed20e12005-05-01 08:59:14 -0700793 if (!valid_signal(data)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 pt_error_return(regs, EIO);
795 goto out_tsk;
796 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797
798 if (request == PTRACE_SYSCALL)
799 set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
800 else
801 clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
802
803 child->exit_code = data;
804#ifdef DEBUG_PTRACE
805 printk("CONT: %s [%d]: set exit_code = %x %lx %lx\n",
806 child->comm, child->pid, child->exit_code,
807 child->thread.kregs->pc,
808 child->thread.kregs->npc);
809#endif
810 wake_up_process(child);
811 pt_succ_return(regs, 0);
812 goto out_tsk;
813 }
814
815/*
816 * make the child exit. Best I can do is send it a sigkill.
817 * perhaps it should be put in the status that it wants to
818 * exit.
819 */
820 case PTRACE_KILL: {
821 if (child->exit_state == EXIT_ZOMBIE) { /* already dead */
822 pt_succ_return(regs, 0);
823 goto out_tsk;
824 }
825 wake_up_process(child);
826 child->exit_code = SIGKILL;
827 pt_succ_return(regs, 0);
828 goto out_tsk;
829 }
830
831 case PTRACE_SUNDETACH: { /* detach a process that was attached. */
832 int err = ptrace_detach(child, data);
833 if (err) {
834 pt_error_return(regs, EIO);
835 goto out_tsk;
836 }
837 pt_succ_return(regs, 0);
838 goto out_tsk;
839 }
840
841 /* PTRACE_DUMPCORE unsupported... */
842
843 default: {
844 int err = ptrace_request(child, request, addr, data);
845 if (err)
846 pt_error_return(regs, -err);
847 else
848 pt_succ_return(regs, 0);
849 goto out_tsk;
850 }
851 }
852out_tsk:
853 if (child)
854 put_task_struct(child);
855out:
856 unlock_kernel();
857}
858
859asmlinkage void syscall_trace(void)
860{
861#ifdef DEBUG_PTRACE
862 printk("%s [%d]: syscall_trace\n", current->comm, current->pid);
863#endif
864 if (!test_thread_flag(TIF_SYSCALL_TRACE))
865 return;
866 if (!(current->ptrace & PT_PTRACED))
867 return;
868 current->thread.flags ^= MAGIC_CONSTANT;
869 ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
870 ? 0x80 : 0));
871 /*
872 * this isn't the same as continuing with a signal, but it will do
873 * for normal use. strace only continues with a signal if the
874 * stopping signal is not SIGTRAP. -brl
875 */
876#ifdef DEBUG_PTRACE
877 printk("%s [%d]: syscall_trace exit= %x\n", current->comm,
878 current->pid, current->exit_code);
879#endif
880 if (current->exit_code) {
881 send_sig (current->exit_code, current, 1);
882 current->exit_code = 0;
883 }
884}