18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 1994 Linus Torvalds
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Pentium III FXSR, SSE support
68c2ecf20Sopenharmony_ci * General FPU state handling cleanups
78c2ecf20Sopenharmony_ci *	Gareth Hughes <gareth@valinux.com>, May 2000
88c2ecf20Sopenharmony_ci * x86-64 work by Andi Kleen 2002
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#ifndef _ASM_X86_FPU_INTERNAL_H
128c2ecf20Sopenharmony_ci#define _ASM_X86_FPU_INTERNAL_H
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/compat.h>
158c2ecf20Sopenharmony_ci#include <linux/sched.h>
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci#include <linux/mm.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <asm/user.h>
208c2ecf20Sopenharmony_ci#include <asm/fpu/api.h>
218c2ecf20Sopenharmony_ci#include <asm/fpu/xstate.h>
228c2ecf20Sopenharmony_ci#include <asm/fpu/xcr.h>
238c2ecf20Sopenharmony_ci#include <asm/cpufeature.h>
248c2ecf20Sopenharmony_ci#include <asm/trace/fpu.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci/*
278c2ecf20Sopenharmony_ci * High level FPU state handling functions:
288c2ecf20Sopenharmony_ci */
298c2ecf20Sopenharmony_ciextern void fpu__prepare_read(struct fpu *fpu);
308c2ecf20Sopenharmony_ciextern void fpu__prepare_write(struct fpu *fpu);
318c2ecf20Sopenharmony_ciextern void fpu__save(struct fpu *fpu);
328c2ecf20Sopenharmony_ciextern int  fpu__restore_sig(void __user *buf, int ia32_frame);
338c2ecf20Sopenharmony_ciextern void fpu__drop(struct fpu *fpu);
348c2ecf20Sopenharmony_ciextern int  fpu__copy(struct task_struct *dst, struct task_struct *src);
358c2ecf20Sopenharmony_ciextern void fpu__clear_user_states(struct fpu *fpu);
368c2ecf20Sopenharmony_ciextern void fpu__clear_all(struct fpu *fpu);
378c2ecf20Sopenharmony_ciextern int  fpu__exception_code(struct fpu *fpu, int trap_nr);
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci/*
408c2ecf20Sopenharmony_ci * Boot time FPU initialization functions:
418c2ecf20Sopenharmony_ci */
428c2ecf20Sopenharmony_ciextern void fpu__init_cpu(void);
438c2ecf20Sopenharmony_ciextern void fpu__init_system_xstate(void);
448c2ecf20Sopenharmony_ciextern void fpu__init_cpu_xstate(void);
458c2ecf20Sopenharmony_ciextern void fpu__init_system(void);
468c2ecf20Sopenharmony_ciextern void fpu__init_check_bugs(void);
478c2ecf20Sopenharmony_ciextern void fpu__resume_cpu(void);
488c2ecf20Sopenharmony_ciextern u64 fpu__get_supported_xfeatures_mask(void);
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/*
518c2ecf20Sopenharmony_ci * Debugging facility:
528c2ecf20Sopenharmony_ci */
538c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_DEBUG_FPU
548c2ecf20Sopenharmony_ci# define WARN_ON_FPU(x) WARN_ON_ONCE(x)
558c2ecf20Sopenharmony_ci#else
568c2ecf20Sopenharmony_ci# define WARN_ON_FPU(x) ({ (void)(x); 0; })
578c2ecf20Sopenharmony_ci#endif
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci/*
608c2ecf20Sopenharmony_ci * FPU related CPU feature flag helper routines:
618c2ecf20Sopenharmony_ci */
628c2ecf20Sopenharmony_cistatic __always_inline __pure bool use_xsaveopt(void)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	return static_cpu_has(X86_FEATURE_XSAVEOPT);
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic __always_inline __pure bool use_xsave(void)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	return static_cpu_has(X86_FEATURE_XSAVE);
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic __always_inline __pure bool use_fxsr(void)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	return static_cpu_has(X86_FEATURE_FXSR);
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci/*
788c2ecf20Sopenharmony_ci * fpstate handling functions:
798c2ecf20Sopenharmony_ci */
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ciextern union fpregs_state init_fpstate;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ciextern void fpstate_init(union fpregs_state *state);
848c2ecf20Sopenharmony_ci#ifdef CONFIG_MATH_EMULATION
858c2ecf20Sopenharmony_ciextern void fpstate_init_soft(struct swregs_state *soft);
868c2ecf20Sopenharmony_ci#else
878c2ecf20Sopenharmony_cistatic inline void fpstate_init_soft(struct swregs_state *soft) {}
888c2ecf20Sopenharmony_ci#endif
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic inline void fpstate_init_xstate(struct xregs_state *xsave)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	/*
938c2ecf20Sopenharmony_ci	 * XRSTORS requires these bits set in xcomp_bv, or it will
948c2ecf20Sopenharmony_ci	 * trigger #GP:
958c2ecf20Sopenharmony_ci	 */
968c2ecf20Sopenharmony_ci	xsave->header.xcomp_bv = XCOMP_BV_COMPACTED_FORMAT | xfeatures_mask_all;
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic inline void fpstate_init_fxstate(struct fxregs_state *fx)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	fx->cwd = 0x37f;
1028c2ecf20Sopenharmony_ci	fx->mxcsr = MXCSR_DEFAULT;
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ciextern void fpstate_sanitize_xstate(struct fpu *fpu);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci/* Returns 0 or the negated trap number, which results in -EFAULT for #PF */
1078c2ecf20Sopenharmony_ci#define user_insn(insn, output, input...)				\
1088c2ecf20Sopenharmony_ci({									\
1098c2ecf20Sopenharmony_ci	int err;							\
1108c2ecf20Sopenharmony_ci									\
1118c2ecf20Sopenharmony_ci	might_fault();							\
1128c2ecf20Sopenharmony_ci									\
1138c2ecf20Sopenharmony_ci	asm volatile(ASM_STAC "\n"					\
1148c2ecf20Sopenharmony_ci		     "1: " #insn "\n"					\
1158c2ecf20Sopenharmony_ci		     "2: " ASM_CLAC "\n"				\
1168c2ecf20Sopenharmony_ci		     ".section .fixup,\"ax\"\n"				\
1178c2ecf20Sopenharmony_ci		     "3:  negl %%eax\n"					\
1188c2ecf20Sopenharmony_ci		     "    jmp  2b\n"					\
1198c2ecf20Sopenharmony_ci		     ".previous\n"					\
1208c2ecf20Sopenharmony_ci		     _ASM_EXTABLE_FAULT(1b, 3b)				\
1218c2ecf20Sopenharmony_ci		     : [err] "=a" (err), output				\
1228c2ecf20Sopenharmony_ci		     : "0"(0), input);					\
1238c2ecf20Sopenharmony_ci	err;								\
1248c2ecf20Sopenharmony_ci})
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci#define kernel_insn_err(insn, output, input...)				\
1278c2ecf20Sopenharmony_ci({									\
1288c2ecf20Sopenharmony_ci	int err;							\
1298c2ecf20Sopenharmony_ci	asm volatile("1:" #insn "\n\t"					\
1308c2ecf20Sopenharmony_ci		     "2:\n"						\
1318c2ecf20Sopenharmony_ci		     ".section .fixup,\"ax\"\n"				\
1328c2ecf20Sopenharmony_ci		     "3:  movl $-1,%[err]\n"				\
1338c2ecf20Sopenharmony_ci		     "    jmp  2b\n"					\
1348c2ecf20Sopenharmony_ci		     ".previous\n"					\
1358c2ecf20Sopenharmony_ci		     _ASM_EXTABLE(1b, 3b)				\
1368c2ecf20Sopenharmony_ci		     : [err] "=r" (err), output				\
1378c2ecf20Sopenharmony_ci		     : "0"(0), input);					\
1388c2ecf20Sopenharmony_ci	err;								\
1398c2ecf20Sopenharmony_ci})
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci#define kernel_insn(insn, output, input...)				\
1428c2ecf20Sopenharmony_ci	asm volatile("1:" #insn "\n\t"					\
1438c2ecf20Sopenharmony_ci		     "2:\n"						\
1448c2ecf20Sopenharmony_ci		     _ASM_EXTABLE_HANDLE(1b, 2b, ex_handler_fprestore)	\
1458c2ecf20Sopenharmony_ci		     : output : input)
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic inline int copy_fregs_to_user(struct fregs_state __user *fx)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	return user_insn(fnsave %[fx]; fwait,  [fx] "=m" (*fx), "m" (*fx));
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistatic inline int copy_fxregs_to_user(struct fxregs_state __user *fx)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_X86_32))
1558c2ecf20Sopenharmony_ci		return user_insn(fxsave %[fx], [fx] "=m" (*fx), "m" (*fx));
1568c2ecf20Sopenharmony_ci	else
1578c2ecf20Sopenharmony_ci		return user_insn(fxsaveq %[fx], [fx] "=m" (*fx), "m" (*fx));
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistatic inline void copy_kernel_to_fxregs(struct fxregs_state *fx)
1628c2ecf20Sopenharmony_ci{
1638c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_X86_32))
1648c2ecf20Sopenharmony_ci		kernel_insn(fxrstor %[fx], "=m" (*fx), [fx] "m" (*fx));
1658c2ecf20Sopenharmony_ci	else
1668c2ecf20Sopenharmony_ci		kernel_insn(fxrstorq %[fx], "=m" (*fx), [fx] "m" (*fx));
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic inline int copy_kernel_to_fxregs_err(struct fxregs_state *fx)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_X86_32))
1728c2ecf20Sopenharmony_ci		return kernel_insn_err(fxrstor %[fx], "=m" (*fx), [fx] "m" (*fx));
1738c2ecf20Sopenharmony_ci	else
1748c2ecf20Sopenharmony_ci		return kernel_insn_err(fxrstorq %[fx], "=m" (*fx), [fx] "m" (*fx));
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic inline int copy_user_to_fxregs(struct fxregs_state __user *fx)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_X86_32))
1808c2ecf20Sopenharmony_ci		return user_insn(fxrstor %[fx], "=m" (*fx), [fx] "m" (*fx));
1818c2ecf20Sopenharmony_ci	else
1828c2ecf20Sopenharmony_ci		return user_insn(fxrstorq %[fx], "=m" (*fx), [fx] "m" (*fx));
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic inline void copy_kernel_to_fregs(struct fregs_state *fx)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	kernel_insn(frstor %[fx], "=m" (*fx), [fx] "m" (*fx));
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistatic inline int copy_kernel_to_fregs_err(struct fregs_state *fx)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	return kernel_insn_err(frstor %[fx], "=m" (*fx), [fx] "m" (*fx));
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistatic inline int copy_user_to_fregs(struct fregs_state __user *fx)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	return user_insn(frstor %[fx], "=m" (*fx), [fx] "m" (*fx));
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic inline void copy_fxregs_to_kernel(struct fpu *fpu)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_X86_32))
2038c2ecf20Sopenharmony_ci		asm volatile( "fxsave %[fx]" : [fx] "=m" (fpu->state.fxsave));
2048c2ecf20Sopenharmony_ci	else
2058c2ecf20Sopenharmony_ci		asm volatile("fxsaveq %[fx]" : [fx] "=m" (fpu->state.fxsave));
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic inline void fxsave(struct fxregs_state *fx)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_X86_32))
2118c2ecf20Sopenharmony_ci		asm volatile( "fxsave %[fx]" : [fx] "=m" (*fx));
2128c2ecf20Sopenharmony_ci	else
2138c2ecf20Sopenharmony_ci		asm volatile("fxsaveq %[fx]" : [fx] "=m" (*fx));
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci/* These macros all use (%edi)/(%rdi) as the single memory argument. */
2178c2ecf20Sopenharmony_ci#define XSAVE		".byte " REX_PREFIX "0x0f,0xae,0x27"
2188c2ecf20Sopenharmony_ci#define XSAVEOPT	".byte " REX_PREFIX "0x0f,0xae,0x37"
2198c2ecf20Sopenharmony_ci#define XSAVES		".byte " REX_PREFIX "0x0f,0xc7,0x2f"
2208c2ecf20Sopenharmony_ci#define XRSTOR		".byte " REX_PREFIX "0x0f,0xae,0x2f"
2218c2ecf20Sopenharmony_ci#define XRSTORS		".byte " REX_PREFIX "0x0f,0xc7,0x1f"
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci/*
2248c2ecf20Sopenharmony_ci * After this @err contains 0 on success or the negated trap number when
2258c2ecf20Sopenharmony_ci * the operation raises an exception. For faults this results in -EFAULT.
2268c2ecf20Sopenharmony_ci */
2278c2ecf20Sopenharmony_ci#define XSTATE_OP(op, st, lmask, hmask, err)				\
2288c2ecf20Sopenharmony_ci	asm volatile("1:" op "\n\t"					\
2298c2ecf20Sopenharmony_ci		     "xor %[err], %[err]\n"				\
2308c2ecf20Sopenharmony_ci		     "2:\n\t"						\
2318c2ecf20Sopenharmony_ci		     ".pushsection .fixup,\"ax\"\n\t"			\
2328c2ecf20Sopenharmony_ci		     "3: negl %%eax\n\t"				\
2338c2ecf20Sopenharmony_ci		     "jmp 2b\n\t"					\
2348c2ecf20Sopenharmony_ci		     ".popsection\n\t"					\
2358c2ecf20Sopenharmony_ci		     _ASM_EXTABLE_FAULT(1b, 3b)				\
2368c2ecf20Sopenharmony_ci		     : [err] "=a" (err)					\
2378c2ecf20Sopenharmony_ci		     : "D" (st), "m" (*st), "a" (lmask), "d" (hmask)	\
2388c2ecf20Sopenharmony_ci		     : "memory")
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci/*
2418c2ecf20Sopenharmony_ci * If XSAVES is enabled, it replaces XSAVEOPT because it supports a compact
2428c2ecf20Sopenharmony_ci * format and supervisor states in addition to modified optimization in
2438c2ecf20Sopenharmony_ci * XSAVEOPT.
2448c2ecf20Sopenharmony_ci *
2458c2ecf20Sopenharmony_ci * Otherwise, if XSAVEOPT is enabled, XSAVEOPT replaces XSAVE because XSAVEOPT
2468c2ecf20Sopenharmony_ci * supports modified optimization which is not supported by XSAVE.
2478c2ecf20Sopenharmony_ci *
2488c2ecf20Sopenharmony_ci * We use XSAVE as a fallback.
2498c2ecf20Sopenharmony_ci *
2508c2ecf20Sopenharmony_ci * The 661 label is defined in the ALTERNATIVE* macros as the address of the
2518c2ecf20Sopenharmony_ci * original instruction which gets replaced. We need to use it here as the
2528c2ecf20Sopenharmony_ci * address of the instruction where we might get an exception at.
2538c2ecf20Sopenharmony_ci */
2548c2ecf20Sopenharmony_ci#define XSTATE_XSAVE(st, lmask, hmask, err)				\
2558c2ecf20Sopenharmony_ci	asm volatile(ALTERNATIVE_2(XSAVE,				\
2568c2ecf20Sopenharmony_ci				   XSAVEOPT, X86_FEATURE_XSAVEOPT,	\
2578c2ecf20Sopenharmony_ci				   XSAVES,   X86_FEATURE_XSAVES)	\
2588c2ecf20Sopenharmony_ci		     "\n"						\
2598c2ecf20Sopenharmony_ci		     "xor %[err], %[err]\n"				\
2608c2ecf20Sopenharmony_ci		     "3:\n"						\
2618c2ecf20Sopenharmony_ci		     ".pushsection .fixup,\"ax\"\n"			\
2628c2ecf20Sopenharmony_ci		     "4: movl $-2, %[err]\n"				\
2638c2ecf20Sopenharmony_ci		     "jmp 3b\n"						\
2648c2ecf20Sopenharmony_ci		     ".popsection\n"					\
2658c2ecf20Sopenharmony_ci		     _ASM_EXTABLE(661b, 4b)				\
2668c2ecf20Sopenharmony_ci		     : [err] "=r" (err)					\
2678c2ecf20Sopenharmony_ci		     : "D" (st), "m" (*st), "a" (lmask), "d" (hmask)	\
2688c2ecf20Sopenharmony_ci		     : "memory")
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci/*
2718c2ecf20Sopenharmony_ci * Use XRSTORS to restore context if it is enabled. XRSTORS supports compact
2728c2ecf20Sopenharmony_ci * XSAVE area format.
2738c2ecf20Sopenharmony_ci */
2748c2ecf20Sopenharmony_ci#define XSTATE_XRESTORE(st, lmask, hmask)				\
2758c2ecf20Sopenharmony_ci	asm volatile(ALTERNATIVE(XRSTOR,				\
2768c2ecf20Sopenharmony_ci				 XRSTORS, X86_FEATURE_XSAVES)		\
2778c2ecf20Sopenharmony_ci		     "\n"						\
2788c2ecf20Sopenharmony_ci		     "3:\n"						\
2798c2ecf20Sopenharmony_ci		     _ASM_EXTABLE_HANDLE(661b, 3b, ex_handler_fprestore)\
2808c2ecf20Sopenharmony_ci		     :							\
2818c2ecf20Sopenharmony_ci		     : "D" (st), "m" (*st), "a" (lmask), "d" (hmask)	\
2828c2ecf20Sopenharmony_ci		     : "memory")
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci/*
2858c2ecf20Sopenharmony_ci * This function is called only during boot time when x86 caps are not set
2868c2ecf20Sopenharmony_ci * up and alternative can not be used yet.
2878c2ecf20Sopenharmony_ci */
2888c2ecf20Sopenharmony_cistatic inline void copy_kernel_to_xregs_booting(struct xregs_state *xstate)
2898c2ecf20Sopenharmony_ci{
2908c2ecf20Sopenharmony_ci	u64 mask = -1;
2918c2ecf20Sopenharmony_ci	u32 lmask = mask;
2928c2ecf20Sopenharmony_ci	u32 hmask = mask >> 32;
2938c2ecf20Sopenharmony_ci	int err;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	WARN_ON(system_state != SYSTEM_BOOTING);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	if (boot_cpu_has(X86_FEATURE_XSAVES))
2988c2ecf20Sopenharmony_ci		XSTATE_OP(XRSTORS, xstate, lmask, hmask, err);
2998c2ecf20Sopenharmony_ci	else
3008c2ecf20Sopenharmony_ci		XSTATE_OP(XRSTOR, xstate, lmask, hmask, err);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	/*
3038c2ecf20Sopenharmony_ci	 * We should never fault when copying from a kernel buffer, and the FPU
3048c2ecf20Sopenharmony_ci	 * state we set at boot time should be valid.
3058c2ecf20Sopenharmony_ci	 */
3068c2ecf20Sopenharmony_ci	WARN_ON_FPU(err);
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci/*
3108c2ecf20Sopenharmony_ci * Save processor xstate to xsave area.
3118c2ecf20Sopenharmony_ci */
3128c2ecf20Sopenharmony_cistatic inline void copy_xregs_to_kernel(struct xregs_state *xstate)
3138c2ecf20Sopenharmony_ci{
3148c2ecf20Sopenharmony_ci	u64 mask = xfeatures_mask_all;
3158c2ecf20Sopenharmony_ci	u32 lmask = mask;
3168c2ecf20Sopenharmony_ci	u32 hmask = mask >> 32;
3178c2ecf20Sopenharmony_ci	int err;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	WARN_ON_FPU(!alternatives_patched);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	XSTATE_XSAVE(xstate, lmask, hmask, err);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	/* We should never fault when copying to a kernel buffer: */
3248c2ecf20Sopenharmony_ci	WARN_ON_FPU(err);
3258c2ecf20Sopenharmony_ci}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci/*
3288c2ecf20Sopenharmony_ci * Restore processor xstate from xsave area.
3298c2ecf20Sopenharmony_ci */
3308c2ecf20Sopenharmony_cistatic inline void copy_kernel_to_xregs(struct xregs_state *xstate, u64 mask)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	u32 lmask = mask;
3338c2ecf20Sopenharmony_ci	u32 hmask = mask >> 32;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	XSTATE_XRESTORE(xstate, lmask, hmask);
3368c2ecf20Sopenharmony_ci}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci/*
3398c2ecf20Sopenharmony_ci * Save xstate to user space xsave area.
3408c2ecf20Sopenharmony_ci *
3418c2ecf20Sopenharmony_ci * We don't use modified optimization because xrstor/xrstors might track
3428c2ecf20Sopenharmony_ci * a different application.
3438c2ecf20Sopenharmony_ci *
3448c2ecf20Sopenharmony_ci * We don't use compacted format xsave area for
3458c2ecf20Sopenharmony_ci * backward compatibility for old applications which don't understand
3468c2ecf20Sopenharmony_ci * compacted format of xsave area.
3478c2ecf20Sopenharmony_ci */
3488c2ecf20Sopenharmony_cistatic inline int copy_xregs_to_user(struct xregs_state __user *buf)
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	u64 mask = xfeatures_mask_user();
3518c2ecf20Sopenharmony_ci	u32 lmask = mask;
3528c2ecf20Sopenharmony_ci	u32 hmask = mask >> 32;
3538c2ecf20Sopenharmony_ci	int err;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	/*
3568c2ecf20Sopenharmony_ci	 * Clear the xsave header first, so that reserved fields are
3578c2ecf20Sopenharmony_ci	 * initialized to zero.
3588c2ecf20Sopenharmony_ci	 */
3598c2ecf20Sopenharmony_ci	err = __clear_user(&buf->header, sizeof(buf->header));
3608c2ecf20Sopenharmony_ci	if (unlikely(err))
3618c2ecf20Sopenharmony_ci		return -EFAULT;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	stac();
3648c2ecf20Sopenharmony_ci	XSTATE_OP(XSAVE, buf, lmask, hmask, err);
3658c2ecf20Sopenharmony_ci	clac();
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	return err;
3688c2ecf20Sopenharmony_ci}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci/*
3718c2ecf20Sopenharmony_ci * Restore xstate from user space xsave area.
3728c2ecf20Sopenharmony_ci */
3738c2ecf20Sopenharmony_cistatic inline int copy_user_to_xregs(struct xregs_state __user *buf, u64 mask)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	struct xregs_state *xstate = ((__force struct xregs_state *)buf);
3768c2ecf20Sopenharmony_ci	u32 lmask = mask;
3778c2ecf20Sopenharmony_ci	u32 hmask = mask >> 32;
3788c2ecf20Sopenharmony_ci	int err;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	stac();
3818c2ecf20Sopenharmony_ci	XSTATE_OP(XRSTOR, xstate, lmask, hmask, err);
3828c2ecf20Sopenharmony_ci	clac();
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	return err;
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci/*
3888c2ecf20Sopenharmony_ci * Restore xstate from kernel space xsave area, return an error code instead of
3898c2ecf20Sopenharmony_ci * an exception.
3908c2ecf20Sopenharmony_ci */
3918c2ecf20Sopenharmony_cistatic inline int copy_kernel_to_xregs_err(struct xregs_state *xstate, u64 mask)
3928c2ecf20Sopenharmony_ci{
3938c2ecf20Sopenharmony_ci	u32 lmask = mask;
3948c2ecf20Sopenharmony_ci	u32 hmask = mask >> 32;
3958c2ecf20Sopenharmony_ci	int err;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	if (static_cpu_has(X86_FEATURE_XSAVES))
3988c2ecf20Sopenharmony_ci		XSTATE_OP(XRSTORS, xstate, lmask, hmask, err);
3998c2ecf20Sopenharmony_ci	else
4008c2ecf20Sopenharmony_ci		XSTATE_OP(XRSTOR, xstate, lmask, hmask, err);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	return err;
4038c2ecf20Sopenharmony_ci}
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ciextern int copy_fpregs_to_fpstate(struct fpu *fpu);
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cistatic inline void __copy_kernel_to_fpregs(union fpregs_state *fpstate, u64 mask)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	if (use_xsave()) {
4108c2ecf20Sopenharmony_ci		copy_kernel_to_xregs(&fpstate->xsave, mask);
4118c2ecf20Sopenharmony_ci	} else {
4128c2ecf20Sopenharmony_ci		if (use_fxsr())
4138c2ecf20Sopenharmony_ci			copy_kernel_to_fxregs(&fpstate->fxsave);
4148c2ecf20Sopenharmony_ci		else
4158c2ecf20Sopenharmony_ci			copy_kernel_to_fregs(&fpstate->fsave);
4168c2ecf20Sopenharmony_ci	}
4178c2ecf20Sopenharmony_ci}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_cistatic inline void copy_kernel_to_fpregs(union fpregs_state *fpstate)
4208c2ecf20Sopenharmony_ci{
4218c2ecf20Sopenharmony_ci	/*
4228c2ecf20Sopenharmony_ci	 * AMD K7/K8 CPUs don't save/restore FDP/FIP/FOP unless an exception is
4238c2ecf20Sopenharmony_ci	 * pending. Clear the x87 state here by setting it to fixed values.
4248c2ecf20Sopenharmony_ci	 * "m" is a random variable that should be in L1.
4258c2ecf20Sopenharmony_ci	 */
4268c2ecf20Sopenharmony_ci	if (unlikely(static_cpu_has_bug(X86_BUG_FXSAVE_LEAK))) {
4278c2ecf20Sopenharmony_ci		asm volatile(
4288c2ecf20Sopenharmony_ci			"fnclex\n\t"
4298c2ecf20Sopenharmony_ci			"emms\n\t"
4308c2ecf20Sopenharmony_ci			"fildl %P[addr]"	/* set F?P to defined value */
4318c2ecf20Sopenharmony_ci			: : [addr] "m" (fpstate));
4328c2ecf20Sopenharmony_ci	}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	__copy_kernel_to_fpregs(fpstate, -1);
4358c2ecf20Sopenharmony_ci}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ciextern int copy_fpstate_to_sigframe(void __user *buf, void __user *fp, int size);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci/*
4408c2ecf20Sopenharmony_ci * FPU context switch related helper methods:
4418c2ecf20Sopenharmony_ci */
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ciDECLARE_PER_CPU(struct fpu *, fpu_fpregs_owner_ctx);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci/*
4468c2ecf20Sopenharmony_ci * The in-register FPU state for an FPU context on a CPU is assumed to be
4478c2ecf20Sopenharmony_ci * valid if the fpu->last_cpu matches the CPU, and the fpu_fpregs_owner_ctx
4488c2ecf20Sopenharmony_ci * matches the FPU.
4498c2ecf20Sopenharmony_ci *
4508c2ecf20Sopenharmony_ci * If the FPU register state is valid, the kernel can skip restoring the
4518c2ecf20Sopenharmony_ci * FPU state from memory.
4528c2ecf20Sopenharmony_ci *
4538c2ecf20Sopenharmony_ci * Any code that clobbers the FPU registers or updates the in-memory
4548c2ecf20Sopenharmony_ci * FPU state for a task MUST let the rest of the kernel know that the
4558c2ecf20Sopenharmony_ci * FPU registers are no longer valid for this task.
4568c2ecf20Sopenharmony_ci *
4578c2ecf20Sopenharmony_ci * Either one of these invalidation functions is enough. Invalidate
4588c2ecf20Sopenharmony_ci * a resource you control: CPU if using the CPU for something else
4598c2ecf20Sopenharmony_ci * (with preemption disabled), FPU for the current task, or a task that
4608c2ecf20Sopenharmony_ci * is prevented from running by the current task.
4618c2ecf20Sopenharmony_ci */
4628c2ecf20Sopenharmony_cistatic inline void __cpu_invalidate_fpregs_state(void)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	__this_cpu_write(fpu_fpregs_owner_ctx, NULL);
4658c2ecf20Sopenharmony_ci}
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_cistatic inline void __fpu_invalidate_fpregs_state(struct fpu *fpu)
4688c2ecf20Sopenharmony_ci{
4698c2ecf20Sopenharmony_ci	fpu->last_cpu = -1;
4708c2ecf20Sopenharmony_ci}
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_cistatic inline int fpregs_state_valid(struct fpu *fpu, unsigned int cpu)
4738c2ecf20Sopenharmony_ci{
4748c2ecf20Sopenharmony_ci	return fpu == this_cpu_read(fpu_fpregs_owner_ctx) && cpu == fpu->last_cpu;
4758c2ecf20Sopenharmony_ci}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci/*
4788c2ecf20Sopenharmony_ci * These generally need preemption protection to work,
4798c2ecf20Sopenharmony_ci * do try to avoid using these on their own:
4808c2ecf20Sopenharmony_ci */
4818c2ecf20Sopenharmony_cistatic inline void fpregs_deactivate(struct fpu *fpu)
4828c2ecf20Sopenharmony_ci{
4838c2ecf20Sopenharmony_ci	this_cpu_write(fpu_fpregs_owner_ctx, NULL);
4848c2ecf20Sopenharmony_ci	trace_x86_fpu_regs_deactivated(fpu);
4858c2ecf20Sopenharmony_ci}
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_cistatic inline void fpregs_activate(struct fpu *fpu)
4888c2ecf20Sopenharmony_ci{
4898c2ecf20Sopenharmony_ci	this_cpu_write(fpu_fpregs_owner_ctx, fpu);
4908c2ecf20Sopenharmony_ci	trace_x86_fpu_regs_activated(fpu);
4918c2ecf20Sopenharmony_ci}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci/*
4948c2ecf20Sopenharmony_ci * Internal helper, do not use directly. Use switch_fpu_return() instead.
4958c2ecf20Sopenharmony_ci */
4968c2ecf20Sopenharmony_cistatic inline void __fpregs_load_activate(void)
4978c2ecf20Sopenharmony_ci{
4988c2ecf20Sopenharmony_ci	struct fpu *fpu = &current->thread.fpu;
4998c2ecf20Sopenharmony_ci	int cpu = smp_processor_id();
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(current->flags & PF_KTHREAD))
5028c2ecf20Sopenharmony_ci		return;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	if (!fpregs_state_valid(fpu, cpu)) {
5058c2ecf20Sopenharmony_ci		copy_kernel_to_fpregs(&fpu->state);
5068c2ecf20Sopenharmony_ci		fpregs_activate(fpu);
5078c2ecf20Sopenharmony_ci		fpu->last_cpu = cpu;
5088c2ecf20Sopenharmony_ci	}
5098c2ecf20Sopenharmony_ci	clear_thread_flag(TIF_NEED_FPU_LOAD);
5108c2ecf20Sopenharmony_ci}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci/*
5138c2ecf20Sopenharmony_ci * FPU state switching for scheduling.
5148c2ecf20Sopenharmony_ci *
5158c2ecf20Sopenharmony_ci * This is a two-stage process:
5168c2ecf20Sopenharmony_ci *
5178c2ecf20Sopenharmony_ci *  - switch_fpu_prepare() saves the old state.
5188c2ecf20Sopenharmony_ci *    This is done within the context of the old process.
5198c2ecf20Sopenharmony_ci *
5208c2ecf20Sopenharmony_ci *  - switch_fpu_finish() sets TIF_NEED_FPU_LOAD; the floating point state
5218c2ecf20Sopenharmony_ci *    will get loaded on return to userspace, or when the kernel needs it.
5228c2ecf20Sopenharmony_ci *
5238c2ecf20Sopenharmony_ci * If TIF_NEED_FPU_LOAD is cleared then the CPU's FPU registers
5248c2ecf20Sopenharmony_ci * are saved in the current thread's FPU register state.
5258c2ecf20Sopenharmony_ci *
5268c2ecf20Sopenharmony_ci * If TIF_NEED_FPU_LOAD is set then CPU's FPU registers may not
5278c2ecf20Sopenharmony_ci * hold current()'s FPU registers. It is required to load the
5288c2ecf20Sopenharmony_ci * registers before returning to userland or using the content
5298c2ecf20Sopenharmony_ci * otherwise.
5308c2ecf20Sopenharmony_ci *
5318c2ecf20Sopenharmony_ci * The FPU context is only stored/restored for a user task and
5328c2ecf20Sopenharmony_ci * PF_KTHREAD is used to distinguish between kernel and user threads.
5338c2ecf20Sopenharmony_ci */
5348c2ecf20Sopenharmony_cistatic inline void switch_fpu_prepare(struct task_struct *prev, int cpu)
5358c2ecf20Sopenharmony_ci{
5368c2ecf20Sopenharmony_ci	struct fpu *old_fpu = &prev->thread.fpu;
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	if (static_cpu_has(X86_FEATURE_FPU) && !(prev->flags & PF_KTHREAD)) {
5398c2ecf20Sopenharmony_ci		if (!copy_fpregs_to_fpstate(old_fpu))
5408c2ecf20Sopenharmony_ci			old_fpu->last_cpu = -1;
5418c2ecf20Sopenharmony_ci		else
5428c2ecf20Sopenharmony_ci			old_fpu->last_cpu = cpu;
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci		/* But leave fpu_fpregs_owner_ctx! */
5458c2ecf20Sopenharmony_ci		trace_x86_fpu_regs_deactivated(old_fpu);
5468c2ecf20Sopenharmony_ci	}
5478c2ecf20Sopenharmony_ci}
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci/*
5508c2ecf20Sopenharmony_ci * Misc helper functions:
5518c2ecf20Sopenharmony_ci */
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci/*
5548c2ecf20Sopenharmony_ci * Load PKRU from the FPU context if available. Delay loading of the
5558c2ecf20Sopenharmony_ci * complete FPU state until the return to userland.
5568c2ecf20Sopenharmony_ci */
5578c2ecf20Sopenharmony_cistatic inline void switch_fpu_finish(struct task_struct *next)
5588c2ecf20Sopenharmony_ci{
5598c2ecf20Sopenharmony_ci	u32 pkru_val = init_pkru_value;
5608c2ecf20Sopenharmony_ci	struct pkru_state *pk;
5618c2ecf20Sopenharmony_ci	struct fpu *next_fpu = &next->thread.fpu;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	if (!static_cpu_has(X86_FEATURE_FPU))
5648c2ecf20Sopenharmony_ci		return;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	set_thread_flag(TIF_NEED_FPU_LOAD);
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	if (!cpu_feature_enabled(X86_FEATURE_OSPKE))
5698c2ecf20Sopenharmony_ci		return;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	/*
5728c2ecf20Sopenharmony_ci	 * PKRU state is switched eagerly because it needs to be valid before we
5738c2ecf20Sopenharmony_ci	 * return to userland e.g. for a copy_to_user() operation.
5748c2ecf20Sopenharmony_ci	 */
5758c2ecf20Sopenharmony_ci	if (!(next->flags & PF_KTHREAD)) {
5768c2ecf20Sopenharmony_ci		/*
5778c2ecf20Sopenharmony_ci		 * If the PKRU bit in xsave.header.xfeatures is not set,
5788c2ecf20Sopenharmony_ci		 * then the PKRU component was in init state, which means
5798c2ecf20Sopenharmony_ci		 * XRSTOR will set PKRU to 0. If the bit is not set then
5808c2ecf20Sopenharmony_ci		 * get_xsave_addr() will return NULL because the PKRU value
5818c2ecf20Sopenharmony_ci		 * in memory is not valid. This means pkru_val has to be
5828c2ecf20Sopenharmony_ci		 * set to 0 and not to init_pkru_value.
5838c2ecf20Sopenharmony_ci		 */
5848c2ecf20Sopenharmony_ci		pk = get_xsave_addr(&next_fpu->state.xsave, XFEATURE_PKRU);
5858c2ecf20Sopenharmony_ci		pkru_val = pk ? pk->pkru : 0;
5868c2ecf20Sopenharmony_ci	}
5878c2ecf20Sopenharmony_ci	__write_pkru(pkru_val);
5888c2ecf20Sopenharmony_ci}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci#endif /* _ASM_X86_FPU_INTERNAL_H */
591