i387.c 19.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

35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
/*
 * Were we in an interrupt that interrupted kernel mode?
 *
 * We can do a kernel_fpu_begin/end() pair *ONLY* if that
 * pair does nothing at all: the thread must not have fpu (so
 * that we don't try to save the FPU state), and TS must
 * be set (so that the clts/stts pair does nothing that is
 * visible in the interrupted kernel thread).
 */
static inline bool interrupted_kernel_fpu_idle(void)
{
	return !__thread_has_fpu(current) &&
		(read_cr0() & X86_CR0_TS);
}

/*
 * Were we in user mode (or vm86 mode) when we were
 * interrupted?
 *
 * Doing kernel_fpu_begin/end() is ok if we are running
 * in an interrupt context from user mode - we'll just
 * save the FPU state as required.
 */
static inline bool interrupted_user_mode(void)
{
	struct pt_regs *regs = get_irq_regs();
	return regs && user_mode_vm(regs);
}

/*
 * Can we use the FPU in kernel mode with the
 * whole "kernel_fpu_begin/end()" sequence?
 *
 * It's always ok in process context (ie "not interrupt")
 * but it is sometimes ok even from an irq.
 */
bool irq_fpu_usable(void)
{
	return !in_interrupt() ||
		interrupted_user_mode() ||
		interrupted_kernel_fpu_idle();
}
EXPORT_SYMBOL(irq_fpu_usable);

void kernel_fpu_begin(void)
{
	struct task_struct *me = current;

	WARN_ON_ONCE(!irq_fpu_usable());
	preempt_disable();
	if (__thread_has_fpu(me)) {
		__save_init_fpu(me);
		__thread_clear_has_fpu(me);
		/* We do 'stts()' in kernel_fpu_end() */
	} else {
		percpu_write(fpu_owner_task, NULL);
		clts();
	}
}
EXPORT_SYMBOL(kernel_fpu_begin);

void kernel_fpu_end(void)
{
	stts();
	preempt_enable();
}
EXPORT_SYMBOL(kernel_fpu_end);

void unlazy_fpu(struct task_struct *tsk)
{
	preempt_disable();
	if (__thread_has_fpu(tsk)) {
		__save_init_fpu(tsk);
		__thread_fpu_end(tsk);
	} else
		tsk->fpu_counter = 0;
	preempt_enable();
}
EXPORT_SYMBOL(unlazy_fpu);

L
Linus Torvalds 已提交
115
#ifdef CONFIG_MATH_EMULATION
I
Ingo Molnar 已提交
116
# define HAVE_HWFP		(boot_cpu_data.hard_math)
L
Linus Torvalds 已提交
117
#else
I
Ingo Molnar 已提交
118
# define HAVE_HWFP		1
L
Linus Torvalds 已提交
119 120
#endif

I
Ingo Molnar 已提交
121
static unsigned int		mxcsr_feature_mask __read_mostly = 0xffffffffu;
122
unsigned int xstate_size;
123
EXPORT_SYMBOL_GPL(xstate_size);
124
unsigned int sig_xstate_ia32_size = sizeof(struct _fpstate_ia32);
125
static struct i387_fxsave_struct fx_scratch __cpuinitdata;
L
Linus Torvalds 已提交
126

127
void __cpuinit mxcsr_feature_mask_init(void)
L
Linus Torvalds 已提交
128 129
{
	unsigned long mask = 0;
I
Ingo Molnar 已提交
130

L
Linus Torvalds 已提交
131 132
	clts();
	if (cpu_has_fxsr) {
133 134 135
		memset(&fx_scratch, 0, sizeof(struct i387_fxsave_struct));
		asm volatile("fxsave %0" : : "m" (fx_scratch));
		mask = fx_scratch.mxcsr_mask;
136 137 138
		if (mask == 0)
			mask = 0x0000ffbf;
	}
L
Linus Torvalds 已提交
139 140 141 142
	mxcsr_feature_mask &= mask;
	stts();
}

143
static void __cpuinit init_thread_xstate(void)
144
{
145 146 147 148 149
	/*
	 * Note that xstate_size might be overwriten later during
	 * xsave_init().
	 */

150
	if (!HAVE_HWFP) {
151 152 153 154 155 156
		/*
		 * Disable xsave as we do not support it if i387
		 * emulation is enabled.
		 */
		setup_clear_cpu_cap(X86_FEATURE_XSAVE);
		setup_clear_cpu_cap(X86_FEATURE_XSAVEOPT);
157 158 159 160
		xstate_size = sizeof(struct i387_soft_struct);
		return;
	}

161 162 163 164 165 166
	if (cpu_has_fxsr)
		xstate_size = sizeof(struct i387_fxsave_struct);
	else
		xstate_size = sizeof(struct i387_fsave_struct);
}

