i387.c 15.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
#include <linux/sched.h>
11
#include <linux/slab.h>
I
Ingo Molnar 已提交
12 13

#include <asm/sigcontext.h>
L
Linus Torvalds 已提交
14 15
#include <asm/processor.h>
#include <asm/math_emu.h>
A
Andy Lutomirski 已提交
16
#include <asm/tlbflush.h>
L
Linus Torvalds 已提交
17
#include <asm/uaccess.h>
I
Ingo Molnar 已提交
18 19
#include <asm/ptrace.h>
#include <asm/i387.h>
20
#include <asm/fpu-internal.h>
I
Ingo Molnar 已提交
21
#include <asm/user.h>
L
Linus Torvalds 已提交
22

23 24
static DEFINE_PER_CPU(bool, in_kernel_fpu);

25 26 27 28 29 30 31 32 33 34 35
void kernel_fpu_disable(void)
{
	WARN_ON(this_cpu_read(in_kernel_fpu));
	this_cpu_write(in_kernel_fpu, true);
}

void kernel_fpu_enable(void)
{
	this_cpu_write(in_kernel_fpu, false);
}

36 37 38
/*
 * Were we in an interrupt that interrupted kernel mode?
 *
39
 * On others, we can do a kernel_fpu_begin/end() pair *ONLY* if that
40 41 42 43
 * 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).
44
 *
45 46
 * Except for the eagerfpu case when we return true; in the likely case
 * the thread has FPU but we are not going to set/clear TS.
47 48 49
 */
static inline bool interrupted_kernel_fpu_idle(void)
{
50 51 52
	if (this_cpu_read(in_kernel_fpu))
		return false;

53
	if (use_eager_fpu())
54
		return true;
55

56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
	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();
71
	return regs && user_mode(regs);
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
}

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

89
void __kernel_fpu_begin(void)
90 91 92
{
	struct task_struct *me = current;

93 94
	this_cpu_write(in_kernel_fpu, true);

95
	if (__thread_has_fpu(me)) {
96
		__save_init_fpu(me);
97
	} else {
98
		this_cpu_write(fpu_owner_task, NULL);
99 100
		if (!use_eager_fpu())
			clts();
101 102
	}
}
103
EXPORT_SYMBOL(__kernel_fpu_begin);
104

105
void __kernel_fpu_end(void)
106
{
107 108 109 110
	struct task_struct *me = current;

	if (__thread_has_fpu(me)) {
		if (WARN_ON(restore_fpu_checking(me)))
111
			fpu_reset_state(me);
112
	} else if (!use_eager_fpu()) {
113
		stts();
114
	}
115 116

	this_cpu_write(in_kernel_fpu, false);
117
}
118
EXPORT_SYMBOL(__kernel_fpu_end);
119

120 121
/*
 * Save the FPU state (initialize it if necessary):
122 123
 *
 * This only ever gets called for the current task.
124
 */
125
void fpu__save(struct task_struct *tsk)
126
{
127 128
	WARN_ON(tsk != current);

129 130
	preempt_disable();
	if (__thread_has_fpu(tsk)) {
131 132 133 134 135 136
		if (use_eager_fpu()) {
			__save_fpu(tsk);
		} else {
			__save_init_fpu(tsk);
			__thread_fpu_end(tsk);
		}
137
	}
138 139
	preempt_enable();
}
140
EXPORT_SYMBOL_GPL(fpu__save);
141

142
unsigned int mxcsr_feature_mask __read_mostly = 0xffffffffu;
143
unsigned int xstate_size;
144
EXPORT_SYMBOL_GPL(xstate_size);
145
static struct i387_fxsave_struct fx_scratch;
L
Linus Torvalds 已提交
146

147
static void mxcsr_feature_mask_init(void)
L
Linus Torvalds 已提交
148 149
{
	unsigned long mask = 0;
I
Ingo Molnar 已提交
150

L
Linus Torvalds 已提交
151
	if (cpu_has_fxsr) {
152
		memset(&fx_scratch, 0, sizeof(struct i387_fxsave_struct));
153
		asm volatile("fxsave %0" : "+m" (fx_scratch));
154
		mask = fx_scratch.mxcsr_mask;
155 156 157
		if (mask == 0)
			mask = 0x0000ffbf;
	}
L
Linus Torvalds 已提交
158 159 160
	mxcsr_feature_mask &= mask;
}

