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 */
A
Avi Kivity 已提交
105
	current_thread_info()->status = 0;
R
Roland McGrath 已提交
106 107 108 109
	clear_used_math();
}
#endif	/* CONFIG_X86_64 */

L
Linus Torvalds 已提交
110 111 112 113 114 115
/*
 * 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.
 */
116
int init_fpu(struct task_struct *tsk)
L
Linus Torvalds 已提交
117
{
R
Roland McGrath 已提交
118
	if (tsk_used_math(tsk)) {
119
		if (HAVE_HWFP && tsk == current)
R
Roland McGrath 已提交
120
			unlazy_fpu(tsk);
121 122 123 124 125 126 127 128 129 130 131
		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 已提交
132 133
	}

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

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

		memset(fx, 0, xstate_size);
		fx->cwd = 0x37f;
L
Linus Torvalds 已提交
148
		if (cpu_has_xmm)
149
			fx->mxcsr = MXCSR_DEFAULT;
L
Linus Torvalds 已提交
150
	} else {
151 152 153 154 155 156
		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 已提交
157
	}
R
Roland McGrath 已提交
158 159 160
	/*
	 * Only the device not available exception or ptrace can call init_fpu.
	 */
L
Linus Torvalds 已提交
161
	set_stopped_child_used_math(tsk);
162
	return 0;
L
Linus Torvalds 已提交
163 164
}

165 166 167 168 169
/*
 * 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 已提交
170 171 172 173
int fpregs_active(struct task_struct *target, const struct user_regset *regset)
{
	return tsk_used_math(target) ? regset->n : 0;
}
L
Linus Torvalds 已提交
174

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

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

R
Roland McGrath 已提交
186 187 188
	if (!cpu_has_fxsr)
		return -ENODEV;

189 190 191
	ret = init_fpu(target);
	if (ret)
		return ret;
R
Roland McGrath 已提交
192 193

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

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;

206 207 208 209
	ret = init_fpu(target);
	if (ret)
		return ret;

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

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

218 219 220 221 222 223 224
	/*
	 * 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 已提交
225 226 227
	return ret;
}

228 229 230 231 232 233 234 235 236 237 238 239 240 241
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;

	/*
242 243 244
	 * 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().
245
	 */
246 247
	memcpy(&target->thread.xstate->fxsave.sw_reserved,
	       xstate_fx_sw_bytes, sizeof(xstate_fx_sw_bytes));
248 249

	/*
250
	 * Copy the xstate memory layout.
251 252
	 */
	ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
253
				  &target->thread.xstate->xsave, 0, -1);
254 255 256 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
	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 已提交
290
#if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
L
Linus Torvalds 已提交
291 292 293 294 295

/*
 * FPU tag word conversions.
 */

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

L
Linus Torvalds 已提交
300
	/* Transform each pair of bits into 01 (valid) or 00 (empty) */
301
	tmp = ~twd;
R
Roland McGrath 已提交
302
	tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */
303 304 305 306
	/* 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 已提交
307

308
	return tmp;
L
Linus Torvalds 已提交
309 310 311
}

#define FPREG_ADDR(f, n)	((void *)&(f)->st_space + (n) * 16);
R
Roland McGrath 已提交
312 313 314 315 316 317 318 319 320 321 322 323 324
#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 已提交
325

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

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

/*
R
Roland McGrath 已提交
359
 * FXSR floating point environment conversions.
L
Linus Torvalds 已提交
360 361
 */

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

R
Roland McGrath 已提交
370 371 372 373 374 375 376 377 378 379 380 381
	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 已提交
382 383
		asm("mov %%ds, %[fos]" : [fos] "=r" (env->fos));
		asm("mov %%cs, %[fcs]" : [fcs] "=r" (env->fcs));
