blob: aaae865e79329aafd093974a2fb1a26c61992a5f [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/* ptrace.c: Sparc process tracing support.
2 *
David S. Millerd09c2a22008-02-06 23:02:08 -08003 * Copyright (C) 1996, 2008 David S. Miller (davem@davemloft.net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 * 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>
David S. Millerf7ceba32005-07-10 19:29:45 -070022#include <linux/seccomp.h>
23#include <linux/audit.h>
Jesper Juhl7ed20e12005-05-01 08:59:14 -070024#include <linux/signal.h>
David S. Millerd09c2a22008-02-06 23:02:08 -080025#include <linux/regset.h>
26#include <linux/compat.h>
27#include <linux/elf.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070028
29#include <asm/asi.h>
30#include <asm/pgtable.h>
31#include <asm/system.h>
32#include <asm/uaccess.h>
33#include <asm/psrcompat.h>
34#include <asm/visasm.h>
35#include <asm/spitfire.h>
David S. Miller6a9b4902005-09-19 20:11:57 -070036#include <asm/page.h>
David S. Miller717463d2005-09-29 18:50:34 -070037#include <asm/cpudata.h>
David S. Millerbfdf9eb2008-03-26 00:46:21 -070038#include <asm/cacheflush.h>
39
40#include "entry.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070041
Linus Torvalds1da177e2005-04-16 15:20:36 -070042/* #define ALLOW_INIT_TRACING */
Linus Torvalds1da177e2005-04-16 15:20:36 -070043
44/*
45 * Called by kernel/ptrace.c when detaching..
46 *
47 * Make sure single step bits etc are not set.
48 */
49void ptrace_disable(struct task_struct *child)
50{
51 /* nothing to do */
52}
53
David S. Millerdadeafd2005-04-17 18:03:11 -070054/* To get the necessary page struct, access_process_vm() first calls
55 * get_user_pages(). This has done a flush_dcache_page() on the
56 * accessed page. Then our caller (copy_{to,from}_user_page()) did
57 * to memcpy to read/write the data from that page.
58 *
59 * Now, the only thing we have to do is:
60 * 1) flush the D-cache if it's possible than an illegal alias
61 * has been created
62 * 2) flush the I-cache if this is pre-cheetah and we did a write
63 */
64void flush_ptrace_access(struct vm_area_struct *vma, struct page *page,
65 unsigned long uaddr, void *kaddr,
66 unsigned long len, int write)
67{
68 BUG_ON(len > PAGE_SIZE);
69
David S. Miller7adb37f2006-02-17 15:07:43 -080070 if (tlb_type == hypervisor)
71 return;
72
David S. Millerf6a843d2008-03-26 04:51:12 -070073 preempt_disable();
74
David S. Millerdadeafd2005-04-17 18:03:11 -070075#ifdef DCACHE_ALIASING_POSSIBLE
76 /* If bit 13 of the kernel address we used to access the
77 * user page is the same as the virtual address that page
78 * is mapped to in the user's address space, we can skip the
79 * D-cache flush.
80 */
David S. Miller6a9b4902005-09-19 20:11:57 -070081 if ((uaddr ^ (unsigned long) kaddr) & (1UL << 13)) {
David S. Millerdadeafd2005-04-17 18:03:11 -070082 unsigned long start = __pa(kaddr);
83 unsigned long end = start + len;
David S. Miller717463d2005-09-29 18:50:34 -070084 unsigned long dcache_line_size;
85
86 dcache_line_size = local_cpu_data().dcache_line_size;
David S. Millerdadeafd2005-04-17 18:03:11 -070087
88 if (tlb_type == spitfire) {
David S. Miller717463d2005-09-29 18:50:34 -070089 for (; start < end; start += dcache_line_size)
David S. Miller6a9b4902005-09-19 20:11:57 -070090 spitfire_put_dcache_tag(start & 0x3fe0, 0x0);
David S. Millerdadeafd2005-04-17 18:03:11 -070091 } else {
David S. Miller717463d2005-09-29 18:50:34 -070092 start &= ~(dcache_line_size - 1);
93 for (; start < end; start += dcache_line_size)
David S. Millerdadeafd2005-04-17 18:03:11 -070094 __asm__ __volatile__(
95 "stxa %%g0, [%0] %1\n\t"
96 "membar #Sync"
97 : /* no outputs */
David S. Miller6a9b4902005-09-19 20:11:57 -070098 : "r" (start),
David S. Millerdadeafd2005-04-17 18:03:11 -070099 "i" (ASI_DCACHE_INVALIDATE));
100 }
101 }
102#endif
103 if (write && tlb_type == spitfire) {
104 unsigned long start = (unsigned long) kaddr;
105 unsigned long end = start + len;
David S. Miller717463d2005-09-29 18:50:34 -0700106 unsigned long icache_line_size;
David S. Millerdadeafd2005-04-17 18:03:11 -0700107
David S. Miller717463d2005-09-29 18:50:34 -0700108 icache_line_size = local_cpu_data().icache_line_size;
109
110 for (; start < end; start += icache_line_size)
David S. Millerdadeafd2005-04-17 18:03:11 -0700111 flushi(start);
112 }
David S. Millerf6a843d2008-03-26 04:51:12 -0700113
114 preempt_enable();
David S. Millerdadeafd2005-04-17 18:03:11 -0700115}
116
David S. Millerd09c2a22008-02-06 23:02:08 -0800117enum sparc_regset {
118 REGSET_GENERAL,
119 REGSET_FP,
120};
121
122static int genregs64_get(struct task_struct *target,
123 const struct user_regset *regset,
124 unsigned int pos, unsigned int count,
125 void *kbuf, void __user *ubuf)
126{
127 const struct pt_regs *regs = task_pt_regs(target);
128 int ret;
129
130 if (target == current)
131 flushw_user();
132
133 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
134 regs->u_regs,
135 0, 16 * sizeof(u64));
136 if (!ret) {
137 unsigned long __user *reg_window = (unsigned long __user *)
138 (regs->u_regs[UREG_I6] + STACK_BIAS);
139 unsigned long window[16];
140
141 if (copy_from_user(window, reg_window, sizeof(window)))
142 return -EFAULT;
143
144 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
145 window,
146 16 * sizeof(u64),
147 32 * sizeof(u64));
148 }
149
150 if (!ret) {
151 /* TSTATE, TPC, TNPC */
152 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
153 &regs->tstate,
154 32 * sizeof(u64),
155 35 * sizeof(u64));
156 }
157
158 if (!ret) {
159 unsigned long y = regs->y;
160
161 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
162 &y,
163 35 * sizeof(u64),
164 36 * sizeof(u64));
165 }
166
167 if (!ret)
168 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
169 36 * sizeof(u64), -1);
170
171 return ret;
172}
173
174static int genregs64_set(struct task_struct *target,
175 const struct user_regset *regset,
176 unsigned int pos, unsigned int count,
177 const void *kbuf, const void __user *ubuf)
178{
179 struct pt_regs *regs = task_pt_regs(target);
180 int ret;
181
182 if (target == current)
183 flushw_user();
184
185 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
186 regs->u_regs,
187 0, 16 * sizeof(u64));
188 if (!ret && count > 0) {
189 unsigned long __user *reg_window = (unsigned long __user *)
190 (regs->u_regs[UREG_I6] + STACK_BIAS);
191 unsigned long window[16];
192
193 if (copy_from_user(window, reg_window, sizeof(window)))
194 return -EFAULT;
195
196 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
197 window,
198 16 * sizeof(u64),
199 32 * sizeof(u64));
200 if (!ret &&
201 copy_to_user(reg_window, window, sizeof(window)))
202 return -EFAULT;
203 }
204
205 if (!ret && count > 0) {
206 unsigned long tstate;
207
208 /* TSTATE */
209 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
210 &tstate,
211 32 * sizeof(u64),
212 33 * sizeof(u64));
213 if (!ret) {
214 /* Only the condition codes can be modified
215 * in the %tstate register.
216 */
217 tstate &= (TSTATE_ICC | TSTATE_XCC);
218 regs->tstate &= ~(TSTATE_ICC | TSTATE_XCC);
219 regs->tstate |= tstate;
220 }
221 }
222
223 if (!ret) {
224 /* TPC, TNPC */
225 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
226 &regs->tpc,
227 33 * sizeof(u64),
228 35 * sizeof(u64));
229 }
230
231 if (!ret) {
232 unsigned long y;
233
234 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
235 &y,
236 35 * sizeof(u64),
237 36 * sizeof(u64));
238 if (!ret)
239 regs->y = y;
240 }
241
242 if (!ret)
243 ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
244 36 * sizeof(u64), -1);
245
246 return ret;
247}
248
249static int fpregs64_get(struct task_struct *target,
250 const struct user_regset *regset,
251 unsigned int pos, unsigned int count,
252 void *kbuf, void __user *ubuf)
253{
254 const unsigned long *fpregs = task_thread_info(target)->fpregs;
255 unsigned long fprs, fsr, gsr;
256 int ret;
257
258 if (target == current)
259 save_and_clear_fpu();
260
261 fprs = task_thread_info(target)->fpsaved[0];
262
263 if (fprs & FPRS_DL)
264 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
265 fpregs,
266 0, 16 * sizeof(u64));
267 else
268 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
269 0,
270 16 * sizeof(u64));
271
272 if (!ret) {
273 if (fprs & FPRS_DU)
274 ret = user_regset_copyout(&pos, &count,
275 &kbuf, &ubuf,
276 fpregs + 16,
277 16 * sizeof(u64),
278 32 * sizeof(u64));
279 else
280 ret = user_regset_copyout_zero(&pos, &count,
281 &kbuf, &ubuf,
282 16 * sizeof(u64),
283 32 * sizeof(u64));
284 }
285
286 if (fprs & FPRS_FEF) {
287 fsr = task_thread_info(target)->xfsr[0];
288 gsr = task_thread_info(target)->gsr[0];
289 } else {
290 fsr = gsr = 0;
291 }
292
293 if (!ret)
294 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
295 &fsr,
296 32 * sizeof(u64),
297 33 * sizeof(u64));
298 if (!ret)
299 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
300 &gsr,
301 33 * sizeof(u64),
302 34 * sizeof(u64));
303 if (!ret)
304 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
305 &fprs,
306 34 * sizeof(u64),
307 35 * sizeof(u64));
308
309 if (!ret)
310 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
311 35 * sizeof(u64), -1);
312
313 return ret;
314}
315
316static int fpregs64_set(struct task_struct *target,
317 const struct user_regset *regset,
318 unsigned int pos, unsigned int count,
319 const void *kbuf, const void __user *ubuf)
320{
321 unsigned long *fpregs = task_thread_info(target)->fpregs;
322 unsigned long fprs;
323 int ret;
324
325 if (target == current)
326 save_and_clear_fpu();
327
328 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
329 fpregs,
330 0, 32 * sizeof(u64));
331 if (!ret)
332 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
333 task_thread_info(target)->xfsr,
334 32 * sizeof(u64),
335 33 * sizeof(u64));
336 if (!ret)
337 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
338 task_thread_info(target)->gsr,
339 33 * sizeof(u64),
340 34 * sizeof(u64));
341
342 fprs = task_thread_info(target)->fpsaved[0];
343 if (!ret && count > 0) {
344 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
345 &fprs,
346 34 * sizeof(u64),
347 35 * sizeof(u64));
348 }
349
350 fprs |= (FPRS_FEF | FPRS_DL | FPRS_DU);
351 task_thread_info(target)->fpsaved[0] = fprs;
352
353 if (!ret)
354 ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
355 35 * sizeof(u64), -1);
356 return ret;
357}
358
359static const struct user_regset sparc64_regsets[] = {
360 /* Format is:
361 * G0 --> G7
362 * O0 --> O7
363 * L0 --> L7
364 * I0 --> I7
365 * TSTATE, TPC, TNPC, Y
366 */
367 [REGSET_GENERAL] = {
368 .core_note_type = NT_PRSTATUS,
369 .n = 36 * sizeof(u64),
370 .size = sizeof(u64), .align = sizeof(u64),
371 .get = genregs64_get, .set = genregs64_set
372 },
373 /* Format is:
374 * F0 --> F63
375 * FSR
376 * GSR
377 * FPRS
378 */
379 [REGSET_FP] = {
380 .core_note_type = NT_PRFPREG,
381 .n = 35 * sizeof(u64),
382 .size = sizeof(u64), .align = sizeof(u64),
383 .get = fpregs64_get, .set = fpregs64_set
384 },
385};
386
387static const struct user_regset_view user_sparc64_view = {
388 .name = "sparc64", .e_machine = EM_SPARCV9,
389 .regsets = sparc64_regsets, .n = ARRAY_SIZE(sparc64_regsets)
390};
391
David S. Miller11cc8a32008-03-26 04:31:50 -0700392#ifdef CONFIG_COMPAT
David S. Millerd09c2a22008-02-06 23:02:08 -0800393static int genregs32_get(struct task_struct *target,
394 const struct user_regset *regset,
395 unsigned int pos, unsigned int count,
396 void *kbuf, void __user *ubuf)
397{
398 const struct pt_regs *regs = task_pt_regs(target);
399 compat_ulong_t __user *reg_window;
400 compat_ulong_t *k = kbuf;
401 compat_ulong_t __user *u = ubuf;
402 compat_ulong_t reg;
403
404 if (target == current)
405 flushw_user();
406
407 pos /= sizeof(reg);
408 count /= sizeof(reg);
409
410 if (kbuf) {
411 for (; count > 0 && pos < 16; count--)
412 *k++ = regs->u_regs[pos++];
413
414 reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6];
415 for (; count > 0 && pos < 32; count--) {
416 if (get_user(*k++, &reg_window[pos++]))
417 return -EFAULT;
418 }
419 } else {
420 for (; count > 0 && pos < 16; count--) {
421 if (put_user((compat_ulong_t) regs->u_regs[pos++], u++))
422 return -EFAULT;
423 }
424
425 reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6];
426 for (; count > 0 && pos < 32; count--) {
427 if (get_user(reg, &reg_window[pos++]) ||
428 put_user(reg, u++))
429 return -EFAULT;
430 }
431 }
432 while (count > 0) {
433 switch (pos) {
434 case 32: /* PSR */
435 reg = tstate_to_psr(regs->tstate);
436 break;
437 case 33: /* PC */
438 reg = regs->tpc;
439 break;
440 case 34: /* NPC */
441 reg = regs->tnpc;
442 break;
443 case 35: /* Y */
444 reg = regs->y;
445 break;
446 case 36: /* WIM */
447 case 37: /* TBR */
448 reg = 0;
449 break;
450 default:
451 goto finish;
452 }
453
454 if (kbuf)
455 *k++ = reg;
456 else if (put_user(reg, u++))
457 return -EFAULT;
458 pos++;
459 count--;
460 }
461finish:
462 pos *= sizeof(reg);
463 count *= sizeof(reg);
464
465 return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
466 38 * sizeof(reg), -1);
467}
468
469static int genregs32_set(struct task_struct *target,
470 const struct user_regset *regset,
471 unsigned int pos, unsigned int count,
472 const void *kbuf, const void __user *ubuf)
473{
474 struct pt_regs *regs = task_pt_regs(target);
475 compat_ulong_t __user *reg_window;
476 const compat_ulong_t *k = kbuf;
477 const compat_ulong_t __user *u = ubuf;
478 compat_ulong_t reg;
479
480 if (target == current)
481 flushw_user();
482
483 pos /= sizeof(reg);
484 count /= sizeof(reg);
485
486 if (kbuf) {
487 for (; count > 0 && pos < 16; count--)
488 regs->u_regs[pos++] = *k++;
489
490 reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6];
491 for (; count > 0 && pos < 32; count--) {
492 if (put_user(*k++, &reg_window[pos++]))
493 return -EFAULT;
494 }
495 } else {
496 for (; count > 0 && pos < 16; count--) {
497 if (get_user(reg, u++))
498 return -EFAULT;
499 regs->u_regs[pos++] = reg;
500 }
501
502 reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6];
503 for (; count > 0 && pos < 32; count--) {
504 if (get_user(reg, u++) ||
505 put_user(reg, &reg_window[pos++]))
506 return -EFAULT;
507 }
508 }
509 while (count > 0) {
510 unsigned long tstate;
511
512 if (kbuf)
513 reg = *k++;
514 else if (get_user(reg, u++))
515 return -EFAULT;
516
517 switch (pos) {
518 case 32: /* PSR */
519 tstate = regs->tstate;
520 tstate &= ~(TSTATE_ICC | TSTATE_XCC);
521 tstate |= psr_to_tstate_icc(reg);
522 regs->tstate = tstate;
523 break;
524 case 33: /* PC */
525 regs->tpc = reg;
526 break;
527 case 34: /* NPC */
528 regs->tnpc = reg;
529 break;
530 case 35: /* Y */
531 regs->y = reg;
532 break;
533 case 36: /* WIM */
534 case 37: /* TBR */
535 break;
536 default:
537 goto finish;
538 }
539
540 pos++;
541 count--;
542 }
543finish:
544 pos *= sizeof(reg);
545 count *= sizeof(reg);
546
547 return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
548 38 * sizeof(reg), -1);
549}
550
551static int fpregs32_get(struct task_struct *target,
552 const struct user_regset *regset,
553 unsigned int pos, unsigned int count,
554 void *kbuf, void __user *ubuf)
555{
556 const unsigned long *fpregs = task_thread_info(target)->fpregs;
557 compat_ulong_t enabled;
558 unsigned long fprs;
559 compat_ulong_t fsr;
560 int ret = 0;
561
562 if (target == current)
563 save_and_clear_fpu();
564
565 fprs = task_thread_info(target)->fpsaved[0];
566 if (fprs & FPRS_FEF) {
567 fsr = task_thread_info(target)->xfsr[0];
568 enabled = 1;
569 } else {
570 fsr = 0;
571 enabled = 0;
572 }
573
574 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
575 fpregs,
576 0, 32 * sizeof(u32));
577
578 if (!ret)
579 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
580 32 * sizeof(u32),
581 33 * sizeof(u32));
582 if (!ret)
583 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
584 &fsr,
585 33 * sizeof(u32),
586 34 * sizeof(u32));
587
588 if (!ret) {
589 compat_ulong_t val;
590
591 val = (enabled << 8) | (8 << 16);
592 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
593 &val,
594 34 * sizeof(u32),
595 35 * sizeof(u32));
596 }
597
598 if (!ret)
599 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
600 35 * sizeof(u32), -1);
601
602 return ret;
603}
604
605static int fpregs32_set(struct task_struct *target,
606 const struct user_regset *regset,
607 unsigned int pos, unsigned int count,
608 const void *kbuf, const void __user *ubuf)
609{
610 unsigned long *fpregs = task_thread_info(target)->fpregs;
611 unsigned long fprs;
612 int ret;
613
614 if (target == current)
615 save_and_clear_fpu();
616
617 fprs = task_thread_info(target)->fpsaved[0];
618
619 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
620 fpregs,
621 0, 32 * sizeof(u32));
622 if (!ret)
623 user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
624 32 * sizeof(u32),
625 33 * sizeof(u32));
626 if (!ret && count > 0) {
627 compat_ulong_t fsr;
628 unsigned long val;
629
630 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
631 &fsr,
632 33 * sizeof(u32),
633 34 * sizeof(u32));
634 if (!ret) {
635 val = task_thread_info(target)->xfsr[0];
636 val &= 0xffffffff00000000UL;
637 val |= fsr;
638 task_thread_info(target)->xfsr[0] = val;
639 }
640 }
641
642 fprs |= (FPRS_FEF | FPRS_DL);
643 task_thread_info(target)->fpsaved[0] = fprs;
644
645 if (!ret)
646 ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
647 34 * sizeof(u32), -1);
648 return ret;
649}
650
651static const struct user_regset sparc32_regsets[] = {
652 /* Format is:
653 * G0 --> G7
654 * O0 --> O7
655 * L0 --> L7
656 * I0 --> I7
657 * PSR, PC, nPC, Y, WIM, TBR
658 */
659 [REGSET_GENERAL] = {
660 .core_note_type = NT_PRSTATUS,
661 .n = 38 * sizeof(u32),
662 .size = sizeof(u32), .align = sizeof(u32),
663 .get = genregs32_get, .set = genregs32_set
664 },
665 /* Format is:
666 * F0 --> F31
667 * empty 32-bit word
668 * FSR (32--bit word)
669 * FPU QUEUE COUNT (8-bit char)
670 * FPU QUEUE ENTRYSIZE (8-bit char)
671 * FPU ENABLED (8-bit char)
672 * empty 8-bit char
673 * FPU QUEUE (64 32-bit ints)
674 */
675 [REGSET_FP] = {
676 .core_note_type = NT_PRFPREG,
677 .n = 99 * sizeof(u32),
678 .size = sizeof(u32), .align = sizeof(u32),
679 .get = fpregs32_get, .set = fpregs32_set
680 },
681};
682
683static const struct user_regset_view user_sparc32_view = {
684 .name = "sparc", .e_machine = EM_SPARC,
685 .regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets)
686};
David S. Miller11cc8a32008-03-26 04:31:50 -0700687#endif /* CONFIG_COMPAT */
David S. Millerd09c2a22008-02-06 23:02:08 -0800688
689const struct user_regset_view *task_user_regset_view(struct task_struct *task)
690{
David S. Miller11cc8a32008-03-26 04:31:50 -0700691#ifdef CONFIG_COMPAT
David S. Millerd09c2a22008-02-06 23:02:08 -0800692 if (test_tsk_thread_flag(task, TIF_32BIT))
693 return &user_sparc32_view;
David S. Miller11cc8a32008-03-26 04:31:50 -0700694#endif
David S. Millerd09c2a22008-02-06 23:02:08 -0800695 return &user_sparc64_view;
696}
697
David S. Miller11cc8a32008-03-26 04:31:50 -0700698#ifdef CONFIG_COMPAT
David S. Miller2ba85f32008-02-07 22:46:09 -0800699struct compat_fps {
700 unsigned int regs[32];
701 unsigned int fsr;
702 unsigned int flags;
703 unsigned int extra;
704 unsigned int fpqd;
705 struct compat_fq {
706 unsigned int insnaddr;
707 unsigned int insn;
708 } fpq[16];
709};
710
711long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
712 compat_ulong_t caddr, compat_ulong_t cdata)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713{
David S. Miller2ba85f32008-02-07 22:46:09 -0800714 const struct user_regset_view *view = task_user_regset_view(child);
715 compat_ulong_t caddr2 = task_pt_regs(current)->u_regs[UREG_I4];
716 struct pt_regs32 __user *pregs;
717 struct compat_fps __user *fps;
718 unsigned long addr2 = caddr2;
719 unsigned long addr = caddr;
720 unsigned long data = cdata;
David S. Miller94732722008-02-07 05:06:12 -0800721 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722
David S. Miller2ba85f32008-02-07 22:46:09 -0800723 pregs = (struct pt_regs32 __user *) addr;
724 fps = (struct compat_fps __user *) addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725
David S. Miller2ba85f32008-02-07 22:46:09 -0800726 switch (request) {
David S. Miller1759e582006-04-01 23:28:10 -0800727 case PTRACE_PEEKUSR:
David S. Miller97753692008-02-07 03:00:17 -0800728 ret = (addr != 0) ? -EIO : 0;
729 break;
David S. Miller1759e582006-04-01 23:28:10 -0800730
David S. Miller2ba85f32008-02-07 22:46:09 -0800731 case PTRACE_GETREGS:
David S. Miller94732722008-02-07 05:06:12 -0800732 ret = copy_regset_to_user(child, view, REGSET_GENERAL,
733 32 * sizeof(u32),
734 4 * sizeof(u32),
735 &pregs->psr);
736 if (!ret)
737 ret = copy_regset_to_user(child, view, REGSET_GENERAL,
738 1 * sizeof(u32),
739 15 * sizeof(u32),
740 &pregs->u_regs[0]);
David S. Miller97753692008-02-07 03:00:17 -0800741 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742
David S. Miller2ba85f32008-02-07 22:46:09 -0800743 case PTRACE_SETREGS:
David S. Miller94732722008-02-07 05:06:12 -0800744 ret = copy_regset_from_user(child, view, REGSET_GENERAL,
745 32 * sizeof(u32),
746 4 * sizeof(u32),
747 &pregs->psr);
748 if (!ret)
749 ret = copy_regset_from_user(child, view, REGSET_GENERAL,
750 1 * sizeof(u32),
751 15 * sizeof(u32),
752 &pregs->u_regs[0]);
David S. Miller97753692008-02-07 03:00:17 -0800753 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754
David S. Miller2ba85f32008-02-07 22:46:09 -0800755 case PTRACE_GETFPREGS:
David S. Miller94732722008-02-07 05:06:12 -0800756 ret = copy_regset_to_user(child, view, REGSET_FP,
757 0 * sizeof(u32),
758 32 * sizeof(u32),
759 &fps->regs[0]);
760 if (!ret)
761 ret = copy_regset_to_user(child, view, REGSET_FP,
762 33 * sizeof(u32),
763 1 * sizeof(u32),
764 &fps->fsr);
765 if (!ret) {
766 if (__put_user(0, &fps->flags) ||
767 __put_user(0, &fps->extra) ||
768 __put_user(0, &fps->fpqd) ||
769 clear_user(&fps->fpq[0], 32 * sizeof(unsigned int)))
770 ret = -EFAULT;
771 }
David S. Miller97753692008-02-07 03:00:17 -0800772 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773
David S. Miller2ba85f32008-02-07 22:46:09 -0800774 case PTRACE_SETFPREGS:
David S. Miller94732722008-02-07 05:06:12 -0800775 ret = copy_regset_from_user(child, view, REGSET_FP,
776 0 * sizeof(u32),
777 32 * sizeof(u32),
778 &fps->regs[0]);
779 if (!ret)
780 ret = copy_regset_from_user(child, view, REGSET_FP,
781 33 * sizeof(u32),
782 1 * sizeof(u32),
783 &fps->fsr);
David S. Miller97753692008-02-07 03:00:17 -0800784 break;
David S. Miller731bbe42006-04-04 16:54:40 -0700785
David S. Miller97753692008-02-07 03:00:17 -0800786 case PTRACE_READTEXT:
787 case PTRACE_READDATA:
788 ret = ptrace_readdata(child, addr,
789 (char __user *)addr2, data);
790 if (ret == data)
791 ret = 0;
792 else if (ret >= 0)
793 ret = -EIO;
794 break;
795
796 case PTRACE_WRITETEXT:
797 case PTRACE_WRITEDATA:
798 ret = ptrace_writedata(child, (char __user *) addr2,
799 addr, data);
800 if (ret == data)
801 ret = 0;
802 else if (ret >= 0)
803 ret = -EIO;
804 break;
805
David S. Miller2ba85f32008-02-07 22:46:09 -0800806 default:
807 ret = compat_ptrace_request(child, request, addr, data);
David S. Miller97753692008-02-07 03:00:17 -0800808 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809 }
David S. Miller97753692008-02-07 03:00:17 -0800810
David S. Miller2ba85f32008-02-07 22:46:09 -0800811 return ret;
812}
David S. Miller11cc8a32008-03-26 04:31:50 -0700813#endif /* CONFIG_COMPAT */
David S. Miller2ba85f32008-02-07 22:46:09 -0800814
815struct fps {
816 unsigned int regs[64];
817 unsigned long fsr;
818};
819
820long arch_ptrace(struct task_struct *child, long request, long addr, long data)
821{
822 const struct user_regset_view *view = task_user_regset_view(child);
David S. Miller2ba85f32008-02-07 22:46:09 -0800823 unsigned long addr2 = task_pt_regs(current)->u_regs[UREG_I4];
David S. Millerbfdf9eb2008-03-26 00:46:21 -0700824 struct pt_regs __user *pregs;
825 struct fps __user *fps;
David S. Miller2ba85f32008-02-07 22:46:09 -0800826 int ret;
827
David S. Millerbfdf9eb2008-03-26 00:46:21 -0700828 pregs = (struct pt_regs __user *) (unsigned long) addr;
829 fps = (struct fps __user *) (unsigned long) addr;
830
David S. Miller2ba85f32008-02-07 22:46:09 -0800831 switch (request) {
832 case PTRACE_PEEKUSR:
833 ret = (addr != 0) ? -EIO : 0;
834 break;
835
836 case PTRACE_GETREGS64:
837 ret = copy_regset_to_user(child, view, REGSET_GENERAL,
838 1 * sizeof(u64),
839 15 * sizeof(u64),
840 &pregs->u_regs[0]);
841 if (!ret) {
842 /* XXX doesn't handle 'y' register correctly XXX */
843 ret = copy_regset_to_user(child, view, REGSET_GENERAL,
844 32 * sizeof(u64),
845 4 * sizeof(u64),
846 &pregs->tstate);
847 }
848 break;
849
850 case PTRACE_SETREGS64:
851 ret = copy_regset_from_user(child, view, REGSET_GENERAL,
852 1 * sizeof(u64),
853 15 * sizeof(u64),
854 &pregs->u_regs[0]);
855 if (!ret) {
856 /* XXX doesn't handle 'y' register correctly XXX */
857 ret = copy_regset_from_user(child, view, REGSET_GENERAL,
858 32 * sizeof(u64),
859 4 * sizeof(u64),
860 &pregs->tstate);
861 }
862 break;
863
864 case PTRACE_GETFPREGS64:
865 ret = copy_regset_to_user(child, view, REGSET_FP,
866 0 * sizeof(u64),
867 33 * sizeof(u64),
868 fps);
869 break;
870
871 case PTRACE_SETFPREGS64:
872 ret = copy_regset_to_user(child, view, REGSET_FP,
873 0 * sizeof(u64),
874 33 * sizeof(u64),
875 fps);
876 break;
877
878 case PTRACE_READTEXT:
879 case PTRACE_READDATA:
880 ret = ptrace_readdata(child, addr,
881 (char __user *)addr2, data);
882 if (ret == data)
883 ret = 0;
884 else if (ret >= 0)
885 ret = -EIO;
886 break;
887
888 case PTRACE_WRITETEXT:
889 case PTRACE_WRITEDATA:
890 ret = ptrace_writedata(child, (char __user *) addr2,
891 addr, data);
892 if (ret == data)
893 ret = 0;
894 else if (ret >= 0)
895 ret = -EIO;
896 break;
897
David S. Miller97753692008-02-07 03:00:17 -0800898 default:
899 ret = ptrace_request(child, request, addr, data);
900 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901 }
David S. Miller97753692008-02-07 03:00:17 -0800902
903 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904}
905
David S. Miller8d8a6472005-07-10 16:55:48 -0700906asmlinkage void syscall_trace(struct pt_regs *regs, int syscall_exit_p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907{
David S. Millerbb49bcd2005-07-10 16:49:28 -0700908 /* do the secure computing check first */
David S. Miller8d8a6472005-07-10 16:55:48 -0700909 secure_computing(regs->u_regs[UREG_G1]);
David S. Millerbb49bcd2005-07-10 16:49:28 -0700910
David S. Millerf7ceba32005-07-10 19:29:45 -0700911 if (unlikely(current->audit_context) && syscall_exit_p) {
912 unsigned long tstate = regs->tstate;
913 int result = AUDITSC_SUCCESS;
914
915 if (unlikely(tstate & (TSTATE_XCARRY | TSTATE_ICARRY)))
916 result = AUDITSC_FAILURE;
917
Al Viro5411be52006-03-29 20:23:36 -0500918 audit_syscall_exit(result, regs->u_regs[UREG_I0]);
David S. Millerf7ceba32005-07-10 19:29:45 -0700919 }
920
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921 if (!(current->ptrace & PT_PTRACED))
David S. Millerf7ceba32005-07-10 19:29:45 -0700922 goto out;
923
924 if (!test_thread_flag(TIF_SYSCALL_TRACE))
925 goto out;
926
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927 ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
928 ? 0x80 : 0));
929
930 /*
931 * this isn't the same as continuing with a signal, but it will do
932 * for normal use. strace only continues with a signal if the
933 * stopping signal is not SIGTRAP. -brl
934 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935 if (current->exit_code) {
David S. Millerbb49bcd2005-07-10 16:49:28 -0700936 send_sig(current->exit_code, current, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937 current->exit_code = 0;
938 }
David S. Millerf7ceba32005-07-10 19:29:45 -0700939
940out:
941 if (unlikely(current->audit_context) && !syscall_exit_p)
Al Viro5411be52006-03-29 20:23:36 -0500942 audit_syscall_entry((test_thread_flag(TIF_32BIT) ?
David S. Millerf7ceba32005-07-10 19:29:45 -0700943 AUDIT_ARCH_SPARC :
944 AUDIT_ARCH_SPARC64),
945 regs->u_regs[UREG_G1],
946 regs->u_regs[UREG_I0],
947 regs->u_regs[UREG_I1],
948 regs->u_regs[UREG_I2],
949 regs->u_regs[UREG_I3]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950}