ptrace.c 6.8 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;
L
Linus Torvalds 已提交
164 165 166

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

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

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

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

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

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

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

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

M
Maxim Kuvyrkov 已提交
249 250 251 252 253
	case PTRACE_GET_THREAD_AREA:
		ret = put_user(task_thread_info(child)->tp_value,
			       (unsigned long __user *)data);
		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;
	}
}