ptrace.c 6.7 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
 *  linux/arch/m68k/kernel/ptrace.c
 *
 *  Copyright (C) 1994 by Hamish Macdonald
 *  Taken from linux/kernel/ptrace.c and modified for M680x0.
 *  linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds
 *
 * 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/sched.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/user.h>
20
#include <linux/signal.h>
L
Linus Torvalds 已提交
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37

#include <asm/uaccess.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/system.h>
#include <asm/processor.h>

/*
 * does not yet catch signals sent when the child dies.
 * in exit.c or in signal.c.
 */

/* determines which bits in the SR the user has access to. */
/* 1 = access 0 = no access */
#define SR_MASK 0x001f

/* sets the trace bits. */
38 39 40
#define TRACE_BITS 0xC000
#define T1_BIT 0x8000
#define T0_BIT 0x4000
L
Linus Torvalds 已提交
41 42 43 44 45 46 47 48

/* Find the stack offset for a register, relative to thread.esp0. */
#define PT_REG(reg)	((long)&((struct pt_regs *)0)->reg)
#define SW_REG(reg)	((long)&((struct switch_stack *)0)->reg \
			 - sizeof(struct switch_stack))
/* Mapping from PT_xxx to the stack offset at which the register is
   saved.  Notice that usp has no stack-slot and needs to be treated
   specially (see get_reg/put_reg below). */
A
Andreas Schwab 已提交
49
static const int regoff[] = {
L
Linus Torvalds 已提交
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
	[0]	= PT_REG(d1),
	[1]	= PT_REG(d2),
	[2]	= PT_REG(d3),
	[3]	= PT_REG(d4),
	[4]	= PT_REG(d5),
	[5]	= SW_REG(d6),
	[6]	= SW_REG(d7),
	[7]	= PT_REG(a0),
	[8]	= PT_REG(a1),
	[9]	= PT_REG(a2),
	[10]	= SW_REG(a3),
	[11]	= SW_REG(a4),
	[12]	= SW_REG(a5),
	[13]	= SW_REG(a6),
	[14]	= PT_REG(d0),
	[15]	= -1,
	[16]	= PT_REG(orig_d0),
	[17]	= PT_REG(sr),
	[18]	= PT_REG(pc),
};

/*
 * Get contents of register REGNO in task TASK.
 */
static inline long get_reg(struct task_struct *task, int regno)
{
	unsigned long *addr;

	if (regno == PT_USP)
		addr = &task->thread.usp;
80
	else if (regno < ARRAY_SIZE(regoff))
L
Linus Torvalds 已提交
81 82 83
		addr = (unsigned long *)(task->thread.esp0 + regoff[regno]);
	else
		return 0;
A
Andreas Schwab 已提交
84 85 86 87 88 89 90 91
	/* Need to take stkadj into account. */
	if (regno == PT_SR || regno == PT_PC) {
		long stkadj = *(long *)(task->thread.esp0 + PT_REG(stkadj));
		addr = (unsigned long *) ((unsigned long)addr + stkadj);
		/* The sr is actually a 16 bit register.  */
		if (regno == PT_SR)
			return *(unsigned short *)addr;
	}
L
Linus Torvalds 已提交
92 93 94 95 96 97 98 99 100 101 102 103 104
	return *addr;
}

/*
 * Write contents of register REGNO in task TASK.
 */
static inline int put_reg(struct task_struct *task, int regno,
			  unsigned long data)
{
	unsigned long *addr;

	if (regno == PT_USP)
		addr = &task->thread.usp;
105
	else if (regno < ARRAY_SIZE(regoff))
R
Roman Zippel 已提交
106
		addr = (unsigned long *)(task->thread.esp0 + regoff[regno]);
L
Linus Torvalds 已提交
107 108
	else
		return -1;
A
Andreas Schwab 已提交
109 110 111 112 113 114 115 116 117 118
	/* Need to take stkadj into account. */
	if (regno == PT_SR || regno == PT_PC) {
		long stkadj = *(long *)(task->thread.esp0 + PT_REG(stkadj));
		addr = (unsigned long *) ((unsigned long)addr + stkadj);
		/* The sr is actually a 16 bit register.  */
		if (regno == PT_SR) {
			*(unsigned short *)addr = data;
			return 0;
		}
	}
L
Linus Torvalds 已提交
119 120 121 122 123 124 125
	*addr = data;
	return 0;
}

