i387.c 17.7 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 213 214
	set_stopped_child_used_math(target);

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

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

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

232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 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 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
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;

	/*
	 * First copy the fxsave bytes 0..463.
	 */
	ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
				  &target->thread.xstate->xsave, 0,
				  offsetof(struct user_xstateregs,
					   i387.xstate_fx_sw));
	if (ret)
		return ret;

	/*
	 * Copy the 48bytes defined by software.
	 */
	ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
				  xstate_fx_sw_bytes,
				  offsetof(struct user_xstateregs,
					   i387.xstate_fx_sw),
				  offsetof(struct user_xstateregs,
					   xsave_hdr));
	if (ret)
		return ret;

	/*
	 * Copy the rest of xstate memory layout.
	 */
	ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
				  &target->thread.xstate->xsave.xsave_hdr,
				  offsetof(struct user_xstateregs,
					   xsave_hdr), -1);
	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 已提交
310
#if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
L
Linus Torvalds 已提交
311 312 313 314 315

/*
 * FPU tag word conversions.
 */

316
static inline unsigned short twd_i387_to_fxsr(unsigned short twd)
L
Linus Torvalds 已提交
317 318
{
	unsigned int tmp; /* to avoid 16 bit prefixes in the code */
319

L
Linus Torvalds 已提交
320
	/* Transform each pair of bits into 01 (valid) or 00 (empty) */
321
	tmp = ~twd;
R
Roland McGrath 已提交
322
	tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */
323 324 325 326
	/* 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 已提交
327

328
	return tmp;
L
Linus Torvalds 已提交
329 330 331
}

#define FPREG_ADDR(f, n)	((void *)&(f)->st_space + (n) * 16);
R
Roland McGrath 已提交
332 333 334 335 336 337 338 339 340 341 342 343 344
#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 已提交
345

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

350
			switch (st->exponent & 0x7fff) {
L
Linus Torvalds 已提交
351
			case 0x7fff:
R
Roland McGrath 已提交
352
				tag = FP_EXP_TAG_SPECIAL;
L
Linus Torvalds 已提交
353 354
				break;
			case 0x0000:
355 356 357
				if (!st->significand[0] &&
				    !st->significand[1] &&
				    !st->significand[2] &&
R
Roland McGrath 已提交
358 359 360 361
				    !st->significand[3])
					tag = FP_EXP_TAG_ZERO;
				else
					tag = FP_EXP_TAG_SPECIAL;
L
Linus Torvalds 已提交
362 363
				break;
			default:
R
Roland McGrath 已提交
364 365 366 367
				if (st->significand[3] & 0x8000)
					tag = FP_EXP_TAG_VALID;
				else
					tag = FP_EXP_TAG_SPECIAL;
L
Linus Torvalds 已提交
368 369 370
				break;
			}
		} else {
R
Roland McGrath 已提交
371
			tag = FP_EXP_TAG_EMPTY;
L
Linus Torvalds 已提交
372
		}
R
Roland McGrath 已提交
373
		ret |= tag << (2 * i);
L
Linus Torvalds 已提交
374 375 376 377 378
	}
	return ret;
}

/*
R
Roland McGrath 已提交
379
 * FXSR floating point environment conversions.
L
Linus Torvalds 已提交
380 381
 */

I
Ingo Molnar 已提交
382 383
static void
convert_from_fxsr(struct user_i387_ia32_struct *env, struct task_struct *tsk)
L
Linus Torvalds 已提交
384
{
385
	struct i387_fxsave_struct *fxsave = &tsk->thread.xstate->fxsave;
R
Roland McGrath 已提交
386 387 388
	struct _fpreg *to = (struct _fpreg *) &env->st_space[0];
	struct _fpxreg *from = (struct _fpxreg *) &fxsave->st_space[0];
	int i;
L
Linus Torvalds 已提交
389

R
Roland McGrath 已提交
390 391 392 393 394 395 396 397 398 399 400 401
	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 已提交
402 403
		asm("mov %%ds, %[fos]" : [fos] "=r" (env->fos));
		asm("mov %%cs, %[fcs]" : [fcs] "=r" (env->fcs));
L
Linus Torvalds 已提交
404
	} else {
R
Roland McGrath 已提交
405
		struct pt_regs *regs = task_pt_regs(tsk);
I
Ingo Molnar 已提交
406

R
Roland McGrath 已提交
407 408
		env->fos = 0xffff0000 | tsk->thread.ds;
		env->fcs = regs->cs;
L
Linus Torvalds 已提交
409
	}
R
Roland McGrath 已提交
410 411
#else
	env->fip = fxsave->fip;
J
Jan Beulich 已提交
412
	env->fcs = (u16) fxsave->fcs | ((u32) fxsave->fop << 16);
R
Roland McGrath 已提交
413 414 415
	env->foo = fxsave->foo;
	env->fos = fxsave->fos;
#endif
L
Linus Torvalds 已提交
416

R
Roland McGrath 已提交
417 418
	for (i = 0; i < 8; ++i)
		memcpy(&to[i], &from[i], sizeof(to[0]));
L
Linus Torvalds 已提交
419 420
}

