linux/arch/m68knommu/kernel/ptrace.c
<<
>>
Prefs
   1/*
   2 *  linux/arch/m68knommu/kernel/ptrace.c
   3 *
   4 *  Copyright (C) 1994 by Hamish Macdonald
   5 *  Taken from linux/kernel/ptrace.c and modified for M680x0.
   6 *  linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds
   7 *
   8 * This file is subject to the terms and conditions of the GNU General
   9 * Public License.  See the file COPYING in the main directory of
  10 * this archive for more details.
  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/signal.h>
  21#include <linux/tracehook.h>
  22
  23#include <asm/uaccess.h>
  24#include <asm/page.h>
  25#include <asm/pgtable.h>
  26#include <asm/system.h>
  27#include <asm/processor.h>
  28
  29/*
  30 * does not yet catch signals sent when the child dies.
  31 * in exit.c or in signal.c.
  32 */
  33
  34/* determines which bits in the SR the user has access to. */
  35/* 1 = access 0 = no access */
  36#define SR_MASK 0x001f
  37
  38/* sets the trace bits. */
  39#define TRACE_BITS 0x8000
  40
  41/* Find the stack offset for a register, relative to thread.esp0. */
  42#define PT_REG(reg)     ((long)&((struct pt_regs *)0)->reg)
  43#define SW_REG(reg)     ((long)&((struct switch_stack *)0)->reg \
  44                         - sizeof(struct switch_stack))
  45/* Mapping from PT_xxx to the stack offset at which the register is
  46   saved.  Notice that usp has no stack-slot and needs to be treated
  47   specially (see get_reg/put_reg below). */
  48static int regoff[] = {
  49        PT_REG(d1), PT_REG(d2), PT_REG(d3), PT_REG(d4),
  50        PT_REG(d5), SW_REG(d6), SW_REG(d7), PT_REG(a0),
  51        PT_REG(a1), PT_REG(a2), SW_REG(a3), SW_REG(a4),
  52        SW_REG(a5), SW_REG(a6), PT_REG(d0), -1,
  53        PT_REG(orig_d0), PT_REG(sr), PT_REG(pc),
  54};
  55
  56/*
  57 * Get contents of register REGNO in task TASK.
  58 */
  59static inline long get_reg(struct task_struct *task, int regno)
  60{
  61        unsigned long *addr;
  62
  63        if (regno == PT_USP)
  64                addr = &task->thread.usp;
  65        else if (regno < ARRAY_SIZE(regoff))
  66                addr = (unsigned long *)(task->thread.esp0 + regoff[regno]);
  67        else
  68                return 0;
  69        return *addr;
  70}
  71
  72/*
  73 * Write contents of register REGNO in task TASK.
  74 */
  75static inline int put_reg(struct task_struct *task, int regno,
  76                          unsigned long data)
  77{
  78        unsigned long *addr;
  79
  80        if (regno == PT_USP)
  81                addr = &task->thread.usp;
  82        else if (regno < ARRAY_SIZE(regoff))
  83                addr = (unsigned long *) (task->thread.esp0 + regoff[regno]);
  84        else
  85                return -1;
  86        *addr = data;
  87        return 0;
  88}
  89
  90void user_enable_single_step(struct task_struct *task)
  91{
  92        unsigned long srflags;
  93        srflags = get_reg(task, PT_SR) | (TRACE_BITS << 16);
  94        put_reg(task, PT_SR, srflags);
  95}
  96
  97void user_disable_single_step(struct task_struct *task)
  98{
  99        unsigned long srflags;
 100        srflags = get_reg(task, PT_SR) & ~(TRACE_BITS << 16);
 101        put_reg(task, PT_SR, srflags);
 102}
 103
 104/*
 105 * Called by kernel/ptrace.c when detaching..
 106 *
 107 * Make sure the single step bit is not set.
 108 */
 109void ptrace_disable(struct task_struct *child)
 110{
 111        /* make sure the single step bit is not set. */
 112        user_disable_single_step(child);
 113}
 114
 115long arch_ptrace(struct task_struct *child, long request,
 116                 unsigned long addr, unsigned long data)
 117{
 118        int ret;
 119        int regno = addr >> 2;
 120        unsigned long __user *datap = (unsigned long __user *) data;
 121
 122        switch (request) {
 123                /* read the word at location addr in the USER area. */
 124                case PTRACE_PEEKUSR: {
 125                        unsigned long tmp;
 126                        
 127                        ret = -EIO;
 128                        if ((addr & 3) || addr > sizeof(struct user) - 3)
 129                                break;
 130                        
 131                        tmp = 0;  /* Default return condition */
 132                        ret = -EIO;
 133                        if (regno < 19) {
 134                                tmp = get_reg(child, regno);
 135                                if (regno == PT_SR)
 136                                        tmp >>= 16;
 137                        } else if (regno >= 21 && regno < 49) {
 138                                tmp = child->thread.fp[regno - 21];
 139                        } else if (regno == 49) {
 140                                tmp = child->mm->start_code;
 141                        } else if (regno == 50) {
 142                                tmp = child->mm->start_data;
 143                        } else if (regno == 51) {
 144                                tmp = child->mm->end_code;
 145                        } else
 146                                break;
 147                        ret = put_user(tmp, datap);
 148                        break;
 149                }
 150
 151                case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
 152                        ret = -EIO;
 153                        if ((addr & 3) || addr > sizeof(struct user) - 3)
 154                                break;
 155
 156                        if (regno == PT_SR) {
 157                                data &= SR_MASK;
 158                                data <<= 16;
 159                                data |= get_reg(child, PT_SR) & ~(SR_MASK << 16);
 160                        }
 161                        if (regno < 19) {
 162                                if (put_reg(child, regno, data))
 163                                        break;
 164                                ret = 0;
 165                                break;
 166                        }
 167                        if (regno >= 21 && regno < 48)
 168                        {
 169                                child->thread.fp[regno - 21] = data;
 170                                ret = 0;
 171                        }
 172                        break;
 173
 174                case PTRACE_GETREGS: { /* Get all gp regs from the child. */
 175                        int i;
 176                        unsigned long tmp;
 177                        for (i = 0; i < 19; i++) {
 178                            tmp = get_reg(child, i);
 179                            if (i == PT_SR)
 180                                tmp >>= 16;
 181                            if (put_user(tmp, datap)) {
 182                                ret = -EFAULT;
 183                                break;
 184                            }
 185                            datap++;
 186                        }
 187                        ret = 0;
 188                        break;
 189                }
 190
 191                case PTRACE_SETREGS: { /* Set all gp regs in the child. */
 192                        int i;
 193                        unsigned long tmp;
 194                        for (i = 0; i < 19; i++) {
 195                            if (get_user(tmp, datap)) {
 196                                ret = -EFAULT;
 197                                break;
 198                            }
 199                            if (i == PT_SR) {
 200                                tmp &= SR_MASK;
 201                                tmp <<= 16;
 202                                tmp |= get_reg(child, PT_SR) & ~(SR_MASK << 16);
 203                            }
 204                            put_reg(child, i, tmp);
 205                            datap++;
 206                        }
 207                        ret = 0;
 208                        break;
 209                }
 210
 211#ifdef PTRACE_GETFPREGS
 212                case PTRACE_GETFPREGS: { /* Get the child FPU state. */
 213                        ret = 0;
 214                        if (copy_to_user(datap, &child->thread.fp,
 215                                         sizeof(struct user_m68kfp_struct)))
 216                                ret = -EFAULT;
 217                        break;
 218                }
 219#endif
 220
 221#ifdef PTRACE_SETFPREGS
 222                case PTRACE_SETFPREGS: { /* Set the child FPU state. */
 223                        ret = 0;
 224                        if (copy_from_user(&child->thread.fp, datap,
 225                                           sizeof(struct user_m68kfp_struct)))
 226                                ret = -EFAULT;
 227                        break;
 228                }
 229#endif
 230
 231        case PTRACE_GET_THREAD_AREA:
 232                ret = put_user(task_thread_info(child)->tp_value, datap);
 233                break;
 234
 235                default:
 236                        ret = ptrace_request(child, request, addr, data);
 237                        break;
 238        }
 239        return ret;
 240}
 241
 242asmlinkage int syscall_trace_enter(void)
 243{
 244        int ret = 0;
 245
 246        if (test_thread_flag(TIF_SYSCALL_TRACE))
 247                ret = tracehook_report_syscall_entry(task_pt_regs(current));
 248        return ret;
 249}
 250
 251asmlinkage void syscall_trace_leave(void)
 252{
 253        if (test_thread_flag(TIF_SYSCALL_TRACE))
 254                tracehook_report_syscall_exit(task_pt_regs(current), 0);
 255}
 256