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

Subversion Repositories or1k

[/] [or1k/] [trunk/] [rc203soc/] [sw/] [uClinux/] [arch/] [m68k/] [kernel/] [ptrace.c] - Blame information for rev 1777

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1623 jcastillo
/*
2
 *  linux/arch/m68k/kernel/ptrace.c
3
 *
4
 *  Copyright (C) 1994 by Hamish Macdonald
5
 *  Taken from linux/kernel/ptrace.c and modified for M680x0.
6
 *  linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds
7
 *
8
 * This file is subject to the terms and conditions of the GNU General
9
 * Public License.  See the file COPYING in the main directory of
10
 * this archive for more details.
11
 */
12
 
13
#include <stddef.h>
14
#include <linux/kernel.h>
15
#include <linux/sched.h>
16
#include <linux/mm.h>
17
#include <linux/errno.h>
18
#include <linux/ptrace.h>
19
#include <linux/user.h>
20
 
21
#include <asm/segment.h>
22
#include <asm/page.h>
23
#include <asm/pgtable.h>
24
#include <asm/system.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 bits in the SR the user has access to. */
32
/* 1 = access 0 = no access */
33
#define SR_MASK 0x001f
34
 
35
/* sets the trace bits. */
36
#define TRACE_BITS 0x8000
37
 
38
/* Find the stack offset for a register, relative to tss.esp0. */
39
#define PT_REG(reg)     ((long)&((struct pt_regs *)0)->reg)
40
#define SW_REG(reg)     ((long)&((struct switch_stack *)0)->reg \
41
                         - sizeof(struct switch_stack))
42
/* Mapping from PT_xxx to the stack offset at which the register is
43
   saved.  Notice that usp has no stack-slot and needs to be treated
44
   specially (see get_reg/put_reg below). */
45
static int regoff[] = {
46
        PT_REG(d1), PT_REG(d2), PT_REG(d3), PT_REG(d4),
47
        PT_REG(d5), SW_REG(d6), SW_REG(d7), PT_REG(a0),
48
        PT_REG(a1), SW_REG(a2), SW_REG(a3), SW_REG(a4),
49
        SW_REG(a5), SW_REG(a6), PT_REG(d0), -1,
50
        PT_REG(orig_d0), PT_REG(sr), PT_REG(pc),
51
};
52
 
53
/* change a pid into a task struct. */
54
static inline struct task_struct * get_task(int pid)
55
{
56
        int i;
57
 
58
        for (i = 1; i < NR_TASKS; i++) {
59
                if (task[i] != NULL && (task[i]->pid == pid))
60
                        return task[i];
61
        }
62
        return NULL;
63
}
64
 
65
/*
66
 * Get contents of register REGNO in task TASK.
67
 */
68
static inline long get_reg(struct task_struct *task, int regno)
69
{
70
        unsigned long *addr;
71
 
72
        if (regno == PT_USP)
73
                addr = &task->tss.usp;
74
        else if (regno < sizeof(regoff)/sizeof(regoff[0]))
75
                addr = (unsigned long *)(task->tss.esp0 + regoff[regno]);
76
        else
77
                return 0;
78
        return *addr;
79
}
80
 
81
/*
82
 * Write contents of register REGNO in task TASK.
83
 */
84
static inline int put_reg(struct task_struct *task, int regno,
85
                          unsigned long data)
86
{
87
        unsigned long *addr;
88
 
89
        if (regno == PT_USP)
90
                addr = &task->tss.usp;
91
        else if (regno < sizeof(regoff)/sizeof(regoff[0]))
92
                addr = (unsigned long *) (task->tss.esp0 + regoff[regno]);
93
        else
94
                return -1;
95
        *addr = data;
96
        return 0;
97
}
98
 
99
/*
100
 * This routine gets a long from any process space by following the page
101
 * tables. NOTE! You should check that the long isn't on a page boundary,
102
 * and that it is in the task area before calling this: this routine does
103
 * no checking.
104
 *
105
 */
106
static unsigned long get_long(struct task_struct * tsk,
107
        struct vm_area_struct * vma, unsigned long addr)
