linux/arch/hexagon/kernel/ptrace.c
<<
>>
Prefs
   1/*
   2 * Ptrace support for Hexagon
   3 *
   4 * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 and
   8 * only version 2 as published by the Free Software Foundation.
   9 *
  10 * This program is distributed in the hope that it will be useful,
  11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13 * GNU General Public License for more details.
  14 *
  15 * You should have received a copy of the GNU General Public License
  16 * along with this program; if not, write to the Free Software
  17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  18 * 02110-1301, USA.
  19 */
  20
  21#include <linux/kernel.h>
  22#include <linux/sched.h>
  23#include <linux/sched/task_stack.h>
  24#include <linux/mm.h>
  25#include <linux/smp.h>
  26#include <linux/errno.h>
  27#include <linux/ptrace.h>
  28#include <linux/regset.h>
  29#include <linux/user.h>
  30#include <linux/elf.h>
  31
  32#include <asm/user.h>
  33
  34#if arch_has_single_step()
  35/*  Both called from ptrace_resume  */
  36void user_enable_single_step(struct task_struct *child)
  37{
  38        pt_set_singlestep(task_pt_regs(child));
  39        set_tsk_thread_flag(child, TIF_SINGLESTEP);
  40}
  41
  42void user_disable_single_step(struct task_struct *child)
  43{
  44        pt_clr_singlestep(task_pt_regs(child));
  45        clear_tsk_thread_flag(child, TIF_SINGLESTEP);
  46}
  47#endif
  48
  49static int genregs_get(struct task_struct *target,
  50                   const struct user_regset *regset,
  51                   unsigned int pos, unsigned int count,
  52                   void *kbuf, void __user *ubuf)
  53{
  54        int ret;
  55        unsigned int dummy;
  56        struct pt_regs *regs = task_pt_regs(target);
  57
  58
  59        if (!regs)
  60                return -EIO;
  61
  62        /* The general idea here is that the copyout must happen in
  63         * exactly the same order in which the userspace expects these
  64         * regs. Now, the sequence in userspace does not match the
  65         * sequence in the kernel, so everything past the 32 gprs
  66         * happens one at a time.
  67         */
  68        ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
  69                                  &regs->r00, 0, 32*sizeof(unsigned long));
  70
  71#define ONEXT(KPT_REG, USR_REG) \
  72        if (!ret) \
  73                ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, \
  74                        KPT_REG, offsetof(struct user_regs_struct, USR_REG), \
  75                        offsetof(struct user_regs_struct, USR_REG) + \
  76                                 sizeof(unsigned long));
  77
  78        /* Must be exactly same sequence as struct user_regs_struct */
  79        ONEXT(&regs->sa0, sa0);
  80        ONEXT(&regs->lc0, lc0);
  81        ONEXT(&regs->sa1, sa1);
  82        ONEXT(&regs->lc1, lc1);
  83        ONEXT(&regs->m0, m0);
  84        ONEXT(&regs->m1, m1);
  85        ONEXT(&regs->usr, usr);
  86        ONEXT(&regs->preds, p3_0);
  87        ONEXT(&regs->gp, gp);
  88        ONEXT(&regs->ugp, ugp);
  89        ONEXT(&pt_elr(regs), pc);
  90        dummy = pt_cause(regs);
  91        ONEXT(&dummy, cause);
  92        ONEXT(&pt_badva(regs), badva);
  93#if CONFIG_HEXAGON_ARCH_VERSION >=4
  94        ONEXT(&regs->cs0, cs0);
  95        ONEXT(&regs->cs1, cs1);
  96#endif
  97
  98        /* Pad the rest with zeros, if needed */
  99        if (!ret)
 100                ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
 101                                        offsetof(struct user_regs_struct, pad1), -1);
 102        return ret;
 103}
 104
 105static int genregs_set(struct task_struct *target,
 106                   const struct user_regset *regset,
 107                   unsigned int pos, unsigned int count,
 108                   const void *kbuf, const void __user *ubuf)
 109{
 110        int ret;
 111        unsigned long bucket;
 112        struct pt_regs *regs = task_pt_regs(target);
 113
 114        if (!regs)
 115                return -EIO;
 116
 117        ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 118                                 &regs->r00, 0, 32*sizeof(unsigned long));
 119
 120#define INEXT(KPT_REG, USR_REG) \
 121        if (!ret) \
 122                ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, \
 123                        KPT_REG, offsetof(struct user_regs_struct, USR_REG), \
 124                        offsetof(struct user_regs_struct, USR_REG) + \
 125                                sizeof(unsigned long));
 126
 127        /* Must be exactly same sequence as struct user_regs_struct */
 128        INEXT(&regs->sa0, sa0);
 129        INEXT(&regs->lc0, lc0);
 130        INEXT(&regs->sa1, sa1);
 131        INEXT(&regs->lc1, lc1);
 132        INEXT(&regs->m0, m0);
 133        INEXT(&regs->m1, m1);
 134        INEXT(&regs->usr, usr);
 135        INEXT(&regs->preds, p3_0);
 136        INEXT(&regs->gp, gp);
 137        INEXT(&regs->ugp, ugp);
 138        INEXT(&pt_elr(regs), pc);
 139
 140        /* CAUSE and BADVA aren't writeable. */
 141        INEXT(&bucket, cause);
 142        INEXT(&bucket, badva);
 143
 144#if CONFIG_HEXAGON_ARCH_VERSION >=4
 145        INEXT(&regs->cs0, cs0);
 146        INEXT(&regs->cs1, cs1);
 147#endif
 148
 149        /* Ignore the rest, if needed */
 150        if (!ret)
 151                ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
 152                                        offsetof(struct user_regs_struct, pad1), -1);
 153
 154        if (ret)
 155                return ret;
 156
 157        /*
 158         * This is special; SP is actually restored by the VM via the
 159         * special event record which is set by the special trap.
 160         */
 161        regs->hvmer.vmpsp = regs->r29;
 162        return 0;
 163}
 164
 165enum hexagon_regset {
 166        REGSET_GENERAL,
 167};
 168
 169static const struct user_regset hexagon_regsets[] = {
 170        [REGSET_GENERAL] = {
 171                .core_note_type = NT_PRSTATUS,
 172                .n = ELF_NGREG,
 173                .size = sizeof(unsigned long),
 174                .align = sizeof(unsigned long),
 175                .get = genregs_get,
 176                .set = genregs_set,
 177        },
 178};
 179
 180static const struct user_regset_view hexagon_user_view = {
 181        .name = "hexagon",
 182        .e_machine = ELF_ARCH,
 183        .ei_osabi = ELF_OSABI,
 184        .regsets = hexagon_regsets,
 185        .e_flags = ELF_CORE_EFLAGS,
 186        .n = ARRAY_SIZE(hexagon_regsets)
 187};
 188
 189const struct user_regset_view *task_user_regset_view(struct task_struct *task)
 190{
 191        return &hexagon_user_view;
 192}
 193
 194void ptrace_disable(struct task_struct *child)
 195{
 196        /* Boilerplate - resolves to null inline if no HW single-step */
 197        user_disable_single_step(child);
 198}
 199
 200long arch_ptrace(struct task_struct *child, long request,
 201                 unsigned long addr, unsigned long data)
 202{
 203        return ptrace_request(child, request, addr, data);
 204}
 205