i387.c 17.3 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7
/*
 *  Copyright (C) 1994 Linus Torvalds
 *
 *  Pentium III FXSR, SSE support
 *  General FPU state handling cleanups
 *	Gareth Hughes <gareth@valinux.com>, May 2000
 */
8
#include <linux/module.h>
R
Roland McGrath 已提交
9
#include <linux/regset.h>
I
Ingo Molnar 已提交
10
#include <linux/sched.h>
11
#include <linux/slab.h>
I
Ingo Molnar 已提交
12 13

#include <asm/sigcontext.h>
L
Linus Torvalds 已提交
14 15 16
#include <asm/processor.h>
#include <asm/math_emu.h>
#include <asm/uaccess.h>
I
Ingo Molnar 已提交
17 18 19
#include <asm/ptrace.h>
#include <asm/i387.h>
#include <asm/user.h>
L
Linus Torvalds 已提交
20

R
Roland McGrath 已提交
21
#ifdef CONFIG_X86_64
I
Ingo Molnar 已提交
22 23
# include <asm/sigcontext32.h>
# include <asm/user32.h>
R
Roland McGrath 已提交
24
#else
25 26
# define save_i387_xstate_ia32		save_i387_xstate
# define restore_i387_xstate_ia32	restore_i387_xstate
I
Ingo Molnar 已提交
27
# define _fpstate_ia32		_fpstate
28
# define _xstate_ia32		_xstate
29
# define sig_xstate_ia32_size   sig_xstate_size
30
# define fx_sw_reserved_ia32	fx_sw_reserved
I
Ingo Molnar 已提交
31 32
# define user_i387_ia32_struct	user_i387_struct
# define user32_fxsr_struct	user_fxsr_struct
R
Roland McGrath 已提交
33 34
#endif

L
Linus Torvalds 已提交
35
#ifdef CONFIG_MATH_EMULATION
I
Ingo Molnar 已提交
36
# define HAVE_HWFP		(boot_cpu_data.hard_math)
L
Linus Torvalds 已提交
37
#else
I
Ingo Molnar 已提交
38
# define HAVE_HWFP		1
L
Linus Torvalds 已提交
39 40
#endif

I
Ingo Molnar 已提交
41
static unsigned int		mxcsr_feature_mask __read_mostly = 0xffffffffu;
42
unsigned int xstate_size;
43
unsigned int sig_xstate_ia32_size = sizeof(struct _fpstate_ia32);
44
static struct i387_fxsave_struct fx_scratch __cpuinitdata;
L
Linus Torvalds 已提交
45

46
void __cpuinit mxcsr_feature_mask_init(void)
L
Linus Torvalds 已提交
47 48
{
	unsigned long mask = 0;
I
Ingo Molnar 已提交
49

L
Linus Torvalds 已提交
50 51
	clts();
	if (cpu_has_fxsr) {
52 53 54
		memset(&fx_scratch, 0, sizeof(struct i387_fxsave_struct));
		asm volatile("fxsave %0" : : "m" (fx_scratch));
		mask = fx_scratch.mxcsr_mask;
55 56 57
		if (mask == 0)
			mask = 0x0000ffbf;
	}
L
Linus Torvalds 已提交
58 59 60 61
	mxcsr_feature_mask &= mask;
	stts();
}

62
void __cpuinit init_thread_xstate(void)
63
{
64 65 66 67 68
	if (!HAVE_HWFP) {
		xstate_size = sizeof(struct i387_soft_struct);
		return;
	}

69 70 71 72 73
	if (cpu_has_xsave) {
		xsave_cntxt_init();
		return;
	}

74 75 76 77 78 79 80 81
	if (cpu_has_fxsr)
		xstate_size = sizeof(struct i387_fxsave_struct);
#ifdef CONFIG_X86_32
	else
		xstate_size = sizeof(struct i387_fsave_struct);
#endif
}

R
Roland McGrath 已提交
82 83 84 85 86 87 88 89
#ifdef CONFIG_X86_64
/*
 * Called at bootup to set up the initial FPU state that is later cloned
 * into all processes.
 */
