0001
0002
0003
0004
0005 #include <linux/sched/task_stack.h>
0006 #include <linux/vmalloc.h>
0007
0008 #include <asm/fpu/api.h>
0009 #include <asm/fpu/signal.h>
0010 #include <asm/fpu/regset.h>
0011
0012 #include "context.h"
0013 #include "internal.h"
0014 #include "legacy.h"
0015 #include "xstate.h"
0016
0017
0018
0019
0020
0021
0022 int regset_fpregs_active(struct task_struct *target, const struct user_regset *regset)
0023 {
0024 return regset->n;
0025 }
0026
0027 int regset_xregset_fpregs_active(struct task_struct *target, const struct user_regset *regset)
0028 {
0029 if (boot_cpu_has(X86_FEATURE_FXSR))
0030 return regset->n;
0031 else
0032 return 0;
0033 }
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045 static void sync_fpstate(struct fpu *fpu)
0046 {
0047 if (fpu == ¤t->thread.fpu)
0048 fpu_sync_fpstate(fpu);
0049 }
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059 static void fpu_force_restore(struct fpu *fpu)
0060 {
0061
0062
0063
0064
0065 WARN_ON_FPU(fpu == ¤t->thread.fpu);
0066
0067 __fpu_invalidate_fpregs_state(fpu);
0068 }
0069
0070 int xfpregs_get(struct task_struct *target, const struct user_regset *regset,
0071 struct membuf to)
0072 {
0073 struct fpu *fpu = &target->thread.fpu;
0074
0075 if (!cpu_feature_enabled(X86_FEATURE_FXSR))
0076 return -ENODEV;
0077
0078 sync_fpstate(fpu);
0079
0080 if (!use_xsave()) {
0081 return membuf_write(&to, &fpu->fpstate->regs.fxsave,
0082 sizeof(fpu->fpstate->regs.fxsave));
0083 }
0084
0085 copy_xstate_to_uabi_buf(to, target, XSTATE_COPY_FX);
0086 return 0;
0087 }
0088
0089 int xfpregs_set(struct task_struct *target, const struct user_regset *regset,
0090 unsigned int pos, unsigned int count,
0091 const void *kbuf, const void __user *ubuf)
0092 {
0093 struct fpu *fpu = &target->thread.fpu;
0094 struct fxregs_state newstate;
0095 int ret;
0096
0097 if (!cpu_feature_enabled(X86_FEATURE_FXSR))
0098 return -ENODEV;
0099
0100
0101 if (pos != 0 || count != sizeof(newstate))
0102 return -EINVAL;
0103
0104 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &newstate, 0, -1);
0105 if (ret)
0106 return ret;
0107
0108
0109 if (newstate.mxcsr & ~mxcsr_feature_mask)
0110 return -EINVAL;
0111
0112 fpu_force_restore(fpu);
0113
0114
0115 memcpy(&fpu->fpstate->regs.fxsave, &newstate, sizeof(newstate));
0116
0117
0118 BUILD_BUG_ON(sizeof(fpu->__fpstate.regs.fxsave.xmm_space) != 16 * 16);
0119 if (in_ia32_syscall())
0120 memset(&fpu->fpstate->regs.fxsave.xmm_space[8*4], 0, 8 * 16);
0121
0122
0123 if (use_xsave())
0124 fpu->fpstate->regs.xsave.header.xfeatures |= XFEATURE_MASK_FPSSE;
0125
0126 return 0;
0127 }
0128
0129 int xstateregs_get(struct task_struct *target, const struct user_regset *regset,
0130 struct membuf to)
0131 {
0132 if (!cpu_feature_enabled(X86_FEATURE_XSAVE))
0133 return -ENODEV;
0134
0135 sync_fpstate(&target->thread.fpu);
0136
0137 copy_xstate_to_uabi_buf(to, target, XSTATE_COPY_XSAVE);
0138 return 0;
0139 }
0140
0141 int xstateregs_set(struct task_struct *target, const struct user_regset *regset,
0142 unsigned int pos, unsigned int count,
0143 const void *kbuf, const void __user *ubuf)
0144 {
0145 struct fpu *fpu = &target->thread.fpu;
0146 struct xregs_state *tmpbuf = NULL;
0147 int ret;
0148
0149 if (!cpu_feature_enabled(X86_FEATURE_XSAVE))
0150 return -ENODEV;
0151
0152
0153
0154
0155 if (pos != 0 || count != fpu_user_cfg.max_size)
0156 return -EFAULT;
0157
0158 if (!kbuf) {
0159 tmpbuf = vmalloc(count);
0160 if (!tmpbuf)
0161 return -ENOMEM;
0162
0163 if (copy_from_user(tmpbuf, ubuf, count)) {
0164 ret = -EFAULT;
0165 goto out;
0166 }
0167 }
0168
0169 fpu_force_restore(fpu);
0170 ret = copy_uabi_from_kernel_to_xstate(fpu->fpstate, kbuf ?: tmpbuf);
0171
0172 out:
0173 vfree(tmpbuf);
0174 return ret;
0175 }
0176
0177 #if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
0178
0179
0180
0181
0182
0183 static inline unsigned short twd_i387_to_fxsr(unsigned short twd)
0184 {
0185 unsigned int tmp;
0186
0187
0188 tmp = ~twd;
0189 tmp = (tmp | (tmp>>1)) & 0x5555;
0190
0191 tmp = (tmp | (tmp >> 1)) & 0x3333;
0192 tmp = (tmp | (tmp >> 2)) & 0x0f0f;
0193 tmp = (tmp | (tmp >> 4)) & 0x00ff;
0194
0195 return tmp;
0196 }
0197
0198 #define FPREG_ADDR(f, n) ((void *)&(f)->st_space + (n) * 16)
0199 #define FP_EXP_TAG_VALID 0
0200 #define FP_EXP_TAG_ZERO 1
0201 #define FP_EXP_TAG_SPECIAL 2
0202 #define FP_EXP_TAG_EMPTY 3
0203
0204 static inline u32 twd_fxsr_to_i387(struct fxregs_state *fxsave)
0205 {
0206 struct _fpxreg *st;
0207 u32 tos = (fxsave->swd >> 11) & 7;
0208 u32 twd = (unsigned long) fxsave->twd;
0209 u32 tag;
0210 u32 ret = 0xffff0000u;
0211 int i;
0212
0213 for (i = 0; i < 8; i++, twd >>= 1) {
0214 if (twd & 0x1) {
0215 st = FPREG_ADDR(fxsave, (i - tos) & 7);
0216
0217 switch (st->exponent & 0x7fff) {
0218 case 0x7fff:
0219 tag = FP_EXP_TAG_SPECIAL;
0220 break;
0221 case 0x0000:
0222 if (!st->significand[0] &&
0223 !st->significand[1] &&
0224 !st->significand[2] &&
0225 !st->significand[3])
0226 tag = FP_EXP_TAG_ZERO;
0227 else
0228 tag = FP_EXP_TAG_SPECIAL;
0229 break;
0230 default:
0231 if (st->significand[3] & 0x8000)
0232 tag = FP_EXP_TAG_VALID;
0233 else
0234 tag = FP_EXP_TAG_SPECIAL;
0235 break;
0236 }
0237 } else {
0238 tag = FP_EXP_TAG_EMPTY;
0239 }
0240 ret |= tag << (2 * i);
0241 }
0242 return ret;
0243 }
0244
0245
0246
0247
0248
0249 static void __convert_from_fxsr(struct user_i387_ia32_struct *env,
0250 struct task_struct *tsk,
0251 struct fxregs_state *fxsave)
0252 {
0253 struct _fpreg *to = (struct _fpreg *) &env->st_space[0];
0254 struct _fpxreg *from = (struct _fpxreg *) &fxsave->st_space[0];
0255 int i;
0256
0257 env->cwd = fxsave->cwd | 0xffff0000u;
0258 env->swd = fxsave->swd | 0xffff0000u;
0259 env->twd = twd_fxsr_to_i387(fxsave);
0260
0261 #ifdef CONFIG_X86_64
0262 env->fip = fxsave->rip;
0263 env->foo = fxsave->rdp;
0264
0265
0266
0267
0268 env->fcs = task_pt_regs(tsk)->cs;
0269 if (tsk == current) {
0270 savesegment(ds, env->fos);
0271 } else {
0272 env->fos = tsk->thread.ds;
0273 }
0274 env->fos |= 0xffff0000;
0275 #else
0276 env->fip = fxsave->fip;
0277 env->fcs = (u16) fxsave->fcs | ((u32) fxsave->fop << 16);
0278 env->foo = fxsave->foo;
0279 env->fos = fxsave->fos;
0280 #endif
0281
0282 for (i = 0; i < 8; ++i)
0283 memcpy(&to[i], &from[i], sizeof(to[0]));
0284 }
0285
0286 void
0287 convert_from_fxsr(struct user_i387_ia32_struct *env, struct task_struct *tsk)
0288 {
0289 __convert_from_fxsr(env, tsk, &tsk->thread.fpu.fpstate->regs.fxsave);
0290 }
0291
0292 void convert_to_fxsr(struct fxregs_state *fxsave,
0293 const struct user_i387_ia32_struct *env)
0294
0295 {
0296 struct _fpreg *from = (struct _fpreg *) &env->st_space[0];
0297 struct _fpxreg *to = (struct _fpxreg *) &fxsave->st_space[0];
0298 int i;
0299
0300 fxsave->cwd = env->cwd;
0301 fxsave->swd = env->swd;
0302 fxsave->twd = twd_i387_to_fxsr(env->twd);
0303 fxsave->fop = (u16) ((u32) env->fcs >> 16);
0304 #ifdef CONFIG_X86_64
0305 fxsave->rip = env->fip;
0306 fxsave->rdp = env->foo;
0307
0308 #else
0309 fxsave->fip = env->fip;
0310 fxsave->fcs = (env->fcs & 0xffff);
0311 fxsave->foo = env->foo;
0312 fxsave->fos = env->fos;
0313 #endif
0314
0315 for (i = 0; i < 8; ++i)
0316 memcpy(&to[i], &from[i], sizeof(from[0]));
0317 }
0318
0319 int fpregs_get(struct task_struct *target, const struct user_regset *regset,
0320 struct membuf to)
0321 {
0322 struct fpu *fpu = &target->thread.fpu;
0323 struct user_i387_ia32_struct env;
0324 struct fxregs_state fxsave, *fx;
0325
0326 sync_fpstate(fpu);
0327
0328 if (!cpu_feature_enabled(X86_FEATURE_FPU))
0329 return fpregs_soft_get(target, regset, to);
0330
0331 if (!cpu_feature_enabled(X86_FEATURE_FXSR)) {
0332 return membuf_write(&to, &fpu->fpstate->regs.fsave,
0333 sizeof(struct fregs_state));
0334 }
0335
0336 if (use_xsave()) {
0337 struct membuf mb = { .p = &fxsave, .left = sizeof(fxsave) };
0338
0339
0340 copy_xstate_to_uabi_buf(mb, target, XSTATE_COPY_FP);
0341 fx = &fxsave;
0342 } else {
0343 fx = &fpu->fpstate->regs.fxsave;
0344 }
0345
0346 __convert_from_fxsr(&env, target, fx);
0347 return membuf_write(&to, &env, sizeof(env));
0348 }
0349
0350 int fpregs_set(struct task_struct *target, const struct user_regset *regset,
0351 unsigned int pos, unsigned int count,
0352 const void *kbuf, const void __user *ubuf)
0353 {
0354 struct fpu *fpu = &target->thread.fpu;
0355 struct user_i387_ia32_struct env;
0356 int ret;
0357
0358
0359 if (pos != 0 || count != sizeof(struct user_i387_ia32_struct))
0360 return -EINVAL;
0361
0362 if (!cpu_feature_enabled(X86_FEATURE_FPU))
0363 return fpregs_soft_set(target, regset, pos, count, kbuf, ubuf);
0364
0365 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &env, 0, -1);
0366 if (ret)
0367 return ret;
0368
0369 fpu_force_restore(fpu);
0370
0371 if (cpu_feature_enabled(X86_FEATURE_FXSR))
0372 convert_to_fxsr(&fpu->fpstate->regs.fxsave, &env);
0373 else
0374 memcpy(&fpu->fpstate->regs.fsave, &env, sizeof(env));
0375
0376
0377
0378
0379
0380 if (cpu_feature_enabled(X86_FEATURE_XSAVE))
0381 fpu->fpstate->regs.xsave.header.xfeatures |= XFEATURE_MASK_FP;
0382
0383 return 0;
0384 }
0385
0386 #endif