aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sparc/kernel/ptrace_64.c
diff options
context:
space:
mode:
authorSam Ravnborg <sam@ravnborg.org>2008-12-03 06:11:52 -0500
committerDavid S. Miller <davem@davemloft.net>2008-12-04 12:17:21 -0500
commita88b5ba8bd8ac18aad65ee6c6a254e2e74876db3 (patch)
treeeb3d0ffaf53c3f7ec6083752c2097cecd1cb892a /arch/sparc/kernel/ptrace_64.c
parentd670bd4f803c8b646acd20f3ba21e65458293faf (diff)
sparc,sparc64: unify kernel/
o Move all files from sparc64/kernel/ to sparc/kernel - rename as appropriate o Update sparc/Makefile to the changes o Update sparc/kernel/Makefile to include the sparc64 files NOTE: This commit changes link order on sparc64! Link order had to change for either of sparc32 and sparc64. And assuming sparc64 see more testing than sparc32 change link order on sparc64 where issues will be caught faster. Signed-off-by: Sam Ravnborg <sam@ravnborg.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'arch/sparc/kernel/ptrace_64.c')
-rw-r--r--arch/sparc/kernel/ptrace_64.c1090
1 files changed, 1090 insertions, 0 deletions
diff --git a/arch/sparc/kernel/ptrace_64.c b/arch/sparc/kernel/ptrace_64.c
new file mode 100644
index 000000000000..a941c610e7ce
--- /dev/null
+++ b/arch/sparc/kernel/ptrace_64.c
@@ -0,0 +1,1090 @@
1/* ptrace.c: Sparc process tracing support.
2 *
3 * Copyright (C) 1996, 2008 David S. Miller (davem@davemloft.net)
4 * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
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/smp_lock.h>
21#include <linux/security.h>
22#include <linux/seccomp.h>
23#include <linux/audit.h>
24#include <linux/signal.h>
25#include <linux/regset.h>
26#include <linux/tracehook.h>
27#include <linux/compat.h>
28#include <linux/elf.h>
29
30#include <asm/asi.h>
31#include <asm/pgtable.h>
32#include <asm/system.h>
33#include <asm/uaccess.h>
34#include <asm/psrcompat.h>
35#include <asm/visasm.h>
36#include <asm/spitfire.h>
37#include <asm/page.h>
38#include <asm/cpudata.h>
39#include <asm/cacheflush.h>
40
41#include "entry.h"
42
43/* #define ALLOW_INIT_TRACING */
44
45/*
46 * Called by kernel/ptrace.c when detaching..
47 *
48 * Make sure single step bits etc are not set.
49 */
50void ptrace_disable(struct task_struct *child)
51{
52 /* nothing to do */
53}
54
55/* To get the necessary page struct, access_process_vm() first calls
56 * get_user_pages(). This has done a flush_dcache_page() on the
57 * accessed page. Then our caller (copy_{to,from}_user_page()) did
58 * to memcpy to read/write the data from that page.
59 *
60 * Now, the only thing we have to do is:
61 * 1) flush the D-cache if it's possible than an illegal alias
62 * has been created
63 * 2) flush the I-cache if this is pre-cheetah and we did a write
64 */
65void flush_ptrace_access(struct vm_area_struct *vma, struct page *page,
66 unsigned long uaddr, void *kaddr,
67 unsigned long len, int write)
68{
69 BUG_ON(len > PAGE_SIZE);
70
71 if (tlb_type == hypervisor)
72 return;
73
74 preempt_disable();
75
76#ifdef DCACHE_ALIASING_POSSIBLE
77 /* If bit 13 of the kernel address we used to access the
78 * user page is the same as the virtual address that page
79 * is mapped to in the user's address space, we can skip the
80 * D-cache flush.
81 */
82 if ((uaddr ^ (unsigned long) kaddr) & (1UL << 13)) {
83 unsigned long start = __pa(kaddr);
84 unsigned long end = start + len;
85 unsigned long dcache_line_size;
86
87 dcache_line_size = local_cpu_data().dcache_line_size;
88
89 if (tlb_type == spitfire) {
90 for (; start < end; start += dcache_line_size)
91 spitfire_put_dcache_tag(start & 0x3fe0, 0x0);
92 } else {
93 start &= ~(dcache_line_size - 1);
94 for (; start < end; start += dcache_line_size)
95 __asm__ __volatile__(
96 "stxa %%g0, [%0] %1\n\t"
97 "membar #Sync"
98 : /* no outputs */
99 : "r" (start),
100 "i" (ASI_DCACHE_INVALIDATE));
101 }
102 }
103#endif
104 if (write && tlb_type == spitfire) {
105 unsigned long start = (unsigned long) kaddr;
106 unsigned long end = start + len;
107 unsigned long icache_line_size;
108
109 icache_line_size = local_cpu_data().icache_line_size;
110
111 for (; start < end; start += icache_line_size)
112 flushi(start);
113 }
114
115 preempt_enable();
116}
117
118static int get_from_target(struct task_struct *target, unsigned long uaddr,
119 void *kbuf, int len)
120{
121 if (target == current) {
122 if (copy_from_user(kbuf, (void __user *) uaddr, len))
123 return -EFAULT;
124 } else {
125 int len2 = access_process_vm(target, uaddr, kbuf, len, 0);
126 if (len2 != len)
127 return -EFAULT;
128 }
129 return 0;
130}
131
132static int set_to_target(struct task_struct *target, unsigned long uaddr,
133 void *kbuf, int len)
134{
135 if (target == current) {
136 if (copy_to_user((void __user *) uaddr, kbuf, len))
137 return -EFAULT;
138 } else {
139 int len2 = access_process_vm(target, uaddr, kbuf, len, 1);
140 if (len2 != len)
141 return -EFAULT;
142 }
143 return 0;
144}
145
146static int regwindow64_get(struct task_struct *target,
147 const struct pt_regs *regs,
148 struct reg_window *wbuf)
149{
150 unsigned long rw_addr = regs->u_regs[UREG_I6];
151
152 if (test_tsk_thread_flag(current, TIF_32BIT)) {
153 struct reg_window32 win32;
154 int i;
155
156 if (get_from_target(target, rw_addr, &win32, sizeof(win32)))
157 return -EFAULT;
158 for (i = 0; i < 8; i++)
159 wbuf->locals[i] = win32.locals[i];
160 for (i = 0; i < 8; i++)
161 wbuf->ins[i] = win32.ins[i];
162 } else {
163 rw_addr += STACK_BIAS;
164 if (get_from_target(target, rw_addr, wbuf, sizeof(*wbuf)))
165 return -EFAULT;
166 }
167
168 return 0;
169}
170
171static int regwindow64_set(struct task_struct *target,
172 const struct pt_regs *regs,
173 struct reg_window *wbuf)
174{
175 unsigned long rw_addr = regs->u_regs[UREG_I6];
176
177 if (test_tsk_thread_flag(current, TIF_32BIT)) {
178 struct reg_window32 win32;
179 int i;
180
181 for (i = 0; i < 8; i++)
182 win32.locals[i] = wbuf->locals[i];
183 for (i = 0; i < 8; i++)
184 win32.ins[i] = wbuf->ins[i];
185
186 if (set_to_target(target, rw_addr, &win32, sizeof(win32)))
187 return -EFAULT;
188 } else {
189 rw_addr += STACK_BIAS;
190 if (set_to_target(target, rw_addr, wbuf, sizeof(*wbuf)))
191 return -EFAULT;
192 }
193
194 return 0;
195}
196
197enum sparc_regset {
198 REGSET_GENERAL,
199 REGSET_FP,
200};
201
202static int genregs64_get(struct task_struct *target,
203 const struct user_regset *regset,
204 unsigned int pos, unsigned int count,
205 void *kbuf, void __user *ubuf)
206{
207 const struct pt_regs *regs = task_pt_regs(target);
208 int ret;
209
210 if (target == current)
211 flushw_user();
212
213 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
214 regs->u_regs,
215 0, 16 * sizeof(u64));
216 if (!ret && count && pos < (32 * sizeof(u64))) {
217 struct reg_window window;
218
219 if (regwindow64_get(target, regs, &window))
220 return -EFAULT;
221 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
222 &window,
223 16 * sizeof(u64),
224 32 * sizeof(u64));
225 }
226
227 if (!ret) {
228 /* TSTATE, TPC, TNPC */
229 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
230 &regs->tstate,
231 32 * sizeof(u64),
232 35 * sizeof(u64));
233 }
234
235 if (!ret) {
236 unsigned long y = regs->y;
237
238 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
239 &y,
240 35 * sizeof(u64),
241 36 * sizeof(u64));
242 }
243
244 if (!ret) {
245 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
246 36 * sizeof(u64), -1);
247
248 }
249 return ret;
250}
251
252static int genregs64_set(struct task_struct *target,
253 const struct user_regset *regset,
254 unsigned int pos, unsigned int count,
255 const void *kbuf, const void __user *ubuf)
256{
257 struct pt_regs *regs = task_pt_regs(target);
258 int ret;
259
260 if (target == current)
261 flushw_user();
262
263 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
264 regs->u_regs,
265 0, 16 * sizeof(u64));
266 if (!ret && count && pos < (32 * sizeof(u64))) {
267 struct reg_window window;
268
269 if (regwindow64_get(target, regs, &window))
270 return -EFAULT;
271
272 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
273 &window,
274 16 * sizeof(u64),
275 32 * sizeof(u64));
276
277 if (!ret &&
278 regwindow64_set(target, regs, &window))
279 return -EFAULT;
280 }
281
282 if (!ret && count > 0) {
283 unsigned long tstate;
284
285 /* TSTATE */
286 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
287 &tstate,
288 32 * sizeof(u64),
289 33 * sizeof(u64));
290 if (!ret) {
291 /* Only the condition codes and the "in syscall"
292 * state can be modified in the %tstate register.
293 */
294 tstate &= (TSTATE_ICC | TSTATE_XCC | TSTATE_SYSCALL);
295 regs->tstate &= ~(TSTATE_ICC | TSTATE_XCC | TSTATE_SYSCALL);
296 regs->tstate |= tstate;
297 }
298 }
299
300 if (!ret) {
301 /* TPC, TNPC */
302 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
303 &regs->tpc,
304 33 * sizeof(u64),
305 35 * sizeof(u64));
306 }
307
308 if (!ret) {
309 unsigned long y;
310
311 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
312 &y,
313 35 * sizeof(u64),
314 36 * sizeof(u64));
315 if (!ret)
316 regs->y = y;
317 }
318
319 if (!ret)
320 ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
321 36 * sizeof(u64), -1);
322
323 return ret;
324}
325
326static int fpregs64_get(struct task_struct *target,
327 const struct user_regset *regset,
328 unsigned int pos, unsigned int count,
329 void *kbuf, void __user *ubuf)
330{
331 const unsigned long *fpregs = task_thread_info(target)->fpregs;
332 unsigned long fprs, fsr, gsr;
333 int ret;
334
335 if (target == current)
336 save_and_clear_fpu();
337
338 fprs = task_thread_info(target)->fpsaved[0];
339
340 if (fprs & FPRS_DL)
341 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
342 fpregs,
343 0, 16 * sizeof(u64));
344 else
345 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
346 0,
347 16 * sizeof(u64));
348
349 if (!ret) {
350 if (fprs & FPRS_DU)
351 ret = user_regset_copyout(&pos, &count,
352 &kbuf, &ubuf,
353 fpregs + 16,
354 16 * sizeof(u64),
355 32 * sizeof(u64));
356 else
357 ret = user_regset_copyout_zero(&pos, &count,
358 &kbuf, &ubuf,
359 16 * sizeof(u64),
360 32 * sizeof(u64));
361 }
362
363 if (fprs & FPRS_FEF) {
364 fsr = task_thread_info(target)->xfsr[0];
365 gsr = task_thread_info(target)->gsr[0];
366 } else {
367 fsr = gsr = 0;
368 }
369
370 if (!ret)
371 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
372 &fsr,
373 32 * sizeof(u64),
374 33 * sizeof(u64));
375 if (!ret)
376 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
377 &gsr,
378 33 * sizeof(u64),
379 34 * sizeof(u64));
380 if (!ret)
381 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
382 &fprs,
383 34 * sizeof(u64),
384 35 * sizeof(u64));
385
386 if (!ret)
387 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
388 35 * sizeof(u64), -1);
389
390 return ret;
391}
392
393static int fpregs64_set(struct task_struct *target,
394 const struct user_regset *regset,
395 unsigned int pos, unsigned int count,
396 const void *kbuf, const void __user *ubuf)
397{
398 unsigned long *fpregs = task_thread_info(target)->fpregs;
399 unsigned long fprs;
400 int ret;
401
402 if (target == current)
403 save_and_clear_fpu();
404
405 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
406 fpregs,
407 0, 32 * sizeof(u64));
408 if (!ret)
409 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
410 task_thread_info(target)->xfsr,
411 32 * sizeof(u64),
412 33 * sizeof(u64));
413 if (!ret)
414 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
415 task_thread_info(target)->gsr,
416 33 * sizeof(u64),
417 34 * sizeof(u64));
418
419 fprs = task_thread_info(target)->fpsaved[0];
420 if (!ret && count > 0) {
421 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
422 &fprs,
423 34 * sizeof(u64),
424 35 * sizeof(u64));
425 }
426
427 fprs |= (FPRS_FEF | FPRS_DL | FPRS_DU);
428 task_thread_info(target)->fpsaved[0] = fprs;
429
430 if (!ret)
431 ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
432 35 * sizeof(u64), -1);
433 return ret;
434}
435
436static const struct user_regset sparc64_regsets[] = {
437 /* Format is:
438 * G0 --> G7
439 * O0 --> O7
440 * L0 --> L7
441 * I0 --> I7
442 * TSTATE, TPC, TNPC, Y
443 */
444 [REGSET_GENERAL] = {
445 .core_note_type = NT_PRSTATUS,
446 .n = 36,
447 .size = sizeof(u64), .align = sizeof(u64),
448 .get = genregs64_get, .set = genregs64_set
449 },
450 /* Format is:
451 * F0 --> F63
452 * FSR
453 * GSR
454 * FPRS
455 */
456 [REGSET_FP] = {
457 .core_note_type = NT_PRFPREG,
458 .n = 35,
459 .size = sizeof(u64), .align = sizeof(u64),
460 .get = fpregs64_get, .set = fpregs64_set
461 },
462};
463
464static const struct user_regset_view user_sparc64_view = {
465 .name = "sparc64", .e_machine = EM_SPARCV9,
466 .regsets = sparc64_regsets, .n = ARRAY_SIZE(sparc64_regsets)
467};
468
469#ifdef CONFIG_COMPAT
470static int genregs32_get(struct task_struct *target,
471 const struct user_regset *regset,
472 unsigned int pos, unsigned int count,
473 void *kbuf, void __user *ubuf)
474{
475 const struct pt_regs *regs = task_pt_regs(target);
476 compat_ulong_t __user *reg_window;
477 compat_ulong_t *k = kbuf;
478 compat_ulong_t __user *u = ubuf;
479 compat_ulong_t reg;
480
481 if (target == current)
482 flushw_user();
483
484 pos /= sizeof(reg);
485 count /= sizeof(reg);
486
487 if (kbuf) {
488 for (; count > 0 && pos < 16; count--)
489 *k++ = regs->u_regs[pos++];
490
491 reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6];
492 if (target == current) {
493 for (; count > 0 && pos < 32; count--) {
494 if (get_user(*k++, &reg_window[pos++]))
495 return -EFAULT;
496 }
497 } else {
498 for (; count > 0 && pos < 32; count--) {
499 if (access_process_vm(target,
500 (unsigned long)
501 &reg_window[pos],
502 k, sizeof(*k), 0)
503 != sizeof(*k))
504 return -EFAULT;
505 k++;
506 pos++;
507 }
508 }
509 } else {
510 for (; count > 0 && pos < 16; count--) {
511 if (put_user((compat_ulong_t) regs->u_regs[pos++], u++))
512 return -EFAULT;
513 }
514
515 reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6];
516 if (target == current) {
517 for (; count > 0 && pos < 32; count--) {
518 if (get_user(reg, &reg_window[pos++]) ||
519 put_user(reg, u++))
520 return -EFAULT;
521 }
522 } else {
523 for (; count > 0 && pos < 32; count--) {
524 if (access_process_vm(target,
525 (unsigned long)
526 &reg_window[pos],
527 &reg, sizeof(reg), 0)
528 != sizeof(reg))
529 return -EFAULT;
530 if (access_process_vm(target,
531 (unsigned long) u,
532 &reg, sizeof(reg), 1)
533 != sizeof(reg))
534 return -EFAULT;
535 pos++;
536 u++;
537 }
538 }
539 }
540 while (count > 0) {
541 switch (pos) {
542 case 32: /* PSR */
543 reg = tstate_to_psr(regs->tstate);
544 break;
545 case 33: /* PC */
546 reg = regs->tpc;
547 break;
548 case 34: /* NPC */
549 reg = regs->tnpc;
550 break;
551 case 35: /* Y */
552 reg = regs->y;
553 break;
554 case 36: /* WIM */
555 case 37: /* TBR */
556 reg = 0;
557 break;
558 default:
559 goto finish;
560 }
561
562 if (kbuf)
563 *k++ = reg;
564 else if (put_user(reg, u++))
565 return -EFAULT;
566 pos++;
567 count--;
568 }
569finish:
570 pos *= sizeof(reg);
571 count *= sizeof(reg);
572
573 return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
574 38 * sizeof(reg), -1);
575}
576
577static int genregs32_set(struct task_struct *target,
578 const struct user_regset *regset,
579 unsigned int pos, unsigned int count,
580 const void *kbuf, const void __user *ubuf)
581{
582 struct pt_regs *regs = task_pt_regs(target);
583 compat_ulong_t __user *reg_window;
584 const compat_ulong_t *k = kbuf;
585 const compat_ulong_t __user *u = ubuf;
586 compat_ulong_t reg;
587
588 if (target == current)
589 flushw_user();
590
591 pos /= sizeof(reg);
592 count /= sizeof(reg);
593
594 if (kbuf) {
595 for (; count > 0 && pos < 16; count--)
596 regs->u_regs[pos++] = *k++;
597
598 reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6];
599 if (target == current) {
600 for (; count > 0 && pos < 32; count--) {
601 if (put_user(*k++, &reg_window[pos++]))
602 return -EFAULT;
603 }
604 } else {
605 for (; count > 0 && pos < 32; count--) {
606 if (access_process_vm(target,
607 (unsigned long)
608 &reg_window[pos],
609 (void *) k,
610 sizeof(*k), 1)
611 != sizeof(*k))
612 return -EFAULT;
613 k++;
614 pos++;
615 }
616 }
617 } else {
618 for (; count > 0 && pos < 16; count--) {
619 if (get_user(reg, u++))
620 return -EFAULT;
621 regs->u_regs[pos++] = reg;
622 }
623
624 reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6];
625 if (target == current) {
626 for (; count > 0 && pos < 32; count--) {
627 if (get_user(reg, u++) ||
628 put_user(reg, &reg_window[pos++]))
629 return -EFAULT;
630 }
631 } else {
632 for (; count > 0 && pos < 32; count--) {
633 if (access_process_vm(target,
634 (unsigned long)
635 u,
636 &reg, sizeof(reg), 0)
637 != sizeof(reg))
638 return -EFAULT;
639 if (access_process_vm(target,
640 (unsigned long)
641 &reg_window[pos],
642 &reg, sizeof(reg), 1)
643 != sizeof(reg))
644 return -EFAULT;
645 pos++;
646 u++;
647 }
648 }
649 }
650 while (count > 0) {
651 unsigned long tstate;
652
653 if (kbuf)
654 reg = *k++;
655 else if (get_user(reg, u++))
656 return -EFAULT;
657
658 switch (pos) {
659 case 32: /* PSR */
660 tstate = regs->tstate;
661 tstate &= ~(TSTATE_ICC | TSTATE_XCC | TSTATE_SYSCALL);
662 tstate |= psr_to_tstate_icc(reg);
663 if (reg & PSR_SYSCALL)
664 tstate |= TSTATE_SYSCALL;
665 regs->tstate = tstate;
666 break;
667 case 33: /* PC */
668 regs->tpc = reg;
669 break;
670 case 34: /* NPC */
671 regs->tnpc = reg;
672 break;
673 case 35: /* Y */
674 regs->y = reg;
675 break;
676 case 36: /* WIM */
677 case 37: /* TBR */
678 break;
679 default:
680 goto finish;
681 }
682
683 pos++;
684 count--;
685 }
686finish:
687 pos *= sizeof(reg);
688 count *= sizeof(reg);
689
690 return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
691 38 * sizeof(reg), -1);
692}
693
694static int fpregs32_get(struct task_struct *target,
695 const struct user_regset *regset,
696 unsigned int pos, unsigned int count,
697 void *kbuf, void __user *ubuf)
698{
699 const unsigned long *fpregs = task_thread_info(target)->fpregs;
700 compat_ulong_t enabled;
701 unsigned long fprs;
702 compat_ulong_t fsr;
703 int ret = 0;
704
705 if (target == current)
706 save_and_clear_fpu();
707
708 fprs = task_thread_info(target)->fpsaved[0];
709 if (fprs & FPRS_FEF) {
710 fsr = task_thread_info(target)->xfsr[0];
711 enabled = 1;
712 } else {
713 fsr = 0;
714 enabled = 0;
715 }
716
717 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
718 fpregs,
719 0, 32 * sizeof(u32));
720
721 if (!ret)
722 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
723 32 * sizeof(u32),
724 33 * sizeof(u32));
725 if (!ret)
726 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
727 &fsr,
728 33 * sizeof(u32),
729 34 * sizeof(u32));
730
731 if (!ret) {
732 compat_ulong_t val;
733
734 val = (enabled << 8) | (8 << 16);
735 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
736 &val,
737 34 * sizeof(u32),
738 35 * sizeof(u32));
739 }
740
741 if (!ret)
742 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
743 35 * sizeof(u32), -1);
744
745 return ret;
746}
747
748static int fpregs32_set(struct task_struct *target,
749 const struct user_regset *regset,
750 unsigned int pos, unsigned int count,
751 const void *kbuf, const void __user *ubuf)
752{
753 unsigned long *fpregs = task_thread_info(target)->fpregs;
754 unsigned long fprs;
755 int ret;
756
757 if (target == current)
758 save_and_clear_fpu();
759
760 fprs = task_thread_info(target)->fpsaved[0];
761
762 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
763 fpregs,
764 0, 32 * sizeof(u32));
765 if (!ret)
766 user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
767 32 * sizeof(u32),
768 33 * sizeof(u32));
769 if (!ret && count > 0) {
770 compat_ulong_t fsr;
771 unsigned long val;
772
773 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
774 &fsr,
775 33 * sizeof(u32),
776 34 * sizeof(u32));
777 if (!ret) {
778 val = task_thread_info(target)->xfsr[0];
779 val &= 0xffffffff00000000UL;
780 val |= fsr;
781 task_thread_info(target)->xfsr[0] = val;
782 }
783 }
784
785 fprs |= (FPRS_FEF | FPRS_DL);
786 task_thread_info(target)->fpsaved[0] = fprs;
787
788 if (!ret)
789 ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
790 34 * sizeof(u32), -1);
791 return ret;
792}
793
794static const struct user_regset sparc32_regsets[] = {
795 /* Format is:
796 * G0 --> G7
797 * O0 --> O7
798 * L0 --> L7
799 * I0 --> I7
800 * PSR, PC, nPC, Y, WIM, TBR
801 */
802 [REGSET_GENERAL] = {
803 .core_note_type = NT_PRSTATUS,
804 .n = 38,
805 .size = sizeof(u32), .align = sizeof(u32),
806 .get = genregs32_get, .set = genregs32_set
807 },
808 /* Format is:
809 * F0 --> F31
810 * empty 32-bit word
811 * FSR (32--bit word)
812 * FPU QUEUE COUNT (8-bit char)
813 * FPU QUEUE ENTRYSIZE (8-bit char)
814 * FPU ENABLED (8-bit char)
815 * empty 8-bit char
816 * FPU QUEUE (64 32-bit ints)
817 */
818 [REGSET_FP] = {
819 .core_note_type = NT_PRFPREG,
820 .n = 99,
821 .size = sizeof(u32), .align = sizeof(u32),
822 .get = fpregs32_get, .set = fpregs32_set
823 },
824};
825
826static const struct user_regset_view user_sparc32_view = {
827 .name = "sparc", .e_machine = EM_SPARC,
828 .regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets)
829};
830#endif /* CONFIG_COMPAT */
831
832const struct user_regset_view *task_user_regset_view(struct task_struct *task)
833{
834#ifdef CONFIG_COMPAT
835 if (test_tsk_thread_flag(task, TIF_32BIT))
836 return &user_sparc32_view;
837#endif
838 return &user_sparc64_view;
839}
840
841#ifdef CONFIG_COMPAT
842struct compat_fps {
843 unsigned int regs[32];
844 unsigned int fsr;
845 unsigned int flags;
846 unsigned int extra;
847 unsigned int fpqd;
848 struct compat_fq {
849 unsigned int insnaddr;
850 unsigned int insn;
851 } fpq[16];
852};
853
854long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
855 compat_ulong_t caddr, compat_ulong_t cdata)
856{
857 const struct user_regset_view *view = task_user_regset_view(current);
858 compat_ulong_t caddr2 = task_pt_regs(current)->u_regs[UREG_I4];
859 struct pt_regs32 __user *pregs;
860 struct compat_fps __user *fps;
861 unsigned long addr2 = caddr2;
862 unsigned long addr = caddr;
863 unsigned long data = cdata;
864 int ret;
865
866 pregs = (struct pt_regs32 __user *) addr;
867 fps = (struct compat_fps __user *) addr;
868
869 switch (request) {
870 case PTRACE_PEEKUSR:
871 ret = (addr != 0) ? -EIO : 0;
872 break;
873
874 case PTRACE_GETREGS:
875 ret = copy_regset_to_user(child, view, REGSET_GENERAL,
876 32 * sizeof(u32),
877 4 * sizeof(u32),
878 &pregs->psr);
879 if (!ret)
880 ret = copy_regset_to_user(child, view, REGSET_GENERAL,
881 1 * sizeof(u32),
882 15 * sizeof(u32),
883 &pregs->u_regs[0]);
884 break;
885
886 case PTRACE_SETREGS:
887 ret = copy_regset_from_user(child, view, REGSET_GENERAL,
888 32 * sizeof(u32),
889 4 * sizeof(u32),
890 &pregs->psr);
891 if (!ret)
892 ret = copy_regset_from_user(child, view, REGSET_GENERAL,
893 1 * sizeof(u32),
894 15 * sizeof(u32),
895 &pregs->u_regs[0]);
896 break;
897
898 case PTRACE_GETFPREGS:
899 ret = copy_regset_to_user(child, view, REGSET_FP,
900 0 * sizeof(u32),
901 32 * sizeof(u32),
902 &fps->regs[0]);
903 if (!ret)
904 ret = copy_regset_to_user(child, view, REGSET_FP,
905 33 * sizeof(u32),
906 1 * sizeof(u32),
907 &fps->fsr);
908 if (!ret) {
909 if (__put_user(0, &fps->flags) ||
910 __put_user(0, &fps->extra) ||
911 __put_user(0, &fps->fpqd) ||
912 clear_user(&fps->fpq[0], 32 * sizeof(unsigned int)))
913 ret = -EFAULT;
914 }
915 break;
916
917 case PTRACE_SETFPREGS:
918 ret = copy_regset_from_user(child, view, REGSET_FP,
919 0 * sizeof(u32),
920 32 * sizeof(u32),
921 &fps->regs[0]);
922 if (!ret)
923 ret = copy_regset_from_user(child, view, REGSET_FP,
924 33 * sizeof(u32),
925 1 * sizeof(u32),
926 &fps->fsr);
927 break;
928
929 case PTRACE_READTEXT:
930 case PTRACE_READDATA:
931 ret = ptrace_readdata(child, addr,
932 (char __user *)addr2, data);
933 if (ret == data)
934 ret = 0;
935 else if (ret >= 0)
936 ret = -EIO;
937 break;
938
939 case PTRACE_WRITETEXT:
940 case PTRACE_WRITEDATA:
941 ret = ptrace_writedata(child, (char __user *) addr2,
942 addr, data);
943 if (ret == data)
944 ret = 0;
945 else if (ret >= 0)
946 ret = -EIO;
947 break;
948
949 default:
950 if (request == PTRACE_SPARC_DETACH)
951 request = PTRACE_DETACH;
952 ret = compat_ptrace_request(child, request, addr, data);
953 break;
954 }
955
956 return ret;
957}
958#endif /* CONFIG_COMPAT */
959
960struct fps {
961 unsigned int regs[64];
962 unsigned long fsr;
963};
964
965long arch_ptrace(struct task_struct *child, long request, long addr, long data)
966{
967 const struct user_regset_view *view = task_user_regset_view(current);
968 unsigned long addr2 = task_pt_regs(current)->u_regs[UREG_I4];
969 struct pt_regs __user *pregs;
970 struct fps __user *fps;
971 int ret;
972
973 pregs = (struct pt_regs __user *) (unsigned long) addr;
974 fps = (struct fps __user *) (unsigned long) addr;
975
976 switch (request) {
977 case PTRACE_PEEKUSR:
978 ret = (addr != 0) ? -EIO : 0;
979 break;
980
981 case PTRACE_GETREGS64:
982 ret = copy_regset_to_user(child, view, REGSET_GENERAL,
983 1 * sizeof(u64),
984 15 * sizeof(u64),
985 &pregs->u_regs[0]);
986 if (!ret) {
987 /* XXX doesn't handle 'y' register correctly XXX */
988 ret = copy_regset_to_user(child, view, REGSET_GENERAL,
989 32 * sizeof(u64),
990 4 * sizeof(u64),
991 &pregs->tstate);
992 }
993 break;
994
995 case PTRACE_SETREGS64:
996 ret = copy_regset_from_user(child, view, REGSET_GENERAL,
997 1 * sizeof(u64),
998 15 * sizeof(u64),
999 &pregs->u_regs[0]);
1000 if (!ret) {
1001 /* XXX doesn't handle 'y' register correctly XXX */
1002 ret = copy_regset_from_user(child, view, REGSET_GENERAL,
1003 32 * sizeof(u64),
1004 4 * sizeof(u64),
1005 &pregs->tstate);
1006 }
1007 break;
1008
1009 case PTRACE_GETFPREGS64:
1010 ret = copy_regset_to_user(child, view, REGSET_FP,
1011 0 * sizeof(u64),
1012 33 * sizeof(u64),
1013 fps);
1014 break;
1015
1016 case PTRACE_SETFPREGS64:
1017 ret = copy_regset_from_user(child, view, REGSET_FP,
1018 0 * sizeof(u64),
1019 33 * sizeof(u64),
1020 fps);
1021 break;
1022
1023 case PTRACE_READTEXT:
1024 case PTRACE_READDATA:
1025 ret = ptrace_readdata(child, addr,
1026 (char __user *)addr2, data);
1027 if (ret == data)
1028 ret = 0;
1029 else if (ret >= 0)
1030 ret = -EIO;
1031 break;
1032
1033 case PTRACE_WRITETEXT:
1034 case PTRACE_WRITEDATA:
1035 ret = ptrace_writedata(child, (char __user *) addr2,
1036 addr, data);
1037 if (ret == data)
1038 ret = 0;
1039 else if (ret >= 0)
1040 ret = -EIO;
1041 break;
1042
1043 default:
1044 if (request == PTRACE_SPARC_DETACH)
1045 request = PTRACE_DETACH;
1046 ret = ptrace_request(child, request, addr, data);
1047 break;
1048 }
1049
1050 return ret;
1051}
1052
1053asmlinkage int syscall_trace_enter(struct pt_regs *regs)
1054{
1055 int ret = 0;
1056
1057 /* do the secure computing check first */
1058 secure_computing(regs->u_regs[UREG_G1]);
1059
1060 if (test_thread_flag(TIF_SYSCALL_TRACE))
1061 ret = tracehook_report_syscall_entry(regs);
1062
1063 if (unlikely(current->audit_context) && !ret)
1064 audit_syscall_entry((test_thread_flag(TIF_32BIT) ?
1065 AUDIT_ARCH_SPARC :
1066 AUDIT_ARCH_SPARC64),
1067 regs->u_regs[UREG_G1],
1068 regs->u_regs[UREG_I0],
1069 regs->u_regs[UREG_I1],
1070 regs->u_regs[UREG_I2],
1071 regs->u_regs[UREG_I3]);
1072
1073 return ret;
1074}
1075
1076asmlinkage void syscall_trace_leave(struct pt_regs *regs)
1077{
1078 if (unlikely(current->audit_context)) {
1079 unsigned long tstate = regs->tstate;
1080 int result = AUDITSC_SUCCESS;
1081
1082 if (unlikely(tstate & (TSTATE_XCARRY | TSTATE_ICARRY)))
1083 result = AUDITSC_FAILURE;
1084
1085 audit_syscall_exit(result, regs->u_regs[UREG_I0]);
1086 }
1087
1088 if (test_thread_flag(TIF_SYSCALL_TRACE))
1089 tracehook_report_syscall_exit(regs, 0);
1090}