• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/smp_lock.h>
26 #include <linux/user.h>
27 #include <linux/security.h>
28 
29 #include <asm/cpu.h>
30 #include <asm/dsp.h>
31 #include <asm/fpu.h>
32 #include <asm/mipsregs.h>
33 #include <asm/mipsmtregs.h>
34 #include <asm/pgtable.h>
35 #include <asm/page.h>
36 #include <asm/system.h>
37 #include <asm/uaccess.h>
38 #include <asm/bootinfo.h>
39 
40 /*
41  * Tracing a 32-bit process with a 64-bit strace and vice versa will not
42  * work.  I don't know how to fix this.
43  */
compat_arch_ptrace(struct task_struct * child,compat_long_t request,compat_ulong_t caddr,compat_ulong_t cdata)44 long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
45 			compat_ulong_t caddr, compat_ulong_t cdata)
46 {
47 	int addr = caddr;
48 	int data = cdata;
49 	int ret;
50 
51 	switch (request) {
52 
53 	/*
54 	 * Read 4 bytes of the other process' storage
55 	 *  data is a pointer specifying where the user wants the
56 	 *	4 bytes copied into
57 	 *  addr is a pointer in the user's storage that contains an 8 byte
58 	 *	address in the other process of the 4 bytes that is to be read
59 	 * (this is run in a 32-bit process looking at a 64-bit process)
60 	 * when I and D space are separate, these will need to be fixed.
61 	 */
62 	case PTRACE_PEEKTEXT_3264:
63 	case PTRACE_PEEKDATA_3264: {
64 		u32 tmp;
65 		int copied;
66 		u32 __user * addrOthers;
67 
68 		ret = -EIO;
69 
70 		/* Get the addr in the other process that we want to read */
71 		if (get_user(addrOthers, (u32 __user * __user *) (unsigned long) addr) != 0)
72 			break;
73 
74 		copied = access_process_vm(child, (u64)addrOthers, &tmp,
75 				sizeof(tmp), 0);
76 		if (copied != sizeof(tmp))
77 			break;
78 		ret = put_user(tmp, (u32 __user *) (unsigned long) data);
79 		break;
80 	}
81 
82 	/* Read the word at location addr in the USER area. */
83 	case PTRACE_PEEKUSR: {
84 		struct pt_regs *regs;
85 		unsigned int tmp;
86 
87 		regs = task_pt_regs(child);
88 		ret = 0;  /* Default return value. */
89 
90 		switch (addr) {
91 		case 0 ... 31:
92 			tmp = regs->regs[addr];
93 			break;
94 		case FPR_BASE ... FPR_BASE + 31:
95 			if (tsk_used_math(child)) {
96 				fpureg_t *fregs = get_fpu_regs(child);
97 
98 				/*
99 				 * The odd registers are actually the high
100 				 * order bits of the values stored in the even
101 				 * registers - unless we're using r2k_switch.S.
102 				 */
103 				if (addr & 1)
104 					tmp = (unsigned long) (fregs[((addr & ~1) - 32)] >> 32);
105 				else
106 					tmp = (unsigned long) (fregs[(addr - 32)] & 0xffffffff);
107 			} else {
108 				tmp = -1;	/* FP not yet used  */
109 			}
110 			break;
111 		case PC:
112 			tmp = regs->cp0_epc;
113 			break;
114 		case CAUSE:
115 			tmp = regs->cp0_cause;
116 			break;
117 		case BADVADDR:
118 			tmp = regs->cp0_badvaddr;
119 			break;
120 		case MMHI:
121 			tmp = regs->hi;
122 			break;
123 		case MMLO:
124 			tmp = regs->lo;
125 			break;
126 		case FPC_CSR:
127 			tmp = child->thread.fpu.fcr31;
128 			break;
129 		case FPC_EIR: {	/* implementation / version register */
130 			unsigned int flags;
131 #ifdef CONFIG_MIPS_MT_SMTC
132 			unsigned int irqflags;
133 			unsigned int mtflags;
134 #endif /* CONFIG_MIPS_MT_SMTC */
135 
136 			preempt_disable();
137 			if (!cpu_has_fpu) {
138 				preempt_enable();
139 				tmp = 0;
140 				break;
141 			}
142 
143 #ifdef CONFIG_MIPS_MT_SMTC
144 			/* Read-modify-write of Status must be atomic */
145 			local_irq_save(irqflags);
146 			mtflags = dmt();
147 #endif /* CONFIG_MIPS_MT_SMTC */
148 
149 			if (cpu_has_mipsmt) {
150 				unsigned int vpflags = dvpe();
151 				flags = read_c0_status();
152 				__enable_fpu();
153 				__asm__ __volatile__("cfc1\t%0,$0": "=r" (tmp));
154 				write_c0_status(flags);
155 				evpe(vpflags);
156 			} else {
157 				flags = read_c0_status();
158 				__enable_fpu();
159 				__asm__ __volatile__("cfc1\t%0,$0": "=r" (tmp));
160 				write_c0_status(flags);
161 			}
162 #ifdef CONFIG_MIPS_MT_SMTC
163 			emt(mtflags);
164 			local_irq_restore(irqflags);
165 #endif /* CONFIG_MIPS_MT_SMTC */
166 			preempt_enable();
167 			break;
168 		}
169 		case DSP_BASE ... DSP_BASE + 5: {
170 			dspreg_t *dregs;
171 
172 			if (!cpu_has_dsp) {
173 				tmp = 0;
174 				ret = -EIO;
175 				goto out;
176 			}
177 			dregs = __get_dsp_regs(child);
178 			tmp = (unsigned long) (dregs[addr - DSP_BASE]);
179 			break;
180 		}
181 		case DSP_CONTROL:
182 			if (!cpu_has_dsp) {
183 				tmp = 0;
184 				ret = -EIO;
185 				goto out;
186 			}
187 			tmp = child->thread.dsp.dspcontrol;
188 			break;
189 		default:
190 			tmp = 0;
191 			ret = -EIO;
192 			goto out;
193 		}
194 		ret = put_user(tmp, (unsigned __user *) (unsigned long) data);
195 		break;
196 	}
197 
198 	/*
199 	 * Write 4 bytes into the other process' storage
200 	 *  data is the 4 bytes that the user wants written
201 	 *  addr is a pointer in the user's storage that contains an
202 	 *	8 byte address in the other process where the 4 bytes
203 	 *	that is to be written
204 	 * (this is run in a 32-bit process looking at a 64-bit process)
205 	 * when I and D space are separate, these will need to be fixed.
206 	 */
207 	case PTRACE_POKETEXT_3264:
208 	case PTRACE_POKEDATA_3264: {
209 		u32 __user * addrOthers;
210 
211 		/* Get the addr in the other process that we want to write into */
212 		ret = -EIO;
213 		if (get_user(addrOthers, (u32 __user * __user *) (unsigned long) addr) != 0)
214 			break;
215 		ret = 0;
216 		if (access_process_vm(child, (u64)addrOthers, &data,
217 					sizeof(data), 1) == sizeof(data))
218 			break;
219 		ret = -EIO;
220 		break;
221 	}
222 
223 	case PTRACE_POKEUSR: {
224 		struct pt_regs *regs;
225 		ret = 0;
226 		regs = task_pt_regs(child);
227 
228 		switch (addr) {
229 		case 0 ... 31:
230 			regs->regs[addr] = data;
231 			break;
232 		case FPR_BASE ... FPR_BASE + 31: {
233 			fpureg_t *fregs = get_fpu_regs(child);
234 
235 			if (!tsk_used_math(child)) {
236 				/* FP not yet used  */
237 				memset(&child->thread.fpu, ~0,
238 				       sizeof(child->thread.fpu));
239 				child->thread.fpu.fcr31 = 0;
240 			}
241 			/*
242 			 * The odd registers are actually the high order bits
243 			 * of the values stored in the even registers - unless
244 			 * we're using r2k_switch.S.
245 			 */
246 			if (addr & 1) {
247 				fregs[(addr & ~1) - FPR_BASE] &= 0xffffffff;
248 				fregs[(addr & ~1) - FPR_BASE] |= ((unsigned long long) data) << 32;
249 			} else {
250 				fregs[addr - FPR_BASE] &= ~0xffffffffLL;
251 				/* Must cast, lest sign extension fill upper
252 				   bits!  */
253 				fregs[addr - FPR_BASE] |= (unsigned int)data;
254 			}
255 			break;
256 		}
257 		case PC:
258 			regs->cp0_epc = data;
259 			break;
260 		case MMHI:
261 			regs->hi = data;
262 			break;
263 		case MMLO:
264 			regs->lo = data;
265 			break;
266 		case FPC_CSR:
267 			child->thread.fpu.fcr31 = data;
268 			break;
269 		case DSP_BASE ... DSP_BASE + 5: {
270 			dspreg_t *dregs;
271 
272 			if (!cpu_has_dsp) {
273 				ret = -EIO;
274 				break;
275 			}
276 
277 			dregs = __get_dsp_regs(child);
278 			dregs[addr - DSP_BASE] = data;
279 			break;
280 		}
281 		case DSP_CONTROL:
282 			if (!cpu_has_dsp) {
283 				ret = -EIO;
284 				break;
285 			}
286 			child->thread.dsp.dspcontrol = data;
287 			break;
288 		default:
289 			/* The rest are not allowed. */
290 			ret = -EIO;
291 			break;
292 		}
293 		break;
294 		}
295 
296 	case PTRACE_GETREGS:
297 		ret = ptrace_getregs(child, (__s64 __user *) (__u64) data);
298 		break;
299 
300 	case PTRACE_SETREGS:
301 		ret = ptrace_setregs(child, (__s64 __user *) (__u64) data);
302 		break;
303 
304 	case PTRACE_GETFPREGS:
305 		ret = ptrace_getfpregs(child, (__u32 __user *) (__u64) data);
306 		break;
307 
308 	case PTRACE_SETFPREGS:
309 		ret = ptrace_setfpregs(child, (__u32 __user *) (__u64) data);
310 		break;
311 
312 	case PTRACE_GET_THREAD_AREA:
313 		ret = put_user(task_thread_info(child)->tp_value,
314 				(unsigned int __user *) (unsigned long) data);
315 		break;
316 
317 	case PTRACE_GET_THREAD_AREA_3264:
318 		ret = put_user(task_thread_info(child)->tp_value,
319 				(unsigned long __user *) (unsigned long) data);
320 		break;
321 
322 	case PTRACE_GET_WATCH_REGS:
323 		ret = ptrace_get_watch_regs(child,
324 			(struct pt_watch_regs __user *) (unsigned long) addr);
325 		break;
326 
327 	case PTRACE_SET_WATCH_REGS:
328 		ret = ptrace_set_watch_regs(child,
329 			(struct pt_watch_regs __user *) (unsigned long) addr);
330 		break;
331 
332 	default:
333 		ret = compat_ptrace_request(child, request, addr, data);
334 		break;
335 	}
336 out:
337 	return ret;
338 }
339