R
Roland McGrath 已提交
167 168 169 170
/*
 * Called at bootup to set up the initial FPU state that is later cloned
 * into all processes.
 */
171

R
Roland McGrath 已提交
172 173
void __cpuinit fpu_init(void)
{
B
Brian Gerst 已提交
174 175
	unsigned long cr0;
	unsigned long cr4_mask = 0;
R
Roland McGrath 已提交
176

B
Brian Gerst 已提交
177 178 179 180 181 182 183 184 185 186 187 188
	if (cpu_has_fxsr)
		cr4_mask |= X86_CR4_OSFXSR;
	if (cpu_has_xmm)
		cr4_mask |= X86_CR4_OSXMMEXCPT;
	if (cr4_mask)
		set_in_cr4(cr4_mask);

	cr0 = read_cr0();
	cr0 &= ~(X86_CR0_TS|X86_CR0_EM); /* clear TS and EM */
	if (!HAVE_HWFP)
		cr0 |= X86_CR0_EM;
	write_cr0(cr0);
R
Roland McGrath 已提交
189

190 191 192
	if (!smp_processor_id())
		init_thread_xstate();

R
Roland McGrath 已提交
193 194
	mxcsr_feature_mask_init();
	/* clean state in init */
A
Avi Kivity 已提交
195
	current_thread_info()->status = 0;
R
Roland McGrath 已提交
196 197
	clear_used_math();
}
198

S
Sheng Yang 已提交
199
void fpu_finit(struct fpu *fpu)
L
Linus Torvalds 已提交
200
{
201
	if (!HAVE_HWFP) {
202 203
		finit_soft_fpu(&fpu->state->soft);
		return;
204 205
	}

L
Linus Torvalds 已提交
206
	if (cpu_has_fxsr) {
207
		struct i387_fxsave_struct *fx = &fpu->state->fxsave;
208 209 210

		memset(fx, 0, xstate_size);
		fx->cwd = 0x37f;
L
Linus Torvalds 已提交
211
		if (cpu_has_xmm)
212
			fx->mxcsr = MXCSR_DEFAULT;
L
Linus Torvalds 已提交
213
	} else {
214
		struct i387_fsave_struct *fp = &fpu->state->fsave;
215 216 217 218 219
		memset(fp, 0, xstate_size);
		fp->cwd = 0xffff037fu;
		fp->swd = 0xffff0000u;
		fp->twd = 0xffffffffu;
		fp->fos = 0xffff0000u;
L
Linus Torvalds 已提交
220
	}
221
}
S
Sheng Yang 已提交
222
EXPORT_SYMBOL_GPL(fpu_finit);
223 224 225 226 227

/*
 * 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
L
Lucas De Marchi 已提交
228
 * remember the current task has used the FPU.
229 230 231 232 233 234 235 236 237 238 239
 */
int init_fpu(struct task_struct *tsk)
{
	int ret;

	if (tsk_used_math(tsk)) {
		if (HAVE_HWFP && tsk == current)
			unlazy_fpu(tsk);
		return 0;
	}

R
Roland McGrath 已提交
240
	/*
241
	 * Memory allocation at the first usage of the FPU and other state.
R
Roland McGrath 已提交
242
	 */
243 244 245 246 247 248
	ret = fpu_alloc(&tsk->thread.fpu);
	if (ret)
		return ret;

	fpu_finit(&tsk->thread.fpu);

L
Linus Torvalds 已提交
249
	set_stopped_child_used_math(tsk);
250
	return 0;
L
Linus Torvalds 已提交
251
}
252
EXPORT_SYMBOL_GPL(init_fpu);
L
Linus Torvalds 已提交
253

