ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / arch / cris / arch-v10 / kernel / ptrace.c
1 /*
2  * Copyright (C) 2000-2003, Axis Communications AB.
3  */
4
5 #include <linux/kernel.h>
6 #include <linux/sched.h>
7 #include <linux/mm.h>
8 #include <linux/smp.h>
9 #include <linux/smp_lock.h>
10 #include <linux/errno.h>
11 #include <linux/ptrace.h>
12 #include <linux/user.h>
13
14 #include <asm/uaccess.h>
15 #include <asm/page.h>
16 #include <asm/pgtable.h>
17 #include <asm/system.h>
18 #include <asm/processor.h>
19
20 /* 
21  * Determines which bits in DCCR the user has access to.
22  * 1 = access, 0 = no access.
23  */
24 #define DCCR_MASK 0x0000001f     /* XNZVC */
25
26 extern inline long get_reg(struct task_struct *, unsigned int);
27 extern inline long put_reg(struct task_struct *, unsigned int, unsigned long);
28
29 /*
30  * Called by kernel/ptrace.c when detaching.
31  *
32  * Make sure the single step bit is not set.
33  */
34 void 
35 ptrace_disable(struct task_struct *child)
36 {
37        /* Todo - pending singlesteps? */
38 }
39
40 /* 
41  * Note that this implementation of ptrace behaves differently from vanilla
42  * ptrace.  Contrary to what the man page says, in the PTRACE_PEEKTEXT,
43  * PTRACE_PEEKDATA, and PTRACE_PEEKUSER requests the data variable is not
44  * ignored.  Instead, the data variable is expected to point at a location
45  * (in user space) where the result of the ptrace call is written (instead of
46  * being returned).
47  */
48 asmlinkage int 
49 sys_ptrace(long request, long pid, long addr, long data)
50 {
51         struct task_struct *child;
52         int ret;
53
54         lock_kernel();
55         ret = -EPERM;
56         
57         if (request == PTRACE_TRACEME) {
58                 if (current->ptrace & PT_PTRACED)
59                         goto out;
60
61                 current->ptrace |= PT_PTRACED;
62                 ret = 0;
63                 goto out;
64         }
65         
66         ret = -ESRCH;
67         read_lock(&tasklist_lock);
68         child = find_task_by_pid(pid);
69         
70         if (child)
71                 get_task_struct(child);
72         
73         read_unlock(&tasklist_lock);
74         
75         if (!child)
76                 goto out;
77         
78         ret = -EPERM;
79         
80         if (pid == 1)           /* Leave the init process alone! */
81                 goto out_tsk;
82         
83         if (request == PTRACE_ATTACH) {
84                 ret = ptrace_attach(child);
85                 goto out_tsk;
86         }
87         
88         ret = -ESRCH;
89         
90         if (!(child->ptrace & PT_PTRACED))
91                 goto out_tsk;
92         
93         if (child->state != TASK_STOPPED) {
94                 if (request != PTRACE_KILL)
95                         goto out_tsk;
96         }
97         
98         if (child->parent != current)
99                 goto out_tsk;
100
101         switch (request) {
102                 /* Read word at location address. */ 
103                 case PTRACE_PEEKTEXT:
104                 case PTRACE_PEEKDATA: {
105                         unsigned long tmp;
106                         int copied;
107
108                         copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
109                         ret = -EIO;
110                         
111                         if (copied != sizeof(tmp))
112                                 break;
113                         
114                         ret = put_user(tmp,(unsigned long *) data);
115                         break;
116                 }
117
118                 /* Read the word at location address in the USER area. */
119                 case PTRACE_PEEKUSR: {
120                         unsigned long tmp;
121                         
122                         ret = -EIO;
123                         if ((addr & 3) || addr < 0 || addr >= sizeof(struct user))
124                                 break;
125                         
126                         tmp = 0;  /* Default return condition */
127                         ret = -EIO;
128                         
129                         if (addr < sizeof(struct pt_regs)) {
130                                 tmp = get_reg(child, addr >> 2);
131                                 ret = put_user(tmp, (unsigned long *)data);
132                         }
133                         
134                         break;
135                 }
136                 
137                 /* Write the word at location address. */
138                 case PTRACE_POKETEXT:
139                 case PTRACE_POKEDATA:
140                         ret = 0;
141                         
142                         if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
143                                 break;
144                         
145                         ret = -EIO;
146                         break;
147  
148                 /* Write the word at location address in the USER area. */
149                 case PTRACE_POKEUSR:
150                         ret = -EIO;
151                         
152                         if ((addr & 3) || addr < 0 || addr >= sizeof(struct user))
153                                 break;
154
155                         if (addr < sizeof(struct pt_regs)) {
156                                 addr >>= 2;
157
158                                 if (addr == PT_DCCR) {
159                                         /*
160                                          * Don't allow the tracing process to
161                                          * change stuff like interrupt enable,
162                                          * kernel/user bit, etc.
163                                          */
164                                         data &= DCCR_MASK;
165                                         data |= get_reg(child, PT_DCCR) & ~DCCR_MASK;
166                                 }
167                                 
168                                 if (put_reg(child, addr, data))
169                                         break;
170                                 
171                                 ret = 0;
172                         }
173                         break;
174
175                 case PTRACE_SYSCALL:
176                 case PTRACE_CONT:
177                         ret = -EIO;
178                         
179                         if ((unsigned long) data > _NSIG)
180                                 break;
181                         
182                         if (request == PTRACE_SYSCALL) {
183                                 set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
184                         }
185                         else {
186                                 clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
187                         }
188                         
189                         child->exit_code = data;
190                         
191                         /* TODO: make sure any pending breakpoint is killed */
192                         wake_up_process(child);
193                         ret = 0;
194                         
195                         break;
196                 
197                 /* Make the child exit by sending it a sigkill. */
198                 case PTRACE_KILL:
199                         ret = 0;
200                         
201                         if (child->state == TASK_ZOMBIE)
202                                 break;
203                         
204                         child->exit_code = SIGKILL;
205                         
206                         /* TODO: make sure any pending breakpoint is killed */
207                         wake_up_process(child);
208                         break;
209
210                 /* Set the trap flag. */
211                 case PTRACE_SINGLESTEP:
212                         ret = -EIO;
213                         
214                         if ((unsigned long) data > _NSIG)
215                                 break;
216                         
217                         clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
218
219                         /* TODO: set some clever breakpoint mechanism... */
220
221                         child->exit_code = data;
222                         wake_up_process(child);
223                         ret = 0;
224                         break;
225
226                 case PTRACE_DETACH:
227                         ret = ptrace_detach(child, data);
228                         break;
229
230                 /* Get all GP registers from the child. */
231                 case PTRACE_GETREGS: {
232                         int i;
233                         unsigned long tmp;
234                         
235                         for (i = 0; i <= PT_MAX; i++) {
236                                 tmp = get_reg(child, i);
237                                 
238                                 if (put_user(tmp, (unsigned long *) data)) {
239                                         ret = -EFAULT;
240                                         break;
241                                 }
242                                 
243                                 data += sizeof(long);
244                         }
245
246                         ret = 0;
247                         break;
248                 }
249
250                 /* Set all GP registers in the child. */
251                 case PTRACE_SETREGS: {
252                         int i;
253                         unsigned long tmp;
254                         
255                         for (i = 0; i <= PT_MAX; i++) {
256                                 if (get_user(tmp, (unsigned long *) data)) {
257                                         ret = -EFAULT;
258                                         break;
259                                 }
260                                 
261                                 if (i == PT_DCCR) {
262                                         tmp &= DCCR_MASK;
263                                         tmp |= get_reg(child, PT_DCCR) & ~DCCR_MASK;
264                                 }
265                                 
266                                 put_reg(child, i, tmp);
267                                 data += sizeof(long);
268                         }
269                         
270                         ret = 0;
271                         break;
272                 }
273
274                 default:
275                         ret = ptrace_request(child, request, addr, data);
276                         break;
277         }
278 out_tsk:
279         put_task_struct(child);
280 out:
281         unlock_kernel();
282         return ret;
283 }
284
285 void do_syscall_trace(void)
286 {
287         if (!test_thread_flag(TIF_SYSCALL_TRACE))
288                 return;
289         
290         if (!(current->ptrace & PT_PTRACED))
291                 return;
292         
293         current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
294                                         ? 0x80 : 0);
295         
296         current->state = TASK_STOPPED;
297         notify_parent(current, SIGCHLD);
298         schedule();
299         
300         /*
301          * This isn't the same as continuing with a signal, but it will do for
302          * normal use.
303          */
304         if (current->exit_code) {
305                 send_sig(current->exit_code, current, 1);
306                 current->exit_code = 0;
307         }
308 }