1 /*
2  *  linux/arch/i386/kernel/i387.c
3  *
4  *  Copyright (C) 1994 Linus Torvalds
5  *
6  *  Pentium III FXSR, SSE support
7  *  General FPU state handling cleanups
8  *	Gareth Hughes <gareth@valinux.com>, May 2000
9  */
10 
11 #include <linux/config.h>
12 #include <linux/sched.h>
13 #include <linux/init.h>
14 #include <linux/kernel_stat.h>
15 #include <asm/processor.h>
16 #include <asm/i387.h>
17 #include <asm/math_emu.h>
18 #include <asm/sigcontext.h>
19 #include <asm/user.h>
20 #include <asm/ptrace.h>
21 #include <asm/uaccess.h>
22 
23 #ifdef CONFIG_MATH_EMULATION
24 #define HAVE_HWFP (boot_cpu_data.hard_math)
25 #else
26 #define HAVE_HWFP 1
27 #endif
28 
29 static union i387_union empty_fpu_state;
30 
boot_init_fpu(void)31 void __init boot_init_fpu(void)
32 {
33 	memset(&empty_fpu_state, 0, sizeof(union i387_union));
34 
35 	if (!cpu_has_fxsr) {
36 		empty_fpu_state.fsave.cwd = 0xffff037f;
37 		empty_fpu_state.fsave.swd = 0xffff0000;
38 		empty_fpu_state.fsave.twd = 0xffffffff;
39 		empty_fpu_state.fsave.fos = 0xffff0000;
40 	} else {
41 		empty_fpu_state.fxsave.cwd = 0x37f;
42 		if (cpu_has_xmm)
43 			empty_fpu_state.fxsave.mxcsr = 0x1f80;
44 	}
45 }
46 
load_empty_fpu(struct task_struct * tsk)47 void load_empty_fpu(struct task_struct * tsk)
48 {
49 	memcpy(&tsk->thread.i387, &empty_fpu_state, sizeof(union i387_union));
50 }
51 
52 /*
53  * The _current_ task is using the FPU for the first time
54  * so initialize it and set the mxcsr to its default
55  * value at reset if we support XMM instructions and then
56  * remeber the current task has used the FPU.
57  */
init_fpu(void)58 void init_fpu(void)
59 {
60 	if (cpu_has_fxsr)
61 		asm volatile("fxrstor %0" : : "m" (empty_fpu_state.fxsave));
62 	else
63 		__asm__("fninit");
64 	current->used_math = 1;
65 }
66 
67 /*
68  * FPU lazy state save handling.
69  */
70 
__save_init_fpu(struct task_struct * tsk)71 static inline void __save_init_fpu( struct task_struct *tsk )
72 {
73 	if ( cpu_has_fxsr ) {
74 		asm volatile( "fxsave %0"
75 			      : "=m" (tsk->thread.i387.fxsave) );
76 		if (tsk->thread.i387.fxsave.swd & (1<<7))
77 			asm volatile("fnclex");
78 		/* AMD CPUs leak F?P. Clear it here */
79 		asm volatile("ffree %%st(7) ; fildl %0" :: "m" (kstat.context_swtch));
80 	} else {
81 		asm volatile( "fnsave %0 ; fwait"
82 			      : "=m" (tsk->thread.i387.fsave) );
83 	}
84 	tsk->flags &= ~PF_USEDFPU;
85 }
86 
save_init_fpu(struct task_struct * tsk)87 void save_init_fpu( struct task_struct *tsk )
88 {
89 	__save_init_fpu(tsk);
90 	stts();
91 }
92 
kernel_fpu_begin(void)93 void kernel_fpu_begin(void)
94 {
95 	struct task_struct *tsk = current;
96 
97 	if (tsk->flags & PF_USEDFPU) {
98 		__save_init_fpu(tsk);
99 		return;
100 	}
101 	clts();
102 }
103 
restore_fpu(struct task_struct * tsk)104 void restore_fpu( struct task_struct *tsk )
105 {
106 	if ( cpu_has_fxsr ) {
107 		asm volatile( "fxrstor %0"
108 			      : : "m" (tsk->thread.i387.fxsave) );
109 	} else {
110 		asm volatile( "frstor %0"
111 			      : : "m" (tsk->thread.i387.fsave) );
112 	}
113 }
114 
115 /*
116  * FPU tag word conversions.
117  */
118 
twd_i387_to_fxsr(unsigned short twd)119 static inline unsigned short twd_i387_to_fxsr( unsigned short twd )
120 {
121 	unsigned int tmp; /* to avoid 16 bit prefixes in the code */
122 
123 	/* Transform each pair of bits into 01 (valid) or 00 (empty) */
124         tmp = ~twd;
125         tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */
126         /* and move the valid bits to the lower byte. */
127         tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */
128         tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */
129         tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */
130         return tmp;
131 }
132 
twd_fxsr_to_i387(struct i387_fxsave_struct * fxsave)133 static inline unsigned long twd_fxsr_to_i387( struct i387_fxsave_struct *fxsave )
134 {
135 	struct _fpxreg *st = NULL;
136 	unsigned long tos = (fxsave->swd >> 11) & 7;
137 	unsigned long twd = (unsigned long) fxsave->twd;
138 	unsigned long tag;
139 	unsigned long ret = 0xffff0000;
140 	int i;
141 
142 #define FPREG_ADDR(f, n)	((void *)&(f)->st_space + (n) * 16);
143 
144 	for ( i = 0 ; i < 8 ; i++ ) {
145 		if ( twd & 0x1 ) {
146 			st = FPREG_ADDR( fxsave, (i - tos) & 7 );
147 
148 			switch ( st->exponent & 0x7fff ) {
149 			case 0x7fff:
150 				tag = 2;		/* Special */
151 				break;
152 			case 0x0000:
153 				if ( !st->significand[0] &&
154 				     !st->significand[1] &&
155 				     !st->significand[2] &&
156 				     !st->significand[3] ) {
157 					tag = 1;	/* Zero */
158 				} else {
159 					tag = 2;	/* Special */
160 				}
161 				break;
162 			default:
163 				if ( st->significand[3] & 0x8000 ) {
164 					tag = 0;	/* Valid */
165 				} else {
166 					tag = 2;	/* Special */
167 				}
168 				break;
169 			}
170 		} else {
171 			tag = 3;			/* Empty */
172 		}
173 		ret |= (tag << (2 * i));
174 		twd = twd >> 1;
175 	}
176 	return ret;
177 }
178 
179 /*
180  * FPU state interaction.
181  */
182 
get_fpu_cwd(struct task_struct * tsk)183 unsigned short get_fpu_cwd( struct task_struct *tsk )
184 {
185 	if ( cpu_has_fxsr ) {
186 		return tsk->thread.i387.fxsave.cwd;
187 	} else {
188 		return (unsigned short)tsk->thread.i387.fsave.cwd;
189 	}
190 }
191 
get_fpu_swd(struct task_struct * tsk)192 unsigned short get_fpu_swd( struct task_struct *tsk )
193 {
194 	if ( cpu_has_fxsr ) {
195 		return tsk->thread.i387.fxsave.swd;
196 	} else {
197 		return (unsigned short)tsk->thread.i387.fsave.swd;
198 	}
199 }
200 
get_fpu_twd(struct task_struct * tsk)201 unsigned short get_fpu_twd( struct task_struct *tsk )
202 {
203 	if ( cpu_has_fxsr ) {
204 		return tsk->thread.i387.fxsave.twd;
205 	} else {
206 		return (unsigned short)tsk->thread.i387.fsave.twd;
207 	}
208 }
209 
get_fpu_mxcsr(struct task_struct * tsk)210 unsigned short get_fpu_mxcsr( struct task_struct *tsk )
211 {
212 	if ( cpu_has_xmm ) {
213 		return tsk->thread.i387.fxsave.mxcsr;
214 	} else {
215 		return 0x1f80;
216 	}
217 }
218 
set_fpu_cwd(struct task_struct * tsk,unsigned short cwd)219 void set_fpu_cwd( struct task_struct *tsk, unsigned short cwd )
220 {
221 	if ( cpu_has_fxsr ) {
222 		tsk->thread.i387.fxsave.cwd = cwd;
223 	} else {
224 		tsk->thread.i387.fsave.cwd = ((long)cwd | 0xffff0000);
225 	}
226 }
227 
set_fpu_swd(struct task_struct * tsk,unsigned short swd)228 void set_fpu_swd( struct task_struct *tsk, unsigned short swd )
229 {
230 	if ( cpu_has_fxsr ) {
231 		tsk->thread.i387.fxsave.swd = swd;
232 	} else {
233 		tsk->thread.i387.fsave.swd = ((long)swd | 0xffff0000);
234 	}
235 }
236 
set_fpu_twd(struct task_struct * tsk,unsigned short twd)237 void set_fpu_twd( struct task_struct *tsk, unsigned short twd )
238 {
239 	if ( cpu_has_fxsr ) {
240 		tsk->thread.i387.fxsave.twd = twd_i387_to_fxsr(twd);
241 	} else {
242 		tsk->thread.i387.fsave.twd = ((long)twd | 0xffff0000);
243 	}
244 }
245 
set_fpu_mxcsr(struct task_struct * tsk,unsigned short mxcsr)246 void set_fpu_mxcsr( struct task_struct *tsk, unsigned short mxcsr )
247 {
248 	if ( cpu_has_xmm ) {
249 		tsk->thread.i387.fxsave.mxcsr = (mxcsr & 0xffbf);
250 	}
251 }
252 
253 /*
254  * FXSR floating point environment conversions.
255  */
256 
convert_fxsr_to_user(struct _fpstate * buf,struct i387_fxsave_struct * fxsave)257 static inline int convert_fxsr_to_user( struct _fpstate *buf,
258 					struct i387_fxsave_struct *fxsave )
259 {
260 	unsigned long env[7];
261 	struct _fpreg *to;
262 	struct _fpxreg *from;
263 	int i;
264 
265 	env[0] = (unsigned long)fxsave->cwd | 0xffff0000;
266 	env[1] = (unsigned long)fxsave->swd | 0xffff0000;
267 	env[2] = twd_fxsr_to_i387(fxsave);
268 	env[3] = fxsave->fip;
269 	env[4] = fxsave->fcs | ((unsigned long)fxsave->fop << 16);
270 	env[5] = fxsave->foo;
271 	env[6] = fxsave->fos;
272 
273 	if ( __copy_to_user( buf, env, 7 * sizeof(unsigned long) ) )
274 		return 1;
275 
276 	to = &buf->_st[0];
277 	from = (struct _fpxreg *) &fxsave->st_space[0];
278 	for ( i = 0 ; i < 8 ; i++, to++, from++ ) {
279 		if ( __copy_to_user( to, from, sizeof(*to) ) )
280 			return 1;
281 	}
282 	return 0;
283 }
284 
convert_fxsr_from_user(struct i387_fxsave_struct * fxsave,struct _fpstate * buf)285 static inline int convert_fxsr_from_user( struct i387_fxsave_struct *fxsave,
286 					  struct _fpstate *buf )
287 {
288 	unsigned long env[7];
289 	struct _fpxreg *to;
290 	struct _fpreg *from;
291 	int i;
292 
293 	if ( __copy_from_user( env, buf, 7 * sizeof(long) ) )
294 		return 1;
295 
296 	fxsave->cwd = (unsigned short)(env[0] & 0xffff);
297 	fxsave->swd = (unsigned short)(env[1] & 0xffff);
298 	fxsave->twd = twd_i387_to_fxsr((unsigned short)(env[2] & 0xffff));
299 	fxsave->fip = env[3];
300 	fxsave->fop = (unsigned short)((env[4] & 0xffff0000) >> 16);
301 	fxsave->fcs = (env[4] & 0xffff);
302 	fxsave->foo = env[5];
303 	fxsave->fos = env[6];
304 
305 	to = (struct _fpxreg *) &fxsave->st_space[0];
306 	from = &buf->_st[0];
307 	for ( i = 0 ; i < 8 ; i++, to++, from++ ) {
308 		if ( __copy_from_user( to, from, sizeof(*from) ) )
309 			return 1;
310 	}
311 	return 0;
312 }
313 
314 /*
315  * Signal frame handlers.
316  */
317 
save_i387_fsave(struct _fpstate * buf)318 static inline int save_i387_fsave( struct _fpstate *buf )
319 {
320 	struct task_struct *tsk = current;
321 
322 	unlazy_fpu( tsk );
323 	tsk->thread.i387.fsave.status = tsk->thread.i387.fsave.swd;
324 	if ( __copy_to_user( buf, &tsk->thread.i387.fsave,
325 			     sizeof(struct i387_fsave_struct) ) )
326 		return -1;
327 	return 1;
328 }
329 
save_i387_fxsave(struct _fpstate * buf)330 static inline int save_i387_fxsave( struct _fpstate *buf )
331 {
332 	struct task_struct *tsk = current;
333 	int err = 0;
334 
335 	unlazy_fpu( tsk );
336 
337 	if ( convert_fxsr_to_user( buf, &tsk->thread.i387.fxsave ) )
338 		return -1;
339 
340 	err |= __put_user( tsk->thread.i387.fxsave.swd, &buf->status );
341 	err |= __put_user( X86_FXSR_MAGIC, &buf->magic );
342 	if ( err )
343 		return -1;
344 
345 	if ( __copy_to_user( &buf->_fxsr_env[0], &tsk->thread.i387.fxsave,
346 			     sizeof(struct i387_fxsave_struct) ) )
347 		return -1;
348 	return 1;
349 }
350 
save_i387(struct _fpstate * buf)351 int save_i387( struct _fpstate *buf )
352 {
353 	if ( !current->used_math )
354 		return 0;
355 
356 	/* This will cause a "finit" to be triggered by the next
357 	 * attempted FPU operation by the 'current' process.
358 	 */
359 	current->used_math = 0;
360 
361 	if ( HAVE_HWFP ) {
362 		if ( cpu_has_fxsr ) {
363 			return save_i387_fxsave( buf );
364 		} else {
365 			return save_i387_fsave( buf );
366 		}
367 	} else {
368 		return save_i387_soft( &current->thread.i387.soft, buf );
369 	}
370 }
371 
restore_i387_fsave(struct _fpstate * buf)372 static inline int restore_i387_fsave( struct _fpstate *buf )
373 {
374 	struct task_struct *tsk = current;
375 	clear_fpu( tsk );
376 	return __copy_from_user( &tsk->thread.i387.fsave, buf,
377 				 sizeof(struct i387_fsave_struct) );
378 }
379 
restore_i387_fxsave(struct _fpstate * buf)380 static inline int restore_i387_fxsave( struct _fpstate *buf )
381 {
382 	int err;
383 	struct task_struct *tsk = current;
384 	clear_fpu( tsk );
385 	err = __copy_from_user( &tsk->thread.i387.fxsave, &buf->_fxsr_env[0],
386 				sizeof(struct i387_fxsave_struct) );
387 	/* mxcsr bit 6 and 31-16 must be zero for security reasons */
388 	tsk->thread.i387.fxsave.mxcsr &= 0xffbf;
389 	return err ? 1 : convert_fxsr_from_user( &tsk->thread.i387.fxsave, buf );
390 }
391 
restore_i387(struct _fpstate * buf)392 int restore_i387( struct _fpstate *buf )
393 {
394 	int err;
395 
396 	if ( HAVE_HWFP ) {
397 		if ( cpu_has_fxsr ) {
398 			err =  restore_i387_fxsave( buf );
399 		} else {
400 			err = restore_i387_fsave( buf );
401 		}
402 	} else {
403 		err = restore_i387_soft( &current->thread.i387.soft, buf );
404 	}
405 	current->used_math = 1;
406 	return err;
407 }
408 
409 /*
410  * ptrace request handlers.
411  */
412 
get_fpregs_fsave(struct user_i387_struct * buf,struct task_struct * tsk)413 static inline int get_fpregs_fsave( struct user_i387_struct *buf,
414 				    struct task_struct *tsk )
415 {
416 	return __copy_to_user( buf, &tsk->thread.i387.fsave,
417 			       sizeof(struct user_i387_struct) );
418 }
419 
get_fpregs_fxsave(struct user_i387_struct * buf,struct task_struct * tsk)420 static inline int get_fpregs_fxsave( struct user_i387_struct *buf,
421 				     struct task_struct *tsk )
422 {
423 	return convert_fxsr_to_user( (struct _fpstate *)buf,
424 				     &tsk->thread.i387.fxsave );
425 }
426 
get_fpregs(struct user_i387_struct * buf,struct task_struct * tsk)427 int get_fpregs( struct user_i387_struct *buf, struct task_struct *tsk )
428 {
429 	if ( HAVE_HWFP ) {
430 		if ( cpu_has_fxsr ) {
431 			return get_fpregs_fxsave( buf, tsk );
432 		} else {
433 			return get_fpregs_fsave( buf, tsk );
434 		}
435 	} else {
436 		return save_i387_soft( &tsk->thread.i387.soft,
437 				       (struct _fpstate *)buf );
438 	}
439 }
440 
set_fpregs_fsave(struct task_struct * tsk,struct user_i387_struct * buf)441 static inline int set_fpregs_fsave( struct task_struct *tsk,
442 				    struct user_i387_struct *buf )
443 {
444 	return __copy_from_user( &tsk->thread.i387.fsave, buf,
445 				 sizeof(struct user_i387_struct) );
446 }
447 
set_fpregs_fxsave(struct task_struct * tsk,struct user_i387_struct * buf)448 static inline int set_fpregs_fxsave( struct task_struct *tsk,
449 				     struct user_i387_struct *buf )
450 {
451 	return convert_fxsr_from_user( &tsk->thread.i387.fxsave,
452 				       (struct _fpstate *)buf );
453 }
454 
set_fpregs(struct task_struct * tsk,struct user_i387_struct * buf)455 int set_fpregs( struct task_struct *tsk, struct user_i387_struct *buf )
456 {
457 	if ( HAVE_HWFP ) {
458 		if ( cpu_has_fxsr ) {
459 			return set_fpregs_fxsave( tsk, buf );
460 		} else {
461 			return set_fpregs_fsave( tsk, buf );
462 		}
463 	} else {
464 		return restore_i387_soft( &tsk->thread.i387.soft,
465 					  (struct _fpstate *)buf );
466 	}
467 }
468 
get_fpxregs(struct user_fxsr_struct * buf,struct task_struct * tsk)469 int get_fpxregs( struct user_fxsr_struct *buf, struct task_struct *tsk )
470 {
471 	if ( cpu_has_fxsr ) {
472 		if (__copy_to_user( (void *)buf, &tsk->thread.i387.fxsave,
473 				    sizeof(struct user_fxsr_struct) ))
474 			return -EFAULT;
475 		return 0;
476 	} else {
477 		return -EIO;
478 	}
479 }
480 
set_fpxregs(struct task_struct * tsk,struct user_fxsr_struct * buf)481 int set_fpxregs( struct task_struct *tsk, struct user_fxsr_struct *buf )
482 {
483 	if ( cpu_has_fxsr ) {
484 		__copy_from_user( &tsk->thread.i387.fxsave, (void *)buf,
485 				  sizeof(struct user_fxsr_struct) );
486 		/* mxcsr bit 6 and 31-16 must be zero for security reasons */
487 		tsk->thread.i387.fxsave.mxcsr &= 0xffbf;
488 		return 0;
489 	} else {
490 		return -EIO;
491 	}
492 }
493 
494 /*
495  * FPU state for core dumps.
496  */
497 
copy_fpu_fsave(struct task_struct * tsk,struct user_i387_struct * fpu)498 static inline void copy_fpu_fsave( struct task_struct *tsk,
499 				   struct user_i387_struct *fpu )
500 {
501 	memcpy( fpu, &tsk->thread.i387.fsave,
502 		sizeof(struct user_i387_struct) );
503 }
504 
copy_fpu_fxsave(struct task_struct * tsk,struct user_i387_struct * fpu)505 static inline void copy_fpu_fxsave( struct task_struct *tsk,
506 				   struct user_i387_struct *fpu )
507 {
508 	unsigned short *to;
509 	unsigned short *from;
510 	int i;
511 
512 	memcpy( fpu, &tsk->thread.i387.fxsave, 7 * sizeof(long) );
513 
514 	to = (unsigned short *)&fpu->st_space[0];
515 	from = (unsigned short *)&tsk->thread.i387.fxsave.st_space[0];
516 	for ( i = 0 ; i < 8 ; i++, to += 5, from += 8 ) {
517 		memcpy( to, from, 5 * sizeof(unsigned short) );
518 	}
519 }
520 
dump_fpu(struct pt_regs * regs,struct user_i387_struct * fpu)521 int dump_fpu( struct pt_regs *regs, struct user_i387_struct *fpu )
522 {
523 	int fpvalid;
524 	struct task_struct *tsk = current;
525 
526 	fpvalid = tsk->used_math;
527 	if ( fpvalid ) {
528 		unlazy_fpu( tsk );
529 		if ( cpu_has_fxsr ) {
530 			copy_fpu_fxsave( tsk, fpu );
531 		} else {
532 			copy_fpu_fsave( tsk, fpu );
533 		}
534 	}
535 
536 	return fpvalid;
537 }
538 
dump_extended_fpu(struct pt_regs * regs,struct user_fxsr_struct * fpu)539 int dump_extended_fpu( struct pt_regs *regs, struct user_fxsr_struct *fpu )
540 {
541 	int fpvalid;
542 	struct task_struct *tsk = current;
543 
544 	fpvalid = tsk->used_math && cpu_has_fxsr;
545 	if ( fpvalid ) {
546 		unlazy_fpu( tsk );
547 		memcpy( fpu, &tsk->thread.i387.fxsave,
548 			sizeof(struct user_fxsr_struct) );
549 	}
550 
551 	return fpvalid;
552 }
553