254 255 256 257 258
/*
 * 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 已提交
259 260 261 262
int fpregs_active(struct task_struct *target, const struct user_regset *regset)
{
	return tsk_used_math(target) ? regset->n : 0;
}
L
Linus Torvalds 已提交
263

R
Roland McGrath 已提交
264
int xfpregs_active(struct task_struct *target, const struct user_regset *regset)
L
Linus Torvalds 已提交
265
{
R
Roland McGrath 已提交
266 267
	return (cpu_has_fxsr && tsk_used_math(target)) ? regset->n : 0;
}
L
Linus Torvalds 已提交
268

R
Roland McGrath 已提交
269 270 271 272
int xfpregs_get(struct task_struct *target, const struct user_regset *regset,
		unsigned int pos, unsigned int count,
		void *kbuf, void __user *ubuf)
{
273 274
	int ret;

R
Roland McGrath 已提交
275 276 277
	if (!cpu_has_fxsr)
		return -ENODEV;

278 279 280
	ret = init_fpu(target);
	if (ret)
		return ret;
R
Roland McGrath 已提交
281

282 283
	sanitize_i387_state(target);

R
Roland McGrath 已提交
284
	return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
285
				   &target->thread.fpu.state->fxsave, 0, -1);
L
Linus Torvalds 已提交
286
}
R
Roland McGrath 已提交
287 288 289 290 291 292 293 294 295 296

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;

297 298 299 300
	ret = init_fpu(target);
	if (ret)
		return ret;

301 302
	sanitize_i387_state(target);

R
Roland McGrath 已提交
303
	ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
304
				 &target->thread.fpu.state->fxsave, 0, -1);
R
Roland McGrath 已提交
305 306 307 308

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

311 312 313 314 315
	/*
	 * update the header bits in the xsave header, indicating the
	 * presence of FP and SSE state.
	 */
	if (cpu_has_xsave)
316
		target->thread.fpu.state->xsave.xsave_hdr.xstate_bv |= XSTATE_FPSSE;
317

R
Roland McGrath 已提交
318 319 320
	return ret;
}

321 322 323 324 325 326 327 328 329 330 331 332 333 334
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;

	/*
335 336 337
	 * 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().
338
	 */
339
	memcpy(&target->thread.fpu.state->fxsave.sw_reserved,
340
	       xstate_fx_sw_bytes, sizeof(xstate_fx_sw_bytes));
341 342

	/*
343
	 * Copy the xstate memory layout.
344 345
	 */
	ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
346
				  &target->thread.fpu.state->xsave, 0, -1);
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
	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,
365
				 &target->thread.fpu.state->xsave, 0, -1);
366 367 368 369

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

372
	xsave_hdr = &target->thread.fpu.state->xsave.xsave_hdr;
373 374 375 376 377 378 379 380 381 382

	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 已提交
383
#if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
L
Linus Torvalds 已提交
384 385 386 387 388

/*
 * FPU tag word conversions.
 */

