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

Subversion Repositories or1k_old

[/] [or1k_old/] [trunk/] [rc203soc/] [sw/] [uClinux/] [arch/] [armnommu/] [kernel/] [ptrace.c] - Blame information for rev 1782

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1622 jcastillo
/* ptrace.c */
2
/* By Ross Biro 1/23/92 */
3
/* edited by Linus Torvalds */
4
/* edited for ARM by Russell King */
5
 
6
#include <linux/head.h>
7
#include <linux/kernel.h>
8
#include <linux/sched.h>
9
#include <linux/mm.h>
10
#include <linux/errno.h>
11
#include <linux/ptrace.h>
12
#include <linux/user.h>
13
 
14
#include <asm/segment.h>
15
#include <asm/pgtable.h>
16
#include <asm/system.h>
17
 
18
/*
19
 * does not yet catch signals sent when the child dies.
20
 * in exit.c or in signal.c.
21
 */
22
 
23
/*
24
 * Breakpoint SWI instruction: SWI &9F0001
25
 */
26
#define BREAKINST       0xef9f0001
27
 
28
/* change a pid into a task struct. */
29
static inline struct task_struct * get_task(int pid)
30
{
31
        int i;
32
 
33
        for (i = 1; i < NR_TASKS; i++) {
34
                if (task[i] != NULL && (task[i]->pid == pid))
35
                        return task[i];
36
        }
37
        return NULL;
38
}
39
 
40
/*
41
 * this routine will get a word off of the processes privileged stack.
42
 * the offset is how far from the base addr as stored in the TSS.
43
 * this routine assumes that all the privileged stacks are in our
44
 * data space.
45
 */
46
static inline long get_stack_long(struct task_struct *task, int offset)
47
{
48
        unsigned char *stack;
49
 
50
        stack = (unsigned char *)(task->kernel_stack_page + 4096 - sizeof(struct pt_regs));
51
        stack += offset << 2;
52
        return *(unsigned long *)stack;
53
}
54
 
55
/*
56
 * this routine will put a word on the processes privileged stack.
57
 * the offset is how far from the base addr as stored in the TSS.
58
 * this routine assumes that all the privileged stacks are in our
59
 * data space.
60
 */
61
static inline long put_stack_long(struct task_struct *task, int offset,
62
        unsigned long data)
63
{
64
        unsigned char *stack;
65
 
66
        stack = (unsigned char *)(task->kernel_stack_page + 4096 - sizeof(struct pt_regs));
67
        stack += offset << 2;
68
        *(unsigned long *) stack = data;
69
        return 0;
70
}
71
 
72
 
73
 
74
inline
75
   static unsigned long get_long(struct task_struct * tsk,
76
                                                                 struct vm_area_struct * vma, unsigned long addr)
77
{
78
        return *(unsigned long*)addr;
79
}
80
 
81
inline
82
   static void put_long(struct task_struct * tsk, struct vm_area_struct * vma, unsigned long addr,
83
                                                unsigned long data)
84
{
85
        *(unsigned long*)addr = data;
86
}
87
 
88
 
89
inline
90
   static int read_long(struct task_struct * tsk, unsigned long addr,
91
                                                unsigned long * result)
92
{
93
        *result = *(unsigned long *)addr;
94
        return 0;
95
}
96
 
97
inline
98
   static int write_long(struct task_struct * tsk, unsigned long addr,
99
                                                 unsigned long data)
100
{
101
        *(unsigned long *)addr = data;
102
        return 0;
103
}
104
 
105
 
106
/*
107
 * Get value of register `rn' (in the instruction)
108
 */
109
static unsigned long ptrace_getrn (struct task_struct *child, unsigned long insn)
110
{
111
        unsigned int reg = (insn >> 16) & 15;
112
        unsigned long val;
113
 
114
        if (reg == 15)
115
                val = pc_pointer (get_stack_long (child, reg));
116
        else
117
                val = get_stack_long (child, reg);
118
 
119
printk ("r%02d=%08lX ", reg, val);
120
        return val;
121
}
122
 
123
/*
124
 * Get value of operand 2 (in an ALU instruction)
125
 */
