linux-old/arch/ppc64/kernel/ptrace.c
<<
>>
Prefs
   1/*
   2 *  linux/arch/ppc64/kernel/ptrace.c
   3 *
   4 *  PowerPC version
   5 *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
   6 *
   7 *  Derived from "arch/m68k/kernel/ptrace.c"
   8 *  Copyright (C) 1994 by Hamish Macdonald
   9 *  Taken from linux/kernel/ptrace.c and modified for M680x0.
  10 *  linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds
  11 *
  12 * Modified by Cort Dougan (cort@hq.fsmlabs.com)
  13 * and Paul Mackerras (paulus@linuxcare.com.au).
  14 *
  15 * This file is subject to the terms and conditions of the GNU General
  16 * Public License.  See the file README.legal in the main directory of
  17 * this archive for more details.
  18 */
  19
  20#include <linux/kernel.h>
  21#include <linux/sched.h>
  22#include <linux/mm.h>
  23#include <linux/smp.h>
  24#include <linux/smp_lock.h>
  25#include <linux/errno.h>
  26#include <linux/ptrace.h>
  27#include <linux/user.h>
  28
  29#include <asm/uaccess.h>
  30#include <asm/page.h>
  31#include <asm/pgtable.h>
  32#include <asm/system.h>
  33
  34/*
  35 * Set of msr bits that gdb can change on behalf of a process.
  36 */
  37#define MSR_DEBUGCHANGE (MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1)
  38
  39/*
  40 * does not yet catch signals sent when the child dies.
  41 * in exit.c or in signal.c.
  42 */
  43
  44/*
  45 * Get contents of register REGNO in task TASK.
  46 */
  47static inline unsigned long get_reg(struct task_struct *task, int regno)
  48{
  49        if (regno < sizeof(struct pt_regs) / sizeof(unsigned long))
  50                return ((unsigned long *)task->thread.regs)[regno];
  51        return (0);
  52}
  53
  54/*
  55 * Write contents of register REGNO in task TASK.
  56 */
  57static inline int put_reg(struct task_struct *task, int regno,
  58                          unsigned long data)
  59{
  60        if (regno < PT_SOFTE) {
  61                if (regno == PT_MSR)
  62                        data = (data & MSR_DEBUGCHANGE)
  63                                | (task->thread.regs->msr & ~MSR_DEBUGCHANGE);
  64                ((unsigned long *)task->thread.regs)[regno] = data;
  65                return 0;
  66        }
  67        return -EIO;
  68}
  69
  70#ifdef CONFIG_ALTIVEC
  71/*
  72 * Get contents of AltiVec register state in task TASK
  73 */
  74static inline int get_vrregs(unsigned long data, struct task_struct *task)
  75{
  76        return (copy_to_user((void *)data,&task->thread.vr[0],
  77                        offsetof(struct thread_struct,vrsave[2])-
  78                             offsetof(struct thread_struct,vr[0])) ? -EFAULT : 0 );
  79}
  80
  81/*
  82 * Write contents of AltiVec register state into task TASK.
  83 */
  84static inline int set_vrregs(struct task_struct *task, unsigned long data)
  85{
  86        return (copy_from_user(&task->thread.vr[0],(void *)data,
  87                        offsetof(struct thread_struct,vrsave[2])-
  88                             offsetof(struct thread_struct,vr[0])) ? -EFAULT : 0 );
  89}
  90#endif
  91
  92static inline void
  93set_single_step(struct task_struct *task)
  94{
  95        struct pt_regs *regs = task->thread.regs;
  96        if (regs != NULL)
  97                regs->msr |= MSR_SE;
  98}
  99
 100static inline void
 101clear_single_step(struct task_struct *task)
 102{
 103        struct pt_regs *regs = task->thread.regs;
 104        if (regs != NULL)
 105                regs->msr &= ~MSR_SE;
 106}
 107
 108/*
 109 * Called by kernel/ptrace.c when detaching..
 110 *
 111 * Make sure single step bits etc are not set.
 112 */
 113void ptrace_disable(struct task_struct *child)
 114{
 115        /* make sure the single step bit is not set. */
 116        clear_single_step(child);
 117}
 118
 119int sys_ptrace(long request, long pid, long addr, long data)
 120{
 121        struct task_struct *child;
 122        int ret = -EPERM;
 123
 124        lock_kernel();
 125        if (request == PTRACE_TRACEME) {
 126                /* are we already being traced? */
 127                if (current->ptrace & PT_PTRACED)
 128                        goto out;
 129                /* set the ptrace bit in the process flags. */
 130                current->ptrace |= PT_PTRACED;
 131                ret = 0;
 132                goto out;
 133        }
 134        ret = -ESRCH;
 135        read_lock(&tasklist_lock);
 136        child = find_task_by_pid(pid);
 137        if (child)
 138                get_task_struct(child);
 139        read_unlock(&tasklist_lock);
 140        if (!child)
 141                goto out;
 142
 143        ret = -EPERM;
 144        if (pid == 1)           /* you may not mess with init */
 145                goto out_tsk;
 146
 147        if (request == PTRACE_ATTACH) {
 148                ret = ptrace_attach(child);
 149                goto out_tsk;
 150        }
 151
 152        ret = ptrace_check_attach(child, request == PTRACE_KILL);
 153        if (ret < 0)
 154                goto out_tsk;
 155
 156        switch (request) {
 157        /* when I and D space are separate, these will need to be fixed. */
 158        case PTRACE_PEEKTEXT: /* read word at location addr. */ 
 159        case PTRACE_PEEKDATA: {
 160                unsigned long tmp;
 161                int copied;
 162
 163                copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
 164                ret = -EIO;
 165                if (copied != sizeof(tmp))
 166                        break;
 167                ret = put_user(tmp,(unsigned long *) data);
 168                break;
 169        }
 170
 171        /* read the word at location addr in the USER area. */
 172        case PTRACE_PEEKUSR: {
 173                unsigned long index;
 174                unsigned long tmp;
 175
 176                ret = -EIO;
 177                /* convert to index and check */
 178                index = (unsigned long) addr >> 3;
 179                if ((addr & 7) || (index > PT_FPSCR))
 180                        break;
 181
 182                if (index < PT_FPR0) {
 183                        tmp = get_reg(child, (int)index);
 184                } else {
 185                        if (child->thread.regs->msr & MSR_FP)
 186                                giveup_fpu(child);
 187                        tmp = ((unsigned long *)child->thread.fpr)[index - PT_FPR0];
 188                }
 189                ret = put_user(tmp,(unsigned long *) data);
 190                break;
 191        }
 192
 193        /* If I and D space are separate, this will have to be fixed. */
 194        case PTRACE_POKETEXT: /* write the word at location addr. */
 195        case PTRACE_POKEDATA:
 196                ret = 0;
 197                if (access_process_vm(child, addr, &data, sizeof(data), 1)
 198                                == sizeof(data))
 199                        break;
 200                ret = -EIO;
 201                break;
 202
 203        /* write the word at location addr in the USER area */
 204        case PTRACE_POKEUSR: {
 205                unsigned long index;
 206
 207                ret = -EIO;
 208                /* convert to index and check */
 209                index = (unsigned long) addr >> 3;
 210                if ((addr & 7) || (index > PT_FPSCR))
 211                        break;
 212
 213                if (index == PT_ORIG_R3)
 214                        break;
 215                if (index < PT_FPR0) {
 216                        ret = put_reg(child, index, data);
 217                } else {
 218                        if (child->thread.regs->msr & MSR_FP)
 219                                giveup_fpu(child);
 220                        ((unsigned long *)child->thread.fpr)[index - PT_FPR0] = data;
 221                        ret = 0;
 222                }
 223                break;
 224        }
 225
 226        case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
 227        case PTRACE_CONT: { /* restart after signal. */
 228                ret = -EIO;
 229                if ((unsigned long) data > _NSIG)
 230                        break;
 231                if (request == PTRACE_SYSCALL)
 232                        child->ptrace |= PT_TRACESYS;
 233                else
 234                        child->ptrace &= ~PT_TRACESYS;
 235                child->exit_code = data;
 236                /* make sure the single step bit is not set. */
 237                clear_single_step(child);
 238                wake_up_process(child);
 239                ret = 0;
 240                break;
 241        }
 242
 243        /*
 244         * make the child exit.  Best I can do is send it a sigkill.
 245         * perhaps it should be put in the status that it wants to
 246         * exit.
 247         */
 248        case PTRACE_KILL: {
 249                ret = 0;
 250                if (child->state == TASK_ZOMBIE)        /* already dead */
 251                        break;
 252                child->exit_code = SIGKILL;
 253                /* make sure the single step bit is not set. */
 254                clear_single_step(child);
 255                wake_up_process(child);
 256                break;
 257        }
 258
 259        case PTRACE_SINGLESTEP: {  /* set the trap flag. */
 260                ret = -EIO;
 261                if ((unsigned long) data > _NSIG)
 262                        break;
 263                child->ptrace &= ~PT_TRACESYS;
 264                set_single_step(child);
 265                child->exit_code = data;
 266                /* give it a chance to run. */
 267                wake_up_process(child);
 268                ret = 0;
 269                break;
 270        }
 271
 272        case PTRACE_DETACH:
 273                ret = ptrace_detach(child, data);
 274                break;
 275
 276        case PPC_PTRACE_GETREGS: { /* Get GPRs 0 - 31. */
 277                int i;
 278                unsigned long *reg = &((unsigned long *)child->thread.regs)[0];
 279                unsigned long *tmp = (unsigned long *)addr;
 280
 281                for (i = 0; i < 32; i++) {
 282                        ret = put_user(*reg, tmp);
 283                        if (ret)
 284                                break;
 285                        reg++;
 286                        tmp++;
 287                }
 288                break;
 289        }
 290
 291        case PPC_PTRACE_SETREGS: { /* Set GPRs 0 - 31. */
 292                int i;
 293                unsigned long *reg = &((unsigned long *)child->thread.regs)[0];
 294                unsigned long *tmp = (unsigned long *)addr;
 295
 296                for (i = 0; i < 32; i++) {
 297                        ret = get_user(*reg, tmp);
 298                        if (ret)
 299                                break;
 300                        reg++;
 301                        tmp++;
 302                }
 303                break;
 304        }
 305
 306        case PPC_PTRACE_GETFPREGS: { /* Get FPRs 0 - 31. */
 307                int i;
 308                unsigned long *reg = &((unsigned long *)child->thread.fpr)[0];
 309                unsigned long *tmp = (unsigned long *)addr;
 310
 311                if (child->thread.regs->msr & MSR_FP)
 312                        giveup_fpu(child);
 313
 314                for (i = 0; i < 32; i++) {
 315                        ret = put_user(*reg, tmp);
 316                        if (ret)
 317                                break;
 318                        reg++;
 319                        tmp++;
 320                }
 321                break;
 322        }
 323
 324        case PPC_PTRACE_SETFPREGS: { /* Get FPRs 0 - 31. */
 325                int i;
 326                unsigned long *reg = &((unsigned long *)child->thread.fpr)[0];
 327                unsigned long *tmp = (unsigned long *)addr;
 328
 329                if (child->thread.regs->msr & MSR_FP)
 330                        giveup_fpu(child);
 331
 332                for (i = 0; i < 32; i++) {
 333                        ret = get_user(*reg, tmp);
 334                        if (ret)
 335                                break;
 336                        reg++;
 337                        tmp++;
 338                }
 339                break;
 340        }
 341#ifdef CONFIG_ALTIVEC
 342        case PTRACE_GETVRREGS:
 343                /* Get the child altivec register state. */
 344                if (child->thread.regs->msr & MSR_VEC)
 345                        giveup_altivec(child);
 346                ret = get_vrregs(data, child);
 347                break;
 348
 349        case PTRACE_SETVRREGS:
 350                /* Set the child altivec register state. */
 351                /* this is to clear the MSR_VEC bit to force a reload
 352                 * of register state from memory */
 353                if (child->thread.regs->msr & MSR_VEC)
 354                        giveup_altivec(child);
 355                ret = set_vrregs(child,data);
 356                break;
 357#endif
 358
 359        default:
 360                ret = -EIO;
 361                break;
 362        }
 363out_tsk:
 364        free_task_struct(child);
 365out:
 366        unlock_kernel();
 367        return ret;
 368}
 369
 370void syscall_trace(void)
 371{
 372  if ((current->ptrace & (PT_PTRACED|PT_TRACESYS))
 373                        != (PT_PTRACED|PT_TRACESYS))
 374                return;
 375        current->exit_code = SIGTRAP;
 376        current->state = TASK_STOPPED;
 377        notify_parent(current, SIGCHLD);
 378        schedule();
 379        /*
 380         * this isn't the same as continuing with a signal, but it will do
 381         * for normal use.  strace only continues with a signal if the
 382         * stopping signal is not SIGTRAP.  -brl
 383         */
 384        if (current->exit_code) {
 385                send_sig(current->exit_code, current, 1);
 386                current->exit_code = 0;
 387        }
 388}
 389
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.