root/arch/x86/kernel/fpu/regset.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. regset_fpregs_active
  2. regset_xregset_fpregs_active
  3. xfpregs_get
  4. xfpregs_set
  5. xstateregs_get
  6. xstateregs_set
  7. twd_i387_to_fxsr
  8. twd_fxsr_to_i387
  9. convert_from_fxsr
  10. convert_to_fxsr
  11. fpregs_get
  12. fpregs_set
  13. dump_fpu

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * FPU register's regset abstraction, for ptrace, core dumps, etc.
   4  */
   5 #include <asm/fpu/internal.h>
   6 #include <asm/fpu/signal.h>
   7 #include <asm/fpu/regset.h>
   8 #include <asm/fpu/xstate.h>
   9 #include <linux/sched/task_stack.h>
  10 
  11 /*
  12  * The xstateregs_active() routine is the same as the regset_fpregs_active() routine,
  13  * as the "regset->n" for the xstate regset will be updated based on the feature
  14  * capabilities supported by the xsave.
  15  */
  16 int regset_fpregs_active(struct task_struct *target, const struct user_regset *regset)
  17 {
  18         return regset->n;
  19 }
  20 
  21 int regset_xregset_fpregs_active(struct task_struct *target, const struct user_regset *regset)
  22 {
  23         if (boot_cpu_has(X86_FEATURE_FXSR))
  24                 return regset->n;
  25         else
  26                 return 0;
  27 }
  28 
  29 int xfpregs_get(struct task_struct *target, const struct user_regset *regset,
  30                 unsigned int pos, unsigned int count,
  31                 void *kbuf, void __user *ubuf)
  32 {
  33         struct fpu *fpu = &target->thread.fpu;
  34 
  35         if (!boot_cpu_has(X86_FEATURE_FXSR))
  36                 return -ENODEV;
  37 
  38         fpu__prepare_read(fpu);
  39         fpstate_sanitize_xstate(fpu);
  40 
  41         return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
  42                                    &fpu->state.fxsave, 0, -1);
  43 }
  44 
  45 int xfpregs_set(struct task_struct *target, const struct user_regset *regset,
  46                 unsigned int pos, unsigned int count,
  47                 const void *kbuf, const void __user *ubuf)
  48 {
  49         struct fpu *fpu = &target->thread.fpu;
  50         int ret;
  51 
  52         if (!boot_cpu_has(X86_FEATURE_FXSR))
  53                 return -ENODEV;
  54 
  55         fpu__prepare_write(fpu);
  56         fpstate_sanitize_xstate(fpu);
  57 
  58         ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
  59                                  &fpu->state.fxsave, 0, -1);
  60 
  61         /*
  62          * mxcsr reserved bits must be masked to zero for security reasons.
  63          */
  64         fpu->state.fxsave.mxcsr &= mxcsr_feature_mask;
  65 
  66         /*
  67          * update the header bits in the xsave header, indicating the
  68          * presence of FP and SSE state.
  69          */
  70         if (boot_cpu_has(X86_FEATURE_XSAVE))
  71                 fpu->state.xsave.header.xfeatures |= XFEATURE_MASK_FPSSE;
  72 
  73         return ret;
  74 }
  75 
  76 int xstateregs_get(struct task_struct *target, const struct user_regset *regset,
  77                 unsigned int pos, unsigned int count,
  78                 void *kbuf, void __user *ubuf)
  79 {
  80         struct fpu *fpu = &target->thread.fpu;
  81         struct xregs_state *xsave;
  82         int ret;
  83 
  84         if (!boot_cpu_has(X86_FEATURE_XSAVE))
  85                 return -ENODEV;
  86 
  87         xsave = &fpu->state.xsave;
  88 
  89         fpu__prepare_read(fpu);
  90 
  91         if (using_compacted_format()) {
  92                 if (kbuf)
  93                         ret = copy_xstate_to_kernel(kbuf, xsave, pos, count);
  94                 else
  95                         ret = copy_xstate_to_user(ubuf, xsave, pos, count);
  96         } else {
  97                 fpstate_sanitize_xstate(fpu);
  98                 /*
  99                  * Copy the 48 bytes defined by the software into the xsave
 100                  * area in the thread struct, so that we can copy the whole
 101                  * area to user using one user_regset_copyout().
 102                  */
 103                 memcpy(&xsave->i387.sw_reserved, xstate_fx_sw_bytes, sizeof(xstate_fx_sw_bytes));
 104 
 105                 /*
 106                  * Copy the xstate memory layout.
 107                  */
 108                 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, xsave, 0, -1);
 109         }
 110         return ret;
 111 }
 112 
 113 int xstateregs_set(struct task_struct *target, const struct user_regset *regset,
 114                   unsigned int pos, unsigned int count,
 115                   const void *kbuf, const void __user *ubuf)
 116 {
 117         struct fpu *fpu = &target->thread.fpu;
 118         struct xregs_state *xsave;
 119         int ret;
 120 
 121         if (!boot_cpu_has(X86_FEATURE_XSAVE))
 122                 return -ENODEV;
 123 
 124         /*
 125          * A whole standard-format XSAVE buffer is needed:
 126          */
 127         if ((pos != 0) || (count < fpu_user_xstate_size))
 128                 return -EFAULT;
 129 
 130         xsave = &fpu->state.xsave;
 131 
 132         fpu__prepare_write(fpu);
 133 
 134         if (using_compacted_format()) {
 135                 if (kbuf)
 136                         ret = copy_kernel_to_xstate(xsave, kbuf);
 137                 else
 138                         ret = copy_user_to_xstate(xsave, ubuf);
 139         } else {
 140                 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, xsave, 0, -1);
 141                 if (!ret)
 142                         ret = validate_xstate_header(&xsave->header);
 143         }
 144 
 145         /*
 146          * mxcsr reserved bits must be masked to zero for security reasons.
 147          */
 148         xsave->i387.mxcsr &= mxcsr_feature_mask;
 149 
 150         /*
 151          * In case of failure, mark all states as init:
 152          */
 153         if (ret)
 154                 fpstate_init(&fpu->state);
 155 
 156         return ret;
 157 }
 158 
 159 #if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
 160 
 161 /*
 162  * FPU tag word conversions.
 163  */
 164 
 165 static inline unsigned short twd_i387_to_fxsr(unsigned short twd)
 166 {
 167         unsigned int tmp; /* to avoid 16 bit prefixes in the code */
 168 
 169         /* Transform each pair of bits into 01 (valid) or 00 (empty) */
 170         tmp = ~twd;
 171         tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */
 172         /* and move the valid bits to the lower byte. */
 173         tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */
 174         tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */
 175         tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */
 176 
 177         return tmp;
 178 }
 179 
 180 #define FPREG_ADDR(f, n)        ((void *)&(f)->st_space + (n) * 16)
 181 #define FP_EXP_TAG_VALID        0
 182 #define FP_EXP_TAG_ZERO         1
 183 #define FP_EXP_TAG_SPECIAL      2
 184 #define FP_EXP_TAG_EMPTY        3
 185 
 186 static inline u32 twd_fxsr_to_i387(struct fxregs_state *fxsave)
 187 {
 188         struct _fpxreg *st;
 189         u32 tos = (fxsave->swd >> 11) & 7;
 190         u32 twd = (unsigned long) fxsave->twd;
 191         u32 tag;
 192         u32 ret = 0xffff0000u;
 193         int i;
 194 
 195         for (i = 0; i < 8; i++, twd >>= 1) {
 196                 if (twd & 0x1) {
 197                         st = FPREG_ADDR(fxsave, (i - tos) & 7);
 198 
 199                         switch (st->exponent & 0x7fff) {
 200                         case 0x7fff:
 201                                 tag = FP_EXP_TAG_SPECIAL;
 202                                 break;
 203                         case 0x0000:
 204                                 if (!st->significand[0] &&
 205                                     !st->significand[1] &&
 206                                     !st->significand[2] &&
 207                                     !st->significand[3])
 208                                         tag = FP_EXP_TAG_ZERO;
 209                                 else
 210                                         tag = FP_EXP_TAG_SPECIAL;
 211                                 break;
 212                         default:
 213                                 if (st->significand[3] & 0x8000)
 214                                         tag = FP_EXP_TAG_VALID;
 215                                 else
 216                                         tag = FP_EXP_TAG_SPECIAL;
 217                                 break;
 218                         }
 219                 } else {
 220                         tag = FP_EXP_TAG_EMPTY;
 221                 }
 222                 ret |= tag << (2 * i);
 223         }
 224         return ret;
 225 }
 226 
 227 /*
 228  * FXSR floating point environment conversions.
 229  */
 230 
 231 void
 232 convert_from_fxsr(struct user_i387_ia32_struct *env, struct task_struct *tsk)
 233 {
 234         struct fxregs_state *fxsave = &tsk->thread.fpu.state.fxsave;
 235         struct _fpreg *to = (struct _fpreg *) &env->st_space[0];
 236         struct _fpxreg *from = (struct _fpxreg *) &fxsave->st_space[0];
 237         int i;
 238 
 239         env->cwd = fxsave->cwd | 0xffff0000u;
 240         env->swd = fxsave->swd | 0xffff0000u;
 241         env->twd = twd_fxsr_to_i387(fxsave);
 242 
 243 #ifdef CONFIG_X86_64
 244         env->fip = fxsave->rip;
 245         env->foo = fxsave->rdp;
 246         /*
 247          * should be actually ds/cs at fpu exception time, but
 248          * that information is not available in 64bit mode.
 249          */
 250         env->fcs = task_pt_regs(tsk)->cs;
 251         if (tsk == current) {
 252                 savesegment(ds, env->fos);
 253         } else {
 254                 env->fos = tsk->thread.ds;
 255         }
 256         env->fos |= 0xffff0000;
 257 #else
 258         env->fip = fxsave->fip;
 259         env->fcs = (u16) fxsave->fcs | ((u32) fxsave->fop << 16);
 260         env->foo = fxsave->foo;
 261         env->fos = fxsave->fos;
 262 #endif
 263 
 264         for (i = 0; i < 8; ++i)
 265                 memcpy(&to[i], &from[i], sizeof(to[0]));
 266 }
 267 
 268 void convert_to_fxsr(struct fxregs_state *fxsave,
 269                      const struct user_i387_ia32_struct *env)
 270 
 271 {
 272         struct _fpreg *from = (struct _fpreg *) &env->st_space[0];
 273         struct _fpxreg *to = (struct _fpxreg *) &fxsave->st_space[0];
 274         int i;
 275 
 276         fxsave->cwd = env->cwd;
 277         fxsave->swd = env->swd;
 278         fxsave->twd = twd_i387_to_fxsr(env->twd);
 279         fxsave->fop = (u16) ((u32) env->fcs >> 16);
 280 #ifdef CONFIG_X86_64
 281         fxsave->rip = env->fip;
 282         fxsave->rdp = env->foo;
 283         /* cs and ds ignored */
 284 #else
 285         fxsave->fip = env->fip;
 286         fxsave->fcs = (env->fcs & 0xffff);
 287         fxsave->foo = env->foo;
 288         fxsave->fos = env->fos;
 289 #endif
 290 
 291         for (i = 0; i < 8; ++i)
 292                 memcpy(&to[i], &from[i], sizeof(from[0]));
 293 }
 294 
 295 int fpregs_get(struct task_struct *target, const struct user_regset *regset,
 296                unsigned int pos, unsigned int count,
 297                void *kbuf, void __user *ubuf)
 298 {
 299         struct fpu *fpu = &target->thread.fpu;
 300         struct user_i387_ia32_struct env;
 301 
 302         fpu__prepare_read(fpu);
 303 
 304         if (!boot_cpu_has(X86_FEATURE_FPU))
 305                 return fpregs_soft_get(target, regset, pos, count, kbuf, ubuf);
 306 
 307         if (!boot_cpu_has(X86_FEATURE_FXSR))
 308                 return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
 309                                            &fpu->state.fsave, 0,
 310                                            -1);
 311 
 312         fpstate_sanitize_xstate(fpu);
 313 
 314         if (kbuf && pos == 0 && count == sizeof(env)) {
 315                 convert_from_fxsr(kbuf, target);
 316                 return 0;
 317         }
 318 
 319         convert_from_fxsr(&env, target);
 320 
 321         return user_regset_copyout(&pos, &count, &kbuf, &ubuf, &env, 0, -1);
 322 }
 323 
 324 int fpregs_set(struct task_struct *target, const struct user_regset *regset,
 325                unsigned int pos, unsigned int count,
 326                const void *kbuf, const void __user *ubuf)
 327 {
 328         struct fpu *fpu = &target->thread.fpu;
 329         struct user_i387_ia32_struct env;
 330         int ret;
 331 
 332         fpu__prepare_write(fpu);
 333         fpstate_sanitize_xstate(fpu);
 334 
 335         if (!boot_cpu_has(X86_FEATURE_FPU))
 336                 return fpregs_soft_set(target, regset, pos, count, kbuf, ubuf);
 337 
 338         if (!boot_cpu_has(X86_FEATURE_FXSR))
 339                 return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 340                                           &fpu->state.fsave, 0,
 341                                           -1);
 342 
 343         if (pos > 0 || count < sizeof(env))
 344                 convert_from_fxsr(&env, target);
 345 
 346         ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &env, 0, -1);
 347         if (!ret)
 348                 convert_to_fxsr(&target->thread.fpu.state.fxsave, &env);
 349 
 350         /*
 351          * update the header bit in the xsave header, indicating the
 352          * presence of FP.
 353          */
 354         if (boot_cpu_has(X86_FEATURE_XSAVE))
 355                 fpu->state.xsave.header.xfeatures |= XFEATURE_MASK_FP;
 356         return ret;
 357 }
 358 
 359 /*
 360  * FPU state for core dumps.
 361  * This is only used for a.out dumps now.
 362  * It is declared generically using elf_fpregset_t (which is
 363  * struct user_i387_struct) but is in fact only used for 32-bit
 364  * dumps, so on 64-bit it is really struct user_i387_ia32_struct.
 365  */
 366 int dump_fpu(struct pt_regs *regs, struct user_i387_struct *ufpu)
 367 {
 368         struct task_struct *tsk = current;
 369 
 370         return !fpregs_get(tsk, NULL, 0, sizeof(struct user_i387_ia32_struct),
 371                            ufpu, NULL);
 372 }
 373 EXPORT_SYMBOL(dump_fpu);
 374 
 375 #endif  /* CONFIG_X86_32 || CONFIG_IA32_EMULATION */

/* [<][>][^][v][top][bottom][index][help] */