126
static unsigned long ptrace_getaluop2 (struct task_struct *child, unsigned long insn)
127
{
128
        unsigned long val;
129
        int shift;
130
        int type;
131
 
132
printk ("op2=");
133
        if (insn & 1 << 25) {
134
                val = insn & 255;
135
                shift = (insn >> 8) & 15;
136
                type = 3;
137
printk ("(imm)");
138
        } else {
139
                val = get_stack_long (child, insn & 15);
140
 
141
                if (insn & (1 << 4))
142
                        shift = (int)get_stack_long (child, (insn >> 8) & 15);
143
                else
144
                        shift = (insn >> 7) & 31;
145
 
146
                type = (insn >> 5) & 3;
147
printk ("(r%02ld)", insn & 15);
148
        }
149
printk ("sh%dx%d", type, shift);
150
        switch (type) {
151
        case 0:  val <<= shift;  break;
152
        case 1: val >>= shift;  break;
153
        case 2:
154
                val = (((signed long)val) >> shift);
155
                break;
156
        case 3:
157
                __asm__ __volatile__("mov %0, %0, ror %1" : "=r" (val) : "0" (val), "r" (shift));
158
                break;
159
        }
160
printk ("=%08lX ", val);
161
        return val;
162
}
163
 
164
/*
165
 * Get value of operand 2 (in a LDR instruction)
166
 */