389
static inline unsigned short twd_i387_to_fxsr(unsigned short twd)
L
Linus Torvalds 已提交
390 391
{
	unsigned int tmp; /* to avoid 16 bit prefixes in the code */
392

L
Linus Torvalds 已提交
393
	/* Transform each pair of bits into 01 (valid) or 00 (empty) */
394
	tmp = ~twd;
R
Roland McGrath 已提交
395
	tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */
396 397 398 399
	/* 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 已提交
400

401
	return tmp;
L
Linus Torvalds 已提交
402 403
}

404
#define FPREG_ADDR(f, n)	((void *)&(f)->st_space + (n) * 16)
R
Roland McGrath 已提交
405 406 407 408 409 410 411 412 413 414 415 416 417
#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 已提交
418

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

423
			switch (st->exponent & 0x7fff) {
L
Linus Torvalds 已提交
424
			case 0x7fff:
R
Roland McGrath 已提交
425
				tag = FP_EXP_TAG_SPECIAL;
L
Linus Torvalds 已提交
426 427
				break;
			case 0x0000:
428 429 430
				if (!st->significand[0] &&
				    !st->significand[1] &&
				    !st->significand[2] &&
R
Roland McGrath 已提交
431 432 433 434
				    !st->significand[3])
					tag = FP_EXP_TAG_ZERO;
				else
					tag = FP_EXP_TAG_SPECIAL;
L
Linus Torvalds 已提交
435 436
				break;
			default:
R
Roland McGrath 已提交
437 438 439 440
				if (st->significand[3] & 0x8000)
					tag = FP_EXP_TAG_VALID;
				else
					tag = FP_EXP_TAG_SPECIAL;
L
Linus Torvalds 已提交
441 442 443
				break;
			}
		} else {
R
Roland McGrath 已提交
444
			tag = FP_EXP_TAG_EMPTY;
L
Linus Torvalds 已提交
445
		}
R
Roland McGrath 已提交
446
		ret |= tag << (2 * i);
L
Linus Torvalds 已提交
447 448 449 450 451
	}
	return ret;
}

/*
R
Roland McGrath 已提交
452
 * FXSR floating point environment conversions.
L
Linus Torvalds 已提交
453 454
 */

I
Ingo Molnar 已提交
455 456
static void
convert_from_fxsr(struct user_i387_ia32_struct *env, struct task_struct *tsk)
L
Linus Torvalds 已提交
457
{
458
	struct i387_fxsave_struct *fxsave = &tsk->thread.fpu.state->fxsave;
R
Roland McGrath 已提交
459 460 461
	struct _fpreg *to = (struct _fpreg *) &env->st_space[0];
	struct _fpxreg *from = (struct _fpxreg *) &fxsave->st_space[0];
	int i;
L
Linus Torvalds 已提交
462

R
Roland McGrath 已提交
463 464 465 466 467 468 469
	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;
470 471 472 473 474
	/*
	 * should be actually ds/cs at fpu exception time, but
	 * that information is not available in 64bit mode.
	 */
	env->fcs = task_pt_regs(tsk)->cs;
R
Roland McGrath 已提交
475
	if (tsk == current) {
476
		savesegment(ds, env->fos);
L
Linus Torvalds 已提交
477
	} else {
478
		env->fos = tsk->thread.ds;
L
Linus Torvalds 已提交
479
	}
480
	env->fos |= 0xffff0000;
R
Roland McGrath 已提交
481 482
#else
	env->fip = fxsave->fip;
J
Jan Beulich 已提交
483
	env->fcs = (u16) fxsave->fcs | ((u32) fxsave->fop << 16);
R
Roland McGrath 已提交
484 485 486
	env->foo = fxsave->foo;
	env->fos = fxsave->fos;
#endif
L
Linus Torvalds 已提交
487

R
Roland McGrath 已提交
488 489
	for (i = 0; i < 8; ++i)
		memcpy(&to[i], &from[i], sizeof(to[0]));
L
Linus Torvalds 已提交
490 491
}

R
Roland McGrath 已提交
492 493
static void convert_to_fxsr(struct task_struct *tsk,
			    const struct user_i387_ia32_struct *env)
L
Linus Torvalds 已提交
494 495

{
496
	struct i387_fxsave_struct *fxsave = &tsk->thread.fpu.state->fxsave;
R
Roland McGrath 已提交
497 498 499
	struct _fpreg *from = (struct _fpreg *) &env->st_space[0];
	struct _fpxreg *to = (struct _fpxreg *) &fxsave->st_space[0];
	int i;
L
Linus Torvalds 已提交
500

R
Roland McGrath 已提交
501 502 503 504 505 506 507 508 509 510 511 512 513 514
	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 已提交
515

R
Roland McGrath 已提交
516 517
	for (i = 0; i < 8; ++i)
		memcpy(&to[i], &from[i], sizeof(from[0]));
L
Linus Torvalds 已提交
518 519
}

R
Roland McGrath 已提交
520 521 522
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 已提交
523
{
R
Roland McGrath 已提交
524
	struct user_i387_ia32_struct env;
525
	int ret;
L
Linus Torvalds 已提交
526

527 528 529
	ret = init_fpu(target);
	if (ret)
		return ret;
L
Linus Torvalds 已提交
530

531 532 533
	if (!HAVE_HWFP)
		return fpregs_soft_get(target, regset, pos, count, kbuf, ubuf);

I
Ingo Molnar 已提交
534
	if (!cpu_has_fxsr) {
R
Roland McGrath 已提交
535
		return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
536
					   &target->thread.fpu.state->fsave, 0,
537
					   -1);
I
Ingo Molnar 已提交
538
	}
L
Linus Torvalds 已提交
539

540 541
	sanitize_i387_state(target);

R
Roland McGrath 已提交
542 543 544
	if (kbuf && pos == 0 && count == sizeof(env)) {
		convert_from_fxsr(kbuf, target);
		return 0;
L
Linus Torvalds 已提交
545
	}
R
Roland McGrath 已提交
546 547

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

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

R
Roland McGrath 已提交
552 553 554
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 已提交
555
{
R
Roland McGrath 已提交
556 557
	struct user_i387_ia32_struct env;
	int ret;
L
Linus Torvalds 已提交
558

559 560 561 562
	ret = init_fpu(target);
	if (ret)
		return ret;

563 564
	sanitize_i387_state(target);

565 566 567
	if (!HAVE_HWFP)
		return fpregs_soft_set(target, regset, pos, count, kbuf, ubuf);

I
Ingo Molnar 已提交
568
	if (!cpu_has_fxsr) {
R
Roland McGrath 已提交
569
		return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
570
					  &target->thread.fpu.state->fsave, 0, -1);
I
Ingo Molnar 已提交
571
	}
R
Roland McGrath 已提交
572 573 574 575 576 577 578 579

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

580 581 582 583 584
	/*
	 * update the header bit in the xsave header, indicating the
	 * presence of FP.
	 */
	if (cpu_has_xsave)
585
		target->thread.fpu.state->xsave.xsave_hdr.xstate_bv |= XSTATE_FP;
R
Roland McGrath 已提交
586
	return ret;
L
Linus Torvalds 已提交
587 588 589 590 591 592
}

