linux/arch/tile/kernel/ptrace.c
<<
>>
Prefs
   1/*
   2 * Copyright 2010 Tilera Corporation. All Rights Reserved.
   3 *
   4 *   This program is free software; you can redistribute it and/or
   5 *   modify it under the terms of the GNU General Public License
   6 *   as published by the Free Software Foundation, version 2.
   7 *
   8 *   This program is distributed in the hope that it will be useful, but
   9 *   WITHOUT ANY WARRANTY; without even the implied warranty of
  10 *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
  11 *   NON INFRINGEMENT.  See the GNU General Public License for
  12 *   more details.
  13 *
  14 * Copied from i386: Ross Biro 1/23/92
  15 */
  16
  17#include <linux/kernel.h>
  18#include <linux/ptrace.h>
  19#include <linux/kprobes.h>
  20#include <linux/compat.h>
  21#include <linux/uaccess.h>
  22#include <asm/traps.h>
  23
  24void user_enable_single_step(struct task_struct *child)
  25{
  26        set_tsk_thread_flag(child, TIF_SINGLESTEP);
  27}
  28
  29void user_disable_single_step(struct task_struct *child)
  30{
  31        clear_tsk_thread_flag(child, TIF_SINGLESTEP);
  32}
  33
  34/*
  35 * Called by kernel/ptrace.c when detaching..
  36 */
  37void ptrace_disable(struct task_struct *child)
  38{
  39        clear_tsk_thread_flag(child, TIF_SINGLESTEP);
  40
  41        /*
  42         * These two are currently unused, but will be set by arch_ptrace()
  43         * and used in the syscall assembly when we do support them.
  44         */
  45        clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
  46}
  47
  48long arch_ptrace(struct task_struct *child, long request,
  49                 unsigned long addr, unsigned long data)
  50{
  51        unsigned long __user *datap = (long __user __force *)data;
  52        unsigned long tmp;
  53        long ret = -EIO;
  54        char *childreg;
  55        struct pt_regs copyregs;
  56        int ex1_offset;
  57
  58        switch (request) {
  59
  60        case PTRACE_PEEKUSR:  /* Read register from pt_regs. */
  61                if (addr >= PTREGS_SIZE)
  62                        break;
  63                childreg = (char *)task_pt_regs(child) + addr;
  64#ifdef CONFIG_COMPAT
  65                if (is_compat_task()) {
  66                        if (addr & (sizeof(compat_long_t)-1))
  67                                break;
  68                        ret = put_user(*(compat_long_t *)childreg,
  69                                       (compat_long_t __user *)datap);
  70                } else
  71#endif
  72                {
  73                        if (addr & (sizeof(long)-1))
  74                                break;
  75                        ret = put_user(*(long *)childreg, datap);
  76                }
  77                break;
  78
  79        case PTRACE_POKEUSR:  /* Write register in pt_regs. */
  80                if (addr >= PTREGS_SIZE)
  81                        break;
  82                childreg = (char *)task_pt_regs(child) + addr;
  83
  84                /* Guard against overwrites of the privilege level. */
  85                ex1_offset = PTREGS_OFFSET_EX1;
  86#if defined(CONFIG_COMPAT) && defined(__BIG_ENDIAN)
  87                if (is_compat_task())   /* point at low word */
  88                        ex1_offset += sizeof(compat_long_t);
  89#endif
  90                if (addr == ex1_offset)
  91                        data = PL_ICS_EX1(USER_PL, EX1_ICS(data));
  92
  93#ifdef CONFIG_COMPAT
  94                if (is_compat_task()) {
  95                        if (addr & (sizeof(compat_long_t)-1))
  96                                break;
  97                        *(compat_long_t *)childreg = data;
  98                } else
  99#endif
 100                {
 101                        if (addr & (sizeof(long)-1))
 102                                break;
 103                        *(long *)childreg = data;
 104                }
 105                ret = 0;
 106                break;
 107
 108        case PTRACE_GETREGS:  /* Get all registers from the child. */
 109                if (copy_to_user(datap, task_pt_regs(child),
 110                                 sizeof(struct pt_regs)) == 0) {
 111                        ret = 0;
 112                }
 113                break;
 114
 115        case PTRACE_SETREGS:  /* Set all registers in the child. */
 116                if (copy_from_user(&copyregs, datap,
 117                                   sizeof(struct pt_regs)) == 0) {
 118                        copyregs.ex1 =
 119                                PL_ICS_EX1(USER_PL, EX1_ICS(copyregs.ex1));
 120                        *task_pt_regs(child) = copyregs;
 121                        ret = 0;
 122                }
 123                break;
 124
 125        case PTRACE_GETFPREGS:  /* Get the child FPU state. */
 126        case PTRACE_SETFPREGS:  /* Set the child FPU state. */
 127                break;
 128
 129        case PTRACE_SETOPTIONS:
 130                /* Support TILE-specific ptrace options. */
 131                child->ptrace &= ~PT_TRACE_MASK_TILE;
 132                tmp = data & PTRACE_O_MASK_TILE;
 133                data &= ~PTRACE_O_MASK_TILE;
 134                ret = ptrace_request(child, request, addr, data);
 135                if (tmp & PTRACE_O_TRACEMIGRATE)
 136                        child->ptrace |= PT_TRACE_MIGRATE;
 137                break;
 138
 139        default:
 140#ifdef CONFIG_COMPAT
 141                if (task_thread_info(current)->status & TS_COMPAT) {
 142                        ret = compat_ptrace_request(child, request,
 143                                                    addr, data);
 144                        break;
 145                }
 146#endif
 147                ret = ptrace_request(child, request, addr, data);
 148                break;
 149        }
 150
 151        return ret;
 152}
 153
 154#ifdef CONFIG_COMPAT
 155/* Not used; we handle compat issues in arch_ptrace() directly. */
 156long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
 157                               compat_ulong_t addr, compat_ulong_t data)
 158{
 159        BUG();
 160}
 161#endif
 162
 163void do_syscall_trace(void)
 164{
 165        if (!test_thread_flag(TIF_SYSCALL_TRACE))
 166                return;
 167
 168        if (!(current->ptrace & PT_PTRACED))
 169                return;
 170
 171        /*
 172         * The 0x80 provides a way for the tracing parent to distinguish
 173         * between a syscall stop and SIGTRAP delivery
 174         */
 175        ptrace_notify(SIGTRAP|((current->ptrace & PT_TRACESYSGOOD) ? 0x80 : 0));
 176
 177        /*
 178         * this isn't the same as continuing with a signal, but it will do
 179         * for normal use.  strace only continues with a signal if the
 180         * stopping signal is not SIGTRAP.  -brl
 181         */
 182        if (current->exit_code) {
 183                send_sig(current->exit_code, current, 1);
 184                current->exit_code = 0;
 185        }
 186}
 187
 188void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs, int error_code)
 189{
 190        struct siginfo info;
 191
 192        memset(&info, 0, sizeof(info));
 193        info.si_signo = SIGTRAP;
 194        info.si_code  = TRAP_BRKPT;
 195        info.si_addr  = (void __user *) regs->pc;
 196
 197        /* Send us the fakey SIGTRAP */
 198        force_sig_info(SIGTRAP, &info, tsk);
 199}
 200
 201/* Handle synthetic interrupt delivered only by the simulator. */
 202void __kprobes do_breakpoint(struct pt_regs* regs, int fault_num)
 203{
 204        send_sigtrap(current, regs, fault_num);
 205}
 206