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

22 23 24
/*
 * Were we in an interrupt that interrupted kernel mode?
 *
25
 * On others, we can do a kernel_fpu_begin/end() pair *ONLY* if that
26 27 28 29
 * 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).
30 31 32
 *
 * Except for the eagerfpu case when we return 1 unless we've already
 * been eager and saved the state in kernel_fpu_begin().
33 34 35
 */
static inline bool interrupted_kernel_fpu_idle(void)
{
36
	if (use_eager_fpu())
37
		return __thread_has_fpu(current);
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
	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);

72
void __kernel_fpu_begin(void)
73 74 75 76 77
{
	struct task_struct *me = current;

	if (__thread_has_fpu(me)) {
		__thread_clear_has_fpu(me);
78
		__save_init_fpu(me);
79
		/* We do 'stts()' in __kernel_fpu_end() */
80
	} else if (!use_eager_fpu()) {
81
		this_cpu_write(fpu_owner_task, NULL);
82 83 84
		clts();
	}
}
85
EXPORT_SYMBOL(__kernel_fpu_begin);
86

87
void __kernel_fpu_end(void)
88
{
89
	if (use_eager_fpu())
90 91 92
		math_state_restore();
	else
		stts();
93
}
94
EXPORT_SYMBOL(__kernel_fpu_end);
95 96 97 98 99 100 101 102 103 104 105 106 107

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

108
unsigned int mxcsr_feature_mask __read_mostly = 0xffffffffu;
109
unsigned int xstate_size;
110
EXPORT_SYMBOL_GPL(xstate_size);
111
static struct i387_fxsave_struct fx_scratch __cpuinitdata;
L
Linus Torvalds 已提交
112

113
static void __cpuinit mxcsr_feature_mask_init(void)
L
Linus Torvalds 已提交
114 115
{
	unsigned long mask = 0;
I
Ingo Molnar 已提交
116

L
Linus Torvalds 已提交
117
	if (cpu_has_fxsr) {
118 119 120
		memset(&fx_scratch, 0, sizeof(struct i387_fxsave_struct));
		asm volatile("fxsave %0" : : "m" (fx_scratch));
		mask = fx_scratch.mxcsr_mask;
121 122 123
		if (mask == 0)
			mask = 0x0000ffbf;
	}
L
Linus Torvalds 已提交
124 125 126
	mxcsr_feature_mask &= mask;
}

