1/*
2 * This file is subject to the terms and conditions of the GNU General Public
3 * License.  See the file "COPYING" in the main directory of this archive
4 * for more details.
5 *
6 * Copyright (C) 1992 Ross Biro
7 * Copyright (C) Linus Torvalds
8 * Copyright (C) 1994, 95, 96, 97, 98, 2000 Ralf Baechle
9 * Copyright (C) 1996 David S. Miller
10 * Copyright (C) 2000 Ulf Carlsson
11 *
12 * At this time Linux/MIPS64 only supports syscall tracing, even for 32-bit
13 * binaries.
14 */
15#include <linux/config.h>
16#include <linux/kernel.h>
17#include <linux/sched.h>
18#include <linux/mm.h>
19#include <linux/errno.h>
20#include <linux/ptrace.h>
21#include <linux/smp.h>
22#include <linux/smp_lock.h>
23#include <linux/user.h>
24
25#include <asm/cpu.h>
26#include <asm/fpu.h>
27#include <asm/mipsregs.h>
28#include <asm/pgtable.h>
29#include <asm/page.h>
30#include <asm/system.h>
31#include <asm/uaccess.h>
32#include <asm/bootinfo.h>
33
34/*
35 * Called by kernel/ptrace.c when detaching..
36 *
37 * Make sure single step bits etc are not set.
38 */
39void ptrace_disable(struct task_struct *child)
40{
41	/* Nothing to do.. */
42}
43
44/*
45 * Tracing a 32-bit process with a 64-bit strace and vice versa will not
46 * work.  I don't know how to fix this.
47 */
48asmlinkage int sys32_ptrace(int request, int pid, int addr, int data)
49{
50	struct task_struct *child;
51	int ret;
52
53	lock_kernel();
54	ret = -EPERM;
55	if (request == PTRACE_TRACEME) {
56		/* are we already being traced? */
57		if (current->ptrace & PT_PTRACED)
58			goto out;
59		/* set the ptrace bit in the process flags. */
60		current->ptrace |= PT_PTRACED;
61		ret = 0;
62		goto out;
63	}
64	ret = -ESRCH;
65	read_lock(&tasklist_lock);
66	child = find_task_by_pid(pid);
67	if (child)
68		get_task_struct(child);
69	read_unlock(&tasklist_lock);
70	if (!child)
71		goto out;
72
73	ret = -EPERM;
74	if (pid == 1)		/* you may not mess with init */
75		goto out_tsk;
76
77	if (request == PTRACE_ATTACH) {
78		ret = ptrace_attach(child);
79		goto out_tsk;
80	}
81
82	ret = ptrace_check_attach(child, request == PTRACE_KILL);
83	if (ret < 0)
84		goto out_tsk;
85
86	switch (request) {
87	/* when I and D space are separate, these will need to be fixed. */
88	case PTRACE_PEEKTEXT: /* read word at location addr. */
89	case PTRACE_PEEKDATA: {
90		unsigned int tmp;
91		int copied;
92
93		copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
94		ret = -EIO;
95		if (copied != sizeof(tmp))
96			break;
97		ret = put_user(tmp, (unsigned int *) (unsigned long) data);
98		break;
99	}
100
101	/* read the word at location addr in the USER area. */
102	case PTRACE_PEEKUSR: {
103		struct pt_regs *regs;
104		unsigned int tmp;
105
106		regs = (struct pt_regs *) ((unsigned long) child +
107			KERNEL_STACK_SIZE - 32 - sizeof(struct pt_regs));
108		ret = 0;
109
110		switch (addr) {
111		case 0 ... 31:
112			tmp = regs->regs[addr];
113			break;
114		case FPR_BASE ... FPR_BASE + 31:
115			if (child->used_math) {
116				unsigned long *fregs = get_fpu_regs(child);
117				tmp = fregs[addr - FPR_BASE];
118			} else {
119				tmp = -EIO;
120			}
121			break;
122		case PC:
123			tmp = regs->cp0_epc;
124			break;
125		case CAUSE:
126			tmp = regs->cp0_cause;
127			break;
128		case BADVADDR:
129			tmp = regs->cp0_badvaddr;
130			break;
131		case MMHI:
132			tmp = regs->hi;
133			break;
134		case MMLO:
135			tmp = regs->lo;
136			break;
137		case FPC_CSR:
138			if (mips_cpu.options & MIPS_CPU_FPU)
139				tmp = child->thread.fpu.hard.control;
140			else
141				tmp = child->thread.fpu.soft.sr;
142			break;
143		case FPC_EIR: { /* implementation / version register */
144			unsigned int flags;
145			__save_flags(flags);
146			__enable_fpu();
147			__asm__ __volatile__("cfc1\t%0,$0": "=r" (tmp));
148			__restore_flags(flags);
149			break;
150		}
151		default:
152			tmp = 0;
153			ret = -EIO;
154			goto out_tsk;
155		}
156		ret = put_user(tmp, (unsigned *) (unsigned long) data);
157		break;
158		}
159	/* when I and D space are separate, this will have to be fixed. */
160	case PTRACE_POKETEXT: /* write the word at location addr. */
161	case PTRACE_POKEDATA:
162		ret = 0;
163		if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
164			break;
165		ret = -EIO;
166		break;
167
168	case PTRACE_POKEUSR: {
169		struct pt_regs *regs;
170		ret = 0;
171		regs = (struct pt_regs *) ((unsigned long) child +
172			KERNEL_STACK_SIZE - 32 - sizeof(struct pt_regs));
173
174		switch (addr) {
175		case 0 ... 31:
176			regs->regs[addr] = data;
177			break;
178		case FPR_BASE ... FPR_BASE + 31: {
179			unsigned long *fregs = get_fpu_regs(child);
180			if (!child->used_math) {
181				/* FP not yet used  */
182				memset(&child->thread.fpu.hard, ~0,
183				       sizeof(child->thread.fpu.hard));
184				child->thread.fpu.hard.control = 0;
185			}
186			fregs[addr - FPR_BASE] = data;
187			break;
188		}
189		case PC:
190			regs->cp0_epc = data;
191			break;
192		case MMHI:
193			regs->hi = data;
194			break;
195		case MMLO:
196			regs->lo = data;
197			break;
198		case FPC_CSR:
199			if (mips_cpu.options & MIPS_CPU_FPU)
200				child->thread.fpu.hard.control = data;
201			else
202				child->thread.fpu.soft.sr = data;
203			break;
204		default:
205			/* The rest are not allowed. */
206			ret = -EIO;
207			break;
208		}
209		goto out;
210		}
211	case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
212	case PTRACE_CONT: { /* restart after signal. */
213		ret = -EIO;
214		if ((unsigned int) data > _NSIG)
215			break;
216		if (request == PTRACE_SYSCALL)
217			child->ptrace |= PT_TRACESYS;
218		else
219			child->ptrace &= ~PT_TRACESYS;
220		child->exit_code = data;
221		wake_up_process(child);
222		ret = 0;
223		break;
224	}
225
226/*
227 * make the child exit.  Best I can do is send it a sigkill.
228 * perhaps it should be put in the status that it wants to
229 * exit.
230 */
231	case PTRACE_KILL: {
232		if (child->state == TASK_ZOMBIE)	/* already dead */
233			break;
234		child->exit_code = SIGKILL;
235		wake_up_process(child);
236		break;
237	}
238
239	case PTRACE_DETACH: /* detach a process that was attached. */
240		ret = ptrace_detach(child, data);
241		break;
242
243	case PTRACE_SETOPTIONS: {
244		if (data & PTRACE_O_TRACESYSGOOD)
245			child->ptrace |= PT_TRACESYSGOOD;
246		else
247			child->ptrace &= ~PT_TRACESYSGOOD;
248		ret = 0;
249		break;
250	}
251
252	default:
253		ret = -EIO;
254		break;
255	}
256
257out_tsk:
258	free_task_struct(child);
259out:
260	unlock_kernel();
261	return ret;
262}
263
264asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
265{
266	struct task_struct *child;
267	int ret;
268
269	lock_kernel();
270	ret = -EPERM;
271	if (request == PTRACE_TRACEME) {
272		/* are we already being traced? */
273		if (current->ptrace & PT_PTRACED)
274			goto out;
275		/* set the ptrace bit in the process flags. */
276		current->ptrace |= PT_PTRACED;
277		ret = 0;
278		goto out;
279	}
280	ret = -ESRCH;
281	read_lock(&tasklist_lock);
282	child = find_task_by_pid(pid);
283	if (child)
284		get_task_struct(child);
285	read_unlock(&tasklist_lock);
286	if (!child)
287		goto out;
288
289	ret = -EPERM;
290	if (pid == 1)		/* you may not mess with init */
291		goto out;
292
293	if (request == PTRACE_ATTACH) {
294		ret = ptrace_attach(child);
295		goto out_tsk;
296	}
297
298	ret = ptrace_check_attach(child, request == PTRACE_KILL);
299	if (ret < 0)
300		goto out_tsk;
301
302	switch (request) {
303	/* when I and D space are separate, these will need to be fixed. */
304	case PTRACE_PEEKTEXT: /* read word at location addr. */
305	case PTRACE_PEEKDATA: {
306		unsigned long tmp;
307		int copied;
308
309		copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
310		ret = -EIO;
311		if (copied != sizeof(tmp))
312			break;
313		ret = put_user(tmp,(unsigned long *) data);
314		break;
315	}
316
317	/* read the word at location addr in the USER area. */
318	case PTRACE_PEEKUSR: {
319		struct pt_regs *regs;
320		unsigned long tmp;
321
322		regs = (struct pt_regs *) ((unsigned long) child +
323			KERNEL_STACK_SIZE - 32 - sizeof(struct pt_regs));
324		ret = 0;
325
326		switch (addr) {
327		case 0 ... 31:
328			tmp = regs->regs[addr];
329			break;
330		case FPR_BASE ... FPR_BASE + 31:
331			if (child->used_math) {
332				unsigned long *fregs = get_fpu_regs(child);
333				tmp = fregs[addr - FPR_BASE];
334			} else {
335				tmp = -EIO;
336			}
337			break;
338		case PC:
339			tmp = regs->cp0_epc;
340			break;
341		case CAUSE:
342			tmp = regs->cp0_cause;
343			break;
344		case BADVADDR:
345			tmp = regs->cp0_badvaddr;
346			break;
347		case MMHI:
348			tmp = regs->hi;
349			break;
350		case MMLO:
351			tmp = regs->lo;
352			break;
353		case FPC_CSR:
354			if (mips_cpu.options & MIPS_CPU_FPU)
355				tmp = child->thread.fpu.hard.control;
356			else
357				tmp = child->thread.fpu.soft.sr;
358			break;
359		case FPC_EIR: { /* implementation / version register */
360			unsigned int flags;
361			__save_flags(flags);
362			__enable_fpu();
363			__asm__ __volatile__("cfc1\t%0,$0": "=r" (tmp));
364			__restore_flags(flags);
365			break;
366		}
367		default:
368			tmp = 0;
369			ret = -EIO;
370			goto out_tsk;
371		}
372		ret = put_user(tmp, (unsigned long *) data);
373		break;
374		}
375	/* when I and D space are separate, this will have to be fixed. */
376	case PTRACE_POKETEXT: /* write the word at location addr. */
377	case PTRACE_POKEDATA:
378		ret = 0;
379		if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
380			break;
381		ret = -EIO;
382		break;
383
384	case PTRACE_POKEUSR: {
385		struct pt_regs *regs;
386		ret = 0;
387		regs = (struct pt_regs *) ((unsigned long) child +
388			KERNEL_STACK_SIZE - 32 - sizeof(struct pt_regs));
389
390		switch (addr) {
391		case 0 ... 31:
392			regs->regs[addr] = data;
393			break;
394		case FPR_BASE ... FPR_BASE + 31: {
395			unsigned long *fregs = get_fpu_regs(child);
396			if (!child->used_math) {
397				/* FP not yet used  */
398				memset(&child->thread.fpu.hard, ~0,
399				       sizeof(child->thread.fpu.hard));
400				child->thread.fpu.hard.control = 0;
401			}
402			fregs[addr - FPR_BASE] = data;
403			break;
404		}
405		case PC:
406			regs->cp0_epc = data;
407			break;
408		case MMHI:
409			regs->hi = data;
410			break;
411		case MMLO:
412			regs->lo = data;
413			break;
414		case FPC_CSR:
415			if (mips_cpu.options & MIPS_CPU_FPU)
416				child->thread.fpu.hard.control = data;
417			else
418				child->thread.fpu.soft.sr = data;
419			break;
420		default:
421			/* The rest are not allowed. */
422			ret = -EIO;
423			break;
424		}
425		goto out;
426		}
427	case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
428	case PTRACE_CONT: { /* restart after signal. */
429		ret = -EIO;
430		if ((unsigned long) data > _NSIG)
431			break;
432		if (request == PTRACE_SYSCALL)
433			child->ptrace |= PT_TRACESYS;
434		else
435			child->ptrace &= ~PT_TRACESYS;
436		child->exit_code = data;
437		wake_up_process(child);
438		ret = 0;
439		break;
440	}
441
442/*
443 * make the child exit.  Best I can do is send it a sigkill.
444 * perhaps it should be put in the status that it wants to
445 * exit.
446 */
447	case PTRACE_KILL: {
448		if (child->state == TASK_ZOMBIE)	/* already dead */
449			break;
450		child->exit_code = SIGKILL;
451		wake_up_process(child);
452		break;
453	}
454
455	case PTRACE_DETACH: /* detach a process that was attached. */
456		ret = ptrace_detach(child, data);
457		break;
458
459	case PTRACE_SETOPTIONS: {
460		if (data & PTRACE_O_TRACESYSGOOD)
461			child->ptrace |= PT_TRACESYSGOOD;
462		else
463			child->ptrace &= ~PT_TRACESYSGOOD;
464		ret = 0;
465		break;
466	}
467
468	default:
469		ret = -EIO;
470		break;
471	}
472
473out_tsk:
474	free_task_struct(child);
475out:
476	unlock_kernel();
477	return ret;
478}
479
480asmlinkage void syscall_trace(void)
481{
482	if ((current->ptrace & (PT_PTRACED|PT_TRACESYS))
483	    != (PT_PTRACED|PT_TRACESYS))
484		return;
485
486	/* The 0x80 provides a way for the tracing parent to distinguish
487	   between a syscall stop and SIGTRAP delivery */
488	current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
489	                                ? 0x80 : 0);
490	current->state = TASK_STOPPED;
491	notify_parent(current, SIGCHLD);
492	schedule();
493	/*
494	 * this isn't the same as continuing with a signal, but it will do
495	 * for normal use.  strace only continues with a signal if the
496	 * stopping signal is not SIGTRAP.  -brl
497	 */
498	if (current->exit_code) {
499		send_sig(current->exit_code, current, 1);
500		current->exit_code = 0;
501	}
502}
503