108
{
109
        pgd_t * pgdir;
110
        pmd_t * pgmiddle;
111
        pte_t * pgtable;
112
        unsigned long page;
113
 
114
repeat:
115
        pgdir = pgd_offset(vma->vm_mm, addr);
116
        if (pgd_none(*pgdir)) {
117
                do_no_page(tsk, vma, addr, 0);
118
                goto repeat;
119
        }
120
        if (pgd_bad(*pgdir)) {
121
                printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir));
122
                pgd_clear(pgdir);
123
                return 0;
124
        }
125
        pgmiddle = pmd_offset(pgdir,addr);
126
        if (pmd_none(*pgmiddle)) {
127
                do_no_page(tsk, vma, addr, 0);
128
                goto repeat;
129
        }
130
        if (pmd_bad(*pgmiddle)) {
131
                printk("ptrace: bad page directory %08lx\n",
132
                       pmd_val(*pgmiddle));
133
                pmd_clear(pgmiddle);
134
                return 0;
135
        }
136
        pgtable = pte_offset(pgmiddle, addr);
137
        if (!pte_present(*pgtable)) {
138
                do_no_page(tsk, vma, addr, 0);
139
                goto repeat;
140
        }
141
        page = pte_page(*pgtable);
142
/* this is a hack for non-kernel-mapped video buffers and similar */
143
        if (page >= high_memory)
144
                return 0;
145
        page += addr & ~PAGE_MASK;
146
        return *(unsigned long *) page;
147
}
148
 
149
/*
150
 * This routine puts a long into any process space by following the page
151
 * tables. NOTE! You should check that the long isn't on a page boundary,
152
 * and that it is in the task area before calling this: this routine does
153
 * no checking.
154
 *
155
 * Now keeps R/W state of page so that a text page stays readonly
156
 * even if a debugger scribbles breakpoints into it.  -M.U-
157
 */
158
static void put_long(struct task_struct * tsk, struct vm_area_struct * vma, unsigned long addr,
159
        unsigned long data)
160
{
161
        pgd_t *pgdir;
162
        pmd_t *pgmiddle;
163
        pte_t *pgtable;
164
        unsigned long page;
165
 
166
repeat:
167
        pgdir = pgd_offset(vma->vm_mm, addr);
168
        if (!pgd_present(*pgdir)) {
169
                do_no_page(tsk, vma, addr, 1);
170
                goto repeat;
171
        }
172
        if (pgd_bad(*pgdir)) {
173
                printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir));
174
                pgd_clear(pgdir);
175
                return;
176
        }
177
        pgmiddle = pmd_offset(pgdir,addr);
178
        if (pmd_none(*pgmiddle)) {
179
                do_no_page(tsk, vma, addr, 1);
180
                goto repeat;
181
        }
182
        if (pmd_bad(*pgmiddle)) {
183
                printk("ptrace: bad page directory %08lx\n",
184
                       pmd_val(*pgmiddle));
185
                pmd_clear(pgmiddle);
186
                return;
187
        }
188
        pgtable = pte_offset(pgmiddle, addr);
189
        if (!pte_present(*pgtable)) {
190
                do_no_page(tsk, vma, addr, 1);
191
                goto repeat;
192
        }
193
        page = pte_page(*pgtable);
194
        if (!pte_write(*pgtable)) {
195
                do_wp_page(tsk, vma, addr, 2);
196
                goto repeat;
197
        }
198
/* this is a hack for non-kernel-mapped video buffers and similar */
199
        if (page < high_memory) {
200
                *(unsigned long *) (page + (addr & ~PAGE_MASK)) = data;
201
        }
202
/* we're bypassing pagetables, so we have to set the dirty bit ourselves */
203
/* this should also re-instate whatever read-only mode there was before */
204
        *pgtable = pte_mkdirty(mk_pte(page, vma->vm_page_prot));
205
        flush_tlb_all();
206
}
207
 
