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:50 -0400 |
commit | ce48b2100785e5ca629fb3aa8e3b50aca808f692 (patch) | |
tree | 63532ff7cc68b18ca4902bd10e03fcbaaf01cade /arch/powerpc/kernel/process.c | |
parent | 72ffff5b1792b0fa4d40a8e2f3276fff999820ec (diff) |
powerpc: Add VSX context save/restore, ptrace and signal support
This patch extends the floating point save and restore code to use the
VSX load/stores when VSX is available. This will make FP context
save/restore marginally slower on FP only code, when VSX is available,
as it has to load/store 128bits rather than just 64bits.
Mixing FP, VMX and VSX code will get constant architected state.
The signals interface is extended to enable access to VSR 0-31
doubleword 1 after discussions with tool chain maintainers. Backward
compatibility is maintained.
The ptrace interface is also extended to allow access to VSR 0-31 full
registers.
Signed-off-by: Michael Neuling <mikey@neuling.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc/kernel/process.c')
-rw-r--r-- | arch/powerpc/kernel/process.c | 107 |
1 files changed, 106 insertions, 1 deletions
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index 582df70439cb..d52ded366f14 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c | |||
@@ -53,6 +53,7 @@ extern unsigned long _get_SP(void); | |||
53 | #ifndef CONFIG_SMP | 53 | #ifndef CONFIG_SMP |
54 | struct task_struct *last_task_used_math = NULL; | 54 | struct task_struct *last_task_used_math = NULL; |
55 | struct task_struct *last_task_used_altivec = NULL; | 55 | struct task_struct *last_task_used_altivec = NULL; |
56 | struct task_struct *last_task_used_vsx = NULL; | ||
56 | struct task_struct *last_task_used_spe = NULL; | 57 | struct task_struct *last_task_used_spe = NULL; |
57 | #endif | 58 | #endif |
58 | 59 | ||
@@ -106,11 +107,23 @@ EXPORT_SYMBOL(enable_kernel_fp); | |||
106 | 107 | ||
107 | int dump_task_fpu(struct task_struct *tsk, elf_fpregset_t *fpregs) | 108 | int dump_task_fpu(struct task_struct *tsk, elf_fpregset_t *fpregs) |
108 | { | 109 | { |
110 | #ifdef CONFIG_VSX | ||
111 | int i; | ||
112 | elf_fpreg_t *reg; | ||
113 | #endif | ||
114 | |||
109 | if (!tsk->thread.regs) | 115 | if (!tsk->thread.regs) |
110 | return 0; | 116 | return 0; |
111 | flush_fp_to_thread(current); | 117 | flush_fp_to_thread(current); |
112 | 118 | ||
119 | #ifdef CONFIG_VSX | ||
120 | reg = (elf_fpreg_t *)fpregs; | ||
121 | for (i = 0; i < ELF_NFPREG - 1; i++, reg++) | ||
122 | *reg = tsk->thread.TS_FPR(i); | ||
123 | memcpy(reg, &tsk->thread.fpscr, sizeof(elf_fpreg_t)); | ||
124 | #else | ||
113 | memcpy(fpregs, &tsk->thread.TS_FPR(0), sizeof(*fpregs)); | 125 | memcpy(fpregs, &tsk->thread.TS_FPR(0), sizeof(*fpregs)); |
126 | #endif | ||
114 | 127 | ||
115 | return 1; | 128 | return 1; |
116 | } | 129 | } |
@@ -149,7 +162,7 @@ void flush_altivec_to_thread(struct task_struct *tsk) | |||
149 | } | 162 | } |
150 | } | 163 | } |
151 | 164 | ||
152 | int dump_task_altivec(struct task_struct *tsk, elf_vrregset_t *vrregs) | 165 | int dump_task_altivec(struct task_struct *tsk, elf_vrreg_t *vrregs) |
153 | { | 166 | { |
154 | /* ELF_NVRREG includes the VSCR and VRSAVE which we need to save | 167 | /* ELF_NVRREG includes the VSCR and VRSAVE which we need to save |
155 | * separately, see below */ | 168 | * separately, see below */ |
@@ -179,6 +192,80 @@ int dump_task_altivec(struct task_struct *tsk, elf_vrregset_t *vrregs) | |||
179 | } | 192 | } |
180 | #endif /* CONFIG_ALTIVEC */ | 193 | #endif /* CONFIG_ALTIVEC */ |
181 | 194 | ||
195 | #ifdef CONFIG_VSX | ||
196 | #if 0 | ||
197 | /* not currently used, but some crazy RAID module might want to later */ | ||
198 | void enable_kernel_vsx(void) | ||
199 | { | ||
200 | WARN_ON(preemptible()); | ||
201 | |||
202 | #ifdef CONFIG_SMP | ||
203 | if (current->thread.regs && (current->thread.regs->msr & MSR_VSX)) | ||
204 | giveup_vsx(current); | ||
205 | else | ||
206 | giveup_vsx(NULL); /* just enable vsx for kernel - force */ | ||
207 | #else | ||
208 | giveup_vsx(last_task_used_vsx); | ||
209 | #endif /* CONFIG_SMP */ | ||
210 | } | ||
211 | EXPORT_SYMBOL(enable_kernel_vsx); | ||
212 | #endif | ||
213 | |||
214 | void flush_vsx_to_thread(struct task_struct *tsk) | ||
215 | { | ||
216 | if (tsk->thread.regs) { | ||
217 | preempt_disable(); | ||
218 | if (tsk->thread.regs->msr & MSR_VSX) { | ||
219 | #ifdef CONFIG_SMP | ||
220 | BUG_ON(tsk != current); | ||
221 | #endif | ||
222 | giveup_vsx(tsk); | ||
223 | } | ||
224 | preempt_enable(); | ||
225 | } | ||
226 | } | ||
227 | |||
228 | /* | ||
229 | * This dumps the lower half 64bits of the first 32 VSX registers. | ||
230 | * This needs to be called with dump_task_fp and dump_task_altivec to | ||
231 | * get all the VSX state. | ||
232 | */ | ||
233 | int dump_task_vsx(struct task_struct *tsk, elf_vrreg_t *vrregs) | ||
234 | { | ||
235 | elf_vrreg_t *reg; | ||
236 | double buf[32]; | ||
237 | int i; | ||
238 | |||
239 | if (tsk == current) | ||
240 | flush_vsx_to_thread(tsk); | ||
241 | |||
242 | reg = (elf_vrreg_t *)vrregs; | ||
243 | |||
244 | for (i = 0; i < 32 ; i++) | ||
245 | buf[i] = current->thread.fpr[i][TS_VSRLOWOFFSET]; | ||
246 | memcpy(reg, buf, sizeof(buf)); | ||
247 | |||
248 | return 1; | ||
249 | } | ||
250 | #endif /* CONFIG_VSX */ | ||
251 | |||
252 | int dump_task_vector(struct task_struct *tsk, elf_vrregset_t *vrregs) | ||
253 | { | ||
254 | int rc = 0; | ||
255 | elf_vrreg_t *regs = (elf_vrreg_t *)vrregs; | ||
256 | #ifdef CONFIG_ALTIVEC | ||
257 | rc = dump_task_altivec(tsk, regs); | ||
258 | if (rc) | ||
259 | return rc; | ||
260 | regs += ELF_NVRREG; | ||
261 | #endif | ||
262 | |||
263 | #ifdef CONFIG_VSX | ||
264 | rc = dump_task_vsx(tsk, regs); | ||
265 | #endif | ||
266 | return rc; | ||
267 | } | ||
268 | |||
182 | #ifdef CONFIG_SPE | 269 | #ifdef CONFIG_SPE |
183 | 270 | ||
184 | void enable_kernel_spe(void) | 271 | void enable_kernel_spe(void) |
@@ -233,6 +320,10 @@ void discard_lazy_cpu_state(void) | |||
233 | if (last_task_used_altivec == current) | 320 | if (last_task_used_altivec == current) |
234 | last_task_used_altivec = NULL; | 321 | last_task_used_altivec = NULL; |
235 | #endif /* CONFIG_ALTIVEC */ | 322 | #endif /* CONFIG_ALTIVEC */ |
323 | #ifdef CONFIG_VSX | ||
324 | if (last_task_used_vsx == current) | ||
325 | last_task_used_vsx = NULL; | ||
326 | #endif /* CONFIG_VSX */ | ||
236 | #ifdef CONFIG_SPE | 327 | #ifdef CONFIG_SPE |
237 | if (last_task_used_spe == current) | 328 | if (last_task_used_spe == current) |
238 | last_task_used_spe = NULL; | 329 | last_task_used_spe = NULL; |
@@ -297,6 +388,10 @@ struct task_struct *__switch_to(struct task_struct *prev, | |||
297 | if (prev->thread.regs && (prev->thread.regs->msr & MSR_VEC)) | 388 | if (prev->thread.regs && (prev->thread.regs->msr & MSR_VEC)) |
298 | giveup_altivec(prev); | 389 | giveup_altivec(prev); |
299 | #endif /* CONFIG_ALTIVEC */ | 390 | #endif /* CONFIG_ALTIVEC */ |
391 | #ifdef CONFIG_VSX | ||
392 | if (prev->thread.regs && (prev->thread.regs->msr & MSR_VSX)) | ||
393 | giveup_vsx(prev); | ||
394 | #endif /* CONFIG_VSX */ | ||
300 | #ifdef CONFIG_SPE | 395 | #ifdef CONFIG_SPE |
301 | /* | 396 | /* |
302 | * If the previous thread used spe in the last quantum | 397 | * If the previous thread used spe in the last quantum |
@@ -317,6 +412,10 @@ struct task_struct *__switch_to(struct task_struct *prev, | |||
317 | if (new->thread.regs && last_task_used_altivec == new) | 412 | if (new->thread.regs && last_task_used_altivec == new) |
318 | new->thread.regs->msr |= MSR_VEC; | 413 | new->thread.regs->msr |= MSR_VEC; |
319 | #endif /* CONFIG_ALTIVEC */ | 414 | #endif /* CONFIG_ALTIVEC */ |
415 | #ifdef CONFIG_VSX | ||
416 | if (new->thread.regs && last_task_used_vsx == new) | ||
417 | new->thread.regs->msr |= MSR_VSX; | ||
418 | #endif /* CONFIG_VSX */ | ||
320 | #ifdef CONFIG_SPE | 419 | #ifdef CONFIG_SPE |
321 | /* Avoid the trap. On smp this this never happens since | 420 | /* Avoid the trap. On smp this this never happens since |
322 | * we don't set last_task_used_spe | 421 | * we don't set last_task_used_spe |
@@ -417,6 +516,8 @@ static struct regbit { | |||
417 | {MSR_EE, "EE"}, | 516 | {MSR_EE, "EE"}, |
418 | {MSR_PR, "PR"}, | 517 | {MSR_PR, "PR"}, |
419 | {MSR_FP, "FP"}, | 518 | {MSR_FP, "FP"}, |
519 | {MSR_VEC, "VEC"}, | ||
520 | {MSR_VSX, "VSX"}, | ||
420 | {MSR_ME, "ME"}, | 521 | {MSR_ME, "ME"}, |
421 | {MSR_IR, "IR"}, | 522 | {MSR_IR, "IR"}, |
422 | {MSR_DR, "DR"}, | 523 | {MSR_DR, "DR"}, |
@@ -534,6 +635,7 @@ void prepare_to_copy(struct task_struct *tsk) | |||
534 | { | 635 | { |
535 | flush_fp_to_thread(current); | 636 | flush_fp_to_thread(current); |
536 | flush_altivec_to_thread(current); | 637 | flush_altivec_to_thread(current); |
638 | flush_vsx_to_thread(current); | ||
537 | flush_spe_to_thread(current); | 639 | flush_spe_to_thread(current); |
538 | } | 640 | } |
539 | 641 | ||
@@ -689,6 +791,9 @@ void start_thread(struct pt_regs *regs, unsigned long start, unsigned long sp) | |||
689 | #endif | 791 | #endif |
690 | 792 | ||
691 | discard_lazy_cpu_state(); | 793 | discard_lazy_cpu_state(); |
794 | #ifdef CONFIG_VSX | ||
795 | current->thread.used_vsr = 0; | ||
796 | #endif | ||
692 | memset(current->thread.fpr, 0, sizeof(current->thread.fpr)); | 797 | memset(current->thread.fpr, 0, sizeof(current->thread.fpr)); |
693 | current->thread.fpscr.val = 0; | 798 | current->thread.fpscr.val = 0; |
694 | #ifdef CONFIG_ALTIVEC | 799 | #ifdef CONFIG_ALTIVEC |