linux/arch/arc/kernel/ptrace.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 as
   6 * published by the Free Software Foundation.
   7 */
   8
   9#include <linux/ptrace.h>
  10#include <linux/tracehook.h>
  11#include <linux/regset.h>
  12#include <linux/unistd.h>
  13#include <linux/elf.h>
  14
  15static struct callee_regs *task_callee_regs(struct task_struct *tsk)
  16{
  17        struct callee_regs *tmp = (struct callee_regs *)tsk->thread.callee_reg;
  18        return tmp;
  19}
  20
  21static int genregs_get(struct task_struct *target,
  22                       const struct user_regset *regset,
  23                       unsigned int pos, unsigned int count,
  24                       void *kbuf, void __user *ubuf)
  25{
  26        const struct pt_regs *ptregs = task_pt_regs(target);
  27        const struct callee_regs *cregs = task_callee_regs(target);
  28        int ret = 0;
  29        unsigned int stop_pc_val;
  30
  31#define REG_O_CHUNK(START, END, PTR)    \
  32        if (!ret)       \
  33                ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, PTR, \
  34                        offsetof(struct user_regs_struct, START), \
  35                        offsetof(struct user_regs_struct, END));
  36
  37#define REG_O_ONE(LOC, PTR)     \
  38        if (!ret)               \
  39                ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, PTR, \
  40                        offsetof(struct user_regs_struct, LOC), \
  41                        offsetof(struct user_regs_struct, LOC) + 4);
  42
  43#define REG_O_ZERO(LOC)         \
  44        if (!ret)               \
  45                ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, \
  46                        offsetof(struct user_regs_struct, LOC), \
  47                        offsetof(struct user_regs_struct, LOC) + 4);
  48
  49        REG_O_ZERO(pad);
  50        REG_O_CHUNK(scratch, callee, ptregs);
  51        REG_O_ZERO(pad2);
  52        REG_O_CHUNK(callee, efa, cregs);
  53        REG_O_CHUNK(efa, stop_pc, &target->thread.fault_address);
  54
  55        if (!ret) {
  56                if (in_brkpt_trap(ptregs)) {
  57                        stop_pc_val = target->thread.fault_address;
  58                        pr_debug("\t\tstop_pc (brk-pt)\n");
  59                } else {
  60                        stop_pc_val = ptregs->ret;
  61                        pr_debug("\t\tstop_pc (others)\n");
  62                }
  63
  64                REG_O_ONE(stop_pc, &stop_pc_val);
  65        }
  66
  67        return ret;
  68}
  69
  70static int genregs_set(struct task_struct *target,
  71                       const struct user_regset *regset,
  72                       unsigned int pos, unsigned int count,
  73                       const void *kbuf, const void __user *ubuf)
  74{
  75        const struct pt_regs *ptregs = task_pt_regs(target);
  76        const struct callee_regs *cregs = task_callee_regs(target);
  77        int ret = 0;
  78
  79#define REG_IN_CHUNK(FIRST, NEXT, PTR)  \
  80        if (!ret)                       \
  81                ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, \
  82                        (void *)(PTR), \
  83                        offsetof(struct user_regs_struct, FIRST), \
  84                        offsetof(struct user_regs_struct, NEXT));
  85
  86#define REG_IN_ONE(LOC, PTR)            \
  87        if (!ret)                       \
  88                ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, \
  89                        (void *)(PTR), \
  90                        offsetof(struct user_regs_struct, LOC), \
  91                        offsetof(struct user_regs_struct, LOC) + 4);
  92
  93#define REG_IGNORE_ONE(LOC)             \
  94        if (!ret)                       \
  95                ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, \
  96                        offsetof(struct user_regs_struct, LOC), \
  97                        offsetof(struct user_regs_struct, LOC) + 4);
  98
  99        REG_IGNORE_ONE(pad);
 100        /* TBD: disallow updates to STATUS32 etc*/
 101        REG_IN_CHUNK(scratch, pad2, ptregs);    /* pt_regs[bta..sp] */
 102        REG_IGNORE_ONE(pad2);
 103        REG_IN_CHUNK(callee, efa, cregs);       /* callee_regs[r25..r13] */
 104        REG_IGNORE_ONE(efa);                    /* efa update invalid */
 105        REG_IGNORE_ONE(stop_pc);                        /* PC updated via @ret */
 106
 107        return ret;
 108}
 109
 110enum arc_getset {
 111        REGSET_GENERAL,
 112};
 113
 114static const struct user_regset arc_regsets[] = {
 115        [REGSET_GENERAL] = {
 116               .core_note_type = NT_PRSTATUS,
 117               .n = ELF_NGREG,
 118               .size = sizeof(unsigned long),
 119               .align = sizeof(unsigned long),
 120               .get = genregs_get,
 121               .set = genregs_set,
 122        }
 123};
 124
 125static const struct user_regset_view user_arc_view = {
 126        .name           = UTS_MACHINE,
 127        .e_machine      = EM_ARCOMPACT,
 128        .regsets        = arc_regsets,
 129        .n              = ARRAY_SIZE(arc_regsets)
 130};
 131
 132const struct user_regset_view *task_user_regset_view(struct task_struct *task)
 133{
 134        return &user_arc_view;
 135}
 136
 137void ptrace_disable(struct task_struct *child)
 138{
 139}
 140
 141long arch_ptrace(struct task_struct *child, long request,
 142                 unsigned long addr, unsigned long data)
 143{
 144        int ret = -EIO;
 145
 146        pr_debug("REQ=%ld: ADDR =0x%lx, DATA=0x%lx)\n", request, addr, data);
 147
 148        switch (request) {
 149        case PTRACE_GET_THREAD_AREA:
 150                ret = put_user(task_thread_info(child)->thr_ptr,
 151                               (unsigned long __user *)data);
 152                break;
 153        default:
 154                ret = ptrace_request(child, request, addr, data);
 155                break;
 156        }
 157
 158        return ret;
 159}
 160
 161asmlinkage int syscall_trace_entry(struct pt_regs *regs)
 162{
 163        if (tracehook_report_syscall_entry(regs))
 164                return ULONG_MAX;
 165
 166        return regs->r8;
 167}
 168
 169asmlinkage void syscall_trace_exit(struct pt_regs *regs)
 170{
 171        tracehook_report_syscall_exit(regs, 0);
 172}
 173