OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [arch/] [x86_64/] [kernel/] [ptrace.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.