i387.c 14.9 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 11 12
#include <linux/sched.h>

#include <asm/sigcontext.h>
L
Linus Torvalds 已提交
13 14 15
#include <asm/processor.h>
#include <asm/math_emu.h>
#include <asm/uaccess.h>
I
Ingo Molnar 已提交
16 17 18
#include <asm/ptrace.h>
#include <asm/i387.h>
#include <asm/user.h>
L
Linus Torvalds 已提交
19

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

L
Linus Torvalds 已提交
34
#ifdef CONFIG_MATH_EMULATION
I
Ingo Molnar 已提交
35
# define HAVE_HWFP		(boot_cpu_data.hard_math)
L
Linus Torvalds 已提交
36
#else
I
Ingo Molnar 已提交
37
# define HAVE_HWFP		1
L
Linus Torvalds 已提交
38 39
#endif

I
Ingo Molnar 已提交
40
static unsigned int		mxcsr_feature_mask __read_mostly = 0xffffffffu;
41
unsigned int xstate_size;
42
unsigned int sig_xstate_ia32_size = sizeof(struct _fpstate_ia32);
43
static struct i387_fxsave_struct fx_scratch __cpuinitdata;
L
Linus Torvalds 已提交
44

45
void __cpuinit mxcsr_feature_mask_init(void)
L
Linus Torvalds 已提交
46 47
{
	unsigned long mask = 0;
I
Ingo Molnar 已提交
48

L
Linus Torvalds 已提交
49 50
	clts();
	if (cpu_has_fxsr) {
51 52 53
		memset(&fx_scratch, 0, sizeof(struct i387_fxsave_struct));
		asm volatile("fxsave %0" : : "m" (fx_scratch));
		mask = fx_scratch.mxcsr_mask;
54 55 56
		if (mask == 0)
			mask = 0x0000ffbf;
	}
L
Linus Torvalds 已提交
57 58 59 60
	mxcsr_feature_mask &= mask;
	stts();
}

61 62
void __init init_thread_xstate(void)
{
63 64 65 66 67
	if (!HAVE_HWFP) {
		xstate_size = sizeof(struct i387_soft_struct);
		return;
	}

68 69 70 71 72
	if (cpu_has_xsave) {
		xsave_cntxt_init();
		return;
	}

73 74 75 76 77 78 79 80
	if (cpu_has_fxsr)
		xstate_size = sizeof(struct i387_fxsave_struct);
#ifdef CONFIG_X86_32
	else
		xstate_size = sizeof(struct i387_fsave_struct);
#endif
}

R
Roland McGrath 已提交
81 82 83 84 85 86 87 88
#ifdef CONFIG_X86_64
/*
 * Called at bootup to set up the initial FPU state that is later cloned
 * into all processes.
 */
void __cpuinit fpu_init(void)
{
	unsigned long oldcr0 = read_cr0();
I
Ingo Molnar 已提交
89

R
Roland McGrath 已提交
90 91 92
	set_in_cr4(X86_CR4_OSFXSR);
	set_in_cr4(X86_CR4_OSXMMEXCPT);

I
Ingo Molnar 已提交
93
	write_cr0(oldcr0 & ~(X86_CR0_TS|X86_CR0_EM)); /* clear TS and EM */
R
Roland McGrath 已提交
94

95 96 97 98 99 100 101
	/*
	 * Boot processor to setup the FP and extended state context info.
	 */
	if (!smp_processor_id())
		init_thread_xstate();
	xsave_init();

R
Roland McGrath 已提交
102 103
	mxcsr_feature_mask_init();
	/* clean state in init */
104 105 106 107
	if (cpu_has_xsave)
		current_thread_info()->status = TS_XSAVE;
	else
		current_thread_info()->status = 0;
R
Roland McGrath 已提交
108 109 110 111
	clear_used_math();
}
#endif	/* CONFIG_X86_64 */

L
Linus Torvalds 已提交
112 113 114 115 116 117
/*
 * 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
 * remeber the current task has used the FPU.
 */
