diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-07-02 19:26:44 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-07-02 19:26:44 -0400 |
commit | d652df0b2f0b9c2e655be0d6f90989fae0a511b4 (patch) | |
tree | fce939bb45c3d656798d91f7a6dd9d3fbf7189ca | |
parent | 4d6f843a38fa26035598f1f35181aa5f328db897 (diff) | |
parent | 5f8c4218148822fde6eebbeefc34bd0a6061e031 (diff) |
Merge branch 'x86-fpu-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86 FPU changes from Ingo Molnar:
"There are two bigger changes in this tree:
- Add an [early-use-]safe static_cpu_has() variant and other
robustness improvements, including the new X86_DEBUG_STATIC_CPU_HAS
configurable debugging facility, motivated by recent obscure FPU
code bugs, by Borislav Petkov
- Reimplement FPU detection code in C and drop the old asm code, by
Peter Anvin."
* 'x86-fpu-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
x86, fpu: Use static_cpu_has_safe before alternatives
x86: Add a static_cpu_has_safe variant
x86: Sanity-check static_cpu_has usage
x86, cpu: Add a synthetic, always true, cpu feature
x86: Get rid of ->hard_math and all the FPU asm fu
-rw-r--r-- | arch/x86/Kconfig.debug | 10 | ||||
-rw-r--r-- | arch/x86/include/asm/cpufeature.h | 118 | ||||
-rw-r--r-- | arch/x86/include/asm/fpu-internal.h | 4 | ||||
-rw-r--r-- | arch/x86/include/asm/processor.h | 3 | ||||
-rw-r--r-- | arch/x86/kernel/asm-offsets_32.c | 1 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/bugs.c | 21 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/common.c | 19 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/cyrix.c | 2 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/proc.c | 4 | ||||
-rw-r--r-- | arch/x86/kernel/head_32.S | 21 | ||||
-rw-r--r-- | arch/x86/kernel/i387.c | 59 | ||||
-rw-r--r-- | arch/x86/kernel/xsave.c | 5 | ||||
-rw-r--r-- | arch/x86/lguest/boot.c | 2 | ||||
-rw-r--r-- | arch/x86/xen/enlighten.c | 2 |
14 files changed, 201 insertions, 70 deletions
diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug index b6a770132b67..c963881de0d0 100644 --- a/arch/x86/Kconfig.debug +++ b/arch/x86/Kconfig.debug | |||
@@ -303,4 +303,14 @@ config DEBUG_NMI_SELFTEST | |||
303 | 303 | ||
304 | If unsure, say N. | 304 | If unsure, say N. |
305 | 305 | ||
306 | config X86_DEBUG_STATIC_CPU_HAS | ||
307 | bool "Debug alternatives" | ||
308 | depends on DEBUG_KERNEL | ||
309 | ---help--- | ||
310 | This option causes additional code to be generated which | ||
311 | fails if static_cpu_has() is used before alternatives have | ||
312 | run. | ||
313 | |||
314 | If unsure, say N. | ||
315 | |||
306 | endmenu | 316 | endmenu |
diff --git a/arch/x86/include/asm/cpufeature.h b/arch/x86/include/asm/cpufeature.h index e99ac27f95b2..47538a61c91b 100644 --- a/arch/x86/include/asm/cpufeature.h +++ b/arch/x86/include/asm/cpufeature.h | |||
@@ -92,7 +92,7 @@ | |||
92 | #define X86_FEATURE_LFENCE_RDTSC (3*32+18) /* "" Lfence synchronizes RDTSC */ | 92 | #define X86_FEATURE_LFENCE_RDTSC (3*32+18) /* "" Lfence synchronizes RDTSC */ |
93 | #define X86_FEATURE_11AP (3*32+19) /* "" Bad local APIC aka 11AP */ | 93 | #define X86_FEATURE_11AP (3*32+19) /* "" Bad local APIC aka 11AP */ |
94 | #define X86_FEATURE_NOPL (3*32+20) /* The NOPL (0F 1F) instructions */ | 94 | #define X86_FEATURE_NOPL (3*32+20) /* The NOPL (0F 1F) instructions */ |
95 | /* 21 available, was AMD_C1E */ | 95 | #define X86_FEATURE_ALWAYS (3*32+21) /* "" Always-present feature */ |
96 | #define X86_FEATURE_XTOPOLOGY (3*32+22) /* cpu topology enum extensions */ | 96 | #define X86_FEATURE_XTOPOLOGY (3*32+22) /* cpu topology enum extensions */ |
97 | #define X86_FEATURE_TSC_RELIABLE (3*32+23) /* TSC is known to be reliable */ | 97 | #define X86_FEATURE_TSC_RELIABLE (3*32+23) /* TSC is known to be reliable */ |
98 | #define X86_FEATURE_NONSTOP_TSC (3*32+24) /* TSC does not stop in C states */ | 98 | #define X86_FEATURE_NONSTOP_TSC (3*32+24) /* TSC does not stop in C states */ |
@@ -356,15 +356,36 @@ extern const char * const x86_power_flags[32]; | |||
356 | #endif /* CONFIG_X86_64 */ | 356 | #endif /* CONFIG_X86_64 */ |
357 | 357 | ||
358 | #if __GNUC__ >= 4 | 358 | #if __GNUC__ >= 4 |
359 | extern void warn_pre_alternatives(void); | ||
360 | extern bool __static_cpu_has_safe(u16 bit); | ||
361 | |||
359 | /* | 362 | /* |
360 | * Static testing of CPU features. Used the same as boot_cpu_has(). | 363 | * Static testing of CPU features. Used the same as boot_cpu_has(). |
361 | * These are only valid after alternatives have run, but will statically | 364 | * These are only valid after alternatives have run, but will statically |
362 | * patch the target code for additional performance. | 365 | * patch the target code for additional performance. |
363 | * | ||
364 | */ | 366 | */ |
365 | static __always_inline __pure bool __static_cpu_has(u16 bit) | 367 | static __always_inline __pure bool __static_cpu_has(u16 bit) |
366 | { | 368 | { |
367 | #if __GNUC__ > 4 || __GNUC_MINOR__ >= 5 | 369 | #if __GNUC__ > 4 || __GNUC_MINOR__ >= 5 |
370 | |||
371 | #ifdef CONFIG_X86_DEBUG_STATIC_CPU_HAS | ||
372 | /* | ||
373 | * Catch too early usage of this before alternatives | ||
374 | * have run. | ||
375 | */ | ||
376 | asm goto("1: jmp %l[t_warn]\n" | ||
377 | "2:\n" | ||
378 | ".section .altinstructions,\"a\"\n" | ||
379 | " .long 1b - .\n" | ||
380 | " .long 0\n" /* no replacement */ | ||
381 | " .word %P0\n" /* 1: do replace */ | ||
382 | " .byte 2b - 1b\n" /* source len */ | ||
383 | " .byte 0\n" /* replacement len */ | ||
384 | ".previous\n" | ||
385 | /* skipping size check since replacement size = 0 */ | ||
386 | : : "i" (X86_FEATURE_ALWAYS) : : t_warn); | ||
387 | #endif | ||
388 | |||
368 | asm goto("1: jmp %l[t_no]\n" | 389 | asm goto("1: jmp %l[t_no]\n" |
369 | "2:\n" | 390 | "2:\n" |
370 | ".section .altinstructions,\"a\"\n" | 391 | ".section .altinstructions,\"a\"\n" |
@@ -379,7 +400,13 @@ static __always_inline __pure bool __static_cpu_has(u16 bit) | |||
379 | return true; | 400 | return true; |
380 | t_no: | 401 | t_no: |
381 | return false; | 402 | return false; |
382 | #else | 403 | |
404 | #ifdef CONFIG_X86_DEBUG_STATIC_CPU_HAS | ||
405 | t_warn: | ||
406 | warn_pre_alternatives(); | ||
407 | return false; | ||
408 | #endif | ||
409 | #else /* GCC_VERSION >= 40500 */ | ||
383 | u8 flag; | 410 | u8 flag; |
384 | /* Open-coded due to __stringify() in ALTERNATIVE() */ | 411 | /* Open-coded due to __stringify() in ALTERNATIVE() */ |
385 | asm volatile("1: movb $0,%0\n" | 412 | asm volatile("1: movb $0,%0\n" |
@@ -411,11 +438,94 @@ static __always_inline __pure bool __static_cpu_has(u16 bit) | |||
411 | __static_cpu_has(bit) : \ | 438 | __static_cpu_has(bit) : \ |
412 | boot_cpu_has(bit) \ | 439 | boot_cpu_has(bit) \ |
413 | ) | 440 | ) |
441 | |||
442 | static __always_inline __pure bool _static_cpu_has_safe(u16 bit) | ||
443 | { | ||
444 | #if __GNUC__ > 4 || __GNUC_MINOR__ >= 5 | ||
445 | /* | ||
446 | * We need to spell the jumps to the compiler because, depending on the offset, | ||
447 | * the replacement jump can be bigger than the original jump, and this we cannot | ||
448 | * have. Thus, we force the jump to the widest, 4-byte, signed relative | ||
449 | * offset even though the last would often fit in less bytes. | ||
450 | */ | ||
451 | asm goto("1: .byte 0xe9\n .long %l[t_dynamic] - 2f\n" | ||
452 | "2:\n" | ||
453 | ".section .altinstructions,\"a\"\n" | ||
454 | " .long 1b - .\n" /* src offset */ | ||
455 | " .long 3f - .\n" /* repl offset */ | ||
456 | " .word %P1\n" /* always replace */ | ||
457 | " .byte 2b - 1b\n" /* src len */ | ||
458 | " .byte 4f - 3f\n" /* repl len */ | ||
459 | ".previous\n" | ||
460 | ".section .altinstr_replacement,\"ax\"\n" | ||
461 | "3: .byte 0xe9\n .long %l[t_no] - 2b\n" | ||
462 | "4:\n" | ||
463 | ".previous\n" | ||
464 | ".section .altinstructions,\"a\"\n" | ||
465 | " .long 1b - .\n" /* src offset */ | ||
466 | " .long 0\n" /* no replacement */ | ||
467 | " .word %P0\n" /* feature bit */ | ||
468 | " .byte 2b - 1b\n" /* src len */ | ||
469 | " .byte 0\n" /* repl len */ | ||
470 | ".previous\n" | ||
471 | : : "i" (bit), "i" (X86_FEATURE_ALWAYS) | ||
472 | : : t_dynamic, t_no); | ||
473 | return true; | ||
474 | t_no: | ||
475 | return false; | ||
476 | t_dynamic: | ||
477 | return __static_cpu_has_safe(bit); | ||
478 | #else /* GCC_VERSION >= 40500 */ | ||
479 | u8 flag; | ||
480 | /* Open-coded due to __stringify() in ALTERNATIVE() */ | ||
481 | asm volatile("1: movb $2,%0\n" | ||
482 | "2:\n" | ||
483 | ".section .altinstructions,\"a\"\n" | ||
484 | " .long 1b - .\n" /* src offset */ | ||
485 | " .long 3f - .\n" /* repl offset */ | ||
486 | " .word %P2\n" /* always replace */ | ||
487 | " .byte 2b - 1b\n" /* source len */ | ||
488 | " .byte 4f - 3f\n" /* replacement len */ | ||
489 | ".previous\n" | ||
490 | ".section .discard,\"aw\",@progbits\n" | ||
491 | " .byte 0xff + (4f-3f) - (2b-1b)\n" /* size check */ | ||
492 | ".previous\n" | ||
493 | ".section .altinstr_replacement,\"ax\"\n" | ||
494 | "3: movb $0,%0\n" | ||
495 | "4:\n" | ||
496 | ".previous\n" | ||
497 | ".section .altinstructions,\"a\"\n" | ||
498 | " .long 1b - .\n" /* src offset */ | ||
499 | " .long 5f - .\n" /* repl offset */ | ||
500 | " .word %P1\n" /* feature bit */ | ||
501 | " .byte 4b - 3b\n" /* src len */ | ||
502 | " .byte 6f - 5f\n" /* repl len */ | ||
503 | ".previous\n" | ||
504 | ".section .discard,\"aw\",@progbits\n" | ||
505 | " .byte 0xff + (6f-5f) - (4b-3b)\n" /* size check */ | ||
506 | ".previous\n" | ||
507 | ".section .altinstr_replacement,\"ax\"\n" | ||
508 | "5: movb $1,%0\n" | ||
509 | "6:\n" | ||
510 | ".previous\n" | ||
511 | : "=qm" (flag) | ||
512 | : "i" (bit), "i" (X86_FEATURE_ALWAYS)); | ||
513 | return (flag == 2 ? __static_cpu_has_safe(bit) : flag); | ||
514 | #endif | ||
515 | } | ||
516 | |||
517 | #define static_cpu_has_safe(bit) \ | ||
518 | ( \ | ||
519 | __builtin_constant_p(boot_cpu_has(bit)) ? \ | ||
520 | boot_cpu_has(bit) : \ | ||
521 | _static_cpu_has_safe(bit) \ | ||
522 | ) | ||
414 | #else | 523 | #else |
415 | /* | 524 | /* |
416 | * gcc 3.x is too stupid to do the static test; fall back to dynamic. | 525 | * gcc 3.x is too stupid to do the static test; fall back to dynamic. |
417 | */ | 526 | */ |
418 | #define static_cpu_has(bit) boot_cpu_has(bit) | 527 | #define static_cpu_has(bit) boot_cpu_has(bit) |
528 | #define static_cpu_has_safe(bit) boot_cpu_has(bit) | ||
419 | #endif | 529 | #endif |
420 | 530 | ||
421 | #define cpu_has_bug(c, bit) cpu_has(c, (bit)) | 531 | #define cpu_has_bug(c, bit) cpu_has(c, (bit)) |
diff --git a/arch/x86/include/asm/fpu-internal.h b/arch/x86/include/asm/fpu-internal.h index e25cc33ec54d..4d0bda7b11e3 100644 --- a/arch/x86/include/asm/fpu-internal.h +++ b/arch/x86/include/asm/fpu-internal.h | |||
@@ -62,10 +62,8 @@ extern user_regset_set_fn fpregs_set, xfpregs_set, fpregs_soft_set, | |||
62 | #define xstateregs_active fpregs_active | 62 | #define xstateregs_active fpregs_active |
63 | 63 | ||
64 | #ifdef CONFIG_MATH_EMULATION | 64 | #ifdef CONFIG_MATH_EMULATION |
65 | # define HAVE_HWFP (boot_cpu_data.hard_math) | ||
66 | extern void finit_soft_fpu(struct i387_soft_struct *soft); | 65 | extern void finit_soft_fpu(struct i387_soft_struct *soft); |
67 | #else | 66 | #else |
68 | # define HAVE_HWFP 1 | ||
69 | static inline void finit_soft_fpu(struct i387_soft_struct *soft) {} | 67 | static inline void finit_soft_fpu(struct i387_soft_struct *soft) {} |
70 | #endif | 68 | #endif |
71 | 69 | ||
@@ -345,7 +343,7 @@ static inline void __thread_fpu_end(struct task_struct *tsk) | |||
345 | 343 | ||
346 | static inline void __thread_fpu_begin(struct task_struct *tsk) | 344 | static inline void __thread_fpu_begin(struct task_struct *tsk) |
347 | { | 345 | { |
348 | if (!use_eager_fpu()) | 346 | if (!static_cpu_has_safe(X86_FEATURE_EAGER_FPU)) |
349 | clts(); | 347 | clts(); |
350 | __thread_set_has_fpu(tsk); | 348 | __thread_set_has_fpu(tsk); |
351 | } | 349 | } |
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index 5b87d52eed0b..29937c4f6ff8 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h | |||
@@ -89,9 +89,9 @@ struct cpuinfo_x86 { | |||
89 | char wp_works_ok; /* It doesn't on 386's */ | 89 | char wp_works_ok; /* It doesn't on 386's */ |
90 | 90 | ||
91 | /* Problems on some 486Dx4's and old 386's: */ | 91 | /* Problems on some 486Dx4's and old 386's: */ |
92 | char hard_math; | ||
93 | char rfu; | 92 | char rfu; |
94 | char pad0; | 93 | char pad0; |
94 | char pad1; | ||
95 | #else | 95 | #else |
96 | /* Number of 4K pages in DTLB/ITLB combined(in pages): */ | 96 | /* Number of 4K pages in DTLB/ITLB combined(in pages): */ |
97 | int x86_tlbsize; | 97 | int x86_tlbsize; |
@@ -164,6 +164,7 @@ extern const struct seq_operations cpuinfo_op; | |||
164 | #define cache_line_size() (boot_cpu_data.x86_cache_alignment) | 164 | #define cache_line_size() (boot_cpu_data.x86_cache_alignment) |
165 | 165 | ||
166 | extern void cpu_detect(struct cpuinfo_x86 *c); | 166 | extern void cpu_detect(struct cpuinfo_x86 *c); |
167 | extern void __cpuinit fpu_detect(struct cpuinfo_x86 *c); | ||
167 | 168 | ||
168 | extern void early_cpu_init(void); | 169 | extern void early_cpu_init(void); |
169 | extern void identify_boot_cpu(void); | 170 | extern void identify_boot_cpu(void); |
diff --git a/arch/x86/kernel/asm-offsets_32.c b/arch/x86/kernel/asm-offsets_32.c index 0ef4bba2acb7..d67c4be3e8b1 100644 --- a/arch/x86/kernel/asm-offsets_32.c +++ b/arch/x86/kernel/asm-offsets_32.c | |||
@@ -28,7 +28,6 @@ void foo(void) | |||
28 | OFFSET(CPUINFO_x86_vendor, cpuinfo_x86, x86_vendor); | 28 | OFFSET(CPUINFO_x86_vendor, cpuinfo_x86, x86_vendor); |
29 | OFFSET(CPUINFO_x86_model, cpuinfo_x86, x86_model); | 29 | OFFSET(CPUINFO_x86_model, cpuinfo_x86, x86_model); |
30 | OFFSET(CPUINFO_x86_mask, cpuinfo_x86, x86_mask); | 30 | OFFSET(CPUINFO_x86_mask, cpuinfo_x86, x86_mask); |
31 | OFFSET(CPUINFO_hard_math, cpuinfo_x86, hard_math); | ||
32 | OFFSET(CPUINFO_cpuid_level, cpuinfo_x86, cpuid_level); | 31 | OFFSET(CPUINFO_cpuid_level, cpuinfo_x86, cpuid_level); |
33 | OFFSET(CPUINFO_x86_capability, cpuinfo_x86, x86_capability); | 32 | OFFSET(CPUINFO_x86_capability, cpuinfo_x86, x86_capability); |
34 | OFFSET(CPUINFO_x86_vendor_id, cpuinfo_x86, x86_vendor_id); | 33 | OFFSET(CPUINFO_x86_vendor_id, cpuinfo_x86, x86_vendor_id); |
diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c index 4112be9a4659..03445346ee0a 100644 --- a/arch/x86/kernel/cpu/bugs.c +++ b/arch/x86/kernel/cpu/bugs.c | |||
@@ -17,15 +17,6 @@ | |||
17 | #include <asm/paravirt.h> | 17 | #include <asm/paravirt.h> |
18 | #include <asm/alternative.h> | 18 | #include <asm/alternative.h> |
19 | 19 | ||
20 | static int __init no_387(char *s) | ||
21 | { | ||
22 | boot_cpu_data.hard_math = 0; | ||
23 | write_cr0(X86_CR0_TS | X86_CR0_EM | X86_CR0_MP | read_cr0()); | ||
24 | return 1; | ||
25 | } | ||
26 | |||
27 | __setup("no387", no_387); | ||
28 | |||
29 | static double __initdata x = 4195835.0; | 20 | static double __initdata x = 4195835.0; |
30 | static double __initdata y = 3145727.0; | 21 | static double __initdata y = 3145727.0; |
31 | 22 | ||
@@ -44,15 +35,6 @@ static void __init check_fpu(void) | |||
44 | { | 35 | { |
45 | s32 fdiv_bug; | 36 | s32 fdiv_bug; |
46 | 37 | ||
47 | if (!boot_cpu_data.hard_math) { | ||
48 | #ifndef CONFIG_MATH_EMULATION | ||
49 | pr_emerg("No coprocessor found and no math emulation present\n"); | ||
50 | pr_emerg("Giving up\n"); | ||
51 | for (;;) ; | ||
52 | #endif | ||
53 | return; | ||
54 | } | ||
55 | |||
56 | kernel_fpu_begin(); | 38 | kernel_fpu_begin(); |
57 | 39 | ||
58 | /* | 40 | /* |
@@ -107,5 +89,6 @@ void __init check_bugs(void) | |||
107 | * kernel_fpu_begin/end() in check_fpu() relies on the patched | 89 | * kernel_fpu_begin/end() in check_fpu() relies on the patched |
108 | * alternative instructions. | 90 | * alternative instructions. |
109 | */ | 91 | */ |
110 | check_fpu(); | 92 | if (cpu_has_fpu) |
93 | check_fpu(); | ||
111 | } | 94 | } |
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 22018f70a671..a4a07c0acb1f 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c | |||
@@ -711,10 +711,9 @@ static void __init early_identify_cpu(struct cpuinfo_x86 *c) | |||
711 | return; | 711 | return; |
712 | 712 | ||
713 | cpu_detect(c); | 713 | cpu_detect(c); |
714 | |||
715 | get_cpu_vendor(c); | 714 | get_cpu_vendor(c); |
716 | |||
717 | get_cpu_cap(c); | 715 | get_cpu_cap(c); |
716 | fpu_detect(c); | ||
718 | 717 | ||
719 | if (this_cpu->c_early_init) | 718 | if (this_cpu->c_early_init) |
720 | this_cpu->c_early_init(c); | 719 | this_cpu->c_early_init(c); |
@@ -724,6 +723,8 @@ static void __init early_identify_cpu(struct cpuinfo_x86 *c) | |||
724 | 723 | ||
725 | if (this_cpu->c_bsp_init) | 724 | if (this_cpu->c_bsp_init) |
726 | this_cpu->c_bsp_init(c); | 725 | this_cpu->c_bsp_init(c); |
726 | |||
727 | setup_force_cpu_cap(X86_FEATURE_ALWAYS); | ||
727 | } | 728 | } |
728 | 729 | ||
729 | void __init early_cpu_init(void) | 730 | void __init early_cpu_init(void) |
@@ -1363,3 +1364,17 @@ void __cpuinit cpu_init(void) | |||
1363 | fpu_init(); | 1364 | fpu_init(); |
1364 | } | 1365 | } |
1365 | #endif | 1366 | #endif |
1367 | |||
1368 | #ifdef CONFIG_X86_DEBUG_STATIC_CPU_HAS | ||
1369 | void warn_pre_alternatives(void) | ||
1370 | { | ||
1371 | WARN(1, "You're using static_cpu_has before alternatives have run!\n"); | ||
1372 | } | ||
1373 | EXPORT_SYMBOL_GPL(warn_pre_alternatives); | ||
1374 | #endif | ||
1375 | |||
1376 | inline bool __static_cpu_has_safe(u16 bit) | ||
1377 | { | ||
1378 | return boot_cpu_has(bit); | ||
1379 | } | ||
1380 | EXPORT_SYMBOL_GPL(__static_cpu_has_safe); | ||
diff --git a/arch/x86/kernel/cpu/cyrix.c b/arch/x86/kernel/cpu/cyrix.c index d048d5ca43c1..7582f475b163 100644 --- a/arch/x86/kernel/cpu/cyrix.c +++ b/arch/x86/kernel/cpu/cyrix.c | |||
@@ -333,7 +333,7 @@ static void __cpuinit init_cyrix(struct cpuinfo_x86 *c) | |||
333 | switch (dir0_lsn) { | 333 | switch (dir0_lsn) { |
334 | case 0xd: /* either a 486SLC or DLC w/o DEVID */ | 334 | case 0xd: /* either a 486SLC or DLC w/o DEVID */ |
335 | dir0_msn = 0; | 335 | dir0_msn = 0; |
336 | p = Cx486_name[(c->hard_math) ? 1 : 0]; | 336 | p = Cx486_name[(cpu_has_fpu ? 1 : 0)]; |
337 | break; | 337 | break; |
338 | 338 | ||
339 | case 0xe: /* a 486S A step */ | 339 | case 0xe: /* a 486S A step */ |
diff --git a/arch/x86/kernel/cpu/proc.c b/arch/x86/kernel/cpu/proc.c index 37a198bd48c8..aee6317b902f 100644 --- a/arch/x86/kernel/cpu/proc.c +++ b/arch/x86/kernel/cpu/proc.c | |||
@@ -37,8 +37,8 @@ static void show_cpuinfo_misc(struct seq_file *m, struct cpuinfo_x86 *c) | |||
37 | static_cpu_has_bug(X86_BUG_FDIV) ? "yes" : "no", | 37 | static_cpu_has_bug(X86_BUG_FDIV) ? "yes" : "no", |
38 | static_cpu_has_bug(X86_BUG_F00F) ? "yes" : "no", | 38 | static_cpu_has_bug(X86_BUG_F00F) ? "yes" : "no", |
39 | static_cpu_has_bug(X86_BUG_COMA) ? "yes" : "no", | 39 | static_cpu_has_bug(X86_BUG_COMA) ? "yes" : "no", |
40 | c->hard_math ? "yes" : "no", | 40 | static_cpu_has(X86_FEATURE_FPU) ? "yes" : "no", |
41 | c->hard_math ? "yes" : "no", | 41 | static_cpu_has(X86_FEATURE_FPU) ? "yes" : "no", |
42 | c->cpuid_level, | 42 | c->cpuid_level, |
43 | c->wp_works_ok ? "yes" : "no"); | 43 | c->wp_works_ok ? "yes" : "no"); |
44 | } | 44 | } |
diff --git a/arch/x86/kernel/head_32.S b/arch/x86/kernel/head_32.S index 73afd11799ca..e65ddc62e113 100644 --- a/arch/x86/kernel/head_32.S +++ b/arch/x86/kernel/head_32.S | |||
@@ -444,7 +444,6 @@ is486: | |||
444 | orl %ecx,%eax | 444 | orl %ecx,%eax |
445 | movl %eax,%cr0 | 445 | movl %eax,%cr0 |
446 | 446 | ||
447 | call check_x87 | ||
448 | lgdt early_gdt_descr | 447 | lgdt early_gdt_descr |
449 | lidt idt_descr | 448 | lidt idt_descr |
450 | ljmp $(__KERNEL_CS),$1f | 449 | ljmp $(__KERNEL_CS),$1f |
@@ -467,26 +466,6 @@ is486: | |||
467 | pushl $0 # fake return address for unwinder | 466 | pushl $0 # fake return address for unwinder |
468 | jmp *(initial_code) | 467 | jmp *(initial_code) |
469 | 468 | ||
470 | /* | ||
471 | * We depend on ET to be correct. This checks for 287/387. | ||
472 | */ | ||
473 | check_x87: | ||
474 | movb $0,X86_HARD_MATH | ||
475 | clts | ||
476 | fninit | ||
477 | fstsw %ax | ||
478 | cmpb $0,%al | ||
479 | je 1f | ||
480 | movl %cr0,%eax /* no coprocessor: have to set bits */ | ||
481 | xorl $4,%eax /* set EM */ | ||
482 | movl %eax,%cr0 | ||
483 | ret | ||
484 | ALIGN | ||
485 | 1: movb $1,X86_HARD_MATH | ||
486 | .byte 0xDB,0xE4 /* fsetpm for 287, ignored by 387 */ | ||
487 | ret | ||
488 | |||
489 | |||
490 | #include "verify_cpu.S" | 469 | #include "verify_cpu.S" |
491 | 470 | ||
492 | /* | 471 | /* |
diff --git a/arch/x86/kernel/i387.c b/arch/x86/kernel/i387.c index cb339097b9ea..b627746f6b1a 100644 --- a/arch/x86/kernel/i387.c +++ b/arch/x86/kernel/i387.c | |||
@@ -131,7 +131,7 @@ static void __cpuinit init_thread_xstate(void) | |||
131 | * xsave_init(). | 131 | * xsave_init(). |
132 | */ | 132 | */ |
133 | 133 | ||
134 | if (!HAVE_HWFP) { | 134 | if (!cpu_has_fpu) { |
135 | /* | 135 | /* |
136 | * Disable xsave as we do not support it if i387 | 136 | * Disable xsave as we do not support it if i387 |
137 | * emulation is enabled. | 137 | * emulation is enabled. |
@@ -158,6 +158,14 @@ void __cpuinit fpu_init(void) | |||
158 | unsigned long cr0; | 158 | unsigned long cr0; |
159 | unsigned long cr4_mask = 0; | 159 | unsigned long cr4_mask = 0; |
160 | 160 | ||
161 | #ifndef CONFIG_MATH_EMULATION | ||
162 | if (!cpu_has_fpu) { | ||
163 | pr_emerg("No FPU found and no math emulation present\n"); | ||
164 | pr_emerg("Giving up\n"); | ||
165 | for (;;) | ||
166 | asm volatile("hlt"); | ||
167 | } | ||
168 | #endif | ||
161 | if (cpu_has_fxsr) | 169 | if (cpu_has_fxsr) |
162 | cr4_mask |= X86_CR4_OSFXSR; | 170 | cr4_mask |= X86_CR4_OSFXSR; |
163 | if (cpu_has_xmm) | 171 | if (cpu_has_xmm) |
@@ -167,7 +175,7 @@ void __cpuinit fpu_init(void) | |||
167 | 175 | ||
168 | cr0 = read_cr0(); | 176 | cr0 = read_cr0(); |
169 | cr0 &= ~(X86_CR0_TS|X86_CR0_EM); /* clear TS and EM */ | 177 | cr0 &= ~(X86_CR0_TS|X86_CR0_EM); /* clear TS and EM */ |
170 | if (!HAVE_HWFP) | 178 | if (!cpu_has_fpu) |
171 | cr0 |= X86_CR0_EM; | 179 | cr0 |= X86_CR0_EM; |
172 | write_cr0(cr0); | 180 | write_cr0(cr0); |
173 | 181 | ||
@@ -185,7 +193,7 @@ void __cpuinit fpu_init(void) | |||
185 | 193 | ||
186 | void fpu_finit(struct fpu *fpu) | 194 | void fpu_finit(struct fpu *fpu) |
187 | { | 195 | { |
188 | if (!HAVE_HWFP) { | 196 | if (!cpu_has_fpu) { |
189 | finit_soft_fpu(&fpu->state->soft); | 197 | finit_soft_fpu(&fpu->state->soft); |
190 | return; | 198 | return; |
191 | } | 199 | } |
@@ -214,7 +222,7 @@ int init_fpu(struct task_struct *tsk) | |||
214 | int ret; | 222 | int ret; |
215 | 223 | ||
216 | if (tsk_used_math(tsk)) { | 224 | if (tsk_used_math(tsk)) { |
217 | if (HAVE_HWFP && tsk == current) | 225 | if (cpu_has_fpu && tsk == current) |
218 | unlazy_fpu(tsk); | 226 | unlazy_fpu(tsk); |
219 | tsk->thread.fpu.last_cpu = ~0; | 227 | tsk->thread.fpu.last_cpu = ~0; |
220 | return 0; | 228 | return 0; |
@@ -511,14 +519,13 @@ int fpregs_get(struct task_struct *target, const struct user_regset *regset, | |||
511 | if (ret) | 519 | if (ret) |
512 | return ret; | 520 | return ret; |
513 | 521 | ||
514 | if (!HAVE_HWFP) | 522 | if (!static_cpu_has(X86_FEATURE_FPU)) |
515 | return fpregs_soft_get(target, regset, pos, count, kbuf, ubuf); | 523 | return fpregs_soft_get(target, regset, pos, count, kbuf, ubuf); |
516 | 524 | ||
517 | if (!cpu_has_fxsr) { | 525 | if (!cpu_has_fxsr) |
518 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, | 526 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, |
519 | &target->thread.fpu.state->fsave, 0, | 527 | &target->thread.fpu.state->fsave, 0, |
520 | -1); | 528 | -1); |
521 | } | ||
522 | 529 | ||
523 | sanitize_i387_state(target); | 530 | sanitize_i387_state(target); |
524 | 531 | ||
@@ -545,13 +552,13 @@ int fpregs_set(struct task_struct *target, const struct user_regset *regset, | |||
545 | 552 | ||
546 | sanitize_i387_state(target); | 553 | sanitize_i387_state(target); |
547 | 554 | ||
548 | if (!HAVE_HWFP) | 555 | if (!static_cpu_has(X86_FEATURE_FPU)) |
549 | return fpregs_soft_set(target, regset, pos, count, kbuf, ubuf); | 556 | return fpregs_soft_set(target, regset, pos, count, kbuf, ubuf); |
550 | 557 | ||
551 | if (!cpu_has_fxsr) { | 558 | if (!cpu_has_fxsr) |
552 | return user_regset_copyin(&pos, &count, &kbuf, &ubuf, | 559 | return user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
553 | &target->thread.fpu.state->fsave, 0, -1); | 560 | &target->thread.fpu.state->fsave, 0, |
554 | } | 561 | -1); |
555 | 562 | ||
556 | if (pos > 0 || count < sizeof(env)) | 563 | if (pos > 0 || count < sizeof(env)) |
557 | convert_from_fxsr(&env, target); | 564 | convert_from_fxsr(&env, target); |
@@ -592,3 +599,33 @@ int dump_fpu(struct pt_regs *regs, struct user_i387_struct *fpu) | |||
592 | EXPORT_SYMBOL(dump_fpu); | 599 | EXPORT_SYMBOL(dump_fpu); |
593 | 600 | ||
594 | #endif /* CONFIG_X86_32 || CONFIG_IA32_EMULATION */ | 601 | #endif /* CONFIG_X86_32 || CONFIG_IA32_EMULATION */ |
602 | |||
603 | static int __init no_387(char *s) | ||
604 | { | ||
605 | setup_clear_cpu_cap(X86_FEATURE_FPU); | ||
606 | return 1; | ||
607 | } | ||
608 | |||
609 | __setup("no387", no_387); | ||
610 | |||
611 | void __cpuinit fpu_detect(struct cpuinfo_x86 *c) | ||
612 | { | ||
613 | unsigned long cr0; | ||
614 | u16 fsw, fcw; | ||
615 | |||
616 | fsw = fcw = 0xffff; | ||
617 | |||
618 | cr0 = read_cr0(); | ||
619 | cr0 &= ~(X86_CR0_TS | X86_CR0_EM); | ||
620 | write_cr0(cr0); | ||
621 | |||
622 | asm volatile("fninit ; fnstsw %0 ; fnstcw %1" | ||
623 | : "+m" (fsw), "+m" (fcw)); | ||
624 | |||
625 | if (fsw == 0 && (fcw & 0x103f) == 0x003f) | ||
626 | set_cpu_cap(c, X86_FEATURE_FPU); | ||
627 | else | ||
628 | clear_cpu_cap(c, X86_FEATURE_FPU); | ||
629 | |||
630 | /* The final cr0 value is set in fpu_init() */ | ||
631 | } | ||
diff --git a/arch/x86/kernel/xsave.c b/arch/x86/kernel/xsave.c index ada87a329edc..d6c28acdf99c 100644 --- a/arch/x86/kernel/xsave.c +++ b/arch/x86/kernel/xsave.c | |||
@@ -243,7 +243,7 @@ int save_xstate_sig(void __user *buf, void __user *buf_fx, int size) | |||
243 | if (!access_ok(VERIFY_WRITE, buf, size)) | 243 | if (!access_ok(VERIFY_WRITE, buf, size)) |
244 | return -EACCES; | 244 | return -EACCES; |
245 | 245 | ||
246 | if (!HAVE_HWFP) | 246 | if (!static_cpu_has(X86_FEATURE_FPU)) |
247 | return fpregs_soft_get(current, NULL, 0, | 247 | return fpregs_soft_get(current, NULL, 0, |
248 | sizeof(struct user_i387_ia32_struct), NULL, | 248 | sizeof(struct user_i387_ia32_struct), NULL, |
249 | (struct _fpstate_ia32 __user *) buf) ? -1 : 1; | 249 | (struct _fpstate_ia32 __user *) buf) ? -1 : 1; |
@@ -350,11 +350,10 @@ int __restore_xstate_sig(void __user *buf, void __user *buf_fx, int size) | |||
350 | if (!used_math() && init_fpu(tsk)) | 350 | if (!used_math() && init_fpu(tsk)) |
351 | return -1; | 351 | return -1; |
352 | 352 | ||
353 | if (!HAVE_HWFP) { | 353 | if (!static_cpu_has(X86_FEATURE_FPU)) |
354 | return fpregs_soft_set(current, NULL, | 354 | return fpregs_soft_set(current, NULL, |
355 | 0, sizeof(struct user_i387_ia32_struct), | 355 | 0, sizeof(struct user_i387_ia32_struct), |
356 | NULL, buf) != 0; | 356 | NULL, buf) != 0; |
357 | } | ||
358 | 357 | ||
359 | if (use_xsave()) { | 358 | if (use_xsave()) { |
360 | struct _fpx_sw_bytes fx_sw_user; | 359 | struct _fpx_sw_bytes fx_sw_user; |
diff --git a/arch/x86/lguest/boot.c b/arch/x86/lguest/boot.c index 7114c63f047d..d482bcaf61c1 100644 --- a/arch/x86/lguest/boot.c +++ b/arch/x86/lguest/boot.c | |||
@@ -1410,7 +1410,7 @@ __init void lguest_init(void) | |||
1410 | new_cpu_data.x86_capability[0] = cpuid_edx(1); | 1410 | new_cpu_data.x86_capability[0] = cpuid_edx(1); |
1411 | 1411 | ||
1412 | /* Math is always hard! */ | 1412 | /* Math is always hard! */ |
1413 | new_cpu_data.hard_math = 1; | 1413 | set_cpu_cap(&new_cpu_data, X86_FEATURE_FPU); |
1414 | 1414 | ||
1415 | /* We don't have features. We have puppies! Puppies! */ | 1415 | /* We don't have features. We have puppies! Puppies! */ |
1416 | #ifdef CONFIG_X86_MCE | 1416 | #ifdef CONFIG_X86_MCE |
diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c index a492be2635ac..2fa02bc50034 100644 --- a/arch/x86/xen/enlighten.c +++ b/arch/x86/xen/enlighten.c | |||
@@ -1557,7 +1557,7 @@ asmlinkage void __init xen_start_kernel(void) | |||
1557 | #ifdef CONFIG_X86_32 | 1557 | #ifdef CONFIG_X86_32 |
1558 | /* set up basic CPUID stuff */ | 1558 | /* set up basic CPUID stuff */ |
1559 | cpu_detect(&new_cpu_data); | 1559 | cpu_detect(&new_cpu_data); |
1560 | new_cpu_data.hard_math = 1; | 1560 | set_cpu_cap(&new_cpu_data, X86_FEATURE_FPU); |
1561 | new_cpu_data.wp_works_ok = 1; | 1561 | new_cpu_data.wp_works_ok = 1; |
1562 | new_cpu_data.x86_capability[0] = cpuid_edx(1); | 1562 | new_cpu_data.x86_capability[0] = cpuid_edx(1); |
1563 | #endif | 1563 | #endif |