161
static void init_thread_xstate(void)
162
{
163 164 165 166 167
	/*
	 * Note that xstate_size might be overwriten later during
	 * xsave_init().
	 */

168
	if (!cpu_has_fpu) {
169 170 171 172 173 174
		/*
		 * 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);
175 176 177 178
		xstate_size = sizeof(struct i387_soft_struct);
		return;
	}

179 180 181 182 183 184
	if (cpu_has_fxsr)
		xstate_size = sizeof(struct i387_fxsave_struct);
	else
		xstate_size = sizeof(struct i387_fsave_struct);
}

R
Roland McGrath 已提交
185 186 187 188
/*
 * Called at bootup to set up the initial FPU state that is later cloned
 * into all processes.
 */
189

190
void fpu_init(void)
R
Roland McGrath 已提交
191
{
B
Brian Gerst 已提交
192 193
	unsigned long cr0;
	unsigned long cr4_mask = 0;
R
Roland McGrath 已提交
194

195 196 197 198 199 200 201 202
#ifndef CONFIG_MATH_EMULATION
	if (!cpu_has_fpu) {
		pr_emerg("No FPU found and no math emulation present\n");
		pr_emerg("Giving up\n");
		for (;;)
			asm volatile("hlt");
	}
#endif
B
Brian Gerst 已提交
203 204 205 206 207
	if (cpu_has_fxsr)
		cr4_mask |= X86_CR4_OSFXSR;
	if (cpu_has_xmm)
		cr4_mask |= X86_CR4_OSXMMEXCPT;
	if (cr4_mask)
A
Andy Lutomirski 已提交
208
		cr4_set_bits(cr4_mask);
B
Brian Gerst 已提交
209 210 211

	cr0 = read_cr0();
	cr0 &= ~(X86_CR0_TS|X86_CR0_EM); /* clear TS and EM */
212
	if (!cpu_has_fpu)
B
Brian Gerst 已提交
213 214
		cr0 |= X86_CR0_EM;
	write_cr0(cr0);
R
Roland McGrath 已提交
215

216 217 218 219 220
	/*
	 * init_thread_xstate is only called once to avoid overriding
	 * xstate_size during boot time or during CPU hotplug.
	 */
	if (xstate_size == 0)
221 222
		init_thread_xstate();

R
Roland McGrath 已提交
223
	mxcsr_feature_mask_init();
224 225
	xsave_init();
	eager_fpu_init();
R
Roland McGrath 已提交
226
}
227

S
Sheng Yang 已提交
228
void fpu_finit(struct fpu *fpu)
L
Linus Torvalds 已提交
229
{
230
	if (!cpu_has_fpu) {
231 232
		finit_soft_fpu(&fpu->state->soft);
		return;
233 234
	}

235 236
	memset(fpu->state, 0, xstate_size);

L
Linus Torvalds 已提交
237
	if (cpu_has_fxsr) {
238
		fx_finit(&fpu->state->fxsave);
L
Linus Torvalds 已提交
239
	} else {
240
		struct i387_fsave_struct *fp = &fpu->state->fsave;
241 242 243 244
		fp->cwd = 0xffff037fu;
		fp->swd = 0xffff0000u;
		fp->twd = 0xffffffffu;
		fp->fos = 0xffff0000u;
L
Linus Torvalds 已提交
245
	}
246
}
S
Sheng Yang 已提交
247
EXPORT_SYMBOL_GPL(fpu_finit);
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
/*
 * Allocate the backing store for the current task's FPU registers
 * and initialize the registers themselves as well.
 *
 * Can fail.
 */
int fpstate_alloc_init(struct task_struct *curr)
{
	int ret;

	if (WARN_ON_ONCE(curr != current))
		return -EINVAL;
	if (WARN_ON_ONCE(curr->flags & PF_USED_MATH))
		return -EINVAL;

	/*
	 * Memory allocation at the first usage of the FPU and other state.
	 */
	ret = fpu_alloc(&curr->thread.fpu);
	if (ret)
		return ret;

	fpu_finit(&curr->thread.fpu);

	/* Safe to do for the current task: */
	curr->flags |= PF_USED_MATH;

	return 0;
}
EXPORT_SYMBOL_GPL(fpstate_alloc_init);

280 281 282 283
/*
 * 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 已提交
284
 * remember the current task has used the FPU.
285
 */
286
static int fpu__unlazy_stopped(struct task_struct *child)
287 288 289
{
	int ret;

290 291 292 293 294
	if (WARN_ON_ONCE(child == current))
		return -EINVAL;

	if (tsk_used_math(child)) {
		task_disable_lazy_fpu_restore(child);
295 296 297
		return 0;
	}

R
Roland McGrath 已提交
298
	/*
299
	 * Memory allocation at the first usage of the FPU and other state.
R
Roland McGrath 已提交
300
	 */
301
	ret = fpu_alloc(&child->thread.fpu);
302 303 304
	if (ret)
		return ret;

305
	fpu_finit(&child->thread.fpu);
306

307
	set_stopped_child_used_math(child);
308
	return 0;
L
Linus Torvalds 已提交
309 310
}

311 312 313 314 315
/*
 * 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 已提交
316 317 318 319
int fpregs_active(struct task_struct *target, const struct user_regset *regset)
{
	return tsk_used_math(target) ? regset->n : 0;
}
L
Linus Torvalds 已提交
320

R
Roland McGrath 已提交
321
int xfpregs_active(struct task_struct *target, const struct user_regset *regset)
L
Linus Torvalds 已提交
322
{
R
Roland McGrath 已提交
323 324
	return (cpu_has_fxsr && tsk_used_math(target)) ? regset->n : 0;
}
L
Linus Torvalds 已提交
325

R
Roland McGrath 已提交
326 327 328 329
int xfpregs_get(struct task_struct *target, const struct user_regset *regset,
		unsigned int pos, unsigned int count,
		void *kbuf, void __user *ubuf)
{
330 331
	int ret;

R
Roland McGrath 已提交
332 333 334
	if (!cpu_has_fxsr)
		return -ENODEV;

335
	ret = fpu__unlazy_stopped(target);
336 337
	if (ret)
		return ret;
R
Roland McGrath 已提交
338

339 340
	sanitize_i387_state(target);

R
Roland McGrath 已提交
341
	return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
342
				   &target->thread.fpu.state->fxsave, 0, -1);
L
Linus Torvalds 已提交
343
}
R
Roland McGrath 已提交
344 345 346 347 348 349 350 351 352 353

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;

354
	ret = fpu__unlazy_stopped(target);
355 356 357
	if (ret)
		return ret;

358 359
	sanitize_i387_state(target);

R
Roland McGrath 已提交
360
	ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
361
				 &target->thread.fpu.state->fxsave, 0, -1);
R
Roland McGrath 已提交
362 363 364 365

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

368 369 370 371 372
	/*
	 * update the header bits in the xsave header, indicating the
	 * presence of FP and SSE state.
	 */
	if (cpu_has_xsave)
373
		target->thread.fpu.state->xsave.xsave_hdr.xstate_bv |= XSTATE_FPSSE;
374

R
Roland McGrath 已提交
375 376 377
	return ret;
}

378 379 380 381
int xstateregs_get(struct task_struct *target, const struct user_regset *regset,
		unsigned int pos, unsigned int count,
		void *kbuf, void __user *ubuf)
{
382
	struct xsave_struct *xsave;
383 384 385 386 387
	int ret;

	if (!cpu_has_xsave)
		return -ENODEV;

388
	ret = fpu__unlazy_stopped(target);
389 390 391
	if (ret)
		return ret;

392 393
	xsave = &target->thread.fpu.state->xsave;

394
	/*
395 396 397
	 * 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().
398
	 */
399 400
	memcpy(&xsave->i387.sw_reserved,
		xstate_fx_sw_bytes, sizeof(xstate_fx_sw_bytes));
401
	/*
402
	 * Copy the xstate memory layout.
403
	 */
404
	ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, xsave, 0, -1);
405 406 407 408 409 410 411
	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)
{
412
	struct xsave_struct *xsave;
413 414 415 416 417
	int ret;

	if (!cpu_has_xsave)
		return -ENODEV;

418
	ret = fpu__unlazy_stopped(target);
419 420 421
	if (ret)
		return ret;

422 423
	xsave = &target->thread.fpu.state->xsave;

424
	ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, xsave, 0, -1);
