diff options
author | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2008-02-07 13:21:26 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2008-02-07 13:21:26 -0500 |
commit | 0afc2edfada50980bec999f94dcea26ebad3dda6 (patch) | |
tree | 8963dd8fd78ee5c3481acad5903bc459bd3d055c /arch/sparc64 | |
parent | a8e98d6d51a3eb7bb061b1625193a129c8bd094f (diff) | |
parent | d256eb8db60e36fc5dd0a27ce8a64f65df31f7b5 (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc-2.6:
[SPARC32]: Use regsets in arch_ptrace().
[SPARC64]: Use regsets in arch_ptrace().
[SPARC32]: Use regsets for ELF core dumping.
[SPARC64]: Use regsets for ELF core dumping.
[SPARC64]: Remove unintentional ptrace debugging messages.
[SPARC]: Move over to arch_ptrace().
[SPARC]: Remove PTRACE_SUN* handling.
[SPARC]: Kill DEBUG_PTRACE code.
[SPARC32]: Add user regset support.
[SPARC64]: Add user regsets.
[SPARC64]: Fix booting on non-zero cpu.
Diffstat (limited to 'arch/sparc64')
-rw-r--r-- | arch/sparc64/kernel/binfmt_elf32.c | 31 | ||||
-rw-r--r-- | arch/sparc64/kernel/entry.S | 4 | ||||
-rw-r--r-- | arch/sparc64/kernel/head.S | 25 | ||||
-rw-r--r-- | arch/sparc64/kernel/ptrace.c | 1095 | ||||
-rw-r--r-- | arch/sparc64/prom/init.c | 3 |
5 files changed, 726 insertions, 432 deletions
diff --git a/arch/sparc64/kernel/binfmt_elf32.c b/arch/sparc64/kernel/binfmt_elf32.c index 1587a29a4b0e..d141300e76b7 100644 --- a/arch/sparc64/kernel/binfmt_elf32.c +++ b/arch/sparc64/kernel/binfmt_elf32.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * binfmt_elf32.c: Support 32-bit Sparc ELF binaries on Ultra. | 2 | * binfmt_elf32.c: Support 32-bit Sparc ELF binaries on Ultra. |
3 | * | 3 | * |
4 | * Copyright (C) 1995, 1996, 1997, 1998 David S. Miller (davem@davemloft.net) | 4 | * Copyright (C) 1995, 1996, 1997, 1998, 2008 David S. Miller (davem@davemloft.net) |
5 | * Copyright (C) 1995, 1996, 1997, 1998 Jakub Jelinek (jj@ultra.linux.cz) | 5 | * Copyright (C) 1995, 1996, 1997, 1998 Jakub Jelinek (jj@ultra.linux.cz) |
6 | */ | 6 | */ |
7 | 7 | ||
@@ -9,13 +9,6 @@ | |||
9 | #define ELF_CLASS ELFCLASS32 | 9 | #define ELF_CLASS ELFCLASS32 |
10 | #define ELF_DATA ELFDATA2MSB; | 10 | #define ELF_DATA ELFDATA2MSB; |
11 | 11 | ||
12 | /* For the most part we present code dumps in the format | ||
13 | * Solaris does. | ||
14 | */ | ||
15 | typedef unsigned int elf_greg_t; | ||
16 | #define ELF_NGREG 38 | ||
17 | typedef elf_greg_t elf_gregset_t[ELF_NGREG]; | ||
18 | |||
19 | /* Format is: | 12 | /* Format is: |
20 | * G0 --> G7 | 13 | * G0 --> G7 |
21 | * O0 --> O7 | 14 | * O0 --> O7 |
@@ -23,25 +16,9 @@ typedef elf_greg_t elf_gregset_t[ELF_NGREG]; | |||
23 | * I0 --> I7 | 16 | * I0 --> I7 |
24 | * PSR, PC, nPC, Y, WIM, TBR | 17 | * PSR, PC, nPC, Y, WIM, TBR |
25 | */ | 18 | */ |
26 | #include <asm/psrcompat.h> | 19 | typedef unsigned int elf_greg_t; |
27 | #define ELF_CORE_COPY_REGS(__elf_regs, __pt_regs) \ | 20 | #define ELF_NGREG 38 |
28 | do { unsigned int *dest = &(__elf_regs[0]); \ | 21 | typedef elf_greg_t elf_gregset_t[ELF_NGREG]; |
29 | struct pt_regs *src = (__pt_regs); \ | ||
30 | unsigned int __user *sp; \ | ||
31 | int i; \ | ||
32 | for(i = 0; i < 16; i++) \ | ||
33 | dest[i] = (unsigned int) src->u_regs[i];\ | ||
34 | /* Don't try this at home kids... */ \ | ||
35 | sp = (unsigned int __user *) (src->u_regs[14] & \ | ||
36 | 0x00000000fffffffc); \ | ||
37 | for(i = 0; i < 16; i++) \ | ||
38 | __get_user(dest[i+16], &sp[i]); \ | ||
39 | dest[32] = tstate_to_psr(src->tstate); \ | ||
40 | dest[33] = (unsigned int) src->tpc; \ | ||
41 | dest[34] = (unsigned int) src->tnpc; \ | ||
42 | dest[35] = src->y; \ | ||
43 | dest[36] = dest[37] = 0; /* XXX */ \ | ||
44 | } while(0); | ||
45 | 22 | ||
46 | typedef struct { | 23 | typedef struct { |
47 | union { | 24 | union { |
diff --git a/arch/sparc64/kernel/entry.S b/arch/sparc64/kernel/entry.S index ea257e828364..6be4d2d2904e 100644 --- a/arch/sparc64/kernel/entry.S +++ b/arch/sparc64/kernel/entry.S | |||
@@ -1477,10 +1477,6 @@ sys32_rt_sigreturn: | |||
1477 | add %o7, 1f-.-4, %o7 | 1477 | add %o7, 1f-.-4, %o7 |
1478 | nop | 1478 | nop |
1479 | #endif | 1479 | #endif |
1480 | sys_ptrace: add %sp, PTREGS_OFF, %o0 | ||
1481 | call do_ptrace | ||
1482 | add %o7, 1f-.-4, %o7 | ||
1483 | nop | ||
1484 | .align 32 | 1480 | .align 32 |
1485 | 1: ldx [%curptr + TI_FLAGS], %l5 | 1481 | 1: ldx [%curptr + TI_FLAGS], %l5 |
1486 | andcc %l5, (_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT), %g0 | 1482 | andcc %l5, (_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT), %g0 |
diff --git a/arch/sparc64/kernel/head.S b/arch/sparc64/kernel/head.S index c4147ad8677b..44b105c04dd3 100644 --- a/arch/sparc64/kernel/head.S +++ b/arch/sparc64/kernel/head.S | |||
@@ -632,11 +632,36 @@ tlb_fixup_done: | |||
632 | /* Not reached... */ | 632 | /* Not reached... */ |
633 | 633 | ||
634 | 1: | 634 | 1: |
635 | /* If we boot on a non-zero cpu, all of the per-cpu | ||
636 | * variable references we make before setting up the | ||
637 | * per-cpu areas will use a bogus offset. Put a | ||
638 | * compensating factor into __per_cpu_base to handle | ||
639 | * this cleanly. | ||
640 | * | ||
641 | * What the per-cpu code calculates is: | ||
642 | * | ||
643 | * __per_cpu_base + (cpu << __per_cpu_shift) | ||
644 | * | ||
645 | * These two variables are zero initially, so to | ||
646 | * make it all cancel out to zero we need to put | ||
647 | * "0 - (cpu << 0)" into __per_cpu_base so that the | ||
648 | * above formula evaluates to zero. | ||
649 | * | ||
650 | * We cannot even perform a printk() until this stuff | ||
651 | * is setup as that calls cpu_clock() which uses | ||
652 | * per-cpu variables. | ||
653 | */ | ||
654 | sub %g0, %o0, %o1 | ||
655 | sethi %hi(__per_cpu_base), %o2 | ||
656 | stx %o1, [%o2 + %lo(__per_cpu_base)] | ||
635 | #else | 657 | #else |
636 | mov 0, %o0 | 658 | mov 0, %o0 |
637 | #endif | 659 | #endif |
638 | sth %o0, [%g6 + TI_CPU] | 660 | sth %o0, [%g6 + TI_CPU] |
639 | 661 | ||
662 | call prom_init_report | ||
663 | nop | ||
664 | |||
640 | /* Off we go.... */ | 665 | /* Off we go.... */ |
641 | call start_kernel | 666 | call start_kernel |
642 | nop | 667 | nop |
diff --git a/arch/sparc64/kernel/ptrace.c b/arch/sparc64/kernel/ptrace.c index 81111a12f0a8..51f012410f9d 100644 --- a/arch/sparc64/kernel/ptrace.c +++ b/arch/sparc64/kernel/ptrace.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* ptrace.c: Sparc process tracing support. | 1 | /* ptrace.c: Sparc process tracing support. |
2 | * | 2 | * |
3 | * Copyright (C) 1996 David S. Miller (davem@caipfs.rutgers.edu) | 3 | * Copyright (C) 1996, 2008 David S. Miller (davem@davemloft.net) |
4 | * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) | 4 | * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) |
5 | * | 5 | * |
6 | * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson, | 6 | * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson, |
@@ -22,6 +22,9 @@ | |||
22 | #include <linux/seccomp.h> | 22 | #include <linux/seccomp.h> |
23 | #include <linux/audit.h> | 23 | #include <linux/audit.h> |
24 | #include <linux/signal.h> | 24 | #include <linux/signal.h> |
25 | #include <linux/regset.h> | ||
26 | #include <linux/compat.h> | ||
27 | #include <linux/elf.h> | ||
25 | 28 | ||
26 | #include <asm/asi.h> | 29 | #include <asm/asi.h> |
27 | #include <asm/pgtable.h> | 30 | #include <asm/pgtable.h> |
@@ -33,70 +36,7 @@ | |||
33 | #include <asm/page.h> | 36 | #include <asm/page.h> |
34 | #include <asm/cpudata.h> | 37 | #include <asm/cpudata.h> |
35 | 38 | ||
36 | /* Returning from ptrace is a bit tricky because the syscall return | ||
37 | * low level code assumes any value returned which is negative and | ||
38 | * is a valid errno will mean setting the condition codes to indicate | ||
39 | * an error return. This doesn't work, so we have this hook. | ||
40 | */ | ||
41 | static inline void pt_error_return(struct pt_regs *regs, unsigned long error) | ||
42 | { | ||
43 | regs->u_regs[UREG_I0] = error; | ||
44 | regs->tstate |= (TSTATE_ICARRY | TSTATE_XCARRY); | ||
45 | regs->tpc = regs->tnpc; | ||
46 | regs->tnpc += 4; | ||
47 | } | ||
48 | |||
49 | static inline void pt_succ_return(struct pt_regs *regs, unsigned long value) | ||
50 | { | ||
51 | regs->u_regs[UREG_I0] = value; | ||
52 | regs->tstate &= ~(TSTATE_ICARRY | TSTATE_XCARRY); | ||
53 | regs->tpc = regs->tnpc; | ||
54 | regs->tnpc += 4; | ||
55 | } | ||
56 | |||
57 | static inline void | ||
58 | pt_succ_return_linux(struct pt_regs *regs, unsigned long value, void __user *addr) | ||
59 | { | ||
60 | if (test_thread_flag(TIF_32BIT)) { | ||
61 | if (put_user(value, (unsigned int __user *) addr)) { | ||
62 | pt_error_return(regs, EFAULT); | ||
63 | return; | ||
64 | } | ||
65 | } else { | ||
66 | if (put_user(value, (long __user *) addr)) { | ||
67 | pt_error_return(regs, EFAULT); | ||
68 | return; | ||
69 | } | ||
70 | } | ||
71 | regs->u_regs[UREG_I0] = 0; | ||
72 | regs->tstate &= ~(TSTATE_ICARRY | TSTATE_XCARRY); | ||
73 | regs->tpc = regs->tnpc; | ||
74 | regs->tnpc += 4; | ||
75 | } | ||
76 | |||
77 | static void | ||
78 | pt_os_succ_return (struct pt_regs *regs, unsigned long val, void __user *addr) | ||
79 | { | ||
80 | if (current->personality == PER_SUNOS) | ||
81 | pt_succ_return (regs, val); | ||
82 | else | ||
83 | pt_succ_return_linux (regs, val, addr); | ||
84 | } | ||
85 | |||
86 | /* #define ALLOW_INIT_TRACING */ | 39 | /* #define ALLOW_INIT_TRACING */ |
87 | /* #define DEBUG_PTRACE */ | ||
88 | |||
89 | #ifdef DEBUG_PTRACE | ||
90 | char *pt_rq [] = { | ||
91 | /* 0 */ "TRACEME", "PEEKTEXT", "PEEKDATA", "PEEKUSR", | ||
92 | /* 4 */ "POKETEXT", "POKEDATA", "POKEUSR", "CONT", | ||
93 | /* 8 */ "KILL", "SINGLESTEP", "SUNATTACH", "SUNDETACH", | ||
94 | /* 12 */ "GETREGS", "SETREGS", "GETFPREGS", "SETFPREGS", | ||
95 | /* 16 */ "READDATA", "WRITEDATA", "READTEXT", "WRITETEXT", | ||
96 | /* 20 */ "GETFPAREGS", "SETFPAREGS", "unknown", "unknown", | ||
97 | /* 24 */ "SYSCALL", "" | ||
98 | }; | ||
99 | #endif | ||
100 | 40 | ||
101 | /* | 41 | /* |
102 | * Called by kernel/ptrace.c when detaching.. | 42 | * Called by kernel/ptrace.c when detaching.. |
@@ -167,267 +107,709 @@ void flush_ptrace_access(struct vm_area_struct *vma, struct page *page, | |||
167 | } | 107 | } |
168 | } | 108 | } |
169 | 109 | ||
170 | asmlinkage void do_ptrace(struct pt_regs *regs) | 110 | enum sparc_regset { |
111 | REGSET_GENERAL, | ||
112 | REGSET_FP, | ||
113 | }; | ||
114 | |||
115 | static int genregs64_get(struct task_struct *target, | ||
116 | const struct user_regset *regset, | ||
117 | unsigned int pos, unsigned int count, | ||
118 | void *kbuf, void __user *ubuf) | ||
171 | { | 119 | { |
172 | int request = regs->u_regs[UREG_I0]; | 120 | const struct pt_regs *regs = task_pt_regs(target); |
173 | pid_t pid = regs->u_regs[UREG_I1]; | ||
174 | unsigned long addr = regs->u_regs[UREG_I2]; | ||
175 | unsigned long data = regs->u_regs[UREG_I3]; | ||
176 | unsigned long addr2 = regs->u_regs[UREG_I4]; | ||
177 | struct task_struct *child; | ||
178 | int ret; | 121 | int ret; |
179 | 122 | ||
180 | if (test_thread_flag(TIF_32BIT)) { | 123 | if (target == current) |
181 | addr &= 0xffffffffUL; | 124 | flushw_user(); |
182 | data &= 0xffffffffUL; | 125 | |
183 | addr2 &= 0xffffffffUL; | 126 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, |
127 | regs->u_regs, | ||
128 | 0, 16 * sizeof(u64)); | ||
129 | if (!ret) { | ||
130 | unsigned long __user *reg_window = (unsigned long __user *) | ||
131 | (regs->u_regs[UREG_I6] + STACK_BIAS); | ||
132 | unsigned long window[16]; | ||
133 | |||
134 | if (copy_from_user(window, reg_window, sizeof(window))) | ||
135 | return -EFAULT; | ||
136 | |||
137 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
138 | window, | ||
139 | 16 * sizeof(u64), | ||
140 | 32 * sizeof(u64)); | ||
184 | } | 141 | } |
185 | lock_kernel(); | ||
186 | #ifdef DEBUG_PTRACE | ||
187 | { | ||
188 | char *s; | ||
189 | 142 | ||
190 | if ((request >= 0) && (request <= 24)) | 143 | if (!ret) { |
191 | s = pt_rq [request]; | 144 | /* TSTATE, TPC, TNPC */ |
192 | else | 145 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, |
193 | s = "unknown"; | 146 | ®s->tstate, |
147 | 32 * sizeof(u64), | ||
148 | 35 * sizeof(u64)); | ||
149 | } | ||
150 | |||
151 | if (!ret) { | ||
152 | unsigned long y = regs->y; | ||
194 | 153 | ||
195 | if (request == PTRACE_POKEDATA && data == 0x91d02001){ | 154 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, |
196 | printk ("do_ptrace: breakpoint pid=%d, addr=%016lx addr2=%016lx\n", | 155 | &y, |
197 | pid, addr, addr2); | 156 | 35 * sizeof(u64), |
198 | } else | 157 | 36 * sizeof(u64)); |
199 | printk("do_ptrace: rq=%s(%d) pid=%d addr=%016lx data=%016lx addr2=%016lx\n", | ||
200 | s, request, pid, addr, data, addr2); | ||
201 | } | 158 | } |
202 | #endif | 159 | |
203 | if (request == PTRACE_TRACEME) { | 160 | if (!ret) |
204 | ret = ptrace_traceme(); | 161 | ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, |
205 | if (ret < 0) | 162 | 36 * sizeof(u64), -1); |
206 | pt_error_return(regs, -ret); | 163 | |
164 | return ret; | ||
165 | } | ||
166 | |||
167 | static int genregs64_set(struct task_struct *target, | ||
168 | const struct user_regset *regset, | ||
169 | unsigned int pos, unsigned int count, | ||
170 | const void *kbuf, const void __user *ubuf) | ||
171 | { | ||
172 | struct pt_regs *regs = task_pt_regs(target); | ||
173 | int ret; | ||
174 | |||
175 | if (target == current) | ||
176 | flushw_user(); | ||
177 | |||
178 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
179 | regs->u_regs, | ||
180 | 0, 16 * sizeof(u64)); | ||
181 | if (!ret && count > 0) { | ||
182 | unsigned long __user *reg_window = (unsigned long __user *) | ||
183 | (regs->u_regs[UREG_I6] + STACK_BIAS); | ||
184 | unsigned long window[16]; | ||
185 | |||
186 | if (copy_from_user(window, reg_window, sizeof(window))) | ||
187 | return -EFAULT; | ||
188 | |||
189 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
190 | window, | ||
191 | 16 * sizeof(u64), | ||
192 | 32 * sizeof(u64)); | ||
193 | if (!ret && | ||
194 | copy_to_user(reg_window, window, sizeof(window))) | ||
195 | return -EFAULT; | ||
196 | } | ||
197 | |||
198 | if (!ret && count > 0) { | ||
199 | unsigned long tstate; | ||
200 | |||
201 | /* TSTATE */ | ||
202 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
203 | &tstate, | ||
204 | 32 * sizeof(u64), | ||
205 | 33 * sizeof(u64)); | ||
206 | if (!ret) { | ||
207 | /* Only the condition codes can be modified | ||
208 | * in the %tstate register. | ||
209 | */ | ||
210 | tstate &= (TSTATE_ICC | TSTATE_XCC); | ||
211 | regs->tstate &= ~(TSTATE_ICC | TSTATE_XCC); | ||
212 | regs->tstate |= tstate; | ||
213 | } | ||
214 | } | ||
215 | |||
216 | if (!ret) { | ||
217 | /* TPC, TNPC */ | ||
218 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
219 | ®s->tpc, | ||
220 | 33 * sizeof(u64), | ||
221 | 35 * sizeof(u64)); | ||
222 | } | ||
223 | |||
224 | if (!ret) { | ||
225 | unsigned long y; | ||
226 | |||
227 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
228 | &y, | ||
229 | 35 * sizeof(u64), | ||
230 | 36 * sizeof(u64)); | ||
231 | if (!ret) | ||
232 | regs->y = y; | ||
233 | } | ||
234 | |||
235 | if (!ret) | ||
236 | ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | ||
237 | 36 * sizeof(u64), -1); | ||
238 | |||
239 | return ret; | ||
240 | } | ||
241 | |||
242 | static int fpregs64_get(struct task_struct *target, | ||
243 | const struct user_regset *regset, | ||
244 | unsigned int pos, unsigned int count, | ||
245 | void *kbuf, void __user *ubuf) | ||
246 | { | ||
247 | const unsigned long *fpregs = task_thread_info(target)->fpregs; | ||
248 | unsigned long fprs, fsr, gsr; | ||
249 | int ret; | ||
250 | |||
251 | if (target == current) | ||
252 | save_and_clear_fpu(); | ||
253 | |||
254 | fprs = task_thread_info(target)->fpsaved[0]; | ||
255 | |||
256 | if (fprs & FPRS_DL) | ||
257 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
258 | fpregs, | ||
259 | 0, 16 * sizeof(u64)); | ||
260 | else | ||
261 | ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | ||
262 | 0, | ||
263 | 16 * sizeof(u64)); | ||
264 | |||
265 | if (!ret) { | ||
266 | if (fprs & FPRS_DU) | ||
267 | ret = user_regset_copyout(&pos, &count, | ||
268 | &kbuf, &ubuf, | ||
269 | fpregs + 16, | ||
270 | 16 * sizeof(u64), | ||
271 | 32 * sizeof(u64)); | ||
207 | else | 272 | else |
208 | pt_succ_return(regs, 0); | 273 | ret = user_regset_copyout_zero(&pos, &count, |
209 | goto out; | 274 | &kbuf, &ubuf, |
275 | 16 * sizeof(u64), | ||
276 | 32 * sizeof(u64)); | ||
210 | } | 277 | } |
211 | 278 | ||
212 | child = ptrace_get_task_struct(pid); | 279 | if (fprs & FPRS_FEF) { |
213 | if (IS_ERR(child)) { | 280 | fsr = task_thread_info(target)->xfsr[0]; |
214 | ret = PTR_ERR(child); | 281 | gsr = task_thread_info(target)->gsr[0]; |
215 | pt_error_return(regs, -ret); | 282 | } else { |
216 | goto out; | 283 | fsr = gsr = 0; |
217 | } | 284 | } |
218 | 285 | ||
219 | if ((current->personality == PER_SUNOS && request == PTRACE_SUNATTACH) | 286 | if (!ret) |
220 | || (current->personality != PER_SUNOS && request == PTRACE_ATTACH)) { | 287 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, |
221 | if (ptrace_attach(child)) { | 288 | &fsr, |
222 | pt_error_return(regs, EPERM); | 289 | 32 * sizeof(u64), |
223 | goto out_tsk; | 290 | 33 * sizeof(u64)); |
291 | if (!ret) | ||
292 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
293 | &gsr, | ||
294 | 33 * sizeof(u64), | ||
295 | 34 * sizeof(u64)); | ||
296 | if (!ret) | ||
297 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
298 | &fprs, | ||
299 | 34 * sizeof(u64), | ||
300 | 35 * sizeof(u64)); | ||
301 | |||
302 | if (!ret) | ||
303 | ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | ||
304 | 35 * sizeof(u64), -1); | ||
305 | |||
306 | return ret; | ||
307 | } | ||
308 | |||
309 | static int fpregs64_set(struct task_struct *target, | ||
310 | const struct user_regset *regset, | ||
311 | unsigned int pos, unsigned int count, | ||
312 | const void *kbuf, const void __user *ubuf) | ||
313 | { | ||
314 | unsigned long *fpregs = task_thread_info(target)->fpregs; | ||
315 | unsigned long fprs; | ||
316 | int ret; | ||
317 | |||
318 | if (target == current) | ||
319 | save_and_clear_fpu(); | ||
320 | |||
321 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
322 | fpregs, | ||
323 | 0, 32 * sizeof(u64)); | ||
324 | if (!ret) | ||
325 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
326 | task_thread_info(target)->xfsr, | ||
327 | 32 * sizeof(u64), | ||
328 | 33 * sizeof(u64)); | ||
329 | if (!ret) | ||
330 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
331 | task_thread_info(target)->gsr, | ||
332 | 33 * sizeof(u64), | ||
333 | 34 * sizeof(u64)); | ||
334 | |||
335 | fprs = task_thread_info(target)->fpsaved[0]; | ||
336 | if (!ret && count > 0) { | ||
337 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
338 | &fprs, | ||
339 | 34 * sizeof(u64), | ||
340 | 35 * sizeof(u64)); | ||
341 | } | ||
342 | |||
343 | fprs |= (FPRS_FEF | FPRS_DL | FPRS_DU); | ||
344 | task_thread_info(target)->fpsaved[0] = fprs; | ||
345 | |||
346 | if (!ret) | ||
347 | ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | ||
348 | 35 * sizeof(u64), -1); | ||
349 | return ret; | ||
350 | } | ||
351 | |||
352 | static const struct user_regset sparc64_regsets[] = { | ||
353 | /* Format is: | ||
354 | * G0 --> G7 | ||
355 | * O0 --> O7 | ||
356 | * L0 --> L7 | ||
357 | * I0 --> I7 | ||
358 | * TSTATE, TPC, TNPC, Y | ||
359 | */ | ||
360 | [REGSET_GENERAL] = { | ||
361 | .core_note_type = NT_PRSTATUS, | ||
362 | .n = 36 * sizeof(u64), | ||
363 | .size = sizeof(u64), .align = sizeof(u64), | ||
364 | .get = genregs64_get, .set = genregs64_set | ||
365 | }, | ||
366 | /* Format is: | ||
367 | * F0 --> F63 | ||
368 | * FSR | ||
369 | * GSR | ||
370 | * FPRS | ||
371 | */ | ||
372 | [REGSET_FP] = { | ||
373 | .core_note_type = NT_PRFPREG, | ||
374 | .n = 35 * sizeof(u64), | ||
375 | .size = sizeof(u64), .align = sizeof(u64), | ||
376 | .get = fpregs64_get, .set = fpregs64_set | ||
377 | }, | ||
378 | }; | ||
379 | |||
380 | static const struct user_regset_view user_sparc64_view = { | ||
381 | .name = "sparc64", .e_machine = EM_SPARCV9, | ||
382 | .regsets = sparc64_regsets, .n = ARRAY_SIZE(sparc64_regsets) | ||
383 | }; | ||
384 | |||
385 | static int genregs32_get(struct task_struct *target, | ||
386 | const struct user_regset *regset, | ||
387 | unsigned int pos, unsigned int count, | ||
388 | void *kbuf, void __user *ubuf) | ||
389 | { | ||
390 | const struct pt_regs *regs = task_pt_regs(target); | ||
391 | compat_ulong_t __user *reg_window; | ||
392 | compat_ulong_t *k = kbuf; | ||
393 | compat_ulong_t __user *u = ubuf; | ||
394 | compat_ulong_t reg; | ||
395 | |||
396 | if (target == current) | ||
397 | flushw_user(); | ||
398 | |||
399 | pos /= sizeof(reg); | ||
400 | count /= sizeof(reg); | ||
401 | |||
402 | if (kbuf) { | ||
403 | for (; count > 0 && pos < 16; count--) | ||
404 | *k++ = regs->u_regs[pos++]; | ||
405 | |||
406 | reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6]; | ||
407 | for (; count > 0 && pos < 32; count--) { | ||
408 | if (get_user(*k++, ®_window[pos++])) | ||
409 | return -EFAULT; | ||
410 | } | ||
411 | } else { | ||
412 | for (; count > 0 && pos < 16; count--) { | ||
413 | if (put_user((compat_ulong_t) regs->u_regs[pos++], u++)) | ||
414 | return -EFAULT; | ||
415 | } | ||
416 | |||
417 | reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6]; | ||
418 | for (; count > 0 && pos < 32; count--) { | ||
419 | if (get_user(reg, ®_window[pos++]) || | ||
420 | put_user(reg, u++)) | ||
421 | return -EFAULT; | ||
224 | } | 422 | } |
225 | pt_succ_return(regs, 0); | ||
226 | goto out_tsk; | ||
227 | } | 423 | } |
424 | while (count > 0) { | ||
425 | switch (pos) { | ||
426 | case 32: /* PSR */ | ||
427 | reg = tstate_to_psr(regs->tstate); | ||
428 | break; | ||
429 | case 33: /* PC */ | ||
430 | reg = regs->tpc; | ||
431 | break; | ||
432 | case 34: /* NPC */ | ||
433 | reg = regs->tnpc; | ||
434 | break; | ||
435 | case 35: /* Y */ | ||
436 | reg = regs->y; | ||
437 | break; | ||
438 | case 36: /* WIM */ | ||
439 | case 37: /* TBR */ | ||
440 | reg = 0; | ||
441 | break; | ||
442 | default: | ||
443 | goto finish; | ||
444 | } | ||
228 | 445 | ||
229 | ret = ptrace_check_attach(child, request == PTRACE_KILL); | 446 | if (kbuf) |
230 | if (ret < 0) { | 447 | *k++ = reg; |
231 | pt_error_return(regs, -ret); | 448 | else if (put_user(reg, u++)) |
232 | goto out_tsk; | 449 | return -EFAULT; |
450 | pos++; | ||
451 | count--; | ||
233 | } | 452 | } |
453 | finish: | ||
454 | pos *= sizeof(reg); | ||
455 | count *= sizeof(reg); | ||
456 | |||
457 | return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | ||
458 | 38 * sizeof(reg), -1); | ||
459 | } | ||
234 | 460 | ||
235 | if (!(test_thread_flag(TIF_32BIT)) && | 461 | static int genregs32_set(struct task_struct *target, |
236 | ((request == PTRACE_READDATA64) || | 462 | const struct user_regset *regset, |
237 | (request == PTRACE_WRITEDATA64) || | 463 | unsigned int pos, unsigned int count, |
238 | (request == PTRACE_READTEXT64) || | 464 | const void *kbuf, const void __user *ubuf) |
239 | (request == PTRACE_WRITETEXT64) || | 465 | { |
240 | (request == PTRACE_PEEKTEXT64) || | 466 | struct pt_regs *regs = task_pt_regs(target); |
241 | (request == PTRACE_POKETEXT64) || | 467 | compat_ulong_t __user *reg_window; |
242 | (request == PTRACE_PEEKDATA64) || | 468 | const compat_ulong_t *k = kbuf; |
243 | (request == PTRACE_POKEDATA64))) { | 469 | const compat_ulong_t __user *u = ubuf; |
244 | addr = regs->u_regs[UREG_G2]; | 470 | compat_ulong_t reg; |
245 | addr2 = regs->u_regs[UREG_G3]; | 471 | |
246 | request -= 30; /* wheee... */ | 472 | if (target == current) |
473 | flushw_user(); | ||
474 | |||
475 | pos /= sizeof(reg); | ||
476 | count /= sizeof(reg); | ||
477 | |||
478 | if (kbuf) { | ||
479 | for (; count > 0 && pos < 16; count--) | ||
480 | regs->u_regs[pos++] = *k++; | ||
481 | |||
482 | reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6]; | ||
483 | for (; count > 0 && pos < 32; count--) { | ||
484 | if (put_user(*k++, ®_window[pos++])) | ||
485 | return -EFAULT; | ||
486 | } | ||
487 | } else { | ||
488 | for (; count > 0 && pos < 16; count--) { | ||
489 | if (get_user(reg, u++)) | ||
490 | return -EFAULT; | ||
491 | regs->u_regs[pos++] = reg; | ||
492 | } | ||
493 | |||
494 | reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6]; | ||
495 | for (; count > 0 && pos < 32; count--) { | ||
496 | if (get_user(reg, u++) || | ||
497 | put_user(reg, ®_window[pos++])) | ||
498 | return -EFAULT; | ||
499 | } | ||
247 | } | 500 | } |
501 | while (count > 0) { | ||
502 | unsigned long tstate; | ||
503 | |||
504 | if (kbuf) | ||
505 | reg = *k++; | ||
506 | else if (get_user(reg, u++)) | ||
507 | return -EFAULT; | ||
508 | |||
509 | switch (pos) { | ||
510 | case 32: /* PSR */ | ||
511 | tstate = regs->tstate; | ||
512 | tstate &= ~(TSTATE_ICC | TSTATE_XCC); | ||
513 | tstate |= psr_to_tstate_icc(reg); | ||
514 | regs->tstate = tstate; | ||
515 | break; | ||
516 | case 33: /* PC */ | ||
517 | regs->tpc = reg; | ||
518 | break; | ||
519 | case 34: /* NPC */ | ||
520 | regs->tnpc = reg; | ||
521 | break; | ||
522 | case 35: /* Y */ | ||
523 | regs->y = reg; | ||
524 | break; | ||
525 | case 36: /* WIM */ | ||
526 | case 37: /* TBR */ | ||
527 | break; | ||
528 | default: | ||
529 | goto finish; | ||
530 | } | ||
531 | |||
532 | pos++; | ||
533 | count--; | ||
534 | } | ||
535 | finish: | ||
536 | pos *= sizeof(reg); | ||
537 | count *= sizeof(reg); | ||
538 | |||
539 | return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | ||
540 | 38 * sizeof(reg), -1); | ||
541 | } | ||
542 | |||
543 | static int fpregs32_get(struct task_struct *target, | ||
544 | const struct user_regset *regset, | ||
545 | unsigned int pos, unsigned int count, | ||
546 | void *kbuf, void __user *ubuf) | ||
547 | { | ||
548 | const unsigned long *fpregs = task_thread_info(target)->fpregs; | ||
549 | compat_ulong_t enabled; | ||
550 | unsigned long fprs; | ||
551 | compat_ulong_t fsr; | ||
552 | int ret = 0; | ||
553 | |||
554 | if (target == current) | ||
555 | save_and_clear_fpu(); | ||
556 | |||
557 | fprs = task_thread_info(target)->fpsaved[0]; | ||
558 | if (fprs & FPRS_FEF) { | ||
559 | fsr = task_thread_info(target)->xfsr[0]; | ||
560 | enabled = 1; | ||
561 | } else { | ||
562 | fsr = 0; | ||
563 | enabled = 0; | ||
564 | } | ||
565 | |||
566 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
567 | fpregs, | ||
568 | 0, 32 * sizeof(u32)); | ||
569 | |||
570 | if (!ret) | ||
571 | ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | ||
572 | 32 * sizeof(u32), | ||
573 | 33 * sizeof(u32)); | ||
574 | if (!ret) | ||
575 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
576 | &fsr, | ||
577 | 33 * sizeof(u32), | ||
578 | 34 * sizeof(u32)); | ||
579 | |||
580 | if (!ret) { | ||
581 | compat_ulong_t val; | ||
582 | |||
583 | val = (enabled << 8) | (8 << 16); | ||
584 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
585 | &val, | ||
586 | 34 * sizeof(u32), | ||
587 | 35 * sizeof(u32)); | ||
588 | } | ||
589 | |||
590 | if (!ret) | ||
591 | ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | ||
592 | 35 * sizeof(u32), -1); | ||
593 | |||
594 | return ret; | ||
595 | } | ||
596 | |||
597 | static int fpregs32_set(struct task_struct *target, | ||
598 | const struct user_regset *regset, | ||
599 | unsigned int pos, unsigned int count, | ||
600 | const void *kbuf, const void __user *ubuf) | ||
601 | { | ||
602 | unsigned long *fpregs = task_thread_info(target)->fpregs; | ||
603 | unsigned long fprs; | ||
604 | int ret; | ||
605 | |||
606 | if (target == current) | ||
607 | save_and_clear_fpu(); | ||
608 | |||
609 | fprs = task_thread_info(target)->fpsaved[0]; | ||
610 | |||
611 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
612 | fpregs, | ||
613 | 0, 32 * sizeof(u32)); | ||
614 | if (!ret) | ||
615 | user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | ||
616 | 32 * sizeof(u32), | ||
617 | 33 * sizeof(u32)); | ||
618 | if (!ret && count > 0) { | ||
619 | compat_ulong_t fsr; | ||
620 | unsigned long val; | ||
621 | |||
622 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
623 | &fsr, | ||
624 | 33 * sizeof(u32), | ||
625 | 34 * sizeof(u32)); | ||
626 | if (!ret) { | ||
627 | val = task_thread_info(target)->xfsr[0]; | ||
628 | val &= 0xffffffff00000000UL; | ||
629 | val |= fsr; | ||
630 | task_thread_info(target)->xfsr[0] = val; | ||
631 | } | ||
632 | } | ||
633 | |||
634 | fprs |= (FPRS_FEF | FPRS_DL); | ||
635 | task_thread_info(target)->fpsaved[0] = fprs; | ||
636 | |||
637 | if (!ret) | ||
638 | ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | ||
639 | 34 * sizeof(u32), -1); | ||
640 | return ret; | ||
641 | } | ||
642 | |||
643 | static const struct user_regset sparc32_regsets[] = { | ||
644 | /* Format is: | ||
645 | * G0 --> G7 | ||
646 | * O0 --> O7 | ||
647 | * L0 --> L7 | ||
648 | * I0 --> I7 | ||
649 | * PSR, PC, nPC, Y, WIM, TBR | ||
650 | */ | ||
651 | [REGSET_GENERAL] = { | ||
652 | .core_note_type = NT_PRSTATUS, | ||
653 | .n = 38 * sizeof(u32), | ||
654 | .size = sizeof(u32), .align = sizeof(u32), | ||
655 | .get = genregs32_get, .set = genregs32_set | ||
656 | }, | ||
657 | /* Format is: | ||
658 | * F0 --> F31 | ||
659 | * empty 32-bit word | ||
660 | * FSR (32--bit word) | ||
661 | * FPU QUEUE COUNT (8-bit char) | ||
662 | * FPU QUEUE ENTRYSIZE (8-bit char) | ||
663 | * FPU ENABLED (8-bit char) | ||
664 | * empty 8-bit char | ||
665 | * FPU QUEUE (64 32-bit ints) | ||
666 | */ | ||
667 | [REGSET_FP] = { | ||
668 | .core_note_type = NT_PRFPREG, | ||
669 | .n = 99 * sizeof(u32), | ||
670 | .size = sizeof(u32), .align = sizeof(u32), | ||
671 | .get = fpregs32_get, .set = fpregs32_set | ||
672 | }, | ||
673 | }; | ||
674 | |||
675 | static const struct user_regset_view user_sparc32_view = { | ||
676 | .name = "sparc", .e_machine = EM_SPARC, | ||
677 | .regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets) | ||
678 | }; | ||
679 | |||
680 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) | ||
681 | { | ||
682 | if (test_tsk_thread_flag(task, TIF_32BIT)) | ||
683 | return &user_sparc32_view; | ||
684 | return &user_sparc64_view; | ||
685 | } | ||
686 | |||
687 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) | ||
688 | { | ||
689 | long addr2 = task_pt_regs(current)->u_regs[UREG_I4]; | ||
690 | const struct user_regset_view *view; | ||
691 | int ret; | ||
692 | |||
693 | if (test_thread_flag(TIF_32BIT)) | ||
694 | addr2 &= 0xffffffffUL; | ||
695 | |||
696 | view = task_user_regset_view(child); | ||
248 | 697 | ||
249 | switch(request) { | 698 | switch(request) { |
250 | case PTRACE_PEEKUSR: | 699 | case PTRACE_PEEKUSR: |
251 | if (addr != 0) | 700 | ret = (addr != 0) ? -EIO : 0; |
252 | pt_error_return(regs, EIO); | 701 | break; |
253 | else | ||
254 | pt_succ_return(regs, 0); | ||
255 | goto out_tsk; | ||
256 | 702 | ||
257 | case PTRACE_PEEKTEXT: /* read word at location addr. */ | 703 | case PTRACE_PEEKTEXT: /* read word at location addr. */ |
258 | case PTRACE_PEEKDATA: { | 704 | case PTRACE_PEEKDATA: { |
259 | unsigned long tmp64; | 705 | unsigned long tmp64; |
260 | unsigned int tmp32; | 706 | unsigned int tmp32; |
261 | int res, copied; | 707 | int copied; |
262 | 708 | ||
263 | res = -EIO; | 709 | ret = -EIO; |
264 | if (test_thread_flag(TIF_32BIT)) { | 710 | if (test_thread_flag(TIF_32BIT)) { |
265 | copied = access_process_vm(child, addr, | 711 | copied = access_process_vm(child, addr, |
266 | &tmp32, sizeof(tmp32), 0); | 712 | &tmp32, sizeof(tmp32), 0); |
267 | tmp64 = (unsigned long) tmp32; | ||
268 | if (copied == sizeof(tmp32)) | 713 | if (copied == sizeof(tmp32)) |
269 | res = 0; | 714 | ret = put_user(tmp32, |
715 | (unsigned int __user *) data); | ||
270 | } else { | 716 | } else { |
271 | copied = access_process_vm(child, addr, | 717 | copied = access_process_vm(child, addr, |
272 | &tmp64, sizeof(tmp64), 0); | 718 | &tmp64, sizeof(tmp64), 0); |
273 | if (copied == sizeof(tmp64)) | 719 | if (copied == sizeof(tmp64)) |
274 | res = 0; | 720 | ret = put_user(tmp64, |
721 | (unsigned long __user *) data); | ||
275 | } | 722 | } |
276 | if (res < 0) | 723 | break; |
277 | pt_error_return(regs, -res); | ||
278 | else | ||
279 | pt_os_succ_return(regs, tmp64, (void __user *) data); | ||
280 | goto out_tsk; | ||
281 | } | 724 | } |
282 | 725 | ||
283 | case PTRACE_POKETEXT: /* write the word at location addr. */ | 726 | case PTRACE_POKETEXT: /* write the word at location addr. */ |
284 | case PTRACE_POKEDATA: { | 727 | case PTRACE_POKEDATA: { |
285 | unsigned long tmp64; | 728 | unsigned long tmp64; |
286 | unsigned int tmp32; | 729 | unsigned int tmp32; |
287 | int copied, res = -EIO; | 730 | int copied; |
288 | 731 | ||
732 | ret = -EIO; | ||
289 | if (test_thread_flag(TIF_32BIT)) { | 733 | if (test_thread_flag(TIF_32BIT)) { |
290 | tmp32 = data; | 734 | tmp32 = data; |
291 | copied = access_process_vm(child, addr, | 735 | copied = access_process_vm(child, addr, |
292 | &tmp32, sizeof(tmp32), 1); | 736 | &tmp32, sizeof(tmp32), 1); |
293 | if (copied == sizeof(tmp32)) | 737 | if (copied == sizeof(tmp32)) |
294 | res = 0; | 738 | ret = 0; |
295 | } else { | 739 | } else { |
296 | tmp64 = data; | 740 | tmp64 = data; |
297 | copied = access_process_vm(child, addr, | 741 | copied = access_process_vm(child, addr, |
298 | &tmp64, sizeof(tmp64), 1); | 742 | &tmp64, sizeof(tmp64), 1); |
299 | if (copied == sizeof(tmp64)) | 743 | if (copied == sizeof(tmp64)) |
300 | res = 0; | 744 | ret = 0; |
301 | } | 745 | } |
302 | if (res < 0) | 746 | break; |
303 | pt_error_return(regs, -res); | ||
304 | else | ||
305 | pt_succ_return(regs, res); | ||
306 | goto out_tsk; | ||
307 | } | 747 | } |
308 | 748 | ||
309 | case PTRACE_GETREGS: { | 749 | case PTRACE_GETREGS: { |
310 | struct pt_regs32 __user *pregs = | 750 | struct pt_regs32 __user *pregs = |
311 | (struct pt_regs32 __user *) addr; | 751 | (struct pt_regs32 __user *) addr; |
312 | struct pt_regs *cregs = task_pt_regs(child); | 752 | |
313 | int rval; | 753 | ret = copy_regset_to_user(child, view, REGSET_GENERAL, |
314 | 754 | 32 * sizeof(u32), | |
315 | if (__put_user(tstate_to_psr(cregs->tstate), (&pregs->psr)) || | 755 | 4 * sizeof(u32), |
316 | __put_user(cregs->tpc, (&pregs->pc)) || | 756 | &pregs->psr); |
317 | __put_user(cregs->tnpc, (&pregs->npc)) || | 757 | if (!ret) |
318 | __put_user(cregs->y, (&pregs->y))) { | 758 | ret = copy_regset_to_user(child, view, REGSET_GENERAL, |
319 | pt_error_return(regs, EFAULT); | 759 | 1 * sizeof(u32), |
320 | goto out_tsk; | 760 | 15 * sizeof(u32), |
321 | } | 761 | &pregs->u_regs[0]); |
322 | for (rval = 1; rval < 16; rval++) | 762 | break; |
323 | if (__put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]))) { | ||
324 | pt_error_return(regs, EFAULT); | ||
325 | goto out_tsk; | ||
326 | } | ||
327 | pt_succ_return(regs, 0); | ||
328 | #ifdef DEBUG_PTRACE | ||
329 | printk ("PC=%lx nPC=%lx o7=%lx\n", cregs->tpc, cregs->tnpc, cregs->u_regs [15]); | ||
330 | #endif | ||
331 | goto out_tsk; | ||
332 | } | 763 | } |
333 | 764 | ||
334 | case PTRACE_GETREGS64: { | 765 | case PTRACE_GETREGS64: { |
335 | struct pt_regs __user *pregs = (struct pt_regs __user *) addr; | 766 | struct pt_regs __user *pregs = (struct pt_regs __user *) addr; |
336 | struct pt_regs *cregs = task_pt_regs(child); | 767 | |
337 | unsigned long tpc = cregs->tpc; | 768 | ret = copy_regset_to_user(child, view, REGSET_GENERAL, |
338 | int rval; | 769 | 1 * sizeof(u64), |
339 | 770 | 15 * sizeof(u64), | |
340 | if ((task_thread_info(child)->flags & _TIF_32BIT) != 0) | 771 | &pregs->u_regs[0]); |
341 | tpc &= 0xffffffff; | 772 | if (!ret) { |
342 | if (__put_user(cregs->tstate, (&pregs->tstate)) || | 773 | /* XXX doesn't handle 'y' register correctly XXX */ |
343 | __put_user(tpc, (&pregs->tpc)) || | 774 | ret = copy_regset_to_user(child, view, REGSET_GENERAL, |
344 | __put_user(cregs->tnpc, (&pregs->tnpc)) || | 775 | 32 * sizeof(u64), |
345 | __put_user(cregs->y, (&pregs->y))) { | 776 | 4 * sizeof(u64), |
346 | pt_error_return(regs, EFAULT); | 777 | &pregs->tstate); |
347 | goto out_tsk; | ||
348 | } | 778 | } |
349 | for (rval = 1; rval < 16; rval++) | 779 | break; |
350 | if (__put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]))) { | ||
351 | pt_error_return(regs, EFAULT); | ||
352 | goto out_tsk; | ||
353 | } | ||
354 | pt_succ_return(regs, 0); | ||
355 | #ifdef DEBUG_PTRACE | ||
356 | printk ("PC=%lx nPC=%lx o7=%lx\n", cregs->tpc, cregs->tnpc, cregs->u_regs [15]); | ||
357 | #endif | ||
358 | goto out_tsk; | ||
359 | } | 780 | } |
360 | 781 | ||
361 | case PTRACE_SETREGS: { | 782 | case PTRACE_SETREGS: { |
362 | struct pt_regs32 __user *pregs = | 783 | struct pt_regs32 __user *pregs = |
363 | (struct pt_regs32 __user *) addr; | 784 | (struct pt_regs32 __user *) addr; |
364 | struct pt_regs *cregs = task_pt_regs(child); | 785 | |
365 | unsigned int psr, pc, npc, y; | 786 | ret = copy_regset_from_user(child, view, REGSET_GENERAL, |
366 | int i; | 787 | 32 * sizeof(u32), |
367 | 788 | 4 * sizeof(u32), | |
368 | /* Must be careful, tracing process can only set certain | 789 | &pregs->psr); |
369 | * bits in the psr. | 790 | if (!ret) |
370 | */ | 791 | ret = copy_regset_from_user(child, view, REGSET_GENERAL, |
371 | if (__get_user(psr, (&pregs->psr)) || | 792 | 1 * sizeof(u32), |
372 | __get_user(pc, (&pregs->pc)) || | 793 | 15 * sizeof(u32), |
373 | __get_user(npc, (&pregs->npc)) || | 794 | &pregs->u_regs[0]); |
374 | __get_user(y, (&pregs->y))) { | 795 | break; |
375 | pt_error_return(regs, EFAULT); | ||
376 | goto out_tsk; | ||
377 | } | ||
378 | cregs->tstate &= ~(TSTATE_ICC); | ||
379 | cregs->tstate |= psr_to_tstate_icc(psr); | ||
380 | if (!((pc | npc) & 3)) { | ||
381 | cregs->tpc = pc; | ||
382 | cregs->tnpc = npc; | ||
383 | } | ||
384 | cregs->y = y; | ||
385 | for (i = 1; i < 16; i++) { | ||
386 | if (__get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]))) { | ||
387 | pt_error_return(regs, EFAULT); | ||
388 | goto out_tsk; | ||
389 | } | ||
390 | } | ||
391 | pt_succ_return(regs, 0); | ||
392 | goto out_tsk; | ||
393 | } | 796 | } |
394 | 797 | ||
395 | case PTRACE_SETREGS64: { | 798 | case PTRACE_SETREGS64: { |
396 | struct pt_regs __user *pregs = (struct pt_regs __user *) addr; | 799 | struct pt_regs __user *pregs = (struct pt_regs __user *) addr; |
397 | struct pt_regs *cregs = task_pt_regs(child); | 800 | |
398 | unsigned long tstate, tpc, tnpc, y; | 801 | ret = copy_regset_from_user(child, view, REGSET_GENERAL, |
399 | int i; | 802 | 1 * sizeof(u64), |
400 | 803 | 15 * sizeof(u64), | |
401 | /* Must be careful, tracing process can only set certain | 804 | &pregs->u_regs[0]); |
402 | * bits in the psr. | 805 | if (!ret) { |
403 | */ | 806 | /* XXX doesn't handle 'y' register correctly XXX */ |
404 | if (__get_user(tstate, (&pregs->tstate)) || | 807 | ret = copy_regset_from_user(child, view, REGSET_GENERAL, |
405 | __get_user(tpc, (&pregs->tpc)) || | 808 | 32 * sizeof(u64), |
406 | __get_user(tnpc, (&pregs->tnpc)) || | 809 | 4 * sizeof(u64), |
407 | __get_user(y, (&pregs->y))) { | 810 | &pregs->tstate); |
408 | pt_error_return(regs, EFAULT); | ||
409 | goto out_tsk; | ||
410 | } | ||
411 | if ((task_thread_info(child)->flags & _TIF_32BIT) != 0) { | ||
412 | tpc &= 0xffffffff; | ||
413 | tnpc &= 0xffffffff; | ||
414 | } | ||
415 | tstate &= (TSTATE_ICC | TSTATE_XCC); | ||
416 | cregs->tstate &= ~(TSTATE_ICC | TSTATE_XCC); | ||
417 | cregs->tstate |= tstate; | ||
418 | if (!((tpc | tnpc) & 3)) { | ||
419 | cregs->tpc = tpc; | ||
420 | cregs->tnpc = tnpc; | ||
421 | } | ||
422 | cregs->y = y; | ||
423 | for (i = 1; i < 16; i++) { | ||
424 | if (__get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]))) { | ||
425 | pt_error_return(regs, EFAULT); | ||
426 | goto out_tsk; | ||
427 | } | ||
428 | } | 811 | } |
429 | pt_succ_return(regs, 0); | 812 | break; |
430 | goto out_tsk; | ||
431 | } | 813 | } |
432 | 814 | ||
433 | case PTRACE_GETFPREGS: { | 815 | case PTRACE_GETFPREGS: { |
@@ -443,20 +825,24 @@ asmlinkage void do_ptrace(struct pt_regs *regs) | |||
443 | } fpq[16]; | 825 | } fpq[16]; |
444 | }; | 826 | }; |
445 | struct fps __user *fps = (struct fps __user *) addr; | 827 | struct fps __user *fps = (struct fps __user *) addr; |
446 | unsigned long *fpregs = task_thread_info(child)->fpregs; | 828 | |
447 | 829 | ret = copy_regset_to_user(child, view, REGSET_FP, | |
448 | if (copy_to_user(&fps->regs[0], fpregs, | 830 | 0 * sizeof(u32), |
449 | (32 * sizeof(unsigned int))) || | 831 | 32 * sizeof(u32), |
450 | __put_user(task_thread_info(child)->xfsr[0], (&fps->fsr)) || | 832 | &fps->regs[0]); |
451 | __put_user(0, (&fps->fpqd)) || | 833 | if (!ret) |
452 | __put_user(0, (&fps->flags)) || | 834 | ret = copy_regset_to_user(child, view, REGSET_FP, |
453 | __put_user(0, (&fps->extra)) || | 835 | 33 * sizeof(u32), |
454 | clear_user(&fps->fpq[0], 32 * sizeof(unsigned int))) { | 836 | 1 * sizeof(u32), |
455 | pt_error_return(regs, EFAULT); | 837 | &fps->fsr); |
456 | goto out_tsk; | 838 | if (!ret) { |
839 | if (__put_user(0, &fps->flags) || | ||
840 | __put_user(0, &fps->extra) || | ||
841 | __put_user(0, &fps->fpqd) || | ||
842 | clear_user(&fps->fpq[0], 32 * sizeof(unsigned int))) | ||
843 | ret = -EFAULT; | ||
457 | } | 844 | } |
458 | pt_succ_return(regs, 0); | 845 | break; |
459 | goto out_tsk; | ||
460 | } | 846 | } |
461 | 847 | ||
462 | case PTRACE_GETFPREGS64: { | 848 | case PTRACE_GETFPREGS64: { |
@@ -465,16 +851,12 @@ asmlinkage void do_ptrace(struct pt_regs *regs) | |||
465 | unsigned long fsr; | 851 | unsigned long fsr; |
466 | }; | 852 | }; |
467 | struct fps __user *fps = (struct fps __user *) addr; | 853 | struct fps __user *fps = (struct fps __user *) addr; |
468 | unsigned long *fpregs = task_thread_info(child)->fpregs; | ||
469 | 854 | ||
470 | if (copy_to_user(&fps->regs[0], fpregs, | 855 | ret = copy_regset_to_user(child, view, REGSET_FP, |
471 | (64 * sizeof(unsigned int))) || | 856 | 0 * sizeof(u64), |
472 | __put_user(task_thread_info(child)->xfsr[0], (&fps->fsr))) { | 857 | 33 * sizeof(u64), |
473 | pt_error_return(regs, EFAULT); | 858 | fps); |
474 | goto out_tsk; | 859 | break; |
475 | } | ||
476 | pt_succ_return(regs, 0); | ||
477 | goto out_tsk; | ||
478 | } | 860 | } |
479 | 861 | ||
480 | case PTRACE_SETFPREGS: { | 862 | case PTRACE_SETFPREGS: { |
@@ -490,22 +872,17 @@ asmlinkage void do_ptrace(struct pt_regs *regs) | |||
490 | } fpq[16]; | 872 | } fpq[16]; |
491 | }; | 873 | }; |
492 | struct fps __user *fps = (struct fps __user *) addr; | 874 | struct fps __user *fps = (struct fps __user *) addr; |
493 | unsigned long *fpregs = task_thread_info(child)->fpregs; | 875 | |
494 | unsigned fsr; | 876 | ret = copy_regset_from_user(child, view, REGSET_FP, |
495 | 877 | 0 * sizeof(u32), | |
496 | if (copy_from_user(fpregs, &fps->regs[0], | 878 | 32 * sizeof(u32), |
497 | (32 * sizeof(unsigned int))) || | 879 | &fps->regs[0]); |
498 | __get_user(fsr, (&fps->fsr))) { | 880 | if (!ret) |
499 | pt_error_return(regs, EFAULT); | 881 | ret = copy_regset_from_user(child, view, REGSET_FP, |
500 | goto out_tsk; | 882 | 33 * sizeof(u32), |
501 | } | 883 | 1 * sizeof(u32), |
502 | task_thread_info(child)->xfsr[0] &= 0xffffffff00000000UL; | 884 | &fps->fsr); |
503 | task_thread_info(child)->xfsr[0] |= fsr; | 885 | break; |
504 | if (!(task_thread_info(child)->fpsaved[0] & FPRS_FEF)) | ||
505 | task_thread_info(child)->gsr[0] = 0; | ||
506 | task_thread_info(child)->fpsaved[0] |= (FPRS_FEF | FPRS_DL); | ||
507 | pt_succ_return(regs, 0); | ||
508 | goto out_tsk; | ||
509 | } | 886 | } |
510 | 887 | ||
511 | case PTRACE_SETFPREGS64: { | 888 | case PTRACE_SETFPREGS64: { |
@@ -514,134 +891,50 @@ asmlinkage void do_ptrace(struct pt_regs *regs) | |||
514 | unsigned long fsr; | 891 | unsigned long fsr; |
515 | }; | 892 | }; |
516 | struct fps __user *fps = (struct fps __user *) addr; | 893 | struct fps __user *fps = (struct fps __user *) addr; |
517 | unsigned long *fpregs = task_thread_info(child)->fpregs; | ||
518 | 894 | ||
519 | if (copy_from_user(fpregs, &fps->regs[0], | 895 | ret = copy_regset_to_user(child, view, REGSET_FP, |
520 | (64 * sizeof(unsigned int))) || | 896 | 0 * sizeof(u64), |
521 | __get_user(task_thread_info(child)->xfsr[0], (&fps->fsr))) { | 897 | 33 * sizeof(u64), |
522 | pt_error_return(regs, EFAULT); | 898 | fps); |
523 | goto out_tsk; | 899 | break; |
524 | } | ||
525 | if (!(task_thread_info(child)->fpsaved[0] & FPRS_FEF)) | ||
526 | task_thread_info(child)->gsr[0] = 0; | ||
527 | task_thread_info(child)->fpsaved[0] |= (FPRS_FEF | FPRS_DL | FPRS_DU); | ||
528 | pt_succ_return(regs, 0); | ||
529 | goto out_tsk; | ||
530 | } | 900 | } |
531 | 901 | ||
532 | case PTRACE_READTEXT: | 902 | case PTRACE_READTEXT: |
533 | case PTRACE_READDATA: { | 903 | case PTRACE_READDATA: |
534 | int res = ptrace_readdata(child, addr, | 904 | ret = ptrace_readdata(child, addr, |
535 | (char __user *)addr2, data); | 905 | (char __user *)addr2, data); |
536 | if (res == data) { | 906 | if (ret == data) |
537 | pt_succ_return(regs, 0); | 907 | ret = 0; |
538 | goto out_tsk; | 908 | else if (ret >= 0) |
539 | } | 909 | ret = -EIO; |
540 | if (res >= 0) | 910 | break; |
541 | res = -EIO; | ||
542 | pt_error_return(regs, -res); | ||
543 | goto out_tsk; | ||
544 | } | ||
545 | 911 | ||
546 | case PTRACE_WRITETEXT: | 912 | case PTRACE_WRITETEXT: |
547 | case PTRACE_WRITEDATA: { | 913 | case PTRACE_WRITEDATA: |
548 | int res = ptrace_writedata(child, (char __user *) addr2, | 914 | ret = ptrace_writedata(child, (char __user *) addr2, |
549 | addr, data); | 915 | addr, data); |
550 | if (res == data) { | 916 | if (ret == data) |
551 | pt_succ_return(regs, 0); | 917 | ret = 0; |
552 | goto out_tsk; | 918 | else if (ret >= 0) |
553 | } | 919 | ret = -EIO; |
554 | if (res >= 0) | 920 | break; |
555 | res = -EIO; | ||
556 | pt_error_return(regs, -res); | ||
557 | goto out_tsk; | ||
558 | } | ||
559 | case PTRACE_SYSCALL: /* continue and stop at (return from) syscall */ | ||
560 | addr = 1; | ||
561 | |||
562 | case PTRACE_CONT: { /* restart after signal. */ | ||
563 | if (!valid_signal(data)) { | ||
564 | pt_error_return(regs, EIO); | ||
565 | goto out_tsk; | ||
566 | } | ||
567 | |||
568 | if (request == PTRACE_SYSCALL) { | ||
569 | set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | ||
570 | } else { | ||
571 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | ||
572 | } | ||
573 | |||
574 | child->exit_code = data; | ||
575 | #ifdef DEBUG_PTRACE | ||
576 | printk("CONT: %s [%d]: set exit_code = %x %lx %lx\n", child->comm, | ||
577 | child->pid, child->exit_code, | ||
578 | task_pt_regs(child)->tpc, | ||
579 | task_pt_regs(child)->tnpc); | ||
580 | |||
581 | #endif | ||
582 | wake_up_process(child); | ||
583 | pt_succ_return(regs, 0); | ||
584 | goto out_tsk; | ||
585 | } | ||
586 | |||
587 | /* | ||
588 | * make the child exit. Best I can do is send it a sigkill. | ||
589 | * perhaps it should be put in the status that it wants to | ||
590 | * exit. | ||
591 | */ | ||
592 | case PTRACE_KILL: { | ||
593 | if (child->exit_state == EXIT_ZOMBIE) { /* already dead */ | ||
594 | pt_succ_return(regs, 0); | ||
595 | goto out_tsk; | ||
596 | } | ||
597 | child->exit_code = SIGKILL; | ||
598 | wake_up_process(child); | ||
599 | pt_succ_return(regs, 0); | ||
600 | goto out_tsk; | ||
601 | } | ||
602 | |||
603 | case PTRACE_SUNDETACH: { /* detach a process that was attached. */ | ||
604 | int error = ptrace_detach(child, data); | ||
605 | if (error) { | ||
606 | pt_error_return(regs, EIO); | ||
607 | goto out_tsk; | ||
608 | } | ||
609 | pt_succ_return(regs, 0); | ||
610 | goto out_tsk; | ||
611 | } | ||
612 | |||
613 | /* PTRACE_DUMPCORE unsupported... */ | ||
614 | 921 | ||
615 | case PTRACE_GETEVENTMSG: { | 922 | case PTRACE_GETEVENTMSG: { |
616 | int err; | ||
617 | |||
618 | if (test_thread_flag(TIF_32BIT)) | 923 | if (test_thread_flag(TIF_32BIT)) |
619 | err = put_user(child->ptrace_message, | 924 | ret = put_user(child->ptrace_message, |
620 | (unsigned int __user *) data); | 925 | (unsigned int __user *) data); |
621 | else | 926 | else |
622 | err = put_user(child->ptrace_message, | 927 | ret = put_user(child->ptrace_message, |
623 | (unsigned long __user *) data); | 928 | (unsigned long __user *) data); |
624 | if (err) | ||
625 | pt_error_return(regs, -err); | ||
626 | else | ||
627 | pt_succ_return(regs, 0); | ||
628 | break; | 929 | break; |
629 | } | 930 | } |
630 | 931 | ||
631 | default: { | 932 | default: |
632 | int err = ptrace_request(child, request, addr, data); | 933 | ret = ptrace_request(child, request, addr, data); |
633 | if (err) | 934 | break; |
634 | pt_error_return(regs, -err); | ||
635 | else | ||
636 | pt_succ_return(regs, 0); | ||
637 | goto out_tsk; | ||
638 | } | ||
639 | } | 935 | } |
640 | out_tsk: | 936 | |
641 | if (child) | 937 | return ret; |
642 | put_task_struct(child); | ||
643 | out: | ||
644 | unlock_kernel(); | ||
645 | } | 938 | } |
646 | 939 | ||
647 | asmlinkage void syscall_trace(struct pt_regs *regs, int syscall_exit_p) | 940 | asmlinkage void syscall_trace(struct pt_regs *regs, int syscall_exit_p) |
diff --git a/arch/sparc64/prom/init.c b/arch/sparc64/prom/init.c index 1c0db842a6f4..87e7c7ea0ee6 100644 --- a/arch/sparc64/prom/init.c +++ b/arch/sparc64/prom/init.c | |||
@@ -48,7 +48,10 @@ void __init prom_init(void *cif_handler, void *cif_stack) | |||
48 | prom_getstring(node, "version", prom_version, sizeof(prom_version)); | 48 | prom_getstring(node, "version", prom_version, sizeof(prom_version)); |
49 | 49 | ||
50 | prom_printf("\n"); | 50 | prom_printf("\n"); |
51 | } | ||
51 | 52 | ||
53 | void __init prom_init_report(void) | ||
54 | { | ||
52 | printk("PROMLIB: Sun IEEE Boot Prom '%s'\n", prom_version); | 55 | printk("PROMLIB: Sun IEEE Boot Prom '%s'\n", prom_version); |
53 | printk("PROMLIB: Root node compatible: %s\n", prom_root_compatible); | 56 | printk("PROMLIB: Root node compatible: %s\n", prom_root_compatible); |
54 | } | 57 | } |