/*
 * Make sure the single step bit is not set.
 */
R
Roman Zippel 已提交
126
static inline void singlestep_disable(struct task_struct *child)
L
Linus Torvalds 已提交
127
{
A
Andreas Schwab 已提交
128
	unsigned long tmp = get_reg(child, PT_SR) & ~TRACE_BITS;
L
Linus Torvalds 已提交
129
	put_reg(child, PT_SR, tmp);
130
	clear_tsk_thread_flag(child, TIF_DELAYED_TRACE);
R
Roman Zippel 已提交
131 132 133 134 135 136 137 138
}

/*
 * Called by kernel/ptrace.c when detaching..
 */
void ptrace_disable(struct task_struct *child)
{
	singlestep_disable(child);
L
Linus Torvalds 已提交
139 140
}

141 142
void user_enable_single_step(struct task_struct *child)
{
A
Andreas Schwab 已提交
143 144
	unsigned long tmp = get_reg(child, PT_SR) & ~TRACE_BITS;
	put_reg(child, PT_SR, tmp | T1_BIT);
145 146 147 148 149
	set_tsk_thread_flag(child, TIF_DELAYED_TRACE);
}

void user_enable_block_step(struct task_struct *child)
{
A
Andreas Schwab 已提交
150 151
	unsigned long tmp = get_reg(child, PT_SR) & ~TRACE_BITS;
	put_reg(child, PT_SR, tmp | T0_BIT);
152 153 154 155 156 157 158
}

void user_disable_single_step(struct task_struct *child)
{
	singlestep_disable(child);
}

159 160
long arch_ptrace(struct task_struct *child, long request,
		 unsigned long addr, unsigned long data)