127
static void __cpuinit init_thread_xstate(void)
128
{
129 130 131 132 133
	/*
	 * Note that xstate_size might be overwriten later during
	 * xsave_init().
	 */

134
	if (!cpu_has_fpu) {
135 136 137 138 139 140
		/*
		 * 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);
141 142 143 144
		xstate_size = sizeof(struct i387_soft_struct);
		return;
	}

145 146 147 148 149 150
	if (cpu_has_fxsr)
		xstate_size = sizeof(struct i387_fxsave_struct);
	else
		xstate_size = sizeof(struct i387_fsave_struct);
}

R
Roland McGrath 已提交
151 152 153 154
/*
 * Called at bootup to set up the initial FPU state that is later cloned
 * into all processes.
 */
155

R
Roland McGrath 已提交
156 157
void __cpuinit fpu_init(void)
{
B
Brian Gerst 已提交
158 159
	unsigned long cr0;
	unsigned long cr4_mask = 0;
R
Roland McGrath 已提交
160

161 162 163 164 165 166 167 168
#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 已提交
169 170 171 172 173 174 175 176 177
	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 */
178
	if (!cpu_has_fpu)
B
Brian Gerst 已提交
179 180
		cr0 |= X86_CR0_EM;
	write_cr0(cr0);
R
Roland McGrath 已提交
181

182 183 184 185 186
	/*
	 * init_thread_xstate is only called once to avoid overriding
	 * xstate_size during boot time or during CPU hotplug.
	 */
	if (xstate_size == 0)
187 188
		init_thread_xstate();

R
Roland McGrath 已提交
189
	mxcsr_feature_mask_init();
190 191
	xsave_init();
	eager_fpu_init();
R
Roland McGrath 已提交
192
}
193

S
Sheng Yang 已提交
194
void fpu_finit(struct fpu *fpu)
L
Linus Torvalds 已提交
195
{
196
	if (!cpu_has_fpu) {
197 198
		finit_soft_fpu(&fpu->state->soft);
		return;
199 200
	}

L
Linus Torvalds 已提交
201
	if (cpu_has_fxsr) {
202
		fx_finit(&fpu->state->fxsave);
L
Linus Torvalds 已提交
203
	} else {
204
		struct i387_fsave_struct *fp = &fpu->state->fsave;
205 206 207 208 209
		memset(fp, 0, xstate_size);
		fp->cwd = 0xffff037fu;
		fp->swd = 0xffff0000u;
		fp->twd = 0xffffffffu;
		fp->fos = 0xffff0000u;
L
Linus Torvalds 已提交
210
	}
211
}
S
Sheng Yang 已提交
212
EXPORT_SYMBOL_GPL(fpu_finit);
213 214 215 216 217

/*
 * 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 已提交
218
 * remember the current task has used the FPU.
219 220 221 222 223 224
 */
int init_fpu(struct task_struct *tsk)
{
	int ret;

	if (tsk_used_math(tsk)) {
225
		if (cpu_has_fpu && tsk == current)
226
			unlazy_fpu(tsk);
227
		tsk->thread.fpu.last_cpu = ~0;
228 229 230
		return 0;
	}

R
Roland McGrath 已提交
231
	/*
232
	 * Memory allocation at the first usage of the FPU and other state.
R
Roland McGrath 已提交
233
	 */
234 235 236 237 238 239
	ret = fpu_alloc(&tsk->thread.fpu);
	if (ret)
		return ret;

	fpu_finit(&tsk->thread.fpu);

L
Linus Torvalds 已提交
240
	set_stopped_child_used_math(tsk);
241
	return 0;
L
Linus Torvalds 已提交
242
}
243
EXPORT_SYMBOL_GPL(init_fpu);
L
Linus Torvalds 已提交
244

245 246 247 248 249
/*
 * 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 已提交
250 251 252 253
int fpregs_active(struct task_struct *target, const struct user_regset *regset)
{
	return tsk_used_math(target) ? regset->n : 0;
}
L
Linus Torvalds 已提交
254

R
Roland McGrath 已提交
255
int xfpregs_active(struct task_struct *target, const struct user_regset *regset)
L
Linus Torvalds 已提交
256
{
R
Roland McGrath 已提交
257 258
	return (cpu_has_fxsr && tsk_used_math(target)) ? regset->n : 0;
}
L
Linus Torvalds 已提交
259

R
Roland McGrath 已提交
260 261 262 263
int xfpregs_get(struct task_struct *target, const struct user_regset *regset,
		unsigned int pos, unsigned int count,
		void *kbuf, void __user *ubuf)
{
264 265
	int ret;

R
Roland McGrath 已提交
266 267 268
	if (!cpu_has_fxsr)
		return -ENODEV;

269 270 271
	ret = init_fpu(target);
	if (ret)
		return ret;
R
Roland McGrath 已提交
272

273 274
	sanitize_i387_state(target);

R
Roland McGrath 已提交
275
	return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
276
				   &target->thread.fpu.state->fxsave, 0, -1);
L
Linus Torvalds 已提交
277
}
R
Roland McGrath 已提交
278 279 280 281 282 283 284 285 286 287

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;

288 289 290 291
	ret = init_fpu(target);
	if (ret)
		return ret;

292 293
	sanitize_i387_state(target);

R
Roland McGrath 已提交
294
	ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
295
				 &target->thread.fpu.state->fxsave, 0, -1);
R
Roland McGrath 已提交
296 297 298 299

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

302 303 304 305 306
	/*
	 * update the header bits in the xsave header, indicating the
	 * presence of FP and SSE state.
	 */
	if (cpu_has_xsave)
307
		target->thread.fpu.state->xsave.xsave_hdr.xstate_bv |= XSTATE_FPSSE;
308

R
Roland McGrath 已提交
309 310 311
	return ret;
}

312 313 314 315 316 317 318 319 320 321 322 323 324 325
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;

	/*
326 327 328
	 * 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().
329
	 */
330
	memcpy(&target->thread.fpu.state->fxsave.sw_reserved,
331
	       xstate_fx_sw_bytes, sizeof(xstate_fx_sw_bytes));
332 333

	/*
334
	 * Copy the xstate memory layout.
335 336
	 */
	ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
337
				  &target->thread.fpu.state->xsave, 0, -1);
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355
	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,
