1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 20c306bcfSIngo Molnar /* 30c306bcfSIngo Molnar * FPU register's regset abstraction, for ptrace, core dumps, etc. 40c306bcfSIngo Molnar */ 543be46e8SThomas Gleixner #include <linux/sched/task_stack.h> 643be46e8SThomas Gleixner #include <linux/vmalloc.h> 743be46e8SThomas Gleixner 80c306bcfSIngo Molnar #include <asm/fpu/internal.h> 90c306bcfSIngo Molnar #include <asm/fpu/signal.h> 100c306bcfSIngo Molnar #include <asm/fpu/regset.h> 1191c3dba7SYu-cheng Yu #include <asm/fpu/xstate.h> 120c306bcfSIngo Molnar 130c306bcfSIngo Molnar /* 140c306bcfSIngo Molnar * The xstateregs_active() routine is the same as the regset_fpregs_active() routine, 150c306bcfSIngo Molnar * as the "regset->n" for the xstate regset will be updated based on the feature 166a6256f9SAdam Buchbinder * capabilities supported by the xsave. 170c306bcfSIngo Molnar */ 180c306bcfSIngo Molnar int regset_fpregs_active(struct task_struct *target, const struct user_regset *regset) 190c306bcfSIngo Molnar { 202722146eSSebastian Andrzej Siewior return regset->n; 210c306bcfSIngo Molnar } 220c306bcfSIngo Molnar 230c306bcfSIngo Molnar int regset_xregset_fpregs_active(struct task_struct *target, const struct user_regset *regset) 240c306bcfSIngo Molnar { 252722146eSSebastian Andrzej Siewior if (boot_cpu_has(X86_FEATURE_FXSR)) 2601f8fd73SBorislav Petkov return regset->n; 2701f8fd73SBorislav Petkov else 2801f8fd73SBorislav Petkov return 0; 290c306bcfSIngo Molnar } 300c306bcfSIngo Molnar 310c306bcfSIngo Molnar int xfpregs_get(struct task_struct *target, const struct user_regset *regset, 320557d64dSAl Viro struct membuf to) 330c306bcfSIngo Molnar { 340c306bcfSIngo Molnar struct fpu *fpu = &target->thread.fpu; 350c306bcfSIngo Molnar 3601f8fd73SBorislav Petkov if (!boot_cpu_has(X86_FEATURE_FXSR)) 370c306bcfSIngo Molnar return -ENODEV; 380c306bcfSIngo Molnar 39369a036dSIngo Molnar fpu__prepare_read(fpu); 400c306bcfSIngo Molnar fpstate_sanitize_xstate(fpu); 410c306bcfSIngo Molnar 420557d64dSAl Viro return membuf_write(&to, &fpu->state.fxsave, sizeof(struct fxregs_state)); 430c306bcfSIngo Molnar } 440c306bcfSIngo Molnar 450c306bcfSIngo Molnar int xfpregs_set(struct task_struct *target, const struct user_regset *regset, 460c306bcfSIngo Molnar unsigned int pos, unsigned int count, 470c306bcfSIngo Molnar const void *kbuf, const void __user *ubuf) 480c306bcfSIngo Molnar { 490c306bcfSIngo Molnar struct fpu *fpu = &target->thread.fpu; 500c306bcfSIngo Molnar int ret; 510c306bcfSIngo Molnar 5201f8fd73SBorislav Petkov if (!boot_cpu_has(X86_FEATURE_FXSR)) 530c306bcfSIngo Molnar return -ENODEV; 540c306bcfSIngo Molnar 55369a036dSIngo Molnar fpu__prepare_write(fpu); 560c306bcfSIngo Molnar fpstate_sanitize_xstate(fpu); 570c306bcfSIngo Molnar 580c306bcfSIngo Molnar ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, 590c306bcfSIngo Molnar &fpu->state.fxsave, 0, -1); 600c306bcfSIngo Molnar 610c306bcfSIngo Molnar /* 620c306bcfSIngo Molnar * mxcsr reserved bits must be masked to zero for security reasons. 630c306bcfSIngo Molnar */ 640c306bcfSIngo Molnar fpu->state.fxsave.mxcsr &= mxcsr_feature_mask; 650c306bcfSIngo Molnar 660c306bcfSIngo Molnar /* 670c306bcfSIngo Molnar * update the header bits in the xsave header, indicating the 680c306bcfSIngo Molnar * presence of FP and SSE state. 690c306bcfSIngo Molnar */ 70d366bf7eSBorislav Petkov if (boot_cpu_has(X86_FEATURE_XSAVE)) 71d91cab78SDave Hansen fpu->state.xsave.header.xfeatures |= XFEATURE_MASK_FPSSE; 720c306bcfSIngo Molnar 730c306bcfSIngo Molnar return ret; 740c306bcfSIngo Molnar } 750c306bcfSIngo Molnar 760c306bcfSIngo Molnar int xstateregs_get(struct task_struct *target, const struct user_regset *regset, 770557d64dSAl Viro struct membuf to) 780c306bcfSIngo Molnar { 790c306bcfSIngo Molnar struct fpu *fpu = &target->thread.fpu; 800c306bcfSIngo Molnar 81*3a335112SDave Hansen if (!cpu_feature_enabled(X86_FEATURE_XSAVE)) 820c306bcfSIngo Molnar return -ENODEV; 830c306bcfSIngo Molnar 84369a036dSIngo Molnar fpu__prepare_read(fpu); 8591c3dba7SYu-cheng Yu 86*3a335112SDave Hansen copy_xstate_to_kernel(to, &fpu->state.xsave); 870557d64dSAl Viro return 0; 880c306bcfSIngo Molnar } 890c306bcfSIngo Molnar 900c306bcfSIngo Molnar int xstateregs_set(struct task_struct *target, const struct user_regset *regset, 910c306bcfSIngo Molnar unsigned int pos, unsigned int count, 920c306bcfSIngo Molnar const void *kbuf, const void __user *ubuf) 930c306bcfSIngo Molnar { 940c306bcfSIngo Molnar struct fpu *fpu = &target->thread.fpu; 9543be46e8SThomas Gleixner struct xregs_state *tmpbuf = NULL; 960c306bcfSIngo Molnar int ret; 970c306bcfSIngo Molnar 9843be46e8SThomas Gleixner if (!cpu_feature_enabled(X86_FEATURE_XSAVE)) 990c306bcfSIngo Molnar return -ENODEV; 1000c306bcfSIngo Molnar 10191c3dba7SYu-cheng Yu /* 10291c3dba7SYu-cheng Yu * A whole standard-format XSAVE buffer is needed: 10391c3dba7SYu-cheng Yu */ 10407d6688bSThomas Gleixner if (pos != 0 || count != fpu_user_xstate_size) 10591c3dba7SYu-cheng Yu return -EFAULT; 1060c306bcfSIngo Molnar 10743be46e8SThomas Gleixner if (!kbuf) { 10843be46e8SThomas Gleixner tmpbuf = vmalloc(count); 10943be46e8SThomas Gleixner if (!tmpbuf) 11043be46e8SThomas Gleixner return -ENOMEM; 1110c306bcfSIngo Molnar 11243be46e8SThomas Gleixner if (copy_from_user(tmpbuf, ubuf, count)) { 11343be46e8SThomas Gleixner ret = -EFAULT; 11443be46e8SThomas Gleixner goto out; 11543be46e8SThomas Gleixner } 11679fecc2bSIngo Molnar } 11791c3dba7SYu-cheng Yu 11843be46e8SThomas Gleixner fpu__prepare_write(fpu); 11943be46e8SThomas Gleixner ret = copy_kernel_to_xstate(&fpu->state.xsave, kbuf ?: tmpbuf); 120cf9df81bSEric Biggers 12143be46e8SThomas Gleixner out: 12243be46e8SThomas Gleixner vfree(tmpbuf); 1230c306bcfSIngo Molnar return ret; 1240c306bcfSIngo Molnar } 1250c306bcfSIngo Molnar 1260c306bcfSIngo Molnar #if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION 1270c306bcfSIngo Molnar 1280c306bcfSIngo Molnar /* 1290c306bcfSIngo Molnar * FPU tag word conversions. 1300c306bcfSIngo Molnar */ 1310c306bcfSIngo Molnar 1320c306bcfSIngo Molnar static inline unsigned short twd_i387_to_fxsr(unsigned short twd) 1330c306bcfSIngo Molnar { 1340c306bcfSIngo Molnar unsigned int tmp; /* to avoid 16 bit prefixes in the code */ 1350c306bcfSIngo Molnar 1360c306bcfSIngo Molnar /* Transform each pair of bits into 01 (valid) or 00 (empty) */ 1370c306bcfSIngo Molnar tmp = ~twd; 1380c306bcfSIngo Molnar tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */ 1390c306bcfSIngo Molnar /* and move the valid bits to the lower byte. */ 1400c306bcfSIngo Molnar tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */ 1410c306bcfSIngo Molnar tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */ 1420c306bcfSIngo Molnar tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */ 1430c306bcfSIngo Molnar 1440c306bcfSIngo Molnar return tmp; 1450c306bcfSIngo Molnar } 1460c306bcfSIngo Molnar 1470c306bcfSIngo Molnar #define FPREG_ADDR(f, n) ((void *)&(f)->st_space + (n) * 16) 1480c306bcfSIngo Molnar #define FP_EXP_TAG_VALID 0 1490c306bcfSIngo Molnar #define FP_EXP_TAG_ZERO 1 1500c306bcfSIngo Molnar #define FP_EXP_TAG_SPECIAL 2 1510c306bcfSIngo Molnar #define FP_EXP_TAG_EMPTY 3 1520c306bcfSIngo Molnar 153c47ada30SIngo Molnar static inline u32 twd_fxsr_to_i387(struct fxregs_state *fxsave) 1540c306bcfSIngo Molnar { 1550c306bcfSIngo Molnar struct _fpxreg *st; 1560c306bcfSIngo Molnar u32 tos = (fxsave->swd >> 11) & 7; 1570c306bcfSIngo Molnar u32 twd = (unsigned long) fxsave->twd; 1580c306bcfSIngo Molnar u32 tag; 1590c306bcfSIngo Molnar u32 ret = 0xffff0000u; 1600c306bcfSIngo Molnar int i; 1610c306bcfSIngo Molnar 1620c306bcfSIngo Molnar for (i = 0; i < 8; i++, twd >>= 1) { 1630c306bcfSIngo Molnar if (twd & 0x1) { 1640c306bcfSIngo Molnar st = FPREG_ADDR(fxsave, (i - tos) & 7); 1650c306bcfSIngo Molnar 1660c306bcfSIngo Molnar switch (st->exponent & 0x7fff) { 1670c306bcfSIngo Molnar case 0x7fff: 1680c306bcfSIngo Molnar tag = FP_EXP_TAG_SPECIAL; 1690c306bcfSIngo Molnar break; 1700c306bcfSIngo Molnar case 0x0000: 1710c306bcfSIngo Molnar if (!st->significand[0] && 1720c306bcfSIngo Molnar !st->significand[1] && 1730c306bcfSIngo Molnar !st->significand[2] && 1740c306bcfSIngo Molnar !st->significand[3]) 1750c306bcfSIngo Molnar tag = FP_EXP_TAG_ZERO; 1760c306bcfSIngo Molnar else 1770c306bcfSIngo Molnar tag = FP_EXP_TAG_SPECIAL; 1780c306bcfSIngo Molnar break; 1790c306bcfSIngo Molnar default: 1800c306bcfSIngo Molnar if (st->significand[3] & 0x8000) 1810c306bcfSIngo Molnar tag = FP_EXP_TAG_VALID; 1820c306bcfSIngo Molnar else 1830c306bcfSIngo Molnar tag = FP_EXP_TAG_SPECIAL; 1840c306bcfSIngo Molnar break; 1850c306bcfSIngo Molnar } 1860c306bcfSIngo Molnar } else { 1870c306bcfSIngo Molnar tag = FP_EXP_TAG_EMPTY; 1880c306bcfSIngo Molnar } 1890c306bcfSIngo Molnar ret |= tag << (2 * i); 1900c306bcfSIngo Molnar } 1910c306bcfSIngo Molnar return ret; 1920c306bcfSIngo Molnar } 1930c306bcfSIngo Molnar 1940c306bcfSIngo Molnar /* 1950c306bcfSIngo Molnar * FXSR floating point environment conversions. 1960c306bcfSIngo Molnar */ 1970c306bcfSIngo Molnar 1980c306bcfSIngo Molnar void 1990c306bcfSIngo Molnar convert_from_fxsr(struct user_i387_ia32_struct *env, struct task_struct *tsk) 2000c306bcfSIngo Molnar { 201c47ada30SIngo Molnar struct fxregs_state *fxsave = &tsk->thread.fpu.state.fxsave; 2020c306bcfSIngo Molnar struct _fpreg *to = (struct _fpreg *) &env->st_space[0]; 2030c306bcfSIngo Molnar struct _fpxreg *from = (struct _fpxreg *) &fxsave->st_space[0]; 2040c306bcfSIngo Molnar int i; 2050c306bcfSIngo Molnar 2060c306bcfSIngo Molnar env->cwd = fxsave->cwd | 0xffff0000u; 2070c306bcfSIngo Molnar env->swd = fxsave->swd | 0xffff0000u; 2080c306bcfSIngo Molnar env->twd = twd_fxsr_to_i387(fxsave); 2090c306bcfSIngo Molnar 2100c306bcfSIngo Molnar #ifdef CONFIG_X86_64 2110c306bcfSIngo Molnar env->fip = fxsave->rip; 2120c306bcfSIngo Molnar env->foo = fxsave->rdp; 2130c306bcfSIngo Molnar /* 2140c306bcfSIngo Molnar * should be actually ds/cs at fpu exception time, but 2150c306bcfSIngo Molnar * that information is not available in 64bit mode. 2160c306bcfSIngo Molnar */ 2170c306bcfSIngo Molnar env->fcs = task_pt_regs(tsk)->cs; 2180c306bcfSIngo Molnar if (tsk == current) { 2190c306bcfSIngo Molnar savesegment(ds, env->fos); 2200c306bcfSIngo Molnar } else { 2210c306bcfSIngo Molnar env->fos = tsk->thread.ds; 2220c306bcfSIngo Molnar } 2230c306bcfSIngo Molnar env->fos |= 0xffff0000; 2240c306bcfSIngo Molnar #else 2250c306bcfSIngo Molnar env->fip = fxsave->fip; 2260c306bcfSIngo Molnar env->fcs = (u16) fxsave->fcs | ((u32) fxsave->fop << 16); 2270c306bcfSIngo Molnar env->foo = fxsave->foo; 2280c306bcfSIngo Molnar env->fos = fxsave->fos; 2290c306bcfSIngo Molnar #endif 2300c306bcfSIngo Molnar 2310c306bcfSIngo Molnar for (i = 0; i < 8; ++i) 2320c306bcfSIngo Molnar memcpy(&to[i], &from[i], sizeof(to[0])); 2330c306bcfSIngo Molnar } 2340c306bcfSIngo Molnar 23539ea9bafSSebastian Andrzej Siewior void convert_to_fxsr(struct fxregs_state *fxsave, 2360c306bcfSIngo Molnar const struct user_i387_ia32_struct *env) 2370c306bcfSIngo Molnar 2380c306bcfSIngo Molnar { 2390c306bcfSIngo Molnar struct _fpreg *from = (struct _fpreg *) &env->st_space[0]; 2400c306bcfSIngo Molnar struct _fpxreg *to = (struct _fpxreg *) &fxsave->st_space[0]; 2410c306bcfSIngo Molnar int i; 2420c306bcfSIngo Molnar 2430c306bcfSIngo Molnar fxsave->cwd = env->cwd; 2440c306bcfSIngo Molnar fxsave->swd = env->swd; 2450c306bcfSIngo Molnar fxsave->twd = twd_i387_to_fxsr(env->twd); 2460c306bcfSIngo Molnar fxsave->fop = (u16) ((u32) env->fcs >> 16); 2470c306bcfSIngo Molnar #ifdef CONFIG_X86_64 2480c306bcfSIngo Molnar fxsave->rip = env->fip; 2490c306bcfSIngo Molnar fxsave->rdp = env->foo; 2500c306bcfSIngo Molnar /* cs and ds ignored */ 2510c306bcfSIngo Molnar #else 2520c306bcfSIngo Molnar fxsave->fip = env->fip; 2530c306bcfSIngo Molnar fxsave->fcs = (env->fcs & 0xffff); 2540c306bcfSIngo Molnar fxsave->foo = env->foo; 2550c306bcfSIngo Molnar fxsave->fos = env->fos; 2560c306bcfSIngo Molnar #endif 2570c306bcfSIngo Molnar 2580c306bcfSIngo Molnar for (i = 0; i < 8; ++i) 2590c306bcfSIngo Molnar memcpy(&to[i], &from[i], sizeof(from[0])); 2600c306bcfSIngo Molnar } 2610c306bcfSIngo Molnar 2620c306bcfSIngo Molnar int fpregs_get(struct task_struct *target, const struct user_regset *regset, 2630557d64dSAl Viro struct membuf to) 2640c306bcfSIngo Molnar { 2650c306bcfSIngo Molnar struct fpu *fpu = &target->thread.fpu; 2660c306bcfSIngo Molnar struct user_i387_ia32_struct env; 2670c306bcfSIngo Molnar 268369a036dSIngo Molnar fpu__prepare_read(fpu); 2690c306bcfSIngo Molnar 27078df526cSBorislav Petkov if (!boot_cpu_has(X86_FEATURE_FPU)) 2710557d64dSAl Viro return fpregs_soft_get(target, regset, to); 2720c306bcfSIngo Molnar 2730557d64dSAl Viro if (!boot_cpu_has(X86_FEATURE_FXSR)) { 2740557d64dSAl Viro return membuf_write(&to, &fpu->state.fsave, 2750557d64dSAl Viro sizeof(struct fregs_state)); 2760557d64dSAl Viro } 2770c306bcfSIngo Molnar 2780c306bcfSIngo Molnar fpstate_sanitize_xstate(fpu); 2790c306bcfSIngo Molnar 2800557d64dSAl Viro if (to.left == sizeof(env)) { 2810557d64dSAl Viro convert_from_fxsr(to.p, target); 2820c306bcfSIngo Molnar return 0; 2830c306bcfSIngo Molnar } 2840c306bcfSIngo Molnar 2850c306bcfSIngo Molnar convert_from_fxsr(&env, target); 2860557d64dSAl Viro return membuf_write(&to, &env, sizeof(env)); 2870c306bcfSIngo Molnar } 2880c306bcfSIngo Molnar 2890c306bcfSIngo Molnar int fpregs_set(struct task_struct *target, const struct user_regset *regset, 2900c306bcfSIngo Molnar unsigned int pos, unsigned int count, 2910c306bcfSIngo Molnar const void *kbuf, const void __user *ubuf) 2920c306bcfSIngo Molnar { 2930c306bcfSIngo Molnar struct fpu *fpu = &target->thread.fpu; 2940c306bcfSIngo Molnar struct user_i387_ia32_struct env; 2950c306bcfSIngo Molnar int ret; 2960c306bcfSIngo Molnar 297369a036dSIngo Molnar fpu__prepare_write(fpu); 2980c306bcfSIngo Molnar fpstate_sanitize_xstate(fpu); 2990c306bcfSIngo Molnar 30078df526cSBorislav Petkov if (!boot_cpu_has(X86_FEATURE_FPU)) 3010c306bcfSIngo Molnar return fpregs_soft_set(target, regset, pos, count, kbuf, ubuf); 3020c306bcfSIngo Molnar 30301f8fd73SBorislav Petkov if (!boot_cpu_has(X86_FEATURE_FXSR)) 3040c306bcfSIngo Molnar return user_regset_copyin(&pos, &count, &kbuf, &ubuf, 3050c306bcfSIngo Molnar &fpu->state.fsave, 0, 3060c306bcfSIngo Molnar -1); 3070c306bcfSIngo Molnar 3080c306bcfSIngo Molnar if (pos > 0 || count < sizeof(env)) 3090c306bcfSIngo Molnar convert_from_fxsr(&env, target); 3100c306bcfSIngo Molnar 3110c306bcfSIngo Molnar ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &env, 0, -1); 3120c306bcfSIngo Molnar if (!ret) 31339ea9bafSSebastian Andrzej Siewior convert_to_fxsr(&target->thread.fpu.state.fxsave, &env); 3140c306bcfSIngo Molnar 3150c306bcfSIngo Molnar /* 3160c306bcfSIngo Molnar * update the header bit in the xsave header, indicating the 3170c306bcfSIngo Molnar * presence of FP. 3180c306bcfSIngo Molnar */ 319d366bf7eSBorislav Petkov if (boot_cpu_has(X86_FEATURE_XSAVE)) 320d91cab78SDave Hansen fpu->state.xsave.header.xfeatures |= XFEATURE_MASK_FP; 3210c306bcfSIngo Molnar return ret; 3220c306bcfSIngo Molnar } 3230c306bcfSIngo Molnar 3240c306bcfSIngo Molnar #endif /* CONFIG_X86_32 || CONFIG_IA32_EMULATION */ 325