diff options
Diffstat (limited to 'arch/sparc64/kernel/ptrace.c')
-rw-r--r-- | arch/sparc64/kernel/ptrace.c | 222 |
1 files changed, 189 insertions, 33 deletions
diff --git a/arch/sparc64/kernel/ptrace.c b/arch/sparc64/kernel/ptrace.c index 9a1ba1fe859d..e9fc0aa2da38 100644 --- a/arch/sparc64/kernel/ptrace.c +++ b/arch/sparc64/kernel/ptrace.c | |||
@@ -35,6 +35,9 @@ | |||
35 | #include <asm/spitfire.h> | 35 | #include <asm/spitfire.h> |
36 | #include <asm/page.h> | 36 | #include <asm/page.h> |
37 | #include <asm/cpudata.h> | 37 | #include <asm/cpudata.h> |
38 | #include <asm/cacheflush.h> | ||
39 | |||
40 | #include "entry.h" | ||
38 | 41 | ||
39 | /* #define ALLOW_INIT_TRACING */ | 42 | /* #define ALLOW_INIT_TRACING */ |
40 | 43 | ||
@@ -67,6 +70,8 @@ void flush_ptrace_access(struct vm_area_struct *vma, struct page *page, | |||
67 | if (tlb_type == hypervisor) | 70 | if (tlb_type == hypervisor) |
68 | return; | 71 | return; |
69 | 72 | ||
73 | preempt_disable(); | ||
74 | |||
70 | #ifdef DCACHE_ALIASING_POSSIBLE | 75 | #ifdef DCACHE_ALIASING_POSSIBLE |
71 | /* If bit 13 of the kernel address we used to access the | 76 | /* If bit 13 of the kernel address we used to access the |
72 | * user page is the same as the virtual address that page | 77 | * user page is the same as the virtual address that page |
@@ -105,6 +110,87 @@ void flush_ptrace_access(struct vm_area_struct *vma, struct page *page, | |||
105 | for (; start < end; start += icache_line_size) | 110 | for (; start < end; start += icache_line_size) |
106 | flushi(start); | 111 | flushi(start); |
107 | } | 112 | } |
113 | |||
114 | preempt_enable(); | ||
115 | } | ||
116 | |||
117 | static int get_from_target(struct task_struct *target, unsigned long uaddr, | ||
118 | void *kbuf, int len) | ||
119 | { | ||
120 | if (target == current) { | ||
121 | if (copy_from_user(kbuf, (void __user *) uaddr, len)) | ||
122 | return -EFAULT; | ||
123 | } else { | ||
124 | int len2 = access_process_vm(target, uaddr, kbuf, len, 0); | ||
125 | if (len2 != len) | ||
126 | return -EFAULT; | ||
127 | } | ||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | static int set_to_target(struct task_struct *target, unsigned long uaddr, | ||
132 | void *kbuf, int len) | ||
133 | { | ||
134 | if (target == current) { | ||
135 | if (copy_to_user((void __user *) uaddr, kbuf, len)) | ||
136 | return -EFAULT; | ||
137 | } else { | ||
138 | int len2 = access_process_vm(target, uaddr, kbuf, len, 1); | ||
139 | if (len2 != len) | ||
140 | return -EFAULT; | ||
141 | } | ||
142 | return 0; | ||
143 | } | ||
144 | |||
145 | static int regwindow64_get(struct task_struct *target, | ||
146 | const struct pt_regs *regs, | ||
147 | struct reg_window *wbuf) | ||
148 | { | ||
149 | unsigned long rw_addr = regs->u_regs[UREG_I6]; | ||
150 | |||
151 | if (test_tsk_thread_flag(current, TIF_32BIT)) { | ||
152 | struct reg_window32 win32; | ||
153 | int i; | ||
154 | |||
155 | if (get_from_target(target, rw_addr, &win32, sizeof(win32))) | ||
156 | return -EFAULT; | ||
157 | for (i = 0; i < 8; i++) | ||
158 | wbuf->locals[i] = win32.locals[i]; | ||
159 | for (i = 0; i < 8; i++) | ||
160 | wbuf->ins[i] = win32.ins[i]; | ||
161 | } else { | ||
162 | rw_addr += STACK_BIAS; | ||
163 | if (get_from_target(target, rw_addr, wbuf, sizeof(*wbuf))) | ||
164 | return -EFAULT; | ||
165 | } | ||
166 | |||
167 | return 0; | ||
168 | } | ||
169 | |||
170 | static int regwindow64_set(struct task_struct *target, | ||
171 | const struct pt_regs *regs, | ||
172 | struct reg_window *wbuf) | ||
173 | { | ||
174 | unsigned long rw_addr = regs->u_regs[UREG_I6]; | ||
175 | |||
176 | if (test_tsk_thread_flag(current, TIF_32BIT)) { | ||
177 | struct reg_window32 win32; | ||
178 | int i; | ||
179 | |||
180 | for (i = 0; i < 8; i++) | ||
181 | win32.locals[i] = wbuf->locals[i]; | ||
182 | for (i = 0; i < 8; i++) | ||
183 | win32.ins[i] = wbuf->ins[i]; | ||
184 | |||
185 | if (set_to_target(target, rw_addr, &win32, sizeof(win32))) | ||
186 | return -EFAULT; | ||
187 | } else { | ||
188 | rw_addr += STACK_BIAS; | ||
189 | if (set_to_target(target, rw_addr, wbuf, sizeof(*wbuf))) | ||
190 | return -EFAULT; | ||
191 | } | ||
192 | |||
193 | return 0; | ||
108 | } | 194 | } |
109 | 195 | ||
110 | enum sparc_regset { | 196 | enum sparc_regset { |
@@ -126,16 +212,13 @@ static int genregs64_get(struct task_struct *target, | |||
126 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | 212 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, |
127 | regs->u_regs, | 213 | regs->u_regs, |
128 | 0, 16 * sizeof(u64)); | 214 | 0, 16 * sizeof(u64)); |
129 | if (!ret) { | 215 | if (!ret && count && pos < (32 * sizeof(u64))) { |
130 | unsigned long __user *reg_window = (unsigned long __user *) | 216 | struct reg_window window; |
131 | (regs->u_regs[UREG_I6] + STACK_BIAS); | ||
132 | unsigned long window[16]; | ||
133 | 217 | ||
134 | if (copy_from_user(window, reg_window, sizeof(window))) | 218 | if (regwindow64_get(target, regs, &window)) |
135 | return -EFAULT; | 219 | return -EFAULT; |
136 | |||
137 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | 220 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, |
138 | window, | 221 | &window, |
139 | 16 * sizeof(u64), | 222 | 16 * sizeof(u64), |
140 | 32 * sizeof(u64)); | 223 | 32 * sizeof(u64)); |
141 | } | 224 | } |
@@ -157,10 +240,11 @@ static int genregs64_get(struct task_struct *target, | |||
157 | 36 * sizeof(u64)); | 240 | 36 * sizeof(u64)); |
158 | } | 241 | } |
159 | 242 | ||
160 | if (!ret) | 243 | if (!ret) { |
161 | ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | 244 | ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, |
162 | 36 * sizeof(u64), -1); | 245 | 36 * sizeof(u64), -1); |
163 | 246 | ||
247 | } | ||
164 | return ret; | 248 | return ret; |
165 | } | 249 | } |
166 | 250 | ||
@@ -178,20 +262,19 @@ static int genregs64_set(struct task_struct *target, | |||
178 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | 262 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
179 | regs->u_regs, | 263 | regs->u_regs, |
180 | 0, 16 * sizeof(u64)); | 264 | 0, 16 * sizeof(u64)); |
181 | if (!ret && count > 0) { | 265 | if (!ret && count && pos < (32 * sizeof(u64))) { |
182 | unsigned long __user *reg_window = (unsigned long __user *) | 266 | struct reg_window window; |
183 | (regs->u_regs[UREG_I6] + STACK_BIAS); | ||
184 | unsigned long window[16]; | ||
185 | 267 | ||
186 | if (copy_from_user(window, reg_window, sizeof(window))) | 268 | if (regwindow64_get(target, regs, &window)) |
187 | return -EFAULT; | 269 | return -EFAULT; |
188 | 270 | ||
189 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | 271 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
190 | window, | 272 | &window, |
191 | 16 * sizeof(u64), | 273 | 16 * sizeof(u64), |
192 | 32 * sizeof(u64)); | 274 | 32 * sizeof(u64)); |
275 | |||
193 | if (!ret && | 276 | if (!ret && |
194 | copy_to_user(reg_window, window, sizeof(window))) | 277 | regwindow64_set(target, regs, &window)) |
195 | return -EFAULT; | 278 | return -EFAULT; |
196 | } | 279 | } |
197 | 280 | ||
@@ -382,6 +465,7 @@ static const struct user_regset_view user_sparc64_view = { | |||
382 | .regsets = sparc64_regsets, .n = ARRAY_SIZE(sparc64_regsets) | 465 | .regsets = sparc64_regsets, .n = ARRAY_SIZE(sparc64_regsets) |
383 | }; | 466 | }; |
384 | 467 | ||
468 | #ifdef CONFIG_COMPAT | ||
385 | static int genregs32_get(struct task_struct *target, | 469 | static int genregs32_get(struct task_struct *target, |
386 | const struct user_regset *regset, | 470 | const struct user_regset *regset, |
387 | unsigned int pos, unsigned int count, | 471 | unsigned int pos, unsigned int count, |
@@ -404,9 +488,22 @@ static int genregs32_get(struct task_struct *target, | |||
404 | *k++ = regs->u_regs[pos++]; | 488 | *k++ = regs->u_regs[pos++]; |
405 | 489 | ||
406 | reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6]; | 490 | reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6]; |
407 | for (; count > 0 && pos < 32; count--) { | 491 | if (target == current) { |
408 | if (get_user(*k++, ®_window[pos++])) | 492 | for (; count > 0 && pos < 32; count--) { |
409 | return -EFAULT; | 493 | if (get_user(*k++, ®_window[pos++])) |
494 | return -EFAULT; | ||
495 | } | ||
496 | } else { | ||
497 | for (; count > 0 && pos < 32; count--) { | ||
498 | if (access_process_vm(target, | ||
499 | (unsigned long) | ||
500 | ®_window[pos], | ||
501 | k, sizeof(*k), 0) | ||
502 | != sizeof(*k)) | ||
503 | return -EFAULT; | ||
504 | k++; | ||
505 | pos++; | ||
506 | } | ||
410 | } | 507 | } |
411 | } else { | 508 | } else { |
412 | for (; count > 0 && pos < 16; count--) { | 509 | for (; count > 0 && pos < 16; count--) { |
@@ -415,10 +512,28 @@ static int genregs32_get(struct task_struct *target, | |||
415 | } | 512 | } |
416 | 513 | ||
417 | reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6]; | 514 | reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6]; |
418 | for (; count > 0 && pos < 32; count--) { | 515 | if (target == current) { |
419 | if (get_user(reg, ®_window[pos++]) || | 516 | for (; count > 0 && pos < 32; count--) { |
420 | put_user(reg, u++)) | 517 | if (get_user(reg, ®_window[pos++]) || |
421 | return -EFAULT; | 518 | put_user(reg, u++)) |
519 | return -EFAULT; | ||
520 | } | ||
521 | } else { | ||
522 | for (; count > 0 && pos < 32; count--) { | ||
523 | if (access_process_vm(target, | ||
524 | (unsigned long) | ||
525 | ®_window[pos], | ||
526 | ®, sizeof(reg), 0) | ||
527 | != sizeof(reg)) | ||
528 | return -EFAULT; | ||
529 | if (access_process_vm(target, | ||
530 | (unsigned long) u, | ||
531 | ®, sizeof(reg), 1) | ||
532 | != sizeof(reg)) | ||
533 | return -EFAULT; | ||
534 | pos++; | ||
535 | u++; | ||
536 | } | ||
422 | } | 537 | } |
423 | } | 538 | } |
424 | while (count > 0) { | 539 | while (count > 0) { |
@@ -480,9 +595,23 @@ static int genregs32_set(struct task_struct *target, | |||
480 | regs->u_regs[pos++] = *k++; | 595 | regs->u_regs[pos++] = *k++; |
481 | 596 | ||
482 | reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6]; | 597 | reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6]; |
483 | for (; count > 0 && pos < 32; count--) { | 598 | if (target == current) { |
484 | if (put_user(*k++, ®_window[pos++])) | 599 | for (; count > 0 && pos < 32; count--) { |
485 | return -EFAULT; | 600 | if (put_user(*k++, ®_window[pos++])) |
601 | return -EFAULT; | ||
602 | } | ||
603 | } else { | ||
604 | for (; count > 0 && pos < 32; count--) { | ||
605 | if (access_process_vm(target, | ||
606 | (unsigned long) | ||
607 | ®_window[pos], | ||
608 | (void *) k, | ||
609 | sizeof(*k), 1) | ||
610 | != sizeof(*k)) | ||
611 | return -EFAULT; | ||
612 | k++; | ||
613 | pos++; | ||
614 | } | ||
486 | } | 615 | } |
487 | } else { | 616 | } else { |
488 | for (; count > 0 && pos < 16; count--) { | 617 | for (; count > 0 && pos < 16; count--) { |
@@ -492,10 +621,29 @@ static int genregs32_set(struct task_struct *target, | |||
492 | } | 621 | } |
493 | 622 | ||
494 | reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6]; | 623 | reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6]; |
495 | for (; count > 0 && pos < 32; count--) { | 624 | if (target == current) { |
496 | if (get_user(reg, u++) || | 625 | for (; count > 0 && pos < 32; count--) { |
497 | put_user(reg, ®_window[pos++])) | 626 | if (get_user(reg, u++) || |
498 | return -EFAULT; | 627 | put_user(reg, ®_window[pos++])) |
628 | return -EFAULT; | ||
629 | } | ||
630 | } else { | ||
631 | for (; count > 0 && pos < 32; count--) { | ||
632 | if (access_process_vm(target, | ||
633 | (unsigned long) | ||
634 | u, | ||
635 | ®, sizeof(reg), 0) | ||
636 | != sizeof(reg)) | ||
637 | return -EFAULT; | ||
638 | if (access_process_vm(target, | ||
639 | (unsigned long) | ||
640 | ®_window[pos], | ||
641 | ®, sizeof(reg), 1) | ||
642 | != sizeof(reg)) | ||
643 | return -EFAULT; | ||
644 | pos++; | ||
645 | u++; | ||
646 | } | ||
499 | } | 647 | } |
500 | } | 648 | } |
501 | while (count > 0) { | 649 | while (count > 0) { |
@@ -676,14 +824,18 @@ static const struct user_regset_view user_sparc32_view = { | |||
676 | .name = "sparc", .e_machine = EM_SPARC, | 824 | .name = "sparc", .e_machine = EM_SPARC, |
677 | .regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets) | 825 | .regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets) |
678 | }; | 826 | }; |
827 | #endif /* CONFIG_COMPAT */ | ||
679 | 828 | ||
680 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) | 829 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) |
681 | { | 830 | { |
831 | #ifdef CONFIG_COMPAT | ||
682 | if (test_tsk_thread_flag(task, TIF_32BIT)) | 832 | if (test_tsk_thread_flag(task, TIF_32BIT)) |
683 | return &user_sparc32_view; | 833 | return &user_sparc32_view; |
834 | #endif | ||
684 | return &user_sparc64_view; | 835 | return &user_sparc64_view; |
685 | } | 836 | } |
686 | 837 | ||
838 | #ifdef CONFIG_COMPAT | ||
687 | struct compat_fps { | 839 | struct compat_fps { |
688 | unsigned int regs[32]; | 840 | unsigned int regs[32]; |
689 | unsigned int fsr; | 841 | unsigned int fsr; |
@@ -699,7 +851,7 @@ struct compat_fps { | |||
699 | long compat_arch_ptrace(struct task_struct *child, compat_long_t request, | 851 | long compat_arch_ptrace(struct task_struct *child, compat_long_t request, |
700 | compat_ulong_t caddr, compat_ulong_t cdata) | 852 | compat_ulong_t caddr, compat_ulong_t cdata) |
701 | { | 853 | { |
702 | const struct user_regset_view *view = task_user_regset_view(child); | 854 | const struct user_regset_view *view = task_user_regset_view(current); |
703 | compat_ulong_t caddr2 = task_pt_regs(current)->u_regs[UREG_I4]; | 855 | compat_ulong_t caddr2 = task_pt_regs(current)->u_regs[UREG_I4]; |
704 | struct pt_regs32 __user *pregs; | 856 | struct pt_regs32 __user *pregs; |
705 | struct compat_fps __user *fps; | 857 | struct compat_fps __user *fps; |
@@ -798,6 +950,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, | |||
798 | 950 | ||
799 | return ret; | 951 | return ret; |
800 | } | 952 | } |
953 | #endif /* CONFIG_COMPAT */ | ||
801 | 954 | ||
802 | struct fps { | 955 | struct fps { |
803 | unsigned int regs[64]; | 956 | unsigned int regs[64]; |
@@ -806,12 +959,15 @@ struct fps { | |||
806 | 959 | ||
807 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) | 960 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) |
808 | { | 961 | { |
809 | const struct user_regset_view *view = task_user_regset_view(child); | 962 | const struct user_regset_view *view = task_user_regset_view(current); |
810 | struct pt_regs __user *pregs = (struct pt_regs __user *) addr; | ||
811 | unsigned long addr2 = task_pt_regs(current)->u_regs[UREG_I4]; | 963 | unsigned long addr2 = task_pt_regs(current)->u_regs[UREG_I4]; |
812 | struct fps __user *fps = (struct fps __user *) addr; | 964 | struct pt_regs __user *pregs; |
965 | struct fps __user *fps; | ||
813 | int ret; | 966 | int ret; |
814 | 967 | ||
968 | pregs = (struct pt_regs __user *) (unsigned long) addr; | ||
969 | fps = (struct fps __user *) (unsigned long) addr; | ||
970 | |||
815 | switch (request) { | 971 | switch (request) { |
816 | case PTRACE_PEEKUSR: | 972 | case PTRACE_PEEKUSR: |
817 | ret = (addr != 0) ? -EIO : 0; | 973 | ret = (addr != 0) ? -EIO : 0; |