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 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
/*
 * 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 已提交
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 238 239 240
 */
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 已提交
241
	/*
242
	 * Memory allocation at the first usage of the FPU and other state.
R
Roland McGrath 已提交
243
	 */
244 245 246 247 248 249
	ret = fpu_alloc(&tsk->thread.fpu);
	if (ret)
		return ret;

	fpu_finit(&tsk->thread.fpu);

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

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

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

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

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

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

283 284
	sanitize_i387_state(target);

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

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;

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

302 303
	sanitize_i387_state(target);

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

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

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

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

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

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

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

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

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

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

/*
 * FPU tag word conversions.
 */

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

541 542
	sanitize_i387_state(target);

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

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

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

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

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

564 565
	sanitize_i387_state(target);

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

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

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

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

/*
 * Signal frame handlers.
 */

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

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

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

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

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

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

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

632 633 634

	sanitize_i387_state(tsk);

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

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

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

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

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

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

684 685
	unlazy_fpu(tsk);

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

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

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

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

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

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

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

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

735
	mask = fx_sw_user.xstate_bv;
736 737 738

	err = restore_i387_fxsave(buf, fx_sw_user.xstate_size);

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

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

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

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

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

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

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

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

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

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

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

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