L
Linus Torvalds 已提交
384
	} else {
R
Roland McGrath 已提交
385
		struct pt_regs *regs = task_pt_regs(tsk);
I
Ingo Molnar 已提交
386

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

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

R
Roland McGrath 已提交
401 402
static void convert_to_fxsr(struct task_struct *tsk,
			    const struct user_i387_ia32_struct *env)
L
Linus Torvalds 已提交
403 404

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

R
Roland McGrath 已提交
410 411 412 413 414 415 416 417 418 419 420 421 422 423
	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 已提交
424

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

R
Roland McGrath 已提交
429 430 431
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 已提交
432
{
R
Roland McGrath 已提交
433
	struct user_i387_ia32_struct env;
434
	int ret;
L
Linus Torvalds 已提交
435

436 437 438
	ret = init_fpu(target);
	if (ret)
		return ret;
L
Linus Torvalds 已提交
439

440 441 442
	if (!HAVE_HWFP)
		return fpregs_soft_get(target, regset, pos, count, kbuf, ubuf);

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

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

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

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

R
Roland McGrath 已提交
459 460 461
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 已提交
462
{
R
Roland McGrath 已提交
463 464
	struct user_i387_ia32_struct env;
	int ret;
L
Linus Torvalds 已提交
465

466 467 468 469
	ret = init_fpu(target);
	if (ret)
		return ret;

470 471 472
	if (!HAVE_HWFP)
		return fpregs_soft_set(target, regset, pos, count, kbuf, ubuf);

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

	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);

485 486 487 488 489 490
	/*
	 * 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 已提交
491
	return ret;
L
Linus Torvalds 已提交
492 493 494 495 496 497
}

/*
 * Signal frame handlers.
 */

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

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

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

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

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

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

530 531
static int save_i387_xsave(void __user *buf)
{
532
	struct task_struct *tsk = current;
533 534 535
	struct _fpstate_ia32 __user *fx = buf;
	int err = 0;

536 537 538 539 540 541 542 543 544 545 546 547 548
	/*
	 * 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;

549 550 551 552 553 554 555 556 557 558 559 560 561 562
	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;
}

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

568
	if (!used_math())
L
Linus Torvalds 已提交
569
		return 0;
570 571 572

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

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

585 586
	unlazy_fpu(tsk);

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

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

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

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

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

R
Roland McGrath 已提交
618
	return 0;
L
Linus Torvalds 已提交
619 620
}

621 622 623 624 625 626 627 628 629
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;
630
	u64 mask;
631 632 633 634 635
	int err;

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

636
	mask = fx_sw_user.xstate_bv;
637 638 639

	err = restore_i387_fxsave(buf, fx_sw_user.xstate_size);

640
	xsave_hdr->xstate_bv &= pcntxt_mask;
641 642 643 644 645 646 647 648 649
	/*
	 * 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.
	 */
650 651
	mask = ~(pcntxt_mask & ~mask);
	xsave_hdr->xstate_bv &= mask;
652 653 654 655 656 657 658 659 660 661 662 663

	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));
}

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

670
	if (HAVE_HWFP)
671 672
		clear_fpu(tsk);

673 674 675 676 677 678 679 680 681 682 683
	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;

684 685 686 687 688
	if (!used_math()) {
		err = init_fpu(tsk);
		if (err)
			return err;
	}
689

690
	if (HAVE_HWFP) {
691 692 693 694 695
		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 已提交
696
		else
697
			err = restore_i387_fsave(fp);
L
Linus Torvalds 已提交
698
	} else {
R
Roland McGrath 已提交
699 700
		err = fpregs_soft_set(current, NULL,
				      0, sizeof(struct user_i387_ia32_struct),
701
				      NULL, fp) != 0;
L
Linus Torvalds 已提交
702 703
	}
	set_used_math();
I
Ingo Molnar 已提交
704

L
Linus Torvalds 已提交
705 706 707 708 709
	return err;
}

/*
 * FPU state for core dumps.
R
Roland McGrath 已提交
710 711 712 713
 * 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 已提交
714
 */
715
int dump_fpu(struct pt_regs *regs, struct user_i387_struct *fpu)
L
Linus Torvalds 已提交
716 717
{
	struct task_struct *tsk = current;
I
Ingo Molnar 已提交
718
	int fpvalid;
L
Linus Torvalds 已提交
719 720

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

	return fpvalid;
}
728
EXPORT_SYMBOL(dump_fpu);
L
Linus Torvalds 已提交
729

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