linux/arch/mn10300/kernel/ptrace.c
<<
>>
Prefs
   1/* MN10300 Process tracing
   2 *
   3 * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd.
   4 * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
   5 * Modified by David Howells (dhowells@redhat.com)
   6 *
   7 * This program is free software; you can redistribute it and/or
   8 * modify it under the terms of the GNU General Public Licence
   9 * as published by the Free Software Foundation; either version
  10 * 2 of the Licence, or (at your option) any later version.
  11 */
  12#include <linux/kernel.h>
  13#include <linux/sched.h>
  14#include <linux/mm.h>
  15#include <linux/smp.h>
  16#include <linux/errno.h>
  17#include <linux/ptrace.h>
  18#include <linux/user.h>
  19#include <linux/regset.h>
  20#include <linux/elf.h>
  21#include <linux/tracehook.h>
  22#include <asm/uaccess.h>
  23#include <asm/pgtable.h>
  24#include <asm/processor.h>
  25#include <asm/cacheflush.h>
  26#include <asm/fpu.h>
  27#include <asm/asm-offsets.h>
  28
  29/*
  30 * translate ptrace register IDs into struct pt_regs offsets
  31 */
  32static const u8 ptrace_regid_to_frame[] = {
  33        [PT_A3 << 2]            = REG_A3,
  34        [PT_A2 << 2]            = REG_A2,
  35        [PT_D3 << 2]            = REG_D3,
  36        [PT_D2 << 2]            = REG_D2,
  37        [PT_MCVF << 2]          = REG_MCVF,
  38        [PT_MCRL << 2]          = REG_MCRL,
  39        [PT_MCRH << 2]          = REG_MCRH,
  40        [PT_MDRQ << 2]          = REG_MDRQ,
  41        [PT_E1 << 2]            = REG_E1,
  42        [PT_E0 << 2]            = REG_E0,
  43        [PT_E7 << 2]            = REG_E7,
  44        [PT_E6 << 2]            = REG_E6,
  45        [PT_E5 << 2]            = REG_E5,
  46        [PT_E4 << 2]            = REG_E4,
  47        [PT_E3 << 2]            = REG_E3,
  48        [PT_E2 << 2]            = REG_E2,
  49        [PT_SP << 2]            = REG_SP,
  50        [PT_LAR << 2]           = REG_LAR,
  51        [PT_LIR << 2]           = REG_LIR,
  52        [PT_MDR << 2]           = REG_MDR,
  53        [PT_A1 << 2]            = REG_A1,
  54        [PT_A0 << 2]            = REG_A0,
  55        [PT_D1 << 2]            = REG_D1,
  56        [PT_D0 << 2]            = REG_D0,
  57        [PT_ORIG_D0 << 2]       = REG_ORIG_D0,
  58        [PT_EPSW << 2]          = REG_EPSW,
  59        [PT_PC << 2]            = REG_PC,
  60};
  61
  62static inline int get_stack_long(struct task_struct *task, int offset)
  63{
  64        return *(unsigned long *)
  65                ((unsigned long) task->thread.uregs + offset);
  66}
  67
  68static inline
  69int put_stack_long(struct task_struct *task, int offset, unsigned long data)
  70{
  71        unsigned long stack;
  72
  73        stack = (unsigned long) task->thread.uregs + offset;
  74        *(unsigned long *) stack = data;
  75        return 0;
  76}
  77
  78/*
  79 * retrieve the contents of MN10300 userspace general registers
  80 */
  81static int genregs_get(struct task_struct *target,
  82                       const struct user_regset *regset,
  83                       unsigned int pos, unsigned int count,
  84                       void *kbuf, void __user *ubuf)
  85{
  86        const struct pt_regs *regs = task_pt_regs(target);
  87        int ret;
  88
  89        /* we need to skip regs->next */
  90        ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
  91                                  regs, 0, PT_ORIG_D0 * sizeof(long));
  92        if (ret < 0)
  93                return ret;
  94
  95        ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
  96                                  &regs->orig_d0, PT_ORIG_D0 * sizeof(long),
  97                                  NR_PTREGS * sizeof(long));
  98        if (ret < 0)
  99                return ret;
 100
 101        return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
 102                                        NR_PTREGS * sizeof(long), -1);
 103}
 104
 105/*
 106 * update the contents of the MN10300 userspace general registers
 107 */
 108static int genregs_set(struct task_struct *target,
 109                       const struct user_regset *regset,
 110                       unsigned int pos, unsigned int count,
 111                       const void *kbuf, const void __user *ubuf)
 112{
 113        struct pt_regs *regs = task_pt_regs(target);
 114        unsigned long tmp;
 115        int ret;
 116
 117        /* we need to skip regs->next */
 118        ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 119                                 regs, 0, PT_ORIG_D0 * sizeof(long));
 120        if (ret < 0)
 121                return ret;
 122
 123        ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 124                                 &regs->orig_d0, PT_ORIG_D0 * sizeof(long),
 125                                 PT_EPSW * sizeof(long));
 126        if (ret < 0)
 127                return ret;
 128
 129        /* we need to mask off changes to EPSW */
 130        tmp = regs->epsw;
 131        ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 132                                 &tmp, PT_EPSW * sizeof(long),
 133                                 PT_PC * sizeof(long));
 134        tmp &= EPSW_FLAG_V | EPSW_FLAG_C | EPSW_FLAG_N | EPSW_FLAG_Z;
 135        tmp |= regs->epsw & ~(EPSW_FLAG_V | EPSW_FLAG_C | EPSW_FLAG_N |
 136                              EPSW_FLAG_Z);
 137        regs->epsw = tmp;
 138
 139        if (ret < 0)
 140                return ret;
 141
 142        /* and finally load the PC */
 143        ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 144                                 &regs->pc, PT_PC * sizeof(long),
 145                                 NR_PTREGS * sizeof(long));
 146
 147        if (ret < 0)
 148                return ret;
 149
 150        return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
 151                                         NR_PTREGS * sizeof(long), -1);
 152}
 153
 154/*
 155 * retrieve the contents of MN10300 userspace FPU registers
 156 */
 157static int fpuregs_get(struct task_struct *target,
 158                       const struct user_regset *regset,
 159                       unsigned int pos, unsigned int count,
 160                       void *kbuf, void __user *ubuf)
 161{
 162        const struct fpu_state_struct *fpregs = &target->thread.fpu_state;
 163        int ret;
 164
 165        unlazy_fpu(target);
 166
 167        ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
 168                                  fpregs, 0, sizeof(*fpregs));
 169        if (ret < 0)
 170                return ret;
 171
 172        return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
 173                                        sizeof(*fpregs), -1);
 174}
 175
 176/*
 177 * update the contents of the MN10300 userspace FPU registers
 178 */
 179static int fpuregs_set(struct task_struct *target,
 180                       const struct user_regset *regset,
 181                       unsigned int pos, unsigned int count,
 182                       const void *kbuf, const void __user *ubuf)
 183{
 184        struct fpu_state_struct fpu_state = target->thread.fpu_state;
 185        int ret;
 186
 187        ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 188                                 &fpu_state, 0, sizeof(fpu_state));
 189        if (ret < 0)
 190                return ret;
 191
 192        fpu_kill_state(target);
 193        target->thread.fpu_state = fpu_state;
 194        set_using_fpu(target);
 195
 196        return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
 197                                         sizeof(fpu_state), -1);
 198}
 199
 200/*
 201 * determine if the FPU registers have actually been used
 202 */
 203static int fpuregs_active(struct task_struct *target,
 204                          const struct user_regset *regset)
 205{
 206        return is_using_fpu(target) ? regset->n : 0;
 207}
 208
 209/*
 210 * Define the register sets available on the MN10300 under Linux
 211 */
 212enum mn10300_regset {
 213        REGSET_GENERAL,
 214        REGSET_FPU,
 215};
 216
 217static const struct user_regset mn10300_regsets[] = {
 218        /*
 219         * General register format is:
 220         *      A3, A2, D3, D2, MCVF, MCRL, MCRH, MDRQ
 221         *      E1, E0, E7...E2, SP, LAR, LIR, MDR
 222         *      A1, A0, D1, D0, ORIG_D0, EPSW, PC
 223         */
 224        [REGSET_GENERAL] = {
 225                .core_note_type = NT_PRSTATUS,
 226                .n              = ELF_NGREG,
 227                .size           = sizeof(long),
 228                .align          = sizeof(long),
 229                .get            = genregs_get,
 230                .set            = genregs_set,
 231        },
 232        /*
 233         * FPU register format is:
 234         *      FS0-31, FPCR
 235         */
 236        [REGSET_FPU] = {
 237                .core_note_type = NT_PRFPREG,
 238                .n              = sizeof(struct fpu_state_struct) / sizeof(long),
 239                .size           = sizeof(long),
 240                .align          = sizeof(long),
 241                .get            = fpuregs_get,
 242                .set            = fpuregs_set,
 243                .active         = fpuregs_active,
 244        },
 245};
 246
 247static const struct user_regset_view user_mn10300_native_view = {
 248        .name           = "mn10300",
 249        .e_machine      = EM_MN10300,
 250        .regsets        = mn10300_regsets,
 251        .n              = ARRAY_SIZE(mn10300_regsets),
 252};
 253
 254const struct user_regset_view *task_user_regset_view(struct task_struct *task)
 255{
 256        return &user_mn10300_native_view;
 257}
 258
 259/*
 260 * set the single-step bit
 261 */
 262void user_enable_single_step(struct task_struct *child)
 263{
 264#ifndef CONFIG_MN10300_USING_JTAG
 265        struct user *dummy = NULL;
 266        long tmp;
 267
 268        tmp = get_stack_long(child, (unsigned long) &dummy->regs.epsw);
 269        tmp |= EPSW_T;
 270        put_stack_long(child, (unsigned long) &dummy->regs.epsw, tmp);
 271#endif
 272}
 273
 274/*
 275 * make sure the single-step bit is not set
 276 */
 277void user_disable_single_step(struct task_struct *child)
 278{
 279#ifndef CONFIG_MN10300_USING_JTAG
 280        struct user *dummy = NULL;
 281        long tmp;
 282
 283        tmp = get_stack_long(child, (unsigned long) &dummy->regs.epsw);
 284        tmp &= ~EPSW_T;
 285        put_stack_long(child, (unsigned long) &dummy->regs.epsw, tmp);
 286#endif
 287}
 288
 289void ptrace_disable(struct task_struct *child)
 290{
 291        user_disable_single_step(child);
 292}
 293
 294/*
 295 * handle the arch-specific side of process tracing
 296 */
 297long arch_ptrace(struct task_struct *child, long request,
 298                 unsigned long addr, unsigned long data)
 299{
 300        unsigned long tmp;
 301        int ret;
 302        unsigned long __user *datap = (unsigned long __user *) data;
 303
 304        switch (request) {
 305        /* read the word at location addr in the USER area. */
 306        case PTRACE_PEEKUSR:
 307                ret = -EIO;
 308                if ((addr & 3) || addr > sizeof(struct user) - 3)
 309                        break;
 310
 311                tmp = 0;  /* Default return condition */
 312                if (addr < NR_PTREGS << 2)
 313                        tmp = get_stack_long(child,
 314                                             ptrace_regid_to_frame[addr]);
 315                ret = put_user(tmp, datap);
 316                break;
 317
 318                /* write the word at location addr in the USER area */
 319        case PTRACE_POKEUSR:
 320                ret = -EIO;
 321                if ((addr & 3) || addr > sizeof(struct user) - 3)
 322                        break;
 323
 324                ret = 0;
 325                if (addr < NR_PTREGS << 2)
 326                        ret = put_stack_long(child, ptrace_regid_to_frame[addr],
 327                                             data);
 328                break;
 329
 330        case PTRACE_GETREGS:    /* Get all integer regs from the child. */
 331                return copy_regset_to_user(child, &user_mn10300_native_view,
 332                                           REGSET_GENERAL,
 333                                           0, NR_PTREGS * sizeof(long),
 334                                           datap);
 335
 336        case PTRACE_SETREGS:    /* Set all integer regs in the child. */
 337                return copy_regset_from_user(child, &user_mn10300_native_view,
 338                                             REGSET_GENERAL,
 339                                             0, NR_PTREGS * sizeof(long),
 340                                             datap);
 341
 342        case PTRACE_GETFPREGS:  /* Get the child FPU state. */
 343                return copy_regset_to_user(child, &user_mn10300_native_view,
 344                                           REGSET_FPU,
 345                                           0, sizeof(struct fpu_state_struct),
 346                                           datap);
 347
 348        case PTRACE_SETFPREGS:  /* Set the child FPU state. */
 349                return copy_regset_from_user(child, &user_mn10300_native_view,
 350                                             REGSET_FPU,
 351                                             0, sizeof(struct fpu_state_struct),
 352                                             datap);
 353
 354        default:
 355                ret = ptrace_request(child, request, addr, data);
 356                break;
 357        }
 358
 359        return ret;
 360}
 361
 362/*
 363 * handle tracing of system call entry
 364 * - return the revised system call number or ULONG_MAX to cause ENOSYS
 365 */
 366asmlinkage unsigned long syscall_trace_entry(struct pt_regs *regs)
 367{
 368        if (tracehook_report_syscall_entry(regs))
 369                /* tracing decided this syscall should not happen, so
 370                 * We'll return a bogus call number to get an ENOSYS
 371                 * error, but leave the original number in
 372                 * regs->orig_d0
 373                 */
 374                return ULONG_MAX;
 375
 376        return regs->orig_d0;
 377}
 378
 379/*
 380 * handle tracing of system call exit
 381 */
 382asmlinkage void syscall_trace_exit(struct pt_regs *regs)
 383{
 384        tracehook_report_syscall_exit(regs, 0);
 385}
 386