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 | |
| 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>
| -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 | ||||
| -rw-r--r-- | include/asm-powerpc/elf.h | 6 | ||||
| -rw-r--r-- | include/asm-powerpc/ptrace.h | 12 | ||||
| -rw-r--r-- | include/asm-powerpc/reg.h | 2 | ||||
| -rw-r--r-- | include/asm-powerpc/sigcontext.h | 37 | ||||
| -rw-r--r-- | include/asm-powerpc/system.h | 9 |
16 files changed, 451 insertions, 8 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) |
diff --git a/include/asm-powerpc/elf.h b/include/asm-powerpc/elf.h index d1e3bda0625d..746e53d60cbe 100644 --- a/include/asm-powerpc/elf.h +++ b/include/asm-powerpc/elf.h | |||
| @@ -109,6 +109,7 @@ typedef elf_gregset_t32 compat_elf_gregset_t; | |||
| 109 | #ifdef __powerpc64__ | 109 | #ifdef __powerpc64__ |
| 110 | # define ELF_NVRREG32 33 /* includes vscr & vrsave stuffed together */ | 110 | # define ELF_NVRREG32 33 /* includes vscr & vrsave stuffed together */ |
| 111 | # define ELF_NVRREG 34 /* includes vscr & vrsave in split vectors */ | 111 | # define ELF_NVRREG 34 /* includes vscr & vrsave in split vectors */ |
| 112 | # define ELF_NVSRHALFREG 32 /* Half the vsx registers */ | ||
| 112 | # define ELF_GREG_TYPE elf_greg_t64 | 113 | # define ELF_GREG_TYPE elf_greg_t64 |
| 113 | #else | 114 | #else |
| 114 | # define ELF_NEVRREG 34 /* includes acc (as 2) */ | 115 | # define ELF_NEVRREG 34 /* includes acc (as 2) */ |
| @@ -158,6 +159,7 @@ typedef __vector128 elf_vrreg_t; | |||
| 158 | typedef elf_vrreg_t elf_vrregset_t[ELF_NVRREG]; | 159 | typedef elf_vrreg_t elf_vrregset_t[ELF_NVRREG]; |
| 159 | #ifdef __powerpc64__ | 160 | #ifdef __powerpc64__ |
| 160 | typedef elf_vrreg_t elf_vrregset_t32[ELF_NVRREG32]; | 161 | typedef elf_vrreg_t elf_vrregset_t32[ELF_NVRREG32]; |
| 162 | typedef elf_fpreg_t elf_vsrreghalf_t32[ELF_NVSRHALFREG]; | ||
| 161 | #endif | 163 | #endif |
| 162 | 164 | ||
| 163 | #ifdef __KERNEL__ | 165 | #ifdef __KERNEL__ |
| @@ -219,8 +221,8 @@ extern int dump_task_fpu(struct task_struct *, elf_fpregset_t *); | |||
| 219 | typedef elf_vrregset_t elf_fpxregset_t; | 221 | typedef elf_vrregset_t elf_fpxregset_t; |
| 220 | 222 | ||
| 221 | #ifdef CONFIG_ALTIVEC | 223 | #ifdef CONFIG_ALTIVEC |
| 222 | extern int dump_task_altivec(struct task_struct *, elf_vrregset_t *vrregs); | 224 | extern int dump_task_vector(struct task_struct *, elf_vrregset_t *vrregs); |
| 223 | #define ELF_CORE_COPY_XFPREGS(tsk, regs) dump_task_altivec(tsk, regs) | 225 | #define ELF_CORE_COPY_XFPREGS(tsk, regs) dump_task_vector(tsk, regs) |
| 224 | #define ELF_CORE_XFPREG_TYPE NT_PPC_VMX | 226 | #define ELF_CORE_XFPREG_TYPE NT_PPC_VMX |
| 225 | #endif | 227 | #endif |
| 226 | 228 | ||
diff --git a/include/asm-powerpc/ptrace.h b/include/asm-powerpc/ptrace.h index 38d87e5e569d..3d6e31024e56 100644 --- a/include/asm-powerpc/ptrace.h +++ b/include/asm-powerpc/ptrace.h | |||
| @@ -224,6 +224,14 @@ extern void user_disable_single_step(struct task_struct *); | |||
| 224 | #define PT_VRSAVE_32 (PT_VR0 + 33*4) | 224 | #define PT_VRSAVE_32 (PT_VR0 + 33*4) |
| 225 | #endif | 225 | #endif |
| 226 | 226 | ||
| 227 | /* | ||
| 228 | * Only store first 32 VSRs here. The second 32 VSRs in VR0-31 | ||
| 229 | */ | ||
| 230 | #define PT_VSR0 150 /* each VSR reg occupies 2 slots in 64-bit */ | ||
| 231 | #define PT_VSR31 (PT_VSR0 + 2*31) | ||
| 232 | #ifdef __KERNEL__ | ||
| 233 | #define PT_VSR0_32 300 /* each VSR reg occupies 4 slots in 32-bit */ | ||
| 234 | #endif | ||
| 227 | #endif /* __powerpc64__ */ | 235 | #endif /* __powerpc64__ */ |
| 228 | 236 | ||
| 229 | /* | 237 | /* |
| @@ -246,6 +254,10 @@ extern void user_disable_single_step(struct task_struct *); | |||
| 246 | #define PTRACE_GETEVRREGS 20 | 254 | #define PTRACE_GETEVRREGS 20 |
| 247 | #define PTRACE_SETEVRREGS 21 | 255 | #define PTRACE_SETEVRREGS 21 |
| 248 | 256 | ||
| 257 | /* Get the first 32 128bit VSX registers */ | ||
| 258 | #define PTRACE_GETVSRREGS 27 | ||
| 259 | #define PTRACE_SETVSRREGS 28 | ||
| 260 | |||
| 249 | /* | 261 | /* |
| 250 | * Get or set a debug register. The first 16 are DABR registers and the | 262 | * Get or set a debug register. The first 16 are DABR registers and the |
| 251 | * second 16 are IABR registers. | 263 | * second 16 are IABR registers. |
diff --git a/include/asm-powerpc/reg.h b/include/asm-powerpc/reg.h index 7256efb5c140..bbccadfee0d6 100644 --- a/include/asm-powerpc/reg.h +++ b/include/asm-powerpc/reg.h | |||
| @@ -30,6 +30,7 @@ | |||
| 30 | #define MSR_ISF_LG 61 /* Interrupt 64b mode valid on 630 */ | 30 | #define MSR_ISF_LG 61 /* Interrupt 64b mode valid on 630 */ |
| 31 | #define MSR_HV_LG 60 /* Hypervisor state */ | 31 | #define MSR_HV_LG 60 /* Hypervisor state */ |
| 32 | #define MSR_VEC_LG 25 /* Enable AltiVec */ | 32 | #define MSR_VEC_LG 25 /* Enable AltiVec */ |
| 33 | #define MSR_VSX_LG 23 /* Enable VSX */ | ||
| 33 | #define MSR_POW_LG 18 /* Enable Power Management */ | 34 | #define MSR_POW_LG 18 /* Enable Power Management */ |
| 34 | #define MSR_WE_LG 18 /* Wait State Enable */ | 35 | #define MSR_WE_LG 18 /* Wait State Enable */ |
| 35 | #define MSR_TGPR_LG 17 /* TLB Update registers in use */ | 36 | #define MSR_TGPR_LG 17 /* TLB Update registers in use */ |
| @@ -71,6 +72,7 @@ | |||
| 71 | #endif | 72 | #endif |
| 72 | 73 | ||
| 73 | #define MSR_VEC __MASK(MSR_VEC_LG) /* Enable AltiVec */ | 74 | #define MSR_VEC __MASK(MSR_VEC_LG) /* Enable AltiVec */ |
| 75 | #define MSR_VSX __MASK(MSR_VSX_LG) /* Enable VSX */ | ||
| 74 | #define MSR_POW __MASK(MSR_POW_LG) /* Enable Power Management */ | 76 | #define MSR_POW __MASK(MSR_POW_LG) /* Enable Power Management */ |
| 75 | #define MSR_WE __MASK(MSR_WE_LG) /* Wait State Enable */ | 77 | #define MSR_WE __MASK(MSR_WE_LG) /* Wait State Enable */ |
| 76 | #define MSR_TGPR __MASK(MSR_TGPR_LG) /* TLB Update registers in use */ | 78 | #define MSR_TGPR __MASK(MSR_TGPR_LG) /* TLB Update registers in use */ |
diff --git a/include/asm-powerpc/sigcontext.h b/include/asm-powerpc/sigcontext.h index 165d630e1cf3..9c1f24fd5d11 100644 --- a/include/asm-powerpc/sigcontext.h +++ b/include/asm-powerpc/sigcontext.h | |||
| @@ -43,9 +43,44 @@ struct sigcontext { | |||
| 43 | * it must be copied via a vector register to/from storage) or as a word. | 43 | * it must be copied via a vector register to/from storage) or as a word. |
| 44 | * The entry with index 33 contains the vrsave as the first word (offset 0) | 44 | * The entry with index 33 contains the vrsave as the first word (offset 0) |
| 45 | * within the quadword. | 45 | * within the quadword. |
| 46 | * | ||
| 47 | * Part of the VSX data is stored here also by extending vmx_restore | ||
| 48 | * by an additional 32 double words. Architecturally the layout of | ||
| 49 | * the VSR registers and how they overlap on top of the legacy FPR and | ||
| 50 | * VR registers is shown below: | ||
| 51 | * | ||
| 52 | * VSR doubleword 0 VSR doubleword 1 | ||
| 53 | * ---------------------------------------------------------------- | ||
| 54 | * VSR[0] | FPR[0] | | | ||
| 55 | * ---------------------------------------------------------------- | ||
| 56 | * VSR[1] | FPR[1] | | | ||
| 57 | * ---------------------------------------------------------------- | ||
| 58 | * | ... | | | ||
| 59 | * | ... | | | ||
| 60 | * ---------------------------------------------------------------- | ||
| 61 | * VSR[30] | FPR[30] | | | ||
| 62 | * ---------------------------------------------------------------- | ||
| 63 | * VSR[31] | FPR[31] | | | ||
| 64 | * ---------------------------------------------------------------- | ||
| 65 | * VSR[32] | VR[0] | | ||
| 66 | * ---------------------------------------------------------------- | ||
| 67 | * VSR[33] | VR[1] | | ||
| 68 | * ---------------------------------------------------------------- | ||
| 69 | * | ... | | ||
| 70 | * | ... | | ||
| 71 | * ---------------------------------------------------------------- | ||
| 72 | * VSR[62] | VR[30] | | ||
| 73 | * ---------------------------------------------------------------- | ||
| 74 | * VSR[63] | VR[31] | | ||
| 75 | * ---------------------------------------------------------------- | ||
| 76 | * | ||
| 77 | * FPR/VSR 0-31 doubleword 0 is stored in fp_regs, and VMX/VSR 32-63 | ||
| 78 | * is stored at the start of vmx_reserve. vmx_reserve is extended for | ||
| 79 | * backwards compatility to store VSR 0-31 doubleword 1 after the VMX | ||
| 80 | * registers and vscr/vrsave. | ||
| 46 | */ | 81 | */ |
| 47 | elf_vrreg_t __user *v_regs; | 82 | elf_vrreg_t __user *v_regs; |
| 48 | long vmx_reserve[ELF_NVRREG+ELF_NVRREG+1]; | 83 | long vmx_reserve[ELF_NVRREG+ELF_NVRREG+32+1]; |
| 49 | #endif | 84 | #endif |
| 50 | }; | 85 | }; |
| 51 | 86 | ||
diff --git a/include/asm-powerpc/system.h b/include/asm-powerpc/system.h index 2642a92b724f..0c12c66733f6 100644 --- a/include/asm-powerpc/system.h +++ b/include/asm-powerpc/system.h | |||
| @@ -139,6 +139,7 @@ extern void enable_kernel_altivec(void); | |||
| 139 | extern void giveup_altivec(struct task_struct *); | 139 | extern void giveup_altivec(struct task_struct *); |
| 140 | extern void load_up_altivec(struct task_struct *); | 140 | extern void load_up_altivec(struct task_struct *); |
| 141 | extern int emulate_altivec(struct pt_regs *); | 141 | extern int emulate_altivec(struct pt_regs *); |
| 142 | extern void giveup_vsx(struct task_struct *); | ||
| 142 | extern void enable_kernel_spe(void); | 143 | extern void enable_kernel_spe(void); |
| 143 | extern void giveup_spe(struct task_struct *); | 144 | extern void giveup_spe(struct task_struct *); |
| 144 | extern void load_up_spe(struct task_struct *); | 145 | extern void load_up_spe(struct task_struct *); |
| @@ -162,6 +163,14 @@ static inline void flush_altivec_to_thread(struct task_struct *t) | |||
| 162 | } | 163 | } |
| 163 | #endif | 164 | #endif |
| 164 | 165 | ||
| 166 | #ifdef CONFIG_VSX | ||
| 167 | extern void flush_vsx_to_thread(struct task_struct *); | ||
| 168 | #else | ||
| 169 | static inline void flush_vsx_to_thread(struct task_struct *t) | ||
| 170 | { | ||
| 171 | } | ||
| 172 | #endif | ||
| 173 | |||
| 165 | #ifdef CONFIG_SPE | 174 | #ifdef CONFIG_SPE |
| 166 | extern void flush_spe_to_thread(struct task_struct *); | 175 | extern void flush_spe_to_thread(struct task_struct *); |
| 167 | #else | 176 | #else |
