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 | |
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')
-rw-r--r-- | arch/sparc/kernel/entry.S | 17 | ||||
-rw-r--r-- | arch/sparc/kernel/ptrace.c | 813 | ||||
-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 |
7 files changed, 1066 insertions, 922 deletions
diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S index 88d2cefd01be..c2eed8f71516 100644 --- a/arch/sparc/kernel/entry.S +++ b/arch/sparc/kernel/entry.S | |||
@@ -1224,23 +1224,6 @@ sys_nis_syscall: | |||
1224 | call c_sys_nis_syscall | 1224 | call c_sys_nis_syscall |
1225 | mov %l5, %o7 | 1225 | mov %l5, %o7 |
1226 | 1226 | ||
1227 | .align 4 | ||
1228 | .globl sys_ptrace | ||
1229 | sys_ptrace: | ||
1230 | call do_ptrace | ||
1231 | add %sp, STACKFRAME_SZ, %o0 | ||
1232 | |||
1233 | ld [%curptr + TI_FLAGS], %l5 | ||
1234 | andcc %l5, _TIF_SYSCALL_TRACE, %g0 | ||
1235 | be 1f | ||
1236 | nop | ||
1237 | |||
1238 | call syscall_trace | ||
1239 | nop | ||
1240 | |||
1241 | 1: | ||
1242 | RESTORE_ALL | ||
1243 | |||
1244 | .align 4 | 1227 | .align 4 |
1245 | .globl sys_execve | 1228 | .globl sys_execve |
1246 | sys_execve: | 1229 | sys_execve: |
diff --git a/arch/sparc/kernel/ptrace.c b/arch/sparc/kernel/ptrace.c index 7452269bba2a..5b54f11f4e59 100644 --- a/arch/sparc/kernel/ptrace.c +++ b/arch/sparc/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 | * | 4 | * |
5 | * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson, | 5 | * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson, |
6 | * and David Mosberger. | 6 | * and David Mosberger. |
@@ -19,389 +19,343 @@ | |||
19 | #include <linux/smp_lock.h> | 19 | #include <linux/smp_lock.h> |
20 | #include <linux/security.h> | 20 | #include <linux/security.h> |
21 | #include <linux/signal.h> | 21 | #include <linux/signal.h> |
22 | #include <linux/regset.h> | ||
23 | #include <linux/elf.h> | ||
22 | 24 | ||
23 | #include <asm/pgtable.h> | 25 | #include <asm/pgtable.h> |
24 | #include <asm/system.h> | 26 | #include <asm/system.h> |
25 | #include <asm/uaccess.h> | 27 | #include <asm/uaccess.h> |
26 | 28 | ||
27 | #define MAGIC_CONSTANT 0x80000000 | 29 | /* #define ALLOW_INIT_TRACING */ |
28 | |||
29 | 30 | ||
30 | /* Returning from ptrace is a bit tricky because the syscall return | 31 | /* |
31 | * low level code assumes any value returned which is negative and | 32 | * Called by kernel/ptrace.c when detaching.. |
32 | * is a valid errno will mean setting the condition codes to indicate | 33 | * |
33 | * an error return. This doesn't work, so we have this hook. | 34 | * Make sure single step bits etc are not set. |
34 | */ | 35 | */ |
35 | static inline void pt_error_return(struct pt_regs *regs, unsigned long error) | 36 | void ptrace_disable(struct task_struct *child) |
36 | { | 37 | { |
37 | regs->u_regs[UREG_I0] = error; | 38 | /* nothing to do */ |
38 | regs->psr |= PSR_C; | ||
39 | regs->pc = regs->npc; | ||
40 | regs->npc += 4; | ||
41 | } | 39 | } |
42 | 40 | ||
43 | static inline void pt_succ_return(struct pt_regs *regs, unsigned long value) | 41 | enum sparc_regset { |
44 | { | 42 | REGSET_GENERAL, |
45 | regs->u_regs[UREG_I0] = value; | 43 | REGSET_FP, |
46 | regs->psr &= ~PSR_C; | 44 | }; |
47 | regs->pc = regs->npc; | ||
48 | regs->npc += 4; | ||
49 | } | ||
50 | 45 | ||
51 | static void | 46 | static int genregs32_get(struct task_struct *target, |
52 | pt_succ_return_linux(struct pt_regs *regs, unsigned long value, long __user *addr) | 47 | const struct user_regset *regset, |
48 | unsigned int pos, unsigned int count, | ||
49 | void *kbuf, void __user *ubuf) | ||
53 | { | 50 | { |
54 | if (put_user(value, addr)) { | 51 | const struct pt_regs *regs = target->thread.kregs; |
55 | pt_error_return(regs, EFAULT); | 52 | unsigned long __user *reg_window; |
56 | return; | 53 | unsigned long *k = kbuf; |
54 | unsigned long __user *u = ubuf; | ||
55 | unsigned long reg; | ||
56 | |||
57 | if (target == current) | ||
58 | flush_user_windows(); | ||
59 | |||
60 | pos /= sizeof(reg); | ||
61 | count /= sizeof(reg); | ||
62 | |||
63 | if (kbuf) { | ||
64 | for (; count > 0 && pos < 16; count--) | ||
65 | *k++ = regs->u_regs[pos++]; | ||
66 | |||
67 | reg_window = (unsigned long __user *) regs->u_regs[UREG_I6]; | ||
68 | for (; count > 0 && pos < 32; count--) { | ||
69 | if (get_user(*k++, ®_window[pos++])) | ||
70 | return -EFAULT; | ||
71 | } | ||
72 | } else { | ||
73 | for (; count > 0 && pos < 16; count--) { | ||
74 | if (put_user(regs->u_regs[pos++], u++)) | ||
75 | return -EFAULT; | ||
76 | } | ||
77 | |||
78 | reg_window = (unsigned long __user *) regs->u_regs[UREG_I6]; | ||
79 | for (; count > 0 && pos < 32; count--) { | ||
80 | if (get_user(reg, ®_window[pos++]) || | ||
81 | put_user(reg, u++)) | ||
82 | return -EFAULT; | ||
83 | } | ||
57 | } | 84 | } |
58 | regs->u_regs[UREG_I0] = 0; | 85 | while (count > 0) { |
59 | regs->psr &= ~PSR_C; | 86 | switch (pos) { |
60 | regs->pc = regs->npc; | 87 | case 32: /* PSR */ |
61 | regs->npc += 4; | 88 | reg = regs->psr; |
62 | } | 89 | break; |
90 | case 33: /* PC */ | ||
91 | reg = regs->pc; | ||
92 | break; | ||
93 | case 34: /* NPC */ | ||
94 | reg = regs->npc; | ||
95 | break; | ||
96 | case 35: /* Y */ | ||
97 | reg = regs->y; | ||
98 | break; | ||
99 | case 36: /* WIM */ | ||
100 | case 37: /* TBR */ | ||
101 | reg = 0; | ||
102 | break; | ||
103 | default: | ||
104 | goto finish; | ||
105 | } | ||
63 | 106 | ||
64 | static void | 107 | if (kbuf) |
65 | pt_os_succ_return (struct pt_regs *regs, unsigned long val, long __user *addr) | 108 | *k++ = reg; |
66 | { | 109 | else if (put_user(reg, u++)) |
67 | if (current->personality == PER_SUNOS) | 110 | return -EFAULT; |
68 | pt_succ_return (regs, val); | 111 | pos++; |
69 | else | 112 | count--; |
70 | pt_succ_return_linux (regs, val, addr); | 113 | } |
114 | finish: | ||
115 | pos *= sizeof(reg); | ||
116 | count *= sizeof(reg); | ||
117 | |||
118 | return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | ||
119 | 38 * sizeof(reg), -1); | ||
71 | } | 120 | } |
72 | 121 | ||
73 | /* Fuck me gently with a chainsaw... */ | 122 | static int genregs32_set(struct task_struct *target, |
74 | static inline void read_sunos_user(struct pt_regs *regs, unsigned long offset, | 123 | const struct user_regset *regset, |
75 | struct task_struct *tsk, long __user *addr) | 124 | unsigned int pos, unsigned int count, |
125 | const void *kbuf, const void __user *ubuf) | ||
76 | { | 126 | { |
77 | struct pt_regs *cregs = tsk->thread.kregs; | 127 | struct pt_regs *regs = target->thread.kregs; |
78 | struct thread_info *t = task_thread_info(tsk); | 128 | unsigned long __user *reg_window; |
79 | int v; | 129 | const unsigned long *k = kbuf; |
80 | 130 | const unsigned long __user *u = ubuf; | |
81 | if(offset >= 1024) | 131 | unsigned long reg; |
82 | offset -= 1024; /* whee... */ | 132 | |
83 | if(offset & ((sizeof(unsigned long) - 1))) { | 133 | if (target == current) |
84 | pt_error_return(regs, EIO); | 134 | flush_user_windows(); |
85 | return; | 135 | |
86 | } | 136 | pos /= sizeof(reg); |
87 | if(offset >= 16 && offset < 784) { | 137 | count /= sizeof(reg); |
88 | offset -= 16; offset >>= 2; | 138 | |
89 | pt_os_succ_return(regs, *(((unsigned long *)(&t->reg_window[0]))+offset), addr); | 139 | if (kbuf) { |
90 | return; | 140 | for (; count > 0 && pos < 16; count--) |
91 | } | 141 | regs->u_regs[pos++] = *k++; |
92 | if(offset >= 784 && offset < 832) { | 142 | |
93 | offset -= 784; offset >>= 2; | 143 | reg_window = (unsigned long __user *) regs->u_regs[UREG_I6]; |
94 | pt_os_succ_return(regs, *(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset), addr); | 144 | for (; count > 0 && pos < 32; count--) { |
95 | return; | 145 | if (put_user(*k++, ®_window[pos++])) |
96 | } | 146 | return -EFAULT; |
97 | switch(offset) { | 147 | } |
98 | case 0: | 148 | } else { |
99 | v = t->ksp; | 149 | for (; count > 0 && pos < 16; count--) { |
100 | break; | 150 | if (get_user(reg, u++)) |
101 | case 4: | 151 | return -EFAULT; |
102 | v = t->kpc; | 152 | regs->u_regs[pos++] = reg; |
103 | break; | 153 | } |
104 | case 8: | ||
105 | v = t->kpsr; | ||
106 | break; | ||
107 | case 12: | ||
108 | v = t->uwinmask; | ||
109 | break; | ||
110 | case 832: | ||
111 | v = t->w_saved; | ||
112 | break; | ||
113 | case 896: | ||
114 | v = cregs->u_regs[UREG_I0]; | ||
115 | break; | ||
116 | case 900: | ||
117 | v = cregs->u_regs[UREG_I1]; | ||
118 | break; | ||
119 | case 904: | ||
120 | v = cregs->u_regs[UREG_I2]; | ||
121 | break; | ||
122 | case 908: | ||
123 | v = cregs->u_regs[UREG_I3]; | ||
124 | break; | ||
125 | case 912: | ||
126 | v = cregs->u_regs[UREG_I4]; | ||
127 | break; | ||
128 | case 916: | ||
129 | v = cregs->u_regs[UREG_I5]; | ||
130 | break; | ||
131 | case 920: | ||
132 | v = cregs->u_regs[UREG_I6]; | ||
133 | break; | ||
134 | case 924: | ||
135 | if(tsk->thread.flags & MAGIC_CONSTANT) | ||
136 | v = cregs->u_regs[UREG_G1]; | ||
137 | else | ||
138 | v = 0; | ||
139 | break; | ||
140 | case 940: | ||
141 | v = cregs->u_regs[UREG_I0]; | ||
142 | break; | ||
143 | case 944: | ||
144 | v = cregs->u_regs[UREG_I1]; | ||
145 | break; | ||
146 | 154 | ||
147 | case 948: | 155 | reg_window = (unsigned long __user *) regs->u_regs[UREG_I6]; |
148 | /* Isn't binary compatibility _fun_??? */ | 156 | for (; count > 0 && pos < 32; count--) { |
149 | if(cregs->psr & PSR_C) | 157 | if (get_user(reg, u++) || |
150 | v = cregs->u_regs[UREG_I0] << 24; | 158 | put_user(reg, ®_window[pos++])) |
151 | else | 159 | return -EFAULT; |
152 | v = 0; | 160 | } |
153 | break; | 161 | } |
162 | while (count > 0) { | ||
163 | unsigned long psr; | ||
164 | |||
165 | if (kbuf) | ||
166 | reg = *k++; | ||
167 | else if (get_user(reg, u++)) | ||
168 | return -EFAULT; | ||
169 | |||
170 | switch (pos) { | ||
171 | case 32: /* PSR */ | ||
172 | psr = regs->psr; | ||
173 | psr &= ~PSR_ICC; | ||
174 | psr |= (reg & PSR_ICC); | ||
175 | regs->psr = psr; | ||
176 | break; | ||
177 | case 33: /* PC */ | ||
178 | regs->pc = reg; | ||
179 | break; | ||
180 | case 34: /* NPC */ | ||
181 | regs->npc = reg; | ||
182 | break; | ||
183 | case 35: /* Y */ | ||
184 | regs->y = reg; | ||
185 | break; | ||
186 | case 36: /* WIM */ | ||
187 | case 37: /* TBR */ | ||
188 | break; | ||
189 | default: | ||
190 | goto finish; | ||
191 | } | ||
154 | 192 | ||
155 | /* Rest of them are completely unsupported. */ | 193 | pos++; |
156 | default: | 194 | count--; |
157 | printk("%s [%d]: Wants to read user offset %ld\n", | ||
158 | current->comm, task_pid_nr(current), offset); | ||
159 | pt_error_return(regs, EIO); | ||
160 | return; | ||
161 | } | 195 | } |
162 | if (current->personality == PER_SUNOS) | 196 | finish: |
163 | pt_succ_return (regs, v); | 197 | pos *= sizeof(reg); |
164 | else | 198 | count *= sizeof(reg); |
165 | pt_succ_return_linux (regs, v, addr); | 199 | |
166 | return; | 200 | return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, |
201 | 38 * sizeof(reg), -1); | ||
167 | } | 202 | } |
168 | 203 | ||
169 | static inline void write_sunos_user(struct pt_regs *regs, unsigned long offset, | 204 | static int fpregs32_get(struct task_struct *target, |
170 | struct task_struct *tsk) | 205 | const struct user_regset *regset, |
206 | unsigned int pos, unsigned int count, | ||
207 | void *kbuf, void __user *ubuf) | ||
171 | { | 208 | { |
172 | struct pt_regs *cregs = tsk->thread.kregs; | 209 | const unsigned long *fpregs = target->thread.float_regs; |
173 | struct thread_info *t = task_thread_info(tsk); | 210 | int ret = 0; |
174 | unsigned long value = regs->u_regs[UREG_I3]; | ||
175 | |||
176 | if(offset >= 1024) | ||
177 | offset -= 1024; /* whee... */ | ||
178 | if(offset & ((sizeof(unsigned long) - 1))) | ||
179 | goto failure; | ||
180 | if(offset >= 16 && offset < 784) { | ||
181 | offset -= 16; offset >>= 2; | ||
182 | *(((unsigned long *)(&t->reg_window[0]))+offset) = value; | ||
183 | goto success; | ||
184 | } | ||
185 | if(offset >= 784 && offset < 832) { | ||
186 | offset -= 784; offset >>= 2; | ||
187 | *(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset) = value; | ||
188 | goto success; | ||
189 | } | ||
190 | switch(offset) { | ||
191 | case 896: | ||
192 | cregs->u_regs[UREG_I0] = value; | ||
193 | break; | ||
194 | case 900: | ||
195 | cregs->u_regs[UREG_I1] = value; | ||
196 | break; | ||
197 | case 904: | ||
198 | cregs->u_regs[UREG_I2] = value; | ||
199 | break; | ||
200 | case 908: | ||
201 | cregs->u_regs[UREG_I3] = value; | ||
202 | break; | ||
203 | case 912: | ||
204 | cregs->u_regs[UREG_I4] = value; | ||
205 | break; | ||
206 | case 916: | ||
207 | cregs->u_regs[UREG_I5] = value; | ||
208 | break; | ||
209 | case 920: | ||
210 | cregs->u_regs[UREG_I6] = value; | ||
211 | break; | ||
212 | case 924: | ||
213 | cregs->u_regs[UREG_I7] = value; | ||
214 | break; | ||
215 | case 940: | ||
216 | cregs->u_regs[UREG_I0] = value; | ||
217 | break; | ||
218 | case 944: | ||
219 | cregs->u_regs[UREG_I1] = value; | ||
220 | break; | ||
221 | 211 | ||
222 | /* Rest of them are completely unsupported or "no-touch". */ | 212 | #if 0 |
223 | default: | 213 | if (target == current) |
224 | printk("%s [%d]: Wants to write user offset %ld\n", | 214 | save_and_clear_fpu(); |
225 | current->comm, task_pid_nr(current), offset); | 215 | #endif |
226 | goto failure; | 216 | |
217 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
218 | fpregs, | ||
219 | 0, 32 * sizeof(u32)); | ||
220 | |||
221 | if (!ret) | ||
222 | ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | ||
223 | 32 * sizeof(u32), | ||
224 | 33 * sizeof(u32)); | ||
225 | if (!ret) | ||
226 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
227 | &target->thread.fsr, | ||
228 | 33 * sizeof(u32), | ||
229 | 34 * sizeof(u32)); | ||
230 | |||
231 | if (!ret) { | ||
232 | unsigned long val; | ||
233 | |||
234 | val = (1 << 8) | (8 << 16); | ||
235 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
236 | &val, | ||
237 | 34 * sizeof(u32), | ||
238 | 35 * sizeof(u32)); | ||
227 | } | 239 | } |
228 | success: | ||
229 | pt_succ_return(regs, 0); | ||
230 | return; | ||
231 | failure: | ||
232 | pt_error_return(regs, EIO); | ||
233 | return; | ||
234 | } | ||
235 | 240 | ||
236 | /* #define ALLOW_INIT_TRACING */ | 241 | if (!ret) |
237 | /* #define DEBUG_PTRACE */ | 242 | ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, |
238 | 243 | 35 * sizeof(u32), -1); | |
239 | #ifdef DEBUG_PTRACE | ||
240 | char *pt_rq [] = { | ||
241 | /* 0 */ "TRACEME", "PEEKTEXT", "PEEKDATA", "PEEKUSR", | ||
242 | /* 4 */ "POKETEXT", "POKEDATA", "POKEUSR", "CONT", | ||
243 | /* 8 */ "KILL", "SINGLESTEP", "SUNATTACH", "SUNDETACH", | ||
244 | /* 12 */ "GETREGS", "SETREGS", "GETFPREGS", "SETFPREGS", | ||
245 | /* 16 */ "READDATA", "WRITEDATA", "READTEXT", "WRITETEXT", | ||
246 | /* 20 */ "GETFPAREGS", "SETFPAREGS", "unknown", "unknown", | ||
247 | /* 24 */ "SYSCALL", "" | ||
248 | }; | ||
249 | #endif | ||
250 | 244 | ||
251 | /* | 245 | return ret; |
252 | * Called by kernel/ptrace.c when detaching.. | ||
253 | * | ||
254 | * Make sure single step bits etc are not set. | ||
255 | */ | ||
256 | void ptrace_disable(struct task_struct *child) | ||
257 | { | ||
258 | /* nothing to do */ | ||
259 | } | 246 | } |
260 | 247 | ||
261 | asmlinkage void do_ptrace(struct pt_regs *regs) | 248 | static int fpregs32_set(struct task_struct *target, |
249 | const struct user_regset *regset, | ||
250 | unsigned int pos, unsigned int count, | ||
251 | const void *kbuf, const void __user *ubuf) | ||
262 | { | 252 | { |
263 | unsigned long request = regs->u_regs[UREG_I0]; | 253 | unsigned long *fpregs = target->thread.float_regs; |
264 | unsigned long pid = regs->u_regs[UREG_I1]; | ||
265 | unsigned long addr = regs->u_regs[UREG_I2]; | ||
266 | unsigned long data = regs->u_regs[UREG_I3]; | ||
267 | unsigned long addr2 = regs->u_regs[UREG_I4]; | ||
268 | struct task_struct *child; | ||
269 | int ret; | 254 | int ret; |
270 | 255 | ||
271 | lock_kernel(); | 256 | #if 0 |
272 | #ifdef DEBUG_PTRACE | 257 | if (target == current) |
273 | { | 258 | save_and_clear_fpu(); |
274 | char *s; | ||
275 | |||
276 | if ((request >= 0) && (request <= 24)) | ||
277 | s = pt_rq [request]; | ||
278 | else | ||
279 | s = "unknown"; | ||
280 | |||
281 | if (request == PTRACE_POKEDATA && data == 0x91d02001){ | ||
282 | printk ("do_ptrace: breakpoint pid=%d, addr=%08lx addr2=%08lx\n", | ||
283 | pid, addr, addr2); | ||
284 | } else | ||
285 | printk("do_ptrace: rq=%s(%d) pid=%d addr=%08lx data=%08lx addr2=%08lx\n", | ||
286 | s, (int) request, (int) pid, addr, data, addr2); | ||
287 | } | ||
288 | #endif | 259 | #endif |
289 | 260 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
290 | if (request == PTRACE_TRACEME) { | 261 | fpregs, |
291 | ret = ptrace_traceme(); | 262 | 0, 32 * sizeof(u32)); |
292 | if (ret < 0) | 263 | if (!ret) |
293 | pt_error_return(regs, -ret); | 264 | user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, |
294 | else | 265 | 32 * sizeof(u32), |
295 | pt_succ_return(regs, 0); | 266 | 33 * sizeof(u32)); |
296 | goto out; | 267 | if (!ret && count > 0) { |
268 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
269 | &target->thread.fsr, | ||
270 | 33 * sizeof(u32), | ||
271 | 34 * sizeof(u32)); | ||
297 | } | 272 | } |
298 | 273 | ||
299 | child = ptrace_get_task_struct(pid); | 274 | if (!ret) |
300 | if (IS_ERR(child)) { | 275 | ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, |
301 | ret = PTR_ERR(child); | 276 | 34 * sizeof(u32), -1); |
302 | pt_error_return(regs, -ret); | 277 | return ret; |
303 | goto out; | 278 | } |
304 | } | ||
305 | 279 | ||
306 | if ((current->personality == PER_SUNOS && request == PTRACE_SUNATTACH) | 280 | static const struct user_regset sparc32_regsets[] = { |
307 | || (current->personality != PER_SUNOS && request == PTRACE_ATTACH)) { | 281 | /* Format is: |
308 | if (ptrace_attach(child)) { | 282 | * G0 --> G7 |
309 | pt_error_return(regs, EPERM); | 283 | * O0 --> O7 |
310 | goto out_tsk; | 284 | * L0 --> L7 |
311 | } | 285 | * I0 --> I7 |
312 | pt_succ_return(regs, 0); | 286 | * PSR, PC, nPC, Y, WIM, TBR |
313 | goto out_tsk; | 287 | */ |
314 | } | 288 | [REGSET_GENERAL] = { |
289 | .core_note_type = NT_PRSTATUS, | ||
290 | .n = 38 * sizeof(u32), | ||
291 | .size = sizeof(u32), .align = sizeof(u32), | ||
292 | .get = genregs32_get, .set = genregs32_set | ||
293 | }, | ||
294 | /* Format is: | ||
295 | * F0 --> F31 | ||
296 | * empty 32-bit word | ||
297 | * FSR (32--bit word) | ||
298 | * FPU QUEUE COUNT (8-bit char) | ||
299 | * FPU QUEUE ENTRYSIZE (8-bit char) | ||
300 | * FPU ENABLED (8-bit char) | ||
301 | * empty 8-bit char | ||
302 | * FPU QUEUE (64 32-bit ints) | ||
303 | */ | ||
304 | [REGSET_FP] = { | ||
305 | .core_note_type = NT_PRFPREG, | ||
306 | .n = 99 * sizeof(u32), | ||
307 | .size = sizeof(u32), .align = sizeof(u32), | ||
308 | .get = fpregs32_get, .set = fpregs32_set | ||
309 | }, | ||
310 | }; | ||
315 | 311 | ||
316 | ret = ptrace_check_attach(child, request == PTRACE_KILL); | 312 | static const struct user_regset_view user_sparc32_view = { |
317 | if (ret < 0) { | 313 | .name = "sparc", .e_machine = EM_SPARC, |
318 | pt_error_return(regs, -ret); | 314 | .regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets) |
319 | goto out_tsk; | 315 | }; |
320 | } | ||
321 | 316 | ||
322 | switch(request) { | 317 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) |
323 | case PTRACE_PEEKTEXT: /* read word at location addr. */ | 318 | { |
324 | case PTRACE_PEEKDATA: { | 319 | return &user_sparc32_view; |
325 | unsigned long tmp; | 320 | } |
326 | |||
327 | if (access_process_vm(child, addr, | ||
328 | &tmp, sizeof(tmp), 0) == sizeof(tmp)) | ||
329 | pt_os_succ_return(regs, tmp, (long __user *)data); | ||
330 | else | ||
331 | pt_error_return(regs, EIO); | ||
332 | goto out_tsk; | ||
333 | } | ||
334 | 321 | ||
335 | case PTRACE_PEEKUSR: | 322 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) |
336 | read_sunos_user(regs, addr, child, (long __user *) data); | 323 | { |
337 | goto out_tsk; | 324 | unsigned long addr2 = current->thread.kregs->u_regs[UREG_I4]; |
338 | 325 | const struct user_regset_view *view; | |
339 | case PTRACE_POKEUSR: | 326 | int ret; |
340 | write_sunos_user(regs, addr, child); | 327 | |
341 | goto out_tsk; | 328 | view = task_user_regset_view(child); |
342 | |||
343 | case PTRACE_POKETEXT: /* write the word at location addr. */ | ||
344 | case PTRACE_POKEDATA: { | ||
345 | if (access_process_vm(child, addr, | ||
346 | &data, sizeof(data), 1) == sizeof(data)) | ||
347 | pt_succ_return(regs, 0); | ||
348 | else | ||
349 | pt_error_return(regs, EIO); | ||
350 | goto out_tsk; | ||
351 | } | ||
352 | 329 | ||
330 | switch(request) { | ||
353 | case PTRACE_GETREGS: { | 331 | case PTRACE_GETREGS: { |
354 | struct pt_regs __user *pregs = (struct pt_regs __user *) addr; | 332 | struct pt_regs __user *pregs = (struct pt_regs __user *) addr; |
355 | struct pt_regs *cregs = child->thread.kregs; | ||
356 | int rval; | ||
357 | 333 | ||
358 | if (!access_ok(VERIFY_WRITE, pregs, sizeof(struct pt_regs))) { | 334 | ret = copy_regset_to_user(child, view, REGSET_GENERAL, |
359 | rval = -EFAULT; | 335 | 32 * sizeof(u32), |
360 | pt_error_return(regs, -rval); | 336 | 4 * sizeof(u32), |
361 | goto out_tsk; | 337 | &pregs->psr); |
362 | } | 338 | if (!ret) |
363 | __put_user(cregs->psr, (&pregs->psr)); | 339 | copy_regset_to_user(child, view, REGSET_GENERAL, |
364 | __put_user(cregs->pc, (&pregs->pc)); | 340 | 1 * sizeof(u32), |
365 | __put_user(cregs->npc, (&pregs->npc)); | 341 | 15 * sizeof(u32), |
366 | __put_user(cregs->y, (&pregs->y)); | 342 | &pregs->u_regs[0]); |
367 | for(rval = 1; rval < 16; rval++) | 343 | break; |
368 | __put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1])); | ||
369 | pt_succ_return(regs, 0); | ||
370 | #ifdef DEBUG_PTRACE | ||
371 | printk ("PC=%x nPC=%x o7=%x\n", cregs->pc, cregs->npc, cregs->u_regs [15]); | ||
372 | #endif | ||
373 | goto out_tsk; | ||
374 | } | 344 | } |
375 | 345 | ||
376 | case PTRACE_SETREGS: { | 346 | case PTRACE_SETREGS: { |
377 | struct pt_regs __user *pregs = (struct pt_regs __user *) addr; | 347 | struct pt_regs __user *pregs = (struct pt_regs __user *) addr; |
378 | struct pt_regs *cregs = child->thread.kregs; | 348 | |
379 | unsigned long psr, pc, npc, y; | 349 | ret = copy_regset_from_user(child, view, REGSET_GENERAL, |
380 | int i; | 350 | 32 * sizeof(u32), |
381 | 351 | 4 * sizeof(u32), | |
382 | /* Must be careful, tracing process can only set certain | 352 | &pregs->psr); |
383 | * bits in the psr. | 353 | if (!ret) |
384 | */ | 354 | copy_regset_from_user(child, view, REGSET_GENERAL, |
385 | if (!access_ok(VERIFY_READ, pregs, sizeof(struct pt_regs))) { | 355 | 1 * sizeof(u32), |
386 | pt_error_return(regs, EFAULT); | 356 | 15 * sizeof(u32), |
387 | goto out_tsk; | 357 | &pregs->u_regs[0]); |
388 | } | 358 | break; |
389 | __get_user(psr, (&pregs->psr)); | ||
390 | __get_user(pc, (&pregs->pc)); | ||
391 | __get_user(npc, (&pregs->npc)); | ||
392 | __get_user(y, (&pregs->y)); | ||
393 | psr &= PSR_ICC; | ||
394 | cregs->psr &= ~PSR_ICC; | ||
395 | cregs->psr |= psr; | ||
396 | if (!((pc | npc) & 3)) { | ||
397 | cregs->pc = pc; | ||
398 | cregs->npc =npc; | ||
399 | } | ||
400 | cregs->y = y; | ||
401 | for(i = 1; i < 16; i++) | ||
402 | __get_user(cregs->u_regs[i], (&pregs->u_regs[i-1])); | ||
403 | pt_succ_return(regs, 0); | ||
404 | goto out_tsk; | ||
405 | } | 359 | } |
406 | 360 | ||
407 | case PTRACE_GETFPREGS: { | 361 | case PTRACE_GETFPREGS: { |
@@ -417,26 +371,25 @@ asmlinkage void do_ptrace(struct pt_regs *regs) | |||
417 | } fpq[16]; | 371 | } fpq[16]; |
418 | }; | 372 | }; |
419 | struct fps __user *fps = (struct fps __user *) addr; | 373 | struct fps __user *fps = (struct fps __user *) addr; |
420 | int i; | ||
421 | 374 | ||
422 | if (!access_ok(VERIFY_WRITE, fps, sizeof(struct fps))) { | 375 | ret = copy_regset_to_user(child, view, REGSET_FP, |
423 | i = -EFAULT; | 376 | 0 * sizeof(u32), |
424 | pt_error_return(regs, -i); | 377 | 32 * sizeof(u32), |
425 | goto out_tsk; | 378 | &fps->regs[0]); |
426 | } | 379 | if (!ret) |
427 | for(i = 0; i < 32; i++) | 380 | ret = copy_regset_to_user(child, view, REGSET_FP, |
428 | __put_user(child->thread.float_regs[i], (&fps->regs[i])); | 381 | 33 * sizeof(u32), |
429 | __put_user(child->thread.fsr, (&fps->fsr)); | 382 | 1 * sizeof(u32), |
430 | __put_user(child->thread.fpqdepth, (&fps->fpqd)); | 383 | &fps->fsr); |
431 | __put_user(0, (&fps->flags)); | 384 | |
432 | __put_user(0, (&fps->extra)); | 385 | if (!ret) { |
433 | for(i = 0; i < 16; i++) { | 386 | if (__put_user(0, &fps->fpqd) || |
434 | __put_user(child->thread.fpqueue[i].insn_addr, | 387 | __put_user(0, &fps->flags) || |
435 | (&fps->fpq[i].insnaddr)); | 388 | __put_user(0, &fps->extra) || |
436 | __put_user(child->thread.fpqueue[i].insn, (&fps->fpq[i].insn)); | 389 | clear_user(fps->fpq, sizeof(fps->fpq))) |
390 | ret = -EFAULT; | ||
437 | } | 391 | } |
438 | pt_succ_return(regs, 0); | 392 | break; |
439 | goto out_tsk; | ||
440 | } | 393 | } |
441 | 394 | ||
442 | case PTRACE_SETFPREGS: { | 395 | case PTRACE_SETFPREGS: { |
@@ -452,137 +405,55 @@ asmlinkage void do_ptrace(struct pt_regs *regs) | |||
452 | } fpq[16]; | 405 | } fpq[16]; |
453 | }; | 406 | }; |
454 | struct fps __user *fps = (struct fps __user *) addr; | 407 | struct fps __user *fps = (struct fps __user *) addr; |
455 | int i; | ||
456 | 408 | ||
457 | if (!access_ok(VERIFY_READ, fps, sizeof(struct fps))) { | 409 | ret = copy_regset_from_user(child, view, REGSET_FP, |
458 | i = -EFAULT; | 410 | 0 * sizeof(u32), |
459 | pt_error_return(regs, -i); | 411 | 32 * sizeof(u32), |
460 | goto out_tsk; | 412 | &fps->regs[0]); |
461 | } | 413 | if (!ret) |
462 | copy_from_user(&child->thread.float_regs[0], &fps->regs[0], (32 * sizeof(unsigned long))); | 414 | ret = copy_regset_from_user(child, view, REGSET_FP, |
463 | __get_user(child->thread.fsr, (&fps->fsr)); | 415 | 33 * sizeof(u32), |
464 | __get_user(child->thread.fpqdepth, (&fps->fpqd)); | 416 | 1 * sizeof(u32), |
465 | for(i = 0; i < 16; i++) { | 417 | &fps->fsr); |
466 | __get_user(child->thread.fpqueue[i].insn_addr, | 418 | break; |
467 | (&fps->fpq[i].insnaddr)); | ||
468 | __get_user(child->thread.fpqueue[i].insn, (&fps->fpq[i].insn)); | ||
469 | } | ||
470 | pt_succ_return(regs, 0); | ||
471 | goto out_tsk; | ||
472 | } | 419 | } |
473 | 420 | ||
474 | case PTRACE_READTEXT: | 421 | case PTRACE_READTEXT: |
475 | case PTRACE_READDATA: { | 422 | case PTRACE_READDATA: |
476 | int res = ptrace_readdata(child, addr, | 423 | ret = ptrace_readdata(child, addr, |
477 | (void __user *) addr2, data); | 424 | (void __user *) addr2, data); |
478 | 425 | ||
479 | if (res == data) { | 426 | if (ret == data) |
480 | pt_succ_return(regs, 0); | 427 | ret = 0; |
481 | goto out_tsk; | 428 | else if (ret >= 0) |
482 | } | 429 | ret = -EIO; |
483 | /* Partial read is an IO failure */ | 430 | break; |
484 | if (res >= 0) | ||
485 | res = -EIO; | ||
486 | pt_error_return(regs, -res); | ||
487 | goto out_tsk; | ||
488 | } | ||
489 | 431 | ||
490 | case PTRACE_WRITETEXT: | 432 | case PTRACE_WRITETEXT: |
491 | case PTRACE_WRITEDATA: { | 433 | case PTRACE_WRITEDATA: |
492 | int res = ptrace_writedata(child, (void __user *) addr2, | 434 | ret = ptrace_writedata(child, (void __user *) addr2, |
493 | addr, data); | 435 | addr, data); |
494 | 436 | ||
495 | if (res == data) { | 437 | if (ret == data) |
496 | pt_succ_return(regs, 0); | 438 | ret = 0; |
497 | goto out_tsk; | 439 | else if (ret >= 0) |
498 | } | 440 | ret = -EIO; |
499 | /* Partial write is an IO failure */ | 441 | break; |
500 | if (res >= 0) | ||
501 | res = -EIO; | ||
502 | pt_error_return(regs, -res); | ||
503 | goto out_tsk; | ||
504 | } | ||
505 | |||
506 | case PTRACE_SYSCALL: /* continue and stop at (return from) syscall */ | ||
507 | addr = 1; | ||
508 | |||
509 | case PTRACE_CONT: { /* restart after signal. */ | ||
510 | if (!valid_signal(data)) { | ||
511 | pt_error_return(regs, EIO); | ||
512 | goto out_tsk; | ||
513 | } | ||
514 | |||
515 | if (request == PTRACE_SYSCALL) | ||
516 | set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | ||
517 | else | ||
518 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | ||
519 | |||
520 | child->exit_code = data; | ||
521 | #ifdef DEBUG_PTRACE | ||
522 | printk("CONT: %s [%d]: set exit_code = %x %lx %lx\n", | ||
523 | child->comm, child->pid, child->exit_code, | ||
524 | child->thread.kregs->pc, | ||
525 | child->thread.kregs->npc); | ||
526 | #endif | ||
527 | wake_up_process(child); | ||
528 | pt_succ_return(regs, 0); | ||
529 | goto out_tsk; | ||
530 | } | ||
531 | |||
532 | /* | ||
533 | * make the child exit. Best I can do is send it a sigkill. | ||
534 | * perhaps it should be put in the status that it wants to | ||
535 | * exit. | ||
536 | */ | ||
537 | case PTRACE_KILL: { | ||
538 | if (child->exit_state == EXIT_ZOMBIE) { /* already dead */ | ||
539 | pt_succ_return(regs, 0); | ||
540 | goto out_tsk; | ||
541 | } | ||
542 | wake_up_process(child); | ||
543 | child->exit_code = SIGKILL; | ||
544 | pt_succ_return(regs, 0); | ||
545 | goto out_tsk; | ||
546 | } | ||
547 | 442 | ||
548 | case PTRACE_SUNDETACH: { /* detach a process that was attached. */ | 443 | default: |
549 | int err = ptrace_detach(child, data); | 444 | ret = ptrace_request(child, request, addr, data); |
550 | if (err) { | 445 | break; |
551 | pt_error_return(regs, EIO); | ||
552 | goto out_tsk; | ||
553 | } | ||
554 | pt_succ_return(regs, 0); | ||
555 | goto out_tsk; | ||
556 | } | 446 | } |
557 | 447 | ||
558 | /* PTRACE_DUMPCORE unsupported... */ | 448 | return ret; |
559 | |||
560 | default: { | ||
561 | int err = ptrace_request(child, request, addr, data); | ||
562 | if (err) | ||
563 | pt_error_return(regs, -err); | ||
564 | else | ||
565 | pt_succ_return(regs, 0); | ||
566 | goto out_tsk; | ||
567 | } | ||
568 | } | ||
569 | out_tsk: | ||
570 | if (child) | ||
571 | put_task_struct(child); | ||
572 | out: | ||
573 | unlock_kernel(); | ||
574 | } | 449 | } |
575 | 450 | ||
576 | asmlinkage void syscall_trace(void) | 451 | asmlinkage void syscall_trace(void) |
577 | { | 452 | { |
578 | #ifdef DEBUG_PTRACE | ||
579 | printk("%s [%d]: syscall_trace\n", current->comm, current->pid); | ||
580 | #endif | ||
581 | if (!test_thread_flag(TIF_SYSCALL_TRACE)) | 453 | if (!test_thread_flag(TIF_SYSCALL_TRACE)) |
582 | return; | 454 | return; |
583 | if (!(current->ptrace & PT_PTRACED)) | 455 | if (!(current->ptrace & PT_PTRACED)) |
584 | return; | 456 | return; |
585 | current->thread.flags ^= MAGIC_CONSTANT; | ||
586 | ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) | 457 | ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) |
587 | ? 0x80 : 0)); | 458 | ? 0x80 : 0)); |
588 | /* | 459 | /* |
@@ -590,10 +461,6 @@ asmlinkage void syscall_trace(void) | |||
590 | * for normal use. strace only continues with a signal if the | 461 | * for normal use. strace only continues with a signal if the |
591 | * stopping signal is not SIGTRAP. -brl | 462 | * stopping signal is not SIGTRAP. -brl |
592 | */ | 463 | */ |
593 | #ifdef DEBUG_PTRACE | ||
594 | printk("%s [%d]: syscall_trace exit= %x\n", current->comm, | ||
595 | current->pid, current->exit_code); | ||
596 | #endif | ||
597 | if (current->exit_code) { | 464 | if (current->exit_code) { |
598 | send_sig (current->exit_code, current, 1); | 465 | send_sig (current->exit_code, current, 1); |
599 | current->exit_code = 0; | 466 | current->exit_code = 0; |
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 | } |