void __cpuinit fpu_init(void)
{
	unsigned long oldcr0 = read_cr0();
I
Ingo Molnar 已提交
90

R
Roland McGrath 已提交
91 92 93
	set_in_cr4(X86_CR4_OSFXSR);
	set_in_cr4(X86_CR4_OSXMMEXCPT);

I
Ingo Molnar 已提交
94
	write_cr0(oldcr0 & ~(X86_CR0_TS|X86_CR0_EM)); /* clear TS and EM */
R
Roland McGrath 已提交
95

96 97 98 99 100 101 102
	/*
	 * Boot processor to setup the FP and extended state context info.
	 */
	if (!smp_processor_id())
		init_thread_xstate();
	xsave_init();

R
Roland McGrath 已提交
103 104
	mxcsr_feature_mask_init();
	/* clean state in init */
105 106 107 108
	if (cpu_has_xsave)
		current_thread_info()->status = TS_XSAVE;
	else
		current_thread_info()->status = 0;
R
Roland McGrath 已提交
109 110 111 112
	clear_used_math();
}
#endif	/* CONFIG_X86_64 */

L
Linus Torvalds 已提交
113 114 115 116 117 118
/*
 * The _current_ task is using the FPU for the first time
 * so initialize it and set the mxcsr to its default
 * value at reset if we support XMM instructions and then
 * remeber the current task has used the FPU.
 */
119
int init_fpu(struct task_struct *tsk)
L
Linus Torvalds 已提交
120
{
R
Roland McGrath 已提交
121
	if (tsk_used_math(tsk)) {
122
		if (HAVE_HWFP && tsk == current)
R
Roland McGrath 已提交
123
			unlazy_fpu(tsk);
124 125 126 127 128 129 130 131 132 133 134
		return 0;
	}

	/*
	 * Memory allocation at the first usage of the FPU and other state.
	 */
	if (!tsk->thread.xstate) {
		tsk->thread.xstate = kmem_cache_alloc(task_xstate_cachep,
						      GFP_KERNEL);
		if (!tsk->thread.xstate)
			return -ENOMEM;
R
Roland McGrath 已提交
135 136
	}

137 138 139
#ifdef CONFIG_X86_32
	if (!HAVE_HWFP) {
		memset(tsk->thread.xstate, 0, xstate_size);
140
		finit_task(tsk);
141 142 143 144 145
		set_stopped_child_used_math(tsk);
		return 0;
	}
#endif

L
Linus Torvalds 已提交
146
	if (cpu_has_fxsr) {
147 148 149 150
		struct i387_fxsave_struct *fx = &tsk->thread.xstate->fxsave;

		memset(fx, 0, xstate_size);
		fx->cwd = 0x37f;
L
Linus Torvalds 已提交
151
		if (cpu_has_xmm)
152
			fx->mxcsr = MXCSR_DEFAULT;
L
Linus Torvalds 已提交
153
	} else {
154 155 156 157 158 159
		struct i387_fsave_struct *fp = &tsk->thread.xstate->fsave;
		memset(fp, 0, xstate_size);
		fp->cwd = 0xffff037fu;
		fp->swd = 0xffff0000u;
		fp->twd = 0xffffffffu;
		fp->fos = 0xffff0000u;
L
Linus Torvalds 已提交
160
	}
R
Roland McGrath 已提交
161 162 163
	/*
	 * Only the device not available exception or ptrace can call init_fpu.
	 */
L
Linus Torvalds 已提交
164
	set_stopped_child_used_math(tsk);
165
	return 0;
L
Linus Torvalds 已提交
166 167
}

168 169 170 171 172
/*
 * The xstateregs_active() routine is the same as the fpregs_active() routine,
 * as the "regset->n" for the xstate regset will be updated based on the feature
 * capabilites supported by the xsave.
 */
R
Roland McGrath 已提交
173 174 175 176
int fpregs_active(struct task_struct *target, const struct user_regset *regset)
{
	return tsk_used_math(target) ? regset->n : 0;
}
L
Linus Torvalds 已提交
177

