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 = ¤t->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