v2.4.2.7 -> v2.4.2.8
[linux-flexiantxendom0-3.2.10.git] / arch / sparc / kernel / ptrace.c
1 /* ptrace.c: Sparc process tracing support.
2  *
3  * Copyright (C) 1996 David S. Miller (davem@caipfs.rutgers.edu)
4  *
5  * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson,
6  * and David Mosberger.
7  *
8  * Added Linux support -miguel (weird, eh?, the orignal code was meant
9  * 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
21 #include <asm/pgtable.h>
22 #include <asm/system.h>
23 #include <asm/uaccess.h>
24
25 #define MAGIC_CONSTANT 0x80000000
26
27
28 /* Returning from ptrace is a bit tricky because the syscall return
29  * low level code assumes any value returned which is negative and
30  * is a valid errno will mean setting the condition codes to indicate
31  * an error return.  This doesn't work, so we have this hook.
32  */
33 static inline void pt_error_return(struct pt_regs *regs, unsigned long error)
34 {
35         regs->u_regs[UREG_I0] = error;
36         regs->psr |= PSR_C;
37         regs->pc = regs->npc;
38         regs->npc += 4;
39 }
40
41 static inline void pt_succ_return(struct pt_regs *regs, unsigned long value)
42 {
43         regs->u_regs[UREG_I0] = value;
44         regs->psr &= ~PSR_C;
45         regs->pc = regs->npc;
46         regs->npc += 4;
47 }
48
49 static void
50 pt_succ_return_linux(struct pt_regs *regs, unsigned long value, long *addr)
51 {
52         if(put_user(value, addr))
53                 return pt_error_return(regs, EFAULT);
54         regs->u_regs[UREG_I0] = 0;
55         regs->psr &= ~PSR_C;
56         regs->pc = regs->npc;
57         regs->npc += 4;
58 }
59
60 static void
61 pt_os_succ_return (struct pt_regs *regs, unsigned long val, long *addr)
62 {
63         if (current->personality == PER_SUNOS)
64                 pt_succ_return (regs, val);
65         else
66                 pt_succ_return_linux (regs, val, addr);
67 }
68
69 /* Fuck me gently with a chainsaw... */
70 static inline void read_sunos_user(struct pt_regs *regs, unsigned long offset,
71                                    struct task_struct *tsk, long *addr)
72 {
73         struct pt_regs *cregs = tsk->thread.kregs;
74         struct thread_struct *t = &tsk->thread;
75         int v;
76         
77         if(offset >= 1024)
78                 offset -= 1024; /* whee... */
79         if(offset & ((sizeof(unsigned long) - 1))) {
80                 pt_error_return(regs, EIO);
81                 return;
82         }
83         if(offset >= 16 && offset < 784) {
84                 offset -= 16; offset >>= 2;
85                 pt_os_succ_return(regs, *(((unsigned long *)(&t->reg_window[0]))+offset), addr);
86                 return;
87         }
88         if(offset >= 784 && offset < 832) {
89                 offset -= 784; offset >>= 2;
90                 pt_os_succ_return(regs, *(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset), addr);
91                 return;
92         }
93         switch(offset) {
94         case 0:
95                 v = t->ksp;
96                 break;
97         case 4:
98                 v = t->kpc;
99                 break;
100         case 8:
101                 v = t->kpsr;
102                 break;
103         case 12:
104                 v = t->uwinmask;
105                 break;
106         case 832:
107                 v = t->w_saved;
108                 break;
109         case 896:
110                 v = cregs->u_regs[UREG_I0];
111                 break;
112         case 900:
113                 v = cregs->u_regs[UREG_I1];
114                 break;
115         case 904:
116                 v = cregs->u_regs[UREG_I2];
117                 break;
118         case 908:
119                 v = cregs->u_regs[UREG_I3];
120                 break;
121         case 912:
122                 v = cregs->u_regs[UREG_I4];
123                 break;
124         case 916:
125                 v = cregs->u_regs[UREG_I5];
126                 break;
127         case 920:
128                 v = cregs->u_regs[UREG_I6];
129                 break;
130         case 924:
131                 if(tsk->thread.flags & MAGIC_CONSTANT)
132                         v = cregs->u_regs[UREG_G1];
133                 else
134                         v = 0;
135                 break;
136         case 940:
137                 v = cregs->u_regs[UREG_I0];
138                 break;
139         case 944:
140                 v = cregs->u_regs[UREG_I1];
141                 break;
142
143         case 948:
144                 /* Isn't binary compatibility _fun_??? */
145                 if(cregs->psr & PSR_C)
146                         v = cregs->u_regs[UREG_I0] << 24;
147                 else
148                         v = 0;
149                 break;
150
151                 /* Rest of them are completely unsupported. */
152         default:
153                 printk("%s [%d]: Wants to read user offset %ld\n",
154                        current->comm, current->pid, offset);
155                 pt_error_return(regs, EIO);
156                 return;
157         }
158         if (current->personality == PER_SUNOS)
159                 pt_succ_return (regs, v);
160         else
161                 pt_succ_return_linux (regs, v, addr);
162         return;
163 }
164
165 static inline void write_sunos_user(struct pt_regs *regs, unsigned long offset,
166                                     struct task_struct *tsk)
167 {
168         struct pt_regs *cregs = tsk->thread.kregs;
169         struct thread_struct *t = &tsk->thread;
170         unsigned long value = regs->u_regs[UREG_I3];
171
172         if(offset >= 1024)
173                 offset -= 1024; /* whee... */
174         if(offset & ((sizeof(unsigned long) - 1)))
175                 goto failure;
176         if(offset >= 16 && offset < 784) {
177                 offset -= 16; offset >>= 2;
178                 *(((unsigned long *)(&t->reg_window[0]))+offset) = value;
179                 goto success;
180         }
181         if(offset >= 784 && offset < 832) {
182                 offset -= 784; offset >>= 2;
183                 *(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset) = value;
184                 goto success;
185         }
186         switch(offset) {
187         case 896:
188                 cregs->u_regs[UREG_I0] = value;
189                 break;
190         case 900:
191                 cregs->u_regs[UREG_I1] = value;
192                 break;
193         case 904:
194                 cregs->u_regs[UREG_I2] = value;
195                 break;
196         case 908:
197                 cregs->u_regs[UREG_I3] = value;
198                 break;
199         case 912:
200                 cregs->u_regs[UREG_I4] = value;
201                 break;
202         case 916:
203                 cregs->u_regs[UREG_I5] = value;
204                 break;
205         case 920:
206                 cregs->u_regs[UREG_I6] = value;
207                 break;
208         case 924:
209                 cregs->u_regs[UREG_I7] = value;
210                 break;
211         case 940:
212                 cregs->u_regs[UREG_I0] = value;
213                 break;
214         case 944:
215                 cregs->u_regs[UREG_I1] = value;
216                 break;
217
218                 /* Rest of them are completely unsupported or "no-touch". */
219         default:
220                 printk("%s [%d]: Wants to write user offset %ld\n",
221                        current->comm, current->pid, offset);
222                 goto failure;
223         }
224 success:
225         pt_succ_return(regs, 0);
226         return;
227 failure:
228         pt_error_return(regs, EIO);
229         return;
230 }
231
232 /* #define ALLOW_INIT_TRACING */
233 /* #define DEBUG_PTRACE */
234
235 #ifdef DEBUG_PTRACE
236 char *pt_rq [] = {
237 "TRACEME",
238 "PEEKTEXT",
239 "PEEKDATA",
240 "PEEKUSR",
241 "POKETEXT",
242 "POKEDATA",
243 "POKEUSR",
244 "CONT",
245 "KILL",
246 "SINGLESTEP",
247 "SUNATTACH",
248 "SUNDETACH",
249 "GETREGS",
250 "SETREGS",
251 "GETFPREGS",
252 "SETFPREGS",
253 "READDATA",
254 "WRITEDATA",
255 "READTEXT",
256 "WRITETEXT",
257 "GETFPAREGS",
258 "SETFPAREGS",
259 ""
260 };
261 #endif
262
263 asmlinkage void do_ptrace(struct pt_regs *regs)
264 {
265         unsigned long request = regs->u_regs[UREG_I0];
266         unsigned long pid = regs->u_regs[UREG_I1];
267         unsigned long addr = regs->u_regs[UREG_I2];
268         unsigned long data = regs->u_regs[UREG_I3];
269         unsigned long addr2 = regs->u_regs[UREG_I4];
270         struct task_struct *child;
271
272         lock_kernel();
273 #ifdef DEBUG_PTRACE
274         {
275                 char *s;
276
277                 if ((request > 0) && (request < 21))
278                         s = pt_rq [request];
279                 else
280                         s = "unknown";
281
282                 if (request == PTRACE_POKEDATA && data == 0x91d02001){
283                         printk ("do_ptrace: breakpoint pid=%d, addr=%08lx addr2=%08lx\n",
284                                 pid, addr, addr2);
285                 } else 
286                         printk("do_ptrace: rq=%s(%d) pid=%d addr=%08lx data=%08lx addr2=%08lx\n",
287                                s, (int) request, (int) pid, addr, data, addr2);
288         }
289 #endif
290         if(request == PTRACE_TRACEME) {
291                 /* are we already being traced? */
292                 if (current->ptrace & PT_PTRACED) {
293                         pt_error_return(regs, EPERM);
294                         goto out;
295                 }
296                 /* set the ptrace bit in the process flags. */
297                 current->ptrace |= PT_PTRACED;
298                 pt_succ_return(regs, 0);
299                 goto out;
300         }
301 #ifndef ALLOW_INIT_TRACING
302         if(pid == 1) {
303                 /* Can't dork with init. */
304                 pt_error_return(regs, EPERM);
305                 goto out;
306         }
307 #endif
308         read_lock(&tasklist_lock);
309         child = find_task_by_pid(pid);
310         if (child)
311                 get_task_struct(child);
312         read_unlock(&tasklist_lock);
313
314         if (!child) {
315                 pt_error_return(regs, ESRCH);
316                 goto out;
317         }
318
319         if ((current->personality == PER_SUNOS && request == PTRACE_SUNATTACH)
320             || (current->personality != PER_SUNOS && request == PTRACE_ATTACH)) {
321                 unsigned long flags;
322
323                 if(child == current) {
324                         /* Try this under SunOS/Solaris, bwa haha
325                          * You'll never be able to kill the process. ;-)
326                          */
327                         pt_error_return(regs, EPERM);
328                         goto out_tsk;
329                 }
330                 if((!child->dumpable ||
331                     (current->uid != child->euid) ||
332                     (current->uid != child->uid) ||
333                     (current->uid != child->suid) ||
334                     (current->gid != child->egid) ||
335                     (current->gid != child->sgid) || 
336                     (!cap_issubset(child->cap_permitted, current->cap_permitted)) ||
337                     (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE)) {
338                         pt_error_return(regs, EPERM);
339                         goto out_tsk;
340                 }
341                 /* the same process cannot be attached many times */
342                 if (child->ptrace & PT_PTRACED) {
343                         pt_error_return(regs, EPERM);
344                         goto out_tsk;
345                 }
346                 child->ptrace |= PT_PTRACED;
347                 write_lock_irqsave(&tasklist_lock, flags);
348                 if(child->p_pptr != current) {
349                         REMOVE_LINKS(child);
350                         child->p_pptr = current;
351                         SET_LINKS(child);
352                 }
353                 write_unlock_irqrestore(&tasklist_lock, flags);
354                 send_sig(SIGSTOP, child, 1);
355                 pt_succ_return(regs, 0);
356                 goto out_tsk;
357         }
358         if (!(child->ptrace & PT_PTRACED)) {
359                 pt_error_return(regs, ESRCH);
360                 goto out_tsk;
361         }
362         if(child->state != TASK_STOPPED) {
363                 if(request != PTRACE_KILL) {
364                         pt_error_return(regs, ESRCH);
365                         goto out_tsk;
366                 }
367         }
368         if(child->p_pptr != current) {
369                 pt_error_return(regs, ESRCH);
370                 goto out_tsk;
371         }
372         switch(request) {
373         case PTRACE_PEEKTEXT: /* read word at location addr. */ 
374         case PTRACE_PEEKDATA: {
375                 unsigned long tmp;
376
377                 if (access_process_vm(child, addr,
378                                       &tmp, sizeof(tmp), 0) == sizeof(tmp))
379                         pt_os_succ_return(regs, tmp, (long *)data);
380                 else
381                         pt_error_return(regs, EIO);
382                 goto out_tsk;
383         }
384
385         case PTRACE_PEEKUSR:
386                 read_sunos_user(regs, addr, child, (long *) data);
387                 goto out_tsk;
388
389         case PTRACE_POKEUSR:
390                 write_sunos_user(regs, addr, child);
391                 goto out_tsk;
392
393         case PTRACE_POKETEXT: /* write the word at location addr. */
394         case PTRACE_POKEDATA: {
395                 if (access_process_vm(child, addr,
396                                       &data, sizeof(data), 1) == sizeof(data))
397                         pt_succ_return(regs, 0);
398                 else
399                         pt_error_return(regs, EIO);
400                 goto out_tsk;
401         }
402
403         case PTRACE_GETREGS: {
404                 struct pt_regs *pregs = (struct pt_regs *) addr;
405                 struct pt_regs *cregs = child->thread.kregs;
406                 int rval;
407
408                 rval = verify_area(VERIFY_WRITE, pregs, sizeof(struct pt_regs));
409                 if(rval) {
410                         pt_error_return(regs, -rval);
411                         goto out_tsk;
412                 }
413                 __put_user(cregs->psr, (&pregs->psr));
414                 __put_user(cregs->pc, (&pregs->pc));
415                 __put_user(cregs->npc, (&pregs->npc));
416                 __put_user(cregs->y, (&pregs->y));
417                 for(rval = 1; rval < 16; rval++)
418                         __put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]));
419                 pt_succ_return(regs, 0);
420 #ifdef DEBUG_PTRACE
421                 printk ("PC=%x nPC=%x o7=%x\n", cregs->pc, cregs->npc, cregs->u_regs [15]);
422 #endif
423                 goto out_tsk;
424         }
425
426         case PTRACE_SETREGS: {
427                 struct pt_regs *pregs = (struct pt_regs *) addr;
428                 struct pt_regs *cregs = child->thread.kregs;
429                 unsigned long psr, pc, npc, y;
430                 int i;
431
432                 /* Must be careful, tracing process can only set certain
433                  * bits in the psr.
434                  */
435                 i = verify_area(VERIFY_READ, pregs, sizeof(struct pt_regs));
436                 if(i) {
437                         pt_error_return(regs, -i);
438                         goto out_tsk;
439                 }
440                 __get_user(psr, (&pregs->psr));
441                 __get_user(pc, (&pregs->pc));
442                 __get_user(npc, (&pregs->npc));
443                 __get_user(y, (&pregs->y));
444                 psr &= PSR_ICC;
445                 cregs->psr &= ~PSR_ICC;
446                 cregs->psr |= psr;
447                 if(!((pc | npc) & 3)) {
448                         cregs->pc = pc;
449                         cregs->npc =npc;
450                 }
451                 cregs->y = y;
452                 for(i = 1; i < 16; i++)
453                         __get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]));
454                 pt_succ_return(regs, 0);
455                 goto out_tsk;
456         }
457
458         case PTRACE_GETFPREGS: {
459                 struct fps {
460                         unsigned long regs[32];
461                         unsigned long fsr;
462                         unsigned long flags;
463                         unsigned long extra;
464                         unsigned long fpqd;
465                         struct fq {
466                                 unsigned long *insnaddr;
467                                 unsigned long insn;
468                         } fpq[16];
469                 } *fps = (struct fps *) addr;
470                 int i;
471
472                 i = verify_area(VERIFY_WRITE, fps, sizeof(struct fps));
473                 if(i) {
474                         pt_error_return(regs, -i);
475                         goto out_tsk;
476                 }
477                 for(i = 0; i < 32; i++)
478                         __put_user(child->thread.float_regs[i], (&fps->regs[i]));
479                 __put_user(child->thread.fsr, (&fps->fsr));
480                 __put_user(child->thread.fpqdepth, (&fps->fpqd));
481                 __put_user(0, (&fps->flags));
482                 __put_user(0, (&fps->extra));
483                 for(i = 0; i < 16; i++) {
484                         __put_user(child->thread.fpqueue[i].insn_addr,
485                                    (&fps->fpq[i].insnaddr));
486                         __put_user(child->thread.fpqueue[i].insn, (&fps->fpq[i].insn));
487                 }
488                 pt_succ_return(regs, 0);
489                 goto out_tsk;
490         }
491
492         case PTRACE_SETFPREGS: {
493                 struct fps {
494                         unsigned long regs[32];
495                         unsigned long fsr;
496                         unsigned long flags;
497                         unsigned long extra;
498                         unsigned long fpqd;
499                         struct fq {
500                                 unsigned long *insnaddr;
501                                 unsigned long insn;
502                         } fpq[16];
503                 } *fps = (struct fps *) addr;
504                 int i;
505
506                 i = verify_area(VERIFY_READ, fps, sizeof(struct fps));
507                 if(i) {
508                         pt_error_return(regs, -i);
509                         goto out_tsk;
510                 }
511                 copy_from_user(&child->thread.float_regs[0], &fps->regs[0], (32 * sizeof(unsigned long)));
512                 __get_user(child->thread.fsr, (&fps->fsr));
513                 __get_user(child->thread.fpqdepth, (&fps->fpqd));
514                 for(i = 0; i < 16; i++) {
515                         __get_user(child->thread.fpqueue[i].insn_addr,
516                                    (&fps->fpq[i].insnaddr));
517                         __get_user(child->thread.fpqueue[i].insn, (&fps->fpq[i].insn));
518                 }
519                 pt_succ_return(regs, 0);
520                 goto out_tsk;
521         }
522
523         case PTRACE_READTEXT:
524         case PTRACE_READDATA: {
525                 int res = ptrace_readdata(child, addr, (void *) addr2, data);
526
527                 if (res == data) {
528                         pt_succ_return(regs, 0);
529                         goto out_tsk;
530                 }
531                 /* Partial read is an IO failure */
532                 if (res >= 0)
533                         res = -EIO;
534                 pt_error_return(regs, -res);
535                 goto out_tsk;
536         }
537
538         case PTRACE_WRITETEXT:
539         case PTRACE_WRITEDATA: {
540                 int res = ptrace_writedata(child, (void *) addr2, addr, data);
541
542                 if (res == data) {
543                         pt_succ_return(regs, 0);
544                         goto out_tsk;
545                 }
546                 /* Partial write is an IO failure */
547                 if (res >= 0)
548                         res = -EIO;
549                 pt_error_return(regs, -res);
550                 goto out_tsk;
551         }
552
553         case PTRACE_SYSCALL: /* continue and stop at (return from) syscall */
554                 addr = 1;
555
556         case PTRACE_CONT: { /* restart after signal. */
557                 if ((unsigned long) data > _NSIG) {
558                         pt_error_return(regs, EIO);
559                         goto out_tsk;
560                 }
561                 if (addr != 1) {
562                         if (addr & 3) {
563                                 pt_error_return(regs, EINVAL);
564                                 goto out_tsk;
565                         }
566 #ifdef DEBUG_PTRACE
567                         printk ("Original: %08lx %08lx\n", child->thread.kregs->pc, child->thread.kregs->npc);
568                         printk ("Continuing with %08lx %08lx\n", addr, addr+4);
569 #endif
570                         child->thread.kregs->pc = addr;
571                         child->thread.kregs->npc = addr + 4;
572                 }
573
574                 if (request == PTRACE_SYSCALL)
575                         child->ptrace |= PT_TRACESYS;
576                 else
577                         child->ptrace &= ~PT_TRACESYS;
578
579                 child->exit_code = data;
580 #ifdef DEBUG_PTRACE
581                 printk("CONT: %s [%d]: set exit_code = %x %x %x\n", child->comm,
582                         child->pid, child->exit_code,
583                         child->thread.kregs->pc,
584                         child->thread.kregs->npc);
585                        
586 #endif
587                 wake_up_process(child);
588                 pt_succ_return(regs, 0);
589                 goto out_tsk;
590         }
591
592 /*
593  * make the child exit.  Best I can do is send it a sigkill. 
594  * perhaps it should be put in the status that it wants to 
595  * exit.
596  */
597         case PTRACE_KILL: {
598                 if (child->state == TASK_ZOMBIE) {      /* already dead */
599                         pt_succ_return(regs, 0);
600                         goto out_tsk;
601                 }
602                 wake_up_process(child);
603                 child->exit_code = SIGKILL;
604                 pt_succ_return(regs, 0);
605                 goto out_tsk;
606         }
607
608         case PTRACE_SUNDETACH: { /* detach a process that was attached. */
609                 unsigned long flags;
610                 if ((unsigned long) data > _NSIG) {
611                         pt_error_return(regs, EIO);
612                         goto out_tsk;
613                 }
614                 child->ptrace &= ~(PT_PTRACED|PT_TRACESYS);
615                 wake_up_process(child);
616                 child->exit_code = data;
617                 write_lock_irqsave(&tasklist_lock, flags);
618                 REMOVE_LINKS(child);
619                 child->p_pptr = child->p_opptr;
620                 SET_LINKS(child);
621                 write_unlock_irqrestore(&tasklist_lock, flags);
622                 pt_succ_return(regs, 0);
623                 goto out_tsk;
624         }
625
626         /* PTRACE_DUMPCORE unsupported... */
627
628         default:
629                 pt_error_return(regs, EIO);
630                 goto out_tsk;
631         }
632 out_tsk:
633         if (child)
634                 free_task_struct(child);
635 out:
636         unlock_kernel();
637 }
638
639 asmlinkage void syscall_trace(void)
640 {
641 #ifdef DEBUG_PTRACE
642         printk("%s [%d]: syscall_trace\n", current->comm, current->pid);
643 #endif
644         if ((current->ptrace & (PT_PTRACED|PT_TRACESYS))
645                         != (PT_PTRACED|PT_TRACESYS))
646                 return;
647         current->exit_code = SIGTRAP;
648         current->state = TASK_STOPPED;
649         current->thread.flags ^= MAGIC_CONSTANT;
650         notify_parent(current, SIGCHLD);
651         schedule();
652         /*
653          * this isn't the same as continuing with a signal, but it will do
654          * for normal use.  strace only continues with a signal if the
655          * stopping signal is not SIGTRAP.  -brl
656          */
657 #ifdef DEBUG_PTRACE
658         printk("%s [%d]: syscall_trace exit= %x\n", current->comm,
659                 current->pid, current->exit_code);
660 #endif
661         if (current->exit_code) {
662                 send_sig (current->exit_code, current, 1);
663                 current->exit_code = 0;
664         }
665 }