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