167
static unsigned long ptrace_getldrop2 (struct task_struct *child, unsigned long insn)
168
{
169
        unsigned long val;
170
        int shift;
171
        int type;
172
 
173
        val = get_stack_long (child, insn & 15);
174
        shift = (insn >> 7) & 31;
175
        type = (insn >> 5) & 3;
176
 
177
printk ("op2=r%02ldsh%dx%d", insn & 15, shift, type);
178
        switch (type) {
179
        case 0:  val <<= shift;  break;
180
        case 1: val >>= shift;  break;
181
        case 2:
182
                val = (((signed long)val) >> shift);
183
                break;
184
        case 3:
185
                __asm__ __volatile__("mov %0, %0, ror %1" : "=r" (val) : "0" (val), "r" (shift));
186
                break;
187
        }
188
printk ("=%08lX ", val);
189
        return val;
190
}
191
#undef pc_pointer
192
#define pc_pointer(x) ((x) & 0x03fffffc)
193
int ptrace_set_bpt (struct task_struct *child)
194
{
195
        unsigned long insn, pc, alt;
196
        int i, nsaved = 0, res;
197
 
198
        pc = pc_pointer (get_stack_long (child, 15/*REG_PC*/));
199
 
200
        res = read_long (child, pc, &insn);
201
        if (res < 0)
202
                return res;
203
 
204
        child->debugreg[nsaved++] = alt = pc + 4;
205
printk ("ptrace_set_bpt: insn=%08lX pc=%08lX ", insn, pc);
206
        switch (insn & 0x0e100000) {
207
        case 0x00000000:
208
        case 0x00100000:
209
        case 0x02000000:
210
        case 0x02100000: /* data processing */
211
                printk ("data ");
212
                switch (insn & 0x01e0f000) {
213
                case 0x0000f000:
214
                        alt = ptrace_getrn(child, insn) & ptrace_getaluop2(child, insn);
215
                        break;
216
                case 0x0020f000:
217
                        alt = ptrace_getrn(child, insn) ^ ptrace_getaluop2(child, insn);
218
                        break;
219
                case 0x0040f000:
220
                        alt = ptrace_getrn(child, insn) - ptrace_getaluop2(child, insn);
221
                        break;
222
                case 0x0060f000:
223
                        alt = ptrace_getaluop2(child, insn) - ptrace_getrn(child, insn);
224
                        break;
225
                case 0x0080f000:
226
                        alt = ptrace_getrn(child, insn) + ptrace_getaluop2(child, insn);
227
                        break;
228
                case 0x00a0f000:
229
                        alt = ptrace_getrn(child, insn) + ptrace_getaluop2(child, insn) +
230
                                (get_stack_long (child, 16/*REG_PSR*/) & CC_C_BIT ? 1 : 0);
231
                        break;
232
                case 0x00c0f000:
233
                        alt = ptrace_getrn(child, insn) - ptrace_getaluop2(child, insn) +
234
                                (get_stack_long (child, 16/*REG_PSR*/) & CC_C_BIT ? 1 : 0);
235
                        break;
236
                case 0x00e0f000:
237
                        alt = ptrace_getaluop2(child, insn) - ptrace_getrn(child, insn) +
238
                                (get_stack_long (child, 16/*REG_PSR*/) & CC_C_BIT ? 1 : 0);
239
                        break;
240
                case 0x0180f000:
241
                        alt = ptrace_getrn(child, insn) | ptrace_getaluop2(child, insn);
242
                        break;
243
                case 0x01a0f000:
244
                        alt = ptrace_getaluop2(child, insn);
245
                        break;
246
                case 0x01c0f000:
247
                        alt = ptrace_getrn(child, insn) & ~ptrace_getaluop2(child, insn);
248
                        break;
249
                case 0x01e0f000:
250
                        alt = ~ptrace_getaluop2(child, insn);
251
                        break;
252
                }
253
                break;
254
 
255
        case 0x04100000: /* ldr imm */
256
                if ((insn & 0xf000) == 0xf000) {
257
printk ("ldrimm ");
258
                        alt = ptrace_getrn(child, insn);
259
                        if (insn & 1 << 24) {
260
                                if (insn & 1 << 23)
261
                                        alt += insn & 0xfff;
262
                                else
263
                                        alt -= insn & 0xfff;
264
                        }
265
                        if (read_long (child, alt, &alt) < 0)
266
                                alt = pc + 4; /* not valid */
267
                        else
268
                                alt = pc_pointer (alt);
269
                }
270
                break;
271
 
272
        case 0x06100000: /* ldr */
273
                if ((insn & 0xf000) == 0xf000) {
274
printk ("ldr ");
275
                        alt = ptrace_getrn(child, insn);
276
                        if (insn & 1 << 24) {
277
                                if (insn & 1 << 23)
278
                                        alt += ptrace_getldrop2 (child, insn);
279
                                else
280
                                        alt -= ptrace_getldrop2 (child, insn);
281
                        }
282
                        if (read_long (child, alt, &alt) < 0)
283
                                alt = pc + 4; /* not valid */
284
                        else
285
                                alt = pc_pointer (alt);
286
                }
287
                break;
288
 
289
        case 0x08100000: /* ldm */
290
                if (insn & (1 << 15)) {
291
                        unsigned long base;
292
                        int nr_regs;
293
printk ("ldm ");
294
 
295
                        if (insn & (1 << 23)) {
296
                                nr_regs = insn & 65535;
297
 
298
                                nr_regs = (nr_regs & 0x5555) + ((nr_regs & 0xaaaa) >> 1);
299
                                nr_regs = (nr_regs & 0x3333) + ((nr_regs & 0xcccc) >> 2);
300
                                nr_regs = (nr_regs & 0x0707) + ((nr_regs & 0x7070) >> 4);
301
                                nr_regs = (nr_regs & 0x000f) + ((nr_regs & 0x0f00) >> 8);
302
                                nr_regs <<= 2;
303
 
304
                                if (!(insn & (1 << 24)))
305
                                        nr_regs -= 4;
306
                        } else {
307
                                if (insn & (1 << 24))
308
                                        nr_regs = -4;
309
                                else
310
                                        nr_regs = 0;
311
                        }
312
 
313
                        base = ptrace_getrn (child, insn);
314
 
315
                        if (read_long (child, base + nr_regs, &alt) < 0)
316
                                alt = pc + 4; /* not valid */
317
                        else
318
                                alt = pc_pointer (alt);
319
                        break;
320
                }
321
                break;
322
 
323
        case 0x0a000000:
324
        case 0x0a100000: { /* bl or b */
325
                signed long displ;
326
printk ("b/bl ");
327
                /* It's a branch/branch link: instead of trying to
328
                 * figure out whether the branch will be taken or not,
329
                 * we'll put a breakpoint at either location.  This is
330
                 * simpler, more reliable, and probably not a whole lot
331
                 * slower than the alternative approach of emulating the
332
                 * branch.
333
                 */
334
                displ = (insn & 0x00ffffff) << 8;
335
                displ = (displ >> 6) + 8;
336
                if (displ != 0 && displ != 4)
337
                        alt = pc + displ;
338
            }
339
            break;
340
        }
341
printk ("=%08lX\n", alt);
342
        if (alt != pc + 4)
343
                child->debugreg[nsaved++] = alt;
344
 
345
        for (i = 0; i < nsaved; i++) {
346
                res = read_long (child, child->debugreg[i], &insn);
347
                if (res >= 0) {
348
                        child->debugreg[i + 2] = insn;
349
                        res = write_long (child, child->debugreg[i], BREAKINST);
350
                        __flush_entry_to_ram(child->debugreg[i]);
351
                }
352
                if (res < 0) {
353
                        child->debugreg[4] = 0;
354
                        return res;
355
                }
356
        }
357
        child->debugreg[4] = nsaved;
358
        return 0;
359
}
360
 