356
				 &target->thread.fpu.state->xsave, 0, -1);
357 358 359 360

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

363
	xsave_hdr = &target->thread.fpu.state->xsave.xsave_hdr;
364 365 366 367 368 369 370 371 372 373

	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 已提交
374
#if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
L
Linus Torvalds 已提交
375 376 377 378 379

/*
 * FPU tag word conversions.
 */

380
static inline unsigned short twd_i387_to_fxsr(unsigned short twd)
L
Linus Torvalds 已提交
381 382
{
	unsigned int tmp; /* to avoid 16 bit prefixes in the code */
383

L
Linus Torvalds 已提交
384
	/* Transform each pair of bits into 01 (valid) or 00 (empty) */
385
	tmp = ~twd;
R
Roland McGrath 已提交
386
	tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */
387 388 389 390
	/* 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 已提交
391

392
	return tmp;
L
Linus Torvalds 已提交
393 394
}

395
#define FPREG_ADDR(f, n)	((void *)&(f)->st_space + (n) * 16)
R
Roland McGrath 已提交
396 397 398 399 400 401 402 403 404 405 406 407 408
#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 已提交
409

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

414
			switch (st->exponent & 0x7fff) {
L
Linus Torvalds 已提交
415
			case 0x7fff:
R
Roland McGrath 已提交
416
				tag = FP_EXP_TAG_SPECIAL;
L
Linus Torvalds 已提交
417 418
				break;
			case 0x0000:
419 420 421
				if (!st->significand[0] &&
				    !st->significand[1] &&
				    !st->significand[2] &&
R
Roland McGrath 已提交
422 423 424 425
				    !st->significand[3])
					tag = FP_EXP_TAG_ZERO;
				else
					tag = FP_EXP_TAG_SPECIAL;
L
Linus Torvalds 已提交
426 427
				break;
			default:
R
Roland McGrath 已提交
428 429 430 431
				if (st->significand[3] & 0x8000)
					tag = FP_EXP_TAG_VALID;
				else
					tag = FP_EXP_TAG_SPECIAL;
L
Linus Torvalds 已提交
432 433 434
				break;
			}
		} else {
R
Roland McGrath 已提交
435
			tag = FP_EXP_TAG_EMPTY;
L
Linus Torvalds 已提交
436
		}
R
Roland McGrath 已提交
437
		ret |= tag << (2 * i);
L
Linus Torvalds 已提交
438 439 440 441 442
	}
	return ret;
}

/*
R
Roland McGrath 已提交
443
 * FXSR floating point environment conversions.
L
Linus Torvalds 已提交
444 445
 */

446
void
I
Ingo Molnar 已提交
447
convert_from_fxsr(struct user_i387_ia32_struct *env, struct task_struct *tsk)
L
Linus Torvalds 已提交
448
{
449
	struct i387_fxsave_struct *fxsave = &tsk->thread.fpu.state->fxsave;
R
Roland McGrath 已提交
450 451 452
	struct _fpreg *to = (struct _fpreg *) &env->st_space[0];
	struct _fpxreg *from = (struct _fpxreg *) &fxsave->st_space[0];
	int i;
L
Linus Torvalds 已提交
453

R
Roland McGrath 已提交
454 455 456 457 458 459 460
	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;
461 462 463 464 465
	/*
	 * 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 已提交
466
	if (tsk == current) {
467
		savesegment(ds, env->fos);
L
Linus Torvalds 已提交
468
	} else {
469
		env->fos = tsk->thread.ds;
L
Linus Torvalds 已提交
470
	}
471
	env->fos |= 0xffff0000;
R
Roland McGrath 已提交
472 473
#else
	env->fip = fxsave->fip;
J
Jan Beulich 已提交
474
	env->fcs = (u16) fxsave->fcs | ((u32) fxsave->fop << 16);
R
Roland McGrath 已提交
475 476 477
	env->foo = fxsave->foo;
	env->fos = fxsave->fos;
#endif
L
Linus Torvalds 已提交
478

R
Roland McGrath 已提交
479 480
	for (i = 0; i < 8; ++i)
		memcpy(&to[i], &from[i], sizeof(to[0]));
L
Linus Torvalds 已提交
481 482
}

483 484
void convert_to_fxsr(struct task_struct *tsk,
		     const struct user_i387_ia32_struct *env)
L
Linus Torvalds 已提交
485 486

{
487
	struct i387_fxsave_struct *fxsave = &tsk->thread.fpu.state->fxsave;
R
Roland McGrath 已提交
488 489 490
	struct _fpreg *from = (struct _fpreg *) &env->st_space[0];
	struct _fpxreg *to = (struct _fpxreg *) &fxsave->st_space[0];
	int i;
L
Linus Torvalds 已提交
491

R
Roland McGrath 已提交
492 493 494 495 496 497 498 499 500 501 502 503 504 505
	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 已提交
506

R
Roland McGrath 已提交
507 508
	for (i = 0; i < 8; ++i)
		memcpy(&to[i], &from[i], sizeof(from[0]));
L
Linus Torvalds 已提交
509 510
}

R
Roland McGrath 已提交
511 512 513
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 已提交
514
{
R
Roland McGrath 已提交
515
	struct user_i387_ia32_struct env;
516
	int ret;
L
Linus Torvalds 已提交
517

518 519 520
	ret = init_fpu(target);
	if (ret)
		return ret;
L
Linus Torvalds 已提交
521

522
	if (!static_cpu_has(X86_FEATURE_FPU))
523 524
		return fpregs_soft_get(target, regset, pos, count, kbuf, ubuf);

525
	if (!cpu_has_fxsr)
R
Roland McGrath 已提交
526
		return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
527
					   &target->thread.fpu.state->fsave, 0,
528
					   -1);
L
Linus Torvalds 已提交
529

530 531
	sanitize_i387_state(target);

R
Roland McGrath 已提交
532 533 534
	if (kbuf && pos == 0 && count == sizeof(env)) {
		convert_from_fxsr(kbuf, target);
		return 0;
L
Linus Torvalds 已提交
535
	}
R
Roland McGrath 已提交
536 537

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

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

R
Roland McGrath 已提交
542 543 544
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 已提交
545
{
R
Roland McGrath 已提交
546 547
	struct user_i387_ia32_struct env;
	int ret;
L
Linus Torvalds 已提交
548

549 550 551 552
	ret = init_fpu(target);
	if (ret)
		return ret;

553 554
	sanitize_i387_state(target);

555
	if (!static_cpu_has(X86_FEATURE_FPU))
556 557
		return fpregs_soft_set(target, regset, pos, count, kbuf, ubuf);

558
	if (!cpu_has_fxsr)
R
Roland McGrath 已提交
559
		return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
560 561
					  &target->thread.fpu.state->fsave, 0,
					  -1);
R
Roland McGrath 已提交
562 563 564 565 566 567 568 569

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

570 571 572 573 574
	/*
	 * update the header bit in the xsave header, indicating the
	 * presence of FP.
	 */
	if (cpu_has_xsave)
575
		target->thread.fpu.state->xsave.xsave_hdr.xstate_bv |= XSTATE_FP;
R
Roland McGrath 已提交
576
	return ret;
L
Linus Torvalds 已提交
577 578 579 580
}

/*
 * FPU state for core dumps.
R
Roland McGrath 已提交
581 582 583 584
 * 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 已提交
585
 */
586
int dump_fpu(struct pt_regs *regs, struct user_i387_struct *fpu)
L
Linus Torvalds 已提交
587 588
{
	struct task_struct *tsk = current;
I
Ingo Molnar 已提交
589
	int fpvalid;
L
Linus Torvalds 已提交
590 591

	fpvalid = !!used_math();
R
Roland McGrath 已提交
592 593 594 595
	if (fpvalid)
		fpvalid = !fpregs_get(tsk, NULL,
				      0, sizeof(struct user_i387_ia32_struct),
				      fpu, NULL);
L
Linus Torvalds 已提交
596 597 598

	return fpvalid;
}
599
EXPORT_SYMBOL(dump_fpu);
L
Linus Torvalds 已提交
600

R
Roland McGrath 已提交
601
#endif	/* CONFIG_X86_32 || CONFIG_IA32_EMULATION */
602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631

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

__setup("no387", no_387);

void __cpuinit fpu_detect(struct cpuinfo_x86 *c)
{
	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() */
}