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 <generated/compile.h>
  22
  23#include <linux/kernel.h>
  24#include <linux/sched.h>
  25#include <linux/mm.h>
  26#include <linux/smp.h>
  27#include <linux/errno.h>
  28#include <linux/ptrace.h>
  29#include <linux/regset.h>
  30#include <linux/user.h>
  31#include <linux/elf.h>
  32
  33#include <asm/user.h>
  34
  35#if arch_has_single_step()
  36/*  Both called from ptrace_resume  */
  37void user_enable_single_step(struct task_struct *child)
  38{
  39        pt_set_singlestep(task_pt_regs(child));
  40        set_tsk_thread_flag(child, TIF_SINGLESTEP);
  41}
  42
  43void user_disable_single_step(struct task_struct *child)
  44{
  45        pt_clr_singlestep(task_pt_regs(child));
  46        clear_tsk_thread_flag(child, TIF_SINGLESTEP);
  47}
  48#endif
  49
  50static int genregs_get(struct task_struct *target,
  51                   const struct user_regset *regset,
  52                   unsigned int pos, unsigned int count,
  53                   void *kbuf, void __user *ubuf)
  54{
  55        int ret;
  56        unsigned int dummy;
  57        struct pt_regs *regs = task_pt_regs(target);
  58
  59
  60        if (!regs)
  61                return -EIO;
  62
  63        /* The general idea here is that the copyout must happen in
  64         * exactly the same order in which the userspace expects these
  65         * regs. Now, the sequence in userspace does not match the
  66         * sequence in the kernel, so everything past the 32 gprs
  67         * happens one at a time.
  68         */
  69        ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
  70                                  &regs->r00, 0, 32*sizeof(unsigned long));
  71
  72#define ONEXT(KPT_REG, USR_REG) \
  73        if (!ret) \
  74                ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, \
  75                        KPT_REG, offsetof(struct user_regs_struct, USR_REG), \
  76                        offsetof(struct user_regs_struct, USR_REG) + \
  77                                 sizeof(unsigned long));
  78
  79        /* Must be exactly same sequence as struct user_regs_struct */
  80        ONEXT(&regs->sa0, sa0);
  81        ONEXT(&regs->lc0, lc0);
  82        ONEXT(&regs->sa1, sa1);
  83        ONEXT(&regs->lc1, lc1);
  84        ONEXT(&regs->m0, m0);
  85        ONEXT(&regs->m1, m1);
  86        ONEXT(&regs->usr, usr);
  87        ONEXT(&regs->preds, p3_0);
  88        ONEXT(&regs->gp, gp);
  89        ONEXT(&regs->ugp, ugp);
  90        ONEXT(&pt_elr(regs), pc);
  91        dummy = pt_cause(regs);
  92        ONEXT(&dummy, cause);
  93        ONEXT(&pt_badva(regs), badva);
  94#if CONFIG_HEXAGON_ARCH_VERSION >=4
  95        ONEXT(&regs->cs0, cs0);
  96        ONEXT(&regs->cs1, cs1);
  97#endif
  98
  99        /* Pad the rest with zeros, if needed */
 100        if (!ret)
 101                ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
 102                                        offsetof(struct user_regs_struct, pad1), -1);
 103        return ret;
 104}
 105
 106static int genregs_set(struct task_struct *target,
 107                   const struct user_regset *regset,
 108                   unsigned int pos, unsigned int count,
 109                   const void *kbuf, const void __user *ubuf)
 110{
 111        int ret;
 112        unsigned long bucket;
 113        struct pt_regs *regs = task_pt_regs(target);
 114
 115        if (!regs)
 116                return -EIO;
 117
 118        ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 119                                 &regs->r00, 0, 32*sizeof(unsigned long));
 120
 121#define INEXT(KPT_REG, USR_REG) \
 122        if (!ret) \
 123                ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, \
 124                        KPT_REG, offsetof(struct user_regs_struct, USR_REG), \
 125                        offsetof(struct user_regs_struct, USR_REG) + \
 126                                sizeof(unsigned long));
 127
 128        /* Must be exactly same sequence as struct user_regs_struct */
 129        INEXT(&regs->sa0, sa0);
 130        INEXT(&regs->lc0, lc0);
 131        INEXT(&regs->sa1, sa1);
 132        INEXT(&regs->lc1, lc1);
 133        INEXT(&regs->m0, m0);
 134        INEXT(&regs->m1, m1);
 135        INEXT(&regs->usr, usr);
 136        INEXT(&regs->preds, p3_0);
 137        INEXT(&regs->gp, gp);
 138        INEXT(&regs->ugp, ugp);
 139        INEXT(&pt_elr(regs), pc);
 140
 141        /* CAUSE and BADVA aren't writeable. */
 142        INEXT(&bucket, cause);
 143        INEXT(&bucket, badva);
 144
 145#if CONFIG_HEXAGON_ARCH_VERSION >=4
 146        INEXT(&regs->cs0, cs0);
 147        INEXT(&regs->cs1, cs1);
 148#endif
 149
 150        /* Ignore the rest, if needed */
 151        if (!ret)
 152                ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
 153                                        offsetof(struct user_regs_struct, pad1), -1);
 154
 155        if (ret)
 156                return ret;
 157
 158        /*
 159         * This is special; SP is actually restored by the VM via the
 160         * special event record which is set by the special trap.
 161         */
 162        regs->hvmer.vmpsp = regs->r29;
 163        return 0;
 164}
 165
 166enum hexagon_regset {
 167        REGSET_GENERAL,
 168};
 169
 170static const struct user_regset hexagon_regsets[] = {
 171        [REGSET_GENERAL] = {
 172                .core_note_type = NT_PRSTATUS,
 173                .n = ELF_NGREG,
 174                .size = sizeof(unsigned long),
 175                .align = sizeof(unsigned long),
 176                .get = genregs_get,
 177                .set = genregs_set,
 178        },
 179};
 180
 181static const struct user_regset_view hexagon_user_view = {
 182        .name = UTS_MACHINE,
 183        .e_machine = ELF_ARCH,
 184        .ei_osabi = ELF_OSABI,
 185        .regsets = hexagon_regsets,
 186        .e_flags = ELF_CORE_EFLAGS,
 187        .n = ARRAY_SIZE(hexagon_regsets)
 188};
 189
 190const struct user_regset_view *task_user_regset_view(struct task_struct *task)
 191{
 192        return &hexagon_user_view;
 193}
 194
 195void ptrace_disable(struct task_struct *child)
 196{
 197        /* Boilerplate - resolves to null inline if no HW single-step */
 198        user_disable_single_step(child);
 199}
 200
 201long arch_ptrace(struct task_struct *child, long request,
 202                 unsigned long addr, unsigned long data)
 203{
 204        return ptrace_request(child, request, addr, data);
 205}
 206