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( ¤t->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( ¤t->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