208
static struct vm_area_struct * find_extend_vma(struct task_struct * tsk, unsigned long addr)
209
{
210
        struct vm_area_struct * vma;
211
 
212
        addr &= PAGE_MASK;
213
        vma = find_vma(tsk,addr);
214
        if (!vma)
215
                return NULL;
216
        if (vma->vm_start <= addr)
217
                return vma;
218
        if (!(vma->vm_flags & VM_GROWSDOWN))
219
                return NULL;
220
        if (vma->vm_end - addr > tsk->rlim[RLIMIT_STACK].rlim_cur)
221
                return NULL;
222
        vma->vm_offset -= vma->vm_start - addr;
223
        vma->vm_start = addr;
224
        return vma;
225
}
226
 
227
/*
228
 * This routine checks the page boundaries, and that the offset is
229
 * within the task area. It then calls get_long() to read a long.
230
 */
231
static int read_long(struct task_struct * tsk, unsigned long addr,
232
        unsigned long * result)
233
{
234
        struct vm_area_struct * vma = find_extend_vma(tsk, addr);
235
 
236
        if (!vma)
237
                return -EIO;
238
        if ((addr & ~PAGE_MASK) > PAGE_SIZE-sizeof(long)) {
239
                unsigned long low,high;
240
                struct vm_area_struct * vma_low = vma;
241
 
242
                if (addr + sizeof(long) >= vma->vm_end) {
243
                        vma_low = vma->vm_next;
244
                        if (!vma_low || vma_low->vm_start != vma->vm_end)
245
                                return -EIO;
246
                }
247
                high = get_long(tsk, vma,addr & ~(sizeof(long)-1));
248
                low = get_long(tsk, vma_low,(addr+sizeof(long)) & ~(sizeof(long)-1));
249
                switch (addr & (sizeof(long)-1)) {
250
                        case 3:
251
                                low >>= 8;
252
                                low |= high << 24;
253
                                break;
254
                        case 2:
255
                                low >>= 16;
256
                                low |= high << 16;
257
                                break;
258
                        case 1:
259
                                low >>= 24;
260
                                low |= high << 8;
261
                                break;
262
                }
263
                *result = low;
264
        } else
265
                *result = get_long(tsk, vma,addr);
266
        return 0;
267
}
268
 
269
/*
270
 * This routine checks the page boundaries, and that the offset is
271
 * within the task area. It then calls put_long() to write a long.
272
 */
273
static int write_long(struct task_struct * tsk, unsigned long addr,
274
        unsigned long data)
275
{
276
        struct vm_area_struct * vma = find_extend_vma(tsk, addr);
277
 
278
        if (!vma)
279
                return -EIO;
280
        if ((addr & ~PAGE_MASK) > PAGE_SIZE-sizeof(long)) {
281
                unsigned long low,high;
282
                struct vm_area_struct * vma_low = vma;
283
 
284
                if (addr + sizeof(long) >= vma->vm_end) {
285
                        vma_low = vma->vm_next;
286
                        if (!vma_low || vma_low->vm_start != vma->vm_end)
287
                                return -EIO;
288
                }
289
                high = get_long(tsk, vma,addr & ~(sizeof(long)-1));
290
                low = get_long(tsk, vma_low,(addr+sizeof(long)) & ~(sizeof(long)-1));
291
                switch (addr & (sizeof(long)-1)) {
292
                        case 0: /* shouldn't happen, but safety first */
293
                                high = data;
294
                                break;
295
                        case 3:
296
                                low &= 0x000000ff;
297
                                low |= data << 8;
298
                                high &= ~0xff;
299
                                high |= data >> 24;
300
                                break;
301
                        case 2:
302
                                low &= 0x0000ffff;
303
                                low |= data << 16;
304
                                high &= ~0xffff;
305
                                high |= data >> 16;
306
                                break;
307
                        case 1:
308
                                low &= 0x00ffffff;
309
                                low |= data << 24;
310
                                high &= ~0xffffff;
311
                                high |= data >> 8;
312
                                break;
313
                }
314
                put_long(tsk, vma,addr & ~(sizeof(long)-1),high);
315
                put_long(tsk, vma_low,(addr+sizeof(long)) & ~(sizeof(long)-1),low);
316
        } else
317
                put_long(tsk, vma,addr,data);
318
        return 0;
319
}
320
 