118
int init_fpu(struct task_struct *tsk)
L
Linus Torvalds 已提交
119
{
R
Roland McGrath 已提交
120
	if (tsk_used_math(tsk)) {
121
		if (HAVE_HWFP && tsk == current)
R
Roland McGrath 已提交
122
			unlazy_fpu(tsk);
123 124 125 126 127 128 129 130 131 132 133
		return 0;
	}

	/*
	 * Memory allocation at the first usage of the FPU and other state.
	 */
	if (!tsk->thread.xstate) {
		tsk->thread.xstate = kmem_cache_alloc(task_xstate_cachep,
						      GFP_KERNEL);
		if (!tsk->thread.xstate)
			return -ENOMEM;
R
Roland McGrath 已提交
134 135
	}

136 137 138 139 140 141 142 143 144
#ifdef CONFIG_X86_32
	if (!HAVE_HWFP) {
		memset(tsk->thread.xstate, 0, xstate_size);
		finit();
		set_stopped_child_used_math(tsk);
		return 0;
	}
#endif

L
Linus Torvalds 已提交
145
	if (cpu_has_fxsr) {
146 147 148 149
		struct i387_fxsave_struct *fx = &tsk->thread.xstate->fxsave;

		memset(fx, 0, xstate_size);
		fx->cwd = 0x37f;
L
Linus Torvalds 已提交
150
		if (cpu_has_xmm)
151
			fx->mxcsr = MXCSR_DEFAULT;
L
Linus Torvalds 已提交
152
	} else {
153 154 155 156 157 158
		struct i387_fsave_struct *fp = &tsk->thread.xstate->fsave;
		memset(fp, 0, xstate_size);
		fp->cwd = 0xffff037fu;
		fp->swd = 0xffff0000u;
		fp->twd = 0xffffffffu;
		fp->fos = 0xffff0000u;
L
Linus Torvalds 已提交
159
	}
R
Roland McGrath 已提交
160 161 162
	/*
	 * Only the device not available exception or ptrace can call init_fpu.
	 */
L
Linus Torvalds 已提交
163
	set_stopped_child_used_math(tsk);
164
	return 0;
L
Linus Torvalds 已提交
165 166
}

R
Roland McGrath 已提交
167 168 169 170
int fpregs_active(struct task_struct *target, const struct user_regset *regset)
{
	return tsk_used_math(target) ? regset->n : 0;
}
L
Linus Torvalds 已提交
171

R
Roland McGrath 已提交
172
int xfpregs_active(struct task_struct *target, const struct user_regset *regset)
L
Linus Torvalds 已提交
173
{
R
Roland McGrath 已提交
174 175
	return (cpu_has_fxsr && tsk_used_math(target)) ? regset->n : 0;
}
L
Linus Torvalds 已提交
176

R
Roland McGrath 已提交
177 178 179 180
int xfpregs_get(struct task_struct *target, const struct user_regset *regset,
		unsigned int pos, unsigned int count,
		void *kbuf, void __user *ubuf)
{
181 182
	int ret;

R
Roland McGrath 已提交
183 184 185
	if (!cpu_has_fxsr)
		return -ENODEV;

186 187 188
	ret = init_fpu(target);
	if (ret)
		return ret;
R
Roland McGrath 已提交
189 190

	return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
191
				   &target->thread.xstate->fxsave, 0, -1);
L
Linus Torvalds 已提交
192
}
R
Roland McGrath 已提交
193 194 195 196 197 198 199 200 201 202

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;

203 204 205 206
	ret = init_fpu(target);
	if (ret)
		return ret;

R
Roland McGrath 已提交
207 208 209
	set_stopped_child_used_math(target);

	ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
210
				 &target->thread.xstate->fxsave, 0, -1);
R
Roland McGrath 已提交
211 212 213 214

	/*
	 * mxcsr reserved bits must be masked to zero for security reasons.
	 */
215
	target->thread.xstate->fxsave.mxcsr &= mxcsr_feature_mask;
R
Roland McGrath 已提交
216 217 218 219 220

	return ret;
}

#if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
L
Linus Torvalds 已提交
221 222 223 224 225

/*
 * FPU tag word conversions.
 */

