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