FRV: Remove in-kernel strace code
[linux-2.6] / arch / frv / kernel / ptrace.c
1 /* ptrace.c: FRV specific parts of process tracing
2  *
3  * Copyright (C) 2003-5 Red Hat, Inc. All Rights Reserved.
4  * Written by David Howells (dhowells@redhat.com)
5  * - Derived from arch/m68k/kernel/ptrace.c
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version
10  * 2 of the License, or (at your option) any later version.
11  */
12
13 #include <linux/kernel.h>
14 #include <linux/sched.h>
15 #include <linux/mm.h>
16 #include <linux/smp.h>
17 #include <linux/errno.h>
18 #include <linux/ptrace.h>
19 #include <linux/user.h>
20 #include <linux/security.h>
21 #include <linux/signal.h>
22
23 #include <asm/uaccess.h>
24 #include <asm/page.h>
25 #include <asm/pgtable.h>
26 #include <asm/system.h>
27 #include <asm/processor.h>
28 #include <asm/unistd.h>
29
30 /*
31  * does not yet catch signals sent when the child dies.
32  * in exit.c or in signal.c.
33  */
34
35 /*
36  * Get contents of register REGNO in task TASK.
37  */
38 static inline long get_reg(struct task_struct *task, int regno)
39 {
40         struct user_context *user = task->thread.user;
41
42         if (regno < 0 || regno >= PT__END)
43                 return 0;
44
45         return ((unsigned long *) user)[regno];
46 }
47
48 /*
49  * Write contents of register REGNO in task TASK.
50  */
51 static inline int put_reg(struct task_struct *task, int regno,
52                           unsigned long data)
53 {
54         struct user_context *user = task->thread.user;
55
56         if (regno < 0 || regno >= PT__END)
57                 return -EIO;
58
59         switch (regno) {
60         case PT_GR(0):
61                 return 0;
62         case PT_PSR:
63         case PT__STATUS:
64                 return -EIO;
65         default:
66                 ((unsigned long *) user)[regno] = data;
67                 return 0;
68         }
69 }
70
71 /*
72  * check that an address falls within the bounds of the target process's memory
73  * mappings
74  */
75 static inline int is_user_addr_valid(struct task_struct *child,
76                                      unsigned long start, unsigned long len)
77 {
78 #ifdef CONFIG_MMU
79         if (start >= PAGE_OFFSET || len > PAGE_OFFSET - start)
80                 return -EIO;
81         return 0;
82 #else
83         struct vm_area_struct *vma;
84
85         vma = find_vma(child->mm, start);
86         if (vma && start >= vma->vm_start && start + len <= vma->vm_end)
87                 return 0;
88
89         return -EIO;
90 #endif
91 }
92
93 /*
94  * Called by kernel/ptrace.c when detaching..
95  *
96  * Control h/w single stepping
97  */
98 void ptrace_disable(struct task_struct *child)
99 {
100         child->thread.frame0->__status &= ~REG__STATUS_STEP;
101 }
102
103 void ptrace_enable(struct task_struct *child)
104 {
105         child->thread.frame0->__status |= REG__STATUS_STEP;
106 }
107
108 long arch_ptrace(struct task_struct *child, long request, long addr, long data)
109 {
110         unsigned long tmp;
111         int ret;
112
113         switch (request) {
114                 /* when I and D space are separate, these will need to be fixed. */
115         case PTRACE_PEEKTEXT: /* read word at location addr. */
116         case PTRACE_PEEKDATA:
117                 ret = -EIO;
118                 if (is_user_addr_valid(child, addr, sizeof(tmp)) < 0)
119                         break;
120                 ret = generic_ptrace_peekdata(child, addr, data);
121                 break;
122
123                 /* read the word at location addr in the USER area. */
124         case PTRACE_PEEKUSR: {
125                 tmp = 0;
126                 ret = -EIO;
127                 if ((addr & 3) || addr < 0)
128                         break;
129
130                 ret = 0;
131                 switch (addr >> 2) {
132                 case 0 ... PT__END - 1:
133                         tmp = get_reg(child, addr >> 2);
134                         break;
135
136                 case PT__END + 0:
137                         tmp = child->mm->end_code - child->mm->start_code;
138                         break;
139
140                 case PT__END + 1:
141                         tmp = child->mm->end_data - child->mm->start_data;
142                         break;
143
144                 case PT__END + 2:
145                         tmp = child->mm->start_stack - child->mm->start_brk;
146                         break;
147
148                 case PT__END + 3:
149                         tmp = child->mm->start_code;
150                         break;
151
152                 case PT__END + 4:
153                         tmp = child->mm->start_stack;
154                         break;
155
156                 default:
157                         ret = -EIO;
158                         break;
159                 }
160
161                 if (ret == 0)
162                         ret = put_user(tmp, (unsigned long *) data);
163                 break;
164         }
165
166                 /* when I and D space are separate, this will have to be fixed. */
167         case PTRACE_POKETEXT: /* write the word at location addr. */
168         case PTRACE_POKEDATA:
169                 ret = -EIO;
170                 if (is_user_addr_valid(child, addr, sizeof(tmp)) < 0)
171                         break;
172                 ret = generic_ptrace_pokedata(child, addr, data);
173                 break;
174
175         case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
176                 ret = -EIO;
177                 if ((addr & 3) || addr < 0)
178                         break;
179
180                 ret = 0;
181                 switch (addr >> 2) {
182                 case 0 ... PT__END-1:
183                         ret = put_reg(child, addr >> 2, data);
184                         break;
185
186                 default:
187                         ret = -EIO;
188                         break;
189                 }
190                 break;
191
192         case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
193         case PTRACE_CONT: /* restart after signal. */
194                 ret = -EIO;
195                 if (!valid_signal(data))
196                         break;
197                 if (request == PTRACE_SYSCALL)
198                         set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
199                 else
200                         clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
201                 child->exit_code = data;
202                 ptrace_disable(child);
203                 wake_up_process(child);
204                 ret = 0;
205                 break;
206
207                 /* make the child exit.  Best I can do is send it a sigkill.
208                  * perhaps it should be put in the status that it wants to
209                  * exit.
210                  */
211         case PTRACE_KILL:
212                 ret = 0;
213                 if (child->exit_state == EXIT_ZOMBIE)   /* already dead */
214                         break;
215                 child->exit_code = SIGKILL;
216                 clear_tsk_thread_flag(child, TIF_SINGLESTEP);
217                 ptrace_disable(child);
218                 wake_up_process(child);
219                 break;
220
221         case PTRACE_SINGLESTEP:  /* set the trap flag. */
222                 ret = -EIO;
223                 if (!valid_signal(data))
224                         break;
225                 clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
226                 ptrace_enable(child);
227                 child->exit_code = data;
228                 wake_up_process(child);
229                 ret = 0;
230                 break;
231
232         case PTRACE_DETACH:     /* detach a process that was attached. */
233                 ret = ptrace_detach(child, data);
234                 break;
235
236         case PTRACE_GETREGS: { /* Get all integer regs from the child. */
237                 int i;
238                 for (i = 0; i < PT__GPEND; i++) {
239                         tmp = get_reg(child, i);
240                         if (put_user(tmp, (unsigned long *) data)) {
241                                 ret = -EFAULT;
242                                 break;
243                         }
244                         data += sizeof(long);
245                 }
246                 ret = 0;
247                 break;
248         }
249
250         case PTRACE_SETREGS: { /* Set all integer regs in the child. */
251                 int i;
252                 for (i = 0; i < PT__GPEND; i++) {
253                         if (get_user(tmp, (unsigned long *) data)) {
254                                 ret = -EFAULT;
255                                 break;
256                         }
257                         put_reg(child, i, tmp);
258                         data += sizeof(long);
259                 }
260                 ret = 0;
261                 break;
262         }
263
264         case PTRACE_GETFPREGS: { /* Get the child FP/Media state. */
265                 ret = 0;
266                 if (copy_to_user((void *) data,
267                                  &child->thread.user->f,
268                                  sizeof(child->thread.user->f)))
269                         ret = -EFAULT;
270                 break;
271         }
272
273         case PTRACE_SETFPREGS: { /* Set the child FP/Media state. */
274                 ret = 0;
275                 if (copy_from_user(&child->thread.user->f,
276                                    (void *) data,
277                                    sizeof(child->thread.user->f)))
278                         ret = -EFAULT;
279                 break;
280         }
281
282         case PTRACE_GETFDPIC:
283                 tmp = 0;
284                 switch (addr) {
285                 case PTRACE_GETFDPIC_EXEC:
286                         tmp = child->mm->context.exec_fdpic_loadmap;
287                         break;
288                 case PTRACE_GETFDPIC_INTERP:
289                         tmp = child->mm->context.interp_fdpic_loadmap;
290                         break;
291                 default:
292                         break;
293                 }
294
295                 ret = 0;
296                 if (put_user(tmp, (unsigned long *) data)) {
297                         ret = -EFAULT;
298                         break;
299                 }
300                 break;
301
302         default:
303                 ret = -EIO;
304                 break;
305         }
306         return ret;
307 }
308
309 asmlinkage void do_syscall_trace(int leaving)
310 {
311         if (!test_thread_flag(TIF_SYSCALL_TRACE))
312                 return;
313
314         if (!(current->ptrace & PT_PTRACED))
315                 return;
316
317         /* we need to indicate entry or exit to strace */
318         if (leaving)
319                 __frame->__status |= REG__STATUS_SYSC_EXIT;
320         else
321                 __frame->__status |= REG__STATUS_SYSC_ENTRY;
322
323         ptrace_notify(SIGTRAP);
324
325         /*
326          * this isn't the same as continuing with a signal, but it will do
327          * for normal use.  strace only continues with a signal if the
328          * stopping signal is not SIGTRAP.  -brl
329          */
330         if (current->exit_code) {
331                 send_sig(current->exit_code, current, 1);
332                 current->exit_code = 0;
333         }
334 }