Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 | /*
* arch/v850/kernel/ptrace.c -- `ptrace' system call
*
* Copyright (C) 2002,03,04 NEC Electronics Corporation
* Copyright (C) 2002,03,04 Miles Bader <miles@gnu.org>
*
* Derived from arch/mips/kernel/ptrace.c:
*
* Copyright (C) 1992 Ross Biro
* Copyright (C) Linus Torvalds
* Copyright (C) 1994, 95, 96, 97, 98, 2000 Ralf Baechle
* Copyright (C) 1996 David S. Miller
* Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com
* Copyright (C) 1999 MIPS Technologies, Inc.
*
* This file is subject to the terms and conditions of the GNU General
* Public License. See the file COPYING in the main directory of this
* archive for more details.
*/
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/smp_lock.h>
#include <linux/ptrace.h>
#include <linux/signal.h>
#include <asm/errno.h>
#include <asm/ptrace.h>
#include <asm/processor.h>
#include <asm/uaccess.h>
/* Returns the address where the register at REG_OFFS in P is stashed away. */
static v850_reg_t *reg_save_addr (unsigned reg_offs, struct task_struct *t)
{
struct pt_regs *regs;
/* Three basic cases:
(1) A register normally saved before calling the scheduler, is
available in the kernel entry pt_regs structure at the top
of the kernel stack. The kernel trap/irq exit path takes
care to save/restore almost all registers for ptrace'd
processes.
(2) A call-clobbered register, where the process P entered the
kernel via [syscall] trap, is not stored anywhere; that's
OK, because such registers are not expected to be preserved
when the trap returns anyway (so we don't actually bother to
test for this case).
(3) A few registers not used at all by the kernel, and so
normally never saved except by context-switches, are in the
context switch state. */
if (reg_offs == PT_CTPC || reg_offs == PT_CTPSW || reg_offs == PT_CTBP)
/* Register saved during context switch. */
regs = thread_saved_regs (t);
else
/* Register saved during kernel entry (or not available). */
regs = task_pt_regs (t);
return (v850_reg_t *)((char *)regs + reg_offs);
}
/* Set the bits SET and clear the bits CLEAR in the v850e DIR
(`debug information register'). Returns the new value of DIR. */
static inline v850_reg_t set_dir (v850_reg_t set, v850_reg_t clear)
{
register v850_reg_t rval asm ("r10");
register v850_reg_t arg0 asm ("r6") = set;
register v850_reg_t arg1 asm ("r7") = clear;
/* The dbtrap handler has exactly this functionality when called
from kernel mode. 0xf840 is a `dbtrap' insn. */
asm (".short 0xf840" : "=r" (rval) : "r" (arg0), "r" (arg1));
return rval;
}
/* Makes sure hardware single-stepping is (globally) enabled.
Returns true if successful. */
static inline int enable_single_stepping (void)
{
static int enabled = 0; /* Remember whether we already did it. */
if (! enabled) {
/* Turn on the SE (`single-step enable') bit, 0x100, in the
DIR (`debug information register'). This may fail if a
processor doesn't support it or something. We also try
to clear bit 0x40 (`INI'), which is necessary to use the
debug stuff on the v850e2; on the v850e, clearing 0x40
shouldn't cause any problem. */
v850_reg_t dir = set_dir (0x100, 0x40);
/* Make sure it really got set. */
if (dir & 0x100)
enabled = 1;
}
return enabled;
}
/* Try to set CHILD's single-step flag to VAL. Returns true if successful. */
static int set_single_step (struct task_struct *t, int val)
{
v850_reg_t *psw_addr = reg_save_addr(PT_PSW, t);
if (val) {
/* Make sure single-stepping is enabled. */
if (! enable_single_stepping ())
return 0;
/* Set T's single-step flag. */
*psw_addr |= 0x800;
} else
*psw_addr &= ~0x800;
return 1;
}
long arch_ptrace(struct task_struct *child, long request, long addr, long data)
{
int rval;
switch (request) {
unsigned long val, copied;
case PTRACE_PEEKTEXT: /* read word at location addr. */
case PTRACE_PEEKDATA:
copied = access_process_vm(child, addr, &val, sizeof(val), 0);
rval = -EIO;
if (copied != sizeof(val))
break;
rval = put_user(val, (unsigned long *)data);
goto out;
case PTRACE_POKETEXT: /* write the word at location addr. */
case PTRACE_POKEDATA:
rval = 0;
if (access_process_vm(child, addr, &data, sizeof(data), 1)
== sizeof(data))
break;
rval = -EIO;
goto out;
/* Read/write the word at location ADDR in the registers. */
case PTRACE_PEEKUSR:
case PTRACE_POKEUSR:
rval = 0;
if (addr >= PT_SIZE && request == PTRACE_PEEKUSR) {
/* Special requests that don't actually correspond
to offsets in struct pt_regs. */
if (addr == PT_TEXT_ADDR)
val = child->mm->start_code;
else if (addr == PT_DATA_ADDR)
val = child->mm->start_data;
else if (addr == PT_TEXT_LEN)
val = child->mm->end_code
- child->mm->start_code;
else
rval = -EIO;
} else if (addr >= 0 && addr < PT_SIZE && (addr & 0x3) == 0) {
v850_reg_t *reg_addr = reg_save_addr(addr, child);
if (request == PTRACE_PEEKUSR)
val = *reg_addr;
else
*reg_addr = data;
} else
rval = -EIO;
if (rval == 0 && request == PTRACE_PEEKUSR)
rval = put_user (val, (unsigned long *)data);
goto out;
/* Continue and stop at next (return from) syscall */
case PTRACE_SYSCALL:
/* Restart after a signal. */
case PTRACE_CONT:
/* Execute a single instruction. */
case PTRACE_SINGLESTEP:
rval = -EIO;
if (!valid_signal(data))
break;
/* Turn CHILD's single-step flag on or off. */
if (! set_single_step (child, request == PTRACE_SINGLESTEP))
break;
if (request == PTRACE_SYSCALL)
set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
else
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
child->exit_code = data;
wake_up_process(child);
rval = 0;
break;
/*
* make the child exit. Best I can do is send it a sigkill.
* perhaps it should be put in the status that it wants to
* exit.
*/
case PTRACE_KILL:
rval = 0;
if (child->exit_state == EXIT_ZOMBIE) /* already dead */
break;
child->exit_code = SIGKILL;
wake_up_process(child);
break;
case PTRACE_DETACH: /* detach a process that was attached. */
set_single_step (child, 0); /* Clear single-step flag */
rval = ptrace_detach(child, data);
break;
default:
rval = -EIO;
goto out;
}
out:
return rval;
}
asmlinkage void syscall_trace(void)
{
if (!test_thread_flag(TIF_SYSCALL_TRACE))
return;
if (!(current->ptrace & PT_PTRACED))
return;
/* The 0x80 provides a way for the tracing parent to distinguish
between a syscall stop and SIGTRAP delivery */
ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
? 0x80 : 0));
/*
* this isn't the same as continuing with a signal, but it will do
* for normal use. strace only continues with a signal if the
* stopping signal is not SIGTRAP. -brl
*/
if (current->exit_code) {
send_sig(current->exit_code, current, 1);
current->exit_code = 0;
}
}
void ptrace_disable (struct task_struct *child)
{
/* nothing to do */
}
|