i387.c 15.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 62
void __init init_thread_xstate(void)
{
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 139 140 141 142 143 144
#ifdef CONFIG_X86_32
	if (!HAVE_HWFP) {
		memset(tsk->thread.xstate, 0, xstate_size);
		finit();
		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
}

R
Roland McGrath 已提交
167 168 169 170
int fpregs_active(struct task_struct *target, const struct user_regset *regset)
{
	return tsk_used_math(target) ? regset->n : 0;
}
L
Linus Torvalds 已提交
171

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

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

R
Roland McGrath 已提交
183 184 185
	if (!cpu_has_fxsr)
		return -ENODEV;

186 187 188
	ret = init_fpu(target);
	if (ret)
		return ret;
R
Roland McGrath 已提交
189 190

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

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;

203 204 205 206
	ret = init_fpu(target);
	if (ret)
		return ret;

R
Roland McGrath 已提交
207 208 209
	set_stopped_child_used_math(target);

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

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

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

#if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
L
Linus Torvalds 已提交
228 229 230 231 232

/*
 * FPU tag word conversions.
 */

233
static inline unsigned short twd_i387_to_fxsr(unsigned short twd)
L
Linus Torvalds 已提交
234 235
{
	unsigned int tmp; /* to avoid 16 bit prefixes in the code */
236

L
Linus Torvalds 已提交
237
	/* Transform each pair of bits into 01 (valid) or 00 (empty) */
238
	tmp = ~twd;
R
Roland McGrath 已提交
239
	tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */
240 241 242 243
	/* 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 已提交
244

245
	return tmp;
L
Linus Torvalds 已提交
246 247 248
}

#define FPREG_ADDR(f, n)	((void *)&(f)->st_space + (n) * 16);
R
Roland McGrath 已提交
249 250 251 252 253 254 255 256 257 258 259 260 261
#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 已提交
262

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

267
			switch (st->exponent & 0x7fff) {
L
Linus Torvalds 已提交
268
			case 0x7fff:
R
Roland McGrath 已提交
269
				tag = FP_EXP_TAG_SPECIAL;
L
Linus Torvalds 已提交
270 271
				break;
			case 0x0000:
272 273 274
				if (!st->significand[0] &&
				    !st->significand[1] &&
				    !st->significand[2] &&
R
Roland McGrath 已提交
275 276 277 278
				    !st->significand[3])
					tag = FP_EXP_TAG_ZERO;
				else
					tag = FP_EXP_TAG_SPECIAL;
L
Linus Torvalds 已提交
279 280
				break;
			default:
R
Roland McGrath 已提交
281 282 283 284
				if (st->significand[3] & 0x8000)
					tag = FP_EXP_TAG_VALID;
				else
					tag = FP_EXP_TAG_SPECIAL;
L
Linus Torvalds 已提交
285 286 287
				break;
			}
		} else {
R
Roland McGrath 已提交
288
			tag = FP_EXP_TAG_EMPTY;
L
Linus Torvalds 已提交
289
		}
R
Roland McGrath 已提交
290
		ret |= tag << (2 * i);
L
Linus Torvalds 已提交
291 292 293 294 295
	}
	return ret;
}

/*
R
Roland McGrath 已提交
296
 * FXSR floating point environment conversions.
L
Linus Torvalds 已提交
297 298
 */

I
Ingo Molnar 已提交
299 300
static void
convert_from_fxsr(struct user_i387_ia32_struct *env, struct task_struct *tsk)
L
Linus Torvalds 已提交
301
{
302
	struct i387_fxsave_struct *fxsave = &tsk->thread.xstate->fxsave;
R
Roland McGrath 已提交
303 304 305
	struct _fpreg *to = (struct _fpreg *) &env->st_space[0];
	struct _fpxreg *from = (struct _fpxreg *) &fxsave->st_space[0];
	int i;
L
Linus Torvalds 已提交
306

R
Roland McGrath 已提交
307 308 309 310 311 312 313 314 315 316 317 318
	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 已提交
319 320
		asm("mov %%ds, %[fos]" : [fos] "=r" (env->fos));
		asm("mov %%cs, %[fcs]" : [fcs] "=r" (env->fcs));
L
Linus Torvalds 已提交
321
	} else {
R
Roland McGrath 已提交
322
		struct pt_regs *regs = task_pt_regs(tsk);
I
Ingo Molnar 已提交
323

R
Roland McGrath 已提交
324 325
		env->fos = 0xffff0000 | tsk->thread.ds;
		env->fcs = regs->cs;
L
Linus Torvalds 已提交
326
	}
R
Roland McGrath 已提交
327 328
#else
	env->fip = fxsave->fip;
J
Jan Beulich 已提交
329
	env->fcs = (u16) fxsave->fcs | ((u32) fxsave->fop << 16);
R
Roland McGrath 已提交
330 331 332
	env->foo = fxsave->foo;
	env->fos = fxsave->fos;
#endif
L
Linus Torvalds 已提交
333

R
Roland McGrath 已提交
334 335
	for (i = 0; i < 8; ++i)
		memcpy(&to[i], &from[i], sizeof(to[0]));
L
Linus Torvalds 已提交
336 337
}

R
Roland McGrath 已提交
338 339
static void convert_to_fxsr(struct task_struct *tsk,
			    const struct user_i387_ia32_struct *env)
L
Linus Torvalds 已提交
340 341

{
342
	struct i387_fxsave_struct *fxsave = &tsk->thread.xstate->fxsave;
R
Roland McGrath 已提交
343 344 345
	struct _fpreg *from = (struct _fpreg *) &env->st_space[0];
	struct _fpxreg *to = (struct _fpxreg *) &fxsave->st_space[0];
	int i;
L
Linus Torvalds 已提交
346

R
Roland McGrath 已提交
347 348 349 350 351 352 353 354 355 356 357 358 359 360
	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 已提交
361

R
Roland McGrath 已提交
362 363
	for (i = 0; i < 8; ++i)
		memcpy(&to[i], &from[i], sizeof(from[0]));
L
Linus Torvalds 已提交
364 365
}

R
Roland McGrath 已提交
366 367 368
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 已提交
369
{
R
Roland McGrath 已提交
370
	struct user_i387_ia32_struct env;
371
	int ret;
L
Linus Torvalds 已提交
372

373 374 375
	ret = init_fpu(target);
	if (ret)
		return ret;
L
Linus Torvalds 已提交
376

377 378 379
	if (!HAVE_HWFP)
		return fpregs_soft_get(target, regset, pos, count, kbuf, ubuf);

I
Ingo Molnar 已提交
380
	if (!cpu_has_fxsr) {
R
Roland McGrath 已提交
381
		return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
382 383
					   &target->thread.xstate->fsave, 0,
					   -1);
I
Ingo Molnar 已提交
384
	}
L
Linus Torvalds 已提交
385

R
Roland McGrath 已提交
386 387 388
	if (kbuf && pos == 0 && count == sizeof(env)) {
		convert_from_fxsr(kbuf, target);
		return 0;
L
Linus Torvalds 已提交
389
	}
R
Roland McGrath 已提交
390 391

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

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

R
Roland McGrath 已提交
396 397 398
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 已提交
399
{
R
Roland McGrath 已提交
400 401
	struct user_i387_ia32_struct env;
	int ret;
L
Linus Torvalds 已提交
402

403 404 405 406
	ret = init_fpu(target);
	if (ret)
		return ret;

R
Roland McGrath 已提交
407 408
	set_stopped_child_used_math(target);

409 410 411
	if (!HAVE_HWFP)
		return fpregs_soft_set(target, regset, pos, count, kbuf, ubuf);

I
Ingo Molnar 已提交
412
	if (!cpu_has_fxsr) {
R
Roland McGrath 已提交
413
		return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
414
					  &target->thread.xstate->fsave, 0, -1);
I
Ingo Molnar 已提交
415
	}
R
Roland McGrath 已提交
416 417 418 419 420 421 422 423

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

424 425 426 427 428 429
	/*
	 * 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 已提交
430
	return ret;
L
Linus Torvalds 已提交
431 432 433 434 435 436
}

/*
 * Signal frame handlers.
 */

R
Roland McGrath 已提交
437
static inline int save_i387_fsave(struct _fpstate_ia32 __user *buf)
L
Linus Torvalds 已提交
438 439
{
	struct task_struct *tsk = current;
440
	struct i387_fsave_struct *fp = &tsk->thread.xstate->fsave;
L
Linus Torvalds 已提交
441

442 443
	fp->status = fp->swd;
	if (__copy_to_user(buf, fp, sizeof(struct i387_fsave_struct)))
L
Linus Torvalds 已提交
444 445 446 447
		return -1;
	return 1;
}

R
Roland McGrath 已提交
448
static int save_i387_fxsave(struct _fpstate_ia32 __user *buf)
L
Linus Torvalds 已提交
449 450
{
	struct task_struct *tsk = current;
451
	struct i387_fxsave_struct *fx = &tsk->thread.xstate->fxsave;
R
Roland McGrath 已提交
452
	struct user_i387_ia32_struct env;
L
Linus Torvalds 已提交
453 454
	int err = 0;

R
Roland McGrath 已提交
455 456
	convert_from_fxsr(&env, tsk);
	if (__copy_to_user(buf, &env, sizeof(env)))
L
Linus Torvalds 已提交
457 458
		return -1;

459
	err |= __put_user(fx->swd, &buf->status);
460 461
	err |= __put_user(X86_FXSR_MAGIC, &buf->magic);
	if (err)
L
Linus Torvalds 已提交
462 463
		return -1;

464
	if (__copy_to_user(&buf->_fxsr_env[0], fx, xstate_size))
L
Linus Torvalds 已提交
465 466 467 468
		return -1;
	return 1;
}

469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487
static int save_i387_xsave(void __user *buf)
{
	struct _fpstate_ia32 __user *fx = buf;
	int err = 0;

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

488
int save_i387_xstate_ia32(void __user *buf)
L
Linus Torvalds 已提交
489
{
490 491 492
	struct _fpstate_ia32 __user *fp = (struct _fpstate_ia32 __user *) buf;
	struct task_struct *tsk = current;

493
	if (!used_math())
L
Linus Torvalds 已提交
494
		return 0;
495 496 497

	if (!access_ok(VERIFY_WRITE, buf, sig_xstate_ia32_size))
		return -EACCES;
I
Ingo Molnar 已提交
498 499
	/*
	 * This will cause a "finit" to be triggered by the next
L
Linus Torvalds 已提交
500 501 502 503
	 * attempted FPU operation by the 'current' process.
	 */
	clear_used_math();

I
Ingo Molnar 已提交
504
	if (!HAVE_HWFP) {
R
Roland McGrath 已提交
505 506
		return fpregs_soft_get(current, NULL,
				       0, sizeof(struct user_i387_ia32_struct),
507
				       NULL, fp) ? -1 : 1;
L
Linus Torvalds 已提交
508
	}
I
Ingo Molnar 已提交
509

510 511
	unlazy_fpu(tsk);

512 513
	if (cpu_has_xsave)
		return save_i387_xsave(fp);
I
Ingo Molnar 已提交
514
	if (cpu_has_fxsr)
515
		return save_i387_fxsave(fp);
I
Ingo Molnar 已提交
516
	else
517
		return save_i387_fsave(fp);
L
Linus Torvalds 已提交
518 519
}

R
Roland McGrath 已提交
520
static inline int restore_i387_fsave(struct _fpstate_ia32 __user *buf)
L
Linus Torvalds 已提交
521 522
{
	struct task_struct *tsk = current;
I
Ingo Molnar 已提交
523

524
	return __copy_from_user(&tsk->thread.xstate->fsave, buf,
525
				sizeof(struct i387_fsave_struct));
L
Linus Torvalds 已提交
526 527
}

528 529
static int restore_i387_fxsave(struct _fpstate_ia32 __user *buf,
			       unsigned int size)
L
Linus Torvalds 已提交
530 531
{
	struct task_struct *tsk = current;
R
Roland McGrath 已提交
532
	struct user_i387_ia32_struct env;
I
Ingo Molnar 已提交
533 534
	int err;

535
	err = __copy_from_user(&tsk->thread.xstate->fxsave, &buf->_fxsr_env[0],
536
			       size);
L
Linus Torvalds 已提交
537
	/* mxcsr reserved bits must be masked to zero for security reasons */
538
	tsk->thread.xstate->fxsave.mxcsr &= mxcsr_feature_mask;
R
Roland McGrath 已提交
539 540 541
	if (err || __copy_from_user(&env, buf, sizeof(env)))
		return 1;
	convert_to_fxsr(tsk, &env);
I
Ingo Molnar 已提交
542

R
Roland McGrath 已提交
543
	return 0;
L
Linus Torvalds 已提交
544 545
}

546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590
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;
	unsigned int lmask, hmask;
	int err;

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

	lmask = fx_sw_user.xstate_bv;
	hmask = fx_sw_user.xstate_bv >> 32;

	err = restore_i387_fxsave(buf, fx_sw_user.xstate_size);

	xsave_hdr->xstate_bv &=  (pcntxt_lmask | (((u64) pcntxt_hmask) << 32));
	/*
	 * 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.
	 */
	lmask = ~(pcntxt_lmask & ~lmask);
	hmask = ~(pcntxt_hmask & ~hmask);
	xsave_hdr->xstate_bv &=  (lmask | (((u64) hmask) << 32));

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

591
int restore_i387_xstate_ia32(void __user *buf)
L
Linus Torvalds 已提交
592 593
{
	int err;
594
	struct task_struct *tsk = current;
595
	struct _fpstate_ia32 __user *fp = (struct _fpstate_ia32 __user *) buf;
L
Linus Torvalds 已提交
596

597
	if (HAVE_HWFP)
598 599
		clear_fpu(tsk);

600 601 602 603 604 605 606 607 608 609 610
	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;

611 612 613 614 615
	if (!used_math()) {
		err = init_fpu(tsk);
		if (err)
			return err;
	}
616

617
	if (HAVE_HWFP) {
618 619 620 621 622
		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 已提交
623
		else
624
			err = restore_i387_fsave(fp);
L
Linus Torvalds 已提交
625
	} else {
R
Roland McGrath 已提交
626 627
		err = fpregs_soft_set(current, NULL,
				      0, sizeof(struct user_i387_ia32_struct),
628
				      NULL, fp) != 0;
L
Linus Torvalds 已提交
629 630
	}
	set_used_math();
I
Ingo Molnar 已提交
631

L
Linus Torvalds 已提交
632 633 634 635 636
	return err;
}

/*
 * FPU state for core dumps.
R
Roland McGrath 已提交
637 638 639 640
 * 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 已提交
641
 */
642
int dump_fpu(struct pt_regs *regs, struct user_i387_struct *fpu)
L
Linus Torvalds 已提交
643 644
{
	struct task_struct *tsk = current;
I
Ingo Molnar 已提交
645
	int fpvalid;
L
Linus Torvalds 已提交
646 647

	fpvalid = !!used_math();
R
Roland McGrath 已提交
648 649 650 651
	if (fpvalid)
		fpvalid = !fpregs_get(tsk, NULL,
				      0, sizeof(struct user_i387_ia32_struct),
				      fpu, NULL);
L
Linus Torvalds 已提交
652 653 654

	return fpvalid;
}
655
EXPORT_SYMBOL(dump_fpu);
L
Linus Torvalds 已提交
656

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