/*
 * Signal frame handlers.
 */

R
Roland McGrath 已提交
593
static inline int save_i387_fsave(struct _fpstate_ia32 __user *buf)
L
Linus Torvalds 已提交
594 595
{
	struct task_struct *tsk = current;
596
	struct i387_fsave_struct *fp = &tsk->thread.fpu.state->fsave;
L
Linus Torvalds 已提交
597

598 599
	fp->status = fp->swd;
	if (__copy_to_user(buf, fp, sizeof(struct i387_fsave_struct)))
L
Linus Torvalds 已提交
600 601 602 603
		return -1;
	return 1;
}

R
Roland McGrath 已提交
604
static int save_i387_fxsave(struct _fpstate_ia32 __user *buf)
L
Linus Torvalds 已提交
605 606
{
	struct task_struct *tsk = current;
607
	struct i387_fxsave_struct *fx = &tsk->thread.fpu.state->fxsave;
R
Roland McGrath 已提交
608
	struct user_i387_ia32_struct env;
L
Linus Torvalds 已提交
609 610
	int err = 0;

R
Roland McGrath 已提交
611 612
	convert_from_fxsr(&env, tsk);
	if (__copy_to_user(buf, &env, sizeof(env)))
L
Linus Torvalds 已提交
613 614
		return -1;

615
	err |= __put_user(fx->swd, &buf->status);
616 617
	err |= __put_user(X86_FXSR_MAGIC, &buf->magic);
	if (err)
L
Linus Torvalds 已提交
618 619
		return -1;

620
	if (__copy_to_user(&buf->_fxsr_env[0], fx, xstate_size))
L
Linus Torvalds 已提交
621 622 623 624
		return -1;
	return 1;
}

625 626
static int save_i387_xsave(void __user *buf)
{
627
	struct task_struct *tsk = current;
628 629 630
	struct _fpstate_ia32 __user *fx = buf;
	int err = 0;

631 632 633

	sanitize_i387_state(tsk);

634 635 636 637 638 639 640 641 642 643 644
	/*
	 * 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.
	 */
645
	tsk->thread.fpu.state->xsave.xsave_hdr.xstate_bv |= XSTATE_FPSSE;
646

647 648 649 650 651 652 653 654 655 656 657 658 659 660
	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;
}

661
int save_i387_xstate_ia32(void __user *buf)
L
Linus Torvalds 已提交
662
{
663 664 665
	struct _fpstate_ia32 __user *fp = (struct _fpstate_ia32 __user *) buf;
	struct task_struct *tsk = current;

666
	if (!used_math())
L
Linus Torvalds 已提交
667
		return 0;
668 669 670

	if (!access_ok(VERIFY_WRITE, buf, sig_xstate_ia32_size))
		return -EACCES;
I
Ingo Molnar 已提交
671 672
	/*
	 * This will cause a "finit" to be triggered by the next
L
Linus Torvalds 已提交
673 674 675 676
	 * attempted FPU operation by the 'current' process.
	 */
	clear_used_math();

I
Ingo Molnar 已提交
677
	if (!HAVE_HWFP) {
R
Roland McGrath 已提交
678 679
		return fpregs_soft_get(current, NULL,
				       0, sizeof(struct user_i387_ia32_struct),
680
				       NULL, fp) ? -1 : 1;
L
Linus Torvalds 已提交
681
	}
I
Ingo Molnar 已提交
682

683 684
	unlazy_fpu(tsk);

685 686
	if (cpu_has_xsave)
		return save_i387_xsave(fp);
I
Ingo Molnar 已提交
687
	if (cpu_has_fxsr)
688
		return save_i387_fxsave(fp);
I
Ingo Molnar 已提交
689
	else
690
		return save_i387_fsave(fp);
L
Linus Torvalds 已提交
691 692
}

