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