diff options
Diffstat (limited to 'arch/x86/kernel/i387.c')
-rw-r--r-- | arch/x86/kernel/i387.c | 88 |
1 files changed, 46 insertions, 42 deletions
diff --git a/arch/x86/kernel/i387.c b/arch/x86/kernel/i387.c index d2e39e69aaf8..8f8102d967b3 100644 --- a/arch/x86/kernel/i387.c +++ b/arch/x86/kernel/i387.c | |||
@@ -5,45 +5,41 @@ | |||
5 | * General FPU state handling cleanups | 5 | * General FPU state handling cleanups |
6 | * Gareth Hughes <gareth@valinux.com>, May 2000 | 6 | * Gareth Hughes <gareth@valinux.com>, May 2000 |
7 | */ | 7 | */ |
8 | |||
9 | #include <linux/sched.h> | ||
10 | #include <linux/module.h> | 8 | #include <linux/module.h> |
11 | #include <linux/regset.h> | 9 | #include <linux/regset.h> |
10 | #include <linux/sched.h> | ||
11 | |||
12 | #include <asm/sigcontext.h> | ||
12 | #include <asm/processor.h> | 13 | #include <asm/processor.h> |
13 | #include <asm/i387.h> | ||
14 | #include <asm/math_emu.h> | 14 | #include <asm/math_emu.h> |
15 | #include <asm/sigcontext.h> | ||
16 | #include <asm/user.h> | ||
17 | #include <asm/ptrace.h> | ||
18 | #include <asm/uaccess.h> | 15 | #include <asm/uaccess.h> |
16 | #include <asm/ptrace.h> | ||
17 | #include <asm/i387.h> | ||
18 | #include <asm/user.h> | ||
19 | 19 | ||
20 | #ifdef CONFIG_X86_64 | 20 | #ifdef CONFIG_X86_64 |
21 | 21 | # include <asm/sigcontext32.h> | |
22 | #include <asm/sigcontext32.h> | 22 | # include <asm/user32.h> |
23 | #include <asm/user32.h> | ||
24 | |||
25 | #else | 23 | #else |
26 | 24 | # define save_i387_ia32 save_i387 | |
27 | #define save_i387_ia32 save_i387 | 25 | # define restore_i387_ia32 restore_i387 |
28 | #define restore_i387_ia32 restore_i387 | 26 | # define _fpstate_ia32 _fpstate |
29 | 27 | # define user_i387_ia32_struct user_i387_struct | |
30 | #define _fpstate_ia32 _fpstate | 28 | # define user32_fxsr_struct user_fxsr_struct |
31 | #define user_i387_ia32_struct user_i387_struct | ||
32 | #define user32_fxsr_struct user_fxsr_struct | ||
33 | |||
34 | #endif | 29 | #endif |
35 | 30 | ||
36 | #ifdef CONFIG_MATH_EMULATION | 31 | #ifdef CONFIG_MATH_EMULATION |
37 | #define HAVE_HWFP (boot_cpu_data.hard_math) | 32 | # define HAVE_HWFP (boot_cpu_data.hard_math) |
38 | #else | 33 | #else |
39 | #define HAVE_HWFP 1 | 34 | # define HAVE_HWFP 1 |
40 | #endif | 35 | #endif |
41 | 36 | ||
42 | static unsigned int mxcsr_feature_mask __read_mostly = 0xffffffffu; | 37 | static unsigned int mxcsr_feature_mask __read_mostly = 0xffffffffu; |
43 | 38 | ||
44 | void mxcsr_feature_mask_init(void) | 39 | void mxcsr_feature_mask_init(void) |
45 | { | 40 | { |
46 | unsigned long mask = 0; | 41 | unsigned long mask = 0; |
42 | |||
47 | clts(); | 43 | clts(); |
48 | if (cpu_has_fxsr) { | 44 | if (cpu_has_fxsr) { |
49 | memset(¤t->thread.i387.fxsave, 0, | 45 | memset(¤t->thread.i387.fxsave, 0, |
@@ -69,10 +65,11 @@ void __cpuinit fpu_init(void) | |||
69 | 65 | ||
70 | if (offsetof(struct task_struct, thread.i387.fxsave) & 15) | 66 | if (offsetof(struct task_struct, thread.i387.fxsave) & 15) |
71 | __bad_fxsave_alignment(); | 67 | __bad_fxsave_alignment(); |
68 | |||
72 | set_in_cr4(X86_CR4_OSFXSR); | 69 | set_in_cr4(X86_CR4_OSFXSR); |
73 | set_in_cr4(X86_CR4_OSXMMEXCPT); | 70 | set_in_cr4(X86_CR4_OSXMMEXCPT); |
74 | 71 | ||
75 | write_cr0(oldcr0 & ~((1UL<<3)|(1UL<<2))); /* clear TS and EM */ | 72 | write_cr0(oldcr0 & ~(X86_CR0_TS|X86_CR0_EM)); /* clear TS and EM */ |
76 | 73 | ||
77 | mxcsr_feature_mask_init(); | 74 | mxcsr_feature_mask_init(); |
78 | /* clean state in init */ | 75 | /* clean state in init */ |
@@ -178,6 +175,7 @@ static inline unsigned short twd_i387_to_fxsr(unsigned short twd) | |||
178 | tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */ | 175 | tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */ |
179 | tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */ | 176 | tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */ |
180 | tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */ | 177 | tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */ |
178 | |||
181 | return tmp; | 179 | return tmp; |
182 | } | 180 | } |
183 | 181 | ||
@@ -232,8 +230,8 @@ static inline u32 twd_fxsr_to_i387(struct i387_fxsave_struct *fxsave) | |||
232 | * FXSR floating point environment conversions. | 230 | * FXSR floating point environment conversions. |
233 | */ | 231 | */ |
234 | 232 | ||
235 | static void convert_from_fxsr(struct user_i387_ia32_struct *env, | 233 | static void |
236 | struct task_struct *tsk) | 234 | convert_from_fxsr(struct user_i387_ia32_struct *env, struct task_struct *tsk) |
237 | { | 235 | { |
238 | struct i387_fxsave_struct *fxsave = &tsk->thread.i387.fxsave; | 236 | struct i387_fxsave_struct *fxsave = &tsk->thread.i387.fxsave; |
239 | struct _fpreg *to = (struct _fpreg *) &env->st_space[0]; | 237 | struct _fpreg *to = (struct _fpreg *) &env->st_space[0]; |
@@ -252,10 +250,11 @@ static void convert_from_fxsr(struct user_i387_ia32_struct *env, | |||
252 | * should be actually ds/cs at fpu exception time, but | 250 | * should be actually ds/cs at fpu exception time, but |
253 | * that information is not available in 64bit mode. | 251 | * that information is not available in 64bit mode. |
254 | */ | 252 | */ |
255 | asm("mov %%ds,%0" : "=r" (env->fos)); | 253 | asm("mov %%ds, %[fos]" : [fos] "=r" (env->fos)); |
256 | asm("mov %%cs,%0" : "=r" (env->fcs)); | 254 | asm("mov %%cs, %[fcs]" : [fcs] "=r" (env->fcs)); |
257 | } else { | 255 | } else { |
258 | struct pt_regs *regs = task_pt_regs(tsk); | 256 | struct pt_regs *regs = task_pt_regs(tsk); |
257 | |||
259 | env->fos = 0xffff0000 | tsk->thread.ds; | 258 | env->fos = 0xffff0000 | tsk->thread.ds; |
260 | env->fcs = regs->cs; | 259 | env->fcs = regs->cs; |
261 | } | 260 | } |
@@ -309,9 +308,10 @@ int fpregs_get(struct task_struct *target, const struct user_regset *regset, | |||
309 | 308 | ||
310 | init_fpu(target); | 309 | init_fpu(target); |
311 | 310 | ||
312 | if (!cpu_has_fxsr) | 311 | if (!cpu_has_fxsr) { |
313 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, | 312 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, |
314 | &target->thread.i387.fsave, 0, -1); | 313 | &target->thread.i387.fsave, 0, -1); |
314 | } | ||
315 | 315 | ||
316 | if (kbuf && pos == 0 && count == sizeof(env)) { | 316 | if (kbuf && pos == 0 && count == sizeof(env)) { |
317 | convert_from_fxsr(kbuf, target); | 317 | convert_from_fxsr(kbuf, target); |
@@ -319,6 +319,7 @@ int fpregs_get(struct task_struct *target, const struct user_regset *regset, | |||
319 | } | 319 | } |
320 | 320 | ||
321 | convert_from_fxsr(&env, target); | 321 | convert_from_fxsr(&env, target); |
322 | |||
322 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, &env, 0, -1); | 323 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, &env, 0, -1); |
323 | } | 324 | } |
324 | 325 | ||
@@ -335,9 +336,10 @@ int fpregs_set(struct task_struct *target, const struct user_regset *regset, | |||
335 | init_fpu(target); | 336 | init_fpu(target); |
336 | set_stopped_child_used_math(target); | 337 | set_stopped_child_used_math(target); |
337 | 338 | ||
338 | if (!cpu_has_fxsr) | 339 | if (!cpu_has_fxsr) { |
339 | return user_regset_copyin(&pos, &count, &kbuf, &ubuf, | 340 | return user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
340 | &target->thread.i387.fsave, 0, -1); | 341 | &target->thread.i387.fsave, 0, -1); |
342 | } | ||
341 | 343 | ||
342 | if (pos > 0 || count < sizeof(env)) | 344 | if (pos > 0 || count < sizeof(env)) |
343 | convert_from_fxsr(&env, target); | 345 | convert_from_fxsr(&env, target); |
@@ -392,28 +394,28 @@ int save_i387_ia32(struct _fpstate_ia32 __user *buf) | |||
392 | { | 394 | { |
393 | if (!used_math()) | 395 | if (!used_math()) |
394 | return 0; | 396 | return 0; |
395 | 397 | /* | |
396 | /* This will cause a "finit" to be triggered by the next | 398 | * This will cause a "finit" to be triggered by the next |
397 | * attempted FPU operation by the 'current' process. | 399 | * attempted FPU operation by the 'current' process. |
398 | */ | 400 | */ |
399 | clear_used_math(); | 401 | clear_used_math(); |
400 | 402 | ||
401 | if (HAVE_HWFP) { | 403 | if (!HAVE_HWFP) { |
402 | if (cpu_has_fxsr) { | ||
403 | return save_i387_fxsave(buf); | ||
404 | } else { | ||
405 | return save_i387_fsave(buf); | ||
406 | } | ||
407 | } else { | ||
408 | return fpregs_soft_get(current, NULL, | 404 | return fpregs_soft_get(current, NULL, |
409 | 0, sizeof(struct user_i387_ia32_struct), | 405 | 0, sizeof(struct user_i387_ia32_struct), |
410 | NULL, buf) ? -1 : 1; | 406 | NULL, buf) ? -1 : 1; |
411 | } | 407 | } |
408 | |||
409 | if (cpu_has_fxsr) | ||
410 | return save_i387_fxsave(buf); | ||
411 | else | ||
412 | return save_i387_fsave(buf); | ||
412 | } | 413 | } |
413 | 414 | ||
414 | static inline int restore_i387_fsave(struct _fpstate_ia32 __user *buf) | 415 | static inline int restore_i387_fsave(struct _fpstate_ia32 __user *buf) |
415 | { | 416 | { |
416 | struct task_struct *tsk = current; | 417 | struct task_struct *tsk = current; |
418 | |||
417 | clear_fpu(tsk); | 419 | clear_fpu(tsk); |
418 | return __copy_from_user(&tsk->thread.i387.fsave, buf, | 420 | return __copy_from_user(&tsk->thread.i387.fsave, buf, |
419 | sizeof(struct i387_fsave_struct)); | 421 | sizeof(struct i387_fsave_struct)); |
@@ -421,9 +423,10 @@ static inline int restore_i387_fsave(struct _fpstate_ia32 __user *buf) | |||
421 | 423 | ||
422 | static int restore_i387_fxsave(struct _fpstate_ia32 __user *buf) | 424 | static int restore_i387_fxsave(struct _fpstate_ia32 __user *buf) |
423 | { | 425 | { |
424 | int err; | ||
425 | struct task_struct *tsk = current; | 426 | struct task_struct *tsk = current; |
426 | struct user_i387_ia32_struct env; | 427 | struct user_i387_ia32_struct env; |
428 | int err; | ||
429 | |||
427 | clear_fpu(tsk); | 430 | clear_fpu(tsk); |
428 | err = __copy_from_user(&tsk->thread.i387.fxsave, &buf->_fxsr_env[0], | 431 | err = __copy_from_user(&tsk->thread.i387.fxsave, &buf->_fxsr_env[0], |
429 | sizeof(struct i387_fxsave_struct)); | 432 | sizeof(struct i387_fxsave_struct)); |
@@ -432,6 +435,7 @@ static int restore_i387_fxsave(struct _fpstate_ia32 __user *buf) | |||
432 | if (err || __copy_from_user(&env, buf, sizeof(env))) | 435 | if (err || __copy_from_user(&env, buf, sizeof(env))) |
433 | return 1; | 436 | return 1; |
434 | convert_to_fxsr(tsk, &env); | 437 | convert_to_fxsr(tsk, &env); |
438 | |||
435 | return 0; | 439 | return 0; |
436 | } | 440 | } |
437 | 441 | ||
@@ -440,17 +444,17 @@ int restore_i387_ia32(struct _fpstate_ia32 __user *buf) | |||
440 | int err; | 444 | int err; |
441 | 445 | ||
442 | if (HAVE_HWFP) { | 446 | if (HAVE_HWFP) { |
443 | if (cpu_has_fxsr) { | 447 | if (cpu_has_fxsr) |
444 | err = restore_i387_fxsave(buf); | 448 | err = restore_i387_fxsave(buf); |
445 | } else { | 449 | else |
446 | err = restore_i387_fsave(buf); | 450 | err = restore_i387_fsave(buf); |
447 | } | ||
448 | } else { | 451 | } else { |
449 | err = fpregs_soft_set(current, NULL, | 452 | err = fpregs_soft_set(current, NULL, |
450 | 0, sizeof(struct user_i387_ia32_struct), | 453 | 0, sizeof(struct user_i387_ia32_struct), |
451 | NULL, buf) != 0; | 454 | NULL, buf) != 0; |
452 | } | 455 | } |
453 | set_used_math(); | 456 | set_used_math(); |
457 | |||
454 | return err; | 458 | return err; |
455 | } | 459 | } |
456 | 460 | ||
@@ -463,8 +467,8 @@ int restore_i387_ia32(struct _fpstate_ia32 __user *buf) | |||
463 | */ | 467 | */ |
464 | int dump_fpu(struct pt_regs *regs, struct user_i387_struct *fpu) | 468 | int dump_fpu(struct pt_regs *regs, struct user_i387_struct *fpu) |
465 | { | 469 | { |
466 | int fpvalid; | ||
467 | struct task_struct *tsk = current; | 470 | struct task_struct *tsk = current; |
471 | int fpvalid; | ||
468 | 472 | ||
469 | fpvalid = !!used_math(); | 473 | fpvalid = !!used_math(); |
470 | if (fpvalid) | 474 | if (fpvalid) |