diff options
author | Michael Neuling <mikey@neuling.org> | 2008-07-02 00:06:37 -0400 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2008-07-03 02:58:11 -0400 |
commit | 6a274c08f2f4dfac7167bbd849621f3a2b55d424 (patch) | |
tree | 1bad45a11c1e436ffbcb84107783da0e423925f9 | |
parent | 2d1b2027626d5151fff8ef7c06ca8e7876a1a510 (diff) |
powerpc: Clean up copy_to/from_user for vsx and fpr
This merges and cleans up some of the ugly copy/to from user code
which is required for the new fpr and vsx layout in the thread_struct.
Also fixes some hard coded buffer sizes and removes a redundant
fpr_flush_to_thread.
Signed-off-by: Michael Neuling <mikey@neuling.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
-rw-r--r-- | arch/powerpc/kernel/signal.h | 10 | ||||
-rw-r--r-- | arch/powerpc/kernel/signal_32.c | 109 | ||||
-rw-r--r-- | arch/powerpc/kernel/signal_64.c | 43 |
3 files changed, 96 insertions, 66 deletions
diff --git a/arch/powerpc/kernel/signal.h b/arch/powerpc/kernel/signal.h index 77efb3d5465a..28f4b9f5fe5e 100644 --- a/arch/powerpc/kernel/signal.h +++ b/arch/powerpc/kernel/signal.h | |||
@@ -24,6 +24,16 @@ extern int handle_rt_signal32(unsigned long sig, struct k_sigaction *ka, | |||
24 | siginfo_t *info, sigset_t *oldset, | 24 | siginfo_t *info, sigset_t *oldset, |
25 | struct pt_regs *regs); | 25 | struct pt_regs *regs); |
26 | 26 | ||
27 | extern unsigned long copy_fpr_to_user(void __user *to, | ||
28 | struct task_struct *task); | ||
29 | extern unsigned long copy_fpr_from_user(struct task_struct *task, | ||
30 | void __user *from); | ||
31 | #ifdef CONFIG_VSX | ||
32 | extern unsigned long copy_vsx_to_user(void __user *to, | ||
33 | struct task_struct *task); | ||
34 | extern unsigned long copy_vsx_from_user(struct task_struct *task, | ||
35 | void __user *from); | ||
36 | #endif | ||
27 | 37 | ||
28 | #ifdef CONFIG_PPC64 | 38 | #ifdef CONFIG_PPC64 |
29 | 39 | ||
diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c index 349d3487d920..9991e2a58bf4 100644 --- a/arch/powerpc/kernel/signal_32.c +++ b/arch/powerpc/kernel/signal_32.c | |||
@@ -328,6 +328,75 @@ struct rt_sigframe { | |||
328 | int abigap[56]; | 328 | int abigap[56]; |
329 | }; | 329 | }; |
330 | 330 | ||
331 | #ifdef CONFIG_VSX | ||
332 | unsigned long copy_fpr_to_user(void __user *to, | ||
333 | struct task_struct *task) | ||
334 | { | ||
335 | double buf[ELF_NFPREG]; | ||
336 | int i; | ||
337 | |||
338 | /* save FPR copy to local buffer then write to the thread_struct */ | ||
339 | for (i = 0; i < (ELF_NFPREG - 1) ; i++) | ||
340 | buf[i] = task->thread.TS_FPR(i); | ||
341 | memcpy(&buf[i], &task->thread.fpscr, sizeof(double)); | ||
342 | return __copy_to_user(to, buf, ELF_NFPREG * sizeof(double)); | ||
343 | } | ||
344 | |||
345 | unsigned long copy_fpr_from_user(struct task_struct *task, | ||
346 | void __user *from) | ||
347 | { | ||
348 | double buf[ELF_NFPREG]; | ||
349 | int i; | ||
350 | |||
351 | if (__copy_from_user(buf, from, ELF_NFPREG * sizeof(double))) | ||
352 | return 1; | ||
353 | for (i = 0; i < (ELF_NFPREG - 1) ; i++) | ||
354 | task->thread.TS_FPR(i) = buf[i]; | ||
355 | memcpy(&task->thread.fpscr, &buf[i], sizeof(double)); | ||
356 | |||
357 | return 0; | ||
358 | } | ||
359 | |||
360 | unsigned long copy_vsx_to_user(void __user *to, | ||
361 | struct task_struct *task) | ||
362 | { | ||
363 | double buf[ELF_NVSRHALFREG]; | ||
364 | int i; | ||
365 | |||
366 | /* save FPR copy to local buffer then write to the thread_struct */ | ||
367 | for (i = 0; i < ELF_NVSRHALFREG; i++) | ||
368 | buf[i] = task->thread.fpr[i][TS_VSRLOWOFFSET]; | ||
369 | return __copy_to_user(to, buf, ELF_NVSRHALFREG * sizeof(double)); | ||
370 | } | ||
371 | |||
372 | unsigned long copy_vsx_from_user(struct task_struct *task, | ||
373 | void __user *from) | ||
374 | { | ||
375 | double buf[ELF_NVSRHALFREG]; | ||
376 | int i; | ||
377 | |||
378 | if (__copy_from_user(buf, from, ELF_NVSRHALFREG * sizeof(double))) | ||
379 | return 1; | ||
380 | for (i = 0; i < ELF_NVSRHALFREG ; i++) | ||
381 | task->thread.fpr[i][TS_VSRLOWOFFSET] = buf[i]; | ||
382 | return 0; | ||
383 | } | ||
384 | #else | ||
385 | inline unsigned long copy_fpr_to_user(void __user *to, | ||
386 | struct task_struct *task) | ||
387 | { | ||
388 | return __copy_to_user(to, task->thread.fpr, | ||
389 | ELF_NFPREG * sizeof(double)); | ||
390 | } | ||
391 | |||
392 | inline unsigned long copy_fpr_from_user(struct task_struct *task, | ||
393 | void __user *from) | ||
394 | { | ||
395 | return __copy_from_user(task->thread.fpr, from, | ||
396 | ELF_NFPREG * sizeof(double)); | ||
397 | } | ||
398 | #endif | ||
399 | |||
331 | /* | 400 | /* |
332 | * Save the current user registers on the user stack. | 401 | * Save the current user registers on the user stack. |
333 | * We only save the altivec/spe registers if the process has used | 402 | * We only save the altivec/spe registers if the process has used |
@@ -337,10 +406,6 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame, | |||
337 | int sigret) | 406 | int sigret) |
338 | { | 407 | { |
339 | unsigned long msr = regs->msr; | 408 | unsigned long msr = regs->msr; |
340 | #ifdef CONFIG_VSX | ||
341 | double buf[32]; | ||
342 | int i; | ||
343 | #endif | ||
344 | 409 | ||
345 | /* Make sure floating point registers are stored in regs */ | 410 | /* Make sure floating point registers are stored in regs */ |
346 | flush_fp_to_thread(current); | 411 | flush_fp_to_thread(current); |
@@ -370,14 +435,9 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame, | |||
370 | if (__put_user(current->thread.vrsave, (u32 __user *)&frame->mc_vregs[32])) | 435 | if (__put_user(current->thread.vrsave, (u32 __user *)&frame->mc_vregs[32])) |
371 | return 1; | 436 | return 1; |
372 | #endif /* CONFIG_ALTIVEC */ | 437 | #endif /* CONFIG_ALTIVEC */ |
373 | #ifdef CONFIG_VSX | 438 | if (copy_fpr_to_user(&frame->mc_fregs, current)) |
374 | /* save FPR copy to local buffer then write to the thread_struct */ | ||
375 | flush_fp_to_thread(current); | ||
376 | for (i = 0; i < 32 ; i++) | ||
377 | buf[i] = current->thread.TS_FPR(i); | ||
378 | memcpy(&buf[i], ¤t->thread.fpscr, sizeof(double)); | ||
379 | if (__copy_to_user(&frame->mc_fregs, buf, ELF_NFPREG * sizeof(double))) | ||
380 | return 1; | 439 | return 1; |
440 | #ifdef CONFIG_VSX | ||
381 | /* | 441 | /* |
382 | * Copy VSR 0-31 upper half from thread_struct to local | 442 | * Copy VSR 0-31 upper half from thread_struct to local |
383 | * buffer, then write that to userspace. Also set MSR_VSX in | 443 | * buffer, then write that to userspace. Also set MSR_VSX in |
@@ -386,18 +446,10 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame, | |||
386 | */ | 446 | */ |
387 | if (current->thread.used_vsr) { | 447 | if (current->thread.used_vsr) { |
388 | flush_vsx_to_thread(current); | 448 | flush_vsx_to_thread(current); |
389 | for (i = 0; i < 32 ; i++) | 449 | if (copy_vsx_to_user(&frame->mc_vsregs, current)) |
390 | buf[i] = current->thread.fpr[i][TS_VSRLOWOFFSET]; | ||
391 | if (__copy_to_user(&frame->mc_vsregs, buf, | ||
392 | ELF_NVSRHALFREG * sizeof(double))) | ||
393 | return 1; | 450 | return 1; |
394 | msr |= MSR_VSX; | 451 | msr |= MSR_VSX; |
395 | } | 452 | } |
396 | #else | ||
397 | /* save floating-point registers */ | ||
398 | if (__copy_to_user(&frame->mc_fregs, current->thread.fpr, | ||
399 | ELF_NFPREG * sizeof(double))) | ||
400 | return 1; | ||
401 | #endif /* CONFIG_VSX */ | 453 | #endif /* CONFIG_VSX */ |
402 | #ifdef CONFIG_SPE | 454 | #ifdef CONFIG_SPE |
403 | /* save spe registers */ | 455 | /* save spe registers */ |
@@ -442,7 +494,6 @@ static long restore_user_regs(struct pt_regs *regs, | |||
442 | unsigned int save_r2 = 0; | 494 | unsigned int save_r2 = 0; |
443 | unsigned long msr; | 495 | unsigned long msr; |
444 | #ifdef CONFIG_VSX | 496 | #ifdef CONFIG_VSX |
445 | double buf[32]; | ||
446 | int i; | 497 | int i; |
447 | #endif | 498 | #endif |
448 | 499 | ||
@@ -490,13 +541,10 @@ static long restore_user_regs(struct pt_regs *regs, | |||
490 | if (__get_user(current->thread.vrsave, (u32 __user *)&sr->mc_vregs[32])) | 541 | if (__get_user(current->thread.vrsave, (u32 __user *)&sr->mc_vregs[32])) |
491 | return 1; | 542 | return 1; |
492 | #endif /* CONFIG_ALTIVEC */ | 543 | #endif /* CONFIG_ALTIVEC */ |
544 | if (copy_fpr_from_user(current, &sr->mc_fregs)) | ||
545 | return 1; | ||
493 | 546 | ||
494 | #ifdef CONFIG_VSX | 547 | #ifdef CONFIG_VSX |
495 | if (__copy_from_user(buf, &sr->mc_fregs,sizeof(sr->mc_fregs))) | ||
496 | return 1; | ||
497 | for (i = 0; i < 32 ; i++) | ||
498 | current->thread.TS_FPR(i) = buf[i]; | ||
499 | memcpy(¤t->thread.fpscr, &buf[i], sizeof(double)); | ||
500 | /* | 548 | /* |
501 | * Force the process to reload the VSX registers from | 549 | * Force the process to reload the VSX registers from |
502 | * current->thread when it next does VSX instruction. | 550 | * current->thread when it next does VSX instruction. |
@@ -507,18 +555,11 @@ static long restore_user_regs(struct pt_regs *regs, | |||
507 | * Restore altivec registers from the stack to a local | 555 | * Restore altivec registers from the stack to a local |
508 | * buffer, then write this out to the thread_struct | 556 | * buffer, then write this out to the thread_struct |
509 | */ | 557 | */ |
510 | if (__copy_from_user(buf, &sr->mc_vsregs, | 558 | if (copy_vsx_from_user(current, &sr->mc_vsregs)) |
511 | sizeof(sr->mc_vsregs))) | ||
512 | return 1; | 559 | return 1; |
513 | for (i = 0; i < 32 ; i++) | ||
514 | current->thread.fpr[i][TS_VSRLOWOFFSET] = buf[i]; | ||
515 | } else if (current->thread.used_vsr) | 560 | } else if (current->thread.used_vsr) |
516 | for (i = 0; i < 32 ; i++) | 561 | for (i = 0; i < 32 ; i++) |
517 | current->thread.fpr[i][TS_VSRLOWOFFSET] = 0; | 562 | current->thread.fpr[i][TS_VSRLOWOFFSET] = 0; |
518 | #else | ||
519 | if (__copy_from_user(current->thread.fpr, &sr->mc_fregs, | ||
520 | sizeof(sr->mc_fregs))) | ||
521 | return 1; | ||
522 | #endif /* CONFIG_VSX */ | 563 | #endif /* CONFIG_VSX */ |
523 | /* | 564 | /* |
524 | * force the process to reload the FP registers from | 565 | * force the process to reload the FP registers from |
diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c index 8214e57aab67..93ebfb6944b6 100644 --- a/arch/powerpc/kernel/signal_64.c +++ b/arch/powerpc/kernel/signal_64.c | |||
@@ -89,10 +89,6 @@ static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, | |||
89 | #endif | 89 | #endif |
90 | unsigned long msr = regs->msr; | 90 | unsigned long msr = regs->msr; |
91 | long err = 0; | 91 | long err = 0; |
92 | #ifdef CONFIG_VSX | ||
93 | double buf[FP_REGS_SIZE]; | ||
94 | int i; | ||
95 | #endif | ||
96 | 92 | ||
97 | flush_fp_to_thread(current); | 93 | flush_fp_to_thread(current); |
98 | 94 | ||
@@ -117,12 +113,9 @@ static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, | |||
117 | err |= __put_user(0, &sc->v_regs); | 113 | err |= __put_user(0, &sc->v_regs); |
118 | #endif /* CONFIG_ALTIVEC */ | 114 | #endif /* CONFIG_ALTIVEC */ |
119 | flush_fp_to_thread(current); | 115 | flush_fp_to_thread(current); |
116 | /* copy fpr regs and fpscr */ | ||
117 | err |= copy_fpr_to_user(&sc->fp_regs, current); | ||
120 | #ifdef CONFIG_VSX | 118 | #ifdef CONFIG_VSX |
121 | /* Copy FP to local buffer then write that out */ | ||
122 | for (i = 0; i < 32 ; i++) | ||
123 | buf[i] = current->thread.TS_FPR(i); | ||
124 | memcpy(&buf[i], ¤t->thread.fpscr, sizeof(double)); | ||
125 | err |= __copy_to_user(&sc->fp_regs, buf, FP_REGS_SIZE); | ||
126 | /* | 119 | /* |
127 | * Copy VSX low doubleword to local buffer for formatting, | 120 | * Copy VSX low doubleword to local buffer for formatting, |
128 | * then out to userspace. Update v_regs to point after the | 121 | * then out to userspace. Update v_regs to point after the |
@@ -131,17 +124,12 @@ static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, | |||
131 | if (current->thread.used_vsr) { | 124 | if (current->thread.used_vsr) { |
132 | flush_vsx_to_thread(current); | 125 | flush_vsx_to_thread(current); |
133 | v_regs += ELF_NVRREG; | 126 | v_regs += ELF_NVRREG; |
134 | for (i = 0; i < 32 ; i++) | 127 | err |= copy_vsx_to_user(v_regs, current); |
135 | buf[i] = current->thread.fpr[i][TS_VSRLOWOFFSET]; | ||
136 | err |= __copy_to_user(v_regs, buf, 32 * sizeof(double)); | ||
137 | /* set MSR_VSX in the MSR value in the frame to | 128 | /* set MSR_VSX in the MSR value in the frame to |
138 | * indicate that sc->vs_reg) contains valid data. | 129 | * indicate that sc->vs_reg) contains valid data. |
139 | */ | 130 | */ |
140 | msr |= MSR_VSX; | 131 | msr |= MSR_VSX; |
141 | } | 132 | } |
142 | #else /* CONFIG_VSX */ | ||
143 | /* copy fpr regs and fpscr */ | ||
144 | err |= __copy_to_user(&sc->fp_regs, ¤t->thread.fpr, FP_REGS_SIZE); | ||
145 | #endif /* CONFIG_VSX */ | 133 | #endif /* CONFIG_VSX */ |
146 | err |= __put_user(&sc->gp_regs, &sc->regs); | 134 | err |= __put_user(&sc->gp_regs, &sc->regs); |
147 | WARN_ON(!FULL_REGS(regs)); | 135 | WARN_ON(!FULL_REGS(regs)); |
@@ -165,13 +153,12 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig, | |||
165 | #ifdef CONFIG_ALTIVEC | 153 | #ifdef CONFIG_ALTIVEC |
166 | elf_vrreg_t __user *v_regs; | 154 | elf_vrreg_t __user *v_regs; |
167 | #endif | 155 | #endif |
168 | #ifdef CONFIG_VSX | ||
169 | double buf[FP_REGS_SIZE]; | ||
170 | int i; | ||
171 | #endif | ||
172 | unsigned long err = 0; | 156 | unsigned long err = 0; |
173 | unsigned long save_r13 = 0; | 157 | unsigned long save_r13 = 0; |
174 | unsigned long msr; | 158 | unsigned long msr; |
159 | #ifdef CONFIG_VSX | ||
160 | int i; | ||
161 | #endif | ||
175 | 162 | ||
176 | /* If this is not a signal return, we preserve the TLS in r13 */ | 163 | /* If this is not a signal return, we preserve the TLS in r13 */ |
177 | if (!sig) | 164 | if (!sig) |
@@ -234,15 +221,9 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig, | |||
234 | else | 221 | else |
235 | current->thread.vrsave = 0; | 222 | current->thread.vrsave = 0; |
236 | #endif /* CONFIG_ALTIVEC */ | 223 | #endif /* CONFIG_ALTIVEC */ |
237 | #ifdef CONFIG_VSX | ||
238 | /* restore floating point */ | 224 | /* restore floating point */ |
239 | err |= __copy_from_user(buf, &sc->fp_regs, FP_REGS_SIZE); | 225 | err |= copy_fpr_from_user(current, &sc->fp_regs); |
240 | if (err) | 226 | #ifdef CONFIG_VSX |
241 | return err; | ||
242 | for (i = 0; i < 32 ; i++) | ||
243 | current->thread.TS_FPR(i) = buf[i]; | ||
244 | memcpy(¤t->thread.fpscr, &buf[i], sizeof(double)); | ||
245 | |||
246 | /* | 227 | /* |
247 | * Get additional VSX data. Update v_regs to point after the | 228 | * Get additional VSX data. Update v_regs to point after the |
248 | * VMX data. Copy VSX low doubleword from userspace to local | 229 | * VMX data. Copy VSX low doubleword from userspace to local |
@@ -250,14 +231,12 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig, | |||
250 | */ | 231 | */ |
251 | v_regs += ELF_NVRREG; | 232 | v_regs += ELF_NVRREG; |
252 | if ((msr & MSR_VSX) != 0) | 233 | if ((msr & MSR_VSX) != 0) |
253 | err |= __copy_from_user(buf, v_regs, 32 * sizeof(double)); | 234 | err |= copy_vsx_from_user(current, v_regs); |
254 | else | 235 | else |
255 | memset(buf, 0, 32 * sizeof(double)); | 236 | for (i = 0; i < 32 ; i++) |
237 | current->thread.fpr[i][TS_VSRLOWOFFSET] = 0; | ||
256 | 238 | ||
257 | for (i = 0; i < 32 ; i++) | ||
258 | current->thread.fpr[i][TS_VSRLOWOFFSET] = buf[i]; | ||
259 | #else | 239 | #else |
260 | err |= __copy_from_user(¤t->thread.fpr, &sc->fp_regs, FP_REGS_SIZE); | ||
261 | #endif | 240 | #endif |
262 | return err; | 241 | return err; |
263 | } | 242 | } |