[PATCH] unify sys_ptrace prototype
[linux-2.6.git] / arch / xtensa / kernel / ptrace.c
1 // TODO some minor issues
2 /*
3  * This file is subject to the terms and conditions of the GNU General Public
4  * License.  See the file "COPYING" in the main directory of this archive
5  * for more details.
6  *
7  * Copyright (C) 2001 - 2005  Tensilica Inc.
8  *
9  * Joe Taylor   <joe@tensilica.com, joetylr@yahoo.com>
10  * Chris Zankel <chris@zankel.net>
11  * Scott Foehner<sfoehner@yahoo.com>,
12  * Kevin Chea
13  * Marc Gauthier<marc@tensilica.com> <marc@alumni.uwaterloo.ca>
14  */
15
16 #include <linux/config.h>
17 #include <linux/kernel.h>
18 #include <linux/sched.h>
19 #include <linux/mm.h>
20 #include <linux/errno.h>
21 #include <linux/ptrace.h>
22 #include <linux/smp.h>
23 #include <linux/smp_lock.h>
24 #include <linux/security.h>
25 #include <linux/signal.h>
26
27 #include <asm/pgtable.h>
28 #include <asm/page.h>
29 #include <asm/system.h>
30 #include <asm/uaccess.h>
31 #include <asm/ptrace.h>
32 #include <asm/elf.h>
33
34 #define TEST_KERNEL     // verify kernel operations FIXME: remove
35
36
37 /*
38  * Called by kernel/ptrace.c when detaching..
39  *
40  * Make sure single step bits etc are not set.
41  */
42
43 void ptrace_disable(struct task_struct *child)
44 {
45         /* Nothing to do.. */
46 }
47
48 long sys_ptrace(long request, long pid, long addr, long data)
49 {
50         struct task_struct *child;
51         int ret = -EPERM;
52
53         lock_kernel();
54
55 #if 0
56         if ((int)request != 1)
57         printk("ptrace(r=%d,pid=%d,addr=%08lx,data=%08lx)\n",
58                (int) request, (int) pid, (unsigned long) addr,
59                (unsigned long) data);
60 #endif
61
62         if (request == PTRACE_TRACEME) {
63
64                 /* Are we already being traced? */
65
66                 if (current->ptrace & PT_PTRACED)
67                         goto out;
68
69                 if ((ret = security_ptrace(current->parent, current)))
70                         goto out;
71
72                 /* Set the ptrace bit in the process flags. */
73
74                 current->ptrace |= PT_PTRACED;
75                 ret = 0;
76                 goto out;
77         }
78
79         ret = -ESRCH;
80         read_lock(&tasklist_lock);
81         child = find_task_by_pid(pid);
82         if (child)
83                 get_task_struct(child);
84         read_unlock(&tasklist_lock);
85         if (!child)
86                 goto out;
87
88         ret = -EPERM;
89         if (pid == 1)           /* you may not mess with init */
90                 goto out;
91
92         if (request == PTRACE_ATTACH) {
93                 ret = ptrace_attach(child);
94                 goto out_tsk;
95         }
96
97         if ((ret = ptrace_check_attach(child, request == PTRACE_KILL)) < 0)
98                 goto out_tsk;
99
100         switch (request) {
101         case PTRACE_PEEKTEXT: /* read word at location addr. */
102         case PTRACE_PEEKDATA:
103         {
104                 unsigned long tmp;
105                 int copied;
106
107                 copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
108                 ret = -EIO;
109                 if (copied != sizeof(tmp))
110                         break;
111                 ret = put_user(tmp,(unsigned long *) data);
112
113                 goto out;
114         }
115
116         /* Read the word at location addr in the USER area.  */
117
118         case PTRACE_PEEKUSR:
119                 {
120                 struct pt_regs *regs;
121                 unsigned long tmp;
122
123                 regs = xtensa_pt_regs(child);
124                 tmp = 0;  /* Default return value. */
125
126                 switch(addr) {
127
128                 case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1:
129                         {
130                         int ar = addr - REG_AR_BASE - regs->windowbase * 4;
131                         ar &= (XCHAL_NUM_AREGS - 1);
132                         if (ar < 16 && ar + (regs->wmask >> 4) * 4 >= 0)
133                                 tmp = regs->areg[ar];
134                         else
135                                 ret = -EIO;
136                         break;
137                         }
138                 case REG_A_BASE ... REG_A_BASE + 15:
139                         tmp = regs->areg[addr - REG_A_BASE];
140                         break;
141                 case REG_PC:
142                         tmp = regs->pc;
143                         break;
144                 case REG_PS:
145                         /* Note:  PS.EXCM is not set while user task is running;
146                          * its being set in regs is for exception handling
147                          * convenience.  */
148                         tmp = (regs->ps & ~XCHAL_PS_EXCM_MASK);
149                         break;
150                 case REG_WB:
151                         tmp = regs->windowbase;
152                         break;
153                 case REG_WS:
154                         tmp = regs->windowstart;
155                         break;
156                 case REG_LBEG:
157                         tmp = regs->lbeg;
158                         break;
159                 case REG_LEND:
160                         tmp = regs->lend;
161                         break;
162                 case REG_LCOUNT:
163                         tmp = regs->lcount;
164                         break;
165                 case REG_SAR:
166                         tmp = regs->sar;
167                         break;
168                 case REG_DEPC:
169                         tmp = regs->depc;
170                         break;
171                 case REG_EXCCAUSE:
172                         tmp = regs->exccause;
173                         break;
174                 case REG_EXCVADDR:
175                         tmp = regs->excvaddr;
176                         break;
177                 case SYSCALL_NR:
178                         tmp = regs->syscall;
179                         break;
180                 default:
181                         tmp = 0;
182                         ret = -EIO;
183                         goto out;
184                 }
185                 ret = put_user(tmp, (unsigned long *) data);
186                 goto out;
187                 }
188
189         case PTRACE_POKETEXT: /* write the word at location addr. */
190         case PTRACE_POKEDATA:
191                 if (access_process_vm(child, addr, &data, sizeof(data), 1)
192                     == sizeof(data))
193                         break;
194                 ret = -EIO;
195                 goto out;
196
197         case PTRACE_POKEUSR:
198                 {
199                 struct pt_regs *regs;
200                 regs = xtensa_pt_regs(child);
201
202                 switch (addr) {
203                 case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1:
204                         {
205                         int ar = addr - REG_AR_BASE - regs->windowbase * 4;
206                         if (ar < 16 && ar + (regs->wmask >> 4) * 4 >= 0)
207                                 regs->areg[ar & (XCHAL_NUM_AREGS - 1)] = data;
208                         else
209                                 ret = -EIO;
210                         break;
211                         }
212                 case REG_A_BASE ... REG_A_BASE + 15:
213                         regs->areg[addr - REG_A_BASE] = data;
214                         break;
215                 case REG_PC:
216                         regs->pc = data;
217                         break;
218                 case SYSCALL_NR:
219                         regs->syscall = data;
220                         break;
221 #ifdef TEST_KERNEL
222                 case REG_WB:
223                         regs->windowbase = data;
224                         break;
225                 case REG_WS:
226                         regs->windowstart = data;
227                         break;
228 #endif
229
230                 default:
231                         /* The rest are not allowed. */
232                         ret = -EIO;
233                         break;
234                 }
235                 break;
236                 }
237
238         /* continue and stop at next (return from) syscall */
239         case PTRACE_SYSCALL:
240         case PTRACE_CONT: /* restart after signal. */
241         {
242                 ret = -EIO;
243                 if (!valid_signal(data))
244                         break;
245                 if (request == PTRACE_SYSCALL)
246                         set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
247                 else
248                         clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
249                 child->exit_code = data;
250                 /* Make sure the single step bit is not set. */
251                 child->ptrace &= ~PT_SINGLESTEP;
252                 wake_up_process(child);
253                 ret = 0;
254                 break;
255         }
256
257         /*
258          * make the child exit.  Best I can do is send it a sigkill.
259          * perhaps it should be put in the status that it wants to
260          * exit.
261          */
262         case PTRACE_KILL:
263                 ret = 0;
264                 if (child->state == EXIT_ZOMBIE)        /* already dead */
265                         break;
266                 child->exit_code = SIGKILL;
267                 child->ptrace &= ~PT_SINGLESTEP;
268                 wake_up_process(child);
269                 break;
270
271         case PTRACE_SINGLESTEP:
272                 ret = -EIO;
273                 if (!valid_signal(data))
274                         break;
275                 clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
276                 child->ptrace |= PT_SINGLESTEP;
277                 child->exit_code = data;
278                 wake_up_process(child);
279                 ret = 0;
280                 break;
281
282         case PTRACE_GETREGS:
283         {
284                 /* 'data' points to user memory in which to write.
285                  * Mainly due to the non-live register values, we
286                  * reformat the register values into something more
287                  * standard.  For convenience, we use the handy
288                  * elf_gregset_t format. */
289
290                 xtensa_gregset_t format;
291                 struct pt_regs *regs = xtensa_pt_regs(child);
292
293                 do_copy_regs (&format, regs, child);
294
295                 /* Now, copy to user space nice and easy... */
296                 ret = 0;
297                 if (copy_to_user((void *)data, &format, sizeof(elf_gregset_t)))
298                         ret = -EFAULT;
299                 break;
300         }
301
302         case PTRACE_SETREGS:
303         {
304                 /* 'data' points to user memory that contains the new
305                  * values in the elf_gregset_t format. */
306
307                 xtensa_gregset_t format;
308                 struct pt_regs *regs = xtensa_pt_regs(child);
309
310                 if (copy_from_user(&format,(void *)data,sizeof(elf_gregset_t))){
311                         ret = -EFAULT;
312                         break;
313                 }
314
315                 /* FIXME: Perhaps we want some sanity checks on
316                  * these user-space values?  See ARM version.  Are
317                  * debuggers a security concern? */
318
319                 do_restore_regs (&format, regs, child);
320
321                 ret = 0;
322                 break;
323         }
324
325         case PTRACE_GETFPREGS:
326         {
327                 /* 'data' points to user memory in which to write.
328                  * For convenience, we use the handy
329                  * elf_fpregset_t format. */
330
331                 elf_fpregset_t fpregs;
332                 struct pt_regs *regs = xtensa_pt_regs(child);
333
334                 do_save_fpregs (&fpregs, regs, child);
335
336                 /* Now, copy to user space nice and easy... */
337                 ret = 0;
338                 if (copy_to_user((void *)data, &fpregs, sizeof(elf_fpregset_t)))
339                         ret = -EFAULT;
340
341                 break;
342         }
343
344         case PTRACE_SETFPREGS:
345         {
346                 /* 'data' points to user memory that contains the new
347                  * values in the elf_fpregset_t format.
348                  */
349                 elf_fpregset_t fpregs;
350                 struct pt_regs *regs = xtensa_pt_regs(child);
351
352                 ret = 0;
353                 if (copy_from_user(&fpregs, (void *)data, sizeof(elf_fpregset_t))) {
354                         ret = -EFAULT;
355                         break;
356                 }
357
358                 if (do_restore_fpregs (&fpregs, regs, child))
359                         ret = -EIO;
360                 break;
361         }
362
363         case PTRACE_GETFPREGSIZE:
364                 /* 'data' points to 'unsigned long' set to the size
365                  * of elf_fpregset_t
366                  */
367                 ret = put_user(sizeof(elf_fpregset_t), (unsigned long *) data);
368                 break;
369
370         case PTRACE_DETACH: /* detach a process that was attached. */
371                 ret = ptrace_detach(child, data);
372                 break;
373
374         default:
375                 ret = ptrace_request(child, request, addr, data);
376                 goto out;
377         }
378 out_tsk:
379         put_task_struct(child);
380 out:
381         unlock_kernel();
382         return ret;
383 }
384
385 void do_syscall_trace(void)
386 {
387         if (!test_thread_flag(TIF_SYSCALL_TRACE))
388                 return;
389
390         if (!(current->ptrace & PT_PTRACED))
391                 return;
392
393         /*
394          * The 0x80 provides a way for the tracing parent to distinguish
395          * between a syscall stop and SIGTRAP delivery
396          */
397         ptrace_notify(SIGTRAP|((current->ptrace & PT_TRACESYSGOOD) ? 0x80 : 0));
398
399         /*
400          * this isn't the same as continuing with a signal, but it will do
401          * for normal use.  strace only continues with a signal if the
402          * stopping signal is not SIGTRAP.  -brl
403          */
404         if (current->exit_code) {
405                 send_sig(current->exit_code, current, 1);
406                 current->exit_code = 0;
407         }
408 }