diff options
author | H. Peter Anvin <hpa@linux.intel.com> | 2012-09-21 20:18:44 -0400 |
---|---|---|
committer | H. Peter Anvin <hpa@linux.intel.com> | 2012-09-21 20:18:44 -0400 |
commit | 49b8c695e331c9685e6ffdbf34872509d77c8459 (patch) | |
tree | d4afdfae0115b2ab56687d23d6329d6ad934788f /arch/x86/include/asm/fpu-internal.h | |
parent | e59d1b0a24199db01978e6c1e89859eda93ce683 (diff) | |
parent | b1a74bf8212367be2b1d6685c11a84e056eaaaf1 (diff) |
Merge branch 'x86/fpu' into x86/smap
Reason for merge:
x86/fpu changed the structure of some of the code that x86/smap
changes; mostly fpu-internal.h but also minor changes to the
signal code.
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
Resolved Conflicts:
arch/x86/ia32/ia32_signal.c
arch/x86/include/asm/fpu-internal.h
arch/x86/kernel/signal.c
Diffstat (limited to 'arch/x86/include/asm/fpu-internal.h')
-rw-r--r-- | arch/x86/include/asm/fpu-internal.h | 411 |
1 files changed, 244 insertions, 167 deletions
diff --git a/arch/x86/include/asm/fpu-internal.h b/arch/x86/include/asm/fpu-internal.h index 0fe13583a028..409b9ccf5518 100644 --- a/arch/x86/include/asm/fpu-internal.h +++ b/arch/x86/include/asm/fpu-internal.h | |||
@@ -12,6 +12,7 @@ | |||
12 | 12 | ||
13 | #include <linux/kernel_stat.h> | 13 | #include <linux/kernel_stat.h> |
14 | #include <linux/regset.h> | 14 | #include <linux/regset.h> |
15 | #include <linux/compat.h> | ||
15 | #include <linux/slab.h> | 16 | #include <linux/slab.h> |
16 | #include <asm/asm.h> | 17 | #include <asm/asm.h> |
17 | #include <asm/cpufeature.h> | 18 | #include <asm/cpufeature.h> |
@@ -20,43 +21,76 @@ | |||
20 | #include <asm/user.h> | 21 | #include <asm/user.h> |
21 | #include <asm/uaccess.h> | 22 | #include <asm/uaccess.h> |
22 | #include <asm/xsave.h> | 23 | #include <asm/xsave.h> |
24 | #include <asm/smap.h> | ||
23 | 25 | ||
24 | extern unsigned int sig_xstate_size; | 26 | #ifdef CONFIG_X86_64 |
27 | # include <asm/sigcontext32.h> | ||
28 | # include <asm/user32.h> | ||
29 | int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | ||
30 | compat_sigset_t *set, struct pt_regs *regs); | ||
31 | int ia32_setup_frame(int sig, struct k_sigaction *ka, | ||
32 | compat_sigset_t *set, struct pt_regs *regs); | ||
33 | #else | ||
34 | # define user_i387_ia32_struct user_i387_struct | ||
35 | # define user32_fxsr_struct user_fxsr_struct | ||
36 | # define ia32_setup_frame __setup_frame | ||
37 | # define ia32_setup_rt_frame __setup_rt_frame | ||
38 | #endif | ||
39 | |||
40 | extern unsigned int mxcsr_feature_mask; | ||
25 | extern void fpu_init(void); | 41 | extern void fpu_init(void); |
42 | extern void eager_fpu_init(void); | ||
26 | 43 | ||
27 | DECLARE_PER_CPU(struct task_struct *, fpu_owner_task); | 44 | DECLARE_PER_CPU(struct task_struct *, fpu_owner_task); |
28 | 45 | ||
46 | extern void convert_from_fxsr(struct user_i387_ia32_struct *env, | ||
47 | struct task_struct *tsk); | ||
48 | extern void convert_to_fxsr(struct task_struct *tsk, | ||
49 | const struct user_i387_ia32_struct *env); | ||
50 | |||
29 | extern user_regset_active_fn fpregs_active, xfpregs_active; | 51 | extern user_regset_active_fn fpregs_active, xfpregs_active; |
30 | extern user_regset_get_fn fpregs_get, xfpregs_get, fpregs_soft_get, | 52 | extern user_regset_get_fn fpregs_get, xfpregs_get, fpregs_soft_get, |
31 | xstateregs_get; | 53 | xstateregs_get; |
32 | extern user_regset_set_fn fpregs_set, xfpregs_set, fpregs_soft_set, | 54 | extern user_regset_set_fn fpregs_set, xfpregs_set, fpregs_soft_set, |
33 | xstateregs_set; | 55 | xstateregs_set; |
34 | 56 | ||
35 | |||
36 | /* | 57 | /* |
37 | * xstateregs_active == fpregs_active. Please refer to the comment | 58 | * xstateregs_active == fpregs_active. Please refer to the comment |
38 | * at the definition of fpregs_active. | 59 | * at the definition of fpregs_active. |
39 | */ | 60 | */ |
40 | #define xstateregs_active fpregs_active | 61 | #define xstateregs_active fpregs_active |
41 | 62 | ||
42 | extern struct _fpx_sw_bytes fx_sw_reserved; | ||
43 | #ifdef CONFIG_IA32_EMULATION | ||
44 | extern unsigned int sig_xstate_ia32_size; | ||
45 | extern struct _fpx_sw_bytes fx_sw_reserved_ia32; | ||
46 | struct _fpstate_ia32; | ||
47 | struct _xstate_ia32; | ||
48 | extern int save_i387_xstate_ia32(void __user *buf); | ||
49 | extern int restore_i387_xstate_ia32(void __user *buf); | ||
50 | #endif | ||
51 | |||
52 | #ifdef CONFIG_MATH_EMULATION | 63 | #ifdef CONFIG_MATH_EMULATION |
64 | # define HAVE_HWFP (boot_cpu_data.hard_math) | ||
53 | extern void finit_soft_fpu(struct i387_soft_struct *soft); | 65 | extern void finit_soft_fpu(struct i387_soft_struct *soft); |
54 | #else | 66 | #else |
67 | # define HAVE_HWFP 1 | ||
55 | static inline void finit_soft_fpu(struct i387_soft_struct *soft) {} | 68 | static inline void finit_soft_fpu(struct i387_soft_struct *soft) {} |
56 | #endif | 69 | #endif |
57 | 70 | ||
71 | static inline int is_ia32_compat_frame(void) | ||
72 | { | ||
73 | return config_enabled(CONFIG_IA32_EMULATION) && | ||
74 | test_thread_flag(TIF_IA32); | ||
75 | } | ||
76 | |||
77 | static inline int is_ia32_frame(void) | ||
78 | { | ||
79 | return config_enabled(CONFIG_X86_32) || is_ia32_compat_frame(); | ||
80 | } | ||
81 | |||
82 | static inline int is_x32_frame(void) | ||
83 | { | ||
84 | return config_enabled(CONFIG_X86_X32_ABI) && test_thread_flag(TIF_X32); | ||
85 | } | ||
86 | |||
58 | #define X87_FSW_ES (1 << 7) /* Exception Summary */ | 87 | #define X87_FSW_ES (1 << 7) /* Exception Summary */ |
59 | 88 | ||
89 | static __always_inline __pure bool use_eager_fpu(void) | ||
90 | { | ||
91 | return static_cpu_has(X86_FEATURE_EAGER_FPU); | ||
92 | } | ||
93 | |||
60 | static __always_inline __pure bool use_xsaveopt(void) | 94 | static __always_inline __pure bool use_xsaveopt(void) |
61 | { | 95 | { |
62 | return static_cpu_has(X86_FEATURE_XSAVEOPT); | 96 | return static_cpu_has(X86_FEATURE_XSAVEOPT); |
@@ -72,6 +106,13 @@ static __always_inline __pure bool use_fxsr(void) | |||
72 | return static_cpu_has(X86_FEATURE_FXSR); | 106 | return static_cpu_has(X86_FEATURE_FXSR); |
73 | } | 107 | } |
74 | 108 | ||
109 | static inline void fx_finit(struct i387_fxsave_struct *fx) | ||
110 | { | ||
111 | memset(fx, 0, xstate_size); | ||
112 | fx->cwd = 0x37f; | ||
113 | fx->mxcsr = MXCSR_DEFAULT; | ||
114 | } | ||
115 | |||
75 | extern void __sanitize_i387_state(struct task_struct *); | 116 | extern void __sanitize_i387_state(struct task_struct *); |
76 | 117 | ||
77 | static inline void sanitize_i387_state(struct task_struct *tsk) | 118 | static inline void sanitize_i387_state(struct task_struct *tsk) |
@@ -81,133 +122,104 @@ static inline void sanitize_i387_state(struct task_struct *tsk) | |||
81 | __sanitize_i387_state(tsk); | 122 | __sanitize_i387_state(tsk); |
82 | } | 123 | } |
83 | 124 | ||
84 | #ifdef CONFIG_X86_64 | 125 | #define user_insn(insn, output, input...) \ |
85 | static inline int fxrstor_checking(struct i387_fxsave_struct *fx) | 126 | ({ \ |
86 | { | 127 | int err; \ |
87 | int err; | 128 | asm volatile(ASM_STAC "\n" \ |
88 | 129 | "1:" #insn "\n\t" \ | |
89 | /* See comment in fxsave() below. */ | 130 | "2: " ASM_CLAC "\n" \ |
90 | #ifdef CONFIG_AS_FXSAVEQ | 131 | ".section .fixup,\"ax\"\n" \ |
91 | asm volatile("1: fxrstorq %[fx]\n\t" | 132 | "3: movl $-1,%[err]\n" \ |
92 | "2:\n" | 133 | " jmp 2b\n" \ |
93 | ".section .fixup,\"ax\"\n" | 134 | ".previous\n" \ |
94 | "3: movl $-1,%[err]\n" | 135 | _ASM_EXTABLE(1b, 3b) \ |
95 | " jmp 2b\n" | 136 | : [err] "=r" (err), output \ |
96 | ".previous\n" | 137 | : "0"(0), input); \ |
97 | _ASM_EXTABLE(1b, 3b) | 138 | err; \ |
98 | : [err] "=r" (err) | 139 | }) |
99 | : [fx] "m" (*fx), "0" (0)); | 140 | |
100 | #else | 141 | #define check_insn(insn, output, input...) \ |
101 | asm volatile("1: rex64/fxrstor (%[fx])\n\t" | 142 | ({ \ |
102 | "2:\n" | 143 | int err; \ |
103 | ".section .fixup,\"ax\"\n" | 144 | asm volatile("1:" #insn "\n\t" \ |
104 | "3: movl $-1,%[err]\n" | 145 | "2:\n" \ |
105 | " jmp 2b\n" | 146 | ".section .fixup,\"ax\"\n" \ |
106 | ".previous\n" | 147 | "3: movl $-1,%[err]\n" \ |
107 | _ASM_EXTABLE(1b, 3b) | 148 | " jmp 2b\n" \ |
108 | : [err] "=r" (err) | 149 | ".previous\n" \ |
109 | : [fx] "R" (fx), "m" (*fx), "0" (0)); | 150 | _ASM_EXTABLE(1b, 3b) \ |
110 | #endif | 151 | : [err] "=r" (err), output \ |
111 | return err; | 152 | : "0"(0), input); \ |
153 | err; \ | ||
154 | }) | ||
155 | |||
156 | static inline int fsave_user(struct i387_fsave_struct __user *fx) | ||
157 | { | ||
158 | return user_insn(fnsave %[fx]; fwait, [fx] "=m" (*fx), "m" (*fx)); | ||
112 | } | 159 | } |
113 | 160 | ||
114 | static inline int fxsave_user(struct i387_fxsave_struct __user *fx) | 161 | static inline int fxsave_user(struct i387_fxsave_struct __user *fx) |
115 | { | 162 | { |
116 | int err; | 163 | if (config_enabled(CONFIG_X86_32)) |
164 | return user_insn(fxsave %[fx], [fx] "=m" (*fx), "m" (*fx)); | ||
165 | else if (config_enabled(CONFIG_AS_FXSAVEQ)) | ||
166 | return user_insn(fxsaveq %[fx], [fx] "=m" (*fx), "m" (*fx)); | ||
117 | 167 | ||
118 | /* | 168 | /* See comment in fpu_fxsave() below. */ |
119 | * Clear the bytes not touched by the fxsave and reserved | 169 | return user_insn(rex64/fxsave (%[fx]), "=m" (*fx), [fx] "R" (fx)); |
120 | * for the SW usage. | ||
121 | */ | ||
122 | err = __clear_user(&fx->sw_reserved, | ||
123 | sizeof(struct _fpx_sw_bytes)); | ||
124 | if (unlikely(err)) | ||
125 | return -EFAULT; | ||
126 | |||
127 | /* See comment in fxsave() below. */ | ||
128 | #ifdef CONFIG_AS_FXSAVEQ | ||
129 | asm volatile(ASM_STAC "\n" | ||
130 | "1: fxsaveq %[fx]\n\t" | ||
131 | "2: " ASM_CLAC "\n" | ||
132 | ".section .fixup,\"ax\"\n" | ||
133 | "3: movl $-1,%[err]\n" | ||
134 | " jmp 2b\n" | ||
135 | ".previous\n" | ||
136 | _ASM_EXTABLE(1b, 3b) | ||
137 | : [err] "=r" (err), [fx] "=m" (*fx) | ||
138 | : "0" (0)); | ||
139 | #else | ||
140 | asm volatile(ASM_STAC "\n" | ||
141 | "1: rex64/fxsave (%[fx])\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), "=m" (*fx) | ||
149 | : [fx] "R" (fx), "0" (0)); | ||
150 | #endif | ||
151 | if (unlikely(err) && | ||
152 | __clear_user(fx, sizeof(struct i387_fxsave_struct))) | ||
153 | err = -EFAULT; | ||
154 | /* No need to clear here because the caller clears USED_MATH */ | ||
155 | return err; | ||
156 | } | 170 | } |
157 | 171 | ||
158 | static inline void fpu_fxsave(struct fpu *fpu) | 172 | static inline int fxrstor_checking(struct i387_fxsave_struct *fx) |
159 | { | 173 | { |
160 | /* Using "rex64; fxsave %0" is broken because, if the memory operand | 174 | if (config_enabled(CONFIG_X86_32)) |
161 | uses any extended registers for addressing, a second REX prefix | 175 | return check_insn(fxrstor %[fx], "=m" (*fx), [fx] "m" (*fx)); |
162 | will be generated (to the assembler, rex64 followed by semicolon | 176 | else if (config_enabled(CONFIG_AS_FXSAVEQ)) |
163 | is a separate instruction), and hence the 64-bitness is lost. */ | 177 | return check_insn(fxrstorq %[fx], "=m" (*fx), [fx] "m" (*fx)); |
164 | 178 | ||
165 | #ifdef CONFIG_AS_FXSAVEQ | 179 | /* See comment in fpu_fxsave() below. */ |
166 | /* Using "fxsaveq %0" would be the ideal choice, but is only supported | 180 | return check_insn(rex64/fxrstor (%[fx]), "=m" (*fx), [fx] "R" (fx), |
167 | starting with gas 2.16. */ | 181 | "m" (*fx)); |
168 | __asm__ __volatile__("fxsaveq %0" | ||
169 | : "=m" (fpu->state->fxsave)); | ||
170 | #else | ||
171 | /* Using, as a workaround, the properly prefixed form below isn't | ||
172 | accepted by any binutils version so far released, complaining that | ||
173 | the same type of prefix is used twice if an extended register is | ||
174 | needed for addressing (fix submitted to mainline 2005-11-21). | ||
175 | asm volatile("rex64/fxsave %0" | ||
176 | : "=m" (fpu->state->fxsave)); | ||
177 | This, however, we can work around by forcing the compiler to select | ||
178 | an addressing mode that doesn't require extended registers. */ | ||
179 | asm volatile("rex64/fxsave (%[fx])" | ||
180 | : "=m" (fpu->state->fxsave) | ||
181 | : [fx] "R" (&fpu->state->fxsave)); | ||
182 | #endif | ||
183 | } | 182 | } |
184 | 183 | ||
185 | #else /* CONFIG_X86_32 */ | 184 | static inline int frstor_checking(struct i387_fsave_struct *fx) |
186 | |||
187 | /* perform fxrstor iff the processor has extended states, otherwise frstor */ | ||
188 | static inline int fxrstor_checking(struct i387_fxsave_struct *fx) | ||
189 | { | 185 | { |
190 | /* | 186 | return check_insn(frstor %[fx], "=m" (*fx), [fx] "m" (*fx)); |
191 | * The "nop" is needed to make the instructions the same | ||
192 | * length. | ||
193 | */ | ||
194 | alternative_input( | ||
195 | "nop ; frstor %1", | ||
196 | "fxrstor %1", | ||
197 | X86_FEATURE_FXSR, | ||
198 | "m" (*fx)); | ||
199 | |||
200 | return 0; | ||
201 | } | 187 | } |
202 | 188 | ||
203 | static inline void fpu_fxsave(struct fpu *fpu) | 189 | static inline void fpu_fxsave(struct fpu *fpu) |
204 | { | 190 | { |
205 | asm volatile("fxsave %[fx]" | 191 | if (config_enabled(CONFIG_X86_32)) |
206 | : [fx] "=m" (fpu->state->fxsave)); | 192 | asm volatile( "fxsave %[fx]" : [fx] "=m" (fpu->state->fxsave)); |
193 | else if (config_enabled(CONFIG_AS_FXSAVEQ)) | ||
194 | asm volatile("fxsaveq %0" : "=m" (fpu->state->fxsave)); | ||
195 | else { | ||
196 | /* Using "rex64; fxsave %0" is broken because, if the memory | ||
197 | * operand uses any extended registers for addressing, a second | ||
198 | * REX prefix will be generated (to the assembler, rex64 | ||
199 | * followed by semicolon is a separate instruction), and hence | ||
200 | * the 64-bitness is lost. | ||
201 | * | ||
202 | * Using "fxsaveq %0" would be the ideal choice, but is only | ||
203 | * supported starting with gas 2.16. | ||
204 | * | ||
205 | * Using, as a workaround, the properly prefixed form below | ||
206 | * isn't accepted by any binutils version so far released, | ||
207 | * complaining that the same type of prefix is used twice if | ||
208 | * an extended register is needed for addressing (fix submitted | ||
209 | * to mainline 2005-11-21). | ||
210 | * | ||
211 | * asm volatile("rex64/fxsave %0" : "=m" (fpu->state->fxsave)); | ||
212 | * | ||
213 | * This, however, we can work around by forcing the compiler to | ||
214 | * select an addressing mode that doesn't require extended | ||
215 | * registers. | ||
216 | */ | ||
217 | asm volatile( "rex64/fxsave (%[fx])" | ||
218 | : "=m" (fpu->state->fxsave) | ||
219 | : [fx] "R" (&fpu->state->fxsave)); | ||
220 | } | ||
207 | } | 221 | } |
208 | 222 | ||
209 | #endif /* CONFIG_X86_64 */ | ||
210 | |||
211 | /* | 223 | /* |
212 | * These must be called with preempt disabled. Returns | 224 | * These must be called with preempt disabled. Returns |
213 | * 'true' if the FPU state is still intact. | 225 | * 'true' if the FPU state is still intact. |
@@ -250,17 +262,14 @@ static inline int __save_init_fpu(struct task_struct *tsk) | |||
250 | return fpu_save_init(&tsk->thread.fpu); | 262 | return fpu_save_init(&tsk->thread.fpu); |
251 | } | 263 | } |
252 | 264 | ||
253 | static inline int fpu_fxrstor_checking(struct fpu *fpu) | ||
254 | { | ||
255 | return fxrstor_checking(&fpu->state->fxsave); | ||
256 | } | ||
257 | |||
258 | static inline int fpu_restore_checking(struct fpu *fpu) | 265 | static inline int fpu_restore_checking(struct fpu *fpu) |
259 | { | 266 | { |
260 | if (use_xsave()) | 267 | if (use_xsave()) |
261 | return fpu_xrstor_checking(fpu); | 268 | return fpu_xrstor_checking(&fpu->state->xsave); |
269 | else if (use_fxsr()) | ||
270 | return fxrstor_checking(&fpu->state->fxsave); | ||
262 | else | 271 | else |
263 | return fpu_fxrstor_checking(fpu); | 272 | return frstor_checking(&fpu->state->fsave); |
264 | } | 273 | } |
265 | 274 | ||
266 | static inline int restore_fpu_checking(struct task_struct *tsk) | 275 | static inline int restore_fpu_checking(struct task_struct *tsk) |
@@ -312,15 +321,52 @@ static inline void __thread_set_has_fpu(struct task_struct *tsk) | |||
312 | static inline void __thread_fpu_end(struct task_struct *tsk) | 321 | static inline void __thread_fpu_end(struct task_struct *tsk) |
313 | { | 322 | { |
314 | __thread_clear_has_fpu(tsk); | 323 | __thread_clear_has_fpu(tsk); |
315 | stts(); | 324 | if (!use_eager_fpu()) |
325 | stts(); | ||
316 | } | 326 | } |
317 | 327 | ||
318 | static inline void __thread_fpu_begin(struct task_struct *tsk) | 328 | static inline void __thread_fpu_begin(struct task_struct *tsk) |
319 | { | 329 | { |
320 | clts(); | 330 | if (!use_eager_fpu()) |
331 | clts(); | ||
321 | __thread_set_has_fpu(tsk); | 332 | __thread_set_has_fpu(tsk); |
322 | } | 333 | } |
323 | 334 | ||
335 | static inline void __drop_fpu(struct task_struct *tsk) | ||
336 | { | ||
337 | if (__thread_has_fpu(tsk)) { | ||
338 | /* Ignore delayed exceptions from user space */ | ||
339 | asm volatile("1: fwait\n" | ||
340 | "2:\n" | ||
341 | _ASM_EXTABLE(1b, 2b)); | ||
342 | __thread_fpu_end(tsk); | ||
343 | } | ||
344 | } | ||
345 | |||
346 | static inline void drop_fpu(struct task_struct *tsk) | ||
347 | { | ||
348 | /* | ||
349 | * Forget coprocessor state.. | ||
350 | */ | ||
351 | preempt_disable(); | ||
352 | tsk->fpu_counter = 0; | ||
353 | __drop_fpu(tsk); | ||
354 | clear_used_math(); | ||
355 | preempt_enable(); | ||
356 | } | ||
357 | |||
358 | static inline void drop_init_fpu(struct task_struct *tsk) | ||
359 | { | ||
360 | if (!use_eager_fpu()) | ||
361 | drop_fpu(tsk); | ||
362 | else { | ||
363 | if (use_xsave()) | ||
364 | xrstor_state(init_xstate_buf, -1); | ||
365 | else | ||
366 | fxrstor_checking(&init_xstate_buf->i387); | ||
367 | } | ||
368 | } | ||
369 | |||
324 | /* | 370 | /* |
325 | * FPU state switching for scheduling. | 371 | * FPU state switching for scheduling. |
326 | * | 372 | * |
@@ -354,7 +400,12 @@ static inline fpu_switch_t switch_fpu_prepare(struct task_struct *old, struct ta | |||
354 | { | 400 | { |
355 | fpu_switch_t fpu; | 401 | fpu_switch_t fpu; |
356 | 402 | ||
357 | fpu.preload = tsk_used_math(new) && new->fpu_counter > 5; | 403 | /* |
404 | * If the task has used the math, pre-load the FPU on xsave processors | ||
405 | * or if the past 5 consecutive context-switches used math. | ||
406 | */ | ||
407 | fpu.preload = tsk_used_math(new) && (use_eager_fpu() || | ||
408 | new->fpu_counter > 5); | ||
358 | if (__thread_has_fpu(old)) { | 409 | if (__thread_has_fpu(old)) { |
359 | if (!__save_init_fpu(old)) | 410 | if (!__save_init_fpu(old)) |
360 | cpu = ~0; | 411 | cpu = ~0; |
@@ -366,14 +417,14 @@ static inline fpu_switch_t switch_fpu_prepare(struct task_struct *old, struct ta | |||
366 | new->fpu_counter++; | 417 | new->fpu_counter++; |
367 | __thread_set_has_fpu(new); | 418 | __thread_set_has_fpu(new); |
368 | prefetch(new->thread.fpu.state); | 419 | prefetch(new->thread.fpu.state); |
369 | } else | 420 | } else if (!use_eager_fpu()) |
370 | stts(); | 421 | stts(); |
371 | } else { | 422 | } else { |
372 | old->fpu_counter = 0; | 423 | old->fpu_counter = 0; |
373 | old->thread.fpu.last_cpu = ~0; | 424 | old->thread.fpu.last_cpu = ~0; |
374 | if (fpu.preload) { | 425 | if (fpu.preload) { |
375 | new->fpu_counter++; | 426 | new->fpu_counter++; |
376 | if (fpu_lazy_restore(new, cpu)) | 427 | if (!use_eager_fpu() && fpu_lazy_restore(new, cpu)) |
377 | fpu.preload = 0; | 428 | fpu.preload = 0; |
378 | else | 429 | else |
379 | prefetch(new->thread.fpu.state); | 430 | prefetch(new->thread.fpu.state); |
@@ -393,44 +444,40 @@ static inline void switch_fpu_finish(struct task_struct *new, fpu_switch_t fpu) | |||
393 | { | 444 | { |
394 | if (fpu.preload) { | 445 | if (fpu.preload) { |
395 | if (unlikely(restore_fpu_checking(new))) | 446 | if (unlikely(restore_fpu_checking(new))) |
396 | __thread_fpu_end(new); | 447 | drop_init_fpu(new); |
397 | } | 448 | } |
398 | } | 449 | } |
399 | 450 | ||
400 | /* | 451 | /* |
401 | * Signal frame handlers... | 452 | * Signal frame handlers... |
402 | */ | 453 | */ |
403 | extern int save_i387_xstate(void __user *buf); | 454 | extern int save_xstate_sig(void __user *buf, void __user *fx, int size); |
404 | extern int restore_i387_xstate(void __user *buf); | 455 | extern int __restore_xstate_sig(void __user *buf, void __user *fx, int size); |
405 | 456 | ||
406 | static inline void __clear_fpu(struct task_struct *tsk) | 457 | static inline int xstate_sigframe_size(void) |
407 | { | 458 | { |
408 | if (__thread_has_fpu(tsk)) { | 459 | return use_xsave() ? xstate_size + FP_XSTATE_MAGIC2_SIZE : xstate_size; |
409 | /* Ignore delayed exceptions from user space */ | 460 | } |
410 | asm volatile("1: fwait\n" | 461 | |
411 | "2:\n" | 462 | static inline int restore_xstate_sig(void __user *buf, int ia32_frame) |
412 | _ASM_EXTABLE(1b, 2b)); | 463 | { |
413 | __thread_fpu_end(tsk); | 464 | void __user *buf_fx = buf; |
465 | int size = xstate_sigframe_size(); | ||
466 | |||
467 | if (ia32_frame && use_fxsr()) { | ||
468 | buf_fx = buf + sizeof(struct i387_fsave_struct); | ||
469 | size += sizeof(struct i387_fsave_struct); | ||
414 | } | 470 | } |
471 | |||
472 | return __restore_xstate_sig(buf, buf_fx, size); | ||
415 | } | 473 | } |
416 | 474 | ||
417 | /* | 475 | /* |
418 | * The actual user_fpu_begin/end() functions | 476 | * Need to be preemption-safe. |
419 | * need to be preemption-safe. | ||
420 | * | 477 | * |
421 | * NOTE! user_fpu_end() must be used only after you | 478 | * NOTE! user_fpu_begin() must be used only immediately before restoring |
422 | * have saved the FP state, and user_fpu_begin() must | 479 | * it. This function does not do any save/restore on their own. |
423 | * be used only immediately before restoring it. | ||
424 | * These functions do not do any save/restore on | ||
425 | * their own. | ||
426 | */ | 480 | */ |
427 | static inline void user_fpu_end(void) | ||
428 | { | ||
429 | preempt_disable(); | ||
430 | __thread_fpu_end(current); | ||
431 | preempt_enable(); | ||
432 | } | ||
433 | |||
434 | static inline void user_fpu_begin(void) | 481 | static inline void user_fpu_begin(void) |
435 | { | 482 | { |
436 | preempt_disable(); | 483 | preempt_disable(); |
@@ -439,25 +486,32 @@ static inline void user_fpu_begin(void) | |||
439 | preempt_enable(); | 486 | preempt_enable(); |
440 | } | 487 | } |
441 | 488 | ||
489 | static inline void __save_fpu(struct task_struct *tsk) | ||
490 | { | ||
491 | if (use_xsave()) | ||
492 | xsave_state(&tsk->thread.fpu.state->xsave, -1); | ||
493 | else | ||
494 | fpu_fxsave(&tsk->thread.fpu); | ||
495 | } | ||
496 | |||
442 | /* | 497 | /* |
443 | * These disable preemption on their own and are safe | 498 | * These disable preemption on their own and are safe |
444 | */ | 499 | */ |
445 | static inline void save_init_fpu(struct task_struct *tsk) | 500 | static inline void save_init_fpu(struct task_struct *tsk) |
446 | { | 501 | { |
447 | WARN_ON_ONCE(!__thread_has_fpu(tsk)); | 502 | WARN_ON_ONCE(!__thread_has_fpu(tsk)); |
503 | |||
504 | if (use_eager_fpu()) { | ||
505 | __save_fpu(tsk); | ||
506 | return; | ||
507 | } | ||
508 | |||
448 | preempt_disable(); | 509 | preempt_disable(); |
449 | __save_init_fpu(tsk); | 510 | __save_init_fpu(tsk); |
450 | __thread_fpu_end(tsk); | 511 | __thread_fpu_end(tsk); |
451 | preempt_enable(); | 512 | preempt_enable(); |
452 | } | 513 | } |
453 | 514 | ||
454 | static inline void clear_fpu(struct task_struct *tsk) | ||
455 | { | ||
456 | preempt_disable(); | ||
457 | __clear_fpu(tsk); | ||
458 | preempt_enable(); | ||
459 | } | ||
460 | |||
461 | /* | 515 | /* |
462 | * i387 state interaction | 516 | * i387 state interaction |
463 | */ | 517 | */ |
@@ -512,11 +566,34 @@ static inline void fpu_free(struct fpu *fpu) | |||
512 | } | 566 | } |
513 | } | 567 | } |
514 | 568 | ||
515 | static inline void fpu_copy(struct fpu *dst, struct fpu *src) | 569 | static inline void fpu_copy(struct task_struct *dst, struct task_struct *src) |
516 | { | 570 | { |
517 | memcpy(dst->state, src->state, xstate_size); | 571 | if (use_eager_fpu()) { |
572 | memset(&dst->thread.fpu.state->xsave, 0, xstate_size); | ||
573 | __save_fpu(dst); | ||
574 | } else { | ||
575 | struct fpu *dfpu = &dst->thread.fpu; | ||
576 | struct fpu *sfpu = &src->thread.fpu; | ||
577 | |||
578 | unlazy_fpu(src); | ||
579 | memcpy(dfpu->state, sfpu->state, xstate_size); | ||
580 | } | ||
518 | } | 581 | } |
519 | 582 | ||
520 | extern void fpu_finit(struct fpu *fpu); | 583 | static inline unsigned long |
584 | alloc_mathframe(unsigned long sp, int ia32_frame, unsigned long *buf_fx, | ||
585 | unsigned long *size) | ||
586 | { | ||
587 | unsigned long frame_size = xstate_sigframe_size(); | ||
588 | |||
589 | *buf_fx = sp = round_down(sp - frame_size, 64); | ||
590 | if (ia32_frame && use_fxsr()) { | ||
591 | frame_size += sizeof(struct i387_fsave_struct); | ||
592 | sp -= sizeof(struct i387_fsave_struct); | ||
593 | } | ||
594 | |||
595 | *size = frame_size; | ||
596 | return sp; | ||
597 | } | ||
521 | 598 | ||
522 | #endif | 599 | #endif |