R
Roland McGrath 已提交
178
int xfpregs_active(struct task_struct *target, const struct user_regset *regset)
L
Linus Torvalds 已提交
179
{
R
Roland McGrath 已提交
180 181
	return (cpu_has_fxsr && tsk_used_math(target)) ? regset->n : 0;
}
L
Linus Torvalds 已提交
182

R
Roland McGrath 已提交
183 184 185 186
int xfpregs_get(struct task_struct *target, const struct user_regset *regset,
		unsigned int pos, unsigned int count,
		void *kbuf, void __user *ubuf)
{
187 188
	int ret;

R
Roland McGrath 已提交
189 190 191
	if (!cpu_has_fxsr)
		return -ENODEV;

192 193 194
	ret = init_fpu(target);
	if (ret)
		return ret;
R
Roland McGrath 已提交
195 196

	return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
197
				   &target->thread.xstate->fxsave, 0, -1);
L
Linus Torvalds 已提交
198
}
R
Roland McGrath 已提交
199 200 201 202 203 204 205 206 207 208

int xfpregs_set(struct task_struct *target, const struct user_regset *regset,
		unsigned int pos, unsigned int count,
		const void *kbuf, const void __user *ubuf)
{
	int ret;

	if (!cpu_has_fxsr)
		return -ENODEV;

209 210 211 212
	ret = init_fpu(target);
	if (ret)
		return ret;

R
Roland McGrath 已提交
213
	ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
214
				 &target->thread.xstate->fxsave, 0, -1);
R
Roland McGrath 已提交
215 216 217 218

	/*
	 * mxcsr reserved bits must be masked to zero for security reasons.
	 */
219
	target->thread.xstate->fxsave.mxcsr &= mxcsr_feature_mask;
R
Roland McGrath 已提交
220

221 222 223 224 225 226 227
	/*
	 * update the header bits in the xsave header, indicating the
	 * presence of FP and SSE state.
	 */
	if (cpu_has_xsave)
		target->thread.xstate->xsave.xsave_hdr.xstate_bv |= XSTATE_FPSSE;

R
Roland McGrath 已提交
228 229 230
	return ret;
}

231 232 233 234 235 236 237 238 239 240 241 242 243 244
int xstateregs_get(struct task_struct *target, const struct user_regset *regset,
		unsigned int pos, unsigned int count,
		void *kbuf, void __user *ubuf)
{
	int ret;

	if (!cpu_has_xsave)
		return -ENODEV;

	ret = init_fpu(target);
	if (ret)
		return ret;

	/*
245 246 247
	 * Copy the 48bytes defined by the software first into the xstate
	 * memory layout in the thread struct, so that we can copy the entire
	 * xstateregs to the user using one user_regset_copyout().
248
	 */
249 250
	memcpy(&target->thread.xstate->fxsave.sw_reserved,
	       xstate_fx_sw_bytes, sizeof(xstate_fx_sw_bytes));
251 252

	/*
253
	 * Copy the xstate memory layout.
254 255
	 */
	ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
256
				  &target->thread.xstate->xsave, 0, -1);
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
	return ret;
}

int xstateregs_set(struct task_struct *target, const struct user_regset *regset,
		  unsigned int pos, unsigned int count,
		  const void *kbuf, const void __user *ubuf)
{
	int ret;
	struct xsave_hdr_struct *xsave_hdr;

	if (!cpu_has_xsave)
		return -ENODEV;

	ret = init_fpu(target);
	if (ret)
		return ret;

	ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
				 &target->thread.xstate->xsave, 0, -1);

	/*
	 * mxcsr reserved bits must be masked to zero for security reasons.
	 */
	target->thread.xstate->fxsave.mxcsr &= mxcsr_feature_mask;

	xsave_hdr = &target->thread.xstate->xsave.xsave_hdr;

	xsave_hdr->xstate_bv &= pcntxt_mask;
	/*
	 * These bits must be zero.
	 */
	xsave_hdr->reserved1[0] = xsave_hdr->reserved1[1] = 0;

	return ret;
}

R
Roland McGrath 已提交
293
#if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
L
Linus Torvalds 已提交
294 295 296 297 298

/*
 * FPU tag word conversions.
 */