226
static inline unsigned short twd_i387_to_fxsr(unsigned short twd)
L
Linus Torvalds 已提交
227 228
{
	unsigned int tmp; /* to avoid 16 bit prefixes in the code */
229

L
Linus Torvalds 已提交
230
	/* Transform each pair of bits into 01 (valid) or 00 (empty) */
231
	tmp = ~twd;
R
Roland McGrath 已提交
232
	tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */
233 234 235 236
	/* 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 已提交
237

238
	return tmp;
L
Linus Torvalds 已提交
239 240 241
}

#define FPREG_ADDR(f, n)	((void *)&(f)->st_space + (n) * 16);
R
Roland McGrath 已提交
242 243 244 245 246 247 248 249 250 251 252 253 254
#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 已提交
255

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

260
			switch (st->exponent & 0x7fff) {
L
Linus Torvalds 已提交
261
			case 0x7fff:
R
Roland McGrath 已提交
262
				tag = FP_EXP_TAG_SPECIAL;
L
Linus Torvalds 已提交
263 264
				break;
			case 0x0000:
265 266 267
				if (!st->significand[0] &&
				    !st->significand[1] &&
				    !st->significand[2] &&
R
Roland McGrath 已提交
268 269 270 271
				    !st->significand[3])
					tag = FP_EXP_TAG_ZERO;
				else
					tag = FP_EXP_TAG_SPECIAL;
L
Linus Torvalds 已提交
272 273
				break;
			default:
R
Roland McGrath 已提交
274 275 276 277
				if (st->significand[3] & 0x8000)
					tag = FP_EXP_TAG_VALID;
				else
					tag = FP_EXP_TAG_SPECIAL;
L
Linus Torvalds 已提交
278 279 280
				break;
			}
		} else {
R
Roland McGrath 已提交
281
			tag = FP_EXP_TAG_EMPTY;
L
Linus Torvalds 已提交
282
		}
R
Roland McGrath 已提交
283
		ret |= tag << (2 * i);
L
Linus Torvalds 已提交
284 285 286 287 288
	}
	return ret;
}

/*
R
Roland McGrath 已提交
289
 * FXSR floating point environment conversions.
L
Linus Torvalds 已提交
290 291
 */

I
Ingo Molnar 已提交
292 293
static void
convert_from_fxsr(struct user_i387_ia32_struct *env, struct task_struct *tsk)
L
Linus Torvalds 已提交
294
{
295
	struct i387_fxsave_struct *fxsave = &tsk->thread.xstate->fxsave;
R
Roland McGrath 已提交
296 297 298
	struct _fpreg *to = (struct _fpreg *) &env->st_space[0];
	struct _fpxreg *from = (struct _fpxreg *) &fxsave->st_space[0];
	int i;
L
Linus Torvalds 已提交
299

R
Roland McGrath 已提交
300 301 302 303 304 305 306 307 308 309 310 311
	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;
	if (tsk == current) {
		/*
		 * should be actually ds/cs at fpu exception time, but
		 * that information is not available in 64bit mode.
		 */
I
Ingo Molnar 已提交
312 313
		asm("mov %%ds, %[fos]" : [fos] "=r" (env->fos));
		asm("mov %%cs, %[fcs]" : [fcs] "=r" (env->fcs));
L
Linus Torvalds 已提交
314
	} else {
R
Roland McGrath 已提交
315
		struct pt_regs *regs = task_pt_regs(tsk);
I
Ingo Molnar 已提交
316

R
Roland McGrath 已提交
317 318
		env->fos = 0xffff0000 | tsk->thread.ds;
		env->fcs = regs->cs;
L
Linus Torvalds 已提交
319
	}
R
Roland McGrath 已提交
320 321
#else
	env->fip = fxsave->fip;
J
Jan Beulich 已提交
322
	env->fcs = (u16) fxsave->fcs | ((u32) fxsave->fop << 16);
R
Roland McGrath 已提交
323 324 325
	env->foo = fxsave->foo;
	env->fos = fxsave->fos;
#endif
L
Linus Torvalds 已提交
326

R
Roland McGrath 已提交
327 328
	for (i = 0; i < 8; ++i)
		memcpy(&to[i], &from[i], sizeof(to[0]));
L
Linus Torvalds 已提交
329 330
}

R
Roland McGrath 已提交
331 332
static void convert_to_fxsr(struct task_struct *tsk,
			    const struct user_i387_ia32_struct *env)
L
Linus Torvalds 已提交
333 334

{
335
	struct i387_fxsave_struct *fxsave = &tsk->thread.xstate->fxsave;
R
Roland McGrath 已提交
336 337 338
	struct _fpreg *from = (struct _fpreg *) &env->st_space[0];
	struct _fpxreg *to = (struct _fpxreg *) &fxsave->st_space[0];
	int i;
L
Linus Torvalds 已提交
339

R
Roland McGrath 已提交
340 341 342 343 344 345 346 347 348 349 350 351 352 353
	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 已提交
354

R
Roland McGrath 已提交
355 356
	for (i = 0; i < 8; ++i)
		memcpy(&to[i], &from[i], sizeof(from[0]));
L
Linus Torvalds 已提交
357 358
}

R
Roland McGrath 已提交
359 360 361
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 已提交
362
{
R
Roland McGrath 已提交
363
	struct user_i387_ia32_struct env;
364
	int ret;
L
Linus Torvalds 已提交
365

366 367 368
	ret = init_fpu(target);
	if (ret)
		return ret;
L
Linus Torvalds 已提交
369

370 371 372
	if (!HAVE_HWFP)
		return fpregs_soft_get(target, regset, pos, count, kbuf, ubuf);

I
Ingo Molnar 已提交
373
	if (!cpu_has_fxsr) {
R
Roland McGrath 已提交
374
		return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
375 376
					   &target->thread.xstate->fsave, 0,
					   -1);
I
Ingo Molnar 已提交
377
	}
L
Linus Torvalds 已提交
378

R
Roland McGrath 已提交
379 380 381
	if (kbuf && pos == 0 && count == sizeof(env)) {
		convert_from_fxsr(kbuf, target);
		return 0;
L
Linus Torvalds 已提交
382
	}
R
Roland McGrath 已提交
383 384

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

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

R
Roland McGrath 已提交
389 390 391
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 已提交
392
{
R
Roland McGrath 已提交
393 394
	struct user_i387_ia32_struct env;
	int ret;
L
Linus Torvalds 已提交
395

396 397 398 399
	ret = init_fpu(target);
	if (ret)
		return ret;

R
Roland McGrath 已提交
400 401
	set_stopped_child_used_math(target);

402 403 404
	if (!HAVE_HWFP)
		return fpregs_soft_set(target, regset, pos, count, kbuf, ubuf);

I
Ingo Molnar 已提交
405
	if (!cpu_has_fxsr) {
R
Roland McGrath 已提交
406
		return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
407
					  &target->thread.xstate->fsave, 0, -1);
I
Ingo Molnar 已提交
408
	}
R
Roland McGrath 已提交
409 410 411 412 413 414 415 416 417

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

	return ret;
L
Linus Torvalds 已提交
418 419 420 421 422 423
}

