more changes on original files
[linux-2.4.git] / arch / i386 / kernel / ptrace.c
1 /* ptrace.c */
2 /* By Ross Biro 1/23/92 */
3 /*
4  * Pentium III FXSR, SSE support
5  *      Gareth Hughes <gareth@valinux.com>, May 2000
6  */
7
8 #include <linux/kernel.h>
9 #include <linux/sched.h>
10 #include <linux/mm.h>
11 #include <linux/smp.h>
12 #include <linux/smp_lock.h>
13 #include <linux/errno.h>
14 #include <linux/ptrace.h>
15 #include <linux/user.h>
16
17 #include <asm/uaccess.h>
18 #include <asm/pgtable.h>
19 #include <asm/system.h>
20 #include <asm/processor.h>
21 #include <asm/i387.h>
22 #include <asm/debugreg.h>
23
24 /*
25  * does not yet catch signals sent when the child dies.
26  * in exit.c or in signal.c.
27  */
28
29 /* determines which flags the user has access to. */
30 /* 1 = access 0 = no access */
31 #define FLAG_MASK 0x00044dd5
32
33 /* set's the trap flag. */
34 #define TRAP_FLAG 0x100
35
36 /*
37  * Offset of eflags on child stack..
38  */
39 #define EFL_OFFSET ((EFL-2)*4-sizeof(struct pt_regs))
40
41 /*
42  * this routine will get a word off of the processes privileged stack. 
43  * the offset is how far from the base addr as stored in the TSS.  
44  * this routine assumes that all the privileged stacks are in our
45  * data space.
46  */   
47 static inline int get_stack_long(struct task_struct *task, int offset)
48 {
49         unsigned char *stack;
50
51         stack = (unsigned char *)task->thread.esp0;
52         stack += offset;
53         return (*((int *)stack));
54 }
55
56 /*
57  * this routine will put a word on the processes privileged stack. 
58  * the offset is how far from the base addr as stored in the TSS.  
59  * this routine assumes that all the privileged stacks are in our
60  * data space.
61  */
62 static inline int put_stack_long(struct task_struct *task, int offset,
63         unsigned long data)
64 {
65         unsigned char * stack;
66
67         stack = (unsigned char *) task->thread.esp0;
68         stack += offset;
69         *(unsigned long *) stack = data;
70         return 0;
71 }
72
73 static int putreg(struct task_struct *child,
74         unsigned long regno, unsigned long value)
75 {
76         switch (regno >> 2) {
77                 case FS:
78                         if (value && (value & 3) != 3)
79                                 return -EIO;
80                         child->thread.fs = value;
81                         return 0;
82                 case GS:
83                         if (value && (value & 3) != 3)
84                                 return -EIO;
85                         child->thread.gs = value;
86                         return 0;
87                 case DS:
88                 case ES:
89                         if (value && (value & 3) != 3)
90                                 return -EIO;
91                         value &= 0xffff;
92                         break;
93                 case SS:
94                 case CS:
95                         if ((value & 3) != 3)
96                                 return -EIO;
97                         value &= 0xffff;
98                         break;
99                 case EFL:
100                         value &= FLAG_MASK;
101                         value |= get_stack_long(child, EFL_OFFSET) & ~FLAG_MASK;
102                         break;
103         }
104         if (regno > GS*4)
105                 regno -= 2*4;
106         put_stack_long(child, regno - sizeof(struct pt_regs), value);
107         return 0;
108 }
109
110 static unsigned long getreg(struct task_struct *child,
111         unsigned long regno)
112 {
113         unsigned long retval = ~0UL;
114
115         switch (regno >> 2) {
116                 case FS:
117                         retval = child->thread.fs;
118                         break;
119                 case GS:
120                         retval = child->thread.gs;
121                         break;
122                 case DS:
123                 case ES:
124                 case SS:
125                 case CS:
126                         retval = 0xffff;
127                         /* fall through */
128                 default:
129                         if (regno > GS*4)
130                                 regno -= 2*4;
131                         regno = regno - sizeof(struct pt_regs);
132                         retval &= get_stack_long(child, regno);
133         }
134         return retval;
135 }
136
137 /*
138  * Called by kernel/ptrace.c when detaching..
139  *
140  * Make sure the single step bit is not set.
141  */
142 void ptrace_disable(struct task_struct *child)
143
144         long tmp;
145
146         tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
147         put_stack_long(child, EFL_OFFSET, tmp);
148 }
149
150 asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
151 {
152         struct task_struct *child;
153         struct user * dummy = NULL;
154         int i, ret;
155
156         lock_kernel();
157         ret = -EPERM;
158         if (request == PTRACE_TRACEME) {
159                 /* are we already being traced? */
160                 if (current->ptrace & PT_PTRACED)
161                         goto out;
162                 /* set the ptrace bit in the process flags. */
163                 current->ptrace |= PT_PTRACED;
164                 ret = 0;
165                 goto out;
166         }
167         ret = -ESRCH;
168         read_lock(&tasklist_lock);
169         child = find_task_by_pid(pid);
170         if (child)
171                 get_task_struct(child);
172         read_unlock(&tasklist_lock);
173         if (!child)
174                 goto out;
175
176         ret = -EPERM;
177         if (pid == 1)           /* you may not mess with init */
178                 goto out_tsk;
179
180         if (request == PTRACE_ATTACH) {
181                 ret = ptrace_attach(child);
182                 goto out_tsk;
183         }
184
185         ret = ptrace_check_attach(child, request == PTRACE_KILL);
186         if (ret < 0)
187                 goto out_tsk;
188
189         switch (request) {
190         /* when I and D space are separate, these will need to be fixed. */
191         case PTRACE_PEEKTEXT: /* read word at location addr. */ 
192         case PTRACE_PEEKDATA: {
193                 unsigned long tmp;
194                 int copied;
195
196                 copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
197                 ret = -EIO;
198                 if (copied != sizeof(tmp))
199                         break;
200                 ret = put_user(tmp,(unsigned long *) data);
201                 break;
202         }
203
204         /* read the word at location addr in the USER area. */
205         case PTRACE_PEEKUSR: {
206                 unsigned long tmp;
207
208                 ret = -EIO;
209                 if ((addr & 3) || addr < 0 || 
210                     addr > sizeof(struct user) - 3)
211                         break;
212
213                 tmp = 0;  /* Default return condition */
214                 if(addr < FRAME_SIZE*sizeof(long))
215                         tmp = getreg(child, addr);
216                 if(addr >= (long) &dummy->u_debugreg[0] &&
217                    addr <= (long) &dummy->u_debugreg[7]){
218                         addr -= (long) &dummy->u_debugreg[0];
219                         addr = addr >> 2;
220                         tmp = child->thread.debugreg[addr];
221                 }
222                 ret = put_user(tmp,(unsigned long *) data);
223                 break;
224         }
225
226         /* when I and D space are separate, this will have to be fixed. */
227         case PTRACE_POKETEXT: /* write the word at location addr. */
228         case PTRACE_POKEDATA:
229                 ret = 0;
230                 if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
231                         break;
232                 ret = -EIO;
233                 break;
234
235         case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
236                 ret = -EIO;
237                 if ((addr & 3) || addr < 0 || 
238                     addr > sizeof(struct user) - 3)
239                         break;
240
241                 if (addr < FRAME_SIZE*sizeof(long)) {
242                         ret = putreg(child, addr, data);
243                         break;
244                 }
245                 /* We need to be very careful here.  We implicitly
246                    want to modify a portion of the task_struct, and we
247                    have to be selective about what portions we allow someone
248                    to modify. */
249
250                   ret = -EIO;
251                   if(addr >= (long) &dummy->u_debugreg[0] &&
252                      addr <= (long) &dummy->u_debugreg[7]){
253
254                           if(addr == (long) &dummy->u_debugreg[4]) break;
255                           if(addr == (long) &dummy->u_debugreg[5]) break;
256                           if(addr < (long) &dummy->u_debugreg[4] &&
257                              ((unsigned long) data) >= TASK_SIZE-3) break;
258                           
259                           if(addr == (long) &dummy->u_debugreg[7]) {
260                                   data &= ~DR_CONTROL_RESERVED;
261                                   for(i=0; i<4; i++)
262                                           if ((0x5f54 >> ((data >> (16 + 4*i)) & 0xf)) & 1)
263                                                   goto out_tsk;
264                           }
265
266                           addr -= (long) &dummy->u_debugreg;
267                           addr = addr >> 2;
268                           child->thread.debugreg[addr] = data;
269                           ret = 0;
270                   }
271                   break;
272
273         case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
274         case PTRACE_CONT: { /* restart after signal. */
275                 long tmp;
276
277                 ret = -EIO;
278                 if ((unsigned long) data > _NSIG)
279                         break;
280                 if (request == PTRACE_SYSCALL)
281                         child->ptrace |= PT_TRACESYS;
282                 else
283                         child->ptrace &= ~PT_TRACESYS;
284                 child->exit_code = data;
285         /* make sure the single step bit is not set. */
286                 tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
287                 put_stack_long(child, EFL_OFFSET,tmp);
288                 wake_up_process(child);
289                 ret = 0;
290                 break;
291         }
292
293 /*
294  * make the child exit.  Best I can do is send it a sigkill. 
295  * perhaps it should be put in the status that it wants to 
296  * exit.
297  */
298         case PTRACE_KILL: {
299                 long tmp;
300
301                 ret = 0;
302                 if (child->state == TASK_ZOMBIE)        /* already dead */
303                         break;
304                 child->exit_code = SIGKILL;
305                 /* make sure the single step bit is not set. */
306                 tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
307                 put_stack_long(child, EFL_OFFSET, tmp);
308                 wake_up_process(child);
309                 break;
310         }
311
312         case PTRACE_SINGLESTEP: {  /* set the trap flag. */
313                 long tmp;
314
315                 ret = -EIO;
316                 if ((unsigned long) data > _NSIG)
317                         break;
318                 child->ptrace &= ~PT_TRACESYS;
319                 if ((child->ptrace & PT_DTRACE) == 0) {
320                         /* Spurious delayed TF traps may occur */
321                         child->ptrace |= PT_DTRACE;
322                 }
323                 tmp = get_stack_long(child, EFL_OFFSET) | TRAP_FLAG;
324                 put_stack_long(child, EFL_OFFSET, tmp);
325                 child->exit_code = data;
326                 /* give it a chance to run. */
327                 wake_up_process(child);
328                 ret = 0;
329                 break;
330         }
331
332         case PTRACE_DETACH:
333                 /* detach a process that was attached. */
334                 ret = ptrace_detach(child, data);
335                 break;
336
337         case PTRACE_GETREGS: { /* Get all gp regs from the child. */
338                 if (!access_ok(VERIFY_WRITE, (unsigned *)data, FRAME_SIZE*sizeof(long))) {
339                         ret = -EIO;
340                         break;
341                 }
342                 for ( i = 0; i < FRAME_SIZE*sizeof(long); i += sizeof(long) ) {
343                         __put_user(getreg(child, i),(unsigned long *) data);
344                         data += sizeof(long);
345                 }
346                 ret = 0;
347                 break;
348         }
349
350         case PTRACE_SETREGS: { /* Set all gp regs in the child. */
351                 unsigned long tmp;
352                 if (!access_ok(VERIFY_READ, (unsigned *)data, FRAME_SIZE*sizeof(long))) {
353                         ret = -EIO;
354                         break;
355                 }
356                 for ( i = 0; i < FRAME_SIZE*sizeof(long); i += sizeof(long) ) {
357                         __get_user(tmp, (unsigned long *) data);
358                         putreg(child, i, tmp);
359                         data += sizeof(long);
360                 }
361                 ret = 0;
362                 break;
363         }
364
365         case PTRACE_GETFPREGS: { /* Get the child FPU state. */
366                 if (!access_ok(VERIFY_WRITE, (unsigned *)data,
367                                sizeof(struct user_i387_struct))) {
368                         ret = -EIO;
369                         break;
370                 }
371                 ret = 0;
372                 if ( !child->used_math )
373                         load_empty_fpu(child);
374                 get_fpregs((struct user_i387_struct *)data, child);
375                 break;
376         }
377
378         case PTRACE_SETFPREGS: { /* Set the child FPU state. */
379                 if (!access_ok(VERIFY_READ, (unsigned *)data,
380                                sizeof(struct user_i387_struct))) {
381                         ret = -EIO;
382                         break;
383                 }
384                 child->used_math = 1;
385                 set_fpregs(child, (struct user_i387_struct *)data);
386                 ret = 0;
387                 break;
388         }
389
390         case PTRACE_GETFPXREGS: { /* Get the child extended FPU state. */
391                 if (!access_ok(VERIFY_WRITE, (unsigned *)data,
392                                sizeof(struct user_fxsr_struct))) {
393                         ret = -EIO;
394                         break;
395                 }
396                 if ( !child->used_math )
397                         load_empty_fpu(child);
398                 ret = get_fpxregs((struct user_fxsr_struct *)data, child);
399                 break;
400         }
401
402         case PTRACE_SETFPXREGS: { /* Set the child extended FPU state. */
403                 if (!access_ok(VERIFY_READ, (unsigned *)data,
404                                sizeof(struct user_fxsr_struct))) {
405                         ret = -EIO;
406                         break;
407                 }
408                 child->used_math = 1;
409                 ret = set_fpxregs(child, (struct user_fxsr_struct *)data);
410                 break;
411         }
412
413         case PTRACE_SETOPTIONS: {
414                 if (data & PTRACE_O_TRACESYSGOOD)
415                         child->ptrace |= PT_TRACESYSGOOD;
416                 else
417                         child->ptrace &= ~PT_TRACESYSGOOD;
418                 ret = 0;
419                 break;
420         }
421
422         default:
423                 ret = -EIO;
424                 break;
425         }
426 out_tsk:
427         free_task_struct(child);
428 out:
429         unlock_kernel();
430         return ret;
431 }
432
433 asmlinkage void syscall_trace(void)
434 {
435         if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) !=
436                         (PT_PTRACED|PT_TRACESYS))
437                 return;
438         /* the 0x80 provides a way for the tracing parent to distinguish
439            between a syscall stop and SIGTRAP delivery */
440         current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
441                                         ? 0x80 : 0);
442         current->state = TASK_STOPPED;
443         notify_parent(current, SIGCHLD);
444         schedule();
445         /*
446          * this isn't the same as continuing with a signal, but it will do
447          * for normal use.  strace only continues with a signal if the
448          * stopping signal is not SIGTRAP.  -brl
449          */
450         if (current->exit_code) {
451                 send_sig(current->exit_code, current, 1);
452                 current->exit_code = 0;
453         }
454 }