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 * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com
11 * Copyright (C) 1999 MIPS Technologies, Inc.
12 * Copyright (C) 2000 Ulf Carlsson
13 *
14 * At this time Linux/MIPS64 only supports syscall tracing, even for 32-bit
15 * binaries.
16 */
17#include <linux/compiler.h>
18#include <linux/compat.h>
19#include <linux/kernel.h>
20#include <linux/sched.h>
21#include <linux/mm.h>
22#include <linux/errno.h>
23#include <linux/ptrace.h>
24#include <linux/smp.h>
25#include <linux/security.h>
26
27#include <asm/cpu.h>
28#include <asm/dsp.h>
29#include <asm/fpu.h>
30#include <asm/mipsregs.h>
31#include <asm/mipsmtregs.h>
32#include <asm/pgtable.h>
33#include <asm/page.h>
34#include <asm/reg.h>
35#include <asm/uaccess.h>
36#include <asm/bootinfo.h>
37
38/*
39 * Tracing a 32-bit process with a 64-bit strace and vice versa will not
40 * work.  I don't know how to fix this.
41 */
42long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
43			compat_ulong_t caddr, compat_ulong_t cdata)
44{
45	int addr = caddr;
46	int data = cdata;
47	int ret;
48
49	switch (request) {
50
51	/*
52	 * Read 4 bytes of the other process' storage
53	 *  data is a pointer specifying where the user wants the
54	 *	4 bytes copied into
55	 *  addr is a pointer in the user's storage that contains an 8 byte
56	 *	address in the other process of the 4 bytes that is to be read
57	 * (this is run in a 32-bit process looking at a 64-bit process)
58	 * when I and D space are separate, these will need to be fixed.
59	 */
60	case PTRACE_PEEKTEXT_3264:
61	case PTRACE_PEEKDATA_3264: {
62		u32 tmp;
63		int copied;
64		u32 __user * addrOthers;
65
66		ret = -EIO;
67
68		/* Get the addr in the other process that we want to read */
69		if (get_user(addrOthers, (u32 __user * __user *) (unsigned long) addr) != 0)
70			break;
71
72		copied = access_process_vm(child, (u64)addrOthers, &tmp,
73				sizeof(tmp), 0);
74		if (copied != sizeof(tmp))
75			break;
76		ret = put_user(tmp, (u32 __user *) (unsigned long) data);
77		break;
78	}
79
80	/* Read the word at location addr in the USER area. */
81	case PTRACE_PEEKUSR: {
82		struct pt_regs *regs;
83		union fpureg *fregs;
84		unsigned int tmp;
85
86		regs = task_pt_regs(child);
87		ret = 0;  /* Default return value. */
88
89		switch (addr) {
90		case 0 ... 31:
91			tmp = regs->regs[addr];
92			break;
93		case FPR_BASE ... FPR_BASE + 31:
94			if (!tsk_used_math(child)) {
95				/* FP not yet used */
96				tmp = -1;
97				break;
98			}
99			fregs = get_fpu_regs(child);
100			if (test_thread_flag(TIF_32BIT_FPREGS)) {
101				/*
102				 * The odd registers are actually the high
103				 * order bits of the values stored in the even
104				 * registers - unless we're using r2k_switch.S.
105				 */
106				tmp = get_fpr32(&fregs[(addr & ~1) - FPR_BASE],
107						addr & 1);
108				break;
109			}
110			tmp = get_fpr32(&fregs[addr - FPR_BASE], 0);
111			break;
112		case PC:
113			tmp = regs->cp0_epc;
114			break;
115		case CAUSE:
116			tmp = regs->cp0_cause;
117			break;
118		case BADVADDR:
119			tmp = regs->cp0_badvaddr;
120			break;
121		case MMHI:
122			tmp = regs->hi;
123			break;
124		case MMLO:
125			tmp = regs->lo;
126			break;
127		case FPC_CSR:
128			tmp = child->thread.fpu.fcr31;
129			break;
130		case FPC_EIR:
131			/* implementation / version register */
132			tmp = boot_cpu_data.fpu_id;
133			break;
134		case DSP_BASE ... DSP_BASE + 5: {
135			dspreg_t *dregs;
136
137			if (!cpu_has_dsp) {
138				tmp = 0;
139				ret = -EIO;
140				goto out;
141			}
142			dregs = __get_dsp_regs(child);
143			tmp = (unsigned long) (dregs[addr - DSP_BASE]);
144			break;
145		}
146		case DSP_CONTROL:
147			if (!cpu_has_dsp) {
148				tmp = 0;
149				ret = -EIO;
150				goto out;
151			}
152			tmp = child->thread.dsp.dspcontrol;
153			break;
154		default:
155			tmp = 0;
156			ret = -EIO;
157			goto out;
158		}
159		ret = put_user(tmp, (unsigned __user *) (unsigned long) data);
160		break;
161	}
162
163	/*
164	 * Write 4 bytes into the other process' storage
165	 *  data is the 4 bytes that the user wants written
166	 *  addr is a pointer in the user's storage that contains an
167	 *	8 byte address in the other process where the 4 bytes
168	 *	that is to be written
169	 * (this is run in a 32-bit process looking at a 64-bit process)
170	 * when I and D space are separate, these will need to be fixed.
171	 */
172	case PTRACE_POKETEXT_3264:
173	case PTRACE_POKEDATA_3264: {
174		u32 __user * addrOthers;
175
176		/* Get the addr in the other process that we want to write into */
177		ret = -EIO;
178		if (get_user(addrOthers, (u32 __user * __user *) (unsigned long) addr) != 0)
179			break;
180		ret = 0;
181		if (access_process_vm(child, (u64)addrOthers, &data,
182					sizeof(data), 1) == sizeof(data))
183			break;
184		ret = -EIO;
185		break;
186	}
187
188	case PTRACE_POKEUSR: {
189		struct pt_regs *regs;
190		ret = 0;
191		regs = task_pt_regs(child);
192
193		switch (addr) {
194		case 0 ... 31:
195			regs->regs[addr] = data;
196			break;
197		case FPR_BASE ... FPR_BASE + 31: {
198			union fpureg *fregs = get_fpu_regs(child);
199
200			if (!tsk_used_math(child)) {
201				/* FP not yet used  */
202				memset(&child->thread.fpu, ~0,
203				       sizeof(child->thread.fpu));
204				child->thread.fpu.fcr31 = 0;
205			}
206			if (test_thread_flag(TIF_32BIT_FPREGS)) {
207				/*
208				 * The odd registers are actually the high
209				 * order bits of the values stored in the even
210				 * registers - unless we're using r2k_switch.S.
211				 */
212				set_fpr32(&fregs[(addr & ~1) - FPR_BASE],
213					  addr & 1, data);
214				break;
215			}
216			set_fpr64(&fregs[addr - FPR_BASE], 0, data);
217			break;
218		}
219		case PC:
220			regs->cp0_epc = data;
221			break;
222		case MMHI:
223			regs->hi = data;
224			break;
225		case MMLO:
226			regs->lo = data;
227			break;
228		case FPC_CSR:
229			child->thread.fpu.fcr31 = data;
230			break;
231		case DSP_BASE ... DSP_BASE + 5: {
232			dspreg_t *dregs;
233
234			if (!cpu_has_dsp) {
235				ret = -EIO;
236				break;
237			}
238
239			dregs = __get_dsp_regs(child);
240			dregs[addr - DSP_BASE] = data;
241			break;
242		}
243		case DSP_CONTROL:
244			if (!cpu_has_dsp) {
245				ret = -EIO;
246				break;
247			}
248			child->thread.dsp.dspcontrol = data;
249			break;
250		default:
251			/* The rest are not allowed. */
252			ret = -EIO;
253			break;
254		}
255		break;
256		}
257
258	case PTRACE_GETREGS:
259		ret = ptrace_getregs(child,
260				(struct user_pt_regs __user *) (__u64) data);
261		break;
262
263	case PTRACE_SETREGS:
264		ret = ptrace_setregs(child,
265				(struct user_pt_regs __user *) (__u64) data);
266		break;
267
268	case PTRACE_GETFPREGS:
269		ret = ptrace_getfpregs(child, (__u32 __user *) (__u64) data);
270		break;
271
272	case PTRACE_SETFPREGS:
273		ret = ptrace_setfpregs(child, (__u32 __user *) (__u64) data);
274		break;
275
276	case PTRACE_GET_THREAD_AREA:
277		ret = put_user(task_thread_info(child)->tp_value,
278				(unsigned int __user *) (unsigned long) data);
279		break;
280
281	case PTRACE_GET_THREAD_AREA_3264:
282		ret = put_user(task_thread_info(child)->tp_value,
283				(unsigned long __user *) (unsigned long) data);
284		break;
285
286	case PTRACE_GET_WATCH_REGS:
287		ret = ptrace_get_watch_regs(child,
288			(struct pt_watch_regs __user *) (unsigned long) addr);
289		break;
290
291	case PTRACE_SET_WATCH_REGS:
292		ret = ptrace_set_watch_regs(child,
293			(struct pt_watch_regs __user *) (unsigned long) addr);
294		break;
295
296	default:
297		ret = compat_ptrace_request(child, request, addr, data);
298		break;
299	}
300out:
301	return ret;
302}
303