blob: 7963595c77cca5317ad05653d38b202d06c91577 [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
David S. Millerad4f9572008-04-03 16:55:14 -0700141 if (target == current) {
142 if (copy_from_user(window, reg_window, sizeof(window)))
143 return -EFAULT;
144 } else {
145 if (access_process_vm(target,
146 (unsigned long) reg_window,
147 window,
148 sizeof(window), 0) !=
149 sizeof(window))
150 return -EFAULT;
151 }
David S. Millerd09c2a22008-02-06 23:02:08 -0800152
153 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
154 window,
155 16 * sizeof(u64),
156 32 * sizeof(u64));
157 }
158
159 if (!ret) {
160 /* TSTATE, TPC, TNPC */
161 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
162 &regs->tstate,
163 32 * sizeof(u64),
164 35 * sizeof(u64));
165 }
166
167 if (!ret) {
168 unsigned long y = regs->y;
169
170 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
171 &y,
172 35 * sizeof(u64),
173 36 * sizeof(u64));
174 }
175
176 if (!ret)
177 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
178 36 * sizeof(u64), -1);
179
180 return ret;
181}
182
183static int genregs64_set(struct task_struct *target,
184 const struct user_regset *regset,
185 unsigned int pos, unsigned int count,
186 const void *kbuf, const void __user *ubuf)
187{
188 struct pt_regs *regs = task_pt_regs(target);
189 int ret;
190
191 if (target == current)
192 flushw_user();
193
194 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
195 regs->u_regs,
196 0, 16 * sizeof(u64));
197 if (!ret && count > 0) {
198 unsigned long __user *reg_window = (unsigned long __user *)
199 (regs->u_regs[UREG_I6] + STACK_BIAS);
200 unsigned long window[16];
201
David S. Millerad4f9572008-04-03 16:55:14 -0700202 if (target == current) {
203 if (copy_from_user(window, reg_window, sizeof(window)))
204 return -EFAULT;
205 } else {
206 if (access_process_vm(target,
207 (unsigned long) reg_window,
208 window,
209 sizeof(window), 0) !=
210 sizeof(window))
211 return -EFAULT;
212 }
David S. Millerd09c2a22008-02-06 23:02:08 -0800213
214 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
215 window,
216 16 * sizeof(u64),
217 32 * sizeof(u64));
David S. Millerad4f9572008-04-03 16:55:14 -0700218 if (!ret) {
219 if (target == current) {
220 if (copy_to_user(reg_window, window,
221 sizeof(window)))
222 return -EFAULT;
223 } else {
224 if (access_process_vm(target,
225 (unsigned long)
226 reg_window,
227 window,
228 sizeof(window), 1) !=
229 sizeof(window))
230 return -EFAULT;
231 }
232 }
David S. Millerd09c2a22008-02-06 23:02:08 -0800233 }
234
235 if (!ret && count > 0) {
236 unsigned long tstate;
237
238 /* TSTATE */
239 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
240 &tstate,
241 32 * sizeof(u64),
242 33 * sizeof(u64));
243 if (!ret) {
244 /* Only the condition codes can be modified
245 * in the %tstate register.
246 */
247 tstate &= (TSTATE_ICC | TSTATE_XCC);
248 regs->tstate &= ~(TSTATE_ICC | TSTATE_XCC);
249 regs->tstate |= tstate;
250 }
251 }
252
253 if (!ret) {
254 /* TPC, TNPC */
255 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
256 &regs->tpc,
257 33 * sizeof(u64),
258 35 * sizeof(u64));
259 }
260
261 if (!ret) {
262 unsigned long y;
263
264 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
265 &y,
266 35 * sizeof(u64),
267 36 * sizeof(u64));
268 if (!ret)
269 regs->y = y;
270 }
271
272 if (!ret)
273 ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
274 36 * sizeof(u64), -1);
275
276 return ret;
277}
278
279static int fpregs64_get(struct task_struct *target,
280 const struct user_regset *regset,
281 unsigned int pos, unsigned int count,
282 void *kbuf, void __user *ubuf)
283{
284 const unsigned long *fpregs = task_thread_info(target)->fpregs;
285 unsigned long fprs, fsr, gsr;
286 int ret;
287
288 if (target == current)
289 save_and_clear_fpu();
290
291 fprs = task_thread_info(target)->fpsaved[0];
292
293 if (fprs & FPRS_DL)
294 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
295 fpregs,
296 0, 16 * sizeof(u64));
297 else
298 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
299 0,
300 16 * sizeof(u64));
301
302 if (!ret) {
303 if (fprs & FPRS_DU)
304 ret = user_regset_copyout(&pos, &count,
305 &kbuf, &ubuf,
306 fpregs + 16,
307 16 * sizeof(u64),
308 32 * sizeof(u64));
309 else
310 ret = user_regset_copyout_zero(&pos, &count,
311 &kbuf, &ubuf,
312 16 * sizeof(u64),
313 32 * sizeof(u64));
314 }
315
316 if (fprs & FPRS_FEF) {
317 fsr = task_thread_info(target)->xfsr[0];
318 gsr = task_thread_info(target)->gsr[0];
319 } else {
320 fsr = gsr = 0;
321 }
322
323 if (!ret)
324 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
325 &fsr,
326 32 * sizeof(u64),
327 33 * sizeof(u64));
328 if (!ret)
329 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
330 &gsr,
331 33 * sizeof(u64),
332 34 * sizeof(u64));
333 if (!ret)
334 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
335 &fprs,
336 34 * sizeof(u64),
337 35 * sizeof(u64));
338
339 if (!ret)
340 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
341 35 * sizeof(u64), -1);
342
343 return ret;
344}
345
346static int fpregs64_set(struct task_struct *target,
347 const struct user_regset *regset,
348 unsigned int pos, unsigned int count,
349 const void *kbuf, const void __user *ubuf)
350{
351 unsigned long *fpregs = task_thread_info(target)->fpregs;
352 unsigned long fprs;
353 int ret;
354
355 if (target == current)
356 save_and_clear_fpu();
357
358 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
359 fpregs,
360 0, 32 * sizeof(u64));
361 if (!ret)
362 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
363 task_thread_info(target)->xfsr,
364 32 * sizeof(u64),
365 33 * sizeof(u64));
366 if (!ret)
367 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
368 task_thread_info(target)->gsr,
369 33 * sizeof(u64),
370 34 * sizeof(u64));
371
372 fprs = task_thread_info(target)->fpsaved[0];
373 if (!ret && count > 0) {
374 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
375 &fprs,
376 34 * sizeof(u64),
377 35 * sizeof(u64));
378 }
379
380 fprs |= (FPRS_FEF | FPRS_DL | FPRS_DU);
381 task_thread_info(target)->fpsaved[0] = fprs;
382
383 if (!ret)
384 ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
385 35 * sizeof(u64), -1);
386 return ret;
387}
388
389static const struct user_regset sparc64_regsets[] = {
390 /* Format is:
391 * G0 --> G7
392 * O0 --> O7
393 * L0 --> L7
394 * I0 --> I7
395 * TSTATE, TPC, TNPC, Y
396 */
397 [REGSET_GENERAL] = {
398 .core_note_type = NT_PRSTATUS,
399 .n = 36 * sizeof(u64),
400 .size = sizeof(u64), .align = sizeof(u64),
401 .get = genregs64_get, .set = genregs64_set
402 },
403 /* Format is:
404 * F0 --> F63
405 * FSR
406 * GSR
407 * FPRS
408 */
409 [REGSET_FP] = {
410 .core_note_type = NT_PRFPREG,
411 .n = 35 * sizeof(u64),
412 .size = sizeof(u64), .align = sizeof(u64),
413 .get = fpregs64_get, .set = fpregs64_set
414 },
415};
416
417static const struct user_regset_view user_sparc64_view = {
418 .name = "sparc64", .e_machine = EM_SPARCV9,
419 .regsets = sparc64_regsets, .n = ARRAY_SIZE(sparc64_regsets)
420};
421
David S. Miller11cc8a32008-03-26 04:31:50 -0700422#ifdef CONFIG_COMPAT
David S. Millerd09c2a22008-02-06 23:02:08 -0800423static int genregs32_get(struct task_struct *target,
424 const struct user_regset *regset,
425 unsigned int pos, unsigned int count,
426 void *kbuf, void __user *ubuf)
427{
428 const struct pt_regs *regs = task_pt_regs(target);
429 compat_ulong_t __user *reg_window;
430 compat_ulong_t *k = kbuf;
431 compat_ulong_t __user *u = ubuf;
432 compat_ulong_t reg;
433
434 if (target == current)
435 flushw_user();
436
437 pos /= sizeof(reg);
438 count /= sizeof(reg);
439
440 if (kbuf) {
441 for (; count > 0 && pos < 16; count--)
442 *k++ = regs->u_regs[pos++];
443
444 reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6];
David S. Millerad4f9572008-04-03 16:55:14 -0700445 if (target == current) {
446 for (; count > 0 && pos < 32; count--) {
447 if (get_user(*k++, &reg_window[pos++]))
448 return -EFAULT;
449 }
450 } else {
451 for (; count > 0 && pos < 32; count--) {
452 if (access_process_vm(target,
453 (unsigned long)
454 &reg_window[pos],
455 k, sizeof(*k), 0)
456 != sizeof(*k))
457 return -EFAULT;
458 k++;
459 pos++;
460 }
David S. Millerd09c2a22008-02-06 23:02:08 -0800461 }
462 } else {
463 for (; count > 0 && pos < 16; count--) {
464 if (put_user((compat_ulong_t) regs->u_regs[pos++], u++))
465 return -EFAULT;
466 }
467
468 reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6];
David S. Millerad4f9572008-04-03 16:55:14 -0700469 if (target == current) {
470 for (; count > 0 && pos < 32; count--) {
471 if (get_user(reg, &reg_window[pos++]) ||
472 put_user(reg, u++))
473 return -EFAULT;
474 }
475 } else {
476 for (; count > 0 && pos < 32; count--) {
477 if (access_process_vm(target,
478 (unsigned long)
479 &reg_window[pos],
480 &reg, sizeof(reg), 0)
481 != sizeof(reg))
482 return -EFAULT;
483 if (access_process_vm(target,
484 (unsigned long) u,
485 &reg, sizeof(reg), 1)
486 != sizeof(reg))
487 return -EFAULT;
488 pos++;
489 u++;
490 }
David S. Millerd09c2a22008-02-06 23:02:08 -0800491 }
492 }
493 while (count > 0) {
494 switch (pos) {
495 case 32: /* PSR */
496 reg = tstate_to_psr(regs->tstate);
497 break;
498 case 33: /* PC */
499 reg = regs->tpc;
500 break;
501 case 34: /* NPC */
502 reg = regs->tnpc;
503 break;
504 case 35: /* Y */
505 reg = regs->y;
506 break;
507 case 36: /* WIM */
508 case 37: /* TBR */
509 reg = 0;
510 break;
511 default:
512 goto finish;
513 }
514
515 if (kbuf)
516 *k++ = reg;
517 else if (put_user(reg, u++))
518 return -EFAULT;
519 pos++;
520 count--;
521 }
522finish:
523 pos *= sizeof(reg);
524 count *= sizeof(reg);
525
526 return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
527 38 * sizeof(reg), -1);
528}
529
530static int genregs32_set(struct task_struct *target,
531 const struct user_regset *regset,
532 unsigned int pos, unsigned int count,
533 const void *kbuf, const void __user *ubuf)
534{
535 struct pt_regs *regs = task_pt_regs(target);
536 compat_ulong_t __user *reg_window;
537 const compat_ulong_t *k = kbuf;
538 const compat_ulong_t __user *u = ubuf;
539 compat_ulong_t reg;
540
541 if (target == current)
542 flushw_user();
543
544 pos /= sizeof(reg);
545 count /= sizeof(reg);
546
547 if (kbuf) {
548 for (; count > 0 && pos < 16; count--)
549 regs->u_regs[pos++] = *k++;
550
551 reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6];
David S. Millerad4f9572008-04-03 16:55:14 -0700552 if (target == current) {
553 for (; count > 0 && pos < 32; count--) {
554 if (put_user(*k++, &reg_window[pos++]))
555 return -EFAULT;
556 }
557 } else {
558 for (; count > 0 && pos < 32; count--) {
559 if (access_process_vm(target,
560 (unsigned long)
561 &reg_window[pos],
562 (void *) k,
563 sizeof(*k), 1)
564 != sizeof(*k))
565 return -EFAULT;
566 k++;
567 pos++;
568 }
David S. Millerd09c2a22008-02-06 23:02:08 -0800569 }
570 } else {
571 for (; count > 0 && pos < 16; count--) {
572 if (get_user(reg, u++))
573 return -EFAULT;
574 regs->u_regs[pos++] = reg;
575 }
576
577 reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6];
David S. Millerad4f9572008-04-03 16:55:14 -0700578 if (target == current) {
579 for (; count > 0 && pos < 32; count--) {
580 if (get_user(reg, u++) ||
581 put_user(reg, &reg_window[pos++]))
582 return -EFAULT;
583 }
584 } else {
585 for (; count > 0 && pos < 32; count--) {
586 if (access_process_vm(target,
587 (unsigned long)
588 u,
589 &reg, sizeof(reg), 0)
590 != sizeof(reg))
591 return -EFAULT;
592 if (access_process_vm(target,
593 (unsigned long)
594 &reg_window[pos],
595 &reg, sizeof(reg), 1)
596 != sizeof(reg))
597 return -EFAULT;
598 pos++;
599 u++;
600 }
David S. Millerd09c2a22008-02-06 23:02:08 -0800601 }
602 }
603 while (count > 0) {
604 unsigned long tstate;
605
606 if (kbuf)
607 reg = *k++;
608 else if (get_user(reg, u++))
609 return -EFAULT;
610
611 switch (pos) {
612 case 32: /* PSR */
613 tstate = regs->tstate;
614 tstate &= ~(TSTATE_ICC | TSTATE_XCC);
615 tstate |= psr_to_tstate_icc(reg);
616 regs->tstate = tstate;
617 break;
618 case 33: /* PC */
619 regs->tpc = reg;
620 break;
621 case 34: /* NPC */
622 regs->tnpc = reg;
623 break;
624 case 35: /* Y */
625 regs->y = reg;
626 break;
627 case 36: /* WIM */
628 case 37: /* TBR */
629 break;
630 default:
631 goto finish;
632 }
633
634 pos++;
635 count--;
636 }
637finish:
638 pos *= sizeof(reg);
639 count *= sizeof(reg);
640
641 return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
642 38 * sizeof(reg), -1);
643}
644
645static int fpregs32_get(struct task_struct *target,
646 const struct user_regset *regset,
647 unsigned int pos, unsigned int count,
648 void *kbuf, void __user *ubuf)
649{
650 const unsigned long *fpregs = task_thread_info(target)->fpregs;
651 compat_ulong_t enabled;
652 unsigned long fprs;
653 compat_ulong_t fsr;
654 int ret = 0;
655
656 if (target == current)
657 save_and_clear_fpu();
658
659 fprs = task_thread_info(target)->fpsaved[0];
660 if (fprs & FPRS_FEF) {
661 fsr = task_thread_info(target)->xfsr[0];
662 enabled = 1;
663 } else {
664 fsr = 0;
665 enabled = 0;
666 }
667
668 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
669 fpregs,
670 0, 32 * sizeof(u32));
671
672 if (!ret)
673 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
674 32 * sizeof(u32),
675 33 * sizeof(u32));
676 if (!ret)
677 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
678 &fsr,
679 33 * sizeof(u32),
680 34 * sizeof(u32));
681
682 if (!ret) {
683 compat_ulong_t val;
684
685 val = (enabled << 8) | (8 << 16);
686 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
687 &val,
688 34 * sizeof(u32),
689 35 * sizeof(u32));
690 }
691
692 if (!ret)
693 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
694 35 * sizeof(u32), -1);
695
696 return ret;
697}
698
699static int fpregs32_set(struct task_struct *target,
700 const struct user_regset *regset,
701 unsigned int pos, unsigned int count,
702 const void *kbuf, const void __user *ubuf)
703{
704 unsigned long *fpregs = task_thread_info(target)->fpregs;
705 unsigned long fprs;
706 int ret;
707
708 if (target == current)
709 save_and_clear_fpu();
710
711 fprs = task_thread_info(target)->fpsaved[0];
712
713 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
714 fpregs,
715 0, 32 * sizeof(u32));
716 if (!ret)
717 user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
718 32 * sizeof(u32),
719 33 * sizeof(u32));
720 if (!ret && count > 0) {
721 compat_ulong_t fsr;
722 unsigned long val;
723
724 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
725 &fsr,
726 33 * sizeof(u32),
727 34 * sizeof(u32));
728 if (!ret) {
729 val = task_thread_info(target)->xfsr[0];
730 val &= 0xffffffff00000000UL;
731 val |= fsr;
732 task_thread_info(target)->xfsr[0] = val;
733 }
734 }
735
736 fprs |= (FPRS_FEF | FPRS_DL);
737 task_thread_info(target)->fpsaved[0] = fprs;
738
739 if (!ret)
740 ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
741 34 * sizeof(u32), -1);
742 return ret;
743}
744
745static const struct user_regset sparc32_regsets[] = {
746 /* Format is:
747 * G0 --> G7
748 * O0 --> O7
749 * L0 --> L7
750 * I0 --> I7
751 * PSR, PC, nPC, Y, WIM, TBR
752 */
753 [REGSET_GENERAL] = {
754 .core_note_type = NT_PRSTATUS,
755 .n = 38 * sizeof(u32),
756 .size = sizeof(u32), .align = sizeof(u32),
757 .get = genregs32_get, .set = genregs32_set
758 },
759 /* Format is:
760 * F0 --> F31
761 * empty 32-bit word
762 * FSR (32--bit word)
763 * FPU QUEUE COUNT (8-bit char)
764 * FPU QUEUE ENTRYSIZE (8-bit char)
765 * FPU ENABLED (8-bit char)
766 * empty 8-bit char
767 * FPU QUEUE (64 32-bit ints)
768 */
769 [REGSET_FP] = {
770 .core_note_type = NT_PRFPREG,
771 .n = 99 * sizeof(u32),
772 .size = sizeof(u32), .align = sizeof(u32),
773 .get = fpregs32_get, .set = fpregs32_set
774 },
775};
776
777static const struct user_regset_view user_sparc32_view = {
778 .name = "sparc", .e_machine = EM_SPARC,
779 .regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets)
780};
David S. Miller11cc8a32008-03-26 04:31:50 -0700781#endif /* CONFIG_COMPAT */
David S. Millerd09c2a22008-02-06 23:02:08 -0800782
783const struct user_regset_view *task_user_regset_view(struct task_struct *task)
784{
David S. Miller11cc8a32008-03-26 04:31:50 -0700785#ifdef CONFIG_COMPAT
David S. Millerd09c2a22008-02-06 23:02:08 -0800786 if (test_tsk_thread_flag(task, TIF_32BIT))
787 return &user_sparc32_view;
David S. Miller11cc8a32008-03-26 04:31:50 -0700788#endif
David S. Millerd09c2a22008-02-06 23:02:08 -0800789 return &user_sparc64_view;
790}
791
David S. Miller11cc8a32008-03-26 04:31:50 -0700792#ifdef CONFIG_COMPAT
David S. Miller2ba85f32008-02-07 22:46:09 -0800793struct compat_fps {
794 unsigned int regs[32];
795 unsigned int fsr;
796 unsigned int flags;
797 unsigned int extra;
798 unsigned int fpqd;
799 struct compat_fq {
800 unsigned int insnaddr;
801 unsigned int insn;
802 } fpq[16];
803};
804
805long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
806 compat_ulong_t caddr, compat_ulong_t cdata)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807{
David S. Miller2ba85f32008-02-07 22:46:09 -0800808 const struct user_regset_view *view = task_user_regset_view(child);
809 compat_ulong_t caddr2 = task_pt_regs(current)->u_regs[UREG_I4];
810 struct pt_regs32 __user *pregs;
811 struct compat_fps __user *fps;
812 unsigned long addr2 = caddr2;
813 unsigned long addr = caddr;
814 unsigned long data = cdata;
David S. Miller94732722008-02-07 05:06:12 -0800815 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816
David S. Miller2ba85f32008-02-07 22:46:09 -0800817 pregs = (struct pt_regs32 __user *) addr;
818 fps = (struct compat_fps __user *) addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819
David S. Miller2ba85f32008-02-07 22:46:09 -0800820 switch (request) {
David S. Miller1759e582006-04-01 23:28:10 -0800821 case PTRACE_PEEKUSR:
David S. Miller97753692008-02-07 03:00:17 -0800822 ret = (addr != 0) ? -EIO : 0;
823 break;
David S. Miller1759e582006-04-01 23:28:10 -0800824
David S. Miller2ba85f32008-02-07 22:46:09 -0800825 case PTRACE_GETREGS:
David S. Miller94732722008-02-07 05:06:12 -0800826 ret = copy_regset_to_user(child, view, REGSET_GENERAL,
827 32 * sizeof(u32),
828 4 * sizeof(u32),
829 &pregs->psr);
830 if (!ret)
831 ret = copy_regset_to_user(child, view, REGSET_GENERAL,
832 1 * sizeof(u32),
833 15 * sizeof(u32),
834 &pregs->u_regs[0]);
David S. Miller97753692008-02-07 03:00:17 -0800835 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836
David S. Miller2ba85f32008-02-07 22:46:09 -0800837 case PTRACE_SETREGS:
David S. Miller94732722008-02-07 05:06:12 -0800838 ret = copy_regset_from_user(child, view, REGSET_GENERAL,
839 32 * sizeof(u32),
840 4 * sizeof(u32),
841 &pregs->psr);
842 if (!ret)
843 ret = copy_regset_from_user(child, view, REGSET_GENERAL,
844 1 * sizeof(u32),
845 15 * sizeof(u32),
846 &pregs->u_regs[0]);
David S. Miller97753692008-02-07 03:00:17 -0800847 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848
David S. Miller2ba85f32008-02-07 22:46:09 -0800849 case PTRACE_GETFPREGS:
David S. Miller94732722008-02-07 05:06:12 -0800850 ret = copy_regset_to_user(child, view, REGSET_FP,
851 0 * sizeof(u32),
852 32 * sizeof(u32),
853 &fps->regs[0]);
854 if (!ret)
855 ret = copy_regset_to_user(child, view, REGSET_FP,
856 33 * sizeof(u32),
857 1 * sizeof(u32),
858 &fps->fsr);
859 if (!ret) {
860 if (__put_user(0, &fps->flags) ||
861 __put_user(0, &fps->extra) ||
862 __put_user(0, &fps->fpqd) ||
863 clear_user(&fps->fpq[0], 32 * sizeof(unsigned int)))
864 ret = -EFAULT;
865 }
David S. Miller97753692008-02-07 03:00:17 -0800866 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867
David S. Miller2ba85f32008-02-07 22:46:09 -0800868 case PTRACE_SETFPREGS:
David S. Miller94732722008-02-07 05:06:12 -0800869 ret = copy_regset_from_user(child, view, REGSET_FP,
870 0 * sizeof(u32),
871 32 * sizeof(u32),
872 &fps->regs[0]);
873 if (!ret)
874 ret = copy_regset_from_user(child, view, REGSET_FP,
875 33 * sizeof(u32),
876 1 * sizeof(u32),
877 &fps->fsr);
David S. Miller97753692008-02-07 03:00:17 -0800878 break;
David S. Miller731bbe42006-04-04 16:54:40 -0700879
David S. Miller97753692008-02-07 03:00:17 -0800880 case PTRACE_READTEXT:
881 case PTRACE_READDATA:
882 ret = ptrace_readdata(child, addr,
883 (char __user *)addr2, data);
884 if (ret == data)
885 ret = 0;
886 else if (ret >= 0)
887 ret = -EIO;
888 break;
889
890 case PTRACE_WRITETEXT:
891 case PTRACE_WRITEDATA:
892 ret = ptrace_writedata(child, (char __user *) addr2,
893 addr, data);
894 if (ret == data)
895 ret = 0;
896 else if (ret >= 0)
897 ret = -EIO;
898 break;
899
David S. Miller2ba85f32008-02-07 22:46:09 -0800900 default:
901 ret = compat_ptrace_request(child, request, addr, data);
David S. Miller97753692008-02-07 03:00:17 -0800902 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903 }
David S. Miller97753692008-02-07 03:00:17 -0800904
David S. Miller2ba85f32008-02-07 22:46:09 -0800905 return ret;
906}
David S. Miller11cc8a32008-03-26 04:31:50 -0700907#endif /* CONFIG_COMPAT */
David S. Miller2ba85f32008-02-07 22:46:09 -0800908
909struct fps {
910 unsigned int regs[64];
911 unsigned long fsr;
912};
913
914long arch_ptrace(struct task_struct *child, long request, long addr, long data)
915{
916 const struct user_regset_view *view = task_user_regset_view(child);
David S. Miller2ba85f32008-02-07 22:46:09 -0800917 unsigned long addr2 = task_pt_regs(current)->u_regs[UREG_I4];
David S. Millerbfdf9eb2008-03-26 00:46:21 -0700918 struct pt_regs __user *pregs;
919 struct fps __user *fps;
David S. Miller2ba85f32008-02-07 22:46:09 -0800920 int ret;
921
David S. Millerbfdf9eb2008-03-26 00:46:21 -0700922 pregs = (struct pt_regs __user *) (unsigned long) addr;
923 fps = (struct fps __user *) (unsigned long) addr;
924
David S. Miller2ba85f32008-02-07 22:46:09 -0800925 switch (request) {
926 case PTRACE_PEEKUSR:
927 ret = (addr != 0) ? -EIO : 0;
928 break;
929
930 case PTRACE_GETREGS64:
931 ret = copy_regset_to_user(child, view, REGSET_GENERAL,
932 1 * sizeof(u64),
933 15 * sizeof(u64),
934 &pregs->u_regs[0]);
935 if (!ret) {
936 /* XXX doesn't handle 'y' register correctly XXX */
937 ret = copy_regset_to_user(child, view, REGSET_GENERAL,
938 32 * sizeof(u64),
939 4 * sizeof(u64),
940 &pregs->tstate);
941 }
942 break;
943
944 case PTRACE_SETREGS64:
945 ret = copy_regset_from_user(child, view, REGSET_GENERAL,
946 1 * sizeof(u64),
947 15 * sizeof(u64),
948 &pregs->u_regs[0]);
949 if (!ret) {
950 /* XXX doesn't handle 'y' register correctly XXX */
951 ret = copy_regset_from_user(child, view, REGSET_GENERAL,
952 32 * sizeof(u64),
953 4 * sizeof(u64),
954 &pregs->tstate);
955 }
956 break;
957
958 case PTRACE_GETFPREGS64:
959 ret = copy_regset_to_user(child, view, REGSET_FP,
960 0 * sizeof(u64),
961 33 * sizeof(u64),
962 fps);
963 break;
964
965 case PTRACE_SETFPREGS64:
966 ret = copy_regset_to_user(child, view, REGSET_FP,
967 0 * sizeof(u64),
968 33 * sizeof(u64),
969 fps);
970 break;
971
972 case PTRACE_READTEXT:
973 case PTRACE_READDATA:
974 ret = ptrace_readdata(child, addr,
975 (char __user *)addr2, data);
976 if (ret == data)
977 ret = 0;
978 else if (ret >= 0)
979 ret = -EIO;
980 break;
981
982 case PTRACE_WRITETEXT:
983 case PTRACE_WRITEDATA:
984 ret = ptrace_writedata(child, (char __user *) addr2,
985 addr, data);
986 if (ret == data)
987 ret = 0;
988 else if (ret >= 0)
989 ret = -EIO;
990 break;
991
David S. Miller97753692008-02-07 03:00:17 -0800992 default:
993 ret = ptrace_request(child, request, addr, data);
994 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995 }
David S. Miller97753692008-02-07 03:00:17 -0800996
997 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998}
999
David S. Miller8d8a6472005-07-10 16:55:48 -07001000asmlinkage void syscall_trace(struct pt_regs *regs, int syscall_exit_p)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001{
David S. Millerbb49bcd2005-07-10 16:49:28 -07001002 /* do the secure computing check first */
David S. Miller8d8a6472005-07-10 16:55:48 -07001003 secure_computing(regs->u_regs[UREG_G1]);
David S. Millerbb49bcd2005-07-10 16:49:28 -07001004
David S. Millerf7ceba32005-07-10 19:29:45 -07001005 if (unlikely(current->audit_context) && syscall_exit_p) {
1006 unsigned long tstate = regs->tstate;
1007 int result = AUDITSC_SUCCESS;
1008
1009 if (unlikely(tstate & (TSTATE_XCARRY | TSTATE_ICARRY)))
1010 result = AUDITSC_FAILURE;
1011
Al Viro5411be52006-03-29 20:23:36 -05001012 audit_syscall_exit(result, regs->u_regs[UREG_I0]);
David S. Millerf7ceba32005-07-10 19:29:45 -07001013 }
1014
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015 if (!(current->ptrace & PT_PTRACED))
David S. Millerf7ceba32005-07-10 19:29:45 -07001016 goto out;
1017
1018 if (!test_thread_flag(TIF_SYSCALL_TRACE))
1019 goto out;
1020
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021 ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
1022 ? 0x80 : 0));
1023
1024 /*
1025 * this isn't the same as continuing with a signal, but it will do
1026 * for normal use. strace only continues with a signal if the
1027 * stopping signal is not SIGTRAP. -brl
1028 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029 if (current->exit_code) {
David S. Millerbb49bcd2005-07-10 16:49:28 -07001030 send_sig(current->exit_code, current, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031 current->exit_code = 0;
1032 }
David S. Millerf7ceba32005-07-10 19:29:45 -07001033
1034out:
1035 if (unlikely(current->audit_context) && !syscall_exit_p)
Al Viro5411be52006-03-29 20:23:36 -05001036 audit_syscall_entry((test_thread_flag(TIF_32BIT) ?
David S. Millerf7ceba32005-07-10 19:29:45 -07001037 AUDIT_ARCH_SPARC :
1038 AUDIT_ARCH_SPARC64),
1039 regs->u_regs[UREG_G1],
1040 regs->u_regs[UREG_I0],
1041 regs->u_regs[UREG_I1],
1042 regs->u_regs[UREG_I2],
1043 regs->u_regs[UREG_I3]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044}