This commit was manufactured by cvs2svn to create tag
[linux-2.6.git] / arch / um / kernel / ptrace.c
1 /* 
2  * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
3  * Licensed under the GPL
4  */
5
6 #include "linux/sched.h"
7 #include "linux/mm.h"
8 #include "linux/errno.h"
9 #include "linux/smp_lock.h"
10 #include "linux/security.h"
11 #include "linux/ptrace.h"
12 #ifdef CONFIG_PROC_MM
13 #include "linux/proc_mm.h"
14 #endif
15 #include "asm/ptrace.h"
16 #include "asm/uaccess.h"
17 #include "kern_util.h"
18 #include "ptrace_user.h"
19
20 /*
21  * Called by kernel/ptrace.c when detaching..
22  */
23 void ptrace_disable(struct task_struct *child)
24
25 }
26
27 extern long do_mmap2(struct task_struct *task, unsigned long addr, 
28                      unsigned long len, unsigned long prot, 
29                      unsigned long flags, unsigned long fd,
30                      unsigned long pgoff);
31
32 int sys_ptrace(long request, long pid, long addr, long data)
33 {
34         struct task_struct *child;
35         int i, ret;
36
37         lock_kernel();
38         ret = -EPERM;
39         if (request == PTRACE_TRACEME) {
40                 /* are we already being traced? */
41                 if (current->ptrace & PT_PTRACED)
42                         goto out;
43
44                 ret = security_ptrace(current->parent, current);
45                 if (ret)
46                         goto out;
47
48                 /* set the ptrace bit in the process flags. */
49                 current->ptrace |= PT_PTRACED;
50                 ret = 0;
51                 goto out;
52         }
53         ret = -ESRCH;
54         read_lock(&tasklist_lock);
55         child = find_task_by_pid(pid);
56         if (child)
57                 get_task_struct(child);
58         read_unlock(&tasklist_lock);
59         if (!child)
60                 goto out;
61         if (!vx_check(vx_task_xid(child), VX_WATCH|VX_IDENT))
62                 goto out_tsk;
63
64         ret = -EPERM;
65         if (pid == 1)           /* you may not mess with init */
66                 goto out_tsk;
67
68         if (request == PTRACE_ATTACH) {
69                 ret = ptrace_attach(child);
70                 goto out_tsk;
71         }
72
73         ret = ptrace_check_attach(child, request == PTRACE_KILL);
74         if (ret < 0)
75                 goto out_tsk;
76
77         switch (request) {
78                 /* when I and D space are separate, these will need to be fixed. */
79         case PTRACE_PEEKTEXT: /* read word at location addr. */ 
80         case PTRACE_PEEKDATA: {
81                 unsigned long tmp;
82                 int copied;
83
84                 ret = -EIO;
85                 copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
86                 if (copied != sizeof(tmp))
87                         break;
88                 ret = put_user(tmp,(unsigned long *) data);
89                 break;
90         }
91
92         /* read the word at location addr in the USER area. */
93         case PTRACE_PEEKUSR: {
94                 unsigned long tmp;
95
96                 ret = -EIO;
97                 if ((addr & 3) || addr < 0) 
98                         break;
99
100                 tmp = 0;  /* Default return condition */
101                 if(addr < FRAME_SIZE_OFFSET){
102                         tmp = getreg(child, addr);
103                 }
104                 else if((addr >= offsetof(struct user, u_debugreg[0])) &&
105                         (addr <= offsetof(struct user, u_debugreg[7]))){
106                         addr -= offsetof(struct user, u_debugreg[0]);
107                         addr = addr >> 2;
108                         tmp = child->thread.arch.debugregs[addr];
109                 }
110                 ret = put_user(tmp, (unsigned long *) data);
111                 break;
112         }
113
114         /* when I and D space are separate, this will have to be fixed. */
115         case PTRACE_POKETEXT: /* write the word at location addr. */
116         case PTRACE_POKEDATA:
117                 ret = -EIO;
118                 if (access_process_vm(child, addr, &data, sizeof(data), 
119                                       1) != sizeof(data))
120                         break;
121                 ret = 0;
122                 break;
123
124         case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
125                 ret = -EIO;
126                 if ((addr & 3) || addr < 0)
127                         break;
128
129                 if (addr < FRAME_SIZE_OFFSET) {
130                         ret = putreg(child, addr, data);
131                         break;
132                 }
133                 else if((addr >= offsetof(struct user, u_debugreg[0])) &&
134                         (addr <= offsetof(struct user, u_debugreg[7]))){
135                           addr -= offsetof(struct user, u_debugreg[0]);
136                           addr = addr >> 2;
137                           if((addr == 4) || (addr == 5)) break;
138                           child->thread.arch.debugregs[addr] = data;
139                           ret = 0;
140                 }
141
142                 break;
143
144         case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
145         case PTRACE_CONT: { /* restart after signal. */
146                 ret = -EIO;
147                 if ((unsigned long) data > _NSIG)
148                         break;
149                 if (request == PTRACE_SYSCALL) {
150                         set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
151                 }
152                 else {
153                         clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
154                 }
155                 child->exit_code = data;
156                 wake_up_process(child);
157                 ret = 0;
158                 break;
159         }
160
161 /*
162  * make the child exit.  Best I can do is send it a sigkill. 
163  * perhaps it should be put in the status that it wants to 
164  * exit.
165  */
166         case PTRACE_KILL: {
167                 ret = 0;
168                 if (child->state == TASK_ZOMBIE)        /* already dead */
169                         break;
170                 child->exit_code = SIGKILL;
171                 wake_up_process(child);
172                 break;
173         }
174
175         case PTRACE_SINGLESTEP: {  /* set the trap flag. */
176                 ret = -EIO;
177                 if ((unsigned long) data > _NSIG)
178                         break;
179                 clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
180                 child->ptrace |= PT_DTRACE;
181                 child->exit_code = data;
182                 /* give it a chance to run. */
183                 wake_up_process(child);
184                 ret = 0;
185                 break;
186         }
187
188         case PTRACE_DETACH:
189                 /* detach a process that was attached. */
190                 ret = ptrace_detach(child, data);
191                 break;
192
193 #ifdef PTRACE_GETREGS
194         case PTRACE_GETREGS: { /* Get all gp regs from the child. */
195                 if (!access_ok(VERIFY_WRITE, (unsigned long *)data, 
196                                FRAME_SIZE_OFFSET)) {
197                         ret = -EIO;
198                         break;
199                 }
200                 for ( i = 0; i < FRAME_SIZE_OFFSET; i += sizeof(long) ) {
201                         __put_user(getreg(child, i), (unsigned long *) data);
202                         data += sizeof(long);
203                 }
204                 ret = 0;
205                 break;
206         }
207 #endif
208 #ifdef PTRACE_SETREGS
209         case PTRACE_SETREGS: { /* Set all gp regs in the child. */
210                 unsigned long tmp = 0;
211                 if (!access_ok(VERIFY_READ, (unsigned *)data, 
212                                FRAME_SIZE_OFFSET)) {
213                         ret = -EIO;
214                         break;
215                 }
216                 for ( i = 0; i < FRAME_SIZE_OFFSET; i += sizeof(long) ) {
217                         __get_user(tmp, (unsigned long *) data);
218                         putreg(child, i, tmp);
219                         data += sizeof(long);
220                 }
221                 ret = 0;
222                 break;
223         }
224 #endif
225 #ifdef PTRACE_GETFPREGS
226         case PTRACE_GETFPREGS: /* Get the child FPU state. */
227                 ret = get_fpregs(data, child);
228                 break;
229 #endif
230 #ifdef PTRACE_SETFPREGS
231         case PTRACE_SETFPREGS: /* Set the child FPU state. */
232                 ret = set_fpregs(data, child);
233                 break;
234 #endif
235 #ifdef PTRACE_GETFPXREGS
236         case PTRACE_GETFPXREGS: /* Get the child FPU state. */
237                 ret = get_fpxregs(data, child);
238                 break;
239 #endif
240 #ifdef PTRACE_SETFPXREGS
241         case PTRACE_SETFPXREGS: /* Set the child FPU state. */
242                 ret = set_fpxregs(data, child);
243                 break;
244 #endif
245         case PTRACE_FAULTINFO: {
246                 struct ptrace_faultinfo fault;
247
248                 fault = ((struct ptrace_faultinfo) 
249                         { .is_write     = child->thread.err,
250                           .addr         = child->thread.cr2 });
251                 ret = copy_to_user((unsigned long *) data, &fault, 
252                                    sizeof(fault));
253                 if(ret)
254                         break;
255                 break;
256         }
257         case PTRACE_SIGPENDING:
258                 ret = copy_to_user((unsigned long *) data, 
259                                    &child->pending.signal,
260                                    sizeof(child->pending.signal));
261                 break;
262
263         case PTRACE_LDT: {
264                 struct ptrace_ldt ldt;
265
266                 if(copy_from_user(&ldt, (unsigned long *) data, 
267                                   sizeof(ldt))){
268                         ret = -EIO;
269                         break;
270                 }
271
272                 /* This one is confusing, so just punt and return -EIO for 
273                  * now
274                  */
275                 ret = -EIO;
276                 break;
277         }
278 #ifdef CONFIG_PROC_MM
279         case PTRACE_SWITCH_MM: {
280                 struct mm_struct *old = child->mm;
281                 struct mm_struct *new = proc_mm_get_mm(data);
282
283                 if(IS_ERR(new)){
284                         ret = PTR_ERR(new);
285                         break;
286                 }
287
288                 atomic_inc(&new->mm_users);
289                 child->mm = new;
290                 child->active_mm = new;
291                 mmput(old);
292                 ret = 0;
293                 break;
294         }
295 #endif
296         default:
297                 ret = -EIO;
298                 break;
299         }
300  out_tsk:
301         put_task_struct(child);
302  out:
303         unlock_kernel();
304         return ret;
305 }
306
307 void syscall_trace(void)
308 {
309         if (!test_thread_flag(TIF_SYSCALL_TRACE))
310                 return;
311         if (!(current->ptrace & PT_PTRACED))
312                 return;
313
314         /* the 0x80 provides a way for the tracing parent to distinguish
315            between a syscall stop and SIGTRAP delivery */
316         current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
317                                         ? 0x80 : 0);
318         current->state = TASK_STOPPED;
319         notify_parent(current, SIGCHLD);
320         schedule();
321
322         /*
323          * this isn't the same as continuing with a signal, but it will do
324          * for normal use.  strace only continues with a signal if the
325          * stopping signal is not SIGTRAP.  -brl
326          */
327         if (current->exit_code) {
328                 send_sig(current->exit_code, current, 1);
329                 current->exit_code = 0;
330         }
331 }
332
333 /*
334  * Overrides for Emacs so that we follow Linus's tabbing style.
335  * Emacs will notice this stuff at the end of the file and automatically
336  * adjust the settings for this buffer only.  This must remain at the end
337  * of the file.
338  * ---------------------------------------------------------------------------
339  * Local variables:
340  * c-file-style: "linux"
341  * End:
342  */