Commit | Line | Data |
---|---|---|
0c867537 IM |
1 | /* |
2 | * x86 FPU boot time init code | |
3 | */ | |
78f7f1e5 | 4 | #include <asm/fpu/internal.h> |
0c867537 IM |
5 | #include <asm/tlbflush.h> |
6 | ||
4d164092 IM |
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 | */ | |
0c867537 | 70 | unsigned int mxcsr_feature_mask __read_mostly = 0xffffffffu; |
91a8c2a5 | 71 | |
0c867537 IM |
72 | unsigned int xstate_size; |
73 | EXPORT_SYMBOL_GPL(xstate_size); | |
0c867537 IM |
74 | |
75 | static void mxcsr_feature_mask_init(void) | |
76 | { | |
91a8c2a5 | 77 | unsigned int mask = 0; |
0c867537 IM |
78 | |
79 | if (cpu_has_fxsr) { | |
91a8c2a5 IM |
80 | struct i387_fxsave_struct fx_tmp __aligned(32) = { }; |
81 | ||
82 | asm volatile("fxsave %0" : "+m" (fx_tmp)); | |
83 | ||
84 | mask = fx_tmp.mxcsr_mask; | |
85 | ||
86 | /* | |
87 | * If zero then use the default features mask, | |
88 | * which has all features set, except the | |
89 | * denormals-are-zero feature bit: | |
90 | */ | |
0c867537 IM |
91 | if (mask == 0) |
92 | mask = 0x0000ffbf; | |
93 | } | |
94 | mxcsr_feature_mask &= mask; | |
95 | } | |
96 | ||
97 | static void fpstate_xstate_init_size(void) | |
98 | { | |
99 | /* | |
100 | * Note that xstate_size might be overwriten later during | |
101 | * xsave_init(). | |
102 | */ | |
103 | ||
104 | if (!cpu_has_fpu) { | |
105 | /* | |
106 | * Disable xsave as we do not support it if i387 | |
107 | * emulation is enabled. | |
108 | */ | |
109 | setup_clear_cpu_cap(X86_FEATURE_XSAVE); | |
110 | setup_clear_cpu_cap(X86_FEATURE_XSAVEOPT); | |
111 | xstate_size = sizeof(struct i387_soft_struct); | |
6a133207 IM |
112 | } else { |
113 | if (cpu_has_fxsr) | |
114 | xstate_size = sizeof(struct i387_fxsave_struct); | |
115 | else | |
116 | xstate_size = sizeof(struct i387_fsave_struct); | |
0c867537 | 117 | } |
0c867537 IM |
118 | } |
119 | ||
120 | /* | |
121 | * Called on the boot CPU at bootup to set up the initial FPU state that | |
122 | * is later cloned into all processes. | |
123 | * | |
124 | * Also called on secondary CPUs to set up the FPU state of their | |
125 | * idle threads. | |
126 | */ | |
127 | void fpu__cpu_init(void) | |
128 | { | |
129 | unsigned long cr0; | |
130 | unsigned long cr4_mask = 0; | |
131 | ||
132 | #ifndef CONFIG_MATH_EMULATION | |
133 | if (!cpu_has_fpu) { | |
134 | pr_emerg("No FPU found and no math emulation present\n"); | |
135 | pr_emerg("Giving up\n"); | |
136 | for (;;) | |
137 | asm volatile("hlt"); | |
138 | } | |
139 | #endif | |
140 | if (cpu_has_fxsr) | |
141 | cr4_mask |= X86_CR4_OSFXSR; | |
142 | if (cpu_has_xmm) | |
143 | cr4_mask |= X86_CR4_OSXMMEXCPT; | |
144 | if (cr4_mask) | |
145 | cr4_set_bits(cr4_mask); | |
146 | ||
147 | cr0 = read_cr0(); | |
148 | cr0 &= ~(X86_CR0_TS|X86_CR0_EM); /* clear TS and EM */ | |
149 | if (!cpu_has_fpu) | |
150 | cr0 |= X86_CR0_EM; | |
151 | write_cr0(cr0); | |
152 | ||
0c867537 IM |
153 | |
154 | mxcsr_feature_mask_init(); | |
155 | xsave_init(); | |
156 | eager_fpu_init(); | |
157 | } | |
146ed598 IM |
158 | |
159 | static int __init no_387(char *s) | |
160 | { | |
161 | setup_clear_cpu_cap(X86_FEATURE_FPU); | |
162 | return 1; | |
163 | } | |
164 | ||
165 | __setup("no387", no_387); | |
166 | ||
167 | /* | |
168 | * Set the X86_FEATURE_FPU CPU-capability bit based on | |
169 | * trying to execute an actual sequence of FPU instructions: | |
170 | */ | |
171 | void fpu__detect(struct cpuinfo_x86 *c) | |
172 | { | |
173 | unsigned long cr0; | |
174 | u16 fsw, fcw; | |
175 | ||
176 | fsw = fcw = 0xffff; | |
177 | ||
178 | cr0 = read_cr0(); | |
179 | cr0 &= ~(X86_CR0_TS | X86_CR0_EM); | |
180 | write_cr0(cr0); | |
181 | ||
182 | asm volatile("fninit ; fnstsw %0 ; fnstcw %1" | |
183 | : "+m" (fsw), "+m" (fcw)); | |
184 | ||
185 | if (fsw == 0 && (fcw & 0x103f) == 0x003f) | |
186 | set_cpu_cap(c, X86_FEATURE_FPU); | |
187 | else | |
188 | clear_cpu_cap(c, X86_FEATURE_FPU); | |
189 | ||
6a133207 IM |
190 | /* The final cr0 value is set later, in fpu_init() */ |
191 | ||
192 | fpstate_xstate_init_size(); | |
146ed598 | 193 | } |