R
Roland McGrath 已提交
421 422
static void convert_to_fxsr(struct task_struct *tsk,
			    const struct user_i387_ia32_struct *env)
L
Linus Torvalds 已提交
423 424

{
425
	struct i387_fxsave_struct *fxsave = &tsk->thread.xstate->fxsave;
R
Roland McGrath 已提交
426 427 428
	struct _fpreg *from = (struct _fpreg *) &env->st_space[0];
	struct _fpxreg *to = (struct _fpxreg *) &fxsave->st_space[0];
	int i;
L
Linus Torvalds 已提交
429

R
Roland McGrath 已提交
430 431 432 433 434 435 436 437 438 439 440 441 442 443
	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 已提交
444

R
Roland McGrath 已提交
445 446
	for (i = 0; i < 8; ++i)
		memcpy(&to[i], &from[i], sizeof(from[0]));
L
Linus Torvalds 已提交
447 448
}

R
Roland McGrath 已提交
449 450 451
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 已提交
452
{
R
Roland McGrath 已提交
453
	struct user_i387_ia32_struct env;
454
	int ret;
L
Linus Torvalds 已提交
455

456 457 458
	ret = init_fpu(target);
	if (ret)
		return ret;
L
Linus Torvalds 已提交
459

460 461 462
	if (!HAVE_HWFP)
		return fpregs_soft_get(target, regset, pos, count, kbuf, ubuf);

I
Ingo Molnar 已提交
463
	if (!cpu_has_fxsr) {
R
Roland McGrath 已提交
464
		return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
465 466
					   &target->thread.xstate->fsave, 0,
					   -1);
I
Ingo Molnar 已提交
467
	}
L
Linus Torvalds 已提交
468

R
Roland McGrath 已提交
469 470 471
	if (kbuf && pos == 0 && count == sizeof(env)) {
		convert_from_fxsr(kbuf, target);
		return 0;
L
Linus Torvalds 已提交
472
	}
R
Roland McGrath 已提交
473 474

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

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

R
Roland McGrath 已提交
479 480 481
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 已提交
482
{
R
Roland McGrath 已提交
483 484
	struct user_i387_ia32_struct env;
	int ret;
L
Linus Torvalds 已提交
485

486 487 488 489
	ret = init_fpu(target);
	if (ret)
		return ret;

R
Roland McGrath 已提交
490 491
	set_stopped_child_used_math(target);

492 493 494
	if (!HAVE_HWFP)
		return fpregs_soft_set(target, regset, pos, count, kbuf, ubuf);

I
Ingo Molnar 已提交
495
	if (!cpu_has_fxsr) {
R
Roland McGrath 已提交
496
		return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
497
					  &target->thread.xstate->fsave, 0, -1);
I
Ingo Molnar 已提交
498
	}
