2 * linux/arch/i386/kernel/i387.c
4 * Copyright (C) 1994 Linus Torvalds
6 * Pentium III FXSR, SSE support
7 * General FPU state handling cleanups
8 * Gareth Hughes <gareth@valinux.com>, May 2000
11 #include <linux/config.h>
12 #include <linux/sched.h>
13 #include <asm/processor.h>
15 #include <asm/math_emu.h>
16 #include <asm/sigcontext.h>
18 #include <asm/ptrace.h>
19 #include <asm/uaccess.h>
21 #if defined(CONFIG_X86_FXSR)
23 #elif defined(CONFIG_X86_RUNTIME_FXSR)
24 #define HAVE_FXSR (cpu_has_fxsr)
29 #ifdef CONFIG_MATH_EMULATION
30 #define HAVE_HWFP (boot_cpu_data.hard_math)
36 * FPU lazy state save handling.
39 void save_fpu( struct task_struct
*tsk
)
42 asm volatile( "fxsave %0 ; fwait"
43 : "=m" (tsk
->thread
.i387
.fxsave
) );
45 asm volatile( "fnsave %0 ; fwait"
46 : "=m" (tsk
->thread
.i387
.fsave
) );
48 tsk
->flags
&= ~PF_USEDFPU
;
52 void save_init_fpu( struct task_struct
*tsk
)
55 asm volatile( "fxsave %0 ; fnclex"
56 : "=m" (tsk
->thread
.i387
.fxsave
) );
58 asm volatile( "fnsave %0 ; fwait"
59 : "=m" (tsk
->thread
.i387
.fsave
) );
61 tsk
->flags
&= ~PF_USEDFPU
;
65 void restore_fpu( struct task_struct
*tsk
)
68 asm volatile( "fxrstor %0"
69 : : "m" (tsk
->thread
.i387
.fxsave
) );
71 asm volatile( "frstor %0"
72 : : "m" (tsk
->thread
.i387
.fsave
) );
77 * FPU tag word conversions.
80 static inline unsigned short twd_i387_to_fxsr( unsigned short twd
)
82 unsigned short ret
= 0;
85 for ( i
= 0 ; i
< 8 ; i
++ ) {
86 if ( (twd
& 0x3) != 0x3 ) {
94 static inline unsigned long twd_fxsr_to_i387( struct i387_fxsave_struct
*fxsave
)
96 struct _fpxreg
*st
= NULL
;
97 unsigned long twd
= (unsigned long) fxsave
->twd
;
99 unsigned long ret
= 0xffff0000;
102 #define FPREG_ADDR(f, n) ((char *)&(f)->st_space + (n) * 16);
104 for ( i
= 0 ; i
< 8 ; i
++ ) {
106 st
= (struct _fpxreg
*) FPREG_ADDR( fxsave
, i
);
108 switch ( st
->exponent
) {
110 tag
= 2; /* Special */
113 if ( !st
->significand
[0] &&
114 !st
->significand
[1] &&
115 !st
->significand
[2] &&
116 !st
->significand
[3] ) {
119 tag
= 2; /* Special */
123 if ( st
->significand
[3] & 0x8000 ) {
126 tag
= 2; /* Special */
133 ret
|= (tag
<< (2 * i
));
140 * FPU state interaction.
143 unsigned short get_fpu_cwd( struct task_struct
*tsk
)
146 return tsk
->thread
.i387
.fxsave
.cwd
;
148 return (unsigned short)tsk
->thread
.i387
.fsave
.cwd
;
152 unsigned short get_fpu_swd( struct task_struct
*tsk
)
155 return tsk
->thread
.i387
.fxsave
.swd
;
157 return (unsigned short)tsk
->thread
.i387
.fsave
.swd
;
161 unsigned short get_fpu_twd( struct task_struct
*tsk
)
164 return tsk
->thread
.i387
.fxsave
.twd
;
166 return (unsigned short)tsk
->thread
.i387
.fsave
.twd
;
170 unsigned short get_fpu_mxcsr( struct task_struct
*tsk
)
173 return tsk
->thread
.i387
.fxsave
.mxcsr
;
179 void set_fpu_cwd( struct task_struct
*tsk
, unsigned short cwd
)
182 tsk
->thread
.i387
.fxsave
.cwd
= cwd
;
184 tsk
->thread
.i387
.fsave
.cwd
= ((long)cwd
| 0xffff0000);
188 void set_fpu_swd( struct task_struct
*tsk
, unsigned short swd
)
191 tsk
->thread
.i387
.fxsave
.swd
= swd
;
193 tsk
->thread
.i387
.fsave
.swd
= ((long)swd
| 0xffff0000);
197 void set_fpu_twd( struct task_struct
*tsk
, unsigned short twd
)
200 tsk
->thread
.i387
.fxsave
.twd
= twd_i387_to_fxsr(twd
);
202 tsk
->thread
.i387
.fsave
.twd
= ((long)twd
| 0xffff0000);
206 void set_fpu_mxcsr( struct task_struct
*tsk
, unsigned short mxcsr
)
209 tsk
->thread
.i387
.fxsave
.mxcsr
= mxcsr
;
214 * FXSR floating point environment conversions.
217 static inline int convert_fxsr_to_user( struct _fpstate
*buf
,
218 struct i387_fxsave_struct
*fxsave
)
220 unsigned long env
[7];
222 struct _fpxreg
*from
;
225 env
[0] = (unsigned long)fxsave
->cwd
| 0xffff0000;
226 env
[1] = (unsigned long)fxsave
->swd
| 0xffff0000;
227 env
[2] = twd_fxsr_to_i387(fxsave
);
228 env
[3] = fxsave
->fip
;
229 env
[4] = fxsave
->fcs
| ((unsigned long)fxsave
->fop
<< 16);
230 env
[5] = fxsave
->foo
;
231 env
[6] = fxsave
->fos
;
233 if ( __copy_to_user( buf
, env
, 7 * sizeof(unsigned long) ) )
237 from
= (struct _fpxreg
*) &fxsave
->st_space
[0];
238 for ( i
= 0 ; i
< 8 ; i
++, to
++, from
++ ) {
239 if ( __copy_to_user( to
, from
, sizeof(*to
) ) )
245 static inline int convert_fxsr_from_user( struct i387_fxsave_struct
*fxsave
,
246 struct _fpstate
*buf
)
248 unsigned long env
[7];
253 if ( __copy_from_user( env
, buf
, 7 * sizeof(long) ) )
256 fxsave
->cwd
= (unsigned short)(env
[0] & 0xffff);
257 fxsave
->swd
= (unsigned short)(env
[1] & 0xffff);
258 fxsave
->twd
= twd_i387_to_fxsr((unsigned short)(env
[2] & 0xffff));
259 fxsave
->fip
= env
[3];
260 fxsave
->fop
= (unsigned short)((env
[4] & 0xffff0000) >> 16);
261 fxsave
->fcs
= (env
[4] & 0xffff);
262 fxsave
->foo
= env
[5];
263 fxsave
->fos
= env
[6];
265 to
= (struct _fpxreg
*) &fxsave
->st_space
[0];
267 for ( i
= 0 ; i
< 8 ; i
++, to
++, from
++ ) {
268 if ( __copy_from_user( to
, from
, sizeof(*from
) ) )
275 * Signal frame handlers.
278 static inline int save_i387_fsave( struct _fpstate
*buf
)
280 struct task_struct
*tsk
= current
;
283 tsk
->thread
.i387
.fsave
.status
= tsk
->thread
.i387
.fsave
.swd
;
284 if ( __copy_to_user( buf
, &tsk
->thread
.i387
.fsave
,
285 sizeof(struct i387_fsave_struct
) ) )
290 static inline int save_i387_fxsave( struct _fpstate
*buf
)
292 struct task_struct
*tsk
= current
;
297 if ( convert_fxsr_to_user( buf
, &tsk
->thread
.i387
.fxsave
) )
300 err
|= __put_user( tsk
->thread
.i387
.fxsave
.swd
, &buf
->status
);
301 err
|= __put_user( X86_FXSR_MAGIC
, &buf
->magic
);
305 if ( __copy_to_user( &buf
->_fxsr_env
[0], &tsk
->thread
.i387
.fxsave
,
306 sizeof(struct i387_fxsave_struct
) ) )
311 int save_i387( struct _fpstate
*buf
)
313 if ( !current
->used_math
)
316 /* This will cause a "finit" to be triggered by the next
317 * attempted FPU operation by the 'current' process.
319 current
->used_math
= 0;
323 return save_i387_fxsave( buf
);
325 return save_i387_fsave( buf
);
328 return save_i387_soft( ¤t
->thread
.i387
.soft
, buf
);
332 static inline int restore_i387_fsave( struct _fpstate
*buf
)
334 struct task_struct
*tsk
= current
;
336 return __copy_from_user( &tsk
->thread
.i387
.fsave
, buf
,
337 sizeof(struct i387_fsave_struct
) );
340 static inline int restore_i387_fxsave( struct _fpstate
*buf
)
342 struct task_struct
*tsk
= current
;
344 if ( __copy_from_user( &tsk
->thread
.i387
.fxsave
, &buf
->_fxsr_env
[0],
345 sizeof(struct i387_fxsave_struct
) ) )
347 return convert_fxsr_from_user( &tsk
->thread
.i387
.fxsave
, buf
);
350 int restore_i387( struct _fpstate
*buf
)
356 err
= restore_i387_fxsave( buf
);
358 err
= restore_i387_fsave( buf
);
361 err
= restore_i387_soft( ¤t
->thread
.i387
.soft
, buf
);
363 current
->used_math
= 1;
368 * ptrace request handlers.
371 static inline int get_fpregs_fsave( struct user_i387_struct
*buf
,
372 struct task_struct
*tsk
)
374 return __copy_to_user( buf
, &tsk
->thread
.i387
.fsave
,
375 sizeof(struct user_i387_struct
) );
378 static inline int get_fpregs_fxsave( struct user_i387_struct
*buf
,
379 struct task_struct
*tsk
)
381 return convert_fxsr_to_user( (struct _fpstate
*)buf
,
382 &tsk
->thread
.i387
.fxsave
);
385 int get_fpregs( struct user_i387_struct
*buf
, struct task_struct
*tsk
)
389 return get_fpregs_fxsave( buf
, tsk
);
391 return get_fpregs_fsave( buf
, tsk
);
394 return save_i387_soft( &tsk
->thread
.i387
.soft
,
395 (struct _fpstate
*)buf
);
399 static inline int set_fpregs_fsave( struct task_struct
*tsk
,
400 struct user_i387_struct
*buf
)
402 return __copy_from_user( &tsk
->thread
.i387
.fsave
, buf
,
403 sizeof(struct user_i387_struct
) );
406 static inline int set_fpregs_fxsave( struct task_struct
*tsk
,
407 struct user_i387_struct
*buf
)
409 return convert_fxsr_from_user( &tsk
->thread
.i387
.fxsave
,
410 (struct _fpstate
*)buf
);
413 int set_fpregs( struct task_struct
*tsk
, struct user_i387_struct
*buf
)
417 return set_fpregs_fxsave( tsk
, buf
);
419 return set_fpregs_fsave( tsk
, buf
);
422 return restore_i387_soft( &tsk
->thread
.i387
.soft
,
423 (struct _fpstate
*)buf
);
427 int get_fpxregs( struct user_fxsr_struct
*buf
, struct task_struct
*tsk
)
430 __copy_to_user( (void *)buf
, &tsk
->thread
.i387
.fxsave
,
431 sizeof(struct user_fxsr_struct
) );
438 int set_fpxregs( struct task_struct
*tsk
, struct user_fxsr_struct
*buf
)
441 __copy_from_user( &tsk
->thread
.i387
.fxsave
, (void *)buf
,
442 sizeof(struct user_fxsr_struct
) );
450 * FPU state for core dumps.
453 static inline void copy_fpu_fsave( struct task_struct
*tsk
,
454 struct user_i387_struct
*fpu
)
456 memcpy( fpu
, &tsk
->thread
.i387
.fsave
,
457 sizeof(struct user_i387_struct
) );
460 static inline void copy_fpu_fxsave( struct task_struct
*tsk
,
461 struct user_i387_struct
*fpu
)
464 unsigned short *from
;
467 memcpy( fpu
, &tsk
->thread
.i387
.fxsave
, 7 * sizeof(long) );
469 to
= (unsigned short *)&fpu
->st_space
[0];
470 from
= (unsigned short *)&tsk
->thread
.i387
.fxsave
.st_space
[0];
471 for ( i
= 0 ; i
< 8 ; i
++, to
+= 5, from
+= 8 ) {
472 memcpy( to
, from
, 5 * sizeof(unsigned short) );
476 int dump_fpu( struct pt_regs
*regs
, struct user_i387_struct
*fpu
)
479 struct task_struct
*tsk
= current
;
481 fpvalid
= tsk
->used_math
;
485 copy_fpu_fxsave( tsk
, fpu
);
487 copy_fpu_fsave( tsk
, fpu
);
494 int dump_extended_fpu( struct pt_regs
*regs
, struct user_fxsr_struct
*fpu
)
497 struct task_struct
*tsk
= current
;
499 fpvalid
= tsk
->used_math
&& HAVE_FXSR
;
502 memcpy( fpu
, &tsk
->thread
.i387
.fxsave
,
503 sizeof(struct user_fxsr_struct
) );