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
long arch_ptrace(struct task_struct *child, long request, long addr, long data)
L
Linus Torvalds 已提交
160
{
R
Roman Zippel 已提交
161 162
	unsigned long tmp;
	int i, ret = 0;
L
Linus Torvalds 已提交
163 164 165

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

R
Roman Zippel 已提交
171
		if (addr >= 0 && addr < 19) {
R
Roman Zippel 已提交
172 173 174 175 176 177 178 179 180 181
			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 已提交
182
			goto out_eio;
R
Roman Zippel 已提交
183 184
		ret = put_user(tmp, (unsigned long *)data);
		break;
L
Linus Torvalds 已提交
185

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

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

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

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

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

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

R
Roman Zippel 已提交
248 249 250
	default:
		ret = ptrace_request(child, request, addr, data);
		break;
L
Linus Torvalds 已提交
251
	}
252

L
Linus Torvalds 已提交
253
	return ret;
R
Roman Zippel 已提交
254
out_eio:
255
	return -EIO;
L
Linus Torvalds 已提交
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
}

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;
	}
}