/*
 * Signal frame handlers.
 */

R
Roland McGrath 已提交
424
static inline int save_i387_fsave(struct _fpstate_ia32 __user *buf)
L
Linus Torvalds 已提交
425 426
{
	struct task_struct *tsk = current;
427
	struct i387_fsave_struct *fp = &tsk->thread.xstate->fsave;
L
Linus Torvalds 已提交
428

429 430
	fp->status = fp->swd;
	if (__copy_to_user(buf, fp, sizeof(struct i387_fsave_struct)))
L
Linus Torvalds 已提交
431 432 433 434
		return -1;
	return 1;
}

R
Roland McGrath 已提交
435
static int save_i387_fxsave(struct _fpstate_ia32 __user *buf)
L
Linus Torvalds 已提交
436 437
{
	struct task_struct *tsk = current;
438
	struct i387_fxsave_struct *fx = &tsk->thread.xstate->fxsave;
R
Roland McGrath 已提交
439
	struct user_i387_ia32_struct env;
L
Linus Torvalds 已提交
440 441
	int err = 0;

R
Roland McGrath 已提交
442 443
	convert_from_fxsr(&env, tsk);
	if (__copy_to_user(buf, &env, sizeof(env)))
L
Linus Torvalds 已提交
444 445
		return -1;

446
	err |= __put_user(fx->swd, &buf->status);
447 448
	err |= __put_user(X86_FXSR_MAGIC, &buf->magic);
	if (err)
L
Linus Torvalds 已提交
449 450
		return -1;

451
	if (__copy_to_user(&buf->_fxsr_env[0], fx, xstate_size))
L
Linus Torvalds 已提交
452 453 454 455
		return -1;
	return 1;
}

