blob: b546ec816fd652773bde9a7e503e2798ac84d0ff [file] [log] [blame]
Linus Torvalds1361b832012-02-21 13:19:22 -08001/*
2 * Copyright (C) 1994 Linus Torvalds
3 *
4 * Pentium III FXSR, SSE support
5 * General FPU state handling cleanups
6 * Gareth Hughes <gareth@valinux.com>, May 2000
7 * x86-64 work by Andi Kleen 2002
8 */
9
Ingo Molnar78f7f1e2015-04-24 02:54:44 +020010#ifndef _ASM_X86_FPU_INTERNAL_H
11#define _ASM_X86_FPU_INTERNAL_H
Linus Torvalds1361b832012-02-21 13:19:22 -080012
Linus Torvalds1361b832012-02-21 13:19:22 -080013#include <linux/regset.h>
Suresh Siddha050902c2012-07-24 16:05:27 -070014#include <linux/compat.h>
Linus Torvalds1361b832012-02-21 13:19:22 -080015#include <linux/slab.h>
Ingo Molnarf89e32e2015-04-22 10:58:10 +020016
Linus Torvalds1361b832012-02-21 13:19:22 -080017#include <asm/user.h>
Ingo Molnardf6b35f2015-04-24 02:46:00 +020018#include <asm/fpu/api.h>
Ingo Molnara137fb62015-04-27 03:58:37 +020019#include <asm/fpu/xsave.h>
Linus Torvalds1361b832012-02-21 13:19:22 -080020
Suresh Siddha72a671c2012-07-24 16:05:29 -070021#ifdef CONFIG_X86_64
22# include <asm/sigcontext32.h>
23# include <asm/user32.h>
Al Viro235b8022012-11-09 23:51:47 -050024struct ksignal;
25int ia32_setup_rt_frame(int sig, struct ksignal *ksig,
Suresh Siddha72a671c2012-07-24 16:05:29 -070026 compat_sigset_t *set, struct pt_regs *regs);
Al Viro235b8022012-11-09 23:51:47 -050027int ia32_setup_frame(int sig, struct ksignal *ksig,
Suresh Siddha72a671c2012-07-24 16:05:29 -070028 compat_sigset_t *set, struct pt_regs *regs);
29#else
30# define user_i387_ia32_struct user_i387_struct
31# define user32_fxsr_struct user_fxsr_struct
32# define ia32_setup_frame __setup_frame
33# define ia32_setup_rt_frame __setup_rt_frame
34#endif
35
Ingo Molnardf639752015-04-24 03:06:56 +020036#define MXCSR_DEFAULT 0x1f80
37
Suresh Siddha72a671c2012-07-24 16:05:29 -070038extern unsigned int mxcsr_feature_mask;
Ingo Molnar3a9c4b02015-04-03 13:16:51 +020039extern void fpu__cpu_init(void);
Suresh Siddha5d2bd702012-09-06 14:58:52 -070040extern void eager_fpu_init(void);
Linus Torvalds1361b832012-02-21 13:19:22 -080041
Ingo Molnar36b544d2015-04-23 12:18:28 +020042DECLARE_PER_CPU(struct fpu *, fpu_fpregs_owner_ctx);
Linus Torvalds1361b832012-02-21 13:19:22 -080043
Suresh Siddha72a671c2012-07-24 16:05:29 -070044extern void convert_from_fxsr(struct user_i387_ia32_struct *env,
45 struct task_struct *tsk);
46extern void convert_to_fxsr(struct task_struct *tsk,
47 const struct user_i387_ia32_struct *env);
48
Ingo Molnar678eaf62015-04-24 14:48:24 +020049extern user_regset_active_fn regset_fpregs_active, regset_xregset_fpregs_active;
Linus Torvalds1361b832012-02-21 13:19:22 -080050extern user_regset_get_fn fpregs_get, xfpregs_get, fpregs_soft_get,
51 xstateregs_get;
52extern user_regset_set_fn fpregs_set, xfpregs_set, fpregs_soft_set,
53 xstateregs_set;
54
Linus Torvalds1361b832012-02-21 13:19:22 -080055/*
Ingo Molnar678eaf62015-04-24 14:48:24 +020056 * xstateregs_active == regset_fpregs_active. Please refer to the comment
57 * at the definition of regset_fpregs_active.
Linus Torvalds1361b832012-02-21 13:19:22 -080058 */
Ingo Molnar678eaf62015-04-24 14:48:24 +020059#define xstateregs_active regset_fpregs_active
Linus Torvalds1361b832012-02-21 13:19:22 -080060
Linus Torvalds1361b832012-02-21 13:19:22 -080061#ifdef CONFIG_MATH_EMULATION
62extern void finit_soft_fpu(struct i387_soft_struct *soft);
63#else
64static inline void finit_soft_fpu(struct i387_soft_struct *soft) {}
65#endif
66
Rik van Riel1c927ee2015-02-06 15:02:01 -050067/*
Ingo Molnar36b544d2015-04-23 12:18:28 +020068 * Must be run with preemption disabled: this clears the fpu_fpregs_owner_ctx,
Rik van Riel1c927ee2015-02-06 15:02:01 -050069 * on this CPU.
70 *
71 * This will disable any lazy FPU state restore of the current FPU state,
72 * but if the current thread owns the FPU, it will still be saved by.
73 */
74static inline void __cpu_disable_lazy_restore(unsigned int cpu)
75{
Ingo Molnar36b544d2015-04-23 12:18:28 +020076 per_cpu(fpu_fpregs_owner_ctx, cpu) = NULL;
Rik van Riel1c927ee2015-02-06 15:02:01 -050077}
78
Ingo Molnar66ddc2c2015-04-23 17:25:44 +020079static inline int fpu_want_lazy_restore(struct fpu *fpu, unsigned int cpu)
Rik van Riel1c927ee2015-02-06 15:02:01 -050080{
Ingo Molnar66ddc2c2015-04-23 17:25:44 +020081 return fpu == this_cpu_read_stable(fpu_fpregs_owner_ctx) && cpu == fpu->last_cpu;
Rik van Riel1c927ee2015-02-06 15:02:01 -050082}
83
Suresh Siddha050902c2012-07-24 16:05:27 -070084static inline int is_ia32_compat_frame(void)
85{
86 return config_enabled(CONFIG_IA32_EMULATION) &&
87 test_thread_flag(TIF_IA32);
88}
89
90static inline int is_ia32_frame(void)
91{
92 return config_enabled(CONFIG_X86_32) || is_ia32_compat_frame();
93}
94
95static inline int is_x32_frame(void)
96{
97 return config_enabled(CONFIG_X86_X32_ABI) && test_thread_flag(TIF_X32);
98}
99
Linus Torvalds1361b832012-02-21 13:19:22 -0800100#define X87_FSW_ES (1 << 7) /* Exception Summary */
101
Suresh Siddha5d2bd702012-09-06 14:58:52 -0700102static __always_inline __pure bool use_eager_fpu(void)
103{
Matt Flemingc6b40692014-03-27 15:10:40 -0700104 return static_cpu_has_safe(X86_FEATURE_EAGER_FPU);
Suresh Siddha5d2bd702012-09-06 14:58:52 -0700105}
106
Linus Torvalds1361b832012-02-21 13:19:22 -0800107static __always_inline __pure bool use_xsaveopt(void)
108{
Matt Flemingc6b40692014-03-27 15:10:40 -0700109 return static_cpu_has_safe(X86_FEATURE_XSAVEOPT);
Linus Torvalds1361b832012-02-21 13:19:22 -0800110}
111
112static __always_inline __pure bool use_xsave(void)
113{
Matt Flemingc6b40692014-03-27 15:10:40 -0700114 return static_cpu_has_safe(X86_FEATURE_XSAVE);
Linus Torvalds1361b832012-02-21 13:19:22 -0800115}
116
117static __always_inline __pure bool use_fxsr(void)
118{
Matt Flemingc6b40692014-03-27 15:10:40 -0700119 return static_cpu_has_safe(X86_FEATURE_FXSR);
Linus Torvalds1361b832012-02-21 13:19:22 -0800120}
121
Suresh Siddha5d2bd702012-09-06 14:58:52 -0700122static inline void fx_finit(struct i387_fxsave_struct *fx)
123{
Suresh Siddha5d2bd702012-09-06 14:58:52 -0700124 fx->cwd = 0x37f;
Suresh Siddhaa8615af2012-09-10 10:40:08 -0700125 fx->mxcsr = MXCSR_DEFAULT;
Suresh Siddha5d2bd702012-09-06 14:58:52 -0700126}
127
Linus Torvalds1361b832012-02-21 13:19:22 -0800128extern void __sanitize_i387_state(struct task_struct *);
129
130static inline void sanitize_i387_state(struct task_struct *tsk)
131{
132 if (!use_xsaveopt())
133 return;
134 __sanitize_i387_state(tsk);
135}
136
H. Peter Anvin49b8c692012-09-21 17:18:44 -0700137#define user_insn(insn, output, input...) \
138({ \
139 int err; \
140 asm volatile(ASM_STAC "\n" \
141 "1:" #insn "\n\t" \
142 "2: " ASM_CLAC "\n" \
143 ".section .fixup,\"ax\"\n" \
144 "3: movl $-1,%[err]\n" \
145 " jmp 2b\n" \
146 ".previous\n" \
147 _ASM_EXTABLE(1b, 3b) \
148 : [err] "=r" (err), output \
149 : "0"(0), input); \
150 err; \
151})
Linus Torvalds1361b832012-02-21 13:19:22 -0800152
Suresh Siddha0ca5bd02012-07-24 16:05:28 -0700153#define check_insn(insn, output, input...) \
154({ \
155 int err; \
156 asm volatile("1:" #insn "\n\t" \
157 "2:\n" \
158 ".section .fixup,\"ax\"\n" \
159 "3: movl $-1,%[err]\n" \
160 " jmp 2b\n" \
161 ".previous\n" \
162 _ASM_EXTABLE(1b, 3b) \
163 : [err] "=r" (err), output \
164 : "0"(0), input); \
165 err; \
166})
Linus Torvalds1361b832012-02-21 13:19:22 -0800167
Suresh Siddha0ca5bd02012-07-24 16:05:28 -0700168static inline int fsave_user(struct i387_fsave_struct __user *fx)
169{
H. Peter Anvin49b8c692012-09-21 17:18:44 -0700170 return user_insn(fnsave %[fx]; fwait, [fx] "=m" (*fx), "m" (*fx));
Linus Torvalds1361b832012-02-21 13:19:22 -0800171}
172
173static inline int fxsave_user(struct i387_fxsave_struct __user *fx)
174{
Suresh Siddha0ca5bd02012-07-24 16:05:28 -0700175 if (config_enabled(CONFIG_X86_32))
H. Peter Anvin49b8c692012-09-21 17:18:44 -0700176 return user_insn(fxsave %[fx], [fx] "=m" (*fx), "m" (*fx));
Suresh Siddha0ca5bd02012-07-24 16:05:28 -0700177 else if (config_enabled(CONFIG_AS_FXSAVEQ))
H. Peter Anvin49b8c692012-09-21 17:18:44 -0700178 return user_insn(fxsaveq %[fx], [fx] "=m" (*fx), "m" (*fx));
Linus Torvalds1361b832012-02-21 13:19:22 -0800179
Suresh Siddha0ca5bd02012-07-24 16:05:28 -0700180 /* See comment in fpu_fxsave() below. */
H. Peter Anvin49b8c692012-09-21 17:18:44 -0700181 return user_insn(rex64/fxsave (%[fx]), "=m" (*fx), [fx] "R" (fx));
Linus Torvalds1361b832012-02-21 13:19:22 -0800182}
183
Linus Torvalds1361b832012-02-21 13:19:22 -0800184static inline int fxrstor_checking(struct i387_fxsave_struct *fx)
185{
Suresh Siddha0ca5bd02012-07-24 16:05:28 -0700186 if (config_enabled(CONFIG_X86_32))
187 return check_insn(fxrstor %[fx], "=m" (*fx), [fx] "m" (*fx));
188 else if (config_enabled(CONFIG_AS_FXSAVEQ))
189 return check_insn(fxrstorq %[fx], "=m" (*fx), [fx] "m" (*fx));
Linus Torvalds1361b832012-02-21 13:19:22 -0800190
Suresh Siddha0ca5bd02012-07-24 16:05:28 -0700191 /* See comment in fpu_fxsave() below. */
192 return check_insn(rex64/fxrstor (%[fx]), "=m" (*fx), [fx] "R" (fx),
193 "m" (*fx));
194}
195
H. Peter Anvine139e952012-09-25 15:42:18 -0700196static inline int fxrstor_user(struct i387_fxsave_struct __user *fx)
197{
198 if (config_enabled(CONFIG_X86_32))
199 return user_insn(fxrstor %[fx], "=m" (*fx), [fx] "m" (*fx));
200 else if (config_enabled(CONFIG_AS_FXSAVEQ))
201 return user_insn(fxrstorq %[fx], "=m" (*fx), [fx] "m" (*fx));
202
203 /* See comment in fpu_fxsave() below. */
204 return user_insn(rex64/fxrstor (%[fx]), "=m" (*fx), [fx] "R" (fx),
205 "m" (*fx));
206}
207
Suresh Siddha0ca5bd02012-07-24 16:05:28 -0700208static inline int frstor_checking(struct i387_fsave_struct *fx)
209{
210 return check_insn(frstor %[fx], "=m" (*fx), [fx] "m" (*fx));
Linus Torvalds1361b832012-02-21 13:19:22 -0800211}
212
H. Peter Anvine139e952012-09-25 15:42:18 -0700213static inline int frstor_user(struct i387_fsave_struct __user *fx)
214{
215 return user_insn(frstor %[fx], "=m" (*fx), [fx] "m" (*fx));
216}
217
Linus Torvalds1361b832012-02-21 13:19:22 -0800218static inline void fpu_fxsave(struct fpu *fpu)
219{
Suresh Siddha0ca5bd02012-07-24 16:05:28 -0700220 if (config_enabled(CONFIG_X86_32))
221 asm volatile( "fxsave %[fx]" : [fx] "=m" (fpu->state->fxsave));
222 else if (config_enabled(CONFIG_AS_FXSAVEQ))
Borislav Petkov6ca7a8a2014-12-21 15:02:23 +0100223 asm volatile("fxsaveq %[fx]" : [fx] "=m" (fpu->state->fxsave));
Suresh Siddha0ca5bd02012-07-24 16:05:28 -0700224 else {
225 /* Using "rex64; fxsave %0" is broken because, if the memory
226 * operand uses any extended registers for addressing, a second
227 * REX prefix will be generated (to the assembler, rex64
228 * followed by semicolon is a separate instruction), and hence
229 * the 64-bitness is lost.
230 *
231 * Using "fxsaveq %0" would be the ideal choice, but is only
232 * supported starting with gas 2.16.
233 *
234 * Using, as a workaround, the properly prefixed form below
235 * isn't accepted by any binutils version so far released,
236 * complaining that the same type of prefix is used twice if
237 * an extended register is needed for addressing (fix submitted
238 * to mainline 2005-11-21).
239 *
240 * asm volatile("rex64/fxsave %0" : "=m" (fpu->state->fxsave));
241 *
242 * This, however, we can work around by forcing the compiler to
243 * select an addressing mode that doesn't require extended
244 * registers.
245 */
246 asm volatile( "rex64/fxsave (%[fx])"
247 : "=m" (fpu->state->fxsave)
248 : [fx] "R" (&fpu->state->fxsave));
249 }
Linus Torvalds1361b832012-02-21 13:19:22 -0800250}
251
Linus Torvalds1361b832012-02-21 13:19:22 -0800252/*
253 * These must be called with preempt disabled. Returns
254 * 'true' if the FPU state is still intact.
255 */
256static inline int fpu_save_init(struct fpu *fpu)
257{
258 if (use_xsave()) {
Ingo Molnar0afc4a92015-04-22 15:14:44 +0200259 xsave_state(&fpu->state->xsave);
Linus Torvalds1361b832012-02-21 13:19:22 -0800260
261 /*
262 * xsave header may indicate the init state of the FP.
263 */
Ingo Molnar400e4b22015-04-24 10:19:47 +0200264 if (!(fpu->state->xsave.header.xfeatures & XSTATE_FP))
Linus Torvalds1361b832012-02-21 13:19:22 -0800265 return 1;
266 } else if (use_fxsr()) {
267 fpu_fxsave(fpu);
268 } else {
269 asm volatile("fnsave %[fx]; fwait"
270 : [fx] "=m" (fpu->state->fsave));
271 return 0;
272 }
273
274 /*
275 * If exceptions are pending, we need to clear them so
276 * that we don't randomly get exceptions later.
277 *
278 * FIXME! Is this perhaps only true for the old-style
279 * irq13 case? Maybe we could leave the x87 state
280 * intact otherwise?
281 */
282 if (unlikely(fpu->state->fxsave.swd & X87_FSW_ES)) {
283 asm volatile("fnclex");
284 return 0;
285 }
286 return 1;
287}
288
Linus Torvalds1361b832012-02-21 13:19:22 -0800289static inline int fpu_restore_checking(struct fpu *fpu)
290{
291 if (use_xsave())
Suresh Siddha0ca5bd02012-07-24 16:05:28 -0700292 return fpu_xrstor_checking(&fpu->state->xsave);
293 else if (use_fxsr())
294 return fxrstor_checking(&fpu->state->fxsave);
Linus Torvalds1361b832012-02-21 13:19:22 -0800295 else
Suresh Siddha0ca5bd02012-07-24 16:05:28 -0700296 return frstor_checking(&fpu->state->fsave);
Linus Torvalds1361b832012-02-21 13:19:22 -0800297}
298
Ingo Molnar11f2d502015-04-23 17:30:59 +0200299static inline int restore_fpu_checking(struct fpu *fpu)
Linus Torvalds1361b832012-02-21 13:19:22 -0800300{
Borislav Petkov6ca7a8a2014-12-21 15:02:23 +0100301 /*
302 * AMD K7/K8 CPUs don't save/restore FDP/FIP/FOP unless an exception is
303 * pending. Clear the x87 state here by setting it to fixed values.
304 * "m" is a random variable that should be in L1.
305 */
Borislav Petkov9b13a932014-06-18 00:06:23 +0200306 if (unlikely(static_cpu_has_bug_safe(X86_BUG_FXSAVE_LEAK))) {
Linus Torvalds26bef132014-01-11 19:15:52 -0800307 asm volatile(
308 "fnclex\n\t"
309 "emms\n\t"
310 "fildl %P[addr]" /* set F?P to defined value */
Ingo Molnard5cea9b2015-04-24 14:19:26 +0200311 : : [addr] "m" (fpu->fpregs_active));
Linus Torvalds26bef132014-01-11 19:15:52 -0800312 }
Linus Torvalds1361b832012-02-21 13:19:22 -0800313
Ingo Molnar11f2d502015-04-23 17:30:59 +0200314 return fpu_restore_checking(fpu);
Linus Torvalds1361b832012-02-21 13:19:22 -0800315}
316
Linus Torvalds1361b832012-02-21 13:19:22 -0800317/* Must be paired with an 'stts' after! */
Ingo Molnar36fe6172015-04-23 12:08:58 +0200318static inline void __thread_clear_has_fpu(struct fpu *fpu)
Linus Torvalds1361b832012-02-21 13:19:22 -0800319{
Ingo Molnard5cea9b2015-04-24 14:19:26 +0200320 fpu->fpregs_active = 0;
Ingo Molnar36b544d2015-04-23 12:18:28 +0200321 this_cpu_write(fpu_fpregs_owner_ctx, NULL);
Linus Torvalds1361b832012-02-21 13:19:22 -0800322}
323
324/* Must be paired with a 'clts' before! */
Ingo Molnarc0311f62015-04-23 12:24:59 +0200325static inline void __thread_set_has_fpu(struct fpu *fpu)
Linus Torvalds1361b832012-02-21 13:19:22 -0800326{
Ingo Molnard5cea9b2015-04-24 14:19:26 +0200327 fpu->fpregs_active = 1;
Ingo Molnarc0311f62015-04-23 12:24:59 +0200328 this_cpu_write(fpu_fpregs_owner_ctx, fpu);
Linus Torvalds1361b832012-02-21 13:19:22 -0800329}
330
331/*
332 * Encapsulate the CR0.TS handling together with the
333 * software flag.
334 *
335 * These generally need preemption protection to work,
336 * do try to avoid using these on their own.
337 */
Ingo Molnar35191e32015-04-23 12:26:55 +0200338static inline void __thread_fpu_end(struct fpu *fpu)
Linus Torvalds1361b832012-02-21 13:19:22 -0800339{
Ingo Molnar35191e32015-04-23 12:26:55 +0200340 __thread_clear_has_fpu(fpu);
Suresh Siddha5d2bd702012-09-06 14:58:52 -0700341 if (!use_eager_fpu())
Suresh Siddha304bced2012-08-24 14:13:02 -0700342 stts();
Linus Torvalds1361b832012-02-21 13:19:22 -0800343}
344
Ingo Molnar4540d3f2015-04-23 12:31:17 +0200345static inline void __thread_fpu_begin(struct fpu *fpu)
Linus Torvalds1361b832012-02-21 13:19:22 -0800346{
Oleg Nesterov31d96332014-09-02 19:57:20 +0200347 if (!use_eager_fpu())
Suresh Siddha304bced2012-08-24 14:13:02 -0700348 clts();
Ingo Molnar4540d3f2015-04-23 12:31:17 +0200349 __thread_set_has_fpu(fpu);
Linus Torvalds1361b832012-02-21 13:19:22 -0800350}
351
Ingo Molnarca6787b2015-04-23 12:33:50 +0200352static inline void drop_fpu(struct fpu *fpu)
Suresh Siddha304bced2012-08-24 14:13:02 -0700353{
354 /*
355 * Forget coprocessor state..
356 */
357 preempt_disable();
Ingo Molnarca6787b2015-04-23 12:33:50 +0200358 fpu->counter = 0;
Borislav Petkovd2d0ac92015-03-14 11:52:12 +0100359
Ingo Molnard5cea9b2015-04-24 14:19:26 +0200360 if (fpu->fpregs_active) {
Borislav Petkovd2d0ac92015-03-14 11:52:12 +0100361 /* Ignore delayed exceptions from user space */
362 asm volatile("1: fwait\n"
363 "2:\n"
364 _ASM_EXTABLE(1b, 2b));
Ingo Molnar35191e32015-04-23 12:26:55 +0200365 __thread_fpu_end(fpu);
Borislav Petkovd2d0ac92015-03-14 11:52:12 +0100366 }
367
Ingo Molnarc5bedc62015-04-23 12:49:20 +0200368 fpu->fpstate_active = 0;
Ingo Molnar4c138412015-04-23 12:46:20 +0200369
Suresh Siddha304bced2012-08-24 14:13:02 -0700370 preempt_enable();
371}
372
Oleg Nesterov8f4d8182015-03-11 18:34:29 +0100373static inline void restore_init_xstate(void)
374{
375 if (use_xsave())
376 xrstor_state(init_xstate_buf, -1);
377 else
378 fxrstor_checking(&init_xstate_buf->i387);
379}
380
Borislav Petkovb85e67d2015-03-16 10:21:55 +0100381/*
382 * Reset the FPU state in the eager case and drop it in the lazy case (later use
383 * will reinit it).
384 */
Ingo Molnaraf2d94f2015-04-23 17:34:20 +0200385static inline void fpu_reset_state(struct fpu *fpu)
Suresh Siddha304bced2012-08-24 14:13:02 -0700386{
Suresh Siddha5d2bd702012-09-06 14:58:52 -0700387 if (!use_eager_fpu())
Ingo Molnarca6787b2015-04-23 12:33:50 +0200388 drop_fpu(fpu);
Oleg Nesterov8f4d8182015-03-11 18:34:29 +0100389 else
390 restore_init_xstate();
Suresh Siddha304bced2012-08-24 14:13:02 -0700391}
392
Linus Torvalds1361b832012-02-21 13:19:22 -0800393/*
394 * FPU state switching for scheduling.
395 *
396 * This is a two-stage process:
397 *
398 * - switch_fpu_prepare() saves the old state and
399 * sets the new state of the CR0.TS bit. This is
400 * done within the context of the old process.
401 *
402 * - switch_fpu_finish() restores the new state as
403 * necessary.
404 */
405typedef struct { int preload; } fpu_switch_t;
406
Ingo Molnarcb8818b2015-04-23 17:39:04 +0200407static inline fpu_switch_t
408switch_fpu_prepare(struct fpu *old_fpu, struct fpu *new_fpu, int cpu)
Linus Torvalds1361b832012-02-21 13:19:22 -0800409{
410 fpu_switch_t fpu;
411
Suresh Siddha304bced2012-08-24 14:13:02 -0700412 /*
413 * If the task has used the math, pre-load the FPU on xsave processors
414 * or if the past 5 consecutive context-switches used math.
415 */
Ingo Molnarc5bedc62015-04-23 12:49:20 +0200416 fpu.preload = new_fpu->fpstate_active &&
Ingo Molnarcb8818b2015-04-23 17:39:04 +0200417 (use_eager_fpu() || new_fpu->counter > 5);
Rik van Riel1361ef22015-02-06 15:02:03 -0500418
Ingo Molnard5cea9b2015-04-24 14:19:26 +0200419 if (old_fpu->fpregs_active) {
Ingo Molnarcb8818b2015-04-23 17:39:04 +0200420 if (!fpu_save_init(old_fpu))
421 old_fpu->last_cpu = -1;
Rik van Riel1361ef22015-02-06 15:02:03 -0500422 else
Ingo Molnarcb8818b2015-04-23 17:39:04 +0200423 old_fpu->last_cpu = cpu;
Rik van Riel1361ef22015-02-06 15:02:03 -0500424
Ingo Molnar36b544d2015-04-23 12:18:28 +0200425 /* But leave fpu_fpregs_owner_ctx! */
Ingo Molnard5cea9b2015-04-24 14:19:26 +0200426 old_fpu->fpregs_active = 0;
Linus Torvalds1361b832012-02-21 13:19:22 -0800427
428 /* Don't change CR0.TS if we just switch! */
429 if (fpu.preload) {
Ingo Molnarcb8818b2015-04-23 17:39:04 +0200430 new_fpu->counter++;
Ingo Molnarc0311f62015-04-23 12:24:59 +0200431 __thread_set_has_fpu(new_fpu);
Ingo Molnarcb8818b2015-04-23 17:39:04 +0200432 prefetch(new_fpu->state);
Suresh Siddha5d2bd702012-09-06 14:58:52 -0700433 } else if (!use_eager_fpu())
Linus Torvalds1361b832012-02-21 13:19:22 -0800434 stts();
435 } else {
Ingo Molnarcb8818b2015-04-23 17:39:04 +0200436 old_fpu->counter = 0;
437 old_fpu->last_cpu = -1;
Linus Torvalds1361b832012-02-21 13:19:22 -0800438 if (fpu.preload) {
Ingo Molnarcb8818b2015-04-23 17:39:04 +0200439 new_fpu->counter++;
Ingo Molnar66ddc2c2015-04-23 17:25:44 +0200440 if (fpu_want_lazy_restore(new_fpu, cpu))
Linus Torvalds1361b832012-02-21 13:19:22 -0800441 fpu.preload = 0;
442 else
Ingo Molnarcb8818b2015-04-23 17:39:04 +0200443 prefetch(new_fpu->state);
Ingo Molnar4540d3f2015-04-23 12:31:17 +0200444 __thread_fpu_begin(new_fpu);
Linus Torvalds1361b832012-02-21 13:19:22 -0800445 }
446 }
447 return fpu;
448}
449
450/*
451 * By the time this gets called, we've already cleared CR0.TS and
452 * given the process the FPU if we are going to preload the FPU
453 * state - all we need to do is to conditionally restore the register
454 * state itself.
455 */
Ingo Molnar384a23f2015-04-23 17:43:27 +0200456static inline void switch_fpu_finish(struct fpu *new_fpu, fpu_switch_t fpu_switch)
Linus Torvalds1361b832012-02-21 13:19:22 -0800457{
Ingo Molnar384a23f2015-04-23 17:43:27 +0200458 if (fpu_switch.preload) {
Ingo Molnar11f2d502015-04-23 17:30:59 +0200459 if (unlikely(restore_fpu_checking(new_fpu)))
Ingo Molnaraf2d94f2015-04-23 17:34:20 +0200460 fpu_reset_state(new_fpu);
Linus Torvalds1361b832012-02-21 13:19:22 -0800461 }
462}
463
464/*
465 * Signal frame handlers...
466 */
Suresh Siddha72a671c2012-07-24 16:05:29 -0700467extern int save_xstate_sig(void __user *buf, void __user *fx, int size);
468extern int __restore_xstate_sig(void __user *buf, void __user *fx, int size);
Linus Torvalds1361b832012-02-21 13:19:22 -0800469
Suresh Siddha72a671c2012-07-24 16:05:29 -0700470static inline int xstate_sigframe_size(void)
Linus Torvalds1361b832012-02-21 13:19:22 -0800471{
Suresh Siddha72a671c2012-07-24 16:05:29 -0700472 return use_xsave() ? xstate_size + FP_XSTATE_MAGIC2_SIZE : xstate_size;
473}
474
475static inline int restore_xstate_sig(void __user *buf, int ia32_frame)
476{
477 void __user *buf_fx = buf;
478 int size = xstate_sigframe_size();
479
480 if (ia32_frame && use_fxsr()) {
481 buf_fx = buf + sizeof(struct i387_fsave_struct);
482 size += sizeof(struct i387_fsave_struct);
Linus Torvalds1361b832012-02-21 13:19:22 -0800483 }
Suresh Siddha72a671c2012-07-24 16:05:29 -0700484
485 return __restore_xstate_sig(buf, buf_fx, size);
Linus Torvalds1361b832012-02-21 13:19:22 -0800486}
487
488/*
Oleg Nesterovfb14b4e2015-03-11 18:34:09 +0100489 * Needs to be preemption-safe.
Linus Torvalds1361b832012-02-21 13:19:22 -0800490 *
Suresh Siddha377ffbc2012-08-24 14:12:58 -0700491 * NOTE! user_fpu_begin() must be used only immediately before restoring
Oleg Nesterovfb14b4e2015-03-11 18:34:09 +0100492 * the save state. It does not do any saving/restoring on its own. In
493 * lazy FPU mode, it is just an optimization to avoid a #NM exception,
494 * the task can lose the FPU right after preempt_enable().
Linus Torvalds1361b832012-02-21 13:19:22 -0800495 */
Linus Torvalds1361b832012-02-21 13:19:22 -0800496static inline void user_fpu_begin(void)
497{
Ingo Molnar4540d3f2015-04-23 12:31:17 +0200498 struct fpu *fpu = &current->thread.fpu;
499
Linus Torvalds1361b832012-02-21 13:19:22 -0800500 preempt_disable();
501 if (!user_has_fpu())
Ingo Molnar4540d3f2015-04-23 12:31:17 +0200502 __thread_fpu_begin(fpu);
Linus Torvalds1361b832012-02-21 13:19:22 -0800503 preempt_enable();
504}
505
506/*
Linus Torvalds1361b832012-02-21 13:19:22 -0800507 * i387 state interaction
508 */
509static inline unsigned short get_fpu_cwd(struct task_struct *tsk)
510{
511 if (cpu_has_fxsr) {
512 return tsk->thread.fpu.state->fxsave.cwd;
513 } else {
514 return (unsigned short)tsk->thread.fpu.state->fsave.cwd;
515 }
516}
517
518static inline unsigned short get_fpu_swd(struct task_struct *tsk)
519{
520 if (cpu_has_fxsr) {
521 return tsk->thread.fpu.state->fxsave.swd;
522 } else {
523 return (unsigned short)tsk->thread.fpu.state->fsave.swd;
524 }
525}
526
527static inline unsigned short get_fpu_mxcsr(struct task_struct *tsk)
528{
529 if (cpu_has_xmm) {
530 return tsk->thread.fpu.state->fxsave.mxcsr;
531 } else {
532 return MXCSR_DEFAULT;
533 }
534}
535
Ingo Molnar8ffb53a2015-04-22 15:41:56 +0200536extern void fpstate_cache_init(void);
537
Ingo Molnared97b082015-04-03 12:41:14 +0200538extern int fpstate_alloc(struct fpu *fpu);
Ingo Molnar5a12bf62015-04-22 15:58:37 +0200539extern void fpstate_free(struct fpu *fpu);
Ingo Molnarc69e0982015-04-24 02:07:15 +0200540extern int fpu__copy(struct fpu *dst_fpu, struct fpu *src_fpu);
Linus Torvalds1361b832012-02-21 13:19:22 -0800541
Suresh Siddha72a671c2012-07-24 16:05:29 -0700542static inline unsigned long
543alloc_mathframe(unsigned long sp, int ia32_frame, unsigned long *buf_fx,
544 unsigned long *size)
545{
546 unsigned long frame_size = xstate_sigframe_size();
547
548 *buf_fx = sp = round_down(sp - frame_size, 64);
549 if (ia32_frame && use_fxsr()) {
550 frame_size += sizeof(struct i387_fsave_struct);
551 sp -= sizeof(struct i387_fsave_struct);
552 }
553
554 *size = frame_size;
555 return sp;
556}
Linus Torvalds1361b832012-02-21 13:19:22 -0800557
Ingo Molnar78f7f1e2015-04-24 02:54:44 +0200558#endif /* _ASM_X86_FPU_INTERNAL_H */