| 1 | // SPDX-License-Identifier: GPL-2.0 | 
|---|
| 2 | /* | 
|---|
| 3 | * x86 FPU bug checks: | 
|---|
| 4 | */ | 
|---|
| 5 | #include <linux/printk.h> | 
|---|
| 6 |  | 
|---|
| 7 | #include <asm/cpufeature.h> | 
|---|
| 8 | #include <asm/fpu/api.h> | 
|---|
| 9 |  | 
|---|
| 10 | /* | 
|---|
| 11 | * Boot time CPU/FPU FDIV bug detection code: | 
|---|
| 12 | */ | 
|---|
| 13 |  | 
|---|
| 14 | static double __initdata x = 4195835.0; | 
|---|
| 15 | static double __initdata y = 3145727.0; | 
|---|
| 16 |  | 
|---|
| 17 | /* | 
|---|
| 18 | * This used to check for exceptions.. | 
|---|
| 19 | * However, it turns out that to support that, | 
|---|
| 20 | * the XMM trap handlers basically had to | 
|---|
| 21 | * be buggy. So let's have a correct XMM trap | 
|---|
| 22 | * handler, and forget about printing out | 
|---|
| 23 | * some status at boot. | 
|---|
| 24 | * | 
|---|
| 25 | * We should really only care about bugs here | 
|---|
| 26 | * anyway. Not features. | 
|---|
| 27 | */ | 
|---|
| 28 | void __init fpu__init_check_bugs(void) | 
|---|
| 29 | { | 
|---|
| 30 | s32 fdiv_bug; | 
|---|
| 31 |  | 
|---|
| 32 | /* kernel_fpu_begin/end() relies on patched alternative instructions. */ | 
|---|
| 33 | if (!boot_cpu_has(X86_FEATURE_FPU)) | 
|---|
| 34 | return; | 
|---|
| 35 |  | 
|---|
| 36 | kernel_fpu_begin(); | 
|---|
| 37 |  | 
|---|
| 38 | /* | 
|---|
| 39 | * trap_init() enabled FXSR and company _before_ testing for FP | 
|---|
| 40 | * problems here. | 
|---|
| 41 | * | 
|---|
| 42 | * Test for the divl bug: http://en.wikipedia.org/wiki/Fdiv_bug | 
|---|
| 43 | */ | 
|---|
| 44 | __asm__( "fninit\n\t" | 
|---|
| 45 | "fldl %1\n\t" | 
|---|
| 46 | "fdivl %2\n\t" | 
|---|
| 47 | "fmull %2\n\t" | 
|---|
| 48 | "fldl %1\n\t" | 
|---|
| 49 | "fsubp %%st,%%st(1)\n\t" | 
|---|
| 50 | "fistpl %0\n\t" | 
|---|
| 51 | "fwait\n\t" | 
|---|
| 52 | "fninit" | 
|---|
| 53 | : "=m"(*&fdiv_bug) | 
|---|
| 54 | : "m"(*&x), "m"(*&y)); | 
|---|
| 55 |  | 
|---|
| 56 | kernel_fpu_end(); | 
|---|
| 57 |  | 
|---|
| 58 | if (fdiv_bug) { | 
|---|
| 59 | set_cpu_bug(&boot_cpu_data, X86_BUG_FDIV); | 
|---|
| 60 | pr_warn( "Hmm, FPU with FDIV bug\n"); | 
|---|
| 61 | } | 
|---|
| 62 | } | 
|---|
| 63 |  | 
|---|