diff options
Diffstat (limited to 'arch/x86/kernel/i387.c')
-rw-r--r-- | arch/x86/kernel/i387.c | 114 |
1 files changed, 74 insertions, 40 deletions
diff --git a/arch/x86/kernel/i387.c b/arch/x86/kernel/i387.c index 8f8102d967b3..db6839b53195 100644 --- a/arch/x86/kernel/i387.c +++ b/arch/x86/kernel/i387.c | |||
@@ -35,17 +35,18 @@ | |||
35 | #endif | 35 | #endif |
36 | 36 | ||
37 | static unsigned int mxcsr_feature_mask __read_mostly = 0xffffffffu; | 37 | static unsigned int mxcsr_feature_mask __read_mostly = 0xffffffffu; |
38 | unsigned int xstate_size; | ||
39 | static struct i387_fxsave_struct fx_scratch __cpuinitdata; | ||
38 | 40 | ||
39 | void mxcsr_feature_mask_init(void) | 41 | void __cpuinit mxcsr_feature_mask_init(void) |
40 | { | 42 | { |
41 | unsigned long mask = 0; | 43 | unsigned long mask = 0; |
42 | 44 | ||
43 | clts(); | 45 | clts(); |
44 | if (cpu_has_fxsr) { | 46 | if (cpu_has_fxsr) { |
45 | memset(¤t->thread.i387.fxsave, 0, | 47 | memset(&fx_scratch, 0, sizeof(struct i387_fxsave_struct)); |
46 | sizeof(struct i387_fxsave_struct)); | 48 | asm volatile("fxsave %0" : : "m" (fx_scratch)); |
47 | asm volatile("fxsave %0" : : "m" (current->thread.i387.fxsave)); | 49 | mask = fx_scratch.mxcsr_mask; |
48 | mask = current->thread.i387.fxsave.mxcsr_mask; | ||
49 | if (mask == 0) | 50 | if (mask == 0) |
50 | mask = 0x0000ffbf; | 51 | mask = 0x0000ffbf; |
51 | } | 52 | } |
@@ -53,6 +54,16 @@ void mxcsr_feature_mask_init(void) | |||
53 | stts(); | 54 | stts(); |
54 | } | 55 | } |
55 | 56 | ||
57 | void __init init_thread_xstate(void) | ||
58 | { | ||
59 | if (cpu_has_fxsr) | ||
60 | xstate_size = sizeof(struct i387_fxsave_struct); | ||
61 | #ifdef CONFIG_X86_32 | ||
62 | else | ||
63 | xstate_size = sizeof(struct i387_fsave_struct); | ||
64 | #endif | ||
65 | } | ||
66 | |||
56 | #ifdef CONFIG_X86_64 | 67 | #ifdef CONFIG_X86_64 |
57 | /* | 68 | /* |
58 | * Called at bootup to set up the initial FPU state that is later cloned | 69 | * Called at bootup to set up the initial FPU state that is later cloned |
@@ -61,10 +72,6 @@ void mxcsr_feature_mask_init(void) | |||
61 | void __cpuinit fpu_init(void) | 72 | void __cpuinit fpu_init(void) |
62 | { | 73 | { |
63 | unsigned long oldcr0 = read_cr0(); | 74 | unsigned long oldcr0 = read_cr0(); |
64 | extern void __bad_fxsave_alignment(void); | ||
65 | |||
66 | if (offsetof(struct task_struct, thread.i387.fxsave) & 15) | ||
67 | __bad_fxsave_alignment(); | ||
68 | 75 | ||
69 | set_in_cr4(X86_CR4_OSFXSR); | 76 | set_in_cr4(X86_CR4_OSFXSR); |
70 | set_in_cr4(X86_CR4_OSXMMEXCPT); | 77 | set_in_cr4(X86_CR4_OSXMMEXCPT); |
@@ -84,32 +91,44 @@ void __cpuinit fpu_init(void) | |||
84 | * value at reset if we support XMM instructions and then | 91 | * value at reset if we support XMM instructions and then |
85 | * remeber the current task has used the FPU. | 92 | * remeber the current task has used the FPU. |
86 | */ | 93 | */ |
87 | void init_fpu(struct task_struct *tsk) | 94 | int init_fpu(struct task_struct *tsk) |
88 | { | 95 | { |
89 | if (tsk_used_math(tsk)) { | 96 | if (tsk_used_math(tsk)) { |
90 | if (tsk == current) | 97 | if (tsk == current) |
91 | unlazy_fpu(tsk); | 98 | unlazy_fpu(tsk); |
92 | return; | 99 | return 0; |
100 | } | ||
101 | |||
102 | /* | ||
103 | * Memory allocation at the first usage of the FPU and other state. | ||
104 | */ | ||
105 | if (!tsk->thread.xstate) { | ||
106 | tsk->thread.xstate = kmem_cache_alloc(task_xstate_cachep, | ||
107 | GFP_KERNEL); | ||
108 | if (!tsk->thread.xstate) | ||
109 | return -ENOMEM; | ||
93 | } | 110 | } |
94 | 111 | ||
95 | if (cpu_has_fxsr) { | 112 | if (cpu_has_fxsr) { |
96 | memset(&tsk->thread.i387.fxsave, 0, | 113 | struct i387_fxsave_struct *fx = &tsk->thread.xstate->fxsave; |
97 | sizeof(struct i387_fxsave_struct)); | 114 | |
98 | tsk->thread.i387.fxsave.cwd = 0x37f; | 115 | memset(fx, 0, xstate_size); |
116 | fx->cwd = 0x37f; | ||
99 | if (cpu_has_xmm) | 117 | if (cpu_has_xmm) |
100 | tsk->thread.i387.fxsave.mxcsr = MXCSR_DEFAULT; | 118 | fx->mxcsr = MXCSR_DEFAULT; |
101 | } else { | 119 | } else { |
102 | memset(&tsk->thread.i387.fsave, 0, | 120 | struct i387_fsave_struct *fp = &tsk->thread.xstate->fsave; |
103 | sizeof(struct i387_fsave_struct)); | 121 | memset(fp, 0, xstate_size); |
104 | tsk->thread.i387.fsave.cwd = 0xffff037fu; | 122 | fp->cwd = 0xffff037fu; |
105 | tsk->thread.i387.fsave.swd = 0xffff0000u; | 123 | fp->swd = 0xffff0000u; |
106 | tsk->thread.i387.fsave.twd = 0xffffffffu; | 124 | fp->twd = 0xffffffffu; |
107 | tsk->thread.i387.fsave.fos = 0xffff0000u; | 125 | fp->fos = 0xffff0000u; |
108 | } | 126 | } |
109 | /* | 127 | /* |
110 | * Only the device not available exception or ptrace can call init_fpu. | 128 | * Only the device not available exception or ptrace can call init_fpu. |
111 | */ | 129 | */ |
112 | set_stopped_child_used_math(tsk); | 130 | set_stopped_child_used_math(tsk); |
131 | return 0; | ||
113 | } | 132 | } |
114 | 133 | ||
115 | int fpregs_active(struct task_struct *target, const struct user_regset *regset) | 134 | int fpregs_active(struct task_struct *target, const struct user_regset *regset) |
@@ -126,13 +145,17 @@ int xfpregs_get(struct task_struct *target, const struct user_regset *regset, | |||
126 | unsigned int pos, unsigned int count, | 145 | unsigned int pos, unsigned int count, |
127 | void *kbuf, void __user *ubuf) | 146 | void *kbuf, void __user *ubuf) |
128 | { | 147 | { |
148 | int ret; | ||
149 | |||
129 | if (!cpu_has_fxsr) | 150 | if (!cpu_has_fxsr) |
130 | return -ENODEV; | 151 | return -ENODEV; |
131 | 152 | ||
132 | init_fpu(target); | 153 | ret = init_fpu(target); |
154 | if (ret) | ||
155 | return ret; | ||
133 | 156 | ||
134 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, | 157 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, |
135 | &target->thread.i387.fxsave, 0, -1); | 158 | &target->thread.xstate->fxsave, 0, -1); |
136 | } | 159 | } |
137 | 160 | ||
138 | int xfpregs_set(struct task_struct *target, const struct user_regset *regset, | 161 | int xfpregs_set(struct task_struct *target, const struct user_regset *regset, |
@@ -144,16 +167,19 @@ int xfpregs_set(struct task_struct *target, const struct user_regset *regset, | |||
144 | if (!cpu_has_fxsr) | 167 | if (!cpu_has_fxsr) |
145 | return -ENODEV; | 168 | return -ENODEV; |
146 | 169 | ||
147 | init_fpu(target); | 170 | ret = init_fpu(target); |
171 | if (ret) | ||
172 | return ret; | ||
173 | |||
148 | set_stopped_child_used_math(target); | 174 | set_stopped_child_used_math(target); |
149 | 175 | ||
150 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | 176 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
151 | &target->thread.i387.fxsave, 0, -1); | 177 | &target->thread.xstate->fxsave, 0, -1); |
152 | 178 | ||
153 | /* | 179 | /* |
154 | * mxcsr reserved bits must be masked to zero for security reasons. | 180 | * mxcsr reserved bits must be masked to zero for security reasons. |
155 | */ | 181 | */ |
156 | target->thread.i387.fxsave.mxcsr &= mxcsr_feature_mask; | 182 | target->thread.xstate->fxsave.mxcsr &= mxcsr_feature_mask; |
157 | 183 | ||
158 | return ret; | 184 | return ret; |
159 | } | 185 | } |
@@ -233,7 +259,7 @@ static inline u32 twd_fxsr_to_i387(struct i387_fxsave_struct *fxsave) | |||
233 | static void | 259 | static void |
234 | convert_from_fxsr(struct user_i387_ia32_struct *env, struct task_struct *tsk) | 260 | convert_from_fxsr(struct user_i387_ia32_struct *env, struct task_struct *tsk) |
235 | { | 261 | { |
236 | struct i387_fxsave_struct *fxsave = &tsk->thread.i387.fxsave; | 262 | struct i387_fxsave_struct *fxsave = &tsk->thread.xstate->fxsave; |
237 | struct _fpreg *to = (struct _fpreg *) &env->st_space[0]; | 263 | struct _fpreg *to = (struct _fpreg *) &env->st_space[0]; |
238 | struct _fpxreg *from = (struct _fpxreg *) &fxsave->st_space[0]; | 264 | struct _fpxreg *from = (struct _fpxreg *) &fxsave->st_space[0]; |
239 | int i; | 265 | int i; |
@@ -273,7 +299,7 @@ static void convert_to_fxsr(struct task_struct *tsk, | |||
273 | const struct user_i387_ia32_struct *env) | 299 | const struct user_i387_ia32_struct *env) |
274 | 300 | ||
275 | { | 301 | { |
276 | struct i387_fxsave_struct *fxsave = &tsk->thread.i387.fxsave; | 302 | struct i387_fxsave_struct *fxsave = &tsk->thread.xstate->fxsave; |
277 | struct _fpreg *from = (struct _fpreg *) &env->st_space[0]; | 303 | struct _fpreg *from = (struct _fpreg *) &env->st_space[0]; |
278 | struct _fpxreg *to = (struct _fpxreg *) &fxsave->st_space[0]; | 304 | struct _fpxreg *to = (struct _fpxreg *) &fxsave->st_space[0]; |
279 | int i; | 305 | int i; |
@@ -302,15 +328,19 @@ int fpregs_get(struct task_struct *target, const struct user_regset *regset, | |||
302 | void *kbuf, void __user *ubuf) | 328 | void *kbuf, void __user *ubuf) |
303 | { | 329 | { |
304 | struct user_i387_ia32_struct env; | 330 | struct user_i387_ia32_struct env; |
331 | int ret; | ||
305 | 332 | ||
306 | if (!HAVE_HWFP) | 333 | if (!HAVE_HWFP) |
307 | return fpregs_soft_get(target, regset, pos, count, kbuf, ubuf); | 334 | return fpregs_soft_get(target, regset, pos, count, kbuf, ubuf); |
308 | 335 | ||
309 | init_fpu(target); | 336 | ret = init_fpu(target); |
337 | if (ret) | ||
338 | return ret; | ||
310 | 339 | ||
311 | if (!cpu_has_fxsr) { | 340 | if (!cpu_has_fxsr) { |
312 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, | 341 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, |
313 | &target->thread.i387.fsave, 0, -1); | 342 | &target->thread.xstate->fsave, 0, |
343 | -1); | ||
314 | } | 344 | } |
315 | 345 | ||
316 | if (kbuf && pos == 0 && count == sizeof(env)) { | 346 | if (kbuf && pos == 0 && count == sizeof(env)) { |
@@ -333,12 +363,15 @@ int fpregs_set(struct task_struct *target, const struct user_regset *regset, | |||
333 | if (!HAVE_HWFP) | 363 | if (!HAVE_HWFP) |
334 | return fpregs_soft_set(target, regset, pos, count, kbuf, ubuf); | 364 | return fpregs_soft_set(target, regset, pos, count, kbuf, ubuf); |
335 | 365 | ||
336 | init_fpu(target); | 366 | ret = init_fpu(target); |
367 | if (ret) | ||
368 | return ret; | ||
369 | |||
337 | set_stopped_child_used_math(target); | 370 | set_stopped_child_used_math(target); |
338 | 371 | ||
339 | if (!cpu_has_fxsr) { | 372 | if (!cpu_has_fxsr) { |
340 | return user_regset_copyin(&pos, &count, &kbuf, &ubuf, | 373 | return user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
341 | &target->thread.i387.fsave, 0, -1); | 374 | &target->thread.xstate->fsave, 0, -1); |
342 | } | 375 | } |
343 | 376 | ||
344 | if (pos > 0 || count < sizeof(env)) | 377 | if (pos > 0 || count < sizeof(env)) |
@@ -358,11 +391,11 @@ int fpregs_set(struct task_struct *target, const struct user_regset *regset, | |||
358 | static inline int save_i387_fsave(struct _fpstate_ia32 __user *buf) | 391 | static inline int save_i387_fsave(struct _fpstate_ia32 __user *buf) |
359 | { | 392 | { |
360 | struct task_struct *tsk = current; | 393 | struct task_struct *tsk = current; |
394 | struct i387_fsave_struct *fp = &tsk->thread.xstate->fsave; | ||
361 | 395 | ||
362 | unlazy_fpu(tsk); | 396 | unlazy_fpu(tsk); |
363 | tsk->thread.i387.fsave.status = tsk->thread.i387.fsave.swd; | 397 | fp->status = fp->swd; |
364 | if (__copy_to_user(buf, &tsk->thread.i387.fsave, | 398 | if (__copy_to_user(buf, fp, sizeof(struct i387_fsave_struct))) |
365 | sizeof(struct i387_fsave_struct))) | ||
366 | return -1; | 399 | return -1; |
367 | return 1; | 400 | return 1; |
368 | } | 401 | } |
@@ -370,6 +403,7 @@ static inline int save_i387_fsave(struct _fpstate_ia32 __user *buf) | |||
370 | static int save_i387_fxsave(struct _fpstate_ia32 __user *buf) | 403 | static int save_i387_fxsave(struct _fpstate_ia32 __user *buf) |
371 | { | 404 | { |
372 | struct task_struct *tsk = current; | 405 | struct task_struct *tsk = current; |
406 | struct i387_fxsave_struct *fx = &tsk->thread.xstate->fxsave; | ||
373 | struct user_i387_ia32_struct env; | 407 | struct user_i387_ia32_struct env; |
374 | int err = 0; | 408 | int err = 0; |
375 | 409 | ||
@@ -379,12 +413,12 @@ static int save_i387_fxsave(struct _fpstate_ia32 __user *buf) | |||
379 | if (__copy_to_user(buf, &env, sizeof(env))) | 413 | if (__copy_to_user(buf, &env, sizeof(env))) |
380 | return -1; | 414 | return -1; |
381 | 415 | ||
382 | err |= __put_user(tsk->thread.i387.fxsave.swd, &buf->status); | 416 | err |= __put_user(fx->swd, &buf->status); |
383 | err |= __put_user(X86_FXSR_MAGIC, &buf->magic); | 417 | err |= __put_user(X86_FXSR_MAGIC, &buf->magic); |
384 | if (err) | 418 | if (err) |
385 | return -1; | 419 | return -1; |
386 | 420 | ||
387 | if (__copy_to_user(&buf->_fxsr_env[0], &tsk->thread.i387.fxsave, | 421 | if (__copy_to_user(&buf->_fxsr_env[0], fx, |
388 | sizeof(struct i387_fxsave_struct))) | 422 | sizeof(struct i387_fxsave_struct))) |
389 | return -1; | 423 | return -1; |
390 | return 1; | 424 | return 1; |
@@ -417,7 +451,7 @@ static inline int restore_i387_fsave(struct _fpstate_ia32 __user *buf) | |||
417 | struct task_struct *tsk = current; | 451 | struct task_struct *tsk = current; |
418 | 452 | ||
419 | clear_fpu(tsk); | 453 | clear_fpu(tsk); |
420 | return __copy_from_user(&tsk->thread.i387.fsave, buf, | 454 | return __copy_from_user(&tsk->thread.xstate->fsave, buf, |
421 | sizeof(struct i387_fsave_struct)); | 455 | sizeof(struct i387_fsave_struct)); |
422 | } | 456 | } |
423 | 457 | ||
@@ -428,10 +462,10 @@ static int restore_i387_fxsave(struct _fpstate_ia32 __user *buf) | |||
428 | int err; | 462 | int err; |
429 | 463 | ||
430 | clear_fpu(tsk); | 464 | clear_fpu(tsk); |
431 | err = __copy_from_user(&tsk->thread.i387.fxsave, &buf->_fxsr_env[0], | 465 | err = __copy_from_user(&tsk->thread.xstate->fxsave, &buf->_fxsr_env[0], |
432 | sizeof(struct i387_fxsave_struct)); | 466 | sizeof(struct i387_fxsave_struct)); |
433 | /* mxcsr reserved bits must be masked to zero for security reasons */ | 467 | /* mxcsr reserved bits must be masked to zero for security reasons */ |
434 | tsk->thread.i387.fxsave.mxcsr &= mxcsr_feature_mask; | 468 | tsk->thread.xstate->fxsave.mxcsr &= mxcsr_feature_mask; |
435 | if (err || __copy_from_user(&env, buf, sizeof(env))) | 469 | if (err || __copy_from_user(&env, buf, sizeof(env))) |
436 | return 1; | 470 | return 1; |
437 | convert_to_fxsr(tsk, &env); | 471 | convert_to_fxsr(tsk, &env); |