linux/arch/x86/kernel/fpu/regset.c
<<
>>
Prefs
   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 */
  16int regset_fpregs_active(struct task_struct *target, const struct user_regset *regset)
  17{
  18        return regset->n;
  19}
  20
  21int 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
  29int 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
  45int 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
  76int 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
 113int 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
 165static 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
 186static 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
 231void
 232convert_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
 268void 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
 295int 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
 324int 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 */
 366int 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}
 373EXPORT_SYMBOL(dump_fpu);
 374
 375#endif  /* CONFIG_X86_32 || CONFIG_IA32_EMULATION */
 376