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 | |
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')
-rw-r--r-- | arch/powerpc/kernel/entry_64.S | 5 | ||||
-rw-r--r-- | arch/powerpc/kernel/fpu.S | 16 | ||||
-rw-r--r-- | arch/powerpc/kernel/head_64.S | 65 | ||||
-rw-r--r-- | arch/powerpc/kernel/misc_64.S | 33 | ||||
-rw-r--r-- | arch/powerpc/kernel/ppc32.h | 1 | ||||
-rw-r--r-- | arch/powerpc/kernel/ppc_ksyms.c | 3 | ||||
-rw-r--r-- | arch/powerpc/kernel/process.c | 107 | ||||
-rw-r--r-- | arch/powerpc/kernel/ptrace.c | 70 | ||||
-rw-r--r-- | arch/powerpc/kernel/signal_32.c | 33 | ||||
-rw-r--r-- | arch/powerpc/kernel/signal_64.c | 31 | ||||
-rw-r--r-- | arch/powerpc/kernel/traps.c | 29 |
11 files changed, 388 insertions, 5 deletions
diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index 6ca3044e2e32..12eb95a80ce9 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S | |||
@@ -353,6 +353,11 @@ _GLOBAL(_switch) | |||
353 | mflr r20 /* Return to switch caller */ | 353 | mflr r20 /* Return to switch caller */ |
354 | mfmsr r22 | 354 | mfmsr r22 |
355 | li r0, MSR_FP | 355 | li r0, MSR_FP |
356 | #ifdef CONFIG_VSX | ||
357 | BEGIN_FTR_SECTION | ||
358 | oris r0,r0,MSR_VSX@h /* Disable VSX */ | ||
359 | END_FTR_SECTION_IFSET(CPU_FTR_VSX) | ||
360 | #endif /* CONFIG_VSX */ | ||
356 | #ifdef CONFIG_ALTIVEC | 361 | #ifdef CONFIG_ALTIVEC |
357 | BEGIN_FTR_SECTION | 362 | BEGIN_FTR_SECTION |
358 | oris r0,r0,MSR_VEC@h /* Disable altivec */ | 363 | oris r0,r0,MSR_VEC@h /* Disable altivec */ |
diff --git a/arch/powerpc/kernel/fpu.S b/arch/powerpc/kernel/fpu.S index 15247fe171a8..a088c064ae40 100644 --- a/arch/powerpc/kernel/fpu.S +++ b/arch/powerpc/kernel/fpu.S | |||
@@ -57,6 +57,11 @@ END_FTR_SECTION_IFSET(CPU_FTR_VSX); \ | |||
57 | _GLOBAL(load_up_fpu) | 57 | _GLOBAL(load_up_fpu) |
58 | mfmsr r5 | 58 | mfmsr r5 |
59 | ori r5,r5,MSR_FP | 59 | ori r5,r5,MSR_FP |
60 | #ifdef CONFIG_VSX | ||
61 | BEGIN_FTR_SECTION | ||
62 | oris r5,r5,MSR_VSX@h | ||
63 | END_FTR_SECTION_IFSET(CPU_FTR_VSX) | ||
64 | #endif | ||
60 | SYNC | 65 | SYNC |
61 | MTMSRD(r5) /* enable use of fpu now */ | 66 | MTMSRD(r5) /* enable use of fpu now */ |
62 | isync | 67 | isync |
@@ -73,7 +78,7 @@ _GLOBAL(load_up_fpu) | |||
73 | beq 1f | 78 | beq 1f |
74 | toreal(r4) | 79 | toreal(r4) |
75 | addi r4,r4,THREAD /* want last_task_used_math->thread */ | 80 | addi r4,r4,THREAD /* want last_task_used_math->thread */ |
76 | SAVE_32FPRS(0, r4) | 81 | SAVE_32FPVSRS(0, r5, r4) |
77 | mffs fr0 | 82 | mffs fr0 |
78 | stfd fr0,THREAD_FPSCR(r4) | 83 | stfd fr0,THREAD_FPSCR(r4) |
79 | PPC_LL r5,PT_REGS(r4) | 84 | PPC_LL r5,PT_REGS(r4) |
@@ -100,7 +105,7 @@ _GLOBAL(load_up_fpu) | |||
100 | #endif | 105 | #endif |
101 | lfd fr0,THREAD_FPSCR(r5) | 106 | lfd fr0,THREAD_FPSCR(r5) |
102 | MTFSF_L(fr0) | 107 | MTFSF_L(fr0) |
103 | REST_32FPRS(0, r5) | 108 | REST_32FPVSRS(0, r4, r5) |
104 | #ifndef CONFIG_SMP | 109 | #ifndef CONFIG_SMP |
105 | subi r4,r5,THREAD | 110 | subi r4,r5,THREAD |
106 | fromreal(r4) | 111 | fromreal(r4) |
@@ -119,6 +124,11 @@ _GLOBAL(load_up_fpu) | |||
119 | _GLOBAL(giveup_fpu) | 124 | _GLOBAL(giveup_fpu) |
120 | mfmsr r5 | 125 | mfmsr r5 |
121 | ori r5,r5,MSR_FP | 126 | ori r5,r5,MSR_FP |
127 | #ifdef CONFIG_VSX | ||
128 | BEGIN_FTR_SECTION | ||
129 | oris r5,r5,MSR_VSX@h | ||
130 | END_FTR_SECTION_IFSET(CPU_FTR_VSX) | ||
131 | #endif | ||
122 | SYNC_601 | 132 | SYNC_601 |
123 | ISYNC_601 | 133 | ISYNC_601 |
124 | MTMSRD(r5) /* enable use of fpu now */ | 134 | MTMSRD(r5) /* enable use of fpu now */ |
@@ -129,7 +139,7 @@ _GLOBAL(giveup_fpu) | |||
129 | addi r3,r3,THREAD /* want THREAD of task */ | 139 | addi r3,r3,THREAD /* want THREAD of task */ |
130 | PPC_LL r5,PT_REGS(r3) | 140 | PPC_LL r5,PT_REGS(r3) |
131 | PPC_LCMPI 0,r5,0 | 141 | PPC_LCMPI 0,r5,0 |
132 | SAVE_32FPRS(0, r3) | 142 | SAVE_32FPVSRS(0, r4 ,r3) |
133 | mffs fr0 | 143 | mffs fr0 |
134 | stfd fr0,THREAD_FPSCR(r3) | 144 | stfd fr0,THREAD_FPSCR(r3) |
135 | beq 1f | 145 | beq 1f |
diff --git a/arch/powerpc/kernel/head_64.S b/arch/powerpc/kernel/head_64.S index 64433731d995..ecced1eb03ae 100644 --- a/arch/powerpc/kernel/head_64.S +++ b/arch/powerpc/kernel/head_64.S | |||
@@ -278,6 +278,9 @@ END_FTR_SECTION_IFSET(CPU_FTR_REAL_LE) | |||
278 | . = 0xf20 | 278 | . = 0xf20 |
279 | b altivec_unavailable_pSeries | 279 | b altivec_unavailable_pSeries |
280 | 280 | ||
281 | . = 0xf40 | ||
282 | b vsx_unavailable_pSeries | ||
283 | |||
281 | #ifdef CONFIG_CBE_RAS | 284 | #ifdef CONFIG_CBE_RAS |
282 | HSTD_EXCEPTION_PSERIES(0x1200, cbe_system_error) | 285 | HSTD_EXCEPTION_PSERIES(0x1200, cbe_system_error) |
283 | #endif /* CONFIG_CBE_RAS */ | 286 | #endif /* CONFIG_CBE_RAS */ |
@@ -297,6 +300,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_REAL_LE) | |||
297 | /* moved from 0xf00 */ | 300 | /* moved from 0xf00 */ |
298 | STD_EXCEPTION_PSERIES(., performance_monitor) | 301 | STD_EXCEPTION_PSERIES(., performance_monitor) |
299 | STD_EXCEPTION_PSERIES(., altivec_unavailable) | 302 | STD_EXCEPTION_PSERIES(., altivec_unavailable) |
303 | STD_EXCEPTION_PSERIES(., vsx_unavailable) | ||
300 | 304 | ||
301 | /* | 305 | /* |
302 | * An interrupt came in while soft-disabled; clear EE in SRR1, | 306 | * An interrupt came in while soft-disabled; clear EE in SRR1, |
@@ -836,6 +840,67 @@ _STATIC(load_up_altivec) | |||
836 | blr | 840 | blr |
837 | #endif /* CONFIG_ALTIVEC */ | 841 | #endif /* CONFIG_ALTIVEC */ |
838 | 842 | ||
843 | .align 7 | ||
844 | .globl vsx_unavailable_common | ||
845 | vsx_unavailable_common: | ||
846 | EXCEPTION_PROLOG_COMMON(0xf40, PACA_EXGEN) | ||
847 | #ifdef CONFIG_VSX | ||
848 | BEGIN_FTR_SECTION | ||
849 | bne .load_up_vsx | ||
850 | 1: | ||
851 | END_FTR_SECTION_IFSET(CPU_FTR_VSX) | ||
852 | #endif | ||
853 | bl .save_nvgprs | ||
854 | addi r3,r1,STACK_FRAME_OVERHEAD | ||
855 | ENABLE_INTS | ||
856 | bl .vsx_unavailable_exception | ||
857 | b .ret_from_except | ||
858 | |||
859 | #ifdef CONFIG_VSX | ||
860 | /* | ||
861 | * load_up_vsx(unused, unused, tsk) | ||
862 | * Disable VSX for the task which had it previously, | ||
863 | * and save its vector registers in its thread_struct. | ||
864 | * Reuse the fp and vsx saves, but first check to see if they have | ||
865 | * been saved already. | ||
866 | * On entry: r13 == 'current' && last_task_used_vsx != 'current' | ||
867 | */ | ||
868 | _STATIC(load_up_vsx) | ||
869 | /* Load FP and VSX registers if they haven't been done yet */ | ||
870 | andi. r5,r12,MSR_FP | ||
871 | beql+ load_up_fpu /* skip if already loaded */ | ||
872 | andis. r5,r12,MSR_VEC@h | ||
873 | beql+ load_up_altivec /* skip if already loaded */ | ||
874 | |||
875 | #ifndef CONFIG_SMP | ||
876 | ld r3,last_task_used_vsx@got(r2) | ||
877 | ld r4,0(r3) | ||
878 | cmpdi 0,r4,0 | ||
879 | beq 1f | ||
880 | /* Disable VSX for last_task_used_vsx */ | ||
881 | addi r4,r4,THREAD | ||
882 | ld r5,PT_REGS(r4) | ||
883 | ld r4,_MSR-STACK_FRAME_OVERHEAD(r5) | ||
884 | lis r6,MSR_VSX@h | ||
885 | andc r6,r4,r6 | ||
886 | std r6,_MSR-STACK_FRAME_OVERHEAD(r5) | ||
887 | 1: | ||
888 | #endif /* CONFIG_SMP */ | ||
889 | ld r4,PACACURRENT(r13) | ||
890 | addi r4,r4,THREAD /* Get THREAD */ | ||
891 | li r6,1 | ||
892 | stw r6,THREAD_USED_VSR(r4) /* ... also set thread used vsr */ | ||
893 | /* enable use of VSX after return */ | ||
894 | oris r12,r12,MSR_VSX@h | ||
895 | std r12,_MSR(r1) | ||
896 | #ifndef CONFIG_SMP | ||
897 | /* Update last_task_used_math to 'current' */ | ||
898 | ld r4,PACACURRENT(r13) | ||
899 | std r4,0(r3) | ||
900 | #endif /* CONFIG_SMP */ | ||
901 | b fast_exception_return | ||
902 | #endif /* CONFIG_VSX */ | ||
903 | |||
839 | /* | 904 | /* |
840 | * Hash table stuff | 905 | * Hash table stuff |
841 | */ | 906 | */ |
diff --git a/arch/powerpc/kernel/misc_64.S b/arch/powerpc/kernel/misc_64.S index 942951e76586..31b9026cf1e3 100644 --- a/arch/powerpc/kernel/misc_64.S +++ b/arch/powerpc/kernel/misc_64.S | |||
@@ -506,6 +506,39 @@ _GLOBAL(giveup_altivec) | |||
506 | 506 | ||
507 | #endif /* CONFIG_ALTIVEC */ | 507 | #endif /* CONFIG_ALTIVEC */ |
508 | 508 | ||
509 | #ifdef CONFIG_VSX | ||
510 | /* | ||
511 | * giveup_vsx(tsk) | ||
512 | * Disable VSX for the task given as the argument, | ||
513 | * and save the vector registers in its thread_struct. | ||
514 | * Enables the VSX for use in the kernel on return. | ||
515 | */ | ||
516 | _GLOBAL(giveup_vsx) | ||
517 | mfmsr r5 | ||
518 | oris r5,r5,MSR_VSX@h | ||
519 | mtmsrd r5 /* enable use of VSX now */ | ||
520 | isync | ||
521 | |||
522 | cmpdi 0,r3,0 | ||
523 | beqlr- /* if no previous owner, done */ | ||
524 | addi r3,r3,THREAD /* want THREAD of task */ | ||
525 | ld r5,PT_REGS(r3) | ||
526 | cmpdi 0,r5,0 | ||
527 | beq 1f | ||
528 | ld r4,_MSR-STACK_FRAME_OVERHEAD(r5) | ||
529 | lis r3,MSR_VSX@h | ||
530 | andc r4,r4,r3 /* disable VSX for previous task */ | ||
531 | std r4,_MSR-STACK_FRAME_OVERHEAD(r5) | ||
532 | 1: | ||
533 | #ifndef CONFIG_SMP | ||
534 | li r5,0 | ||
535 | ld r4,last_task_used_vsx@got(r2) | ||
536 | std r5,0(r4) | ||
537 | #endif /* CONFIG_SMP */ | ||
538 | blr | ||
539 | |||
540 | #endif /* CONFIG_VSX */ | ||
541 | |||
509 | /* kexec_wait(phys_cpu) | 542 | /* kexec_wait(phys_cpu) |
510 | * | 543 | * |
511 | * wait for the flag to change, indicating this kernel is going away but | 544 | * wait for the flag to change, indicating this kernel is going away but |
diff --git a/arch/powerpc/kernel/ppc32.h b/arch/powerpc/kernel/ppc32.h index 90e562771791..dc16aefe1dd0 100644 --- a/arch/powerpc/kernel/ppc32.h +++ b/arch/powerpc/kernel/ppc32.h | |||
@@ -120,6 +120,7 @@ struct mcontext32 { | |||
120 | elf_fpregset_t mc_fregs; | 120 | elf_fpregset_t mc_fregs; |
121 | unsigned int mc_pad[2]; | 121 | unsigned int mc_pad[2]; |
122 | elf_vrregset_t32 mc_vregs __attribute__((__aligned__(16))); | 122 | elf_vrregset_t32 mc_vregs __attribute__((__aligned__(16))); |
123 | elf_vsrreghalf_t32 mc_vsregs __attribute__((__aligned__(16))); | ||
123 | }; | 124 | }; |
124 | 125 | ||
125 | struct ucontext32 { | 126 | struct ucontext32 { |
diff --git a/arch/powerpc/kernel/ppc_ksyms.c b/arch/powerpc/kernel/ppc_ksyms.c index d3ac631cbd26..958ecb9ae7dc 100644 --- a/arch/powerpc/kernel/ppc_ksyms.c +++ b/arch/powerpc/kernel/ppc_ksyms.c | |||
@@ -102,6 +102,9 @@ EXPORT_SYMBOL(giveup_fpu); | |||
102 | #ifdef CONFIG_ALTIVEC | 102 | #ifdef CONFIG_ALTIVEC |
103 | EXPORT_SYMBOL(giveup_altivec); | 103 | EXPORT_SYMBOL(giveup_altivec); |
104 | #endif /* CONFIG_ALTIVEC */ | 104 | #endif /* CONFIG_ALTIVEC */ |
105 | #ifdef CONFIG_VSX | ||
106 | EXPORT_SYMBOL(giveup_vsx); | ||
107 | #endif /* CONFIG_VSX */ | ||
105 | #ifdef CONFIG_SPE | 108 | #ifdef CONFIG_SPE |
106 | EXPORT_SYMBOL(giveup_spe); | 109 | EXPORT_SYMBOL(giveup_spe); |
107 | #endif /* CONFIG_SPE */ | 110 | #endif /* CONFIG_SPE */ |
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 |
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index 70fbde84b83f..4e203a89e189 100644 --- a/arch/powerpc/kernel/ptrace.c +++ b/arch/powerpc/kernel/ptrace.c | |||
@@ -350,6 +350,51 @@ static int vr_set(struct task_struct *target, const struct user_regset *regset, | |||
350 | } | 350 | } |
351 | #endif /* CONFIG_ALTIVEC */ | 351 | #endif /* CONFIG_ALTIVEC */ |
352 | 352 | ||
353 | #ifdef CONFIG_VSX | ||
354 | /* | ||
355 | * Currently to set and and get all the vsx state, you need to call | ||
356 | * the fp and VMX calls aswell. This only get/sets the lower 32 | ||
357 | * 128bit VSX registers. | ||
358 | */ | ||
359 | |||
360 | static int vsr_active(struct task_struct *target, | ||
361 | const struct user_regset *regset) | ||
362 | { | ||
363 | flush_vsx_to_thread(target); | ||
364 | return target->thread.used_vsr ? regset->n : 0; | ||
365 | } | ||
366 | |||
367 | static int vsr_get(struct task_struct *target, const struct user_regset *regset, | ||
368 | unsigned int pos, unsigned int count, | ||
369 | void *kbuf, void __user *ubuf) | ||
370 | { | ||
371 | int ret; | ||
372 | |||
373 | flush_vsx_to_thread(target); | ||
374 | |||
375 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
376 | target->thread.fpr, 0, | ||
377 | 32 * sizeof(vector128)); | ||
378 | |||
379 | return ret; | ||
380 | } | ||
381 | |||
382 | static int vsr_set(struct task_struct *target, const struct user_regset *regset, | ||
383 | unsigned int pos, unsigned int count, | ||
384 | const void *kbuf, const void __user *ubuf) | ||
385 | { | ||
386 | int ret; | ||
387 | |||
388 | flush_vsx_to_thread(target); | ||
389 | |||
390 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
391 | target->thread.fpr, 0, | ||
392 | 32 * sizeof(vector128)); | ||
393 | |||
394 | return ret; | ||
395 | } | ||
396 | #endif /* CONFIG_VSX */ | ||
397 | |||
353 | #ifdef CONFIG_SPE | 398 | #ifdef CONFIG_SPE |
354 | 399 | ||
355 | /* | 400 | /* |
@@ -426,6 +471,9 @@ enum powerpc_regset { | |||
426 | #ifdef CONFIG_ALTIVEC | 471 | #ifdef CONFIG_ALTIVEC |
427 | REGSET_VMX, | 472 | REGSET_VMX, |
428 | #endif | 473 | #endif |
474 | #ifdef CONFIG_VSX | ||
475 | REGSET_VSX, | ||
476 | #endif | ||
429 | #ifdef CONFIG_SPE | 477 | #ifdef CONFIG_SPE |
430 | REGSET_SPE, | 478 | REGSET_SPE, |
431 | #endif | 479 | #endif |
@@ -449,6 +497,13 @@ static const struct user_regset native_regsets[] = { | |||
449 | .active = vr_active, .get = vr_get, .set = vr_set | 497 | .active = vr_active, .get = vr_get, .set = vr_set |
450 | }, | 498 | }, |
451 | #endif | 499 | #endif |
500 | #ifdef CONFIG_VSX | ||
501 | [REGSET_VSX] = { | ||
502 | .n = 32, | ||
503 | .size = sizeof(vector128), .align = sizeof(vector128), | ||
504 | .active = vsr_active, .get = vsr_get, .set = vsr_set | ||
505 | }, | ||
506 | #endif | ||
452 | #ifdef CONFIG_SPE | 507 | #ifdef CONFIG_SPE |
453 | [REGSET_SPE] = { | 508 | [REGSET_SPE] = { |
454 | .n = 35, | 509 | .n = 35, |
@@ -849,6 +904,21 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
849 | sizeof(u32)), | 904 | sizeof(u32)), |
850 | (const void __user *) data); | 905 | (const void __user *) data); |
851 | #endif | 906 | #endif |
907 | #ifdef CONFIG_VSX | ||
908 | case PTRACE_GETVSRREGS: | ||
909 | return copy_regset_to_user(child, &user_ppc_native_view, | ||
910 | REGSET_VSX, | ||
911 | 0, (32 * sizeof(vector128) + | ||
912 | sizeof(u32)), | ||
913 | (void __user *) data); | ||
914 | |||
915 | case PTRACE_SETVSRREGS: | ||
916 | return copy_regset_from_user(child, &user_ppc_native_view, | ||
917 | REGSET_VSX, | ||
918 | 0, (32 * sizeof(vector128) + | ||
919 | sizeof(u32)), | ||
920 | (const void __user *) data); | ||
921 | #endif | ||
852 | #ifdef CONFIG_SPE | 922 | #ifdef CONFIG_SPE |
853 | case PTRACE_GETEVRREGS: | 923 | case PTRACE_GETEVRREGS: |
854 | /* Get the child spe register state. */ | 924 | /* Get the child spe register state. */ |
diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c index f7fa395b9fb5..349d3487d920 100644 --- a/arch/powerpc/kernel/signal_32.c +++ b/arch/powerpc/kernel/signal_32.c | |||
@@ -378,6 +378,21 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame, | |||
378 | memcpy(&buf[i], ¤t->thread.fpscr, sizeof(double)); | 378 | memcpy(&buf[i], ¤t->thread.fpscr, sizeof(double)); |
379 | if (__copy_to_user(&frame->mc_fregs, buf, ELF_NFPREG * sizeof(double))) | 379 | if (__copy_to_user(&frame->mc_fregs, buf, ELF_NFPREG * sizeof(double))) |
380 | return 1; | 380 | return 1; |
381 | /* | ||
382 | * Copy VSR 0-31 upper half from thread_struct to local | ||
383 | * buffer, then write that to userspace. Also set MSR_VSX in | ||
384 | * the saved MSR value to indicate that frame->mc_vregs | ||
385 | * contains valid data | ||
386 | */ | ||
387 | if (current->thread.used_vsr) { | ||
388 | flush_vsx_to_thread(current); | ||
389 | for (i = 0; i < 32 ; i++) | ||
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; | ||
394 | msr |= MSR_VSX; | ||
395 | } | ||
381 | #else | 396 | #else |
382 | /* save floating-point registers */ | 397 | /* save floating-point registers */ |
383 | if (__copy_to_user(&frame->mc_fregs, current->thread.fpr, | 398 | if (__copy_to_user(&frame->mc_fregs, current->thread.fpr, |
@@ -482,6 +497,24 @@ static long restore_user_regs(struct pt_regs *regs, | |||
482 | for (i = 0; i < 32 ; i++) | 497 | for (i = 0; i < 32 ; i++) |
483 | current->thread.TS_FPR(i) = buf[i]; | 498 | current->thread.TS_FPR(i) = buf[i]; |
484 | memcpy(¤t->thread.fpscr, &buf[i], sizeof(double)); | 499 | memcpy(¤t->thread.fpscr, &buf[i], sizeof(double)); |
500 | /* | ||
501 | * Force the process to reload the VSX registers from | ||
502 | * current->thread when it next does VSX instruction. | ||
503 | */ | ||
504 | regs->msr &= ~MSR_VSX; | ||
505 | if (msr & MSR_VSX) { | ||
506 | /* | ||
507 | * Restore altivec registers from the stack to a local | ||
508 | * buffer, then write this out to the thread_struct | ||
509 | */ | ||
510 | if (__copy_from_user(buf, &sr->mc_vsregs, | ||
511 | sizeof(sr->mc_vsregs))) | ||
512 | 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) | ||
516 | for (i = 0; i < 32 ; i++) | ||
517 | current->thread.fpr[i][TS_VSRLOWOFFSET] = 0; | ||
485 | #else | 518 | #else |
486 | if (__copy_from_user(current->thread.fpr, &sr->mc_fregs, | 519 | if (__copy_from_user(current->thread.fpr, &sr->mc_fregs, |
487 | sizeof(sr->mc_fregs))) | 520 | sizeof(sr->mc_fregs))) |
diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c index a587b33cd6b9..34f37e59bacc 100644 --- a/arch/powerpc/kernel/signal_64.c +++ b/arch/powerpc/kernel/signal_64.c | |||
@@ -123,6 +123,22 @@ static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, | |||
123 | buf[i] = current->thread.TS_FPR(i); | 123 | buf[i] = current->thread.TS_FPR(i); |
124 | memcpy(&buf[i], ¤t->thread.fpscr, sizeof(double)); | 124 | memcpy(&buf[i], ¤t->thread.fpscr, sizeof(double)); |
125 | err |= __copy_to_user(&sc->fp_regs, buf, FP_REGS_SIZE); | 125 | err |= __copy_to_user(&sc->fp_regs, buf, FP_REGS_SIZE); |
126 | /* | ||
127 | * Copy VSX low doubleword to local buffer for formatting, | ||
128 | * then out to userspace. Update v_regs to point after the | ||
129 | * VMX data. | ||
130 | */ | ||
131 | if (current->thread.used_vsr) { | ||
132 | flush_vsx_to_thread(current); | ||
133 | v_regs += ELF_NVRREG; | ||
134 | for (i = 0; i < 32 ; i++) | ||
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 | ||
138 | * indicate that sc->vs_reg) contains valid data. | ||
139 | */ | ||
140 | msr |= MSR_VSX; | ||
141 | } | ||
126 | #else /* CONFIG_VSX */ | 142 | #else /* CONFIG_VSX */ |
127 | /* copy fpr regs and fpscr */ | 143 | /* copy fpr regs and fpscr */ |
128 | err |= __copy_to_user(&sc->fp_regs, ¤t->thread.fpr, FP_REGS_SIZE); | 144 | err |= __copy_to_user(&sc->fp_regs, ¤t->thread.fpr, FP_REGS_SIZE); |
@@ -197,7 +213,7 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig, | |||
197 | * This has to be done before copying stuff into current->thread.fpr/vr | 213 | * This has to be done before copying stuff into current->thread.fpr/vr |
198 | * for the reasons explained in the previous comment. | 214 | * for the reasons explained in the previous comment. |
199 | */ | 215 | */ |
200 | regs->msr &= ~(MSR_FP | MSR_FE0 | MSR_FE1 | MSR_VEC); | 216 | regs->msr &= ~(MSR_FP | MSR_FE0 | MSR_FE1 | MSR_VEC | MSR_VSX); |
201 | 217 | ||
202 | #ifdef CONFIG_ALTIVEC | 218 | #ifdef CONFIG_ALTIVEC |
203 | err |= __get_user(v_regs, &sc->v_regs); | 219 | err |= __get_user(v_regs, &sc->v_regs); |
@@ -226,6 +242,19 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig, | |||
226 | current->thread.TS_FPR(i) = buf[i]; | 242 | current->thread.TS_FPR(i) = buf[i]; |
227 | memcpy(¤t->thread.fpscr, &buf[i], sizeof(double)); | 243 | memcpy(¤t->thread.fpscr, &buf[i], sizeof(double)); |
228 | 244 | ||
245 | /* | ||
246 | * Get additional VSX data. Update v_regs to point after the | ||
247 | * VMX data. Copy VSX low doubleword from userspace to local | ||
248 | * buffer for formatting, then into the taskstruct. | ||
249 | */ | ||
250 | v_regs += ELF_NVRREG; | ||
251 | if ((msr & MSR_VSX) != 0) | ||
252 | err |= __copy_from_user(buf, v_regs, 32 * sizeof(double)); | ||
253 | else | ||
254 | memset(buf, 0, 32 * sizeof(double)); | ||
255 | |||
256 | for (i = 0; i < 32 ; i++) | ||
257 | current->thread.fpr[i][TS_VSRLOWOFFSET] = buf[i]; | ||
229 | #else | 258 | #else |
230 | err |= __copy_from_user(¤t->thread.fpr, &sc->fp_regs, FP_REGS_SIZE); | 259 | err |= __copy_from_user(¤t->thread.fpr, &sc->fp_regs, FP_REGS_SIZE); |
231 | #endif | 260 | #endif |
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index b463d48145a4..878fbddb6ae1 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c | |||
@@ -967,6 +967,20 @@ void altivec_unavailable_exception(struct pt_regs *regs) | |||
967 | die("Unrecoverable VMX/Altivec Unavailable Exception", regs, SIGABRT); | 967 | die("Unrecoverable VMX/Altivec Unavailable Exception", regs, SIGABRT); |
968 | } | 968 | } |
969 | 969 | ||
970 | void vsx_unavailable_exception(struct pt_regs *regs) | ||
971 | { | ||
972 | if (user_mode(regs)) { | ||
973 | /* A user program has executed an vsx instruction, | ||
974 | but this kernel doesn't support vsx. */ | ||
975 | _exception(SIGILL, regs, ILL_ILLOPC, regs->nip); | ||
976 | return; | ||
977 | } | ||
978 | |||
979 | printk(KERN_EMERG "Unrecoverable VSX Unavailable Exception " | ||
980 | "%lx at %lx\n", regs->trap, regs->nip); | ||
981 | die("Unrecoverable VSX Unavailable Exception", regs, SIGABRT); | ||
982 | } | ||
983 | |||
970 | void performance_monitor_exception(struct pt_regs *regs) | 984 | void performance_monitor_exception(struct pt_regs *regs) |
971 | { | 985 | { |
972 | perf_irq(regs); | 986 | perf_irq(regs); |
@@ -1099,6 +1113,21 @@ void altivec_assist_exception(struct pt_regs *regs) | |||
1099 | } | 1113 | } |
1100 | #endif /* CONFIG_ALTIVEC */ | 1114 | #endif /* CONFIG_ALTIVEC */ |
1101 | 1115 | ||
1116 | #ifdef CONFIG_VSX | ||
1117 | void vsx_assist_exception(struct pt_regs *regs) | ||
1118 | { | ||
1119 | if (!user_mode(regs)) { | ||
1120 | printk(KERN_EMERG "VSX assist exception in kernel mode" | ||
1121 | " at %lx\n", regs->nip); | ||
1122 | die("Kernel VSX assist exception", regs, SIGILL); | ||
1123 | } | ||
1124 | |||
1125 | flush_vsx_to_thread(current); | ||
1126 | printk(KERN_INFO "VSX assist not supported at %lx\n", regs->nip); | ||
1127 | _exception(SIGILL, regs, ILL_ILLOPC, regs->nip); | ||
1128 | } | ||
1129 | #endif /* CONFIG_VSX */ | ||
1130 | |||
1102 | #ifdef CONFIG_FSL_BOOKE | 1131 | #ifdef CONFIG_FSL_BOOKE |
1103 | void CacheLockingException(struct pt_regs *regs, unsigned long address, | 1132 | void CacheLockingException(struct pt_regs *regs, unsigned long address, |
1104 | unsigned long error_code) | 1133 | unsigned long error_code) |