1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
20c867537SIngo Molnar /*
3ae02679cSIngo Molnar * x86 FPU boot time init code:
40c867537SIngo Molnar */
5b56d2795SThomas Gleixner #include <asm/fpu/api.h>
60c867537SIngo Molnar #include <asm/tlbflush.h>
74f81cbafSyu-cheng yu #include <asm/setup.h>
80c867537SIngo Molnar
95aaeb5c0SIngo Molnar #include <linux/sched.h>
1029930025SIngo Molnar #include <linux/sched/task.h>
114f81cbafSyu-cheng yu #include <linux/init.h>
125aaeb5c0SIngo Molnar
13cdcb6fa1SThomas Gleixner #include "internal.h"
14d9d005f3SThomas Gleixner #include "legacy.h"
156415bb80SThomas Gleixner #include "xstate.h"
16cdcb6fa1SThomas Gleixner
17ae02679cSIngo Molnar /*
1841e78410SIngo Molnar * Initialize the registers found in all CPUs, CR0 and CR4:
1941e78410SIngo Molnar */
fpu__init_cpu_generic(void)2041e78410SIngo Molnar static void fpu__init_cpu_generic(void)
2141e78410SIngo Molnar {
2241e78410SIngo Molnar unsigned long cr0;
2341e78410SIngo Molnar unsigned long cr4_mask = 0;
2441e78410SIngo Molnar
2501f8fd73SBorislav Petkov if (boot_cpu_has(X86_FEATURE_FXSR))
2641e78410SIngo Molnar cr4_mask |= X86_CR4_OSFXSR;
27dda9edf7SBorislav Petkov if (boot_cpu_has(X86_FEATURE_XMM))
2841e78410SIngo Molnar cr4_mask |= X86_CR4_OSXMMEXCPT;
2941e78410SIngo Molnar if (cr4_mask)
3041e78410SIngo Molnar cr4_set_bits(cr4_mask);
3141e78410SIngo Molnar
3241e78410SIngo Molnar cr0 = read_cr0();
3341e78410SIngo Molnar cr0 &= ~(X86_CR0_TS|X86_CR0_EM); /* clear TS and EM */
34a402a8dfSBorislav Petkov if (!boot_cpu_has(X86_FEATURE_FPU))
3541e78410SIngo Molnar cr0 |= X86_CR0_EM;
3641e78410SIngo Molnar write_cr0(cr0);
37b1276c48SIngo Molnar
38b1276c48SIngo Molnar /* Flush out any pending x87 state: */
395fc96038SIngo Molnar #ifdef CONFIG_MATH_EMULATION
40a402a8dfSBorislav Petkov if (!boot_cpu_has(X86_FEATURE_FPU))
41c20942ceSThomas Gleixner fpstate_init_soft(¤t->thread.fpu.fpstate->regs.soft);
425fc96038SIngo Molnar else
435fc96038SIngo Molnar #endif
44b1276c48SIngo Molnar asm volatile ("fninit");
4541e78410SIngo Molnar }
4641e78410SIngo Molnar
4741e78410SIngo Molnar /*
48ae02679cSIngo Molnar * Enable all supported FPU features. Called when a CPU is brought online:
4941e78410SIngo Molnar */
fpu__init_cpu(void)5041e78410SIngo Molnar void fpu__init_cpu(void)
5141e78410SIngo Molnar {
5241e78410SIngo Molnar fpu__init_cpu_generic();
5341e78410SIngo Molnar fpu__init_cpu_xstate();
5441e78410SIngo Molnar }
5541e78410SIngo Molnar
fpu__probe_without_cpuid(void)56*1703db2bSThomas Gleixner static bool __init fpu__probe_without_cpuid(void)
572e2f3da7SIngo Molnar {
582e2f3da7SIngo Molnar unsigned long cr0;
592e2f3da7SIngo Molnar u16 fsw, fcw;
602e2f3da7SIngo Molnar
612e2f3da7SIngo Molnar fsw = fcw = 0xffff;
622e2f3da7SIngo Molnar
632e2f3da7SIngo Molnar cr0 = read_cr0();
642e2f3da7SIngo Molnar cr0 &= ~(X86_CR0_TS | X86_CR0_EM);
652e2f3da7SIngo Molnar write_cr0(cr0);
662e2f3da7SIngo Molnar
6737ac78b6SAndy Lutomirski asm volatile("fninit ; fnstsw %0 ; fnstcw %1" : "+m" (fsw), "+m" (fcw));
682e2f3da7SIngo Molnar
6937ac78b6SAndy Lutomirski pr_info("x86/fpu: Probing for FPU: FSW=0x%04hx FCW=0x%04hx\n", fsw, fcw);
7037ac78b6SAndy Lutomirski
7137ac78b6SAndy Lutomirski return fsw == 0 && (fcw & 0x103f) == 0x003f;
7237ac78b6SAndy Lutomirski }
7337ac78b6SAndy Lutomirski
fpu__init_system_early_generic(void)74*1703db2bSThomas Gleixner static void __init fpu__init_system_early_generic(void)
7537ac78b6SAndy Lutomirski {
7637ac78b6SAndy Lutomirski if (!boot_cpu_has(X86_FEATURE_CPUID) &&
7737ac78b6SAndy Lutomirski !test_bit(X86_FEATURE_FPU, (unsigned long *)cpu_caps_cleared)) {
7837ac78b6SAndy Lutomirski if (fpu__probe_without_cpuid())
7937ac78b6SAndy Lutomirski setup_force_cpu_cap(X86_FEATURE_FPU);
802e2f3da7SIngo Molnar else
8137ac78b6SAndy Lutomirski setup_clear_cpu_cap(X86_FEATURE_FPU);
82f363938cSAndy Lutomirski }
83e83ab9adSIngo Molnar
84e83ab9adSIngo Molnar #ifndef CONFIG_MATH_EMULATION
859729017fSAndy Lutomirski if (!test_cpu_cap(&boot_cpu_data, X86_FEATURE_FPU)) {
86ae02679cSIngo Molnar pr_emerg("x86/fpu: Giving up, no FPU found and no math emulation present\n");
87e83ab9adSIngo Molnar for (;;)
88e83ab9adSIngo Molnar asm volatile("hlt");
89e83ab9adSIngo Molnar }
90e83ab9adSIngo Molnar #endif
912e2f3da7SIngo Molnar }
922e2f3da7SIngo Molnar
932e2f3da7SIngo Molnar /*
944d164092SIngo Molnar * Boot time FPU feature detection code:
954d164092SIngo Molnar */
96ce578f16SThomas Gleixner unsigned int mxcsr_feature_mask __ro_after_init = 0xffffffffu;
97a575813bSWanpeng Li EXPORT_SYMBOL_GPL(mxcsr_feature_mask);
9891a8c2a5SIngo Molnar
fpu__init_system_mxcsr(void)9932231879SIngo Molnar static void __init fpu__init_system_mxcsr(void)
1000c867537SIngo Molnar {
10191a8c2a5SIngo Molnar unsigned int mask = 0;
1020c867537SIngo Molnar
10301f8fd73SBorislav Petkov if (boot_cpu_has(X86_FEATURE_FXSR)) {
104b96fecbfSIngo Molnar /* Static because GCC does not get 16-byte stack alignment right: */
105b96fecbfSIngo Molnar static struct fxregs_state fxregs __initdata;
10691a8c2a5SIngo Molnar
107b96fecbfSIngo Molnar asm volatile("fxsave %0" : "+m" (fxregs));
10891a8c2a5SIngo Molnar
109b96fecbfSIngo Molnar mask = fxregs.mxcsr_mask;
11091a8c2a5SIngo Molnar
11191a8c2a5SIngo Molnar /*
11291a8c2a5SIngo Molnar * If zero then use the default features mask,
11391a8c2a5SIngo Molnar * which has all features set, except the
11491a8c2a5SIngo Molnar * denormals-are-zero feature bit:
11591a8c2a5SIngo Molnar */
1160c867537SIngo Molnar if (mask == 0)
1170c867537SIngo Molnar mask = 0x0000ffbf;
1180c867537SIngo Molnar }
1190c867537SIngo Molnar mxcsr_feature_mask &= mask;
1200c867537SIngo Molnar }
1210c867537SIngo Molnar
1227218e8b7SIngo Molnar /*
1237218e8b7SIngo Molnar * Once per bootup FPU initialization sequences that will run on most x86 CPUs:
1247218e8b7SIngo Molnar */
fpu__init_system_generic(void)12532231879SIngo Molnar static void __init fpu__init_system_generic(void)
1267218e8b7SIngo Molnar {
1277218e8b7SIngo Molnar /*
128126fe040SThomas Gleixner * Set up the legacy init FPU context. Will be updated when the
129126fe040SThomas Gleixner * CPU supports XSAVE[S].
1307218e8b7SIngo Molnar */
131126fe040SThomas Gleixner fpstate_init_user(&init_fpstate);
1327218e8b7SIngo Molnar
1337218e8b7SIngo Molnar fpu__init_system_mxcsr();
1347218e8b7SIngo Molnar }
1357218e8b7SIngo Molnar
13625ec02f2SJiri Olsa /*
13725ec02f2SJiri Olsa * Enforce that 'MEMBER' is the last field of 'TYPE'.
13825ec02f2SJiri Olsa *
13925ec02f2SJiri Olsa * Align the computed size with alignment of the TYPE,
14025ec02f2SJiri Olsa * because that's how C aligns structs.
14125ec02f2SJiri Olsa */
1420c8c0f03SDave Hansen #define CHECK_MEMBER_AT_END_OF(TYPE, MEMBER) \
14355228db2SYingChi Long BUILD_BUG_ON(sizeof(TYPE) != \
14455228db2SYingChi Long ALIGN(offsetofend(TYPE, MEMBER), _Alignof(TYPE)))
1450c8c0f03SDave Hansen
1460c8c0f03SDave Hansen /*
1475aaeb5c0SIngo Molnar * We append the 'struct fpu' to the task_struct:
1480c8c0f03SDave Hansen */
fpu__init_task_struct_size(void)1495aaeb5c0SIngo Molnar static void __init fpu__init_task_struct_size(void)
1500c8c0f03SDave Hansen {
1510c8c0f03SDave Hansen int task_size = sizeof(struct task_struct);
1520c8c0f03SDave Hansen
1530c8c0f03SDave Hansen /*
1540c8c0f03SDave Hansen * Subtract off the static size of the register state.
1550c8c0f03SDave Hansen * It potentially has a bunch of padding.
1560c8c0f03SDave Hansen */
15787d0e5beSThomas Gleixner task_size -= sizeof(current->thread.fpu.__fpstate.regs);
1580c8c0f03SDave Hansen
1590c8c0f03SDave Hansen /*
1600c8c0f03SDave Hansen * Add back the dynamically-calculated register state
1610c8c0f03SDave Hansen * size.
1620c8c0f03SDave Hansen */
1632bd264bcSThomas Gleixner task_size += fpu_kernel_cfg.default_size;
1640c8c0f03SDave Hansen
1650c8c0f03SDave Hansen /*
1660c8c0f03SDave Hansen * We dynamically size 'struct fpu', so we require that
1670c8c0f03SDave Hansen * it be at the end of 'thread_struct' and that
1680c8c0f03SDave Hansen * 'thread_struct' be at the end of 'task_struct'. If
1690c8c0f03SDave Hansen * you hit a compile error here, check the structure to
1700c8c0f03SDave Hansen * see if something got added to the end.
1710c8c0f03SDave Hansen */
17287d0e5beSThomas Gleixner CHECK_MEMBER_AT_END_OF(struct fpu, __fpstate);
1730c8c0f03SDave Hansen CHECK_MEMBER_AT_END_OF(struct thread_struct, fpu);
1740c8c0f03SDave Hansen CHECK_MEMBER_AT_END_OF(struct task_struct, thread);
1750c8c0f03SDave Hansen
1765aaeb5c0SIngo Molnar arch_task_struct_size = task_size;
1770c8c0f03SDave Hansen }
1780c8c0f03SDave Hansen
17941e78410SIngo Molnar /*
180bf15a8cfSFenghua Yu * Set up the user and kernel xstate sizes based on the legacy FPU context size.
18141e78410SIngo Molnar *
18241e78410SIngo Molnar * We set this up first, and later it will be overwritten by
18341e78410SIngo Molnar * fpu__init_system_xstate() if the CPU knows about xstates.
18441e78410SIngo Molnar */
fpu__init_system_xstate_size_legacy(void)18532231879SIngo Molnar static void __init fpu__init_system_xstate_size_legacy(void)
1860c867537SIngo Molnar {
1872bd264bcSThomas Gleixner unsigned int size;
1882bd264bcSThomas Gleixner
1890c867537SIngo Molnar /*
1902bd264bcSThomas Gleixner * Note that the size configuration might be overwritten later
1912bd264bcSThomas Gleixner * during fpu__init_system_xstate().
1920c867537SIngo Molnar */
193c33f0a81SThomas Gleixner if (!cpu_feature_enabled(X86_FEATURE_FPU)) {
1942bd264bcSThomas Gleixner size = sizeof(struct swregs_state);
195c33f0a81SThomas Gleixner } else if (cpu_feature_enabled(X86_FEATURE_FXSR)) {
1962bd264bcSThomas Gleixner size = sizeof(struct fxregs_state);
197c33f0a81SThomas Gleixner fpu_user_cfg.legacy_features = XFEATURE_MASK_FPSSE;
198c33f0a81SThomas Gleixner } else {
1992bd264bcSThomas Gleixner size = sizeof(struct fregs_state);
200c33f0a81SThomas Gleixner fpu_user_cfg.legacy_features = XFEATURE_MASK_FP;
201c33f0a81SThomas Gleixner }
202a1141e0bSFenghua Yu
2032bd264bcSThomas Gleixner fpu_kernel_cfg.max_size = size;
2042bd264bcSThomas Gleixner fpu_kernel_cfg.default_size = size;
2052bd264bcSThomas Gleixner fpu_user_cfg.max_size = size;
2062bd264bcSThomas Gleixner fpu_user_cfg.default_size = size;
207248452ceSThomas Gleixner fpstate_reset(¤t->thread.fpu);
208248452ceSThomas Gleixner }
209248452ceSThomas Gleixner
210e35f6f14SIngo Molnar /*
211ae02679cSIngo Molnar * Called on the boot CPU once per system bootup, to set up the initial
212ae02679cSIngo Molnar * FPU state that is later cloned into all processes:
213e35f6f14SIngo Molnar */
fpu__init_system(void)2141f34bb2aSThomas Gleixner void __init fpu__init_system(void)
215e35f6f14SIngo Molnar {
21687d0e5beSThomas Gleixner fpstate_reset(¤t->thread.fpu);
2171f34bb2aSThomas Gleixner fpu__init_system_early_generic();
218dd863880SIngo Molnar
219ae02679cSIngo Molnar /*
220ae02679cSIngo Molnar * The FPU has to be operational for some of the
221ae02679cSIngo Molnar * later FPU init activities:
222ae02679cSIngo Molnar */
223e35f6f14SIngo Molnar fpu__init_cpu();
2240c867537SIngo Molnar
2257218e8b7SIngo Molnar fpu__init_system_generic();
2267638b74bSIngo Molnar fpu__init_system_xstate_size_legacy();
2272bd264bcSThomas Gleixner fpu__init_system_xstate(fpu_kernel_cfg.max_size);
2285aaeb5c0SIngo Molnar fpu__init_task_struct_size();
2290c867537SIngo Molnar }
230