R
Roland McGrath 已提交
499 500 501 502 503 504 505 506

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

507 508 509 510 511 512
	/*
	 * 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 已提交
513
	return ret;
L
Linus Torvalds 已提交
514 515 516 517 518 519
}

/*
 * Signal frame handlers.
 */

R
Roland McGrath 已提交
520
static inline int save_i387_fsave(struct _fpstate_ia32 __user *buf)
L
Linus Torvalds 已提交
521 522
{
	struct task_struct *tsk = current;
523
	struct i387_fsave_struct *fp = &tsk->thread.xstate->fsave;
L
Linus Torvalds 已提交
524

525 526
	fp->status = fp->swd;
	if (__copy_to_user(buf, fp, sizeof(struct i387_fsave_struct)))
L
Linus Torvalds 已提交
527 528 529 530
		return -1;
	return 1;
}

R
Roland McGrath 已提交
531
static int save_i387_fxsave(struct _fpstate_ia32 __user *buf)
L
Linus Torvalds 已提交
532 533
{
	struct task_struct *tsk = current;
534
	struct i387_fxsave_struct *fx = &tsk->thread.xstate->fxsave;
R
Roland McGrath 已提交
535
	struct user_i387_ia32_struct env;
L
Linus Torvalds 已提交
536 537
	int err = 0;

R
Roland McGrath 已提交
538 539
	convert_from_fxsr(&env, tsk);
	if (__copy_to_user(buf, &env, sizeof(env)))
L
Linus Torvalds 已提交
540 541
		return -1;

542
	err |= __put_user(fx->swd, &buf->status);
543 544
	err |= __put_user(X86_FXSR_MAGIC, &buf->magic);
	if (err)
L
Linus Torvalds 已提交
545 546
		return -1;

547
	if (__copy_to_user(&buf->_fxsr_env[0], fx, xstate_size))
L
Linus Torvalds 已提交
548 549 550 551
		return -1;
	return 1;
}

552 553
static int save_i387_xsave(void __user *buf)
{
554
	struct task_struct *tsk = current;
555 556 557
	struct _fpstate_ia32 __user *fx = buf;
	int err = 0;

558 559 560 561 562 563 564 565 566 567 568 569 570
	/*
	 * 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;

571 572 573 574 575 576 577 578 579 580 581 582 583 584
	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;
}

585
int save_i387_xstate_ia32(void __user *buf)
L
Linus Torvalds 已提交
586
{
587 588 589
	struct _fpstate_ia32 __user *fp = (struct _fpstate_ia32 __user *) buf;
	struct task_struct *tsk = current;

590
	if (!used_math())
L
Linus Torvalds 已提交
591
		return 0;
592 593 594

	if (!access_ok(VERIFY_WRITE, buf, sig_xstate_ia32_size))
		return -EACCES;
I
Ingo Molnar 已提交
595 596
	/*
	 * This will cause a "finit" to be triggered by the next
L
Linus Torvalds 已提交
597 598 599 600
	 * attempted FPU operation by the 'current' process.
	 */
	clear_used_math();

I
Ingo Molnar 已提交
601
	if (!HAVE_HWFP) {
R
Roland McGrath 已提交
602 603
		return fpregs_soft_get(current, NULL,
				       0, sizeof(struct user_i387_ia32_struct),
604
				       NULL, fp) ? -1 : 1;
L
Linus Torvalds 已提交
605
	}
I
Ingo Molnar 已提交
606

607 608
	unlazy_fpu(tsk);

609 610
	if (cpu_has_xsave)
		return save_i387_xsave(fp);
I
Ingo Molnar 已提交
611
	if (cpu_has_fxsr)
612
		return save_i387_fxsave(fp);
I
Ingo Molnar 已提交
613
	else
614
		return save_i387_fsave(fp);
L
Linus Torvalds 已提交
615 616
}

