1/*
2 *  linux/arch/m68k/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/smp_lock.h>
18#include <linux/errno.h>
19#include <linux/ptrace.h>
20#include <linux/user.h>
21#include <linux/config.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 < sizeof(regoff)/sizeof(regoff[0]))
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 < sizeof(regoff)/sizeof(regoff[0]))
83		addr = (unsigned long *) (task->thread.esp0 + regoff[regno]);
84	else
85		return -1;
86	*addr = data;
87	return 0;
88}
89
90/*
91 * Called by kernel/ptrace.c when detaching..
92 *
93 * Make sure the single step bit is not set.
94 */
95void ptrace_disable(struct task_struct *child)
96{
97	unsigned long tmp;
98	/* make sure the single step bit is not set. */
99	tmp = get_reg(child, PT_SR) & ~(TRACE_BITS << 16);
100	put_reg(child, PT_SR, tmp);
101}
102
103asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
104{
105	struct task_struct *child;
106	int ret;
107
108	lock_kernel();
109	ret = -EPERM;
110	if (request == PTRACE_TRACEME) {
111		/* are we already being traced? */
112		if (current->ptrace & PT_PTRACED)
113			goto out;
114		/* set the ptrace bit in the process flags. */
115		current->ptrace |= PT_PTRACED;
116		ret = 0;
117		goto out;
118	}
119	ret = -ESRCH;
120	read_lock(&tasklist_lock);
121	child = find_task_by_pid(pid);
122	if (child)
123		get_task_struct(child);
124	read_unlock(&tasklist_lock);
125	if (!child)
126		goto out;
127
128	ret = -EPERM;
129	if (pid == 1)		/* you may not mess with init */
130		goto out_tsk;
131
132	if (request == PTRACE_ATTACH) {
133		ret = ptrace_attach(child);
134		goto out_tsk;
135	}
136	ret = -ESRCH;
137	if (!(child->ptrace & PT_PTRACED))
138		goto out_tsk;
139	if (child->state != TASK_STOPPED) {
140		if (request != PTRACE_KILL)
141			goto out_tsk;
142	}
143	if (child->p_pptr != current)
144		goto out_tsk;
145
146	switch (request) {
147	/* when I and D space are separate, these will need to be fixed. */
148		case PTRACE_PEEKTEXT: /* read word at location addr. */
149		case PTRACE_PEEKDATA: {
150			unsigned long tmp;
151			int copied;
152
153			copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
154			ret = -EIO;
155			if (copied != sizeof(tmp))
156				break;
157			ret = put_user(tmp,(unsigned long *) data);
158			break;
159		}
160
161	/* read the word at location addr in the USER area. */
162		case PTRACE_PEEKUSR: {
163			unsigned long tmp;
164
165			ret = -EIO;
166			if ((addr & 3) || addr < 0 ||
167			    addr > sizeof(struct user) - 3)
168				break;
169
170			tmp = 0;  /* Default return condition */
171			addr = addr >> 2; /* temporary hack. */
172			ret = -EIO;
173			if (addr < 19) {
174				tmp = get_reg(child, addr);
175				if (addr == PT_SR)
176					tmp >>= 16;
177			} else if (addr >= 21 && addr < 49) {
178				tmp = child->thread.fp[addr - 21];
179#ifdef CONFIG_M68KFPU_EMU
180				/* Convert internal fpu reg representation
181				 * into long double format
182				 */
183				if (FPU_IS_EMU && (addr < 45) && !(addr % 3))
184					tmp = ((tmp & 0xffff0000) << 15) |
185					      ((tmp & 0x0000ffff) << 16);
186#endif
187			} else
188				break;
189			ret = put_user(tmp,(unsigned long *) data);
190			break;
191		}
192
193      /* when I and D space are separate, this will have to be fixed. */
194		case PTRACE_POKETEXT: /* write the word at location addr. */
195		case PTRACE_POKEDATA:
196			ret = 0;
197			if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
198				break;
199			ret = -EIO;
200			break;
201
202		case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
203			ret = -EIO;
204			if ((addr & 3) || addr < 0 ||
205			    addr > sizeof(struct user) - 3)
206				break;
207
208			addr = addr >> 2; /* temporary hack. */
209
210			if (addr == PT_SR) {
211				data &= SR_MASK;
212				data <<= 16;
213				data |= get_reg(child, PT_SR) & ~(SR_MASK << 16);
214			}
215			if (addr < 19) {
216				if (put_reg(child, addr, data))
217					break;
218				ret = 0;
219				break;
220			}
221			if (addr >= 21 && addr < 48)
222			{
223#ifdef CONFIG_M68KFPU_EMU
224				/* Convert long double format
225				 * into internal fpu reg representation
226				 */
227				if (FPU_IS_EMU && (addr < 45) && !(addr % 3)) {
228					data = (unsigned long)data << 15;
229					data = (data & 0xffff0000) |
230					       ((data & 0x0000ffff) >> 1);
231				}
232#endif
233				child->thread.fp[addr - 21] = data;
234				ret = 0;
235			}
236			break;
237
238		case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
239		case PTRACE_CONT: { /* restart after signal. */
240			long tmp;
241
242			ret = -EIO;
243			if ((unsigned long) data > _NSIG)
244				break;
245			if (request == PTRACE_SYSCALL)
246				child->ptrace |= PT_TRACESYS;
247			else
248				child->ptrace &= ~PT_TRACESYS;
249			child->exit_code = data;
250			/* make sure the single step bit is not set. */
251			tmp = get_reg(child, PT_SR) & ~(TRACE_BITS << 16);
252			put_reg(child, PT_SR, tmp);
253			wake_up_process(child);
254			ret = 0;
255			break;
256		}
257
258/*
259 * make the child exit.  Best I can do is send it a sigkill.
260 * perhaps it should be put in the status that it wants to
261 * exit.
262 */
263		case PTRACE_KILL: {
264			long tmp;
265
266			ret = 0;
267			if (child->state == TASK_ZOMBIE) /* already dead */
268				break;
269			child->exit_code = SIGKILL;
270	/* make sure the single step bit is not set. */
271			tmp = get_reg(child, PT_SR) & ~(TRACE_BITS << 16);
272			put_reg(child, PT_SR, tmp);
273			wake_up_process(child);
274			break;
275		}
276
277		case PTRACE_SINGLESTEP: {  /* set the trap flag. */
278			long tmp;
279
280			ret = -EIO;
281			if ((unsigned long) data > _NSIG)
282				break;
283			child->ptrace &= ~PT_TRACESYS;
284			tmp = get_reg(child, PT_SR) | (TRACE_BITS << 16);
285			put_reg(child, PT_SR, tmp);
286
287			child->exit_code = data;
288	/* give it a chance to run. */
289			wake_up_process(child);
290			ret = 0;
291			break;
292		}
293
294		case PTRACE_DETACH:	/* detach a process that was attached. */
295			ret = ptrace_detach(child, data);
296			break;
297
298		case PTRACE_GETREGS: { /* Get all gp regs from the child. */
299		  	int i;
300			unsigned long tmp;
301			for (i = 0; i < 19; i++) {
302			    tmp = get_reg(child, i);
303			    if (i == PT_SR)
304				tmp >>= 16;
305			    if (put_user(tmp, (unsigned long *) data)) {
306				ret = -EFAULT;
307				break;
308			    }
309			    data += sizeof(long);
310			}
311			ret = 0;
312			break;
313		}
314
315		case PTRACE_SETREGS: { /* Set all gp regs in the child. */
316			int i;
317			unsigned long tmp;
318			for (i = 0; i < 19; i++) {
319			    if (get_user(tmp, (unsigned long *) data)) {
320				ret = -EFAULT;
321				break;
322			    }
323			    if (i == PT_SR) {
324				tmp &= SR_MASK;
325				tmp <<= 16;
326				tmp |= get_reg(child, PT_SR) & ~(SR_MASK << 16);
327			    }
328			    put_reg(child, i, tmp);
329			    data += sizeof(long);
330			}
331			ret = 0;
332			break;
333		}
334
335		case PTRACE_GETFPREGS: { /* Get the child FPU state. */
336			ret = 0;
337			if (copy_to_user((void *)data, &child->thread.fp,
338					 sizeof(struct user_m68kfp_struct)))
339				ret = -EFAULT;
340			break;
341		}
342
343		case PTRACE_SETFPREGS: { /* Set the child FPU state. */
344			ret = 0;
345			if (copy_from_user(&child->thread.fp, (void *)data,
346					   sizeof(struct user_m68kfp_struct)))
347				ret = -EFAULT;
348			break;
349		}
350
351		default:
352			ret = -EIO;
353			break;
354	}
355out_tsk:
356	free_task_struct(child);
357out:
358	unlock_kernel();
359	return ret;
360}
361
362asmlinkage void syscall_trace(void)
363{
364	if ((current->ptrace & (PT_PTRACED|PT_TRACESYS))
365			!= (PT_PTRACED|PT_TRACESYS))
366		return;
367	current->exit_code = SIGTRAP;
368	current->state = TASK_STOPPED;
369	notify_parent(current, SIGCHLD);
370	schedule();
371	/*
372	 * this isn't the same as continuing with a signal, but it will do
373	 * for normal use.  strace only continues with a signal if the
374	 * stopping signal is not SIGTRAP.  -brl
375	 */
376	if (current->exit_code) {
377		send_sig(current->exit_code, current, 1);
378		current->exit_code = 0;
379	}
380}
381