linux/arch/frv/kernel/ptrace.c
<<
>>
Prefs
   1/* ptrace.c: FRV specific parts of process tracing
   2 *
   3 * Copyright (C) 2003-5 Red Hat, Inc. All Rights Reserved.
   4 * Written by David Howells (dhowells@redhat.com)
   5 * - Derived from arch/m68k/kernel/ptrace.c
   6 *
   7 * This program is free software; you can redistribute it and/or
   8 * modify it under the terms of the GNU General Public License
   9 * as published by the Free Software Foundation; either version
  10 * 2 of the License, or (at your option) any later version.
  11 */
  12
  13#include <linux/kernel.h>
  14#include <linux/sched.h>
  15#include <linux/mm.h>
  16#include <linux/smp.h>
  17#include <linux/errno.h>
  18#include <linux/ptrace.h>
  19#include <linux/user.h>
  20#include <linux/security.h>
  21#include <linux/signal.h>
  22#include <linux/regset.h>
  23#include <linux/elf.h>
  24#include <linux/tracehook.h>
  25
  26#include <asm/uaccess.h>
  27#include <asm/page.h>
  28#include <asm/pgtable.h>
  29#include <asm/system.h>
  30#include <asm/processor.h>
  31#include <asm/unistd.h>
  32
  33/*
  34 * does not yet catch signals sent when the child dies.
  35 * in exit.c or in signal.c.
  36 */
  37
  38/*
  39 * retrieve the contents of FRV userspace general registers
  40 */
  41static int genregs_get(struct task_struct *target,
  42                       const struct user_regset *regset,
  43                       unsigned int pos, unsigned int count,
  44                       void *kbuf, void __user *ubuf)
  45{
  46        const struct user_int_regs *iregs = &target->thread.user->i;
  47        int ret;
  48
  49        ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
  50                                  iregs, 0, sizeof(*iregs));
  51        if (ret < 0)
  52                return ret;
  53
  54        return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
  55                                        sizeof(*iregs), -1);
  56}
  57
  58/*
  59 * update the contents of the FRV userspace general registers
  60 */
  61static int genregs_set(struct task_struct *target,
  62                       const struct user_regset *regset,
  63                       unsigned int pos, unsigned int count,
  64                       const void *kbuf, const void __user *ubuf)
  65{
  66        struct user_int_regs *iregs = &target->thread.user->i;
  67        unsigned int offs_gr0, offs_gr1;
  68        int ret;
  69
  70        /* not allowed to set PSR or __status */
  71        if (pos < offsetof(struct user_int_regs, psr) + sizeof(long) &&
  72            pos + count > offsetof(struct user_int_regs, psr))
  73                return -EIO;
  74
  75        if (pos < offsetof(struct user_int_regs, __status) + sizeof(long) &&
  76            pos + count > offsetof(struct user_int_regs, __status))
  77                return -EIO;
  78
  79        /* set the control regs */
  80        offs_gr0 = offsetof(struct user_int_regs, gr[0]);
  81        offs_gr1 = offsetof(struct user_int_regs, gr[1]);
  82        ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
  83                                 iregs, 0, offs_gr0);
  84        if (ret < 0)
  85                return ret;
  86
  87        /* skip GR0/TBR */
  88        ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
  89                                        offs_gr0, offs_gr1);
  90        if (ret < 0)
  91                return ret;
  92
  93        /* set the general regs */
  94        ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
  95                                 &iregs->gr[1], offs_gr1, sizeof(*iregs));
  96        if (ret < 0)
  97                return ret;
  98
  99        return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
 100                                        sizeof(*iregs), -1);
 101}
 102
 103/*
 104 * retrieve the contents of FRV userspace FP/Media registers
 105 */
 106static int fpmregs_get(struct task_struct *target,
 107                       const struct user_regset *regset,
 108                       unsigned int pos, unsigned int count,
 109                       void *kbuf, void __user *ubuf)
 110{
 111        const struct user_fpmedia_regs *fpregs = &target->thread.user->f;
 112        int ret;
 113
 114        ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
 115                                  fpregs, 0, sizeof(*fpregs));
 116        if (ret < 0)
 117                return ret;
 118
 119        return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
 120                                        sizeof(*fpregs), -1);
 121}
 122
 123/*
 124 * update the contents of the FRV userspace FP/Media registers
 125 */
 126static int fpmregs_set(struct task_struct *target,
 127                       const struct user_regset *regset,
 128                       unsigned int pos, unsigned int count,
 129                       const void *kbuf, const void __user *ubuf)
 130{
 131        struct user_fpmedia_regs *fpregs = &target->thread.user->f;
 132        int ret;
 133
 134        ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 135                                 fpregs, 0, sizeof(*fpregs));
 136        if (ret < 0)
 137                return ret;
 138
 139        return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
 140                                        sizeof(*fpregs), -1);
 141}
 142
 143/*
 144 * determine if the FP/Media registers have actually been used
 145 */
 146static int fpmregs_active(struct task_struct *target,
 147                          const struct user_regset *regset)
 148{
 149        return tsk_used_math(target) ? regset->n : 0;
 150}
 151
 152/*
 153 * Define the register sets available on the FRV under Linux
 154 */
 155enum frv_regset {
 156        REGSET_GENERAL,
 157        REGSET_FPMEDIA,
 158};
 159
 160static const struct user_regset frv_regsets[] = {
 161        /*
 162         * General register format is:
 163         *      PSR, ISR, CCR, CCCR, LR, LCR, PC, (STATUS), SYSCALLNO, ORIG_G8
 164         *      GNER0-1, IACC0, TBR, GR1-63
 165         */
 166        [REGSET_GENERAL] = {
 167                .core_note_type = NT_PRSTATUS,
 168                .n              = ELF_NGREG,
 169                .size           = sizeof(long),
 170                .align          = sizeof(long),
 171                .get            = genregs_get,
 172                .set            = genregs_set,
 173        },
 174        /*
 175         * FPU/Media register format is:
 176         *      FR0-63, FNER0-1, MSR0-1, ACC0-7, ACCG0-8, FSR
 177         */
 178        [REGSET_FPMEDIA] = {
 179                .core_note_type = NT_PRFPREG,
 180                .n              = sizeof(struct user_fpmedia_regs) / sizeof(long),
 181                .size           = sizeof(long),
 182                .align          = sizeof(long),
 183                .get            = fpmregs_get,
 184                .set            = fpmregs_set,
 185                .active         = fpmregs_active,
 186        },
 187};
 188
 189static const struct user_regset_view user_frv_native_view = {
 190        .name           = "frv",
 191        .e_machine      = EM_FRV,
 192        .regsets        = frv_regsets,
 193        .n              = ARRAY_SIZE(frv_regsets),
 194};
 195
 196const struct user_regset_view *task_user_regset_view(struct task_struct *task)
 197{
 198        return &user_frv_native_view;
 199}
 200
 201/*
 202 * Get contents of register REGNO in task TASK.
 203 */
 204static inline long get_reg(struct task_struct *task, int regno)
 205{
 206        struct user_context *user = task->thread.user;
 207
 208        if (regno < 0 || regno >= PT__END)
 209                return 0;
 210
 211        return ((unsigned long *) user)[regno];
 212}
 213
 214/*
 215 * Write contents of register REGNO in task TASK.
 216 */
 217static inline int put_reg(struct task_struct *task, int regno,
 218                          unsigned long data)
 219{
 220        struct user_context *user = task->thread.user;
 221
 222        if (regno < 0 || regno >= PT__END)
 223                return -EIO;
 224
 225        switch (regno) {
 226        case PT_GR(0):
 227                return 0;
 228        case PT_PSR:
 229        case PT__STATUS:
 230                return -EIO;
 231        default:
 232                ((unsigned long *) user)[regno] = data;
 233                return 0;
 234        }
 235}
 236
 237/*
 238 * Called by kernel/ptrace.c when detaching..
 239 *
 240 * Control h/w single stepping
 241 */
 242void user_enable_single_step(struct task_struct *child)
 243{
 244        child->thread.frame0->__status |= REG__STATUS_STEP;
 245}
 246
 247void user_disable_single_step(struct task_struct *child)
 248{
 249        child->thread.frame0->__status &= ~REG__STATUS_STEP;
 250}
 251
 252void ptrace_disable(struct task_struct *child)
 253{
 254        user_disable_single_step(child);
 255}
 256
 257long arch_ptrace(struct task_struct *child, long request, long addr, long data)
 258{
 259        unsigned long tmp;
 260        int ret;
 261
 262        switch (request) {
 263                /* read the word at location addr in the USER area. */
 264        case PTRACE_PEEKUSR: {
 265                tmp = 0;
 266                ret = -EIO;
 267                if ((addr & 3) || addr < 0)
 268                        break;
 269
 270                ret = 0;
 271                switch (addr >> 2) {
 272                case 0 ... PT__END - 1:
 273                        tmp = get_reg(child, addr >> 2);
 274                        break;
 275
 276                case PT__END + 0:
 277                        tmp = child->mm->end_code - child->mm->start_code;
 278                        break;
 279
 280                case PT__END + 1:
 281                        tmp = child->mm->end_data - child->mm->start_data;
 282                        break;
 283
 284                case PT__END + 2:
 285                        tmp = child->mm->start_stack - child->mm->start_brk;
 286                        break;
 287
 288                case PT__END + 3:
 289                        tmp = child->mm->start_code;
 290                        break;
 291
 292                case PT__END + 4:
 293                        tmp = child->mm->start_stack;
 294                        break;
 295
 296                default:
 297                        ret = -EIO;
 298                        break;
 299                }
 300
 301                if (ret == 0)
 302                        ret = put_user(tmp, (unsigned long *) data);
 303                break;
 304        }
 305
 306        case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
 307                ret = -EIO;
 308                if ((addr & 3) || addr < 0)
 309                        break;
 310
 311                ret = 0;
 312                switch (addr >> 2) {
 313                case 0 ... PT__END - 1:
 314                        ret = put_reg(child, addr >> 2, data);
 315                        break;
 316
 317                default:
 318                        ret = -EIO;
 319                        break;
 320                }
 321                break;
 322
 323        case PTRACE_GETREGS:    /* Get all integer regs from the child. */
 324                return copy_regset_to_user(child, &user_frv_native_view,
 325                                           REGSET_GENERAL,
 326                                           0, sizeof(child->thread.user->i),
 327                                           (void __user *)data);
 328
 329        case PTRACE_SETREGS:    /* Set all integer regs in the child. */
 330                return copy_regset_from_user(child, &user_frv_native_view,
 331                                             REGSET_GENERAL,
 332                                             0, sizeof(child->thread.user->i),
 333                                             (const void __user *)data);
 334
 335        case PTRACE_GETFPREGS:  /* Get the child FP/Media state. */
 336                return copy_regset_to_user(child, &user_frv_native_view,
 337                                           REGSET_FPMEDIA,
 338                                           0, sizeof(child->thread.user->f),
 339                                           (void __user *)data);
 340
 341        case PTRACE_SETFPREGS:  /* Set the child FP/Media state. */
 342                return copy_regset_from_user(child, &user_frv_native_view,
 343                                             REGSET_FPMEDIA,
 344                                             0, sizeof(child->thread.user->f),
 345                                             (const void __user *)data);
 346
 347        case PTRACE_GETFDPIC:
 348                tmp = 0;
 349                switch (addr) {
 350                case PTRACE_GETFDPIC_EXEC:
 351                        tmp = child->mm->context.exec_fdpic_loadmap;
 352                        break;
 353                case PTRACE_GETFDPIC_INTERP:
 354                        tmp = child->mm->context.interp_fdpic_loadmap;
 355                        break;
 356                default:
 357                        break;
 358                }
 359
 360                ret = 0;
 361                if (put_user(tmp, (unsigned long *) data)) {
 362                        ret = -EFAULT;
 363                        break;
 364                }
 365                break;
 366
 367        default:
 368                ret = ptrace_request(child, request, addr, data);
 369                break;
 370        }
 371        return ret;
 372}
 373
 374/*
 375 * handle tracing of system call entry
 376 * - return the revised system call number or ULONG_MAX to cause ENOSYS
 377 */
 378asmlinkage unsigned long syscall_trace_entry(void)
 379{
 380        __frame->__status |= REG__STATUS_SYSC_ENTRY;
 381        if (tracehook_report_syscall_entry(__frame)) {
 382                /* tracing decided this syscall should not happen, so
 383                 * We'll return a bogus call number to get an ENOSYS
 384                 * error, but leave the original number in
 385                 * __frame->syscallno
 386                 */
 387                return ULONG_MAX;
 388        }
 389
 390        return __frame->syscallno;
 391}
 392
 393/*
 394 * handle tracing of system call exit
 395 */
 396asmlinkage void syscall_trace_exit(void)
 397{
 398        __frame->__status |= REG__STATUS_SYSC_EXIT;
 399        tracehook_report_syscall_exit(__frame, 0);
 400}
 401