L
Linus Torvalds 已提交
161
{
R
Roman Zippel 已提交
162 163
	unsigned long tmp;
	int i, ret = 0;
164 165
	int regno = addr >> 2; /* temporary hack. */
	unsigned long __user *datap = (unsigned long __user *) data;
L
Linus Torvalds 已提交
166 167 168

	switch (request) {
	/* read the word at location addr in the USER area. */
R
Roman Zippel 已提交
169 170 171
	case PTRACE_PEEKUSR:
		if (addr & 3)
			goto out_eio;
L
Linus Torvalds 已提交
172

173 174 175 176
		if (regno >= 0 && regno < 19) {
			tmp = get_reg(child, regno);
		} else if (regno >= 21 && regno < 49) {
			tmp = child->thread.fp[regno - 21];
R
Roman Zippel 已提交
177 178 179
			/* Convert internal fpu reg representation
			 * into long double format
			 */
180
			if (FPU_IS_EMU && (regno < 45) && !(regno % 3))
R
Roman Zippel 已提交
181 182 183
				tmp = ((tmp & 0xffff0000) << 15) |
				      ((tmp & 0x0000ffff) << 16);
		} else
A
Andreas Schwab 已提交
184
			goto out_eio;
185
		ret = put_user(tmp, datap);
R
Roman Zippel 已提交
186
		break;
L
Linus Torvalds 已提交
187

188 189
	case PTRACE_POKEUSR:
	/* write the word at location addr in the USER area */
R
Roman Zippel 已提交
190 191
		if (addr & 3)
			goto out_eio;
L
Linus Torvalds 已提交
192

193
		if (regno == PT_SR) {
R
Roman Zippel 已提交
194
			data &= SR_MASK;
A
Andreas Schwab 已提交
195 196
			data |= get_reg(child, PT_SR) & ~SR_MASK;
		}
197 198
		if (regno >= 0 && regno < 19) {
			if (put_reg(child, regno, data))
R
Roman Zippel 已提交
199
				goto out_eio;
200
		} else if (regno >= 21 && regno < 48) {
R
Roman Zippel 已提交
201 202 203
			/* Convert long double format
			 * into internal fpu reg representation
			 */
204
			if (FPU_IS_EMU && (regno < 45) && !(regno % 3)) {
205
				data <<= 15;
R
Roman Zippel 已提交
206 207 208
				data = (data & 0xffff0000) |
				       ((data & 0x0000ffff) >> 1);
			}
209
			child->thread.fp[regno - 21] = data;
R
Roman Zippel 已提交
210 211
		} else
			goto out_eio;
R
Roman Zippel 已提交
212
		break;
L
Linus Torvalds 已提交
213

R
Roman Zippel 已提交
214
	case PTRACE_GETREGS:	/* Get all gp regs from the child. */
R
Roman Zippel 已提交
215 216
		for (i = 0; i < 19; i++) {
			tmp = get_reg(child, i);
217
			ret = put_user(tmp, datap);
R
Roman Zippel 已提交
218
			if (ret)
L
Linus Torvalds 已提交
219
				break;
220
			datap++;
L
Linus Torvalds 已提交
221
		}
R
Roman Zippel 已提交
222
		break;
L
Linus Torvalds 已提交
223

R
Roman Zippel 已提交
224
	case PTRACE_SETREGS:	/* Set all gp regs in the child. */
R
Roman Zippel 已提交
225
		for (i = 0; i < 19; i++) {
226
			ret = get_user(tmp, datap);
R
Roman Zippel 已提交
227
			if (ret)
L
Linus Torvalds 已提交
228
				break;
R
Roman Zippel 已提交
229
			if (i == PT_SR) {
L
Linus Torvalds 已提交
230
				tmp &= SR_MASK;
A
Andreas Schwab 已提交
231
				tmp |= get_reg(child, PT_SR) & ~SR_MASK;
L
Linus Torvalds 已提交
232
			}
R
Roman Zippel 已提交
233
			put_reg(child, i, tmp);
234
			datap++;
L
Linus Torvalds 已提交
235
		}
R
Roman Zippel 已提交
236
		break;
L
Linus Torvalds 已提交
237

R
Roman Zippel 已提交
238
	case PTRACE_GETFPREGS:	/* Get the child FPU state. */
239
		if (copy_to_user(datap, &child->thread.fp,
R
Roman Zippel 已提交
240 241 242
				 sizeof(struct user_m68kfp_struct)))
			ret = -EFAULT;
		break;
L
Linus Torvalds 已提交
243

R
Roman Zippel 已提交
244
	case PTRACE_SETFPREGS:	/* Set the child FPU state. */
245
		if (copy_from_user(&child->thread.fp, datap,
R
Roman Zippel 已提交
246 247 248
				   sizeof(struct user_m68kfp_struct)))
			ret = -EFAULT;
		break;
L
Linus Torvalds 已提交
249

M
Maxim Kuvyrkov 已提交
250
	case PTRACE_GET_THREAD_AREA:
251
		ret = put_user(task_thread_info(child)->tp_value, datap);
M
Maxim Kuvyrkov 已提交
252 253
		break;

R
Roman Zippel 已提交
254 255 256
	default:
		ret = ptrace_request(child, request, addr, data);
		break;
L
Linus Torvalds 已提交
257
	}
258

L
Linus Torvalds 已提交
259
	return ret;
R
Roman Zippel 已提交
260
out_eio:
261
	return -EIO;
L
Linus Torvalds 已提交
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
}

asmlinkage void syscall_trace(void)
{
	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;
	}
}