299
static inline unsigned short twd_i387_to_fxsr(unsigned short twd)
L
Linus Torvalds 已提交
300 301
{
	unsigned int tmp; /* to avoid 16 bit prefixes in the code */
302

L
Linus Torvalds 已提交
303
	/* Transform each pair of bits into 01 (valid) or 00 (empty) */
304
	tmp = ~twd;
R
Roland McGrath 已提交
305
	tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */
306 307 308 309
	/* and move the valid bits to the lower byte. */
	tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */
	tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */
	tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */
I
Ingo Molnar 已提交
310

311
	return tmp;
L
Linus Torvalds 已提交
312 313 314
}

#define FPREG_ADDR(f, n)	((void *)&(f)->st_space + (n) * 16);
R
Roland McGrath 已提交
315 316 317 318 319 320 321 322 323 324 325 326 327
#define FP_EXP_TAG_VALID	0
#define FP_EXP_TAG_ZERO		1
#define FP_EXP_TAG_SPECIAL	2
#define FP_EXP_TAG_EMPTY	3

static inline u32 twd_fxsr_to_i387(struct i387_fxsave_struct *fxsave)
{
	struct _fpxreg *st;
	u32 tos = (fxsave->swd >> 11) & 7;
	u32 twd = (unsigned long) fxsave->twd;
	u32 tag;
	u32 ret = 0xffff0000u;
	int i;
L
Linus Torvalds 已提交
328

R
Roland McGrath 已提交
329
	for (i = 0; i < 8; i++, twd >>= 1) {
330 331
		if (twd & 0x1) {
			st = FPREG_ADDR(fxsave, (i - tos) & 7);
L
Linus Torvalds 已提交
332

333
			switch (st->exponent & 0x7fff) {
L
Linus Torvalds 已提交
334
			case 0x7fff:
R
Roland McGrath 已提交
335
				tag = FP_EXP_TAG_SPECIAL;
L
Linus Torvalds 已提交
336 337
				break;
			case 0x0000:
338 339 340
				if (!st->significand[0] &&
				    !st->significand[1] &&
				    !st->significand[2] &&
R
Roland McGrath 已提交
341 342 343 344
				    !st->significand[3])
					tag = FP_EXP_TAG_ZERO;
				else
					tag = FP_EXP_TAG_SPECIAL;
L
Linus Torvalds 已提交
345 346
				break;
			default:
R
Roland McGrath 已提交
347 348 349 350
				if (st->significand[3] & 0x8000)
					tag = FP_EXP_TAG_VALID;
				else
					tag = FP_EXP_TAG_SPECIAL;
L
Linus Torvalds 已提交
351 352 353
				break;
			}
		} else {
R
Roland McGrath 已提交
354
			tag = FP_EXP_TAG_EMPTY;
L
Linus Torvalds 已提交
355
		}
R
Roland McGrath 已提交
356
		ret |= tag << (2 * i);
L
Linus Torvalds 已提交
357 358 359 360 361
	}
	return ret;
}

/*
R
Roland McGrath 已提交
362
 * FXSR floating point environment conversions.
L
Linus Torvalds 已提交
363 364
 */

