diff options
Diffstat (limited to 'arch/x86/include/asm/i387.h')
-rw-r--r-- | arch/x86/include/asm/i387.h | 129 |
1 files changed, 95 insertions, 34 deletions
diff --git a/arch/x86/include/asm/i387.h b/arch/x86/include/asm/i387.h index da2930924501..c991b3a7b904 100644 --- a/arch/x86/include/asm/i387.h +++ b/arch/x86/include/asm/i387.h | |||
@@ -16,7 +16,9 @@ | |||
16 | #include <linux/kernel_stat.h> | 16 | #include <linux/kernel_stat.h> |
17 | #include <linux/regset.h> | 17 | #include <linux/regset.h> |
18 | #include <linux/hardirq.h> | 18 | #include <linux/hardirq.h> |
19 | #include <linux/slab.h> | ||
19 | #include <asm/asm.h> | 20 | #include <asm/asm.h> |
21 | #include <asm/cpufeature.h> | ||
20 | #include <asm/processor.h> | 22 | #include <asm/processor.h> |
21 | #include <asm/sigcontext.h> | 23 | #include <asm/sigcontext.h> |
22 | #include <asm/user.h> | 24 | #include <asm/user.h> |
@@ -56,6 +58,11 @@ extern int restore_i387_xstate_ia32(void __user *buf); | |||
56 | 58 | ||
57 | #define X87_FSW_ES (1 << 7) /* Exception Summary */ | 59 | #define X87_FSW_ES (1 << 7) /* Exception Summary */ |
58 | 60 | ||
61 | static __always_inline __pure bool use_xsave(void) | ||
62 | { | ||
63 | return static_cpu_has(X86_FEATURE_XSAVE); | ||
64 | } | ||
65 | |||
59 | #ifdef CONFIG_X86_64 | 66 | #ifdef CONFIG_X86_64 |
60 | 67 | ||
61 | /* Ignore delayed exceptions from user space */ | 68 | /* Ignore delayed exceptions from user space */ |
@@ -91,15 +98,15 @@ static inline int fxrstor_checking(struct i387_fxsave_struct *fx) | |||
91 | values. The kernel data segment can be sometimes 0 and sometimes | 98 | values. The kernel data segment can be sometimes 0 and sometimes |
92 | new user value. Both should be ok. | 99 | new user value. Both should be ok. |
93 | Use the PDA as safe address because it should be already in L1. */ | 100 | Use the PDA as safe address because it should be already in L1. */ |
94 | static inline void clear_fpu_state(struct task_struct *tsk) | 101 | static inline void fpu_clear(struct fpu *fpu) |
95 | { | 102 | { |
96 | struct xsave_struct *xstate = &tsk->thread.xstate->xsave; | 103 | struct xsave_struct *xstate = &fpu->state->xsave; |
97 | struct i387_fxsave_struct *fx = &tsk->thread.xstate->fxsave; | 104 | struct i387_fxsave_struct *fx = &fpu->state->fxsave; |
98 | 105 | ||
99 | /* | 106 | /* |
100 | * xsave header may indicate the init state of the FP. | 107 | * xsave header may indicate the init state of the FP. |
101 | */ | 108 | */ |
102 | if ((task_thread_info(tsk)->status & TS_XSAVE) && | 109 | if (use_xsave() && |
103 | !(xstate->xsave_hdr.xstate_bv & XSTATE_FP)) | 110 | !(xstate->xsave_hdr.xstate_bv & XSTATE_FP)) |
104 | return; | 111 | return; |
105 | 112 | ||
@@ -111,6 +118,11 @@ static inline void clear_fpu_state(struct task_struct *tsk) | |||
111 | X86_FEATURE_FXSAVE_LEAK); | 118 | X86_FEATURE_FXSAVE_LEAK); |
112 | } | 119 | } |
113 | 120 | ||
121 | static inline void clear_fpu_state(struct task_struct *tsk) | ||
122 | { | ||
123 | fpu_clear(&tsk->thread.fpu); | ||
124 | } | ||
125 | |||
114 | static inline int fxsave_user(struct i387_fxsave_struct __user *fx) | 126 | static inline int fxsave_user(struct i387_fxsave_struct __user *fx) |
115 | { | 127 | { |
116 | int err; | 128 | int err; |
@@ -135,7 +147,7 @@ static inline int fxsave_user(struct i387_fxsave_struct __user *fx) | |||
135 | return err; | 147 | return err; |
136 | } | 148 | } |
137 | 149 | ||
138 | static inline void fxsave(struct task_struct *tsk) | 150 | static inline void fpu_fxsave(struct fpu *fpu) |
139 | { | 151 | { |
140 | /* Using "rex64; fxsave %0" is broken because, if the memory operand | 152 | /* Using "rex64; fxsave %0" is broken because, if the memory operand |
141 | uses any extended registers for addressing, a second REX prefix | 153 | uses any extended registers for addressing, a second REX prefix |
@@ -145,42 +157,45 @@ static inline void fxsave(struct task_struct *tsk) | |||
145 | /* Using "fxsaveq %0" would be the ideal choice, but is only supported | 157 | /* Using "fxsaveq %0" would be the ideal choice, but is only supported |
146 | starting with gas 2.16. */ | 158 | starting with gas 2.16. */ |
147 | __asm__ __volatile__("fxsaveq %0" | 159 | __asm__ __volatile__("fxsaveq %0" |
148 | : "=m" (tsk->thread.xstate->fxsave)); | 160 | : "=m" (fpu->state->fxsave)); |
149 | #elif 0 | 161 | #elif 0 |
150 | /* Using, as a workaround, the properly prefixed form below isn't | 162 | /* Using, as a workaround, the properly prefixed form below isn't |
151 | accepted by any binutils version so far released, complaining that | 163 | accepted by any binutils version so far released, complaining that |
152 | the same type of prefix is used twice if an extended register is | 164 | the same type of prefix is used twice if an extended register is |
153 | needed for addressing (fix submitted to mainline 2005-11-21). */ | 165 | needed for addressing (fix submitted to mainline 2005-11-21). */ |
154 | __asm__ __volatile__("rex64/fxsave %0" | 166 | __asm__ __volatile__("rex64/fxsave %0" |
155 | : "=m" (tsk->thread.xstate->fxsave)); | 167 | : "=m" (fpu->state->fxsave)); |
156 | #else | 168 | #else |
157 | /* This, however, we can work around by forcing the compiler to select | 169 | /* This, however, we can work around by forcing the compiler to select |
158 | an addressing mode that doesn't require extended registers. */ | 170 | an addressing mode that doesn't require extended registers. */ |
159 | __asm__ __volatile__("rex64/fxsave (%1)" | 171 | __asm__ __volatile__("rex64/fxsave (%1)" |
160 | : "=m" (tsk->thread.xstate->fxsave) | 172 | : "=m" (fpu->state->fxsave) |
161 | : "cdaSDb" (&tsk->thread.xstate->fxsave)); | 173 | : "cdaSDb" (&fpu->state->fxsave)); |
162 | #endif | 174 | #endif |
163 | } | 175 | } |
164 | 176 | ||
165 | static inline void __save_init_fpu(struct task_struct *tsk) | 177 | static inline void fpu_save_init(struct fpu *fpu) |
166 | { | 178 | { |
167 | if (task_thread_info(tsk)->status & TS_XSAVE) | 179 | if (use_xsave()) |
168 | xsave(tsk); | 180 | fpu_xsave(fpu); |
169 | else | 181 | else |
170 | fxsave(tsk); | 182 | fpu_fxsave(fpu); |
183 | |||
184 | fpu_clear(fpu); | ||
185 | } | ||
171 | 186 | ||
172 | clear_fpu_state(tsk); | 187 | static inline void __save_init_fpu(struct task_struct *tsk) |
188 | { | ||
189 | fpu_save_init(&tsk->thread.fpu); | ||
173 | task_thread_info(tsk)->status &= ~TS_USEDFPU; | 190 | task_thread_info(tsk)->status &= ~TS_USEDFPU; |
174 | } | 191 | } |
175 | 192 | ||
176 | #else /* CONFIG_X86_32 */ | 193 | #else /* CONFIG_X86_32 */ |
177 | 194 | ||
178 | #ifdef CONFIG_MATH_EMULATION | 195 | #ifdef CONFIG_MATH_EMULATION |
179 | extern void finit_task(struct task_struct *tsk); | 196 | extern void finit_soft_fpu(struct i387_soft_struct *soft); |
180 | #else | 197 | #else |
181 | static inline void finit_task(struct task_struct *tsk) | 198 | static inline void finit_soft_fpu(struct i387_soft_struct *soft) {} |
182 | { | ||
183 | } | ||
184 | #endif | 199 | #endif |
185 | 200 | ||
186 | static inline void tolerant_fwait(void) | 201 | static inline void tolerant_fwait(void) |
@@ -216,13 +231,13 @@ static inline int fxrstor_checking(struct i387_fxsave_struct *fx) | |||
216 | /* | 231 | /* |
217 | * These must be called with preempt disabled | 232 | * These must be called with preempt disabled |
218 | */ | 233 | */ |
219 | static inline void __save_init_fpu(struct task_struct *tsk) | 234 | static inline void fpu_save_init(struct fpu *fpu) |
220 | { | 235 | { |
221 | if (task_thread_info(tsk)->status & TS_XSAVE) { | 236 | if (use_xsave()) { |
222 | struct xsave_struct *xstate = &tsk->thread.xstate->xsave; | 237 | struct xsave_struct *xstate = &fpu->state->xsave; |
223 | struct i387_fxsave_struct *fx = &tsk->thread.xstate->fxsave; | 238 | struct i387_fxsave_struct *fx = &fpu->state->fxsave; |
224 | 239 | ||
225 | xsave(tsk); | 240 | fpu_xsave(fpu); |
226 | 241 | ||
227 | /* | 242 | /* |
228 | * xsave header may indicate the init state of the FP. | 243 | * xsave header may indicate the init state of the FP. |
@@ -246,8 +261,8 @@ static inline void __save_init_fpu(struct task_struct *tsk) | |||
246 | "fxsave %[fx]\n" | 261 | "fxsave %[fx]\n" |
247 | "bt $7,%[fsw] ; jnc 1f ; fnclex\n1:", | 262 | "bt $7,%[fsw] ; jnc 1f ; fnclex\n1:", |
248 | X86_FEATURE_FXSR, | 263 | X86_FEATURE_FXSR, |
249 | [fx] "m" (tsk->thread.xstate->fxsave), | 264 | [fx] "m" (fpu->state->fxsave), |
250 | [fsw] "m" (tsk->thread.xstate->fxsave.swd) : "memory"); | 265 | [fsw] "m" (fpu->state->fxsave.swd) : "memory"); |
251 | clear_state: | 266 | clear_state: |
252 | /* AMD K7/K8 CPUs don't save/restore FDP/FIP/FOP unless an exception | 267 | /* AMD K7/K8 CPUs don't save/restore FDP/FIP/FOP unless an exception |
253 | is pending. Clear the x87 state here by setting it to fixed | 268 | is pending. Clear the x87 state here by setting it to fixed |
@@ -259,17 +274,34 @@ clear_state: | |||
259 | X86_FEATURE_FXSAVE_LEAK, | 274 | X86_FEATURE_FXSAVE_LEAK, |
260 | [addr] "m" (safe_address)); | 275 | [addr] "m" (safe_address)); |
261 | end: | 276 | end: |
277 | ; | ||
278 | } | ||
279 | |||
280 | static inline void __save_init_fpu(struct task_struct *tsk) | ||
281 | { | ||
282 | fpu_save_init(&tsk->thread.fpu); | ||
262 | task_thread_info(tsk)->status &= ~TS_USEDFPU; | 283 | task_thread_info(tsk)->status &= ~TS_USEDFPU; |
263 | } | 284 | } |
264 | 285 | ||
286 | |||
265 | #endif /* CONFIG_X86_64 */ | 287 | #endif /* CONFIG_X86_64 */ |
266 | 288 | ||
267 | static inline int restore_fpu_checking(struct task_struct *tsk) | 289 | static inline int fpu_fxrstor_checking(struct fpu *fpu) |
268 | { | 290 | { |
269 | if (task_thread_info(tsk)->status & TS_XSAVE) | 291 | return fxrstor_checking(&fpu->state->fxsave); |
270 | return xrstor_checking(&tsk->thread.xstate->xsave); | 292 | } |
293 | |||
294 | static inline int fpu_restore_checking(struct fpu *fpu) | ||
295 | { | ||
296 | if (use_xsave()) | ||
297 | return fpu_xrstor_checking(fpu); | ||
271 | else | 298 | else |
272 | return fxrstor_checking(&tsk->thread.xstate->fxsave); | 299 | return fpu_fxrstor_checking(fpu); |
300 | } | ||
301 | |||
302 | static inline int restore_fpu_checking(struct task_struct *tsk) | ||
303 | { | ||
304 | return fpu_restore_checking(&tsk->thread.fpu); | ||
273 | } | 305 | } |
274 | 306 | ||
275 | /* | 307 | /* |
@@ -397,30 +429,59 @@ static inline void clear_fpu(struct task_struct *tsk) | |||
397 | static inline unsigned short get_fpu_cwd(struct task_struct *tsk) | 429 | static inline unsigned short get_fpu_cwd(struct task_struct *tsk) |
398 | { | 430 | { |
399 | if (cpu_has_fxsr) { | 431 | if (cpu_has_fxsr) { |
400 | return tsk->thread.xstate->fxsave.cwd; | 432 | return tsk->thread.fpu.state->fxsave.cwd; |
401 | } else { | 433 | } else { |
402 | return (unsigned short)tsk->thread.xstate->fsave.cwd; | 434 | return (unsigned short)tsk->thread.fpu.state->fsave.cwd; |
403 | } | 435 | } |
404 | } | 436 | } |
405 | 437 | ||
406 | static inline unsigned short get_fpu_swd(struct task_struct *tsk) | 438 | static inline unsigned short get_fpu_swd(struct task_struct *tsk) |
407 | { | 439 | { |
408 | if (cpu_has_fxsr) { | 440 | if (cpu_has_fxsr) { |
409 | return tsk->thread.xstate->fxsave.swd; | 441 | return tsk->thread.fpu.state->fxsave.swd; |
410 | } else { | 442 | } else { |
411 | return (unsigned short)tsk->thread.xstate->fsave.swd; | 443 | return (unsigned short)tsk->thread.fpu.state->fsave.swd; |
412 | } | 444 | } |
413 | } | 445 | } |
414 | 446 | ||
415 | static inline unsigned short get_fpu_mxcsr(struct task_struct *tsk) | 447 | static inline unsigned short get_fpu_mxcsr(struct task_struct *tsk) |
416 | { | 448 | { |
417 | if (cpu_has_xmm) { | 449 | if (cpu_has_xmm) { |
418 | return tsk->thread.xstate->fxsave.mxcsr; | 450 | return tsk->thread.fpu.state->fxsave.mxcsr; |
419 | } else { | 451 | } else { |
420 | return MXCSR_DEFAULT; | 452 | return MXCSR_DEFAULT; |
421 | } | 453 | } |
422 | } | 454 | } |
423 | 455 | ||
456 | static bool fpu_allocated(struct fpu *fpu) | ||
457 | { | ||
458 | return fpu->state != NULL; | ||
459 | } | ||
460 | |||
461 | static inline int fpu_alloc(struct fpu *fpu) | ||
462 | { | ||
463 | if (fpu_allocated(fpu)) | ||
464 | return 0; | ||
465 | fpu->state = kmem_cache_alloc(task_xstate_cachep, GFP_KERNEL); | ||
466 | if (!fpu->state) | ||
467 | return -ENOMEM; | ||
468 | WARN_ON((unsigned long)fpu->state & 15); | ||
469 | return 0; | ||
470 | } | ||
471 | |||
472 | static inline void fpu_free(struct fpu *fpu) | ||
473 | { | ||
474 | if (fpu->state) { | ||
475 | kmem_cache_free(task_xstate_cachep, fpu->state); | ||
476 | fpu->state = NULL; | ||
477 | } | ||
478 | } | ||
479 | |||
480 | static inline void fpu_copy(struct fpu *dst, struct fpu *src) | ||
481 | { | ||
482 | memcpy(dst->state, src->state, xstate_size); | ||
483 | } | ||
484 | |||
424 | #endif /* __ASSEMBLY__ */ | 485 | #endif /* __ASSEMBLY__ */ |
425 | 486 | ||
426 | #define PSHUFB_XMM5_XMM0 .byte 0x66, 0x0f, 0x38, 0x00, 0xc5 | 487 | #define PSHUFB_XMM5_XMM0 .byte 0x66, 0x0f, 0x38, 0x00, 0xc5 |