R
Roland McGrath 已提交
617
static inline int restore_i387_fsave(struct _fpstate_ia32 __user *buf)
L
Linus Torvalds 已提交
618 619
{
	struct task_struct *tsk = current;
I
Ingo Molnar 已提交
620

621
	return __copy_from_user(&tsk->thread.xstate->fsave, buf,
622
				sizeof(struct i387_fsave_struct));
L
Linus Torvalds 已提交
623 624
}

625 626
static int restore_i387_fxsave(struct _fpstate_ia32 __user *buf,
			       unsigned int size)
L
Linus Torvalds 已提交
627 628
{
	struct task_struct *tsk = current;
R
Roland McGrath 已提交
629
	struct user_i387_ia32_struct env;
I
Ingo Molnar 已提交
630 631
	int err;

632
	err = __copy_from_user(&tsk->thread.xstate->fxsave, &buf->_fxsr_env[0],
633
			       size);
L
Linus Torvalds 已提交
634
	/* mxcsr reserved bits must be masked to zero for security reasons */
635
	tsk->thread.xstate->fxsave.mxcsr &= mxcsr_feature_mask;
R
Roland McGrath 已提交
636 637 638
	if (err || __copy_from_user(&env, buf, sizeof(env)))
		return 1;
	convert_to_fxsr(tsk, &env);
I
Ingo Molnar 已提交
639

R
Roland McGrath 已提交
640
	return 0;
L
Linus Torvalds 已提交
641 642
}

643 644 645 646 647 648 649 650 651
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;
652
	u64 mask;
653 654 655 656 657
	int err;

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

658
	mask = fx_sw_user.xstate_bv;
659 660 661

	err = restore_i387_fxsave(buf, fx_sw_user.xstate_size);

662
	xsave_hdr->xstate_bv &= pcntxt_mask;
663 664 665 666 667 668 669 670 671
	/*
	 * 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.
	 */
672 673
	mask = ~(pcntxt_mask & ~mask);
	xsave_hdr->xstate_bv &= mask;
674 675 676 677 678 679 680 681 682 683 684 685

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

686
int restore_i387_xstate_ia32(void __user *buf)
L
Linus Torvalds 已提交
687 688
{
	int err;
689
	struct task_struct *tsk = current;
690
	struct _fpstate_ia32 __user *fp = (struct _fpstate_ia32 __user *) buf;
L
Linus Torvalds 已提交
691

692
	if (HAVE_HWFP)
693 694
		clear_fpu(tsk);

695 696 697 698 699 700 701 702 703 704 705
	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;

706 707 708 709 710
	if (!used_math()) {
		err = init_fpu(tsk);
		if (err)
			return err;
	}
711

712
	if (HAVE_HWFP) {
713 714 715 716 717
		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 已提交
718
		else
719
			err = restore_i387_fsave(fp);
L
Linus Torvalds 已提交
720
	} else {
R
Roland McGrath 已提交
721 722
		err = fpregs_soft_set(current, NULL,
				      0, sizeof(struct user_i387_ia32_struct),
723
				      NULL, fp) != 0;
L
Linus Torvalds 已提交
724 725
	}
	set_used_math();
I
Ingo Molnar 已提交
726

L
Linus Torvalds 已提交
727 728 729 730 731
	return err;
}

/*
 * FPU state for core dumps.
R
Roland McGrath 已提交
732 733 734 735
 * 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 已提交
736
 */
737
int dump_fpu(struct pt_regs *regs, struct user_i387_struct *fpu)
L
Linus Torvalds 已提交
738 739
{
	struct task_struct *tsk = current;
I
Ingo Molnar 已提交
740
	int fpvalid;
L
Linus Torvalds 已提交
741 742

	fpvalid = !!used_math();
R
Roland McGrath 已提交
743 744 745 746
	if (fpvalid)
		fpvalid = !fpregs_get(tsk, NULL,
				      0, sizeof(struct user_i387_ia32_struct),
				      fpu, NULL);
L
Linus Torvalds 已提交
747 748 749

	return fpvalid;
}
750
EXPORT_SYMBOL(dump_fpu);
L
Linus Torvalds 已提交
751

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