425 426 427
	/*
	 * mxcsr reserved bits must be masked to zero for security reasons.
	 */
428 429
	xsave->i387.mxcsr &= mxcsr_feature_mask;
	xsave->xsave_hdr.xstate_bv &= pcntxt_mask;
430 431 432
	/*
	 * These bits must be zero.
	 */
433
	memset(&xsave->xsave_hdr.reserved, 0, 48);
434 435 436
	return ret;
}

R
Roland McGrath 已提交
437
#if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
L
Linus Torvalds 已提交
438 439 440 441 442

/*
 * FPU tag word conversions.
 */

443
static inline unsigned short twd_i387_to_fxsr(unsigned short twd)
L
Linus Torvalds 已提交
444 445
{
	unsigned int tmp; /* to avoid 16 bit prefixes in the code */
446

L
Linus Torvalds 已提交
447
	/* Transform each pair of bits into 01 (valid) or 00 (empty) */
448
	tmp = ~twd;
R
Roland McGrath 已提交
449
	tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */
450 451 452 453
	/* 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 已提交
454

455
	return tmp;
L
Linus Torvalds 已提交
456 457
}

458
#define FPREG_ADDR(f, n)	((void *)&(f)->st_space + (n) * 16)
R
Roland McGrath 已提交
459 460 461 462 463 464 465 466 467 468 469 470 471
#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 已提交
472

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

477
			switch (st->exponent & 0x7fff) {
L
Linus Torvalds 已提交
478
			case 0x7fff:
R
Roland McGrath 已提交
479
				tag = FP_EXP_TAG_SPECIAL;
L
Linus Torvalds 已提交
480 481
				break;
			case 0x0000:
482 483 484
				if (!st->significand[0] &&
				    !st->significand[1] &&
				    !st->significand[2] &&
R
Roland McGrath 已提交
485 486 487 488
				    !st->significand[3])
					tag = FP_EXP_TAG_ZERO;
				else
					tag = FP_EXP_TAG_SPECIAL;
L
Linus Torvalds 已提交
489 490
				break;
			default:
R
Roland McGrath 已提交
491 492 493 494
				if (st->significand[3] & 0x8000)
					tag = FP_EXP_TAG_VALID;
				else
					tag = FP_EXP_TAG_SPECIAL;
L
Linus Torvalds 已提交
495 496 497
				break;
			}
		} else {
R
Roland McGrath 已提交
498
			tag = FP_EXP_TAG_EMPTY;
L
Linus Torvalds 已提交
499
		}
R
Roland McGrath 已提交
500
		ret |= tag << (2 * i);
L
Linus Torvalds 已提交
501 502 503 504 505
	}
	return ret;
}

/*
R
Roland McGrath 已提交
506
 * FXSR floating point environment conversions.
L
Linus Torvalds 已提交
507 508
 */

509
void
I
Ingo Molnar 已提交
510
convert_from_fxsr(struct user_i387_ia32_struct *env, struct task_struct *tsk)
L
Linus Torvalds 已提交
511
{
512
	struct i387_fxsave_struct *fxsave = &tsk->thread.fpu.state->fxsave;
R
Roland McGrath 已提交
513 514 515
	struct _fpreg *to = (struct _fpreg *) &env->st_space[0];
	struct _fpxreg *from = (struct _fpxreg *) &fxsave->st_space[0];
	int i;
L
Linus Torvalds 已提交
516

R
Roland McGrath 已提交
517 518 519 520 521 522 523
	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;
524 525 526 527 528
	/*
	 * 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 已提交
529
	if (tsk == current) {
530
		savesegment(ds, env->fos);
L
Linus Torvalds 已提交
531
	} else {
532
		env->fos = tsk->thread.ds;
L
Linus Torvalds 已提交
533
	}
534
	env->fos |= 0xffff0000;
R
Roland McGrath 已提交
535 536
#else
	env->fip = fxsave->fip;
J
Jan Beulich 已提交
537
	env->fcs = (u16) fxsave->fcs | ((u32) fxsave->fop << 16);
R
Roland McGrath 已提交
538 539 540
	env->foo = fxsave->foo;
	env->fos = fxsave->fos;
#endif
L
Linus Torvalds 已提交
541

R
Roland McGrath 已提交
542 543
	for (i = 0; i < 8; ++i)
		memcpy(&to[i], &from[i], sizeof(to[0]));
L
Linus Torvalds 已提交
544 545
}

546 547
void convert_to_fxsr(struct task_struct *tsk,
		     const struct user_i387_ia32_struct *env)
L
Linus Torvalds 已提交
548 549

{
550
	struct i387_fxsave_struct *fxsave = &tsk->thread.fpu.state->fxsave;
R
Roland McGrath 已提交
551 552 553
	struct _fpreg *from = (struct _fpreg *) &env->st_space[0];
	struct _fpxreg *to = (struct _fpxreg *) &fxsave->st_space[0];
	int i;
L
Linus Torvalds 已提交
554

R
Roland McGrath 已提交
555 556 557 558 559 560 561 562 563 564 565 566 567 568
	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 已提交
569

R
Roland McGrath 已提交
570 571
	for (i = 0; i < 8; ++i)
		memcpy(&to[i], &from[i], sizeof(from[0]));
L
Linus Torvalds 已提交
572 573
}

R
Roland McGrath 已提交
574 575 576
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 已提交
577
{
R
Roland McGrath 已提交
578
	struct user_i387_ia32_struct env;
579
	int ret;
L
Linus Torvalds 已提交
580

581
	ret = fpu__unlazy_stopped(target);
582 583
	if (ret)
		return ret;
L
Linus Torvalds 已提交
584

585
	if (!static_cpu_has(X86_FEATURE_FPU))
586 587
		return fpregs_soft_get(target, regset, pos, count, kbuf, ubuf);

588
	if (!cpu_has_fxsr)
R
Roland McGrath 已提交
589
		return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
590
					   &target->thread.fpu.state->fsave, 0,
591
					   -1);
L
Linus Torvalds 已提交
592

593 594
	sanitize_i387_state(target);

R
Roland McGrath 已提交
595 596 597
	if (kbuf && pos == 0 && count == sizeof(env)) {
		convert_from_fxsr(kbuf, target);
		return 0;
L
Linus Torvalds 已提交
598
	}
R
Roland McGrath 已提交
599 600

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

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

R
Roland McGrath 已提交
605 606 607
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 已提交
608
{
R
Roland McGrath 已提交
609 610
	struct user_i387_ia32_struct env;
	int ret;
L
Linus Torvalds 已提交
611

612
	ret = fpu__unlazy_stopped(target);
613 614 615
	if (ret)
		return ret;

616 617
	sanitize_i387_state(target);

618
	if (!static_cpu_has(X86_FEATURE_FPU))
619 620
		return fpregs_soft_set(target, regset, pos, count, kbuf, ubuf);

621
	if (!cpu_has_fxsr)
R
Roland McGrath 已提交
622
		return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
623 624
					  &target->thread.fpu.state->fsave, 0,
					  -1);
R
Roland McGrath 已提交
625 626 627 628 629 630 631 632

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

633 634 635 636 637
	/*
	 * update the header bit in the xsave header, indicating the
	 * presence of FP.
	 */
	if (cpu_has_xsave)
638
		target->thread.fpu.state->xsave.xsave_hdr.xstate_bv |= XSTATE_FP;
R
Roland McGrath 已提交
639
	return ret;
L
Linus Torvalds 已提交
640 641 642 643
}

/*
 * FPU state for core dumps.
R
Roland McGrath 已提交
644 645 646 647
 * 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 已提交
648
 */
649
int dump_fpu(struct pt_regs *regs, struct user_i387_struct *fpu)
L
Linus Torvalds 已提交
650 651
{
	struct task_struct *tsk = current;
I
Ingo Molnar 已提交
652
	int fpvalid;
L
Linus Torvalds 已提交
653 654

	fpvalid = !!used_math();
R
Roland McGrath 已提交
655 656 657 658
	if (fpvalid)
		fpvalid = !fpregs_get(tsk, NULL,
				      0, sizeof(struct user_i387_ia32_struct),
				      fpu, NULL);
L
Linus Torvalds 已提交
659 660 661

	return fpvalid;
}
662
EXPORT_SYMBOL(dump_fpu);
L
Linus Torvalds 已提交
663

R
Roland McGrath 已提交
664
#endif	/* CONFIG_X86_32 || CONFIG_IA32_EMULATION */
665 666 667 668 669 670 671 672 673

static int __init no_387(char *s)
{
	setup_clear_cpu_cap(X86_FEATURE_FPU);
	return 1;
}

__setup("no387", no_387);

674 675 676 677 678
/*
 * Set the X86_FEATURE_FPU CPU-capability bit based on
 * trying to execute an actual sequence of FPU instructions:
 */
void fpu__detect(struct cpuinfo_x86 *c)
679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698
{
	unsigned long cr0;
	u16 fsw, fcw;

	fsw = fcw = 0xffff;

	cr0 = read_cr0();
	cr0 &= ~(X86_CR0_TS | X86_CR0_EM);
	write_cr0(cr0);

	asm volatile("fninit ; fnstsw %0 ; fnstcw %1"
		     : "+m" (fsw), "+m" (fcw));

	if (fsw == 0 && (fcw & 0x103f) == 0x003f)
		set_cpu_cap(c, X86_FEATURE_FPU);
	else
		clear_cpu_cap(c, X86_FEATURE_FPU);

	/* The final cr0 value is set in fpu_init() */
}