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 <linux/regset.h>
  23#include <linux/elf.h>
  24#include <asm/traps.h>
  25#include <arch/chip.h>
  26
  27void user_enable_single_step(struct task_struct *child)
  28{
  29        set_tsk_thread_flag(child, TIF_SINGLESTEP);
  30}
  31
  32void user_disable_single_step(struct task_struct *child)
  33{
  34        clear_tsk_thread_flag(child, TIF_SINGLESTEP);
  35}
  36
  37/*
  38 * Called by kernel/ptrace.c when detaching..
  39 */
  40void ptrace_disable(struct task_struct *child)
  41{
  42        clear_tsk_thread_flag(child, TIF_SINGLESTEP);
  43
  44        /*
  45         * These two are currently unused, but will be set by arch_ptrace()
  46         * and used in the syscall assembly when we do support them.
  47         */
  48        clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
  49}
  50
  51/*
  52 * Get registers from task and ready the result for userspace.
  53 * Note that we localize the API issues to getregs() and putregs() at
  54 * some cost in performance, e.g. we need a full pt_regs copy for
  55 * PEEKUSR, and two copies for POKEUSR.  But in general we expect
  56 * GETREGS/PUTREGS to be the API of choice anyway.
  57 */
  58static char *getregs(struct task_struct *child, struct pt_regs *uregs)
  59{
  60        *uregs = *task_pt_regs(child);
  61
  62        /* Set up flags ABI bits. */
  63        uregs->flags = 0;
  64#ifdef CONFIG_COMPAT
  65        if (task_thread_info(child)->status & TS_COMPAT)
  66                uregs->flags |= PT_FLAGS_COMPAT;
  67#endif
  68
  69        return (char *)uregs;
  70}
  71
  72/* Put registers back to task. */
  73static void putregs(struct task_struct *child, struct pt_regs *uregs)
  74{
  75        struct pt_regs *regs = task_pt_regs(child);
  76
  77        /* Don't allow overwriting the kernel-internal flags word. */
  78        uregs->flags = regs->flags;
  79
  80        /* Only allow setting the ICS bit in the ex1 word. */
  81        uregs->ex1 = PL_ICS_EX1(USER_PL, EX1_ICS(uregs->ex1));
  82
  83        *regs = *uregs;
  84}
  85
  86enum tile_regset {
  87        REGSET_GPR,
  88};
  89
  90static int tile_gpr_get(struct task_struct *target,
  91                          const struct user_regset *regset,
  92                          unsigned int pos, unsigned int count,
  93                          void *kbuf, void __user *ubuf)
  94{
  95        struct pt_regs regs;
  96
  97        getregs(target, &regs);
  98
  99        return user_regset_copyout(&pos, &count, &kbuf, &ubuf, &regs, 0,
 100                                   sizeof(regs));
 101}
 102
 103static int tile_gpr_set(struct task_struct *target,
 104                          const struct user_regset *regset,
 105                          unsigned int pos, unsigned int count,
 106                          const void *kbuf, const void __user *ubuf)
 107{
 108        int ret;
 109        struct pt_regs regs;
 110
 111        ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &regs, 0,
 112                                 sizeof(regs));
 113        if (ret)
 114                return ret;
 115
 116        putregs(target, &regs);
 117
 118        return 0;
 119}
 120
 121static const struct user_regset tile_user_regset[] = {
 122        [REGSET_GPR] = {
 123                .core_note_type = NT_PRSTATUS,
 124                .n = ELF_NGREG,
 125                .size = sizeof(elf_greg_t),
 126                .align = sizeof(elf_greg_t),
 127                .get = tile_gpr_get,
 128                .set = tile_gpr_set,
 129        },
 130};
 131
 132static const struct user_regset_view tile_user_regset_view = {
 133        .name = CHIP_ARCH_NAME,
 134        .e_machine = ELF_ARCH,
 135        .ei_osabi = ELF_OSABI,
 136        .regsets = tile_user_regset,
 137        .n = ARRAY_SIZE(tile_user_regset),
 138};
 139
 140const struct user_regset_view *task_user_regset_view(struct task_struct *task)
 141{
 142        return &tile_user_regset_view;
 143}
 144
 145long arch_ptrace(struct task_struct *child, long request,
 146                 unsigned long addr, unsigned long data)
 147{
 148        unsigned long __user *datap = (long __user __force *)data;
 149        unsigned long tmp;
 150        long ret = -EIO;
 151        char *childreg;
 152        struct pt_regs copyregs;
 153
 154        switch (request) {
 155
 156        case PTRACE_PEEKUSR:  /* Read register from pt_regs. */
 157                if (addr >= PTREGS_SIZE)
 158                        break;
 159                childreg = getregs(child, &copyregs) + addr;
 160#ifdef CONFIG_COMPAT
 161                if (is_compat_task()) {
 162                        if (addr & (sizeof(compat_long_t)-1))
 163                                break;
 164                        ret = put_user(*(compat_long_t *)childreg,
 165                                       (compat_long_t __user *)datap);
 166                } else
 167#endif
 168                {
 169                        if (addr & (sizeof(long)-1))
 170                                break;
 171                        ret = put_user(*(long *)childreg, datap);
 172                }
 173                break;
 174
 175        case PTRACE_POKEUSR:  /* Write register in pt_regs. */
 176                if (addr >= PTREGS_SIZE)
 177                        break;
 178                childreg = getregs(child, &copyregs) + addr;
 179#ifdef CONFIG_COMPAT
 180                if (is_compat_task()) {
 181                        if (addr & (sizeof(compat_long_t)-1))
 182                                break;
 183                        *(compat_long_t *)childreg = data;
 184                } else
 185#endif
 186                {
 187                        if (addr & (sizeof(long)-1))
 188                                break;
 189                        *(long *)childreg = data;
 190                }
 191                putregs(child, &copyregs);
 192                ret = 0;
 193                break;
 194
 195        case PTRACE_GETREGS:  /* Get all registers from the child. */
 196                ret = copy_regset_to_user(child, &tile_user_regset_view,
 197                                          REGSET_GPR, 0,
 198                                          sizeof(struct pt_regs), datap);
 199                break;
 200
 201        case PTRACE_SETREGS:  /* Set all registers in the child. */
 202                ret = copy_regset_from_user(child, &tile_user_regset_view,
 203                                            REGSET_GPR, 0,
 204                                            sizeof(struct pt_regs), datap);
 205                break;
 206
 207        case PTRACE_GETFPREGS:  /* Get the child FPU state. */
 208        case PTRACE_SETFPREGS:  /* Set the child FPU state. */
 209                break;
 210
 211        case PTRACE_SETOPTIONS:
 212                /* Support TILE-specific ptrace options. */
 213                BUILD_BUG_ON(PTRACE_O_MASK_TILE & PTRACE_O_MASK);
 214                tmp = data & PTRACE_O_MASK_TILE;
 215                data &= ~PTRACE_O_MASK_TILE;
 216                ret = ptrace_request(child, request, addr, data);
 217                if (ret == 0) {
 218                        unsigned int flags = child->ptrace;
 219                        flags &= ~(PTRACE_O_MASK_TILE << PT_OPT_FLAG_SHIFT);
 220                        flags |= (tmp << PT_OPT_FLAG_SHIFT);
 221                        child->ptrace = flags;
 222                }
 223                break;
 224
 225        default:
 226#ifdef CONFIG_COMPAT
 227                if (task_thread_info(current)->status & TS_COMPAT) {
 228                        ret = compat_ptrace_request(child, request,
 229                                                    addr, data);
 230                        break;
 231                }
 232#endif
 233                ret = ptrace_request(child, request, addr, data);
 234                break;
 235        }
 236
 237        return ret;
 238}
 239
 240#ifdef CONFIG_COMPAT
 241/* Not used; we handle compat issues in arch_ptrace() directly. */
 242long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
 243                               compat_ulong_t addr, compat_ulong_t data)
 244{
 245        BUG();
 246}
 247#endif
 248
 249void do_syscall_trace(void)
 250{
 251        if (!test_thread_flag(TIF_SYSCALL_TRACE))
 252                return;
 253
 254        if (!(current->ptrace & PT_PTRACED))
 255                return;
 256
 257        /*
 258         * The 0x80 provides a way for the tracing parent to distinguish
 259         * between a syscall stop and SIGTRAP delivery
 260         */
 261        ptrace_notify(SIGTRAP|((current->ptrace & PT_TRACESYSGOOD) ? 0x80 : 0));
 262
 263        /*
 264         * this isn't the same as continuing with a signal, but it will do
 265         * for normal use.  strace only continues with a signal if the
 266         * stopping signal is not SIGTRAP.  -brl
 267         */
 268        if (current->exit_code) {
 269                send_sig(current->exit_code, current, 1);
 270                current->exit_code = 0;
 271        }
 272}
 273
 274void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs, int error_code)
 275{
 276        struct siginfo info;
 277
 278        memset(&info, 0, sizeof(info));
 279        info.si_signo = SIGTRAP;
 280        info.si_code  = TRAP_BRKPT;
 281        info.si_addr  = (void __user *) regs->pc;
 282
 283        /* Send us the fakey SIGTRAP */
 284        force_sig_info(SIGTRAP, &info, tsk);
 285}
 286
 287/* Handle synthetic interrupt delivered only by the simulator. */
 288void __kprobes do_breakpoint(struct pt_regs* regs, int fault_num)
 289{
 290        send_sigtrap(current, regs, fault_num);
 291}
 292