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        REG_O_CHUNK(scratch, callee, ptregs);
  44        REG_O_CHUNK(callee, efa, cregs);
  45        REG_O_CHUNK(efa, stop_pc, &target->thread.fault_address);
  46
  47        if (!ret) {
  48                if (in_brkpt_trap(ptregs)) {
  49                        stop_pc_val = target->thread.fault_address;
  50                        pr_debug("\t\tstop_pc (brk-pt)\n");
  51                } else {
  52                        stop_pc_val = ptregs->ret;
  53                        pr_debug("\t\tstop_pc (others)\n");
  54                }
  55
  56                REG_O_ONE(stop_pc, &stop_pc_val);
  57        }
  58
  59        return ret;
  60}
  61
  62static int genregs_set(struct task_struct *target,
  63                       const struct user_regset *regset,
  64                       unsigned int pos, unsigned int count,
  65                       const void *kbuf, const void __user *ubuf)
  66{
  67        const struct pt_regs *ptregs = task_pt_regs(target);
  68        const struct callee_regs *cregs = task_callee_regs(target);
  69        int ret = 0;
  70
  71#define REG_IN_CHUNK(FIRST, NEXT, PTR)  \
  72        if (!ret)                       \
  73                ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, \
  74                        (void *)(PTR), \
  75                        offsetof(struct user_regs_struct, FIRST), \
  76                        offsetof(struct user_regs_struct, NEXT));
  77
  78#define REG_IN_ONE(LOC, PTR)            \
  79        if (!ret)                       \
  80                ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, \
  81                        (void *)(PTR), \
  82                        offsetof(struct user_regs_struct, LOC), \
  83                        offsetof(struct user_regs_struct, LOC) + 4);
  84
  85#define REG_IGNORE_ONE(LOC)             \
  86        if (!ret)                       \
  87                ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, \
  88                        offsetof(struct user_regs_struct, LOC), \
  89                        offsetof(struct user_regs_struct, LOC) + 4);
  90
  91        /* TBD: disallow updates to STATUS32, orig_r8 etc*/
  92        REG_IN_CHUNK(scratch, callee, ptregs);  /* pt_regs[bta..orig_r8] */
  93        REG_IN_CHUNK(callee, efa, cregs);       /* callee_regs[r25..r13] */
  94        REG_IGNORE_ONE(efa);                    /* efa update invalid */
  95        REG_IGNORE_ONE(stop_pc);                        /* PC updated via @ret */
  96
  97        return ret;
  98}
  99
 100enum arc_getset {
 101        REGSET_GENERAL,
 102};
 103
 104static const struct user_regset arc_regsets[] = {
 105        [REGSET_GENERAL] = {
 106               .core_note_type = NT_PRSTATUS,
 107               .n = ELF_NGREG,
 108               .size = sizeof(unsigned long),
 109               .align = sizeof(unsigned long),
 110               .get = genregs_get,
 111               .set = genregs_set,
 112        }
 113};
 114
 115static const struct user_regset_view user_arc_view = {
 116        .name           = UTS_MACHINE,
 117        .e_machine      = EM_ARCOMPACT,
 118        .regsets        = arc_regsets,
 119        .n              = ARRAY_SIZE(arc_regsets)
 120};
 121
 122const struct user_regset_view *task_user_regset_view(struct task_struct *task)
 123{
 124        return &user_arc_view;
 125}
 126
 127void ptrace_disable(struct task_struct *child)
 128{
 129}
 130
 131long arch_ptrace(struct task_struct *child, long request,
 132                 unsigned long addr, unsigned long data)
 133{
 134        int ret = -EIO;
 135
 136        pr_debug("REQ=%ld: ADDR =0x%lx, DATA=0x%lx)\n", request, addr, data);
 137
 138        switch (request) {
 139        default:
 140                ret = ptrace_request(child, request, addr, data);
 141                break;
 142        }
 143
 144        return ret;
 145}
 146
 147asmlinkage int syscall_trace_entry(struct pt_regs *regs)
 148{
 149        if (tracehook_report_syscall_entry(regs))
 150                return ULONG_MAX;
 151
 152        return regs->r8;
 153}
 154
 155asmlinkage void syscall_trace_exit(struct pt_regs *regs)
 156{
 157        tracehook_report_syscall_exit(regs, 0);
 158}
 159