x86/fpu: Factor out the FPU bug detection code into fpu__init_check_bugs()
[platform/kernel/linux-starfive.git] / arch / x86 / kernel / fpu / init.c
1 /*
2  * x86 FPU boot time init code
3  */
4 #include <asm/fpu-internal.h>
5 #include <asm/tlbflush.h>
6
7 /*
8  * Boot time CPU/FPU FDIV bug detection code:
9  */
10
11 static double __initdata x = 4195835.0;
12 static double __initdata y = 3145727.0;
13
14 /*
15  * This used to check for exceptions..
16  * However, it turns out that to support that,
17  * the XMM trap handlers basically had to
18  * be buggy. So let's have a correct XMM trap
19  * handler, and forget about printing out
20  * some status at boot.
21  *
22  * We should really only care about bugs here
23  * anyway. Not features.
24  */
25 static void __init check_fpu(void)
26 {
27         s32 fdiv_bug;
28
29         kernel_fpu_begin();
30
31         /*
32          * trap_init() enabled FXSR and company _before_ testing for FP
33          * problems here.
34          *
35          * Test for the divl bug: http://en.wikipedia.org/wiki/Fdiv_bug
36          */
37         __asm__("fninit\n\t"
38                 "fldl %1\n\t"
39                 "fdivl %2\n\t"
40                 "fmull %2\n\t"
41                 "fldl %1\n\t"
42                 "fsubp %%st,%%st(1)\n\t"
43                 "fistpl %0\n\t"
44                 "fwait\n\t"
45                 "fninit"
46                 : "=m" (*&fdiv_bug)
47                 : "m" (*&x), "m" (*&y));
48
49         kernel_fpu_end();
50
51         if (fdiv_bug) {
52                 set_cpu_bug(&boot_cpu_data, X86_BUG_FDIV);
53                 pr_warn("Hmm, FPU with FDIV bug\n");
54         }
55 }
56
57 void fpu__init_check_bugs(void)
58 {
59         /*
60          * kernel_fpu_begin/end() in check_fpu() relies on the patched
61          * alternative instructions.
62          */
63         if (cpu_has_fpu)
64                 check_fpu();
65 }
66
67 /*
68  * Boot time FPU feature detection code:
69  */
70 unsigned int mxcsr_feature_mask __read_mostly = 0xffffffffu;
71 unsigned int xstate_size;
72 EXPORT_SYMBOL_GPL(xstate_size);
73 static struct i387_fxsave_struct fx_scratch;
74
75 static void mxcsr_feature_mask_init(void)
76 {
77         unsigned long mask = 0;
78
79         if (cpu_has_fxsr) {
80                 memset(&fx_scratch, 0, sizeof(struct i387_fxsave_struct));
81                 asm volatile("fxsave %0" : "+m" (fx_scratch));
82                 mask = fx_scratch.mxcsr_mask;
83                 if (mask == 0)
84                         mask = 0x0000ffbf;
85         }
86         mxcsr_feature_mask &= mask;
87 }
88
89 static void fpstate_xstate_init_size(void)
90 {
91         /*
92          * Note that xstate_size might be overwriten later during
93          * xsave_init().
94          */
95
96         if (!cpu_has_fpu) {
97                 /*
98                  * Disable xsave as we do not support it if i387
99                  * emulation is enabled.
100                  */
101                 setup_clear_cpu_cap(X86_FEATURE_XSAVE);
102                 setup_clear_cpu_cap(X86_FEATURE_XSAVEOPT);
103                 xstate_size = sizeof(struct i387_soft_struct);
104                 return;
105         }
106
107         if (cpu_has_fxsr)
108                 xstate_size = sizeof(struct i387_fxsave_struct);
109         else
110                 xstate_size = sizeof(struct i387_fsave_struct);
111 }
112
113 /*
114  * Called on the boot CPU at bootup to set up the initial FPU state that
115  * is later cloned into all processes.
116  *
117  * Also called on secondary CPUs to set up the FPU state of their
118  * idle threads.
119  */
120 void fpu__cpu_init(void)
121 {
122         unsigned long cr0;
123         unsigned long cr4_mask = 0;
124
125 #ifndef CONFIG_MATH_EMULATION
126         if (!cpu_has_fpu) {
127                 pr_emerg("No FPU found and no math emulation present\n");
128                 pr_emerg("Giving up\n");
129                 for (;;)
130                         asm volatile("hlt");
131         }
132 #endif
133         if (cpu_has_fxsr)
134                 cr4_mask |= X86_CR4_OSFXSR;
135         if (cpu_has_xmm)
136                 cr4_mask |= X86_CR4_OSXMMEXCPT;
137         if (cr4_mask)
138                 cr4_set_bits(cr4_mask);
139
140         cr0 = read_cr0();
141         cr0 &= ~(X86_CR0_TS|X86_CR0_EM); /* clear TS and EM */
142         if (!cpu_has_fpu)
143                 cr0 |= X86_CR0_EM;
144         write_cr0(cr0);
145
146         /*
147          * fpstate_xstate_init_size() is only called once, to avoid overriding
148          * 'xstate_size' during (secondary CPU) bootup or during CPU hotplug.
149          */
150         if (xstate_size == 0)
151                 fpstate_xstate_init_size();
152
153         mxcsr_feature_mask_init();
154         xsave_init();
155         eager_fpu_init();
156 }
157
158 static int __init no_387(char *s)
159 {
160         setup_clear_cpu_cap(X86_FEATURE_FPU);
161         return 1;
162 }
163
164 __setup("no387", no_387);
165
166 /*
167  * Set the X86_FEATURE_FPU CPU-capability bit based on
168  * trying to execute an actual sequence of FPU instructions:
169  */
170 void fpu__detect(struct cpuinfo_x86 *c)
171 {
172         unsigned long cr0;
173         u16 fsw, fcw;
174
175         fsw = fcw = 0xffff;
176
177         cr0 = read_cr0();
178         cr0 &= ~(X86_CR0_TS | X86_CR0_EM);
179         write_cr0(cr0);
180
181         asm volatile("fninit ; fnstsw %0 ; fnstcw %1"
182                      : "+m" (fsw), "+m" (fcw));
183
184         if (fsw == 0 && (fcw & 0x103f) == 0x003f)
185                 set_cpu_cap(c, X86_FEATURE_FPU);
186         else
187                 clear_cpu_cap(c, X86_FEATURE_FPU);
188
189         /* The final cr0 value is set in fpu_init() */
190 }