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
#include <asm/ptrace.h>
#include <asm/i387.h>
19
#include <asm/fpu-internal.h>
I
Ingo Molnar 已提交
20
#include <asm/user.h>
L
Linus Torvalds 已提交
21

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

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
/*
 * 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 {
91
		this_cpu_write(fpu_owner_task, NULL);
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
		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 已提交
116
#ifdef CONFIG_MATH_EMULATION
I
Ingo Molnar 已提交
117
# define HAVE_HWFP		(boot_cpu_data.hard_math)
L
Linus Torvalds 已提交
118
#else
I
Ingo Molnar 已提交
119
# define HAVE_HWFP		1
L
Linus Torvalds 已提交
120 121
#endif

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

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

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

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

151
	if (!HAVE_HWFP) {
152 153 154 155 156 157
		/*
		 * 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);
158 159 160 161
		xstate_size = sizeof(struct i387_soft_struct);
		return;
	}

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

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

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

B
Brian Gerst 已提交
178 179 180 181 182 183 184 185 186 187 188 189
	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 已提交
190

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

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

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

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

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

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

	if (tsk_used_math(tsk)) {
		if (HAVE_HWFP && tsk == current)
			unlazy_fpu(tsk);
238
		tsk->thread.fpu.last_cpu = ~0;
239 240 241
		return 0;
	}

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

	fpu_finit(&tsk->thread.fpu);

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

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

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

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

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

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

284 285
	sanitize_i387_state(target);

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

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;

299 300 301 302
	ret = init_fpu(target);
	if (ret)
		return ret;

303 304
	sanitize_i387_state(target);

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

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

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

R
Roland McGrath 已提交
320 321 322
	return ret;
}

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

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

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

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

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

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

/*
 * FPU tag word conversions.
 */

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

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

403
	return tmp;
L
Linus Torvalds 已提交
404 405
}

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

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

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

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

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

R
Roland McGrath 已提交
465 466 467 468 469 470 471
	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;
472 473 474 475 476
	/*
	 * 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 已提交
477
	if (tsk == current) {
478
		savesegment(ds, env->fos);
L
Linus Torvalds 已提交
479
	} else {
480
		env->fos = tsk->thread.ds;
L
Linus Torvalds 已提交
481
	}
482
	env->fos |= 0xffff0000;
R
Roland McGrath 已提交
483 484
#else
	env->fip = fxsave->fip;
J
Jan Beulich 已提交
485
	env->fcs = (u16) fxsave->fcs | ((u32) fxsave->fop << 16);
R
Roland McGrath 已提交
486 487 488
	env->foo = fxsave->foo;
	env->fos = fxsave->fos;
#endif
L
Linus Torvalds 已提交
489

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

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

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

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

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

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

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

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

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

542 543
	sanitize_i387_state(target);

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

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

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

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

561 562 563 564
	ret = init_fpu(target);
	if (ret)
		return ret;

565 566
	sanitize_i387_state(target);

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

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

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

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

/*
 * Signal frame handlers.
 */

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

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

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

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

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

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

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

633 634 635

	sanitize_i387_state(tsk);

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

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

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

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

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

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

685 686
	unlazy_fpu(tsk);

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

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

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

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

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

R
Roland McGrath 已提交
718
	return 0;
L
Linus Torvalds 已提交
719 720
}

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

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

736
	mask = fx_sw_user.xstate_bv;
737 738 739

	err = restore_i387_fxsave(buf, fx_sw_user.xstate_size);

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

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

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

770
	if (HAVE_HWFP)
771 772
		clear_fpu(tsk);

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

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

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

L
Linus Torvalds 已提交
805 806 807 808 809
	return err;
}

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

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

	return fpvalid;
}
828
EXPORT_SYMBOL(dump_fpu);
L
Linus Torvalds 已提交
829

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