I
Ingo Molnar 已提交
365 366
static void
convert_from_fxsr(struct user_i387_ia32_struct *env, struct task_struct *tsk)
L
Linus Torvalds 已提交
367
{
368
	struct i387_fxsave_struct *fxsave = &tsk->thread.xstate->fxsave;
R
Roland McGrath 已提交
369 370 371
	struct _fpreg *to = (struct _fpreg *) &env->st_space[0];
	struct _fpxreg *from = (struct _fpxreg *) &fxsave->st_space[0];
	int i;
L
Linus Torvalds 已提交
372

R
Roland McGrath 已提交
373 374 375 376 377 378 379 380 381 382 383 384
	env->cwd = fxsave->cwd | 0xffff0000u;
	env->swd = fxsave->swd | 0xffff0000u;
	env->twd = twd_fxsr_to_i387(fxsave);

#ifdef CONFIG_X86_64
	env->fip = fxsave->rip;
	env->foo = fxsave->rdp;
	if (tsk == current) {
		/*
		 * should be actually ds/cs at fpu exception time, but
		 * that information is not available in 64bit mode.
		 */
I
Ingo Molnar 已提交
385 386
		asm("mov %%ds, %[fos]" : [fos] "=r" (env->fos));
		asm("mov %%cs, %[fcs]" : [fcs] "=r" (env->fcs));
L
Linus Torvalds 已提交
387
	} else {
R
Roland McGrath 已提交
388
		struct pt_regs *regs = task_pt_regs(tsk);
I
Ingo Molnar 已提交
389

R
Roland McGrath 已提交
390 391
		env->fos = 0xffff0000 | tsk->thread.ds;
		env->fcs = regs->cs;
L
Linus Torvalds 已提交
392
	}
R
Roland McGrath 已提交
393 394
#else
	env->fip = fxsave->fip;
J
Jan Beulich 已提交
395
	env->fcs = (u16) fxsave->fcs | ((u32) fxsave->fop << 16);
R
Roland McGrath 已提交
396 397 398
	env->foo = fxsave->foo;
	env->fos = fxsave->fos;
#endif
L
Linus Torvalds 已提交
399

R
Roland McGrath 已提交
400 401
	for (i = 0; i < 8; ++i)
		memcpy(&to[i], &from[i], sizeof(to[0]));
L
Linus Torvalds 已提交
402 403
}

R
Roland McGrath 已提交
404 405
static void convert_to_fxsr(struct task_struct *tsk,
			    const struct user_i387_ia32_struct *env)
L
Linus Torvalds 已提交
406 407

{
408
	struct i387_fxsave_struct *fxsave = &tsk->thread.xstate->fxsave;
R
Roland McGrath 已提交
409 410 411
	struct _fpreg *from = (struct _fpreg *) &env->st_space[0];
	struct _fpxreg *to = (struct _fpxreg *) &fxsave->st_space[0];
	int i;
L
Linus Torvalds 已提交
412

R
Roland McGrath 已提交
413 414 415 416 417 418 419 420 421 422 423 424 425 426
	fxsave->cwd = env->cwd;
	fxsave->swd = env->swd;
	fxsave->twd = twd_i387_to_fxsr(env->twd);
	fxsave->fop = (u16) ((u32) env->fcs >> 16);
#ifdef CONFIG_X86_64
	fxsave->rip = env->fip;
	fxsave->rdp = env->foo;
	/* cs and ds ignored */
#else
	fxsave->fip = env->fip;
	fxsave->fcs = (env->fcs & 0xffff);
	fxsave->foo = env->foo;
	fxsave->fos = env->fos;
#endif
L
Linus Torvalds 已提交
427

R
Roland McGrath 已提交
428 429
	for (i = 0; i < 8; ++i)
		memcpy(&to[i], &from[i], sizeof(from[0]));
L
Linus Torvalds 已提交
430 431
}

R
Roland McGrath 已提交
432 433 434
int fpregs_get(struct task_struct *target, const struct user_regset *regset,
	       unsigned int pos, unsigned int count,
	       void *kbuf, void __user *ubuf)
L
Linus Torvalds 已提交
435
{
R
Roland McGrath 已提交
436
	struct user_i387_ia32_struct env;
437
	int ret;
L
Linus Torvalds 已提交
438

439 440 441
	ret = init_fpu(target);
	if (ret)
		return ret;
L
Linus Torvalds 已提交
442

443 444 445
	if (!HAVE_HWFP)
		return fpregs_soft_get(target, regset, pos, count, kbuf, ubuf);

I
Ingo Molnar 已提交
446
	if (!cpu_has_fxsr) {
R
Roland McGrath 已提交
447
		return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
448 449
					   &target->thread.xstate->fsave, 0,
					   -1);
I
Ingo Molnar 已提交
450
	}
L
Linus Torvalds 已提交
451

R
Roland McGrath 已提交
452 453 454
	if (kbuf && pos == 0 && count == sizeof(env)) {
		convert_from_fxsr(kbuf, target);
		return 0;
L
Linus Torvalds 已提交
455
	}
R
Roland McGrath 已提交
456 457

	convert_from_fxsr(&env, target);
I
Ingo Molnar 已提交
458

