patch-2_6_7-vs1_9_1_12
[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 > PT_MAX << 2)
124                                 break;
125
126                         tmp = get_reg(child, addr >> 2);
127                         ret = put_user(tmp, (unsigned long *)data);
128                         break;
129                 }
130                 
131                 /* Write the word at location address. */
132                 case PTRACE_POKETEXT:
133                 case PTRACE_POKEDATA:
134                         ret = 0;
135                         
136                         if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
137                                 break;
138                         
139                         ret = -EIO;
140                         break;
141  
142                 /* Write the word at location address in the USER area. */
143                 case PTRACE_POKEUSR:
144                         ret = -EIO;
145                         if ((addr & 3) || addr < 0 || addr > PT_MAX << 2)
146                                 break;
147
148                         addr >>= 2;
149
150                         if (addr == PT_DCCR) {
151                                 /* don't allow the tracing process to change stuff like
152                                  * interrupt enable, kernel/user bit, dma enables etc.
153                                  */
154                                 data &= DCCR_MASK;
155                                 data |= get_reg(child, PT_DCCR) & ~DCCR_MASK;
156                         }
157                         if (put_reg(child, addr, data))
158                                 break;
159                         ret = 0;
160                         break;
161
162                 case PTRACE_SYSCALL:
163                 case PTRACE_CONT:
164                         ret = -EIO;
165                         
166                         if ((unsigned long) data > _NSIG)
167                                 break;
168                         
169                         if (request == PTRACE_SYSCALL) {
170                                 set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
171                         }
172                         else {
173                                 clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
174                         }
175                         
176                         child->exit_code = data;
177                         
178                         /* TODO: make sure any pending breakpoint is killed */
179                         wake_up_process(child);
180                         ret = 0;
181                         
182                         break;
183                 
184                 /* Make the child exit by sending it a sigkill. */
185                 case PTRACE_KILL:
186                         ret = 0;
187                         
188                         if (child->state == TASK_ZOMBIE)
189                                 break;
190                         
191                         child->exit_code = SIGKILL;
192                         
193                         /* TODO: make sure any pending breakpoint is killed */
194                         wake_up_process(child);
195                         break;
196
197                 /* Set the trap flag. */
198                 case PTRACE_SINGLESTEP:
199                         ret = -EIO;
200                         
201                         if ((unsigned long) data > _NSIG)
202                                 break;
203                         
204                         clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
205
206                         /* TODO: set some clever breakpoint mechanism... */
207
208                         child->exit_code = data;
209                         wake_up_process(child);
210                         ret = 0;
211                         break;
212
213                 case PTRACE_DETACH:
214                         ret = ptrace_detach(child, data);
215                         break;
216
217                 /* Get all GP registers from the child. */
218                 case PTRACE_GETREGS: {
219                         int i;
220                         unsigned long tmp;
221                         
222                         for (i = 0; i <= PT_MAX; i++) {
223                                 tmp = get_reg(child, i);
224                                 
225                                 if (put_user(tmp, (unsigned long *) data)) {
226                                         ret = -EFAULT;
227                                         goto out_tsk;
228                                 }
229                                 
230                                 data += sizeof(long);
231                         }
232
233                         ret = 0;
234                         break;
235                 }
236
237                 /* Set all GP registers in the child. */
238                 case PTRACE_SETREGS: {
239                         int i;
240                         unsigned long tmp;
241                         
242                         for (i = 0; i <= PT_MAX; i++) {
243                                 if (get_user(tmp, (unsigned long *) data)) {
244                                         ret = -EFAULT;
245                                         goto out_tsk;
246                                 }
247                                 
248                                 if (i == PT_DCCR) {
249                                         tmp &= DCCR_MASK;
250                                         tmp |= get_reg(child, PT_DCCR) & ~DCCR_MASK;
251                                 }
252                                 
253                                 put_reg(child, i, tmp);
254                                 data += sizeof(long);
255                         }
256                         
257                         ret = 0;
258                         break;
259                 }
260
261                 default:
262                         ret = ptrace_request(child, request, addr, data);
263                         break;
264         }
265 out_tsk:
266         put_task_struct(child);
267 out:
268         unlock_kernel();
269         return ret;
270 }
271
272 void do_syscall_trace(void)
273 {
274         if (!test_thread_flag(TIF_SYSCALL_TRACE))
275                 return;
276         
277         if (!(current->ptrace & PT_PTRACED))
278                 return;
279         
280         /* the 0x80 provides a way for the tracing parent to distinguish
281            between a syscall stop and SIGTRAP delivery */
282         ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
283                                  ? 0x80 : 0));
284         
285         /*
286          * This isn't the same as continuing with a signal, but it will do for
287          * normal use.
288          */
289         if (current->exit_code) {
290                 send_sig(current->exit_code, current, 1);
291                 current->exit_code = 0;
292         }
293 }