321
asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
322
{
323
        struct task_struct *child;
324
        struct user * dummy;
325
 
326
        dummy = NULL;
327
 
328
        if (request == PTRACE_TRACEME) {
329
                /* are we already being traced? */
330
                if (current->flags & PF_PTRACED)
331
                        return -EPERM;
332
                /* set the ptrace bit in the process flags. */
333
                current->flags |= PF_PTRACED;
334
                return 0;
335
        }
336
        if (pid == 1)           /* you may not mess with init */
337
                return -EPERM;
338
        if (!(child = get_task(pid)))
339
                return -ESRCH;
340
        if (request == PTRACE_ATTACH) {
341
                if (child == current)
342
                        return -EPERM;
343
                if ((!child->dumpable ||
344
                    (current->uid != child->euid) ||
345
                    (current->uid != child->suid) ||
346
                    (current->uid != child->uid) ||
347
                    (current->gid != child->egid) ||
348
                    (current->gid != child->sgid) ||
349
                    (current->gid != child->gid)) && !suser())
350
                        return -EPERM;
351
                /* the same process cannot be attached many times */
352
                if (child->flags & PF_PTRACED)
353
                        return -EPERM;
354
                child->flags |= PF_PTRACED;
355
                if (child->p_pptr != current) {
356
                        REMOVE_LINKS(child);
357
                        child->p_pptr = current;
358
                        SET_LINKS(child);
359
                }
360
                send_sig(SIGSTOP, child, 1);
361
                return 0;
362
        }
363
        if (!(child->flags & PF_PTRACED))
364
                return -ESRCH;
365
        if (child->state != TASK_STOPPED) {
366
                if (request != PTRACE_KILL)
367
                        return -ESRCH;
368
        }
369
        if (child->p_pptr != current)
370
                return -ESRCH;
371
 
372
        switch (request) {
373
        /* when I and D space are separate, these will need to be fixed. */
374
                case PTRACE_PEEKTEXT: /* read word at location addr. */
375
                case PTRACE_PEEKDATA: {
376
                        unsigned long tmp;
377
                        int res;
378
 
379
                        res = read_long(child, addr, &tmp);
380
                        if (res < 0)
381
                                return res;
382
                        res = verify_area(VERIFY_WRITE, (void *) data, sizeof(long));
383
                        if (!res)
384
                                put_user(tmp, (unsigned long *) data);
385
                        return res;
386
                }
387
 
388
        /* read the word at location addr in the USER area. */
389
                case PTRACE_PEEKUSR: {
390
                        unsigned long tmp;
391
                        int res;
392
 
393
                        if ((addr & 3) || addr < 0 || addr >= sizeof(struct user))
394
                                return -EIO;
395
 
396
                        res = verify_area(VERIFY_WRITE, (void *) data,
397
                                          sizeof(long));
398
                        if (res)
399
                                return res;
400
                        tmp = 0;  /* Default return condition */
401
                        addr = addr >> 2; /* temporary hack. */
402
                        if (addr < 19) {
403
                                tmp = get_reg(child, addr);
404
                                if (addr == PT_SR)
405
                                        tmp >>= 16;
406
                        }
407
                        else if (addr >= 21 && addr < 49)
408
                                tmp = child->tss.fp[addr - 21];
409
                        else
410
                                return -EIO;
411
                        put_user(tmp,(unsigned long *) data);
412
                        return 0;
413
                }
414
 
415
      /* when I and D space are separate, this will have to be fixed. */
416
                case PTRACE_POKETEXT: /* write the word at location addr. */
417
                case PTRACE_POKEDATA:
418
                        return write_long(child,addr,data);
419
 
420
                case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
421
                        if ((addr & 3) || addr < 0 || addr >= sizeof(struct user))
422
                                return -EIO;
423
 
424
                        addr = addr >> 2; /* temporary hack. */
425
 
426
                        if (addr == PT_ORIG_D0)
427
                                return -EIO;
428
                        if (addr == PT_SR) {
429
                                data &= SR_MASK;
430
                                data <<= 16;
431
                                data |= get_reg(child, PT_SR) & ~(SR_MASK << 16);
432
                        }
433
                        if (addr < 19) {
434
                                if (put_reg(child, addr, data))
435
                                        return -EIO;
436
                                return 0;
437
                        }
438
                        if (addr >= 21 && addr < 48)
439
                        {
440
                                child->tss.fp[addr - 21] = data;
441
                                return 0;
442
                        }
443
                        return -EIO;
444
 
445
                case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
446
                case PTRACE_CONT: { /* restart after signal. */
447
                        long tmp;
448
 
449
                        if ((unsigned long) data >= NSIG)
450
                                return -EIO;
451
                        if (request == PTRACE_SYSCALL)
452
                                child->flags |= PF_TRACESYS;
453
                        else
454
                                child->flags &= ~PF_TRACESYS;
455
                        child->exit_code = data;
456
                        wake_up_process(child);
457
                        /* make sure the single step bit is not set. */
458
                        tmp = get_reg(child, PT_SR) & ~(TRACE_BITS << 16);
459
                        put_reg(child, PT_SR, tmp);
460
                        return 0;
461
                }
462
 
463
/*
464
 * make the child exit.  Best I can do is send it a sigkill.
465
 * perhaps it should be put in the status that it wants to
466
 * exit.
467
 */
468
                case PTRACE_KILL: {
469
                        long tmp;
470
 
471
                        if (child->state == TASK_ZOMBIE) /* already dead */
472
                                return 0;
473
                        wake_up_process(child);
474
                        child->exit_code = SIGKILL;
475
        /* make sure the single step bit is not set. */
476
                        tmp = get_reg(child, PT_SR) & ~(TRACE_BITS << 16);
477
                        put_reg(child, PT_SR, tmp);
478
                        return 0;
479
                }
480
 
481
                case PTRACE_SINGLESTEP: {  /* set the trap flag. */
482
                        long tmp;
483
 
484
                        if ((unsigned long) data >= NSIG)
485
                                return -EIO;
486
                        child->flags &= ~PF_TRACESYS;
487
                        tmp = get_reg(child, PT_SR) | (TRACE_BITS << 16);
488
                        put_reg(child, PT_SR, tmp);
489
 
490
                        wake_up_process(child);
491
                        child->exit_code = data;
492
        /* give it a chance to run. */
493
                        return 0;
494
                }
495
 
496
                case PTRACE_DETACH: { /* detach a process that was attached. */
497
                        long tmp;
498
 
499
                        if ((unsigned long) data >= NSIG)
500
                                return -EIO;
501
                        child->flags &= ~(PF_PTRACED|PF_TRACESYS);
502
                        wake_up_process(child);
503
                        child->exit_code = data;
504
                        REMOVE_LINKS(child);
505
                        child->p_pptr = child->p_opptr;
506
                        SET_LINKS(child);
507
                        /* make sure the single step bit is not set. */
508
                        tmp = get_reg(child, PT_SR) & ~(TRACE_BITS << 16);
509
                        put_reg(child, PT_SR, tmp);
510
                        return 0;
511
                }
512
 
513
                default:
514
                        return -EIO;
515
        }
516
}
517
 
518
asmlinkage void syscall_trace(void)
519
{
520
        if ((current->flags & (PF_PTRACED|PF_TRACESYS))
521
                        != (PF_PTRACED|PF_TRACESYS))
522
                return;
523
        current->exit_code = SIGTRAP;
524
        current->state = TASK_STOPPED;
525
        notify_parent(current);
526
        schedule();
527
        /*
528
         * this isn't the same as continuing with a signal, but it will do
529
         * for normal use.  strace only continues with a signal if the
530
         * stopping signal is not SIGTRAP.  -brl
531
         */
532
        if (current->exit_code)
533
                current->signal |= (1 << (current->exit_code - 1));
534
        current->exit_code = 0;
535
        return;
536
}

powered by: WebSVN 2.1.0

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