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 11 12
#include <linux/sched.h>

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

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

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

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

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

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

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

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

73 74 75 76 77 78 79 80
	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 已提交
81 82 83 84 85 86 87 88
#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 已提交
89

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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;

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

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

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

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

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

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

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

/*
 * FPU tag word conversions.
 */

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

/*
 * Signal frame handlers.
 */

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

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

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

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

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

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

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

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

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

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

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

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

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

587 588
	unlazy_fpu(tsk);

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

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

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

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

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

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

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

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

638
	mask = fx_sw_user.xstate_bv;
639 640 641

	err = restore_i387_fxsave(buf, fx_sw_user.xstate_size);

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

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

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

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

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

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

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

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

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

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

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

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