xref: /openbmc/linux/arch/x86/kernel/fpu/init.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
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(&current->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(&current->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(&current->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