R
Roland McGrath 已提交
459
	return user_regset_copyout(&pos, &count, &kbuf, &ubuf, &env, 0, -1);
L
Linus Torvalds 已提交
460 461
}

R
Roland McGrath 已提交
462 463 464
int fpregs_set(struct task_struct *target, const struct user_regset *regset,
	       unsigned int pos, unsigned int count,
	       const void *kbuf, const void __user *ubuf)
L
Linus Torvalds 已提交
465
{
R
Roland McGrath 已提交
466 467
	struct user_i387_ia32_struct env;
	int ret;
L
Linus Torvalds 已提交
468

469 470 471 472
	ret = init_fpu(target);
	if (ret)
		return ret;

473 474 475
	if (!HAVE_HWFP)
		return fpregs_soft_set(target, regset, pos, count, kbuf, ubuf);

I
Ingo Molnar 已提交
476
	if (!cpu_has_fxsr) {
R
Roland McGrath 已提交
477
		return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
478
					  &target->thread.xstate->fsave, 0, -1);
I
Ingo Molnar 已提交
479
	}
R
Roland McGrath 已提交
480 481 482 483 484 485 486 487

	if (pos > 0 || count < sizeof(env))
		convert_from_fxsr(&env, target);

	ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &env, 0, -1);
	if (!ret)
		convert_to_fxsr(target, &env);

488 489 490 491 492 493
	/*
	 * update the header bit in the xsave header, indicating the
	 * presence of FP.
	 */
	if (cpu_has_xsave)
		target->thread.xstate->xsave.xsave_hdr.xstate_bv |= XSTATE_FP;
R
Roland McGrath 已提交
494
	return ret;
L
Linus Torvalds 已提交
495 496 497 498 499 500
}

/*
 * Signal frame handlers.
 */

R
Roland McGrath 已提交
501
static inline int save_i387_fsave(struct _fpstate_ia32 __user *buf)
L
Linus Torvalds 已提交
502 503
{
	struct task_struct *tsk = current;
504
	struct i387_fsave_struct *fp = &tsk->thread.xstate->fsave;
L
Linus Torvalds 已提交
505

506 507
	fp->status = fp->swd;
	if (__copy_to_user(buf, fp, sizeof(struct i387_fsave_struct)))
L
Linus Torvalds 已提交
508 509 510 511
		return -1;
	return 1;
}

R
Roland McGrath 已提交
512
static int save_i387_fxsave(struct _fpstate_ia32 __user *buf)
L
Linus Torvalds 已提交
513 514
{
	struct task_struct *tsk = current;
515
	struct i387_fxsave_struct *fx = &tsk->thread.xstate->fxsave;
R
Roland McGrath 已提交
516
	struct user_i387_ia32_struct env;
L
Linus Torvalds 已提交
517 518
	int err = 0;

R
Roland McGrath 已提交
519 520
	convert_from_fxsr(&env, tsk);
	if (__copy_to_user(buf, &env, sizeof(env)))
L
Linus Torvalds 已提交
521 522
		return -1;

523
	err |= __put_user(fx->swd, &buf->status);
524 525
	err |= __put_user(X86_FXSR_MAGIC, &buf->magic);
	if (err)
L
Linus Torvalds 已提交
526 527
		return -1;

528
	if (__copy_to_user(&buf->_fxsr_env[0], fx, xstate_size))
L
Linus Torvalds 已提交
529 530 531 532
		return -1;
	return 1;
}

533 534
static int save_i387_xsave(void __user *buf)
{
535
	struct task_struct *tsk = current;
536 537 538
	struct _fpstate_ia32 __user *fx = buf;
	int err = 0;

539 540 541 542 543 544 545 546 547 548 549 550 551
	/*
	 * For legacy compatible, we always set FP/SSE bits in the bit
	 * vector while saving the state to the user context.
	 * This will enable us capturing any changes(during sigreturn) to
	 * the FP/SSE bits by the legacy applications which don't touch
	 * xstate_bv in the xsave header.
	 *
	 * xsave aware applications can change the xstate_bv in the xsave
	 * header as well as change any contents in the memory layout.
	 * xrestore as part of sigreturn will capture all the changes.
	 */
	tsk->thread.xstate->xsave.xsave_hdr.xstate_bv |= XSTATE_FPSSE;

552 553 554 555 556 557 558 559 560 561 562 563 564 565
	if (save_i387_fxsave(fx) < 0)
		return -1;

	err = __copy_to_user(&fx->sw_reserved, &fx_sw_reserved_ia32,
			     sizeof(struct _fpx_sw_bytes));
	err |= __put_user(FP_XSTATE_MAGIC2,
			  (__u32 __user *) (buf + sig_xstate_ia32_size
					    - FP_XSTATE_MAGIC2_SIZE));
	if (err)
		return -1;

	return 1;
}

