linux/arch/cris/arch-v10/kernel/ptrace.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2000-2003, Axis Communications AB.
   3 */
   4
   5#include <linux/kernel.h>
   6#include <linux/sched.h>
   7#include <linux/mm.h>
   8#include <linux/smp.h>
   9#include <linux/errno.h>
  10#include <linux/ptrace.h>
  11#include <linux/user.h>
  12#include <linux/signal.h>
  13#include <linux/security.h>
  14
  15#include <asm/uaccess.h>
  16#include <asm/page.h>
  17#include <asm/pgtable.h>
  18#include <asm/system.h>
  19#include <asm/processor.h>
  20
  21/* 
  22 * Determines which bits in DCCR the user has access to.
  23 * 1 = access, 0 = no access.
  24 */
  25#define DCCR_MASK 0x0000001f     /* XNZVC */
  26
  27/*
  28 * Get contents of register REGNO in task TASK.
  29 */
  30inline long get_reg(struct task_struct *task, unsigned int regno)
  31{
  32        /* USP is a special case, it's not in the pt_regs struct but
  33         * in the tasks thread struct
  34         */
  35
  36        if (regno == PT_USP)
  37                return task->thread.usp;
  38        else if (regno < PT_MAX)
  39                return ((unsigned long *)task_pt_regs(task))[regno];
  40        else
  41                return 0;
  42}
  43
  44/*
  45 * Write contents of register REGNO in task TASK.
  46 */
  47inline int put_reg(struct task_struct *task, unsigned int regno,
  48                          unsigned long data)
  49{
  50        if (regno == PT_USP)
  51                task->thread.usp = data;
  52        else if (regno < PT_MAX)
  53                ((unsigned long *)task_pt_regs(task))[regno] = data;
  54        else
  55                return -1;
  56        return 0;
  57}
  58
  59/*
  60 * Called by kernel/ptrace.c when detaching.
  61 *
  62 * Make sure the single step bit is not set.
  63 */
  64void 
  65ptrace_disable(struct task_struct *child)
  66{
  67       /* Todo - pending singlesteps? */
  68       clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
  69}
  70
  71/* 
  72 * Note that this implementation of ptrace behaves differently from vanilla
  73 * ptrace.  Contrary to what the man page says, in the PTRACE_PEEKTEXT,
  74 * PTRACE_PEEKDATA, and PTRACE_PEEKUSER requests the data variable is not
  75 * ignored.  Instead, the data variable is expected to point at a location
  76 * (in user space) where the result of the ptrace call is written (instead of
  77 * being returned).
  78 */
  79long arch_ptrace(struct task_struct *child, long request, long addr, long data)
  80{
  81        int ret;
  82        unsigned long __user *datap = (unsigned long __user *)data;
  83
  84        switch (request) {
  85                /* Read word at location address. */ 
  86                case PTRACE_PEEKTEXT:
  87                case PTRACE_PEEKDATA:
  88                        ret = generic_ptrace_peekdata(child, addr, data);
  89                        break;
  90
  91                /* Read the word at location address in the USER area. */
  92                case PTRACE_PEEKUSR: {
  93                        unsigned long tmp;
  94
  95                        ret = -EIO;
  96                        if ((addr & 3) || addr < 0 || addr > PT_MAX << 2)
  97                                break;
  98
  99                        tmp = get_reg(child, addr >> 2);
 100                        ret = put_user(tmp, datap);
 101                        break;
 102                }
 103                
 104                /* Write the word at location address. */
 105                case PTRACE_POKETEXT:
 106                case PTRACE_POKEDATA:
 107                        ret = generic_ptrace_pokedata(child, addr, data);
 108                        break;
 109 
 110                /* Write the word at location address in the USER area. */
 111                case PTRACE_POKEUSR:
 112                        ret = -EIO;
 113                        if ((addr & 3) || addr < 0 || addr > PT_MAX << 2)
 114                                break;
 115
 116                        addr >>= 2;
 117
 118                        if (addr == PT_DCCR) {
 119                                /* don't allow the tracing process to change stuff like
 120                                 * interrupt enable, kernel/user bit, dma enables etc.
 121                                 */
 122                                data &= DCCR_MASK;
 123                                data |= get_reg(child, PT_DCCR) & ~DCCR_MASK;
 124                        }
 125                        if (put_reg(child, addr, data))
 126                                break;
 127                        ret = 0;
 128                        break;
 129
 130                case PTRACE_SYSCALL:
 131                case PTRACE_CONT:
 132                        ret = -EIO;
 133                        
 134                        if (!valid_signal(data))
 135                                break;
 136                        
 137                        if (request == PTRACE_SYSCALL) {
 138                                set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
 139                        }
 140                        else {
 141                                clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
 142                        }
 143                        
 144                        child->exit_code = data;
 145                        
 146                        /* TODO: make sure any pending breakpoint is killed */
 147                        wake_up_process(child);
 148                        ret = 0;
 149                        
 150                        break;
 151                
 152                /* Make the child exit by sending it a sigkill. */
 153                case PTRACE_KILL:
 154                        ret = 0;
 155                        
 156                        if (child->exit_state == EXIT_ZOMBIE)
 157                                break;
 158                        
 159                        child->exit_code = SIGKILL;
 160                        
 161                        /* TODO: make sure any pending breakpoint is killed */
 162                        wake_up_process(child);
 163                        break;
 164
 165                /* Set the trap flag. */
 166                case PTRACE_SINGLESTEP:
 167                        ret = -EIO;
 168                        
 169                        if (!valid_signal(data))
 170                                break;
 171                        
 172                        clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
 173
 174                        /* TODO: set some clever breakpoint mechanism... */
 175
 176                        child->exit_code = data;
 177                        wake_up_process(child);
 178                        ret = 0;
 179                        break;
 180
 181                /* Get all GP registers from the child. */
 182                case PTRACE_GETREGS: {
 183                        int i;
 184                        unsigned long tmp;
 185                        
 186                        ret = 0;
 187                        for (i = 0; i <= PT_MAX; i++) {
 188                                tmp = get_reg(child, i);
 189                                
 190                                if (put_user(tmp, datap)) {
 191                                        ret = -EFAULT;
 192                                        break;
 193                                }
 194                                
 195                                data += sizeof(long);
 196                        }
 197
 198                        break;
 199                }
 200
 201                /* Set all GP registers in the child. */
 202                case PTRACE_SETREGS: {
 203                        int i;
 204                        unsigned long tmp;
 205                        
 206                        ret = 0;
 207                        for (i = 0; i <= PT_MAX; i++) {
 208                                if (get_user(tmp, datap)) {
 209                                        ret = -EFAULT;
 210                                        break;
 211                                }
 212                                
 213                                if (i == PT_DCCR) {
 214                                        tmp &= DCCR_MASK;
 215                                        tmp |= get_reg(child, PT_DCCR) & ~DCCR_MASK;
 216                                }
 217                                
 218                                put_reg(child, i, tmp);
 219                                data += sizeof(long);
 220                        }
 221                        
 222                        break;
 223                }
 224
 225                default:
 226                        ret = ptrace_request(child, request, addr, data);
 227                        break;
 228        }
 229
 230        return ret;
 231}
 232
 233void do_syscall_trace(void)
 234{
 235        if (!test_thread_flag(TIF_SYSCALL_TRACE))
 236                return;
 237        
 238        if (!(current->ptrace & PT_PTRACED))
 239                return;
 240        
 241        /* the 0x80 provides a way for the tracing parent to distinguish
 242           between a syscall stop and SIGTRAP delivery */
 243        ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
 244                                 ? 0x80 : 0));
 245        
 246        /*
 247         * This isn't the same as continuing with a signal, but it will do for
 248         * normal use.
 249         */
 250        if (current->exit_code) {
 251                send_sig(current->exit_code, current, 1);
 252                current->exit_code = 0;
 253        }
 254}
 255