v2.4.9.9 -> v2.4.9.10
[opensuse:kernel.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 asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
138 {
139         struct task_struct *child;
140         struct user * dummy = NULL;
141         int i, ret;
142
143         lock_kernel();
144         ret = -EPERM;
145         if (request == PTRACE_TRACEME) {
146                 /* are we already being traced? */
147                 if (current->ptrace & PT_PTRACED)
148                         goto out;
149                 /* set the ptrace bit in the process flags. */
150                 current->ptrace |= PT_PTRACED;
151                 ret = 0;
152                 goto out;
153         }
154         ret = -ESRCH;
155         read_lock(&tasklist_lock);
156         child = find_task_by_pid(pid);
157         if (child)
158                 get_task_struct(child);
159         read_unlock(&tasklist_lock);
160         if (!child)
161                 goto out;
162
163         ret = -EPERM;
164         if (pid == 1)           /* you may not mess with init */
165                 goto out_tsk;
166
167         if (request == PTRACE_ATTACH) {
168                 ret = ptrace_attach(child);
169                 goto out_tsk;
170         }
171         ret = -ESRCH;
172         if (!(child->ptrace & PT_PTRACED))
173                 goto out_tsk;
174         if (child->state != TASK_STOPPED) {
175                 if (request != PTRACE_KILL)
176                         goto out_tsk;
177         }
178         if (child->p_pptr != current)
179                 goto out_tsk;
180         switch (request) {
181         /* when I and D space are separate, these will need to be fixed. */
182         case PTRACE_PEEKTEXT: /* read word at location addr. */ 
183         case PTRACE_PEEKDATA: {
184                 unsigned long tmp;
185                 int copied;
186
187                 copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
188                 ret = -EIO;
189                 if (copied != sizeof(tmp))
190                         break;
191                 ret = put_user(tmp,(unsigned long *) data);
192                 break;
193         }
194
195         /* read the word at location addr in the USER area. */
196         case PTRACE_PEEKUSR: {
197                 unsigned long tmp;
198
199                 ret = -EIO;
200                 if ((addr & 3) || addr < 0 || 
201                     addr > sizeof(struct user) - 3)
202                         break;
203
204                 tmp = 0;  /* Default return condition */
205                 if(addr < FRAME_SIZE*sizeof(long))
206                         tmp = getreg(child, addr);
207                 if(addr >= (long) &dummy->u_debugreg[0] &&
208                    addr <= (long) &dummy->u_debugreg[7]){
209                         addr -= (long) &dummy->u_debugreg[0];
210                         addr = addr >> 2;
211                         tmp = child->thread.debugreg[addr];
212                 }
213                 ret = put_user(tmp,(unsigned long *) data);
214                 break;
215         }
216
217         /* when I and D space are separate, this will have to be fixed. */
218         case PTRACE_POKETEXT: /* write the word at location addr. */
219         case PTRACE_POKEDATA:
220                 ret = 0;
221                 if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
222                         break;
223                 ret = -EIO;
224                 break;
225
226         case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
227                 ret = -EIO;
228                 if ((addr & 3) || addr < 0 || 
229                     addr > sizeof(struct user) - 3)
230                         break;
231
232                 if (addr < FRAME_SIZE*sizeof(long)) {
233                         ret = putreg(child, addr, data);
234                         break;
235                 }
236                 /* We need to be very careful here.  We implicitly
237                    want to modify a portion of the task_struct, and we
238                    have to be selective about what portions we allow someone
239                    to modify. */
240
241                   ret = -EIO;
242                   if(addr >= (long) &dummy->u_debugreg[0] &&
243                      addr <= (long) &dummy->u_debugreg[7]){
244
245                           if(addr == (long) &dummy->u_debugreg[4]) break;
246                           if(addr == (long) &dummy->u_debugreg[5]) break;
247                           if(addr < (long) &dummy->u_debugreg[4] &&
248                              ((unsigned long) data) >= TASK_SIZE-3) break;
249                           
250                           if(addr == (long) &dummy->u_debugreg[7]) {
251                                   data &= ~DR_CONTROL_RESERVED;
252                                   for(i=0; i<4; i++)
253                                           if ((0x5f54 >> ((data >> (16 + 4*i)) & 0xf)) & 1)
254                                                   goto out_tsk;
255                           }
256
257                           addr -= (long) &dummy->u_debugreg;
258                           addr = addr >> 2;
259                           child->thread.debugreg[addr] = data;
260                           ret = 0;
261                   }
262                   break;
263
264         case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
265         case PTRACE_CONT: { /* restart after signal. */
266                 long tmp;
267
268                 ret = -EIO;
269                 if ((unsigned long) data > _NSIG)
270                         break;
271                 if (request == PTRACE_SYSCALL)
272                         child->ptrace |= PT_TRACESYS;
273                 else
274                         child->ptrace &= ~PT_TRACESYS;
275                 child->exit_code = data;
276         /* make sure the single step bit is not set. */
277                 tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
278                 put_stack_long(child, EFL_OFFSET,tmp);
279                 wake_up_process(child);
280                 ret = 0;
281                 break;
282         }
283
284 /*
285  * make the child exit.  Best I can do is send it a sigkill. 
286  * perhaps it should be put in the status that it wants to 
287  * exit.
288  */
289         case PTRACE_KILL: {
290                 long tmp;
291
292                 ret = 0;
293                 if (child->state == TASK_ZOMBIE)        /* already dead */
294                         break;
295                 child->exit_code = SIGKILL;
296                 /* make sure the single step bit is not set. */
297                 tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
298                 put_stack_long(child, EFL_OFFSET, tmp);
299                 wake_up_process(child);
300                 break;
301         }
302
303         case PTRACE_SINGLESTEP: {  /* set the trap flag. */
304                 long tmp;
305
306                 ret = -EIO;
307                 if ((unsigned long) data > _NSIG)
308                         break;
309                 child->ptrace &= ~PT_TRACESYS;
310                 if ((child->ptrace & PT_DTRACE) == 0) {
311                         /* Spurious delayed TF traps may occur */
312                         child->ptrace |= PT_DTRACE;
313                 }
314                 tmp = get_stack_long(child, EFL_OFFSET) | TRAP_FLAG;
315                 put_stack_long(child, EFL_OFFSET, tmp);
316                 child->exit_code = data;
317                 /* give it a chance to run. */
318                 wake_up_process(child);
319                 ret = 0;
320                 break;
321         }
322
323         case PTRACE_DETACH: { /* detach a process that was attached. */
324                 long tmp;
325
326                 ret = -EIO;
327                 if ((unsigned long) data > _NSIG)
328                         break;
329                 child->ptrace = 0;
330                 child->exit_code = data;
331                 write_lock_irq(&tasklist_lock);
332                 REMOVE_LINKS(child);
333                 child->p_pptr = child->p_opptr;
334                 SET_LINKS(child);
335                 write_unlock_irq(&tasklist_lock);
336                 /* make sure the single step bit is not set. */
337                 tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
338                 put_stack_long(child, EFL_OFFSET, tmp);
339                 wake_up_process(child);
340                 ret = 0;
341                 break;
342         }
343
344         case PTRACE_GETREGS: { /* Get all gp regs from the child. */
345                 if (!access_ok(VERIFY_WRITE, (unsigned *)data, FRAME_SIZE*sizeof(long))) {
346                         ret = -EIO;
347                         break;
348                 }
349                 for ( i = 0; i < FRAME_SIZE*sizeof(long); i += sizeof(long) ) {
350                         __put_user(getreg(child, i),(unsigned long *) data);
351                         data += sizeof(long);
352                 }
353                 ret = 0;
354                 break;
355         }
356
357         case PTRACE_SETREGS: { /* Set all gp regs in the child. */
358                 unsigned long tmp;
359                 if (!access_ok(VERIFY_READ, (unsigned *)data, FRAME_SIZE*sizeof(long))) {
360                         ret = -EIO;
361                         break;
362                 }
363                 for ( i = 0; i < FRAME_SIZE*sizeof(long); i += sizeof(long) ) {
364                         __get_user(tmp, (unsigned long *) data);
365                         putreg(child, i, tmp);
366                         data += sizeof(long);
367                 }
368                 ret = 0;
369                 break;
370         }
371
372         case PTRACE_GETFPREGS: { /* Get the child FPU state. */
373                 if (!access_ok(VERIFY_WRITE, (unsigned *)data,
374                                sizeof(struct user_i387_struct))) {
375                         ret = -EIO;
376                         break;
377                 }
378                 ret = 0;
379                 if ( !child->used_math ) {
380                         /* Simulate an empty FPU. */
381                         set_fpu_cwd(child, 0x037f);
382                         set_fpu_swd(child, 0x0000);
383                         set_fpu_twd(child, 0xffff);
384                 }
385                 get_fpregs((struct user_i387_struct *)data, child);
386                 break;
387         }
388
389         case PTRACE_SETFPREGS: { /* Set the child FPU state. */
390                 if (!access_ok(VERIFY_READ, (unsigned *)data,
391                                sizeof(struct user_i387_struct))) {
392                         ret = -EIO;
393                         break;
394                 }
395                 child->used_math = 1;
396                 set_fpregs(child, (struct user_i387_struct *)data);
397                 ret = 0;
398                 break;
399         }
400
401         case PTRACE_GETFPXREGS: { /* Get the child extended FPU state. */
402                 if (!access_ok(VERIFY_WRITE, (unsigned *)data,
403                                sizeof(struct user_fxsr_struct))) {
404                         ret = -EIO;
405                         break;
406                 }
407                 if ( !child->used_math ) {
408                         /* Simulate an empty FPU. */
409                         set_fpu_cwd(child, 0x037f);
410                         set_fpu_swd(child, 0x0000);
411                         set_fpu_twd(child, 0xffff);
412                         set_fpu_mxcsr(child, 0x1f80);
413                 }
414                 ret = get_fpxregs((struct user_fxsr_struct *)data, child);
415                 break;
416         }
417
418         case PTRACE_SETFPXREGS: { /* Set the child extended FPU state. */
419                 if (!access_ok(VERIFY_READ, (unsigned *)data,
420                                sizeof(struct user_fxsr_struct))) {
421                         ret = -EIO;
422                         break;
423                 }
424                 child->used_math = 1;
425                 ret = set_fpxregs(child, (struct user_fxsr_struct *)data);
426                 break;
427         }
428
429         case PTRACE_SETOPTIONS: {
430                 if (data & PTRACE_O_TRACESYSGOOD)
431                         child->ptrace |= PT_TRACESYSGOOD;
432                 else
433                         child->ptrace &= ~PT_TRACESYSGOOD;
434                 ret = 0;
435                 break;
436         }
437
438         default:
439                 ret = -EIO;
440                 break;
441         }
442 out_tsk:
443         free_task_struct(child);
444 out:
445         unlock_kernel();
446         return ret;
447 }
448
449 asmlinkage void syscall_trace(void)
450 {
451         if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) !=
452                         (PT_PTRACED|PT_TRACESYS))
453                 return;
454         /* the 0x80 provides a way for the tracing parent to distinguish
455            between a syscall stop and SIGTRAP delivery */
456         current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
457                                         ? 0x80 : 0);
458         current->state = TASK_STOPPED;
459         notify_parent(current, SIGCHLD);
460         schedule();
461         /*
462          * this isn't the same as continuing with a signal, but it will do
463          * for normal use.  strace only continues with a signal if the
464          * stopping signal is not SIGTRAP.  -brl
465          */
466         if (current->exit_code) {
467                 send_sig(current->exit_code, current, 1);
468                 current->exit_code = 0;
469         }
470 }