566
int save_i387_xstate_ia32(void __user *buf)
L
Linus Torvalds 已提交
567
{
568 569 570
	struct _fpstate_ia32 __user *fp = (struct _fpstate_ia32 __user *) buf;
	struct task_struct *tsk = current;

571
	if (!used_math())
L
Linus Torvalds 已提交
572
		return 0;
573 574 575

	if (!access_ok(VERIFY_WRITE, buf, sig_xstate_ia32_size))
		return -EACCES;
I
Ingo Molnar 已提交
576 577
	/*
	 * This will cause a "finit" to be triggered by the next
L
Linus Torvalds 已提交
578 579 580 581
	 * attempted FPU operation by the 'current' process.
	 */
	clear_used_math();

I
Ingo Molnar 已提交
582
	if (!HAVE_HWFP) {
R
Roland McGrath 已提交
583 584
		return fpregs_soft_get(current, NULL,
				       0, sizeof(struct user_i387_ia32_struct),
585
				       NULL, fp) ? -1 : 1;
L
Linus Torvalds 已提交
586
	}
I
Ingo Molnar 已提交
587

588 589
	unlazy_fpu(tsk);

590 591
	if (cpu_has_xsave)
		return save_i387_xsave(fp);
I
Ingo Molnar 已提交
592
	if (cpu_has_fxsr)
593
		return save_i387_fxsave(fp);
I
Ingo Molnar 已提交
594
	else
595
		return save_i387_fsave(fp);
L
Linus Torvalds 已提交
596 597
}

R
Roland McGrath 已提交
598
static inline int restore_i387_fsave(struct _fpstate_ia32 __user *buf)
L
Linus Torvalds 已提交
599 600
{
	struct task_struct *tsk = current;
I
Ingo Molnar 已提交
601

602
	return __copy_from_user(&tsk->thread.xstate->fsave, buf,
603
				sizeof(struct i387_fsave_struct));
L
Linus Torvalds 已提交
604 605
}

606 607
static int restore_i387_fxsave(struct _fpstate_ia32 __user *buf,
			       unsigned int size)
L
Linus Torvalds 已提交
608 609
{
	struct task_struct *tsk = current;
R
Roland McGrath 已提交
610
	struct user_i387_ia32_struct env;
I
Ingo Molnar 已提交
611 612
	int err;

613
	err = __copy_from_user(&tsk->thread.xstate->fxsave, &buf->_fxsr_env[0],
614
			       size);
L
Linus Torvalds 已提交
615
	/* mxcsr reserved bits must be masked to zero for security reasons */
616
	tsk->thread.xstate->fxsave.mxcsr &= mxcsr_feature_mask;
R
Roland McGrath 已提交
617 618 619
	if (err || __copy_from_user(&env, buf, sizeof(env)))
		return 1;
	convert_to_fxsr(tsk, &env);
I
Ingo Molnar 已提交
620

R
Roland McGrath 已提交
621
	return 0;
L
Linus Torvalds 已提交
622 623
}

