xref: /openbmc/linux/arch/sparc/kernel/ptrace_32.c (revision e8e0929d)
1 /* ptrace.c: Sparc process tracing support.
2  *
3  * Copyright (C) 1996, 2008 David S. Miller (davem@davemloft.net)
4  *
5  * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson,
6  * and David Mosberger.
7  *
8  * Added Linux support -miguel (weird, eh?, the original code was meant
9  * to emulate SunOS).
10  */
11 
12 #include <linux/kernel.h>
13 #include <linux/sched.h>
14 #include <linux/mm.h>
15 #include <linux/errno.h>
16 #include <linux/ptrace.h>
17 #include <linux/user.h>
18 #include <linux/smp.h>
19 #include <linux/security.h>
20 #include <linux/signal.h>
21 #include <linux/regset.h>
22 #include <linux/elf.h>
23 #include <linux/tracehook.h>
24 
25 #include <asm/pgtable.h>
26 #include <asm/system.h>
27 #include <asm/uaccess.h>
28 
29 /* #define ALLOW_INIT_TRACING */
30 
31 /*
32  * Called by kernel/ptrace.c when detaching..
33  *
34  * Make sure single step bits etc are not set.
35  */
36 void ptrace_disable(struct task_struct *child)
37 {
38 	/* nothing to do */
39 }
40 
41 enum sparc_regset {
42 	REGSET_GENERAL,
43 	REGSET_FP,
44 };
45 
46 static int genregs32_get(struct task_struct *target,
47 			 const struct user_regset *regset,
48 			 unsigned int pos, unsigned int count,
49 			 void *kbuf, void __user *ubuf)
50 {
51 	const struct pt_regs *regs = target->thread.kregs;
52 	unsigned long __user *reg_window;
53 	unsigned long *k = kbuf;
54 	unsigned long __user *u = ubuf;
55 	unsigned long reg;
56 
57 	if (target == current)
58 		flush_user_windows();
59 
60 	pos /= sizeof(reg);
61 	count /= sizeof(reg);
62 
63 	if (kbuf) {
64 		for (; count > 0 && pos < 16; count--)
65 			*k++ = regs->u_regs[pos++];
66 
67 		reg_window = (unsigned long __user *) regs->u_regs[UREG_I6];
68 		for (; count > 0 && pos < 32; count--) {
69 			if (get_user(*k++, &reg_window[pos++]))
70 				return -EFAULT;
71 		}
72 	} else {
73 		for (; count > 0 && pos < 16; count--) {
74 			if (put_user(regs->u_regs[pos++], u++))
75 				return -EFAULT;
76 		}
77 
78 		reg_window = (unsigned long __user *) regs->u_regs[UREG_I6];
79 		for (; count > 0 && pos < 32; count--) {
80 			if (get_user(reg, &reg_window[pos++]) ||
81 			    put_user(reg, u++))
82 				return -EFAULT;
83 		}
84 	}
85 	while (count > 0) {
86 		switch (pos) {
87 		case 32: /* PSR */
88 			reg = regs->psr;
89 			break;
90 		case 33: /* PC */
91 			reg = regs->pc;
92 			break;
93 		case 34: /* NPC */
94 			reg = regs->npc;
95 			break;
96 		case 35: /* Y */
97 			reg = regs->y;
98 			break;
99 		case 36: /* WIM */
100 		case 37: /* TBR */
101 			reg = 0;
102 			break;
103 		default:
104 			goto finish;
105 		}
106 
107 		if (kbuf)
108 			*k++ = reg;
109 		else if (put_user(reg, u++))
110 			return -EFAULT;
111 		pos++;
112 		count--;
113 	}
114 finish:
115 	pos *= sizeof(reg);
116 	count *= sizeof(reg);
117 
118 	return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
119 					38 * sizeof(reg), -1);
120 }
121 
122 static int genregs32_set(struct task_struct *target,
123 			 const struct user_regset *regset,
124 			 unsigned int pos, unsigned int count,
125 			 const void *kbuf, const void __user *ubuf)
126 {
127 	struct pt_regs *regs = target->thread.kregs;
128 	unsigned long __user *reg_window;
129 	const unsigned long *k = kbuf;
130 	const unsigned long __user *u = ubuf;
131 	unsigned long reg;
132 
133 	if (target == current)
134 		flush_user_windows();
135 
136 	pos /= sizeof(reg);
137 	count /= sizeof(reg);
138 
139 	if (kbuf) {
140 		for (; count > 0 && pos < 16; count--)
141 			regs->u_regs[pos++] = *k++;
142 
143 		reg_window = (unsigned long __user *) regs->u_regs[UREG_I6];
144 		for (; count > 0 && pos < 32; count--) {
145 			if (put_user(*k++, &reg_window[pos++]))
146 				return -EFAULT;
147 		}
148 	} else {
149 		for (; count > 0 && pos < 16; count--) {
150 			if (get_user(reg, u++))
151 				return -EFAULT;
152 			regs->u_regs[pos++] = reg;
153 		}
154 
155 		reg_window = (unsigned long __user *) regs->u_regs[UREG_I6];
156 		for (; count > 0 && pos < 32; count--) {
157 			if (get_user(reg, u++) ||
158 			    put_user(reg, &reg_window[pos++]))
159 				return -EFAULT;
160 		}
161 	}
162 	while (count > 0) {
163 		unsigned long psr;
164 
165 		if (kbuf)
166 			reg = *k++;
167 		else if (get_user(reg, u++))
168 			return -EFAULT;
169 
170 		switch (pos) {
171 		case 32: /* PSR */
172 			psr = regs->psr;
173 			psr &= ~(PSR_ICC | PSR_SYSCALL);
174 			psr |= (reg & (PSR_ICC | PSR_SYSCALL));
175 			regs->psr = psr;
176 			break;
177 		case 33: /* PC */
178 			regs->pc = reg;
179 			break;
180 		case 34: /* NPC */
181 			regs->npc = reg;
182 			break;
183 		case 35: /* Y */
184 			regs->y = reg;
185 			break;
186 		case 36: /* WIM */
187 		case 37: /* TBR */
188 			break;
189 		default:
190 			goto finish;
191 		}
192 
193 		pos++;
194 		count--;
195 	}
196 finish:
197 	pos *= sizeof(reg);
198 	count *= sizeof(reg);
199 
200 	return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
201 					 38 * sizeof(reg), -1);
202 }
203 
204 static int fpregs32_get(struct task_struct *target,
205 			const struct user_regset *regset,
206 			unsigned int pos, unsigned int count,
207 			void *kbuf, void __user *ubuf)
208 {
209 	const unsigned long *fpregs = target->thread.float_regs;
210 	int ret = 0;
211 
212 #if 0
213 	if (target == current)
214 		save_and_clear_fpu();
215 #endif
216 
217 	ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
218 				  fpregs,
219 				  0, 32 * sizeof(u32));
220 
221 	if (!ret)
222 		ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
223 					       32 * sizeof(u32),
224 					       33 * sizeof(u32));
225 	if (!ret)
226 		ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
227 					  &target->thread.fsr,
228 					  33 * sizeof(u32),
229 					  34 * sizeof(u32));
230 
231 	if (!ret) {
232 		unsigned long val;
233 
234 		val = (1 << 8) | (8 << 16);
235 		ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
236 					  &val,
237 					  34 * sizeof(u32),
238 					  35 * sizeof(u32));
239 	}
240 
241 	if (!ret)
242 		ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
243 					       35 * sizeof(u32), -1);
244 
245 	return ret;
246 }
247 
248 static int fpregs32_set(struct task_struct *target,
249 			const struct user_regset *regset,
250 			unsigned int pos, unsigned int count,
251 			const void *kbuf, const void __user *ubuf)
252 {
253 	unsigned long *fpregs = target->thread.float_regs;
254 	int ret;
255 
256 #if 0
257 	if (target == current)
258 		save_and_clear_fpu();
259 #endif
260 	ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
261 				 fpregs,
262 				 0, 32 * sizeof(u32));
263 	if (!ret)
264 		user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
265 					  32 * sizeof(u32),
266 					  33 * sizeof(u32));
267 	if (!ret && count > 0) {
268 		ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
269 					 &target->thread.fsr,
270 					 33 * sizeof(u32),
271 					 34 * sizeof(u32));
272 	}
273 
274 	if (!ret)
275 		ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
276 						34 * sizeof(u32), -1);
277 	return ret;
278 }
279 
280 static const struct user_regset sparc32_regsets[] = {
281 	/* Format is:
282 	 * 	G0 --> G7
283 	 *	O0 --> O7
284 	 *	L0 --> L7
285 	 *	I0 --> I7
286 	 *	PSR, PC, nPC, Y, WIM, TBR
287 	 */
288 	[REGSET_GENERAL] = {
289 		.core_note_type = NT_PRSTATUS,
290 		.n = 38,
291 		.size = sizeof(u32), .align = sizeof(u32),
292 		.get = genregs32_get, .set = genregs32_set
293 	},
294 	/* Format is:
295 	 *	F0 --> F31
296 	 *	empty 32-bit word
297 	 *	FSR (32--bit word)
298 	 *	FPU QUEUE COUNT (8-bit char)
299 	 *	FPU QUEUE ENTRYSIZE (8-bit char)
300 	 *	FPU ENABLED (8-bit char)
301 	 *	empty 8-bit char
302 	 *	FPU QUEUE (64 32-bit ints)
303 	 */
304 	[REGSET_FP] = {
305 		.core_note_type = NT_PRFPREG,
306 		.n = 99,
307 		.size = sizeof(u32), .align = sizeof(u32),
308 		.get = fpregs32_get, .set = fpregs32_set
309 	},
310 };
311 
312 static const struct user_regset_view user_sparc32_view = {
313 	.name = "sparc", .e_machine = EM_SPARC,
314 	.regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets)
315 };
316 
317 const struct user_regset_view *task_user_regset_view(struct task_struct *task)
318 {
319 	return &user_sparc32_view;
320 }
321 
322 long arch_ptrace(struct task_struct *child, long request, long addr, long data)
323 {
324 	unsigned long addr2 = current->thread.kregs->u_regs[UREG_I4];
325 	const struct user_regset_view *view;
326 	int ret;
327 
328 	view = task_user_regset_view(current);
329 
330 	switch(request) {
331 	case PTRACE_GETREGS: {
332 		struct pt_regs __user *pregs = (struct pt_regs __user *) addr;
333 
334 		ret = copy_regset_to_user(child, view, REGSET_GENERAL,
335 					  32 * sizeof(u32),
336 					  4 * sizeof(u32),
337 					  &pregs->psr);
338 		if (!ret)
339 			copy_regset_to_user(child, view, REGSET_GENERAL,
340 					    1 * sizeof(u32),
341 					    15 * sizeof(u32),
342 					    &pregs->u_regs[0]);
343 		break;
344 	}
345 
346 	case PTRACE_SETREGS: {
347 		struct pt_regs __user *pregs = (struct pt_regs __user *) addr;
348 
349 		ret = copy_regset_from_user(child, view, REGSET_GENERAL,
350 					    32 * sizeof(u32),
351 					    4 * sizeof(u32),
352 					    &pregs->psr);
353 		if (!ret)
354 			copy_regset_from_user(child, view, REGSET_GENERAL,
355 					      1 * sizeof(u32),
356 					      15 * sizeof(u32),
357 					      &pregs->u_regs[0]);
358 		break;
359 	}
360 
361 	case PTRACE_GETFPREGS: {
362 		struct fps {
363 			unsigned long regs[32];
364 			unsigned long fsr;
365 			unsigned long flags;
366 			unsigned long extra;
367 			unsigned long fpqd;
368 			struct fq {
369 				unsigned long *insnaddr;
370 				unsigned long insn;
371 			} fpq[16];
372 		};
373 		struct fps __user *fps = (struct fps __user *) addr;
374 
375 		ret = copy_regset_to_user(child, view, REGSET_FP,
376 					  0 * sizeof(u32),
377 					  32 * sizeof(u32),
378 					  &fps->regs[0]);
379 		if (!ret)
380 			ret = copy_regset_to_user(child, view, REGSET_FP,
381 						  33 * sizeof(u32),
382 						  1 * sizeof(u32),
383 						  &fps->fsr);
384 
385 		if (!ret) {
386 			if (__put_user(0, &fps->fpqd) ||
387 			    __put_user(0, &fps->flags) ||
388 			    __put_user(0, &fps->extra) ||
389 			    clear_user(fps->fpq, sizeof(fps->fpq)))
390 				ret = -EFAULT;
391 		}
392 		break;
393 	}
394 
395 	case PTRACE_SETFPREGS: {
396 		struct fps {
397 			unsigned long regs[32];
398 			unsigned long fsr;
399 			unsigned long flags;
400 			unsigned long extra;
401 			unsigned long fpqd;
402 			struct fq {
403 				unsigned long *insnaddr;
404 				unsigned long insn;
405 			} fpq[16];
406 		};
407 		struct fps __user *fps = (struct fps __user *) addr;
408 
409 		ret = copy_regset_from_user(child, view, REGSET_FP,
410 					    0 * sizeof(u32),
411 					    32 * sizeof(u32),
412 					    &fps->regs[0]);
413 		if (!ret)
414 			ret = copy_regset_from_user(child, view, REGSET_FP,
415 						    33 * sizeof(u32),
416 						    1 * sizeof(u32),
417 						    &fps->fsr);
418 		break;
419 	}
420 
421 	case PTRACE_READTEXT:
422 	case PTRACE_READDATA:
423 		ret = ptrace_readdata(child, addr,
424 				      (void __user *) addr2, data);
425 
426 		if (ret == data)
427 			ret = 0;
428 		else if (ret >= 0)
429 			ret = -EIO;
430 		break;
431 
432 	case PTRACE_WRITETEXT:
433 	case PTRACE_WRITEDATA:
434 		ret = ptrace_writedata(child, (void __user *) addr2,
435 				       addr, data);
436 
437 		if (ret == data)
438 			ret = 0;
439 		else if (ret >= 0)
440 			ret = -EIO;
441 		break;
442 
443 	default:
444 		if (request == PTRACE_SPARC_DETACH)
445 			request = PTRACE_DETACH;
446 		ret = ptrace_request(child, request, addr, data);
447 		break;
448 	}
449 
450 	return ret;
451 }
452 
453 asmlinkage int syscall_trace(struct pt_regs *regs, int syscall_exit_p)
454 {
455 	int ret = 0;
456 
457 	if (test_thread_flag(TIF_SYSCALL_TRACE)) {
458 		if (syscall_exit_p)
459 			tracehook_report_syscall_exit(regs, 0);
460 		else
461 			ret = tracehook_report_syscall_entry(regs);
462 	}
463 
464 	return ret;
465 }
466