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_SUNOS
)
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
->ptrace
& PT_PTRACED
) {
143 pt_error_return(regs
, EPERM
);
146 /* set the ptrace bit in the process flags. */
147 current
->ptrace
|= PT_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_SUNOS
&& request
== PTRACE_SUNATTACH
)
168 || (current
->personality
!= PER_SUNOS
&& 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
->ptrace
& PT_PTRACED
) {
191 pt_error_return(regs
, EPERM
);
194 child
->ptrace
|= PT_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
->ptrace
& PT_PTRACED
)) {
207 pt_error_return(regs
, ESRCH
);
210 if(child
->state
!= TASK_STOPPED
) {
211 if(request
!= PTRACE_KILL
) {
212 pt_error_return(regs
, ESRCH
);
216 if(child
->p_pptr
!= current
) {
217 pt_error_return(regs
, ESRCH
);
221 if(!(child
->thread
.flags
& SPARC_FLAG_32BIT
) &&
222 ((request
== PTRACE_READDATA64
) ||
223 (request
== PTRACE_WRITEDATA64
) ||
224 (request
== PTRACE_READTEXT64
) ||
225 (request
== PTRACE_WRITETEXT64
) ||
226 (request
== PTRACE_PEEKTEXT64
) ||
227 (request
== PTRACE_POKETEXT64
) ||
228 (request
== PTRACE_PEEKDATA64
) ||
229 (request
== PTRACE_POKEDATA64
))) {
230 addr
= regs
->u_regs
[UREG_G2
];
231 addr2
= regs
->u_regs
[UREG_G3
];
232 request
-= 30; /* wheee... */
236 case PTRACE_PEEKTEXT
: /* read word at location addr. */
237 case PTRACE_PEEKDATA
: {
243 if (current
->thread
.flags
& SPARC_FLAG_32BIT
) {
244 copied
= access_process_vm(child
, addr
,
245 &tmp32
, sizeof(tmp32
), 0);
246 tmp64
= (unsigned long) tmp32
;
247 if (copied
== sizeof(tmp32
))
250 copied
= access_process_vm(child
, addr
,
251 &tmp64
, sizeof(tmp64
), 0);
252 if (copied
== sizeof(tmp64
))
256 pt_error_return(regs
, -res
);
258 pt_os_succ_return(regs
, tmp64
, (long *) data
);
262 case PTRACE_POKETEXT
: /* write the word at location addr. */
263 case PTRACE_POKEDATA
: {
266 int copied
, res
= -EIO
;
268 if (current
->thread
.flags
& SPARC_FLAG_32BIT
) {
270 copied
= access_process_vm(child
, addr
,
271 &tmp32
, sizeof(tmp32
), 1);
272 if (copied
== sizeof(tmp32
))
276 copied
= access_process_vm(child
, addr
,
277 &tmp64
, sizeof(tmp64
), 1);
278 if (copied
== sizeof(tmp64
))
282 pt_error_return(regs
, -res
);
284 pt_succ_return(regs
, res
);
288 case PTRACE_GETREGS
: {
289 struct pt_regs32
*pregs
= (struct pt_regs32
*) addr
;
290 struct pt_regs
*cregs
= child
->thread
.kregs
;
293 if (__put_user(tstate_to_psr(cregs
->tstate
), (&pregs
->psr
)) ||
294 __put_user(cregs
->tpc
, (&pregs
->pc
)) ||
295 __put_user(cregs
->tnpc
, (&pregs
->npc
)) ||
296 __put_user(cregs
->y
, (&pregs
->y
))) {
297 pt_error_return(regs
, EFAULT
);
300 for(rval
= 1; rval
< 16; rval
++)
301 if (__put_user(cregs
->u_regs
[rval
], (&pregs
->u_regs
[rval
- 1]))) {
302 pt_error_return(regs
, EFAULT
);
305 pt_succ_return(regs
, 0);
307 printk ("PC=%lx nPC=%lx o7=%lx\n", cregs
->tpc
, cregs
->tnpc
, cregs
->u_regs
[15]);
312 case PTRACE_GETREGS64
: {
313 struct pt_regs
*pregs
= (struct pt_regs
*) addr
;
314 struct pt_regs
*cregs
= child
->thread
.kregs
;
317 if (__put_user(cregs
->tstate
, (&pregs
->tstate
)) ||
318 __put_user(cregs
->tpc
, (&pregs
->tpc
)) ||
319 __put_user(cregs
->tnpc
, (&pregs
->tnpc
)) ||
320 __put_user(cregs
->y
, (&pregs
->y
))) {
321 pt_error_return(regs
, EFAULT
);
324 for(rval
= 1; rval
< 16; rval
++)
325 if (__put_user(cregs
->u_regs
[rval
], (&pregs
->u_regs
[rval
- 1]))) {
326 pt_error_return(regs
, EFAULT
);
329 pt_succ_return(regs
, 0);
331 printk ("PC=%lx nPC=%lx o7=%lx\n", cregs
->tpc
, cregs
->tnpc
, cregs
->u_regs
[15]);
336 case PTRACE_SETREGS
: {
337 struct pt_regs32
*pregs
= (struct pt_regs32
*) addr
;
338 struct pt_regs
*cregs
= child
->thread
.kregs
;
339 unsigned int psr
, pc
, npc
, y
;
342 /* Must be careful, tracing process can only set certain
345 if (__get_user(psr
, (&pregs
->psr
)) ||
346 __get_user(pc
, (&pregs
->pc
)) ||
347 __get_user(npc
, (&pregs
->npc
)) ||
348 __get_user(y
, (&pregs
->y
))) {
349 pt_error_return(regs
, EFAULT
);
352 cregs
->tstate
&= ~(TSTATE_ICC
);
353 cregs
->tstate
|= psr_to_tstate_icc(psr
);
354 if(!((pc
| npc
) & 3)) {
359 for(i
= 1; i
< 16; i
++) {
360 if (__get_user(cregs
->u_regs
[i
], (&pregs
->u_regs
[i
-1]))) {
361 pt_error_return(regs
, EFAULT
);
365 pt_succ_return(regs
, 0);
369 case PTRACE_SETREGS64
: {
370 struct pt_regs
*pregs
= (struct pt_regs
*) addr
;
371 struct pt_regs
*cregs
= child
->thread
.kregs
;
372 unsigned long tstate
, tpc
, tnpc
, y
;
375 /* Must be careful, tracing process can only set certain
378 if (__get_user(tstate
, (&pregs
->tstate
)) ||
379 __get_user(tpc
, (&pregs
->tpc
)) ||
380 __get_user(tnpc
, (&pregs
->tnpc
)) ||
381 __get_user(y
, (&pregs
->y
))) {
382 pt_error_return(regs
, EFAULT
);
385 tstate
&= (TSTATE_ICC
| TSTATE_XCC
);
386 cregs
->tstate
&= ~(TSTATE_ICC
| TSTATE_XCC
);
387 cregs
->tstate
|= tstate
;
388 if(!((tpc
| tnpc
) & 3)) {
393 for(i
= 1; i
< 16; i
++) {
394 if (__get_user(cregs
->u_regs
[i
], (&pregs
->u_regs
[i
-1]))) {
395 pt_error_return(regs
, EFAULT
);
399 pt_succ_return(regs
, 0);
403 case PTRACE_GETFPREGS
: {
405 unsigned int regs
[32];
411 unsigned int insnaddr
;
414 } *fps
= (struct fps
*) addr
;
415 unsigned long *fpregs
= (unsigned long *)(((char *)child
) + AOFF_task_fpregs
);
417 if (copy_to_user(&fps
->regs
[0], fpregs
,
418 (32 * sizeof(unsigned int))) ||
419 __put_user(child
->thread
.xfsr
[0], (&fps
->fsr
)) ||
420 __put_user(0, (&fps
->fpqd
)) ||
421 __put_user(0, (&fps
->flags
)) ||
422 __put_user(0, (&fps
->extra
)) ||
423 clear_user(&fps
->fpq
[0], 32 * sizeof(unsigned int))) {
424 pt_error_return(regs
, EFAULT
);
427 pt_succ_return(regs
, 0);
431 case PTRACE_GETFPREGS64
: {
433 unsigned int regs
[64];
435 } *fps
= (struct fps
*) addr
;
436 unsigned long *fpregs
= (unsigned long *)(((char *)child
) + AOFF_task_fpregs
);
438 if (copy_to_user(&fps
->regs
[0], fpregs
,
439 (64 * sizeof(unsigned int))) ||
440 __put_user(child
->thread
.xfsr
[0], (&fps
->fsr
))) {
441 pt_error_return(regs
, EFAULT
);
444 pt_succ_return(regs
, 0);
448 case PTRACE_SETFPREGS
: {
450 unsigned int regs
[32];
456 unsigned int insnaddr
;
459 } *fps
= (struct fps
*) addr
;
460 unsigned long *fpregs
= (unsigned long *)(((char *)child
) + AOFF_task_fpregs
);
463 if (copy_from_user(fpregs
, &fps
->regs
[0],
464 (32 * sizeof(unsigned int))) ||
465 __get_user(fsr
, (&fps
->fsr
))) {
466 pt_error_return(regs
, EFAULT
);
469 child
->thread
.xfsr
[0] &= 0xffffffff00000000UL
;
470 child
->thread
.xfsr
[0] |= fsr
;
471 if (!(child
->thread
.fpsaved
[0] & FPRS_FEF
))
472 child
->thread
.gsr
[0] = 0;
473 child
->thread
.fpsaved
[0] |= (FPRS_FEF
| FPRS_DL
);
474 pt_succ_return(regs
, 0);
478 case PTRACE_SETFPREGS64
: {
480 unsigned int regs
[64];
482 } *fps
= (struct fps
*) addr
;
483 unsigned long *fpregs
= (unsigned long *)(((char *)child
) + AOFF_task_fpregs
);
485 if (copy_from_user(fpregs
, &fps
->regs
[0],
486 (64 * sizeof(unsigned int))) ||
487 __get_user(child
->thread
.xfsr
[0], (&fps
->fsr
))) {
488 pt_error_return(regs
, EFAULT
);
491 if (!(child
->thread
.fpsaved
[0] & FPRS_FEF
))
492 child
->thread
.gsr
[0] = 0;
493 child
->thread
.fpsaved
[0] |= (FPRS_FEF
| FPRS_DL
| FPRS_DU
);
494 pt_succ_return(regs
, 0);
498 case PTRACE_READTEXT
:
499 case PTRACE_READDATA
: {
500 int res
= ptrace_readdata(child
, addr
,
501 (void *)addr2
, data
);
503 pt_succ_return(regs
, 0);
508 pt_error_return(regs
, -res
);
512 case PTRACE_WRITETEXT
:
513 case PTRACE_WRITEDATA
: {
514 int res
= ptrace_writedata(child
, (void *) addr2
,
517 pt_succ_return(regs
, 0);
522 pt_error_return(regs
, -res
);
525 case PTRACE_SYSCALL
: /* continue and stop at (return from) syscall */
528 case PTRACE_CONT
: { /* restart after signal. */
530 pt_error_return(regs
, EIO
);
535 pt_error_return(regs
, EINVAL
);
539 printk ("Original: %016lx %016lx\n", child
->thread
.kregs
->tpc
, child
->thread
.kregs
->tnpc
);
540 printk ("Continuing with %016lx %016lx\n", addr
, addr
+4);
542 child
->thread
.kregs
->tpc
= addr
;
543 child
->thread
.kregs
->tnpc
= addr
+ 4;
546 if (request
== PTRACE_SYSCALL
)
547 child
->ptrace
|= PT_TRACESYS
;
549 child
->ptrace
&= ~PT_TRACESYS
;
551 child
->exit_code
= data
;
553 printk("CONT: %s [%d]: set exit_code = %x %lx %lx\n", child
->comm
,
554 child
->pid
, child
->exit_code
,
555 child
->thread
.kregs
->tpc
,
556 child
->thread
.kregs
->tnpc
);
559 wake_up_process(child
);
560 pt_succ_return(regs
, 0);
565 * make the child exit. Best I can do is send it a sigkill.
566 * perhaps it should be put in the status that it wants to
570 if (child
->state
== TASK_ZOMBIE
) { /* already dead */
571 pt_succ_return(regs
, 0);
574 child
->exit_code
= SIGKILL
;
575 wake_up_process(child
);
576 pt_succ_return(regs
, 0);
580 case PTRACE_SUNDETACH
: { /* detach a process that was attached. */
583 if ((unsigned long) data
> _NSIG
) {
584 pt_error_return(regs
, EIO
);
587 child
->ptrace
&= ~(PT_PTRACED
|PT_TRACESYS
);
588 child
->exit_code
= data
;
590 write_lock_irqsave(&tasklist_lock
, flags
);
592 child
->p_pptr
= child
->p_opptr
;
594 write_unlock_irqrestore(&tasklist_lock
, flags
);
596 wake_up_process(child
);
597 pt_succ_return(regs
, 0);
601 /* PTRACE_DUMPCORE unsupported... */
604 pt_error_return(regs
, EIO
);
610 for(va
= 0; va
< (PAGE_SIZE
<< 1); va
+= 32)
611 spitfire_put_dcache_tag(va
, 0x0);
612 if (request
== PTRACE_PEEKTEXT
||
613 request
== PTRACE_POKETEXT
||
614 request
== PTRACE_READTEXT
||
615 request
== PTRACE_WRITETEXT
) {
616 for(va
= 0; va
< (PAGE_SIZE
<< 1); va
+= 32)
617 spitfire_put_icache_tag(va
, 0x0);
618 __asm__
__volatile__("flush %g6");
625 asmlinkage
void syscall_trace(void)
628 printk("%s [%d]: syscall_trace\n", current
->comm
, current
->pid
);
630 if ((current
->ptrace
& (PT_PTRACED
|PT_TRACESYS
))
631 != (PT_PTRACED
|PT_TRACESYS
))
633 current
->exit_code
= SIGTRAP
;
634 current
->state
= TASK_STOPPED
;
635 current
->thread
.flags
^= MAGIC_CONSTANT
;
636 notify_parent(current
, SIGCHLD
);
639 * this isn't the same as continuing with a signal, but it will do
640 * for normal use. strace only continues with a signal if the
641 * stopping signal is not SIGTRAP. -brl
644 printk("%s [%d]: syscall_trace exit= %x\n", current
->comm
,
645 current
->pid
, current
->exit_code
);
647 if (current
->exit_code
) {
648 send_sig (current
->exit_code
, current
, 1);
649 current
->exit_code
= 0;