diff options
author | Helge Deller <deller@gmx.de> | 2016-04-01 16:40:53 -0400 |
---|---|---|
committer | Helge Deller <deller@gmx.de> | 2016-05-22 15:39:13 -0400 |
commit | 64e2a42bca12e408f0258c56adcf3595bcd116e7 (patch) | |
tree | e5eebe549a2ad377e5fc91dae35c052a4747689c /arch/parisc | |
parent | d2ad824f4bb8d146ea6e5f440dddb30b9ccfd98c (diff) |
parisc: Add ARCH_TRACEHOOK and regset support
By adding TRACEHOOK support we now get a clean user interface to access
registers via PTRACE_GETREGS, PTRACE_SETREGS, PTRACE_GETFPREGS and
PTRACE_SETFPREGS.
The user-visible regset struct user_regs_struct and user_fp_struct are
modelled similiar to x86 and can be accessed via PTRACE_GETREGSET.
Signed-off-by: Helge Deller <deller@gmx.de>
Diffstat (limited to 'arch/parisc')
-rw-r--r-- | arch/parisc/Kconfig | 1 | ||||
-rw-r--r-- | arch/parisc/include/uapi/asm/ptrace.h | 48 | ||||
-rw-r--r-- | arch/parisc/kernel/ptrace.c | 356 |
3 files changed, 403 insertions, 2 deletions
diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig index 9589511e9c95..6c68c23dd7c2 100644 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig | |||
@@ -32,6 +32,7 @@ config PARISC | |||
32 | select HAVE_DEBUG_STACKOVERFLOW | 32 | select HAVE_DEBUG_STACKOVERFLOW |
33 | select HAVE_ARCH_AUDITSYSCALL | 33 | select HAVE_ARCH_AUDITSYSCALL |
34 | select HAVE_ARCH_SECCOMP_FILTER | 34 | select HAVE_ARCH_SECCOMP_FILTER |
35 | select HAVE_ARCH_TRACEHOOK | ||
35 | select ARCH_NO_COHERENT_DMA_MMAP | 36 | select ARCH_NO_COHERENT_DMA_MMAP |
36 | select CPU_NO_EFFICIENT_FFS | 37 | select CPU_NO_EFFICIENT_FFS |
37 | 38 | ||
diff --git a/arch/parisc/include/uapi/asm/ptrace.h b/arch/parisc/include/uapi/asm/ptrace.h index c4fa6c8b9ad9..02ce2eb99a7f 100644 --- a/arch/parisc/include/uapi/asm/ptrace.h +++ b/arch/parisc/include/uapi/asm/ptrace.h | |||
@@ -13,6 +13,11 @@ | |||
13 | * N.B. gdb/strace care about the size and offsets within this | 13 | * N.B. gdb/strace care about the size and offsets within this |
14 | * structure. If you change things, you may break object compatibility | 14 | * structure. If you change things, you may break object compatibility |
15 | * for those applications. | 15 | * for those applications. |
16 | * | ||
17 | * Please do NOT use this structure for future programs, but use | ||
18 | * user_regs_struct (see below) instead. | ||
19 | * | ||
20 | * It can be accessed through PTRACE_PEEKUSR/PTRACE_POKEUSR only. | ||
16 | */ | 21 | */ |
17 | 22 | ||
18 | struct pt_regs { | 23 | struct pt_regs { |
@@ -33,6 +38,45 @@ struct pt_regs { | |||
33 | unsigned long ipsw; /* CR22 */ | 38 | unsigned long ipsw; /* CR22 */ |
34 | }; | 39 | }; |
35 | 40 | ||
41 | /** | ||
42 | * struct user_regs_struct - User general purpose registers | ||
43 | * | ||
44 | * This is the user-visible general purpose register state structure | ||
45 | * which is used to define the elf_gregset_t. | ||
46 | * | ||
47 | * It can be accessed through PTRACE_GETREGSET with NT_PRSTATUS | ||
48 | * and through PTRACE_GETREGS. | ||
49 | */ | ||
50 | struct user_regs_struct { | ||
51 | unsigned long gr[32]; /* PSW is in gr[0] */ | ||
52 | unsigned long sr[8]; | ||
53 | unsigned long iaoq[2]; | ||
54 | unsigned long iasq[2]; | ||
55 | unsigned long sar; /* CR11 */ | ||
56 | unsigned long iir; /* CR19 */ | ||
57 | unsigned long isr; /* CR20 */ | ||
58 | unsigned long ior; /* CR21 */ | ||
59 | unsigned long ipsw; /* CR22 */ | ||
60 | unsigned long cr0; | ||
61 | unsigned long cr24, cr25, cr26, cr27, cr28, cr29, cr30, cr31; | ||
62 | unsigned long cr8, cr9, cr12, cr13, cr10, cr15; | ||
63 | unsigned long _pad[80-64]; /* pad to ELF_NGREG (80) */ | ||
64 | }; | ||
65 | |||
66 | /** | ||
67 | * struct user_fp_struct - User floating point registers | ||
68 | * | ||
69 | * This is the user-visible floating point register state structure. | ||
70 | * It uses the same layout and size as elf_fpregset_t. | ||
71 | * | ||
72 | * It can be accessed through PTRACE_GETREGSET with NT_PRFPREG | ||
73 | * and through PTRACE_GETFPREGS. | ||
74 | */ | ||
75 | struct user_fp_struct { | ||
76 | __u64 fr[32]; | ||
77 | }; | ||
78 | |||
79 | |||
36 | /* | 80 | /* |
37 | * The numbers chosen here are somewhat arbitrary but absolutely MUST | 81 | * The numbers chosen here are somewhat arbitrary but absolutely MUST |
38 | * not overlap with any of the number assigned in <linux/ptrace.h>. | 82 | * not overlap with any of the number assigned in <linux/ptrace.h>. |
@@ -43,5 +87,9 @@ struct pt_regs { | |||
43 | */ | 87 | */ |
44 | #define PTRACE_SINGLEBLOCK 12 /* resume execution until next branch */ | 88 | #define PTRACE_SINGLEBLOCK 12 /* resume execution until next branch */ |
45 | 89 | ||
90 | #define PTRACE_GETREGS 18 | ||
91 | #define PTRACE_SETREGS 19 | ||
92 | #define PTRACE_GETFPREGS 14 | ||
93 | #define PTRACE_SETFPREGS 15 | ||
46 | 94 | ||
47 | #endif /* _UAPI_PARISC_PTRACE_H */ | 95 | #endif /* _UAPI_PARISC_PTRACE_H */ |
diff --git a/arch/parisc/kernel/ptrace.c b/arch/parisc/kernel/ptrace.c index 4863761bdbbb..b5458b37fc5b 100644 --- a/arch/parisc/kernel/ptrace.c +++ b/arch/parisc/kernel/ptrace.c | |||
@@ -4,18 +4,20 @@ | |||
4 | * Copyright (C) 2000 Hewlett-Packard Co, Linuxcare Inc. | 4 | * Copyright (C) 2000 Hewlett-Packard Co, Linuxcare Inc. |
5 | * Copyright (C) 2000 Matthew Wilcox <matthew@wil.cx> | 5 | * Copyright (C) 2000 Matthew Wilcox <matthew@wil.cx> |
6 | * Copyright (C) 2000 David Huggins-Daines <dhd@debian.org> | 6 | * Copyright (C) 2000 David Huggins-Daines <dhd@debian.org> |
7 | * Copyright (C) 2008 Helge Deller <deller@gmx.de> | 7 | * Copyright (C) 2008-2016 Helge Deller <deller@gmx.de> |
8 | */ | 8 | */ |
9 | 9 | ||
10 | #include <linux/kernel.h> | 10 | #include <linux/kernel.h> |
11 | #include <linux/sched.h> | 11 | #include <linux/sched.h> |
12 | #include <linux/mm.h> | 12 | #include <linux/mm.h> |
13 | #include <linux/smp.h> | 13 | #include <linux/smp.h> |
14 | #include <linux/elf.h> | ||
14 | #include <linux/errno.h> | 15 | #include <linux/errno.h> |
15 | #include <linux/ptrace.h> | 16 | #include <linux/ptrace.h> |
16 | #include <linux/tracehook.h> | 17 | #include <linux/tracehook.h> |
17 | #include <linux/user.h> | 18 | #include <linux/user.h> |
18 | #include <linux/personality.h> | 19 | #include <linux/personality.h> |
20 | #include <linux/regset.h> | ||
19 | #include <linux/security.h> | 21 | #include <linux/security.h> |
20 | #include <linux/seccomp.h> | 22 | #include <linux/seccomp.h> |
21 | #include <linux/compat.h> | 23 | #include <linux/compat.h> |
@@ -34,6 +36,14 @@ | |||
34 | #include <trace/events/syscalls.h> | 36 | #include <trace/events/syscalls.h> |
35 | 37 | ||
36 | /* | 38 | /* |
39 | * These are our native regset flavors. | ||
40 | */ | ||
41 | enum parisc_regset { | ||
42 | REGSET_GENERAL, | ||
43 | REGSET_FP | ||
44 | }; | ||
45 | |||
46 | /* | ||
37 | * Called by kernel/ptrace.c when detaching.. | 47 | * Called by kernel/ptrace.c when detaching.. |
38 | * | 48 | * |
39 | * Make sure single step bits etc are not set. | 49 | * Make sure single step bits etc are not set. |
@@ -117,6 +127,7 @@ void user_enable_block_step(struct task_struct *task) | |||
117 | long arch_ptrace(struct task_struct *child, long request, | 127 | long arch_ptrace(struct task_struct *child, long request, |
118 | unsigned long addr, unsigned long data) | 128 | unsigned long addr, unsigned long data) |
119 | { | 129 | { |
130 | unsigned long __user *datap = (unsigned long __user *)data; | ||
120 | unsigned long tmp; | 131 | unsigned long tmp; |
121 | long ret = -EIO; | 132 | long ret = -EIO; |
122 | 133 | ||
@@ -129,7 +140,7 @@ long arch_ptrace(struct task_struct *child, long request, | |||
129 | addr >= sizeof(struct pt_regs)) | 140 | addr >= sizeof(struct pt_regs)) |
130 | break; | 141 | break; |
131 | tmp = *(unsigned long *) ((char *) task_regs(child) + addr); | 142 | tmp = *(unsigned long *) ((char *) task_regs(child) + addr); |
132 | ret = put_user(tmp, (unsigned long __user *) data); | 143 | ret = put_user(tmp, datap); |
133 | break; | 144 | break; |
134 | 145 | ||
135 | /* Write the word at location addr in the USER area. This will need | 146 | /* Write the word at location addr in the USER area. This will need |
@@ -168,6 +179,34 @@ long arch_ptrace(struct task_struct *child, long request, | |||
168 | } | 179 | } |
169 | break; | 180 | break; |
170 | 181 | ||
182 | case PTRACE_GETREGS: /* Get all gp regs from the child. */ | ||
183 | return copy_regset_to_user(child, | ||
184 | task_user_regset_view(current), | ||
185 | REGSET_GENERAL, | ||
186 | 0, sizeof(struct user_regs_struct), | ||
187 | datap); | ||
188 | |||
189 | case PTRACE_SETREGS: /* Set all gp regs in the child. */ | ||
190 | return copy_regset_from_user(child, | ||
191 | task_user_regset_view(current), | ||
192 | REGSET_GENERAL, | ||
193 | 0, sizeof(struct user_regs_struct), | ||
194 | datap); | ||
195 | |||
196 | case PTRACE_GETFPREGS: /* Get the child FPU state. */ | ||
197 | return copy_regset_to_user(child, | ||
198 | task_user_regset_view(current), | ||
199 | REGSET_FP, | ||
200 | 0, sizeof(struct user_fp_struct), | ||
201 | datap); | ||
202 | |||
203 | case PTRACE_SETFPREGS: /* Set the child FPU state. */ | ||
204 | return copy_regset_from_user(child, | ||
205 | task_user_regset_view(current), | ||
206 | REGSET_FP, | ||
207 | 0, sizeof(struct user_fp_struct), | ||
208 | datap); | ||
209 | |||
171 | default: | 210 | default: |
172 | ret = ptrace_request(child, request, addr, data); | 211 | ret = ptrace_request(child, request, addr, data); |
173 | break; | 212 | break; |
@@ -326,3 +365,316 @@ void do_syscall_trace_exit(struct pt_regs *regs) | |||
326 | if (stepping || test_thread_flag(TIF_SYSCALL_TRACE)) | 365 | if (stepping || test_thread_flag(TIF_SYSCALL_TRACE)) |
327 | tracehook_report_syscall_exit(regs, stepping); | 366 | tracehook_report_syscall_exit(regs, stepping); |
328 | } | 367 | } |
368 | |||
369 | |||
370 | /* | ||
371 | * regset functions. | ||
372 | */ | ||
373 | |||
374 | static int fpr_get(struct task_struct *target, | ||
375 | const struct user_regset *regset, | ||
376 | unsigned int pos, unsigned int count, | ||
377 | void *kbuf, void __user *ubuf) | ||
378 | { | ||
379 | struct pt_regs *regs = task_regs(target); | ||
380 | __u64 *k = kbuf; | ||
381 | __u64 __user *u = ubuf; | ||
382 | __u64 reg; | ||
383 | |||
384 | pos /= sizeof(reg); | ||
385 | count /= sizeof(reg); | ||
386 | |||
387 | if (kbuf) | ||
388 | for (; count > 0 && pos < ELF_NFPREG; --count) | ||
389 | *k++ = regs->fr[pos++]; | ||
390 | else | ||
391 | for (; count > 0 && pos < ELF_NFPREG; --count) | ||
392 | if (__put_user(regs->fr[pos++], u++)) | ||
393 | return -EFAULT; | ||
394 | |||
395 | kbuf = k; | ||
396 | ubuf = u; | ||
397 | pos *= sizeof(reg); | ||
398 | count *= sizeof(reg); | ||
399 | return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | ||
400 | ELF_NFPREG * sizeof(reg), -1); | ||
401 | } | ||
402 | |||
403 | static int fpr_set(struct task_struct *target, | ||
404 | const struct user_regset *regset, | ||
405 | unsigned int pos, unsigned int count, | ||
406 | const void *kbuf, const void __user *ubuf) | ||
407 | { | ||
408 | struct pt_regs *regs = task_regs(target); | ||
409 | const __u64 *k = kbuf; | ||
410 | const __u64 __user *u = ubuf; | ||
411 | __u64 reg; | ||
412 | |||
413 | pos /= sizeof(reg); | ||
414 | count /= sizeof(reg); | ||
415 | |||
416 | if (kbuf) | ||
417 | for (; count > 0 && pos < ELF_NFPREG; --count) | ||
418 | regs->fr[pos++] = *k++; | ||
419 | else | ||
420 | for (; count > 0 && pos < ELF_NFPREG; --count) { | ||
421 | if (__get_user(reg, u++)) | ||
422 | return -EFAULT; | ||
423 | regs->fr[pos++] = reg; | ||
424 | } | ||
425 | |||
426 | kbuf = k; | ||
427 | ubuf = u; | ||
428 | pos *= sizeof(reg); | ||
429 | count *= sizeof(reg); | ||
430 | return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | ||
431 | ELF_NFPREG * sizeof(reg), -1); | ||
432 | } | ||
433 | |||
434 | #define RI(reg) (offsetof(struct user_regs_struct,reg) / sizeof(long)) | ||
435 | |||
436 | static unsigned long get_reg(struct pt_regs *regs, int num) | ||
437 | { | ||
438 | switch (num) { | ||
439 | case RI(gr[0]) ... RI(gr[31]): return regs->gr[num - RI(gr[0])]; | ||
440 | case RI(sr[0]) ... RI(sr[7]): return regs->sr[num - RI(sr[0])]; | ||
441 | case RI(iasq[0]): return regs->iasq[0]; | ||
442 | case RI(iasq[1]): return regs->iasq[1]; | ||
443 | case RI(iaoq[0]): return regs->iaoq[0]; | ||
444 | case RI(iaoq[1]): return regs->iaoq[1]; | ||
445 | case RI(sar): return regs->sar; | ||
446 | case RI(iir): return regs->iir; | ||
447 | case RI(isr): return regs->isr; | ||
448 | case RI(ior): return regs->ior; | ||
449 | case RI(ipsw): return regs->ipsw; | ||
450 | case RI(cr27): return regs->cr27; | ||
451 | case RI(cr0): return mfctl(0); | ||
452 | case RI(cr24): return mfctl(24); | ||
453 | case RI(cr25): return mfctl(25); | ||
454 | case RI(cr26): return mfctl(26); | ||
455 | case RI(cr28): return mfctl(28); | ||
456 | case RI(cr29): return mfctl(29); | ||
457 | case RI(cr30): return mfctl(30); | ||
458 | case RI(cr31): return mfctl(31); | ||
459 | case RI(cr8): return mfctl(8); | ||
460 | case RI(cr9): return mfctl(9); | ||
461 | case RI(cr12): return mfctl(12); | ||
462 | case RI(cr13): return mfctl(13); | ||
463 | case RI(cr10): return mfctl(10); | ||
464 | case RI(cr15): return mfctl(15); | ||
465 | default: return 0; | ||
466 | } | ||
467 | } | ||
468 | |||
469 | static void set_reg(struct pt_regs *regs, int num, unsigned long val) | ||
470 | { | ||
471 | switch (num) { | ||
472 | case RI(gr[0]): /* | ||
473 | * PSW is in gr[0]. | ||
474 | * Allow writing to Nullify, Divide-step-correction, | ||
475 | * and carry/borrow bits. | ||
476 | * BEWARE, if you set N, and then single step, it won't | ||
477 | * stop on the nullified instruction. | ||
478 | */ | ||
479 | val &= USER_PSW_BITS; | ||
480 | regs->gr[0] &= ~USER_PSW_BITS; | ||
481 | regs->gr[0] |= val; | ||
482 | return; | ||
483 | case RI(gr[1]) ... RI(gr[31]): | ||
484 | regs->gr[num - RI(gr[0])] = val; | ||
485 | return; | ||
486 | case RI(iaoq[0]): | ||
487 | case RI(iaoq[1]): | ||
488 | regs->iaoq[num - RI(iaoq[0])] = val; | ||
489 | return; | ||
490 | case RI(sar): regs->sar = val; | ||
491 | return; | ||
492 | default: return; | ||
493 | #if 0 | ||
494 | /* do not allow to change any of the following registers (yet) */ | ||
495 | case RI(sr[0]) ... RI(sr[7]): return regs->sr[num - RI(sr[0])]; | ||
496 | case RI(iasq[0]): return regs->iasq[0]; | ||
497 | case RI(iasq[1]): return regs->iasq[1]; | ||
498 | case RI(iir): return regs->iir; | ||
499 | case RI(isr): return regs->isr; | ||
500 | case RI(ior): return regs->ior; | ||
501 | case RI(ipsw): return regs->ipsw; | ||
502 | case RI(cr27): return regs->cr27; | ||
503 | case cr0, cr24, cr25, cr26, cr27, cr28, cr29, cr30, cr31; | ||
504 | case cr8, cr9, cr12, cr13, cr10, cr15; | ||
505 | #endif | ||
506 | } | ||
507 | } | ||
508 | |||
509 | static int gpr_get(struct task_struct *target, | ||
510 | const struct user_regset *regset, | ||
511 | unsigned int pos, unsigned int count, | ||
512 | void *kbuf, void __user *ubuf) | ||
513 | { | ||
514 | struct pt_regs *regs = task_regs(target); | ||
515 | unsigned long *k = kbuf; | ||
516 | unsigned long __user *u = ubuf; | ||
517 | unsigned long reg; | ||
518 | |||
519 | pos /= sizeof(reg); | ||
520 | count /= sizeof(reg); | ||
521 | |||
522 | if (kbuf) | ||
523 | for (; count > 0 && pos < ELF_NGREG; --count) | ||
524 | *k++ = get_reg(regs, pos++); | ||
525 | else | ||
526 | for (; count > 0 && pos < ELF_NGREG; --count) | ||
527 | if (__put_user(get_reg(regs, pos++), u++)) | ||
528 | return -EFAULT; | ||
529 | kbuf = k; | ||
530 | ubuf = u; | ||
531 | pos *= sizeof(reg); | ||
532 | count *= sizeof(reg); | ||
533 | return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | ||
534 | ELF_NGREG * sizeof(reg), -1); | ||
535 | } | ||
536 | |||
537 | static int gpr_set(struct task_struct *target, | ||
538 | const struct user_regset *regset, | ||
539 | unsigned int pos, unsigned int count, | ||
540 | const void *kbuf, const void __user *ubuf) | ||
541 | { | ||
542 | struct pt_regs *regs = task_regs(target); | ||
543 | const unsigned long *k = kbuf; | ||
544 | const unsigned long __user *u = ubuf; | ||
545 | unsigned long reg; | ||
546 | |||
547 | pos /= sizeof(reg); | ||
548 | count /= sizeof(reg); | ||
549 | |||
550 | if (kbuf) | ||
551 | for (; count > 0 && pos < ELF_NGREG; --count) | ||
552 | set_reg(regs, pos++, *k++); | ||
553 | else | ||
554 | for (; count > 0 && pos < ELF_NGREG; --count) { | ||
555 | if (__get_user(reg, u++)) | ||
556 | return -EFAULT; | ||
557 | set_reg(regs, pos++, reg); | ||
558 | } | ||
559 | |||
560 | kbuf = k; | ||
561 | ubuf = u; | ||
562 | pos *= sizeof(reg); | ||
563 | count *= sizeof(reg); | ||
564 | return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | ||
565 | ELF_NGREG * sizeof(reg), -1); | ||
566 | } | ||
567 | |||
568 | static const struct user_regset native_regsets[] = { | ||
569 | [REGSET_GENERAL] = { | ||
570 | .core_note_type = NT_PRSTATUS, .n = ELF_NGREG, | ||
571 | .size = sizeof(long), .align = sizeof(long), | ||
572 | .get = gpr_get, .set = gpr_set | ||
573 | }, | ||
574 | [REGSET_FP] = { | ||
575 | .core_note_type = NT_PRFPREG, .n = ELF_NFPREG, | ||
576 | .size = sizeof(__u64), .align = sizeof(__u64), | ||
577 | .get = fpr_get, .set = fpr_set | ||
578 | } | ||
579 | }; | ||
580 | |||
581 | static const struct user_regset_view user_parisc_native_view = { | ||
582 | .name = "parisc", .e_machine = ELF_ARCH, .ei_osabi = ELFOSABI_LINUX, | ||
583 | .regsets = native_regsets, .n = ARRAY_SIZE(native_regsets) | ||
584 | }; | ||
585 | |||
586 | #ifdef CONFIG_64BIT | ||
587 | #include <linux/compat.h> | ||
588 | |||
589 | static int gpr32_get(struct task_struct *target, | ||
590 | const struct user_regset *regset, | ||
591 | unsigned int pos, unsigned int count, | ||
592 | void *kbuf, void __user *ubuf) | ||
593 | { | ||
594 | struct pt_regs *regs = task_regs(target); | ||
595 | compat_ulong_t *k = kbuf; | ||
596 | compat_ulong_t __user *u = ubuf; | ||
597 | compat_ulong_t reg; | ||
598 | |||
599 | pos /= sizeof(reg); | ||
600 | count /= sizeof(reg); | ||
601 | |||
602 | if (kbuf) | ||
603 | for (; count > 0 && pos < ELF_NGREG; --count) | ||
604 | *k++ = get_reg(regs, pos++); | ||
605 | else | ||
606 | for (; count > 0 && pos < ELF_NGREG; --count) | ||
607 | if (__put_user((compat_ulong_t) get_reg(regs, pos++), u++)) | ||
608 | return -EFAULT; | ||
609 | |||
610 | kbuf = k; | ||
611 | ubuf = u; | ||
612 | pos *= sizeof(reg); | ||
613 | count *= sizeof(reg); | ||
614 | return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | ||
615 | ELF_NGREG * sizeof(reg), -1); | ||
616 | } | ||
617 | |||
618 | static int gpr32_set(struct task_struct *target, | ||
619 | const struct user_regset *regset, | ||
620 | unsigned int pos, unsigned int count, | ||
621 | const void *kbuf, const void __user *ubuf) | ||
622 | { | ||
623 | struct pt_regs *regs = task_regs(target); | ||
624 | const compat_ulong_t *k = kbuf; | ||
625 | const compat_ulong_t __user *u = ubuf; | ||
626 | compat_ulong_t reg; | ||
627 | |||
628 | pos /= sizeof(reg); | ||
629 | count /= sizeof(reg); | ||
630 | |||
631 | if (kbuf) | ||
632 | for (; count > 0 && pos < ELF_NGREG; --count) | ||
633 | set_reg(regs, pos++, *k++); | ||
634 | else | ||
635 | for (; count > 0 && pos < ELF_NGREG; --count) { | ||
636 | if (__get_user(reg, u++)) | ||
637 | return -EFAULT; | ||
638 | set_reg(regs, pos++, reg); | ||
639 | } | ||
640 | |||
641 | kbuf = k; | ||
642 | ubuf = u; | ||
643 | pos *= sizeof(reg); | ||
644 | count *= sizeof(reg); | ||
645 | return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | ||
646 | ELF_NGREG * sizeof(reg), -1); | ||
647 | } | ||
648 | |||
649 | /* | ||
650 | * These are the regset flavors matching the 32bit native set. | ||
651 | */ | ||
652 | static const struct user_regset compat_regsets[] = { | ||
653 | [REGSET_GENERAL] = { | ||
654 | .core_note_type = NT_PRSTATUS, .n = ELF_NGREG, | ||
655 | .size = sizeof(compat_long_t), .align = sizeof(compat_long_t), | ||
656 | .get = gpr32_get, .set = gpr32_set | ||
657 | }, | ||
658 | [REGSET_FP] = { | ||
659 | .core_note_type = NT_PRFPREG, .n = ELF_NFPREG, | ||
660 | .size = sizeof(__u64), .align = sizeof(__u64), | ||
661 | .get = fpr_get, .set = fpr_set | ||
662 | } | ||
663 | }; | ||
664 | |||
665 | static const struct user_regset_view user_parisc_compat_view = { | ||
666 | .name = "parisc", .e_machine = EM_PARISC, .ei_osabi = ELFOSABI_LINUX, | ||
667 | .regsets = compat_regsets, .n = ARRAY_SIZE(compat_regsets) | ||
668 | }; | ||
669 | #endif /* CONFIG_64BIT */ | ||
670 | |||
671 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) | ||
672 | { | ||
673 | BUILD_BUG_ON(sizeof(struct user_regs_struct)/sizeof(long) != ELF_NGREG); | ||
674 | BUILD_BUG_ON(sizeof(struct user_fp_struct)/sizeof(__u64) != ELF_NFPREG); | ||
675 | #ifdef CONFIG_64BIT | ||
676 | if (is_compat_task()) | ||
677 | return &user_parisc_compat_view; | ||
678 | #endif | ||
679 | return &user_parisc_native_view; | ||
680 | } | ||