diff options
author | Michael Neuling <mikey@neuling.org> | 2008-06-25 00:07:18 -0400 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2008-06-30 21:28:46 -0400 |
commit | c6e6771b87d4e339d27f1383c8a808ae9b4ee5b8 (patch) | |
tree | 1900b7350ec685c3a31f2233fd88a57e34725b5c /arch/powerpc/kernel | |
parent | 6f3d8e6947ec98e358514fc0f7b2e37fe88a21bb (diff) |
powerpc: Introduce VSX thread_struct and CONFIG_VSX
The layout of the new VSR registers and how they overlap on top of the
legacy FPR and VR registers is:
VSR doubleword 0 VSR doubleword 1
----------------------------------------------------------------
VSR[0] | FPR[0] | |
----------------------------------------------------------------
VSR[1] | FPR[1] | |
----------------------------------------------------------------
| ... | |
| ... | |
----------------------------------------------------------------
VSR[30] | FPR[30] | |
----------------------------------------------------------------
VSR[31] | FPR[31] | |
----------------------------------------------------------------
VSR[32] | VR[0] |
----------------------------------------------------------------
VSR[33] | VR[1] |
----------------------------------------------------------------
| ... |
| ... |
----------------------------------------------------------------
VSR[62] | VR[30] |
----------------------------------------------------------------
VSR[63] | VR[31] |
----------------------------------------------------------------
VSX has 64 128bit registers. The first 32 regs overlap with the FP
registers and hence extend them with and additional 64 bits. The
second 32 regs overlap with the VMX registers.
This commit introduces the thread_struct changes required to reflect
this register layout. Ptrace and signals code is updated so that the
floating point registers are correctly accessed from the thread_struct
when CONFIG_VSX is enabled.
Signed-off-by: Michael Neuling <mikey@neuling.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc/kernel')
-rw-r--r-- | arch/powerpc/kernel/asm-offsets.c | 4 | ||||
-rw-r--r-- | arch/powerpc/kernel/ptrace.c | 29 | ||||
-rw-r--r-- | arch/powerpc/kernel/signal_32.c | 59 | ||||
-rw-r--r-- | arch/powerpc/kernel/signal_64.c | 32 |
4 files changed, 106 insertions, 18 deletions
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 8655c7670350..92768d3006f7 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c | |||
@@ -78,6 +78,10 @@ int main(void) | |||
78 | DEFINE(THREAD_VSCR, offsetof(struct thread_struct, vscr)); | 78 | DEFINE(THREAD_VSCR, offsetof(struct thread_struct, vscr)); |
79 | DEFINE(THREAD_USED_VR, offsetof(struct thread_struct, used_vr)); | 79 | DEFINE(THREAD_USED_VR, offsetof(struct thread_struct, used_vr)); |
80 | #endif /* CONFIG_ALTIVEC */ | 80 | #endif /* CONFIG_ALTIVEC */ |
81 | #ifdef CONFIG_VSX | ||
82 | DEFINE(THREAD_VSR0, offsetof(struct thread_struct, fpr)); | ||
83 | DEFINE(THREAD_USED_VSR, offsetof(struct thread_struct, used_vsr)); | ||
84 | #endif /* CONFIG_VSX */ | ||
81 | #ifdef CONFIG_PPC64 | 85 | #ifdef CONFIG_PPC64 |
82 | DEFINE(KSP_VSID, offsetof(struct thread_struct, ksp_vsid)); | 86 | DEFINE(KSP_VSID, offsetof(struct thread_struct, ksp_vsid)); |
83 | #else /* CONFIG_PPC64 */ | 87 | #else /* CONFIG_PPC64 */ |
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index 59ff08704e94..70fbde84b83f 100644 --- a/arch/powerpc/kernel/ptrace.c +++ b/arch/powerpc/kernel/ptrace.c | |||
@@ -215,29 +215,56 @@ static int fpr_get(struct task_struct *target, const struct user_regset *regset, | |||
215 | unsigned int pos, unsigned int count, | 215 | unsigned int pos, unsigned int count, |
216 | void *kbuf, void __user *ubuf) | 216 | void *kbuf, void __user *ubuf) |
217 | { | 217 | { |
218 | #ifdef CONFIG_VSX | ||
219 | double buf[33]; | ||
220 | int i; | ||
221 | #endif | ||
218 | flush_fp_to_thread(target); | 222 | flush_fp_to_thread(target); |
219 | 223 | ||
224 | #ifdef CONFIG_VSX | ||
225 | /* copy to local buffer then write that out */ | ||
226 | for (i = 0; i < 32 ; i++) | ||
227 | buf[i] = target->thread.TS_FPR(i); | ||
228 | memcpy(&buf[32], &target->thread.fpscr, sizeof(double)); | ||
229 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, buf, 0, -1); | ||
230 | |||
231 | #else | ||
220 | BUILD_BUG_ON(offsetof(struct thread_struct, fpscr) != | 232 | BUILD_BUG_ON(offsetof(struct thread_struct, fpscr) != |
221 | offsetof(struct thread_struct, TS_FPR(32))); | 233 | offsetof(struct thread_struct, TS_FPR(32))); |
222 | 234 | ||
223 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, | 235 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, |
224 | &target->thread.fpr, 0, -1); | 236 | &target->thread.fpr, 0, -1); |
237 | #endif | ||
225 | } | 238 | } |
226 | 239 | ||
227 | static int fpr_set(struct task_struct *target, const struct user_regset *regset, | 240 | static int fpr_set(struct task_struct *target, const struct user_regset *regset, |
228 | unsigned int pos, unsigned int count, | 241 | unsigned int pos, unsigned int count, |
229 | const void *kbuf, const void __user *ubuf) | 242 | const void *kbuf, const void __user *ubuf) |
230 | { | 243 | { |
244 | #ifdef CONFIG_VSX | ||
245 | double buf[33]; | ||
246 | int i; | ||
247 | #endif | ||
231 | flush_fp_to_thread(target); | 248 | flush_fp_to_thread(target); |
232 | 249 | ||
250 | #ifdef CONFIG_VSX | ||
251 | /* copy to local buffer then write that out */ | ||
252 | i = user_regset_copyin(&pos, &count, &kbuf, &ubuf, buf, 0, -1); | ||
253 | if (i) | ||
254 | return i; | ||
255 | for (i = 0; i < 32 ; i++) | ||
256 | target->thread.TS_FPR(i) = buf[i]; | ||
257 | memcpy(&target->thread.fpscr, &buf[32], sizeof(double)); | ||
258 | return 0; | ||
259 | #else | ||
233 | BUILD_BUG_ON(offsetof(struct thread_struct, fpscr) != | 260 | BUILD_BUG_ON(offsetof(struct thread_struct, fpscr) != |
234 | offsetof(struct thread_struct, TS_FPR(32))); | 261 | offsetof(struct thread_struct, TS_FPR(32))); |
235 | 262 | ||
236 | return user_regset_copyin(&pos, &count, &kbuf, &ubuf, | 263 | return user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
237 | &target->thread.fpr, 0, -1); | 264 | &target->thread.fpr, 0, -1); |
265 | #endif | ||
238 | } | 266 | } |
239 | 267 | ||
240 | |||
241 | #ifdef CONFIG_ALTIVEC | 268 | #ifdef CONFIG_ALTIVEC |
242 | /* | 269 | /* |
243 | * Get/set all the altivec registers vr0..vr31, vscr, vrsave, in one go. | 270 | * Get/set all the altivec registers vr0..vr31, vscr, vrsave, in one go. |
diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c index b057e6852a7d..f7fa395b9fb5 100644 --- a/arch/powerpc/kernel/signal_32.c +++ b/arch/powerpc/kernel/signal_32.c | |||
@@ -337,14 +337,16 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame, | |||
337 | int sigret) | 337 | int sigret) |
338 | { | 338 | { |
339 | unsigned long msr = regs->msr; | 339 | unsigned long msr = regs->msr; |
340 | #ifdef CONFIG_VSX | ||
341 | double buf[32]; | ||
342 | int i; | ||
343 | #endif | ||
340 | 344 | ||
341 | /* Make sure floating point registers are stored in regs */ | 345 | /* Make sure floating point registers are stored in regs */ |
342 | flush_fp_to_thread(current); | 346 | flush_fp_to_thread(current); |
343 | 347 | ||
344 | /* save general and floating-point registers */ | 348 | /* save general registers */ |
345 | if (save_general_regs(regs, frame) || | 349 | if (save_general_regs(regs, frame)) |
346 | __copy_to_user(&frame->mc_fregs, current->thread.fpr, | ||
347 | ELF_NFPREG * sizeof(double))) | ||
348 | return 1; | 350 | return 1; |
349 | 351 | ||
350 | #ifdef CONFIG_ALTIVEC | 352 | #ifdef CONFIG_ALTIVEC |
@@ -368,7 +370,20 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame, | |||
368 | if (__put_user(current->thread.vrsave, (u32 __user *)&frame->mc_vregs[32])) | 370 | if (__put_user(current->thread.vrsave, (u32 __user *)&frame->mc_vregs[32])) |
369 | return 1; | 371 | return 1; |
370 | #endif /* CONFIG_ALTIVEC */ | 372 | #endif /* CONFIG_ALTIVEC */ |
371 | 373 | #ifdef CONFIG_VSX | |
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; | ||
381 | #else | ||
382 | /* save floating-point registers */ | ||
383 | if (__copy_to_user(&frame->mc_fregs, current->thread.fpr, | ||
384 | ELF_NFPREG * sizeof(double))) | ||
385 | return 1; | ||
386 | #endif /* CONFIG_VSX */ | ||
372 | #ifdef CONFIG_SPE | 387 | #ifdef CONFIG_SPE |
373 | /* save spe registers */ | 388 | /* save spe registers */ |
374 | if (current->thread.used_spe) { | 389 | if (current->thread.used_spe) { |
@@ -411,6 +426,10 @@ static long restore_user_regs(struct pt_regs *regs, | |||
411 | long err; | 426 | long err; |
412 | unsigned int save_r2 = 0; | 427 | unsigned int save_r2 = 0; |
413 | unsigned long msr; | 428 | unsigned long msr; |
429 | #ifdef CONFIG_VSX | ||
430 | double buf[32]; | ||
431 | int i; | ||
432 | #endif | ||
414 | 433 | ||
415 | /* | 434 | /* |
416 | * restore general registers but not including MSR or SOFTE. Also | 435 | * restore general registers but not including MSR or SOFTE. Also |
@@ -438,16 +457,11 @@ static long restore_user_regs(struct pt_regs *regs, | |||
438 | */ | 457 | */ |
439 | discard_lazy_cpu_state(); | 458 | discard_lazy_cpu_state(); |
440 | 459 | ||
441 | /* force the process to reload the FP registers from | ||
442 | current->thread when it next does FP instructions */ | ||
443 | regs->msr &= ~(MSR_FP | MSR_FE0 | MSR_FE1); | ||
444 | if (__copy_from_user(current->thread.fpr, &sr->mc_fregs, | ||
445 | sizeof(sr->mc_fregs))) | ||
446 | return 1; | ||
447 | |||
448 | #ifdef CONFIG_ALTIVEC | 460 | #ifdef CONFIG_ALTIVEC |
449 | /* force the process to reload the altivec registers from | 461 | /* |
450 | current->thread when it next does altivec instructions */ | 462 | * Force the process to reload the altivec registers from |
463 | * current->thread when it next does altivec instructions | ||
464 | */ | ||
451 | regs->msr &= ~MSR_VEC; | 465 | regs->msr &= ~MSR_VEC; |
452 | if (msr & MSR_VEC) { | 466 | if (msr & MSR_VEC) { |
453 | /* restore altivec registers from the stack */ | 467 | /* restore altivec registers from the stack */ |
@@ -462,6 +476,23 @@ static long restore_user_regs(struct pt_regs *regs, | |||
462 | return 1; | 476 | return 1; |
463 | #endif /* CONFIG_ALTIVEC */ | 477 | #endif /* CONFIG_ALTIVEC */ |
464 | 478 | ||
479 | #ifdef CONFIG_VSX | ||
480 | if (__copy_from_user(buf, &sr->mc_fregs,sizeof(sr->mc_fregs))) | ||
481 | return 1; | ||
482 | for (i = 0; i < 32 ; i++) | ||
483 | current->thread.TS_FPR(i) = buf[i]; | ||
484 | memcpy(¤t->thread.fpscr, &buf[i], sizeof(double)); | ||
485 | #else | ||
486 | if (__copy_from_user(current->thread.fpr, &sr->mc_fregs, | ||
487 | sizeof(sr->mc_fregs))) | ||
488 | return 1; | ||
489 | #endif /* CONFIG_VSX */ | ||
490 | /* | ||
491 | * force the process to reload the FP registers from | ||
492 | * current->thread when it next does FP instructions | ||
493 | */ | ||
494 | regs->msr &= ~(MSR_FP | MSR_FE0 | MSR_FE1); | ||
495 | |||
465 | #ifdef CONFIG_SPE | 496 | #ifdef CONFIG_SPE |
466 | /* force the process to reload the spe registers from | 497 | /* force the process to reload the spe registers from |
467 | current->thread when it next does spe instructions */ | 498 | current->thread when it next does spe instructions */ |
diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c index da7c058e3731..a587b33cd6b9 100644 --- a/arch/powerpc/kernel/signal_64.c +++ b/arch/powerpc/kernel/signal_64.c | |||
@@ -89,6 +89,10 @@ 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 | ||
92 | 96 | ||
93 | flush_fp_to_thread(current); | 97 | flush_fp_to_thread(current); |
94 | 98 | ||
@@ -112,11 +116,21 @@ static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, | |||
112 | #else /* CONFIG_ALTIVEC */ | 116 | #else /* CONFIG_ALTIVEC */ |
113 | err |= __put_user(0, &sc->v_regs); | 117 | err |= __put_user(0, &sc->v_regs); |
114 | #endif /* CONFIG_ALTIVEC */ | 118 | #endif /* CONFIG_ALTIVEC */ |
119 | flush_fp_to_thread(current); | ||
120 | #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 | #else /* CONFIG_VSX */ | ||
127 | /* copy fpr regs and fpscr */ | ||
128 | err |= __copy_to_user(&sc->fp_regs, ¤t->thread.fpr, FP_REGS_SIZE); | ||
129 | #endif /* CONFIG_VSX */ | ||
115 | err |= __put_user(&sc->gp_regs, &sc->regs); | 130 | err |= __put_user(&sc->gp_regs, &sc->regs); |
116 | WARN_ON(!FULL_REGS(regs)); | 131 | WARN_ON(!FULL_REGS(regs)); |
117 | err |= __copy_to_user(&sc->gp_regs, regs, GP_REGS_SIZE); | 132 | err |= __copy_to_user(&sc->gp_regs, regs, GP_REGS_SIZE); |
118 | err |= __put_user(msr, &sc->gp_regs[PT_MSR]); | 133 | err |= __put_user(msr, &sc->gp_regs[PT_MSR]); |
119 | err |= __copy_to_user(&sc->fp_regs, ¤t->thread.fpr, FP_REGS_SIZE); | ||
120 | err |= __put_user(signr, &sc->signal); | 134 | err |= __put_user(signr, &sc->signal); |
121 | err |= __put_user(handler, &sc->handler); | 135 | err |= __put_user(handler, &sc->handler); |
122 | if (set != NULL) | 136 | if (set != NULL) |
@@ -135,6 +149,9 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig, | |||
135 | #ifdef CONFIG_ALTIVEC | 149 | #ifdef CONFIG_ALTIVEC |
136 | elf_vrreg_t __user *v_regs; | 150 | elf_vrreg_t __user *v_regs; |
137 | #endif | 151 | #endif |
152 | #ifdef CONFIG_VSX | ||
153 | double buf[FP_REGS_SIZE]; | ||
154 | #endif | ||
138 | unsigned long err = 0; | 155 | unsigned long err = 0; |
139 | unsigned long save_r13 = 0; | 156 | unsigned long save_r13 = 0; |
140 | elf_greg_t *gregs = (elf_greg_t *)regs; | 157 | elf_greg_t *gregs = (elf_greg_t *)regs; |
@@ -182,8 +199,6 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig, | |||
182 | */ | 199 | */ |
183 | regs->msr &= ~(MSR_FP | MSR_FE0 | MSR_FE1 | MSR_VEC); | 200 | regs->msr &= ~(MSR_FP | MSR_FE0 | MSR_FE1 | MSR_VEC); |
184 | 201 | ||
185 | err |= __copy_from_user(¤t->thread.fpr, &sc->fp_regs, FP_REGS_SIZE); | ||
186 | |||
187 | #ifdef CONFIG_ALTIVEC | 202 | #ifdef CONFIG_ALTIVEC |
188 | err |= __get_user(v_regs, &sc->v_regs); | 203 | err |= __get_user(v_regs, &sc->v_regs); |
189 | if (err) | 204 | if (err) |
@@ -202,7 +217,18 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig, | |||
202 | else | 217 | else |
203 | current->thread.vrsave = 0; | 218 | current->thread.vrsave = 0; |
204 | #endif /* CONFIG_ALTIVEC */ | 219 | #endif /* CONFIG_ALTIVEC */ |
220 | #ifdef CONFIG_VSX | ||
221 | /* restore floating point */ | ||
222 | err |= __copy_from_user(buf, &sc->fp_regs, FP_REGS_SIZE); | ||
223 | if (err) | ||
224 | return err; | ||
225 | for (i = 0; i < 32 ; i++) | ||
226 | current->thread.TS_FPR(i) = buf[i]; | ||
227 | memcpy(¤t->thread.fpscr, &buf[i], sizeof(double)); | ||
205 | 228 | ||
229 | #else | ||
230 | err |= __copy_from_user(¤t->thread.fpr, &sc->fp_regs, FP_REGS_SIZE); | ||
231 | #endif | ||
206 | return err; | 232 | return err; |
207 | } | 233 | } |
208 | 234 | ||