diff options
Diffstat (limited to 'arch/powerpc/kernel/vector.S')
-rw-r--r-- | arch/powerpc/kernel/vector.S | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/arch/powerpc/kernel/vector.S b/arch/powerpc/kernel/vector.S index 49ac3d6e1399..ef36cbbc5882 100644 --- a/arch/powerpc/kernel/vector.S +++ b/arch/powerpc/kernel/vector.S | |||
@@ -1,5 +1,215 @@ | |||
1 | #include <asm/processor.h> | ||
1 | #include <asm/ppc_asm.h> | 2 | #include <asm/ppc_asm.h> |
2 | #include <asm/reg.h> | 3 | #include <asm/reg.h> |
4 | #include <asm/asm-offsets.h> | ||
5 | #include <asm/cputable.h> | ||
6 | #include <asm/thread_info.h> | ||
7 | #include <asm/page.h> | ||
8 | |||
9 | /* | ||
10 | * load_up_altivec(unused, unused, tsk) | ||
11 | * Disable VMX for the task which had it previously, | ||
12 | * and save its vector registers in its thread_struct. | ||
13 | * Enables the VMX for use in the kernel on return. | ||
14 | * On SMP we know the VMX is free, since we give it up every | ||
15 | * switch (ie, no lazy save of the vector registers). | ||
16 | */ | ||
17 | _GLOBAL(load_up_altivec) | ||
18 | mfmsr r5 /* grab the current MSR */ | ||
19 | oris r5,r5,MSR_VEC@h | ||
20 | MTMSRD(r5) /* enable use of AltiVec now */ | ||
21 | isync | ||
22 | |||
23 | /* | ||
24 | * For SMP, we don't do lazy VMX switching because it just gets too | ||
25 | * horrendously complex, especially when a task switches from one CPU | ||
26 | * to another. Instead we call giveup_altvec in switch_to. | ||
27 | * VRSAVE isn't dealt with here, that is done in the normal context | ||
28 | * switch code. Note that we could rely on vrsave value to eventually | ||
29 | * avoid saving all of the VREGs here... | ||
30 | */ | ||
31 | #ifndef CONFIG_SMP | ||
32 | LOAD_REG_ADDRBASE(r3, last_task_used_altivec) | ||
33 | toreal(r3) | ||
34 | PPC_LL r4,ADDROFF(last_task_used_altivec)(r3) | ||
35 | PPC_LCMPI 0,r4,0 | ||
36 | beq 1f | ||
37 | |||
38 | /* Save VMX state to last_task_used_altivec's THREAD struct */ | ||
39 | toreal(r4) | ||
40 | addi r4,r4,THREAD | ||
41 | SAVE_32VRS(0,r5,r4) | ||
42 | mfvscr vr0 | ||
43 | li r10,THREAD_VSCR | ||
44 | stvx vr0,r10,r4 | ||
45 | /* Disable VMX for last_task_used_altivec */ | ||
46 | PPC_LL r5,PT_REGS(r4) | ||
47 | toreal(r5) | ||
48 | PPC_LL r4,_MSR-STACK_FRAME_OVERHEAD(r5) | ||
49 | lis r10,MSR_VEC@h | ||
50 | andc r4,r4,r10 | ||
51 | PPC_STL r4,_MSR-STACK_FRAME_OVERHEAD(r5) | ||
52 | 1: | ||
53 | #endif /* CONFIG_SMP */ | ||
54 | |||
55 | /* Hack: if we get an altivec unavailable trap with VRSAVE | ||
56 | * set to all zeros, we assume this is a broken application | ||
57 | * that fails to set it properly, and thus we switch it to | ||
58 | * all 1's | ||
59 | */ | ||
60 | mfspr r4,SPRN_VRSAVE | ||
61 | cmpdi 0,r4,0 | ||
62 | bne+ 1f | ||
63 | li r4,-1 | ||
64 | mtspr SPRN_VRSAVE,r4 | ||
65 | 1: | ||
66 | /* enable use of VMX after return */ | ||
67 | #ifdef CONFIG_PPC32 | ||
68 | mfspr r5,SPRN_SPRG3 /* current task's THREAD (phys) */ | ||
69 | oris r9,r9,MSR_VEC@h | ||
70 | #else | ||
71 | ld r4,PACACURRENT(r13) | ||
72 | addi r5,r4,THREAD /* Get THREAD */ | ||
73 | oris r12,r12,MSR_VEC@h | ||
74 | std r12,_MSR(r1) | ||
75 | #endif | ||
76 | li r4,1 | ||
77 | li r10,THREAD_VSCR | ||
78 | stw r4,THREAD_USED_VR(r5) | ||
79 | lvx vr0,r10,r5 | ||
80 | mtvscr vr0 | ||
81 | REST_32VRS(0,r4,r5) | ||
82 | #ifndef CONFIG_SMP | ||
83 | /* Update last_task_used_math to 'current' */ | ||
84 | subi r4,r5,THREAD /* Back to 'current' */ | ||
85 | fromreal(r4) | ||
86 | PPC_STL r4,ADDROFF(last_task_used_math)(r3) | ||
87 | #endif /* CONFIG_SMP */ | ||
88 | /* restore registers and return */ | ||
89 | blr | ||
90 | |||
91 | /* | ||
92 | * giveup_altivec(tsk) | ||
93 | * Disable VMX for the task given as the argument, | ||
94 | * and save the vector registers in its thread_struct. | ||
95 | * Enables the VMX for use in the kernel on return. | ||
96 | */ | ||
97 | _GLOBAL(giveup_altivec) | ||
98 | mfmsr r5 | ||
99 | oris r5,r5,MSR_VEC@h | ||
100 | SYNC | ||
101 | MTMSRD(r5) /* enable use of VMX now */ | ||
102 | isync | ||
103 | PPC_LCMPI 0,r3,0 | ||
104 | beqlr- /* if no previous owner, done */ | ||
105 | addi r3,r3,THREAD /* want THREAD of task */ | ||
106 | PPC_LL r5,PT_REGS(r3) | ||
107 | PPC_LCMPI 0,r5,0 | ||
108 | SAVE_32VRS(0,r4,r3) | ||
109 | mfvscr vr0 | ||
110 | li r4,THREAD_VSCR | ||
111 | stvx vr0,r4,r3 | ||
112 | beq 1f | ||
113 | PPC_LL r4,_MSR-STACK_FRAME_OVERHEAD(r5) | ||
114 | #ifdef CONFIG_VSX | ||
115 | BEGIN_FTR_SECTION | ||
116 | lis r3,(MSR_VEC|MSR_VSX)@h | ||
117 | FTR_SECTION_ELSE | ||
118 | lis r3,MSR_VEC@h | ||
119 | ALT_FTR_SECTION_END_IFSET(CPU_FTR_VSX) | ||
120 | #else | ||
121 | lis r3,MSR_VEC@h | ||
122 | #endif | ||
123 | andc r4,r4,r3 /* disable FP for previous task */ | ||
124 | PPC_STL r4,_MSR-STACK_FRAME_OVERHEAD(r5) | ||
125 | 1: | ||
126 | #ifndef CONFIG_SMP | ||
127 | li r5,0 | ||
128 | LOAD_REG_ADDRBASE(r4,last_task_used_altivec) | ||
129 | PPC_STL r5,ADDROFF(last_task_used_altivec)(r4) | ||
130 | #endif /* CONFIG_SMP */ | ||
131 | blr | ||
132 | |||
133 | #ifdef CONFIG_VSX | ||
134 | |||
135 | #ifdef CONFIG_PPC32 | ||
136 | #error This asm code isn't ready for 32-bit kernels | ||
137 | #endif | ||
138 | |||
139 | /* | ||
140 | * load_up_vsx(unused, unused, tsk) | ||
141 | * Disable VSX for the task which had it previously, | ||
142 | * and save its vector registers in its thread_struct. | ||
143 | * Reuse the fp and vsx saves, but first check to see if they have | ||
144 | * been saved already. | ||
145 | */ | ||
146 | _GLOBAL(load_up_vsx) | ||
147 | /* Load FP and VSX registers if they haven't been done yet */ | ||
148 | andi. r5,r12,MSR_FP | ||
149 | beql+ load_up_fpu /* skip if already loaded */ | ||
150 | andis. r5,r12,MSR_VEC@h | ||
151 | beql+ load_up_altivec /* skip if already loaded */ | ||
152 | |||
153 | #ifndef CONFIG_SMP | ||
154 | ld r3,last_task_used_vsx@got(r2) | ||
155 | ld r4,0(r3) | ||
156 | cmpdi 0,r4,0 | ||
157 | beq 1f | ||
158 | /* Disable VSX for last_task_used_vsx */ | ||
159 | addi r4,r4,THREAD | ||
160 | ld r5,PT_REGS(r4) | ||
161 | ld r4,_MSR-STACK_FRAME_OVERHEAD(r5) | ||
162 | lis r6,MSR_VSX@h | ||
163 | andc r6,r4,r6 | ||
164 | std r6,_MSR-STACK_FRAME_OVERHEAD(r5) | ||
165 | 1: | ||
166 | #endif /* CONFIG_SMP */ | ||
167 | ld r4,PACACURRENT(r13) | ||
168 | addi r4,r4,THREAD /* Get THREAD */ | ||
169 | li r6,1 | ||
170 | stw r6,THREAD_USED_VSR(r4) /* ... also set thread used vsr */ | ||
171 | /* enable use of VSX after return */ | ||
172 | oris r12,r12,MSR_VSX@h | ||
173 | std r12,_MSR(r1) | ||
174 | #ifndef CONFIG_SMP | ||
175 | /* Update last_task_used_math to 'current' */ | ||
176 | ld r4,PACACURRENT(r13) | ||
177 | std r4,0(r3) | ||
178 | #endif /* CONFIG_SMP */ | ||
179 | b fast_exception_return | ||
180 | |||
181 | /* | ||
182 | * __giveup_vsx(tsk) | ||
183 | * Disable VSX for the task given as the argument. | ||
184 | * Does NOT save vsx registers. | ||
185 | * Enables the VSX for use in the kernel on return. | ||
186 | */ | ||
187 | _GLOBAL(__giveup_vsx) | ||
188 | mfmsr r5 | ||
189 | oris r5,r5,MSR_VSX@h | ||
190 | mtmsrd r5 /* enable use of VSX now */ | ||
191 | isync | ||
192 | |||
193 | cmpdi 0,r3,0 | ||
194 | beqlr- /* if no previous owner, done */ | ||
195 | addi r3,r3,THREAD /* want THREAD of task */ | ||
196 | ld r5,PT_REGS(r3) | ||
197 | cmpdi 0,r5,0 | ||
198 | beq 1f | ||
199 | ld r4,_MSR-STACK_FRAME_OVERHEAD(r5) | ||
200 | lis r3,MSR_VSX@h | ||
201 | andc r4,r4,r3 /* disable VSX for previous task */ | ||
202 | std r4,_MSR-STACK_FRAME_OVERHEAD(r5) | ||
203 | 1: | ||
204 | #ifndef CONFIG_SMP | ||
205 | li r5,0 | ||
206 | ld r4,last_task_used_vsx@got(r2) | ||
207 | std r5,0(r4) | ||
208 | #endif /* CONFIG_SMP */ | ||
209 | blr | ||
210 | |||
211 | #endif /* CONFIG_VSX */ | ||
212 | |||
3 | 213 | ||
4 | /* | 214 | /* |
5 | * The routines below are in assembler so we can closely control the | 215 | * The routines below are in assembler so we can closely control the |