456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474
static int save_i387_xsave(void __user *buf)
{
	struct _fpstate_ia32 __user *fx = buf;
	int err = 0;

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

475
int save_i387_xstate_ia32(void __user *buf)
L
Linus Torvalds 已提交
476
{
477 478 479
	struct _fpstate_ia32 __user *fp = (struct _fpstate_ia32 __user *) buf;
	struct task_struct *tsk = current;

480
	if (!used_math())
L
Linus Torvalds 已提交
481
		return 0;
482 483 484

	if (!access_ok(VERIFY_WRITE, buf, sig_xstate_ia32_size))
		return -EACCES;
I
Ingo Molnar 已提交
485 486
	/*
	 * This will cause a "finit" to be triggered by the next
L
Linus Torvalds 已提交
487 488 489 490
	 * attempted FPU operation by the 'current' process.
	 */
	clear_used_math();

I
Ingo Molnar 已提交
491
	if (!HAVE_HWFP) {
R
Roland McGrath 已提交
492 493
		return fpregs_soft_get(current, NULL,
				       0, sizeof(struct user_i387_ia32_struct),
494
				       NULL, fp) ? -1 : 1;
L
Linus Torvalds 已提交
495
	}
I
Ingo Molnar 已提交
496

497 498
	unlazy_fpu(tsk);

499 500
	if (cpu_has_xsave)
		return save_i387_xsave(fp);
I
Ingo Molnar 已提交
501
	if (cpu_has_fxsr)
502
		return save_i387_fxsave(fp);
I
Ingo Molnar 已提交
503
	else
504
		return save_i387_fsave(fp);
L
Linus Torvalds 已提交
505 506
}

R
Roland McGrath 已提交
507
static inline int restore_i387_fsave(struct _fpstate_ia32 __user *buf)
L
Linus Torvalds 已提交
508 509
{
	struct task_struct *tsk = current;
I
Ingo Molnar 已提交
510

511
	return __copy_from_user(&tsk->thread.xstate->fsave, buf,
512
				sizeof(struct i387_fsave_struct));
L
Linus Torvalds 已提交
513 514
}

515 516
static int restore_i387_fxsave(struct _fpstate_ia32 __user *buf,
			       unsigned int size)
L
Linus Torvalds 已提交
517 518
{
	struct task_struct *tsk = current;
R
Roland McGrath 已提交
519
	struct user_i387_ia32_struct env;
I
Ingo Molnar 已提交
520 521
	int err;

522
	err = __copy_from_user(&tsk->thread.xstate->fxsave, &buf->_fxsr_env[0],
523
			       size);
L
Linus Torvalds 已提交
524
	/* mxcsr reserved bits must be masked to zero for security reasons */
525
	tsk->thread.xstate->fxsave.mxcsr &= mxcsr_feature_mask;
R
Roland McGrath 已提交
526 527 528
	if (err || __copy_from_user(&env, buf, sizeof(env)))
		return 1;
	convert_to_fxsr(tsk, &env);
I
Ingo Molnar 已提交
529

R
Roland McGrath 已提交
530
	return 0;
L
Linus Torvalds 已提交
531 532
}

533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
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 =
				&current->thread.xstate->xsave.xsave_hdr;
	unsigned int lmask, hmask;
	int err;

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

	lmask = fx_sw_user.xstate_bv;
	hmask = fx_sw_user.xstate_bv >> 32;

	err = restore_i387_fxsave(buf, fx_sw_user.xstate_size);

	xsave_hdr->xstate_bv &=  (pcntxt_lmask | (((u64) pcntxt_hmask) << 32));
	/*
	 * 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.
	 */
	lmask = ~(pcntxt_lmask & ~lmask);
	hmask = ~(pcntxt_hmask & ~hmask);
	xsave_hdr->xstate_bv &=  (lmask | (((u64) hmask) << 32));

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

578
int restore_i387_xstate_ia32(void __user *buf)
L
Linus Torvalds 已提交
579 580
{
	int err;
581
	struct task_struct *tsk = current;
582
	struct _fpstate_ia32 __user *fp = (struct _fpstate_ia32 __user *) buf;
L
Linus Torvalds 已提交
583

584
	if (HAVE_HWFP)
585 586
		clear_fpu(tsk);

587 588 589 590 591 592 593 594 595 596 597
	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;

598 599 600 601 602
	if (!used_math()) {
		err = init_fpu(tsk);
		if (err)
			return err;
	}
603

604
	if (HAVE_HWFP) {
605 606 607 608 609
		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 已提交
610
		else
611
			err = restore_i387_fsave(fp);
L
Linus Torvalds 已提交
612
	} else {
R
Roland McGrath 已提交
613 614
		err = fpregs_soft_set(current, NULL,
				      0, sizeof(struct user_i387_ia32_struct),
615
				      NULL, fp) != 0;
L
Linus Torvalds 已提交
616 617
	}
	set_used_math();
I
Ingo Molnar 已提交
618

L
Linus Torvalds 已提交
619 620 621 622 623
	return err;
}

/*
 * FPU state for core dumps.
R
Roland McGrath 已提交
624 625 626 627
 * 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 已提交
628
 */
629
int dump_fpu(struct pt_regs *regs, struct user_i387_struct *fpu)
L
Linus Torvalds 已提交
630 631
{
	struct task_struct *tsk = current;
I
Ingo Molnar 已提交
632
	int fpvalid;
L
Linus Torvalds 已提交
633 634

	fpvalid = !!used_math();
R
Roland McGrath 已提交
635 636 637 638
	if (fpvalid)
		fpvalid = !fpregs_get(tsk, NULL,
				      0, sizeof(struct user_i387_ia32_struct),
				      fpu, NULL);
L
Linus Torvalds 已提交
639 640 641

	return fpvalid;
}
642
EXPORT_SYMBOL(dump_fpu);
L
Linus Torvalds 已提交
643

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