linux-old/arch/cris/kernel/ptrace.c
<<
>>
Prefs
   1/*
   2 *  linux/arch/cris/kernel/ptrace.c
   3 *
   4 * Parts taken from the m68k port.
   5 * 
   6 * Copyright (c) 2000, 2001 Axis Communications AB
   7 *
   8 * Authors:   Bjorn Wesen
   9 *
  10 * $Log: ptrace.c,v $
  11 * Revision 1.8  2001/11/12 18:26:21  pkj
  12 * Fixed compiler warnings.
  13 *
  14 * Revision 1.7  2001/09/26 11:53:49  bjornw
  15 * PTRACE_DETACH works more simple in 2.4.10
  16 *
  17 * Revision 1.6  2001/07/25 16:08:47  bjornw
  18 * PTRACE_ATTACH bulk moved into arch-independant code in 2.4.7
  19 *
  20 * Revision 1.5  2001/03/26 14:24:28  orjanf
  21 * * Changed loop condition.
  22 * * Added comment documenting non-standard ptrace behaviour.
  23 *
  24 * Revision 1.4  2001/03/20 19:44:41  bjornw
  25 * Use the user_regs macro instead of thread.esp0
  26 *
  27 * Revision 1.3  2000/12/18 23:45:25  bjornw
  28 * Linux/CRIS first version
  29 *
  30 *
  31 */
  32
  33#include <linux/kernel.h>
  34#include <linux/sched.h>
  35#include <linux/mm.h>
  36#include <linux/smp.h>
  37#include <linux/smp_lock.h>
  38#include <linux/errno.h>
  39#include <linux/ptrace.h>
  40#include <linux/user.h>
  41
  42#include <asm/uaccess.h>
  43#include <asm/page.h>
  44#include <asm/pgtable.h>
  45#include <asm/system.h>
  46#include <asm/processor.h>
  47
  48/*
  49 * does not yet catch signals sent when the child dies.
  50 * in exit.c or in signal.c.
  51 */
  52
  53/* determines which bits in DCCR the user has access to. */
  54/* 1 = access 0 = no access */
  55#define DCCR_MASK 0x0000001f     /* XNZVC */
  56
  57/*
  58 * Get contents of register REGNO in task TASK.
  59 */
  60static inline long get_reg(struct task_struct *task, unsigned int regno)
  61{
  62        /* USP is a special case, it's not in the pt_regs struct but
  63         * in the tasks thread struct
  64         */
  65
  66        if (regno == PT_USP)
  67                return task->thread.usp;
  68        else if (regno < PT_MAX)
  69                return ((unsigned long *)user_regs(task))[regno];
  70        else
  71                return 0;
  72}
  73
  74/*
  75 * Write contents of register REGNO in task TASK.
  76 */
  77static inline int put_reg(struct task_struct *task, unsigned int regno,
  78                          unsigned long data)
  79{
  80        if (regno == PT_USP)
  81                task->thread.usp = data;
  82        else if (regno < PT_MAX)
  83                ((unsigned long *)user_regs(task))[regno] = data;
  84        else
  85                return -1;
  86        return 0;
  87}
  88
  89/*
  90 * Called by kernel/ptrace.c when detaching..
  91 *
  92 * Make sure the single step bit is not set.
  93 */
  94void ptrace_disable(struct task_struct *child)
  95{
  96       /* Todo - pending singlesteps? */
  97}
  98
  99/* Note that this implementation of ptrace behaves differently from vanilla
 100 * ptrace.  Contrary to what the man page says, in the PTRACE_PEEKTEXT,
 101 * PTRACE_PEEKDATA, and PTRACE_PEEKUSER requests the data variable is not
 102 * ignored.  Instead, the data variable is expected to point at a location
 103 * (in user space) where the result of the ptrace call is written (instead of
 104 * being returned).
 105 */
 106
 107asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
 108{
 109        struct task_struct *child;
 110        int ret;
 111
 112        lock_kernel();
 113        ret = -EPERM;
 114        if (request == PTRACE_TRACEME) {
 115                /* are we already being traced? */
 116                if (current->ptrace & PT_PTRACED)
 117                        goto out;
 118                /* set the ptrace bit in the process flags. */
 119                current->ptrace |= PT_PTRACED;
 120                ret = 0;
 121                goto out;
 122        }
 123        ret = -ESRCH;
 124        read_lock(&tasklist_lock);
 125        child = find_task_by_pid(pid);
 126        if (child)
 127                get_task_struct(child);
 128        read_unlock(&tasklist_lock);
 129        if (!child)
 130                goto out;
 131        ret = -EPERM;
 132        if (pid == 1)           /* you may not mess with init */
 133                goto out_tsk;
 134        if (request == PTRACE_ATTACH) {
 135                ret = ptrace_attach(child);
 136                goto out_tsk;
 137        }
 138        ret = -ESRCH;
 139        if (!(child->ptrace & PT_PTRACED))
 140                goto out_tsk;
 141        if (child->state != TASK_STOPPED) {
 142                if (request != PTRACE_KILL)
 143                        goto out_tsk;
 144        }
 145        if (child->p_pptr != current)
 146                goto out_tsk;
 147
 148        switch (request) {
 149        /* when I and D space are separate, these will need to be fixed. */
 150                case PTRACE_PEEKTEXT: /* read word at location addr. */ 
 151                case PTRACE_PEEKDATA: {
 152                        unsigned long tmp;
 153                        int copied;
 154
 155                        copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
 156                        ret = -EIO;
 157                        if (copied != sizeof(tmp))
 158                                break;
 159                        ret = put_user(tmp,(unsigned long *) data);
 160                        break;
 161                }
 162
 163                /* read the word at location addr in the USER area. */
 164                case PTRACE_PEEKUSR: {
 165                        unsigned long tmp;
 166                        
 167                        ret = -EIO;
 168                        if ((addr & 3) || addr < 0 || addr >= sizeof(struct user))
 169                                break;
 170                        
 171                        tmp = 0;  /* Default return condition */
 172                        ret = -EIO;
 173                        if (addr < sizeof(struct pt_regs)) {
 174                                tmp = get_reg(child, addr >> 2);
 175                                ret = put_user(tmp, (unsigned long *)data);
 176                        }
 177                        break;
 178                }
 179
 180                /* when I and D space are separate, this will have to be fixed. */
 181                case PTRACE_POKETEXT: /* write the word at location addr. */
 182                case PTRACE_POKEDATA:
 183                        ret = 0;
 184                        if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
 185                                break;
 186                        ret = -EIO;
 187                        break;
 188
 189                case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
 190                        ret = -EIO;
 191                        if ((addr & 3) || addr < 0 || addr >= sizeof(struct user))
 192                                break;
 193
 194                        if (addr < sizeof(struct pt_regs)) {
 195                                addr >>= 2;
 196
 197                                if (addr == PT_DCCR) {
 198                                /* don't allow the tracing process to change stuff like
 199                                 * interrupt enable, kernel/user bit, dma enables etc.
 200                                 */
 201                                        data &= DCCR_MASK;
 202                                        data |= get_reg(child, PT_DCCR) & ~DCCR_MASK;
 203                                }
 204                                if (put_reg(child, addr, data))
 205                                        break;
 206                                ret = 0;
 207                        }
 208                        break;
 209
 210                case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
 211                case PTRACE_CONT: /* restart after signal. */
 212                        ret = -EIO;
 213                        if ((unsigned long) data > _NSIG)
 214                                break;
 215                        if (request == PTRACE_SYSCALL)
 216                                child->ptrace |= PT_TRACESYS;
 217                        else
 218                                child->ptrace &= ~PT_TRACESYS;
 219                        child->exit_code = data;
 220                        /* TODO: make sure any pending breakpoint is killed */
 221                        wake_up_process(child);
 222                        ret = 0;
 223                        break;
 224
 225/*
 226 * make the child exit.  Best I can do is send it a sigkill. 
 227 * perhaps it should be put in the status that it wants to 
 228 * exit.
 229 */
 230                case PTRACE_KILL:
 231                        ret = 0;
 232                        if (child->state == TASK_ZOMBIE) /* already dead */
 233                                break;
 234                        child->exit_code = SIGKILL;
 235                        /* TODO: make sure any pending breakpoint is killed */
 236                        wake_up_process(child);
 237                        break;
 238
 239                case PTRACE_SINGLESTEP: /* set the trap flag. */
 240                        ret = -EIO;
 241                        if ((unsigned long) data > _NSIG)
 242                                break;
 243                        child->ptrace &= ~PT_TRACESYS;
 244
 245                        /* TODO: set some clever breakpoint mechanism... */
 246
 247                        child->exit_code = data;
 248                        /* give it a chance to run. */
 249                        wake_up_process(child);
 250                        ret = 0;
 251                        break;
 252
 253                case PTRACE_DETACH:
 254                        ret = ptrace_detach(child, data);
 255                        break;
 256
 257                case PTRACE_GETREGS: { /* Get all gp regs from the child. */
 258                        int i;
 259                        unsigned long tmp;
 260                        for (i = 0; i <= PT_MAX; i++) {
 261                                tmp = get_reg(child, i);
 262                                if (put_user(tmp, (unsigned long *) data)) {
 263                                        ret = -EFAULT;
 264                                        break;
 265                                }
 266                                data += sizeof(long);
 267                        }
 268                        ret = 0;
 269                        break;
 270                }
 271
 272                case PTRACE_SETREGS: { /* Set all gp regs in the child. */
 273                        int i;
 274                        unsigned long tmp;
 275                        for (i = 0; i <= PT_MAX; i++) {
 276                                if (get_user(tmp, (unsigned long *) data)) {
 277                                        ret = -EFAULT;
 278                                        break;
 279                                }
 280                                if (i == PT_DCCR) {
 281                                        tmp &= DCCR_MASK;
 282                                        tmp |= get_reg(child, PT_DCCR) & ~DCCR_MASK;
 283                                }
 284                                put_reg(child, i, tmp);
 285                                data += sizeof(long);
 286                        }
 287                        ret = 0;
 288                        break;
 289                }
 290
 291                default:
 292                        ret = -EIO;
 293                        break;
 294        }
 295out_tsk:
 296        free_task_struct(child);
 297out:
 298        unlock_kernel();
 299        return ret;
 300}
 301
 302asmlinkage void syscall_trace(void)
 303{
 304        if ((current->ptrace & (PT_PTRACED | PT_TRACESYS)) !=
 305            (PT_PTRACED | PT_TRACESYS))
 306                return;
 307        /* TODO: make a way to distinguish between a syscall stop and SIGTRAP
 308         * delivery like in the i386 port ? 
 309         */
 310        current->exit_code = SIGTRAP;
 311        current->state = TASK_STOPPED;
 312        notify_parent(current, SIGCHLD);
 313        schedule();
 314        /*
 315         * this isn't the same as continuing with a signal, but it will do
 316         * for normal use.  strace only continues with a signal if the
 317         * stopping signal is not SIGTRAP.  -brl
 318         */
 319        if (current->exit_code) {
 320                send_sig(current->exit_code, current, 1);
 321                current->exit_code = 0;
 322        }
 323}
 324
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.