624 625 626 627 628 629 630 631 632
static int restore_i387_xsave(void __user *buf)
{
	struct _fpx_sw_bytes fx_sw_user;
	struct _fpstate_ia32 __user *fx_user =
			((struct _fpstate_ia32 __user *) buf);
	struct i387_fxsave_struct __user *fx =
		(struct i387_fxsave_struct __user *) &fx_user->_fxsr_env[0];
	struct xsave_hdr_struct *xsave_hdr =
				&current->thread.xstate->xsave.xsave_hdr;
633
	u64 mask;
634 635 636 637 638
	int err;

	if (check_for_xstate(fx, buf, &fx_sw_user))
		goto fx_only;

639
	mask = fx_sw_user.xstate_bv;
640 641 642

	err = restore_i387_fxsave(buf, fx_sw_user.xstate_size);

643
	xsave_hdr->xstate_bv &= pcntxt_mask;
644 645 646 647 648 649 650 651 652
	/*
	 * These bits must be zero.
	 */
	xsave_hdr->reserved1[0] = xsave_hdr->reserved1[1] = 0;

	/*
	 * Init the state that is not present in the memory layout
	 * and enabled by the OS.
	 */
653 654
	mask = ~(pcntxt_mask & ~mask);
	xsave_hdr->xstate_bv &= mask;
655 656 657 658 659 660 661 662 663 664 665 666

	return err;
fx_only:
	/*
	 * Couldn't find the extended state information in the memory
	 * layout. Restore the FP/SSE and init the other extended state
	 * enabled by the OS.
	 */
	xsave_hdr->xstate_bv = XSTATE_FPSSE;
	return restore_i387_fxsave(buf, sizeof(struct i387_fxsave_struct));
}

667
int restore_i387_xstate_ia32(void __user *buf)
L
Linus Torvalds 已提交
668 669
{
	int err;
670
	struct task_struct *tsk = current;
671
	struct _fpstate_ia32 __user *fp = (struct _fpstate_ia32 __user *) buf;
L
Linus Torvalds 已提交
672

673
	if (HAVE_HWFP)
674 675
		clear_fpu(tsk);

676 677 678 679 680 681 682 683 684 685 686
	if (!buf) {
		if (used_math()) {
			clear_fpu(tsk);
			clear_used_math();
		}

		return 0;
	} else
		if (!access_ok(VERIFY_READ, buf, sig_xstate_ia32_size))
			return -EACCES;

687 688 689 690 691
	if (!used_math()) {
		err = init_fpu(tsk);
		if (err)
			return err;
	}
692

693
	if (HAVE_HWFP) {
694 695 696 697 698
		if (cpu_has_xsave)
			err = restore_i387_xsave(buf);
		else if (cpu_has_fxsr)
			err = restore_i387_fxsave(fp, sizeof(struct
							   i387_fxsave_struct));
I
Ingo Molnar 已提交
699
		else
700
			err = restore_i387_fsave(fp);
L
Linus Torvalds 已提交
701
	} else {
R
Roland McGrath 已提交
702 703
		err = fpregs_soft_set(current, NULL,
				      0, sizeof(struct user_i387_ia32_struct),
704
				      NULL, fp) != 0;
L
Linus Torvalds 已提交
705 706
	}
	set_used_math();
I
Ingo Molnar 已提交
707

L
Linus Torvalds 已提交
708 709 710 711 712
	return err;
}

/*
 * FPU state for core dumps.
R
Roland McGrath 已提交
713 714 715 716
 * This is only used for a.out dumps now.
 * It is declared generically using elf_fpregset_t (which is
 * struct user_i387_struct) but is in fact only used for 32-bit
 * dumps, so on 64-bit it is really struct user_i387_ia32_struct.
L
Linus Torvalds 已提交
717
 */
718
int dump_fpu(struct pt_regs *regs, struct user_i387_struct *fpu)
L
Linus Torvalds 已提交
719 720
{
	struct task_struct *tsk = current;
I
Ingo Molnar 已提交
721
	int fpvalid;
L
Linus Torvalds 已提交
722 723

	fpvalid = !!used_math();
R
Roland McGrath 已提交
724 725 726 727
	if (fpvalid)
		fpvalid = !fpregs_get(tsk, NULL,
				      0, sizeof(struct user_i387_ia32_struct),
				      fpu, NULL);
L
Linus Torvalds 已提交
728 729 730

	return fpvalid;
}
731
EXPORT_SYMBOL(dump_fpu);
L
Linus Torvalds 已提交
732

R
Roland McGrath 已提交
733
#endif	/* CONFIG_X86_32 || CONFIG_IA32_EMULATION */