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