sparc32: get rid of odd callers of copy_regset_from_user()
[linux-block.git] / arch / sparc / kernel / ptrace_32.c
CommitLineData
b2441318 1// SPDX-License-Identifier: GPL-2.0
1da177e4
LT
2/* ptrace.c: Sparc process tracing support.
3 *
8e3fe806 4 * Copyright (C) 1996, 2008 David S. Miller (davem@davemloft.net)
1da177e4
LT
5 *
6 * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson,
7 * and David Mosberger.
8 *
5b2afff2 9 * Added Linux support -miguel (weird, eh?, the original code was meant
1da177e4
LT
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>
1da177e4 20#include <linux/security.h>
7ed20e1a 21#include <linux/signal.h>
8e3fe806
DM
22#include <linux/regset.h>
23#include <linux/elf.h>
1c133b4b 24#include <linux/tracehook.h>
1da177e4 25
7c0f6ba6 26#include <linux/uaccess.h>
d550bbd4 27#include <asm/cacheflush.h>
1da177e4 28
c8c8782d
SR
29#include "kernel.h"
30
1da177e4 31/* #define ALLOW_INIT_TRACING */
1da177e4
LT
32
33/*
34 * Called by kernel/ptrace.c when detaching..
35 *
36 * Make sure single step bits etc are not set.
37 */
38void ptrace_disable(struct task_struct *child)
39{
40 /* nothing to do */
41}
42
8e3fe806
DM
43enum sparc_regset {
44 REGSET_GENERAL,
45 REGSET_FP,
46};
47
cf51e129
AV
48static 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
66static 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
8e3fe806
DM
84static 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;
cf51e129
AV
90 u32 uregs[16];
91 int ret;
8e3fe806
DM
92
93 if (target == current)
94 flush_user_windows();
95
cf51e129
AV
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;
8e3fe806 101
87d80533
AV
102 if (regwindow32_get(target, regs, uregs))
103 return -EFAULT;
104 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
105 uregs,
106 16 * sizeof(u32), 32 * sizeof(u32));
107 if (ret)
108 return ret;
8e3fe806 109
cf51e129
AV
110 uregs[0] = regs->psr;
111 uregs[1] = regs->pc;
112 uregs[2] = regs->npc;
113 uregs[3] = regs->y;
114 uregs[4] = 0; /* WIM */
115 uregs[5] = 0; /* TBR */
116 return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
117 uregs,
118 32 * sizeof(u32), 38 * sizeof(u32));
8e3fe806
DM
119}
120
121static int genregs32_set(struct task_struct *target,
122 const struct user_regset *regset,
123 unsigned int pos, unsigned int count,
124 const void *kbuf, const void __user *ubuf)
125{
126 struct pt_regs *regs = target->thread.kregs;
cf51e129
AV
127 u32 uregs[16];
128 u32 psr;
129 int ret;
8e3fe806
DM
130
131 if (target == current)
132 flush_user_windows();
133
cf51e129
AV
134 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
135 regs->u_regs,
136 0, 16 * sizeof(u32));
137 if (ret || !count)
138 return ret;
8e3fe806 139
98a7fbf3
AV
140 if (regwindow32_get(target, regs, uregs))
141 return -EFAULT;
142 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
143 uregs,
144 16 * sizeof(u32), 32 * sizeof(u32));
145 if (ret)
146 return ret;
147 if (regwindow32_set(target, regs, uregs))
148 return -EFAULT;
149 if (!count)
150 return 0;
151
cf51e129
AV
152 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
153 &psr,
154 32 * sizeof(u32), 33 * sizeof(u32));
155 if (ret)
156 return ret;
157 regs->psr = (regs->psr & ~(PSR_ICC | PSR_SYSCALL)) |
158 (psr & (PSR_ICC | PSR_SYSCALL));
159 if (!count)
160 return 0;
161 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
162 &regs->pc,
163 33 * sizeof(u32), 34 * sizeof(u32));
164 if (ret || !count)
165 return ret;
166 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
9d964e1b 167 &regs->npc,
cf51e129
AV
168 34 * sizeof(u32), 35 * sizeof(u32));
169 if (ret || !count)
170 return ret;
9d964e1b
AV
171 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
172 &regs->y,
173 35 * sizeof(u32), 36 * sizeof(u32));
174 if (ret || !count)
175 return ret;
8e3fe806 176 return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
9d964e1b 177 36 * sizeof(u32), 38 * sizeof(u32));
8e3fe806
DM
178}
179
180static int fpregs32_get(struct task_struct *target,
181 const struct user_regset *regset,
182 unsigned int pos, unsigned int count,
183 void *kbuf, void __user *ubuf)
184{
185 const unsigned long *fpregs = target->thread.float_regs;
186 int ret = 0;
187
188#if 0
189 if (target == current)
190 save_and_clear_fpu();
191#endif
192
193 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
194 fpregs,
195 0, 32 * sizeof(u32));
196
197 if (!ret)
198 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
199 32 * sizeof(u32),
200 33 * sizeof(u32));
201 if (!ret)
202 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
203 &target->thread.fsr,
204 33 * sizeof(u32),
205 34 * sizeof(u32));
206
207 if (!ret) {
208 unsigned long val;
209
210 val = (1 << 8) | (8 << 16);
211 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
212 &val,
213 34 * sizeof(u32),
214 35 * sizeof(u32));
215 }
216
217 if (!ret)
218 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
219 35 * sizeof(u32), -1);
220
221 return ret;
222}
223
224static int fpregs32_set(struct task_struct *target,
225 const struct user_regset *regset,
226 unsigned int pos, unsigned int count,
227 const void *kbuf, const void __user *ubuf)
228{
229 unsigned long *fpregs = target->thread.float_regs;
230 int ret;
231
232#if 0
233 if (target == current)
234 save_and_clear_fpu();
235#endif
236 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
237 fpregs,
238 0, 32 * sizeof(u32));
239 if (!ret)
240 user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
241 32 * sizeof(u32),
242 33 * sizeof(u32));
98a7fbf3 243 if (!ret)
8e3fe806
DM
244 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
245 &target->thread.fsr,
246 33 * sizeof(u32),
247 34 * sizeof(u32));
8e3fe806
DM
248 if (!ret)
249 ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
250 34 * sizeof(u32), -1);
251 return ret;
252}
253
254static const struct user_regset sparc32_regsets[] = {
255 /* Format is:
256 * G0 --> G7
257 * O0 --> O7
258 * L0 --> L7
259 * I0 --> I7
260 * PSR, PC, nPC, Y, WIM, TBR
261 */
262 [REGSET_GENERAL] = {
263 .core_note_type = NT_PRSTATUS,
7d4ee289 264 .n = 38,
8e3fe806
DM
265 .size = sizeof(u32), .align = sizeof(u32),
266 .get = genregs32_get, .set = genregs32_set
267 },
268 /* Format is:
269 * F0 --> F31
270 * empty 32-bit word
271 * FSR (32--bit word)
272 * FPU QUEUE COUNT (8-bit char)
273 * FPU QUEUE ENTRYSIZE (8-bit char)
274 * FPU ENABLED (8-bit char)
275 * empty 8-bit char
276 * FPU QUEUE (64 32-bit ints)
277 */
278 [REGSET_FP] = {
279 .core_note_type = NT_PRFPREG,
7d4ee289 280 .n = 99,
8e3fe806
DM
281 .size = sizeof(u32), .align = sizeof(u32),
282 .get = fpregs32_get, .set = fpregs32_set
283 },
284};
285
87d80533
AV
286static int getregs_get(struct task_struct *target,
287 const struct user_regset *regset,
288 unsigned int pos, unsigned int count,
289 void *kbuf, void __user *ubuf)
290{
291 const struct pt_regs *regs = target->thread.kregs;
292 u32 v[4];
293 int ret;
294
295 if (target == current)
296 flush_user_windows();
297
298 v[0] = regs->psr;
299 v[1] = regs->pc;
300 v[2] = regs->npc;
301 v[3] = regs->y;
302 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
303 v,
304 0 * sizeof(u32), 4 * sizeof(u32));
305 if (ret)
306 return ret;
307
308 return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
309 regs->u_regs + 1,
310 4 * sizeof(u32), 19 * sizeof(u32));
311}
312
98a7fbf3
AV
313static int setregs_set(struct task_struct *target,
314 const struct user_regset *regset,
315 unsigned int pos, unsigned int count,
316 const void *kbuf, const void __user *ubuf)
317{
318 struct pt_regs *regs = target->thread.kregs;
319 u32 v[4];
320 int ret;
321
322 if (target == current)
323 flush_user_windows();
324
325 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
326 v,
327 0, 4 * sizeof(u32));
328 if (ret)
329 return ret;
330 regs->psr = (regs->psr & ~(PSR_ICC | PSR_SYSCALL)) |
331 (v[0] & (PSR_ICC | PSR_SYSCALL));
332 regs->pc = v[1];
333 regs->npc = v[2];
334 regs->y = v[3];
335 return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
336 regs->u_regs + 1,
337 4 * sizeof(u32) , 19 * sizeof(u32));
338}
339
87d80533
AV
340static int getfpregs_get(struct task_struct *target,
341 const struct user_regset *regset,
342 unsigned int pos, unsigned int count,
343 void *kbuf, void __user *ubuf)
344{
345 const unsigned long *fpregs = target->thread.float_regs;
346 int ret = 0;
347
348#if 0
349 if (target == current)
350 save_and_clear_fpu();
351#endif
352
353 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
354 fpregs,
355 0, 32 * sizeof(u32));
356 if (ret)
357 return ret;
358 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
359 &target->thread.fsr,
360 32 * sizeof(u32), 33 * sizeof(u32));
361 if (ret)
362 return ret;
363 return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
364 33 * sizeof(u32), 68 * sizeof(u32));
365}
366
98a7fbf3
AV
367static int setfpregs_set(struct task_struct *target,
368 const struct user_regset *regset,
369 unsigned int pos, unsigned int count,
370 const void *kbuf, const void __user *ubuf)
371{
372 unsigned long *fpregs = target->thread.float_regs;
373 int ret;
374
375#if 0
376 if (target == current)
377 save_and_clear_fpu();
378#endif
379 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
380 fpregs,
381 0, 32 * sizeof(u32));
382 if (ret)
383 return ret;
384 return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
385 &target->thread.fsr,
386 32 * sizeof(u32),
387 33 * sizeof(u32));
388}
389
87d80533
AV
390static const struct user_regset ptrace32_regsets[] = {
391 [REGSET_GENERAL] = {
98a7fbf3
AV
392 .n = 19, .size = sizeof(u32),
393 .get = getregs_get, .set = setregs_set,
87d80533
AV
394 },
395 [REGSET_FP] = {
98a7fbf3
AV
396 .n = 68, .size = sizeof(u32),
397 .get = getfpregs_get, .set = setfpregs_set,
87d80533
AV
398 },
399};
400
401static const struct user_regset_view ptrace32_view = {
402 .regsets = ptrace32_regsets, .n = ARRAY_SIZE(ptrace32_regsets)
403};
404
8e3fe806
DM
405static const struct user_regset_view user_sparc32_view = {
406 .name = "sparc", .e_machine = EM_SPARC,
407 .regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets)
408};
409
410const struct user_regset_view *task_user_regset_view(struct task_struct *task)
411{
412 return &user_sparc32_view;
413}
414
a9384e23
NK
415struct fps {
416 unsigned long regs[32];
417 unsigned long fsr;
418 unsigned long flags;
419 unsigned long extra;
420 unsigned long fpqd;
421 struct fq {
422 unsigned long *insnaddr;
423 unsigned long insn;
424 } fpq[16];
425};
426
9b05a69e
NK
427long arch_ptrace(struct task_struct *child, long request,
428 unsigned long addr, unsigned long data)
1da177e4 429{
9775369e 430 unsigned long addr2 = current->thread.kregs->u_regs[UREG_I4];
a9384e23 431 void __user *addr2p;
a9384e23
NK
432 struct pt_regs __user *pregs;
433 struct fps __user *fps;
d256eb8d
DM
434 int ret;
435
a9384e23
NK
436 addr2p = (void __user *) addr2;
437 pregs = (struct pt_regs __user *) addr;
438 fps = (struct fps __user *) addr;
1da177e4
LT
439
440 switch(request) {
1da177e4 441 case PTRACE_GETREGS: {
87d80533
AV
442 ret = copy_regset_to_user(child, &ptrace32_view,
443 REGSET_GENERAL, 0,
444 19 * sizeof(u32),
445 pregs);
9775369e 446 break;
1da177e4
LT
447 }
448
449 case PTRACE_SETREGS: {
98a7fbf3
AV
450 ret = copy_regset_from_user(child, &ptrace32_view,
451 REGSET_GENERAL, 0,
452 19 * sizeof(u32),
453 pregs);
9775369e 454 break;
1da177e4
LT
455 }
456
457 case PTRACE_GETFPREGS: {
87d80533
AV
458 ret = copy_regset_to_user(child, &ptrace32_view,
459 REGSET_FP, 0,
460 68 * sizeof(u32),
461 fps);
9775369e 462 break;
1da177e4
LT
463 }
464
465 case PTRACE_SETFPREGS: {
98a7fbf3
AV
466 ret = copy_regset_from_user(child, &ptrace32_view,
467 REGSET_FP, 0,
468 33 * sizeof(u32),
469 fps);
9775369e 470 break;
1da177e4
LT
471 }
472
473 case PTRACE_READTEXT:
9775369e 474 case PTRACE_READDATA:
a9384e23 475 ret = ptrace_readdata(child, addr, addr2p, data);
9775369e
DM
476
477 if (ret == data)
478 ret = 0;
479 else if (ret >= 0)
480 ret = -EIO;
481 break;
1da177e4
LT
482
483 case PTRACE_WRITETEXT:
9775369e 484 case PTRACE_WRITEDATA:
a9384e23 485 ret = ptrace_writedata(child, addr2p, addr, data);
9775369e
DM
486
487 if (ret == data)
488 ret = 0;
489 else if (ret >= 0)
490 ret = -EIO;
491 break;
1da177e4 492
9775369e 493 default:
986bef85
DM
494 if (request == PTRACE_SPARC_DETACH)
495 request = PTRACE_DETACH;
9775369e
DM
496 ret = ptrace_request(child, request, addr, data);
497 break;
1da177e4
LT
498 }
499
9775369e 500 return ret;
1da177e4
LT
501}
502
1c133b4b 503asmlinkage int syscall_trace(struct pt_regs *regs, int syscall_exit_p)
1da177e4 504{
1c133b4b
DM
505 int ret = 0;
506
507 if (test_thread_flag(TIF_SYSCALL_TRACE)) {
508 if (syscall_exit_p)
509 tracehook_report_syscall_exit(regs, 0);
510 else
511 ret = tracehook_report_syscall_entry(regs);
1da177e4 512 }
1c133b4b
DM
513
514 return ret;
1da177e4 515}