R
Roland McGrath 已提交
693
static inline int restore_i387_fsave(struct _fpstate_ia32 __user *buf)
L
Linus Torvalds 已提交
694 695
{
	struct task_struct *tsk = current;
I
Ingo Molnar 已提交
696

697
	return __copy_from_user(&tsk->thread.fpu.state->fsave, buf,
698
				sizeof(struct i387_fsave_struct));
L
Linus Torvalds 已提交
699 700
}

701 702
static int restore_i387_fxsave(struct _fpstate_ia32 __user *buf,
			       unsigned int size)
L
Linus Torvalds 已提交
703 704
{
	struct task_struct *tsk = current;
R
Roland McGrath 已提交
705
	struct user_i387_ia32_struct env;
I
Ingo Molnar 已提交
706 707
	int err;

708
	err = __copy_from_user(&tsk->thread.fpu.state->fxsave, &buf->_fxsr_env[0],
709
			       size);
L
Linus Torvalds 已提交
710
	/* mxcsr reserved bits must be masked to zero for security reasons */
711
	tsk->thread.fpu.state->fxsave.mxcsr &= mxcsr_feature_mask;
R
Roland McGrath 已提交
712 713 714
	if (err || __copy_from_user(&env, buf, sizeof(env)))
		return 1;
	convert_to_fxsr(tsk, &env);
I
Ingo Molnar 已提交
715

R
Roland McGrath 已提交
716
	return 0;
L
Linus Torvalds 已提交
717 718
}

719 720 721 722 723 724 725 726
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 =
727
				&current->thread.fpu.state->xsave.xsave_hdr;
728
	u64 mask;
729 730 731 732 733
	int err;

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

734
	mask = fx_sw_user.xstate_bv;
735 736 737

	err = restore_i387_fxsave(buf, fx_sw_user.xstate_size);

738
	xsave_hdr->xstate_bv &= pcntxt_mask;
739 740 741 742 743 744 745 746 747
	/*
	 * 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.
	 */
748 749
	mask = ~(pcntxt_mask & ~mask);
	xsave_hdr->xstate_bv &= mask;
750 751 752 753 754 755 756 757 758 759 760 761

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

762
int restore_i387_xstate_ia32(void __user *buf)
L
Linus Torvalds 已提交
763 764
{
	int err;
765
	struct task_struct *tsk = current;
766
	struct _fpstate_ia32 __user *fp = (struct _fpstate_ia32 __user *) buf;
L
Linus Torvalds 已提交
767

768
	if (HAVE_HWFP)
769 770
		clear_fpu(tsk);

771 772 773 774 775 776 777 778 779 780 781
	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;

782 783 784 785 786
	if (!used_math()) {
		err = init_fpu(tsk);
		if (err)
			return err;
	}
787

788
	if (HAVE_HWFP) {
789 790 791 792 793
		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 已提交
794
		else
795
			err = restore_i387_fsave(fp);
L
Linus Torvalds 已提交
796
	} else {
R
Roland McGrath 已提交
797 798
		err = fpregs_soft_set(current, NULL,
				      0, sizeof(struct user_i387_ia32_struct),
799
				      NULL, fp) != 0;
L
Linus Torvalds 已提交
800 801
	}
	set_used_math();
I
Ingo Molnar 已提交
802

L
Linus Torvalds 已提交
803 804 805 806 807
	return err;
}

/*
 * FPU state for core dumps.
R
Roland McGrath 已提交
808 809 810 811
 * 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 已提交
812
 */
813
int dump_fpu(struct pt_regs *regs, struct user_i387_struct *fpu)
L
Linus Torvalds 已提交
814 815
{
	struct task_struct *tsk = current;
I
Ingo Molnar 已提交
816
	int fpvalid;
L
Linus Torvalds 已提交
817 818

	fpvalid = !!used_math();
R
Roland McGrath 已提交
819 820 821 822
	if (fpvalid)
		fpvalid = !fpregs_get(tsk, NULL,
				      0, sizeof(struct user_i387_ia32_struct),
				      fpu, NULL);
L
Linus Torvalds 已提交
823 824 825

	return fpvalid;
}
826
EXPORT_SYMBOL(dump_fpu);
L
Linus Torvalds 已提交
827

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