1 /* ptrace.c: Sparc process tracing support.
3 * Copyright (C) 1996 David S. Miller (davem@caipfs.rutgers.edu)
4 * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
6 * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson,
9 * Added Linux support -miguel (wierd, eh?, the orignal code was meant
13 #include <linux/kernel.h>
14 #include <linux/sched.h>
16 #include <linux/errno.h>
17 #include <linux/ptrace.h>
18 #include <linux/user.h>
19 #include <linux/smp.h>
20 #include <linux/smp_lock.h>
23 #include <asm/pgtable.h>
24 #include <asm/system.h>
25 #include <asm/uaccess.h>
26 #include <asm/psrcompat.h>
27 #include <asm/visasm.h>
29 #define MAGIC_CONSTANT 0x80000000
31 /* Returning from ptrace is a bit tricky because the syscall return
32 * low level code assumes any value returned which is negative and
33 * is a valid errno will mean setting the condition codes to indicate
34 * an error return. This doesn't work, so we have this hook.
36 static inline void pt_error_return(struct pt_regs
*regs
, unsigned long error
)
38 regs
->u_regs
[UREG_I0
] = error
;
39 regs
->tstate
|= (TSTATE_ICARRY
| TSTATE_XCARRY
);
40 regs
->tpc
= regs
->tnpc
;
44 static inline void pt_succ_return(struct pt_regs
*regs
, unsigned long value
)
46 regs
->u_regs
[UREG_I0
] = value
;
47 regs
->tstate
&= ~(TSTATE_ICARRY
| TSTATE_XCARRY
);
48 regs
->tpc
= regs
->tnpc
;
53 pt_succ_return_linux(struct pt_regs
*regs
, unsigned long value
, long *addr
)
55 if (current
->thread
.flags
& SPARC_FLAG_32BIT
) {
56 if(put_user(value
, (unsigned int *)addr
))
57 return pt_error_return(regs
, EFAULT
);
59 if(put_user(value
, addr
))
60 return pt_error_return(regs
, EFAULT
);
62 regs
->u_regs
[UREG_I0
] = 0;
63 regs
->tstate
&= ~(TSTATE_ICARRY
| TSTATE_XCARRY
);
64 regs
->tpc
= regs
->tnpc
;
69 pt_os_succ_return (struct pt_regs
*regs
, unsigned long val
, long *addr
)
71 if (current
->personality
& PER_BSD
)
72 pt_succ_return (regs
, val
);
74 pt_succ_return_linux (regs
, val
, addr
);
77 /* #define ALLOW_INIT_TRACING */
78 /* #define DEBUG_PTRACE */
108 asmlinkage
void do_ptrace(struct pt_regs
*regs
)
110 int request
= regs
->u_regs
[UREG_I0
];
111 pid_t pid
= regs
->u_regs
[UREG_I1
];
112 unsigned long addr
= regs
->u_regs
[UREG_I2
];
113 unsigned long data
= regs
->u_regs
[UREG_I3
];
114 unsigned long addr2
= regs
->u_regs
[UREG_I4
];
115 struct task_struct
*child
;
117 if (current
->thread
.flags
& SPARC_FLAG_32BIT
) {
118 addr
&= 0xffffffffUL
;
119 data
&= 0xffffffffUL
;
120 addr2
&= 0xffffffffUL
;
127 if ((request
> 0) && (request
< 21))
132 if (request
== PTRACE_POKEDATA
&& data
== 0x91d02001){
133 printk ("do_ptrace: breakpoint pid=%d, addr=%016lx addr2=%016lx\n",
136 printk("do_ptrace: rq=%s(%d) pid=%d addr=%016lx data=%016lx addr2=%016lx\n",
137 s
, request
, pid
, addr
, data
, addr2
);
140 if(request
== PTRACE_TRACEME
) {
141 /* are we already being traced? */
142 if (current
->flags
& PF_PTRACED
) {
143 pt_error_return(regs
, EPERM
);
146 /* set the ptrace bit in the process flags. */
147 current
->flags
|= PF_PTRACED
;
148 pt_succ_return(regs
, 0);
151 #ifndef ALLOW_INIT_TRACING
153 /* Can't dork with init. */
154 pt_error_return(regs
, EPERM
);
158 read_lock(&tasklist_lock
);
159 child
= find_task_by_pid(pid
);
160 read_unlock(&tasklist_lock
);
163 pt_error_return(regs
, ESRCH
);
167 if (((current
->personality
& PER_BSD
) && (request
== PTRACE_SUNATTACH
))
168 || (!(current
->personality
& PER_BSD
) && (request
== PTRACE_ATTACH
))) {
171 if(child
== current
) {
172 /* Try this under SunOS/Solaris, bwa haha
173 * You'll never be able to kill the process. ;-)
175 pt_error_return(regs
, EPERM
);
178 if((!child
->dumpable
||
179 (current
->uid
!= child
->euid
) ||
180 (current
->uid
!= child
->uid
) ||
181 (current
->uid
!= child
->suid
) ||
182 (current
->gid
!= child
->egid
) ||
183 (current
->gid
!= child
->sgid
) ||
184 (!cap_issubset(child
->cap_permitted
, current
->cap_permitted
)) ||
185 (current
->gid
!= child
->gid
)) && !capable(CAP_SYS_PTRACE
)) {
186 pt_error_return(regs
, EPERM
);
189 /* the same process cannot be attached many times */
190 if (child
->flags
& PF_PTRACED
) {
191 pt_error_return(regs
, EPERM
);
194 child
->flags
|= PF_PTRACED
;
195 write_lock_irqsave(&tasklist_lock
, flags
);
196 if(child
->p_pptr
!= current
) {
198 child
->p_pptr
= current
;
201 write_unlock_irqrestore(&tasklist_lock
, flags
);
202 send_sig(SIGSTOP
, child
, 1);
203 pt_succ_return(regs
, 0);
206 if (!(child
->flags
& PF_PTRACED
)
207 && ((current
->personality
& PER_BSD
) && (request
!= PTRACE_SUNATTACH
))
208 && (!(current
->personality
& PER_BSD
) && (request
!= PTRACE_ATTACH
))) {
209 pt_error_return(regs
, ESRCH
);
212 if(child
->state
!= TASK_STOPPED
) {
213 if(request
!= PTRACE_KILL
) {
214 pt_error_return(regs
, ESRCH
);
218 if(child
->p_pptr
!= current
) {
219 pt_error_return(regs
, ESRCH
);
223 if(!(child
->thread
.flags
& SPARC_FLAG_32BIT
) &&
224 ((request
== PTRACE_READDATA64
) ||
225 (request
== PTRACE_WRITEDATA64
) ||
226 (request
== PTRACE_READTEXT64
) ||
227 (request
== PTRACE_WRITETEXT64
) ||
228 (request
== PTRACE_PEEKTEXT64
) ||
229 (request
== PTRACE_POKETEXT64
) ||
230 (request
== PTRACE_PEEKDATA64
) ||
231 (request
== PTRACE_POKEDATA64
))) {
232 addr
= regs
->u_regs
[UREG_G2
];
233 addr2
= regs
->u_regs
[UREG_G3
];
234 request
-= 30; /* wheee... */
238 case PTRACE_PEEKTEXT
: /* read word at location addr. */
239 case PTRACE_PEEKDATA
: {
245 if (current
->thread
.flags
& SPARC_FLAG_32BIT
) {
246 copied
= access_process_vm(child
, addr
,
247 &tmp32
, sizeof(tmp32
), 0);
248 tmp64
= (unsigned long) tmp32
;
249 if (copied
== sizeof(tmp32
))
252 copied
= access_process_vm(child
, addr
,
253 &tmp64
, sizeof(tmp64
), 0);
254 if (copied
== sizeof(tmp64
))
258 pt_error_return(regs
, -res
);
260 pt_os_succ_return(regs
, tmp64
, (long *) data
);
264 case PTRACE_POKETEXT
: /* write the word at location addr. */
265 case PTRACE_POKEDATA
: {
268 int copied
, res
= -EIO
;
270 if (current
->thread
.flags
& SPARC_FLAG_32BIT
) {
272 copied
= access_process_vm(child
, addr
,
273 &tmp32
, sizeof(tmp32
), 1);
274 if (copied
== sizeof(tmp32
))
278 copied
= access_process_vm(child
, addr
,
279 &tmp64
, sizeof(tmp64
), 1);
280 if (copied
== sizeof(tmp64
))
284 pt_error_return(regs
, -res
);
286 pt_succ_return(regs
, res
);
290 case PTRACE_GETREGS
: {
291 struct pt_regs32
*pregs
= (struct pt_regs32
*) addr
;
292 struct pt_regs
*cregs
= child
->thread
.kregs
;
295 if (__put_user(tstate_to_psr(cregs
->tstate
), (&pregs
->psr
)) ||
296 __put_user(cregs
->tpc
, (&pregs
->pc
)) ||
297 __put_user(cregs
->tnpc
, (&pregs
->npc
)) ||
298 __put_user(cregs
->y
, (&pregs
->y
))) {
299 pt_error_return(regs
, EFAULT
);
302 for(rval
= 1; rval
< 16; rval
++)
303 if (__put_user(cregs
->u_regs
[rval
], (&pregs
->u_regs
[rval
- 1]))) {
304 pt_error_return(regs
, EFAULT
);
307 pt_succ_return(regs
, 0);
309 printk ("PC=%lx nPC=%lx o7=%lx\n", cregs
->tpc
, cregs
->tnpc
, cregs
->u_regs
[15]);
314 case PTRACE_GETREGS64
: {
315 struct pt_regs
*pregs
= (struct pt_regs
*) addr
;
316 struct pt_regs
*cregs
= child
->thread
.kregs
;
319 if (__put_user(cregs
->tstate
, (&pregs
->tstate
)) ||
320 __put_user(cregs
->tpc
, (&pregs
->tpc
)) ||
321 __put_user(cregs
->tnpc
, (&pregs
->tnpc
)) ||
322 __put_user(cregs
->y
, (&pregs
->y
))) {
323 pt_error_return(regs
, EFAULT
);
326 for(rval
= 1; rval
< 16; rval
++)
327 if (__put_user(cregs
->u_regs
[rval
], (&pregs
->u_regs
[rval
- 1]))) {
328 pt_error_return(regs
, EFAULT
);
331 pt_succ_return(regs
, 0);
333 printk ("PC=%lx nPC=%lx o7=%lx\n", cregs
->tpc
, cregs
->tnpc
, cregs
->u_regs
[15]);
338 case PTRACE_SETREGS
: {
339 struct pt_regs32
*pregs
= (struct pt_regs32
*) addr
;
340 struct pt_regs
*cregs
= child
->thread
.kregs
;
341 unsigned int psr
, pc
, npc
, y
;
344 /* Must be careful, tracing process can only set certain
347 if (__get_user(psr
, (&pregs
->psr
)) ||
348 __get_user(pc
, (&pregs
->pc
)) ||
349 __get_user(npc
, (&pregs
->npc
)) ||
350 __get_user(y
, (&pregs
->y
))) {
351 pt_error_return(regs
, EFAULT
);
354 cregs
->tstate
&= ~(TSTATE_ICC
);
355 cregs
->tstate
|= psr_to_tstate_icc(psr
);
356 if(!((pc
| npc
) & 3)) {
361 for(i
= 1; i
< 16; i
++) {
362 if (__get_user(cregs
->u_regs
[i
], (&pregs
->u_regs
[i
-1]))) {
363 pt_error_return(regs
, EFAULT
);
367 pt_succ_return(regs
, 0);
371 case PTRACE_SETREGS64
: {
372 struct pt_regs
*pregs
= (struct pt_regs
*) addr
;
373 struct pt_regs
*cregs
= child
->thread
.kregs
;
374 unsigned long tstate
, tpc
, tnpc
, y
;
377 /* Must be careful, tracing process can only set certain
380 if (__get_user(tstate
, (&pregs
->tstate
)) ||
381 __get_user(tpc
, (&pregs
->tpc
)) ||
382 __get_user(tnpc
, (&pregs
->tnpc
)) ||
383 __get_user(y
, (&pregs
->y
))) {
384 pt_error_return(regs
, EFAULT
);
387 tstate
&= (TSTATE_ICC
| TSTATE_XCC
);
388 cregs
->tstate
&= ~(TSTATE_ICC
| TSTATE_XCC
);
389 cregs
->tstate
|= tstate
;
390 if(!((tpc
| tnpc
) & 3)) {
395 for(i
= 1; i
< 16; i
++) {
396 if (__get_user(cregs
->u_regs
[i
], (&pregs
->u_regs
[i
-1]))) {
397 pt_error_return(regs
, EFAULT
);
401 pt_succ_return(regs
, 0);
405 case PTRACE_GETFPREGS
: {
407 unsigned int regs
[32];
413 unsigned int insnaddr
;
416 } *fps
= (struct fps
*) addr
;
417 unsigned long *fpregs
= (unsigned long *)(((char *)child
) + AOFF_task_fpregs
);
419 if (copy_to_user(&fps
->regs
[0], fpregs
,
420 (32 * sizeof(unsigned int))) ||
421 __put_user(child
->thread
.xfsr
[0], (&fps
->fsr
)) ||
422 __put_user(0, (&fps
->fpqd
)) ||
423 __put_user(0, (&fps
->flags
)) ||
424 __put_user(0, (&fps
->extra
)) ||
425 clear_user(&fps
->fpq
[0], 32 * sizeof(unsigned int))) {
426 pt_error_return(regs
, EFAULT
);
429 pt_succ_return(regs
, 0);
433 case PTRACE_GETFPREGS64
: {
435 unsigned int regs
[64];
437 } *fps
= (struct fps
*) addr
;
438 unsigned long *fpregs
= (unsigned long *)(((char *)child
) + AOFF_task_fpregs
);
440 if (copy_to_user(&fps
->regs
[0], fpregs
,
441 (64 * sizeof(unsigned int))) ||
442 __put_user(child
->thread
.xfsr
[0], (&fps
->fsr
))) {
443 pt_error_return(regs
, EFAULT
);
446 pt_succ_return(regs
, 0);
450 case PTRACE_SETFPREGS
: {
452 unsigned int regs
[32];
458 unsigned int insnaddr
;
461 } *fps
= (struct fps
*) addr
;
462 unsigned long *fpregs
= (unsigned long *)(((char *)child
) + AOFF_task_fpregs
);
465 if (copy_from_user(fpregs
, &fps
->regs
[0],
466 (32 * sizeof(unsigned int))) ||
467 __get_user(fsr
, (&fps
->fsr
))) {
468 pt_error_return(regs
, EFAULT
);
471 child
->thread
.xfsr
[0] &= 0xffffffff00000000UL
;
472 child
->thread
.xfsr
[0] |= fsr
;
473 if (!(child
->thread
.fpsaved
[0] & FPRS_FEF
))
474 child
->thread
.gsr
[0] = 0;
475 child
->thread
.fpsaved
[0] |= (FPRS_FEF
| FPRS_DL
);
476 pt_succ_return(regs
, 0);
480 case PTRACE_SETFPREGS64
: {
482 unsigned int regs
[64];
484 } *fps
= (struct fps
*) addr
;
485 unsigned long *fpregs
= (unsigned long *)(((char *)child
) + AOFF_task_fpregs
);
487 if (copy_from_user(fpregs
, &fps
->regs
[0],
488 (64 * sizeof(unsigned int))) ||
489 __get_user(child
->thread
.xfsr
[0], (&fps
->fsr
))) {
490 pt_error_return(regs
, EFAULT
);
493 if (!(child
->thread
.fpsaved
[0] & FPRS_FEF
))
494 child
->thread
.gsr
[0] = 0;
495 child
->thread
.fpsaved
[0] |= (FPRS_FEF
| FPRS_DL
| FPRS_DU
);
496 pt_succ_return(regs
, 0);
500 case PTRACE_READTEXT
:
501 case PTRACE_READDATA
: {
502 int res
= ptrace_readdata(child
, addr
,
503 (void *)addr2
, data
);
505 pt_succ_return(regs
, 0);
510 pt_error_return(regs
, -res
);
514 case PTRACE_WRITETEXT
:
515 case PTRACE_WRITEDATA
: {
516 int res
= ptrace_writedata(child
, (void *) addr2
,
519 pt_succ_return(regs
, 0);
524 pt_error_return(regs
, -res
);
527 case PTRACE_SYSCALL
: /* continue and stop at (return from) syscall */
530 case PTRACE_CONT
: { /* restart after signal. */
532 pt_error_return(regs
, EIO
);
537 pt_error_return(regs
, EINVAL
);
541 printk ("Original: %016lx %016lx\n", child
->thread
.kregs
->tpc
, child
->thread
.kregs
->tnpc
);
542 printk ("Continuing with %016lx %016lx\n", addr
, addr
+4);
544 child
->thread
.kregs
->tpc
= addr
;
545 child
->thread
.kregs
->tnpc
= addr
+ 4;
548 if (request
== PTRACE_SYSCALL
)
549 child
->flags
|= PF_TRACESYS
;
551 child
->flags
&= ~PF_TRACESYS
;
553 child
->exit_code
= data
;
555 printk("CONT: %s [%d]: set exit_code = %x %lx %lx\n", child
->comm
,
556 child
->pid
, child
->exit_code
,
557 child
->thread
.kregs
->tpc
,
558 child
->thread
.kregs
->tnpc
);
561 wake_up_process(child
);
562 pt_succ_return(regs
, 0);
567 * make the child exit. Best I can do is send it a sigkill.
568 * perhaps it should be put in the status that it wants to
572 if (child
->state
== TASK_ZOMBIE
) { /* already dead */
573 pt_succ_return(regs
, 0);
576 child
->exit_code
= SIGKILL
;
577 wake_up_process(child
);
578 pt_succ_return(regs
, 0);
582 case PTRACE_SUNDETACH
: { /* detach a process that was attached. */
585 if ((unsigned long) data
> _NSIG
) {
586 pt_error_return(regs
, EIO
);
589 child
->flags
&= ~(PF_PTRACED
|PF_TRACESYS
);
590 child
->exit_code
= data
;
592 write_lock_irqsave(&tasklist_lock
, flags
);
594 child
->p_pptr
= child
->p_opptr
;
596 write_unlock_irqrestore(&tasklist_lock
, flags
);
598 wake_up_process(child
);
599 pt_succ_return(regs
, 0);
603 /* PTRACE_DUMPCORE unsupported... */
606 pt_error_return(regs
, EIO
);
612 for(va
= 0; va
< (PAGE_SIZE
<< 1); va
+= 32)
613 spitfire_put_dcache_tag(va
, 0x0);
614 if (request
== PTRACE_PEEKTEXT
||
615 request
== PTRACE_POKETEXT
||
616 request
== PTRACE_READTEXT
||
617 request
== PTRACE_WRITETEXT
) {
618 for(va
= 0; va
< (PAGE_SIZE
<< 1); va
+= 32)
619 spitfire_put_icache_tag(va
, 0x0);
620 __asm__
__volatile__("flush %g6");
627 asmlinkage
void syscall_trace(void)
630 printk("%s [%d]: syscall_trace\n", current
->comm
, current
->pid
);
632 if ((current
->flags
& (PF_PTRACED
|PF_TRACESYS
))
633 != (PF_PTRACED
|PF_TRACESYS
))
635 current
->exit_code
= SIGTRAP
;
636 current
->state
= TASK_STOPPED
;
637 current
->thread
.flags
^= MAGIC_CONSTANT
;
638 notify_parent(current
, SIGCHLD
);
641 * this isn't the same as continuing with a signal, but it will do
642 * for normal use. strace only continues with a signal if the
643 * stopping signal is not SIGTRAP. -brl
646 printk("%s [%d]: syscall_trace exit= %x\n", current
->comm
,
647 current
->pid
, current
->exit_code
);
649 if (current
->exit_code
) {
650 send_sig (current
->exit_code
, current
, 1);
651 current
->exit_code
= 0;