361
/* Ensure no single-step breakpoint is pending.  Returns non-zero
362
 * value if child was being single-stepped.
363
 */
364
int ptrace_cancel_bpt (struct task_struct *child)
365
{
366
        int i, nsaved = child->debugreg[4];
367
 
368
        child->debugreg[4] = 0;
369
 
370
        if (nsaved > 2) {
371
                printk ("ptrace_cancel_bpt: bogus nsaved: %d!\n", nsaved);
372
                nsaved = 2;
373
        }
374
        for (i = 0; i < nsaved; i++) {
375
                write_long (child, child->debugreg[i], child->debugreg[i + 2]);
376
                __flush_entry_to_ram(child->debugreg[i]);
377
        }
378
        return nsaved != 0;
379
}
380
 
381
asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
382
{
383
        struct task_struct *child;
384
        struct user * dummy;
385
 
386
        dummy = NULL;
387
 
388
        if (request == PTRACE_TRACEME) {
389
                /* are we already being traced? */
390
                if (current->flags & PF_PTRACED)
391
                        return -EPERM;
392
                /* set the ptrace bit in the process flags. */
393
                current->flags |= PF_PTRACED;
394
                return 0;
395
        }
396
        if (pid == 1)           /* you may not mess with init */
397
                return -EPERM;
398
        if (!(child = get_task(pid)))
399
                return -ESRCH;
400
        if (request == PTRACE_ATTACH) {
401
                if (child == current)
402
                        return -EPERM;
403
                if ((!child->dumpable ||
404
                    (current->uid != child->euid) ||
405
                    (current->uid != child->suid) ||
406
                    (current->uid != child->uid) ||
407
                    (current->gid != child->egid) ||
408
                    (current->gid != child->sgid) ||
409
                    (current->gid != child->gid)) && !suser())
410
                        return -EPERM;
411
                /* the same process cannot be attached many times */
412
                if (child->flags & PF_PTRACED)
413
                        return -EPERM;
414
                child->flags |= PF_PTRACED;
415
                if (child->p_pptr != current) {
416
                        REMOVE_LINKS(child);
417
                        child->p_pptr = current;
418
                        SET_LINKS(child);
419
                }
420
                send_sig(SIGSTOP, child, 1);
421
                return 0;
422
        }
423
        if (!(child->flags & PF_PTRACED))
424
                return -ESRCH;
425
        if (child->state != TASK_STOPPED) {
426
                if (request != PTRACE_KILL)
427
                        return -ESRCH;
428
        }
429
        if (child->p_pptr != current)
430
                return -ESRCH;
431
 
432
        switch (request) {
433
                case PTRACE_PEEKTEXT:                           /* read word at location addr. */
434
                case PTRACE_PEEKDATA: {
435
                        unsigned long tmp;
436
                        int res;
437
 
438
                        res = read_long(child, addr, &tmp);
439
                        if (res < 0)
440
                                return res;
441
                        res = verify_area(VERIFY_WRITE, (void *) data, sizeof(long));
442
                        if (!res)
443
                                put_fs_long(tmp,(unsigned long *) data);
444
                        return res;
445
                }
446
 
447
                case PTRACE_PEEKUSR: {                          /* read the word at location addr in the USER area. */
448
                        unsigned long tmp;
449
                        int res;
450
 
451
                        if ((addr & 3) || addr < 0 || addr >= sizeof(struct user))
452
                                return -EIO;
453
 
454
                        res = verify_area(VERIFY_WRITE, (void *) data, sizeof(long));
455
                        if (res)
456
                                return res;
457
 
458
                        tmp = 0;  /* Default return condition */
459
 
460
                        if (addr < sizeof (struct pt_regs))
461
                                tmp = get_stack_long(child, (int)addr >> 2);
462
                        put_fs_long(tmp,(unsigned long *) data);
463
                        return 0;
464
                }
465
 
466
                case PTRACE_POKETEXT:                           /* write the word at location addr. */
467
                case PTRACE_POKEDATA:
468
                        return write_long(child,addr,data);
469
 
470
                case PTRACE_POKEUSR:                            /* write the word at location addr in the USER area */
471
                        if ((addr & 3) || addr < 0 || addr >= sizeof(struct user))
472
                                return -EIO;
473
 
474
                        if (addr < sizeof (struct pt_regs))
475
                                put_stack_long(child, (int)addr >> 2, data);
476
                        else
477
                                return -EIO;
478
                        return 0;
479
 
480
                case PTRACE_SYSCALL:                            /* continue and stop at next (return from) syscall */
481
                case PTRACE_CONT:                               /* restart after signal. */
482
                        if ((unsigned long) data > NSIG)
483
                                return -EIO;
484
                        if (request == PTRACE_SYSCALL)
485
                                child->flags |= PF_TRACESYS;
486
                        else
487
                                child->flags &= ~PF_TRACESYS;
488
                        child->exit_code = data;
489
                        wake_up_process (child);
490
                        /* make sure single-step breakpoint is gone. */
491
                        ptrace_cancel_bpt (child);
492
                        return 0;
493
 
494
                /* make the child exit.  Best I can do is send it a sigkill.
495
                 * perhaps it should be put in the status that it wants to
496
                 * exit.
497
                 */
498
                case PTRACE_KILL:
499
                        if (child->state == TASK_ZOMBIE)        /* already dead */
500
                                return 0;
501
                        wake_up_process (child);
502
                        child->exit_code = SIGKILL;
503
                        ptrace_cancel_bpt (child);
504
                        /* make sure single-step breakpoint is gone. */
505
                        ptrace_cancel_bpt (child);
506
                        return 0;
507
 
508
                case PTRACE_SINGLESTEP:                         /* execute single instruction. */
509
                        if ((unsigned long) data > NSIG)
510
                                return -EIO;
511
                        child->debugreg[4] = -1;
512
                        child->flags &= ~PF_TRACESYS;
513
                        wake_up_process(child);
514
                        child->exit_code = data;
515
                        /* give it a chance to run. */
516
                        return 0;
517
 
518
                case PTRACE_DETACH:                             /* detach a process that was attached. */
519
                        if ((unsigned long) data > NSIG)
520
                                return -EIO;
521
                        child->flags &= ~(PF_PTRACED|PF_TRACESYS);
522
                        wake_up_process (child);
523
                        child->exit_code = data;
524
                        REMOVE_LINKS(child);
525
                        child->p_pptr = child->p_opptr;
526
                        SET_LINKS(child);
527
                        /* make sure single-step breakpoint is gone. */
528
                        ptrace_cancel_bpt (child);
529
                        return 0;
530
 
531
                default:
532
                        return -EIO;
533
        }
534
}
535
 
536
asmlinkage void syscall_trace(void)
537
{
538
        if ((current->flags & (PF_PTRACED|PF_TRACESYS))
539
                        != (PF_PTRACED|PF_TRACESYS))
540
                return;
541
        current->exit_code = SIGTRAP;
542
        current->state = TASK_STOPPED;
543
        notify_parent(current, SIGCHLD);
544
        schedule();
545
        /*
546
         * this isn't the same as continuing with a signal, but it will do
547
         * for normal use.  strace only continues with a signal if the
548
         * stopping signal is not SIGTRAP.  -brl
549
         */
550
        if (current->exit_code)
551
                current->signal |= (1 << (current->exit_code - 1));
552
        current->exit_code = 0;
553
}

powered by: WebSVN 2.1.0

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