diff options
author | Avi Kivity <avi@redhat.com> | 2010-05-06 04:45:46 -0400 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2010-05-10 13:48:55 -0400 |
commit | 86603283326c9e95e5ad4e9fdddeec93cac5d9ad (patch) | |
tree | 1a26a37434e920f9519b547814a1a9af35022de8 /arch/x86/include | |
parent | c9ad488289144ae5ef53b012e15895ef1f5e4bb6 (diff) |
x86: Introduce 'struct fpu' and related API
Currently all fpu state access is through tsk->thread.xstate. Since we wish
to generalize fpu access to non-task contexts, wrap the state in a new
'struct fpu' and convert existing access to use an fpu API.
Signal frame handlers are not converted to the API since they will remain
task context only things.
Signed-off-by: Avi Kivity <avi@redhat.com>
Acked-by: Suresh Siddha <suresh.b.siddha@intel.com>
LKML-Reference: <1273135546-29690-3-git-send-email-avi@redhat.com>
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Diffstat (limited to 'arch/x86/include')
-rw-r--r-- | arch/x86/include/asm/i387.h | 115 | ||||
-rw-r--r-- | arch/x86/include/asm/processor.h | 6 | ||||
-rw-r--r-- | arch/x86/include/asm/xsave.h | 7 |
3 files changed, 94 insertions, 34 deletions
diff --git a/arch/x86/include/asm/i387.h b/arch/x86/include/asm/i387.h index a301a6825c3a..1a8cca33b736 100644 --- a/arch/x86/include/asm/i387.h +++ b/arch/x86/include/asm/i387.h | |||
@@ -16,6 +16,7 @@ | |||
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> |
20 | #include <asm/processor.h> | 21 | #include <asm/processor.h> |
21 | #include <asm/sigcontext.h> | 22 | #include <asm/sigcontext.h> |
@@ -103,10 +104,10 @@ static inline int fxrstor_checking(struct i387_fxsave_struct *fx) | |||
103 | values. The kernel data segment can be sometimes 0 and sometimes | 104 | values. The kernel data segment can be sometimes 0 and sometimes |
104 | new user value. Both should be ok. | 105 | new user value. Both should be ok. |
105 | Use the PDA as safe address because it should be already in L1. */ | 106 | Use the PDA as safe address because it should be already in L1. */ |
106 | static inline void clear_fpu_state(struct task_struct *tsk) | 107 | static inline void fpu_clear(struct fpu *fpu) |
107 | { | 108 | { |
108 | struct xsave_struct *xstate = &tsk->thread.xstate->xsave; | 109 | struct xsave_struct *xstate = &fpu->state->xsave; |
109 | struct i387_fxsave_struct *fx = &tsk->thread.xstate->fxsave; | 110 | struct i387_fxsave_struct *fx = &fpu->state->fxsave; |
110 | 111 | ||
111 | /* | 112 | /* |
112 | * xsave header may indicate the init state of the FP. | 113 | * xsave header may indicate the init state of the FP. |
@@ -123,6 +124,11 @@ static inline void clear_fpu_state(struct task_struct *tsk) | |||
123 | X86_FEATURE_FXSAVE_LEAK); | 124 | X86_FEATURE_FXSAVE_LEAK); |
124 | } | 125 | } |
125 | 126 | ||
127 | static inline void clear_fpu_state(struct task_struct *tsk) | ||
128 | { | ||
129 | fpu_clear(&tsk->thread.fpu); | ||
130 | } | ||
131 | |||
126 | static inline int fxsave_user(struct i387_fxsave_struct __user *fx) | 132 | static inline int fxsave_user(struct i387_fxsave_struct __user *fx) |
127 | { | 133 | { |
128 | int err; | 134 | int err; |
@@ -147,7 +153,7 @@ static inline int fxsave_user(struct i387_fxsave_struct __user *fx) | |||
147 | return err; | 153 | return err; |
148 | } | 154 | } |
149 | 155 | ||
150 | static inline void fxsave(struct task_struct *tsk) | 156 | static inline void fpu_fxsave(struct fpu *fpu) |
151 | { | 157 | { |
152 | /* Using "rex64; fxsave %0" is broken because, if the memory operand | 158 | /* Using "rex64; fxsave %0" is broken because, if the memory operand |
153 | uses any extended registers for addressing, a second REX prefix | 159 | uses any extended registers for addressing, a second REX prefix |
@@ -157,42 +163,45 @@ static inline void fxsave(struct task_struct *tsk) | |||
157 | /* Using "fxsaveq %0" would be the ideal choice, but is only supported | 163 | /* Using "fxsaveq %0" would be the ideal choice, but is only supported |
158 | starting with gas 2.16. */ | 164 | starting with gas 2.16. */ |
159 | __asm__ __volatile__("fxsaveq %0" | 165 | __asm__ __volatile__("fxsaveq %0" |
160 | : "=m" (tsk->thread.xstate->fxsave)); | 166 | : "=m" (fpu->state->fxsave)); |
161 | #elif 0 | 167 | #elif 0 |
162 | /* Using, as a workaround, the properly prefixed form below isn't | 168 | /* Using, as a workaround, the properly prefixed form below isn't |
163 | accepted by any binutils version so far released, complaining that | 169 | accepted by any binutils version so far released, complaining that |
164 | the same type of prefix is used twice if an extended register is | 170 | the same type of prefix is used twice if an extended register is |
165 | needed for addressing (fix submitted to mainline 2005-11-21). */ | 171 | needed for addressing (fix submitted to mainline 2005-11-21). */ |
166 | __asm__ __volatile__("rex64/fxsave %0" | 172 | __asm__ __volatile__("rex64/fxsave %0" |
167 | : "=m" (tsk->thread.xstate->fxsave)); | 173 | : "=m" (fpu->state->fxsave)); |
168 | #else | 174 | #else |
169 | /* This, however, we can work around by forcing the compiler to select | 175 | /* This, however, we can work around by forcing the compiler to select |
170 | an addressing mode that doesn't require extended registers. */ | 176 | an addressing mode that doesn't require extended registers. */ |
171 | __asm__ __volatile__("rex64/fxsave (%1)" | 177 | __asm__ __volatile__("rex64/fxsave (%1)" |
172 | : "=m" (tsk->thread.xstate->fxsave) | 178 | : "=m" (fpu->state->fxsave) |
173 | : "cdaSDb" (&tsk->thread.xstate->fxsave)); | 179 | : "cdaSDb" (&fpu->state->fxsave)); |
174 | #endif | 180 | #endif |
175 | } | 181 | } |
176 | 182 | ||
177 | static inline void __save_init_fpu(struct task_struct *tsk) | 183 | static inline void fpu_save_init(struct fpu *fpu) |
178 | { | 184 | { |
179 | if (use_xsave()) | 185 | if (use_xsave()) |
180 | xsave(tsk); | 186 | fpu_xsave(fpu); |
181 | else | 187 | else |
182 | fxsave(tsk); | 188 | fpu_fxsave(fpu); |
183 | 189 | ||
184 | clear_fpu_state(tsk); | 190 | fpu_clear(fpu); |
191 | } | ||
192 | |||
193 | static inline void __save_init_fpu(struct task_struct *tsk) | ||
194 | { | ||
195 | fpu_save_init(&tsk->thread.fpu); | ||
185 | task_thread_info(tsk)->status &= ~TS_USEDFPU; | 196 | task_thread_info(tsk)->status &= ~TS_USEDFPU; |
186 | } | 197 | } |
187 | 198 | ||
188 | #else /* CONFIG_X86_32 */ | 199 | #else /* CONFIG_X86_32 */ |
189 | 200 | ||
190 | #ifdef CONFIG_MATH_EMULATION | 201 | #ifdef CONFIG_MATH_EMULATION |
191 | extern void finit_task(struct task_struct *tsk); | 202 | extern void finit_soft_fpu(struct i387_soft_struct *soft); |
192 | #else | 203 | #else |
193 | static inline void finit_task(struct task_struct *tsk) | 204 | static inline void finit_soft_fpu(struct i387_soft_struct *soft) {} |
194 | { | ||
195 | } | ||
196 | #endif | 205 | #endif |
197 | 206 | ||
198 | static inline void tolerant_fwait(void) | 207 | static inline void tolerant_fwait(void) |
@@ -228,13 +237,13 @@ static inline int fxrstor_checking(struct i387_fxsave_struct *fx) | |||
228 | /* | 237 | /* |
229 | * These must be called with preempt disabled | 238 | * These must be called with preempt disabled |
230 | */ | 239 | */ |
231 | static inline void __save_init_fpu(struct task_struct *tsk) | 240 | static inline void fpu_save_init(struct fpu *fpu) |
232 | { | 241 | { |
233 | if (use_xsave()) { | 242 | if (use_xsave()) { |
234 | struct xsave_struct *xstate = &tsk->thread.xstate->xsave; | 243 | struct xsave_struct *xstate = &fpu->state->xsave; |
235 | struct i387_fxsave_struct *fx = &tsk->thread.xstate->fxsave; | 244 | struct i387_fxsave_struct *fx = &fpu->state->fxsave; |
236 | 245 | ||
237 | xsave(tsk); | 246 | fpu_xsave(fpu); |
238 | 247 | ||
239 | /* | 248 | /* |
240 | * xsave header may indicate the init state of the FP. | 249 | * xsave header may indicate the init state of the FP. |
@@ -258,8 +267,8 @@ static inline void __save_init_fpu(struct task_struct *tsk) | |||
258 | "fxsave %[fx]\n" | 267 | "fxsave %[fx]\n" |
259 | "bt $7,%[fsw] ; jnc 1f ; fnclex\n1:", | 268 | "bt $7,%[fsw] ; jnc 1f ; fnclex\n1:", |
260 | X86_FEATURE_FXSR, | 269 | X86_FEATURE_FXSR, |
261 | [fx] "m" (tsk->thread.xstate->fxsave), | 270 | [fx] "m" (fpu->state->fxsave), |
262 | [fsw] "m" (tsk->thread.xstate->fxsave.swd) : "memory"); | 271 | [fsw] "m" (fpu->state->fxsave.swd) : "memory"); |
263 | clear_state: | 272 | clear_state: |
264 | /* AMD K7/K8 CPUs don't save/restore FDP/FIP/FOP unless an exception | 273 | /* AMD K7/K8 CPUs don't save/restore FDP/FIP/FOP unless an exception |
265 | is pending. Clear the x87 state here by setting it to fixed | 274 | is pending. Clear the x87 state here by setting it to fixed |
@@ -271,17 +280,34 @@ clear_state: | |||
271 | X86_FEATURE_FXSAVE_LEAK, | 280 | X86_FEATURE_FXSAVE_LEAK, |
272 | [addr] "m" (safe_address)); | 281 | [addr] "m" (safe_address)); |
273 | end: | 282 | end: |
283 | ; | ||
284 | } | ||
285 | |||
286 | static inline void __save_init_fpu(struct task_struct *tsk) | ||
287 | { | ||
288 | fpu_save_init(&tsk->thread.fpu); | ||
274 | task_thread_info(tsk)->status &= ~TS_USEDFPU; | 289 | task_thread_info(tsk)->status &= ~TS_USEDFPU; |
275 | } | 290 | } |
276 | 291 | ||
292 | |||
277 | #endif /* CONFIG_X86_64 */ | 293 | #endif /* CONFIG_X86_64 */ |
278 | 294 | ||
279 | static inline int restore_fpu_checking(struct task_struct *tsk) | 295 | static inline int fpu_fxrstor_checking(struct fpu *fpu) |
296 | { | ||
297 | return fxrstor_checking(&fpu->state->fxsave); | ||
298 | } | ||
299 | |||
300 | static inline int fpu_restore_checking(struct fpu *fpu) | ||
280 | { | 301 | { |
281 | if (use_xsave()) | 302 | if (use_xsave()) |
282 | return xrstor_checking(&tsk->thread.xstate->xsave); | 303 | return fpu_xrstor_checking(fpu); |
283 | else | 304 | else |
284 | return fxrstor_checking(&tsk->thread.xstate->fxsave); | 305 | return fpu_fxrstor_checking(fpu); |
306 | } | ||
307 | |||
308 | static inline int restore_fpu_checking(struct task_struct *tsk) | ||
309 | { | ||
310 | return fpu_restore_checking(&tsk->thread.fpu); | ||
285 | } | 311 | } |
286 | 312 | ||
287 | /* | 313 | /* |
@@ -409,30 +435,59 @@ static inline void clear_fpu(struct task_struct *tsk) | |||
409 | static inline unsigned short get_fpu_cwd(struct task_struct *tsk) | 435 | static inline unsigned short get_fpu_cwd(struct task_struct *tsk) |
410 | { | 436 | { |
411 | if (cpu_has_fxsr) { | 437 | if (cpu_has_fxsr) { |
412 | return tsk->thread.xstate->fxsave.cwd; | 438 | return tsk->thread.fpu.state->fxsave.cwd; |
413 | } else { | 439 | } else { |
414 | return (unsigned short)tsk->thread.xstate->fsave.cwd; | 440 | return (unsigned short)tsk->thread.fpu.state->fsave.cwd; |
415 | } | 441 | } |
416 | } | 442 | } |
417 | 443 | ||
418 | static inline unsigned short get_fpu_swd(struct task_struct *tsk) | 444 | static inline unsigned short get_fpu_swd(struct task_struct *tsk) |
419 | { | 445 | { |
420 | if (cpu_has_fxsr) { | 446 | if (cpu_has_fxsr) { |
421 | return tsk->thread.xstate->fxsave.swd; | 447 | return tsk->thread.fpu.state->fxsave.swd; |
422 | } else { | 448 | } else { |
423 | return (unsigned short)tsk->thread.xstate->fsave.swd; | 449 | return (unsigned short)tsk->thread.fpu.state->fsave.swd; |
424 | } | 450 | } |
425 | } | 451 | } |
426 | 452 | ||
427 | static inline unsigned short get_fpu_mxcsr(struct task_struct *tsk) | 453 | static inline unsigned short get_fpu_mxcsr(struct task_struct *tsk) |
428 | { | 454 | { |
429 | if (cpu_has_xmm) { | 455 | if (cpu_has_xmm) { |
430 | return tsk->thread.xstate->fxsave.mxcsr; | 456 | return tsk->thread.fpu.state->fxsave.mxcsr; |
431 | } else { | 457 | } else { |
432 | return MXCSR_DEFAULT; | 458 | return MXCSR_DEFAULT; |
433 | } | 459 | } |
434 | } | 460 | } |
435 | 461 | ||
462 | static bool fpu_allocated(struct fpu *fpu) | ||
463 | { | ||
464 | return fpu->state != NULL; | ||
465 | } | ||
466 | |||
467 | static inline int fpu_alloc(struct fpu *fpu) | ||
468 | { | ||
469 | if (fpu_allocated(fpu)) | ||
470 | return 0; | ||
471 | fpu->state = kmem_cache_alloc(task_xstate_cachep, GFP_KERNEL); | ||
472 | if (!fpu->state) | ||
473 | return -ENOMEM; | ||
474 | WARN_ON((unsigned long)fpu->state & 15); | ||
475 | return 0; | ||
476 | } | ||
477 | |||
478 | static inline void fpu_free(struct fpu *fpu) | ||
479 | { | ||
480 | if (fpu->state) { | ||
481 | kmem_cache_free(task_xstate_cachep, fpu->state); | ||
482 | fpu->state = NULL; | ||
483 | } | ||
484 | } | ||
485 | |||
486 | static inline void fpu_copy(struct fpu *dst, struct fpu *src) | ||
487 | { | ||
488 | memcpy(dst->state, src->state, xstate_size); | ||
489 | } | ||
490 | |||
436 | #endif /* __ASSEMBLY__ */ | 491 | #endif /* __ASSEMBLY__ */ |
437 | 492 | ||
438 | #define PSHUFB_XMM5_XMM0 .byte 0x66, 0x0f, 0x38, 0x00, 0xc5 | 493 | #define PSHUFB_XMM5_XMM0 .byte 0x66, 0x0f, 0x38, 0x00, 0xc5 |
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index b753ea59703a..b684f587647c 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h | |||
@@ -380,6 +380,10 @@ union thread_xstate { | |||
380 | struct xsave_struct xsave; | 380 | struct xsave_struct xsave; |
381 | }; | 381 | }; |
382 | 382 | ||
383 | struct fpu { | ||
384 | union thread_xstate *state; | ||
385 | }; | ||
386 | |||
383 | #ifdef CONFIG_X86_64 | 387 | #ifdef CONFIG_X86_64 |
384 | DECLARE_PER_CPU(struct orig_ist, orig_ist); | 388 | DECLARE_PER_CPU(struct orig_ist, orig_ist); |
385 | 389 | ||
@@ -457,7 +461,7 @@ struct thread_struct { | |||
457 | unsigned long trap_no; | 461 | unsigned long trap_no; |
458 | unsigned long error_code; | 462 | unsigned long error_code; |
459 | /* floating point and extended processor state */ | 463 | /* floating point and extended processor state */ |
460 | union thread_xstate *xstate; | 464 | struct fpu fpu; |
461 | #ifdef CONFIG_X86_32 | 465 | #ifdef CONFIG_X86_32 |
462 | /* Virtual 86 mode info */ | 466 | /* Virtual 86 mode info */ |
463 | struct vm86_struct __user *vm86_info; | 467 | struct vm86_struct __user *vm86_info; |
diff --git a/arch/x86/include/asm/xsave.h b/arch/x86/include/asm/xsave.h index ddc04ccad03b..2c4390cae228 100644 --- a/arch/x86/include/asm/xsave.h +++ b/arch/x86/include/asm/xsave.h | |||
@@ -37,8 +37,9 @@ extern int check_for_xstate(struct i387_fxsave_struct __user *buf, | |||
37 | void __user *fpstate, | 37 | void __user *fpstate, |
38 | struct _fpx_sw_bytes *sw); | 38 | struct _fpx_sw_bytes *sw); |
39 | 39 | ||
40 | static inline int xrstor_checking(struct xsave_struct *fx) | 40 | static inline int fpu_xrstor_checking(struct fpu *fpu) |
41 | { | 41 | { |
42 | struct xsave_struct *fx = &fpu->state->xsave; | ||
42 | int err; | 43 | int err; |
43 | 44 | ||
44 | asm volatile("1: .byte " REX_PREFIX "0x0f,0xae,0x2f\n\t" | 45 | asm volatile("1: .byte " REX_PREFIX "0x0f,0xae,0x2f\n\t" |
@@ -110,12 +111,12 @@ static inline void xrstor_state(struct xsave_struct *fx, u64 mask) | |||
110 | : "memory"); | 111 | : "memory"); |
111 | } | 112 | } |
112 | 113 | ||
113 | static inline void xsave(struct task_struct *tsk) | 114 | static inline void fpu_xsave(struct fpu *fpu) |
114 | { | 115 | { |
115 | /* This, however, we can work around by forcing the compiler to select | 116 | /* This, however, we can work around by forcing the compiler to select |
116 | an addressing mode that doesn't require extended registers. */ | 117 | an addressing mode that doesn't require extended registers. */ |
117 | __asm__ __volatile__(".byte " REX_PREFIX "0x0f,0xae,0x27" | 118 | __asm__ __volatile__(".byte " REX_PREFIX "0x0f,0xae,0x27" |
118 | : : "D" (&(tsk->thread.xstate->xsave)), | 119 | : : "D" (&(fpu->state->xsave)), |
119 | "a" (-1), "d"(-1) : "memory"); | 120 | "a" (-1), "d"(-1) : "memory"); |
120 | } | 121 | } |
121 | #endif | 122 | #endif |