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 * The _current_ task is using the FPU for the first time
37 * so initialize it and set the mxcsr to its default
38 * value at reset if we support FXSR and then
39 * remeber the current task has used the FPU.
47 current
->used_math
= 1;
51 * FPU lazy state save handling.
54 void save_init_fpu( struct task_struct
*tsk
)
57 asm volatile( "fxsave %0 ; fnclex"
58 : "=m" (tsk
->thread
.i387
.fxsave
) );
60 asm volatile( "fnsave %0 ; fwait"
61 : "=m" (tsk
->thread
.i387
.fsave
) );
63 tsk
->flags
&= ~PF_USEDFPU
;
67 void restore_fpu( struct task_struct
*tsk
)
70 asm volatile( "fxrstor %0"
71 : : "m" (tsk
->thread
.i387
.fxsave
) );
73 asm volatile( "frstor %0"
74 : : "m" (tsk
->thread
.i387
.fsave
) );
79 * FPU tag word conversions.
82 static inline unsigned short twd_i387_to_fxsr( unsigned short twd
)
84 unsigned int tmp
; /* to avoid 16 bit prefixes in the code */
86 /* Transform each pair of bits into 01 (valid) or 00 (empty) */
88 tmp
= (tmp
| (tmp
>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */
89 /* and move the valid bits to the lower byte. */
90 tmp
= (tmp
| (tmp
>> 1)) & 0x3333; /* 00VV00VV00VV00VV */
91 tmp
= (tmp
| (tmp
>> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */
92 tmp
= (tmp
| (tmp
>> 4)) & 0x00ff; /* 00000000VVVVVVVV */
96 static inline unsigned long twd_fxsr_to_i387( struct i387_fxsave_struct
*fxsave
)
98 struct _fpxreg
*st
= NULL
;
99 unsigned long twd
= (unsigned long) fxsave
->twd
;
101 unsigned long ret
= 0xffff0000;
104 #define FPREG_ADDR(f, n) ((char *)&(f)->st_space + (n) * 16);
106 for ( i
= 0 ; i
< 8 ; i
++ ) {
108 st
= (struct _fpxreg
*) FPREG_ADDR( fxsave
, i
);
110 switch ( st
->exponent
& 0x7fff ) {
112 tag
= 2; /* Special */
115 if ( !st
->significand
[0] &&
116 !st
->significand
[1] &&
117 !st
->significand
[2] &&
118 !st
->significand
[3] ) {
121 tag
= 2; /* Special */
125 if ( st
->significand
[3] & 0x8000 ) {
128 tag
= 2; /* Special */
135 ret
|= (tag
<< (2 * i
));
142 * FPU state interaction.
145 unsigned short get_fpu_cwd( struct task_struct
*tsk
)
148 return tsk
->thread
.i387
.fxsave
.cwd
;
150 return (unsigned short)tsk
->thread
.i387
.fsave
.cwd
;
154 unsigned short get_fpu_swd( struct task_struct
*tsk
)
157 return tsk
->thread
.i387
.fxsave
.swd
;
159 return (unsigned short)tsk
->thread
.i387
.fsave
.swd
;
163 unsigned short get_fpu_twd( struct task_struct
*tsk
)
166 return tsk
->thread
.i387
.fxsave
.twd
;
168 return (unsigned short)tsk
->thread
.i387
.fsave
.twd
;
172 unsigned short get_fpu_mxcsr( struct task_struct
*tsk
)
175 return tsk
->thread
.i387
.fxsave
.mxcsr
;
181 void set_fpu_cwd( struct task_struct
*tsk
, unsigned short cwd
)
184 tsk
->thread
.i387
.fxsave
.cwd
= cwd
;
186 tsk
->thread
.i387
.fsave
.cwd
= ((long)cwd
| 0xffff0000);
190 void set_fpu_swd( struct task_struct
*tsk
, unsigned short swd
)
193 tsk
->thread
.i387
.fxsave
.swd
= swd
;
195 tsk
->thread
.i387
.fsave
.swd
= ((long)swd
| 0xffff0000);
199 void set_fpu_twd( struct task_struct
*tsk
, unsigned short twd
)
202 tsk
->thread
.i387
.fxsave
.twd
= twd_i387_to_fxsr(twd
);
204 tsk
->thread
.i387
.fsave
.twd
= ((long)twd
| 0xffff0000);
208 void set_fpu_mxcsr( struct task_struct
*tsk
, unsigned short mxcsr
)
211 tsk
->thread
.i387
.fxsave
.mxcsr
= mxcsr
;
216 * FXSR floating point environment conversions.
219 static inline int convert_fxsr_to_user( struct _fpstate
*buf
,
220 struct i387_fxsave_struct
*fxsave
)
222 unsigned long env
[7];
224 struct _fpxreg
*from
;
227 env
[0] = (unsigned long)fxsave
->cwd
| 0xffff0000;
228 env
[1] = (unsigned long)fxsave
->swd
| 0xffff0000;
229 env
[2] = twd_fxsr_to_i387(fxsave
);
230 env
[3] = fxsave
->fip
;
231 env
[4] = fxsave
->fcs
| ((unsigned long)fxsave
->fop
<< 16);
232 env
[5] = fxsave
->foo
;
233 env
[6] = fxsave
->fos
;
235 if ( __copy_to_user( buf
, env
, 7 * sizeof(unsigned long) ) )
239 from
= (struct _fpxreg
*) &fxsave
->st_space
[0];
240 for ( i
= 0 ; i
< 8 ; i
++, to
++, from
++ ) {
241 if ( __copy_to_user( to
, from
, sizeof(*to
) ) )
247 static inline int convert_fxsr_from_user( struct i387_fxsave_struct
*fxsave
,
248 struct _fpstate
*buf
)
250 unsigned long env
[7];
255 if ( __copy_from_user( env
, buf
, 7 * sizeof(long) ) )
258 fxsave
->cwd
= (unsigned short)(env
[0] & 0xffff);
259 fxsave
->swd
= (unsigned short)(env
[1] & 0xffff);
260 fxsave
->twd
= twd_i387_to_fxsr((unsigned short)(env
[2] & 0xffff));
261 fxsave
->fip
= env
[3];
262 fxsave
->fop
= (unsigned short)((env
[4] & 0xffff0000) >> 16);
263 fxsave
->fcs
= (env
[4] & 0xffff);
264 fxsave
->foo
= env
[5];
265 fxsave
->fos
= env
[6];
267 to
= (struct _fpxreg
*) &fxsave
->st_space
[0];
269 for ( i
= 0 ; i
< 8 ; i
++, to
++, from
++ ) {
270 if ( __copy_from_user( to
, from
, sizeof(*from
) ) )
277 * Signal frame handlers.
280 static inline int save_i387_fsave( struct _fpstate
*buf
)
282 struct task_struct
*tsk
= current
;
285 tsk
->thread
.i387
.fsave
.status
= tsk
->thread
.i387
.fsave
.swd
;
286 if ( __copy_to_user( buf
, &tsk
->thread
.i387
.fsave
,
287 sizeof(struct i387_fsave_struct
) ) )
292 static inline int save_i387_fxsave( struct _fpstate
*buf
)
294 struct task_struct
*tsk
= current
;
299 if ( convert_fxsr_to_user( buf
, &tsk
->thread
.i387
.fxsave
) )
302 err
|= __put_user( tsk
->thread
.i387
.fxsave
.swd
, &buf
->status
);
303 err
|= __put_user( X86_FXSR_MAGIC
, &buf
->magic
);
307 if ( __copy_to_user( &buf
->_fxsr_env
[0], &tsk
->thread
.i387
.fxsave
,
308 sizeof(struct i387_fxsave_struct
) ) )
313 int save_i387( struct _fpstate
*buf
)
315 if ( !current
->used_math
)
318 /* This will cause a "finit" to be triggered by the next
319 * attempted FPU operation by the 'current' process.
321 current
->used_math
= 0;
325 return save_i387_fxsave( buf
);
327 return save_i387_fsave( buf
);
330 return save_i387_soft( ¤t
->thread
.i387
.soft
, buf
);
334 static inline int restore_i387_fsave( struct _fpstate
*buf
)
336 struct task_struct
*tsk
= current
;
338 return __copy_from_user( &tsk
->thread
.i387
.fsave
, buf
,
339 sizeof(struct i387_fsave_struct
) );
342 static inline int restore_i387_fxsave( struct _fpstate
*buf
)
344 struct task_struct
*tsk
= current
;
346 if ( __copy_from_user( &tsk
->thread
.i387
.fxsave
, &buf
->_fxsr_env
[0],
347 sizeof(struct i387_fxsave_struct
) ) )
349 return convert_fxsr_from_user( &tsk
->thread
.i387
.fxsave
, buf
);
352 int restore_i387( struct _fpstate
*buf
)
358 err
= restore_i387_fxsave( buf
);
360 err
= restore_i387_fsave( buf
);
363 err
= restore_i387_soft( ¤t
->thread
.i387
.soft
, buf
);
365 current
->used_math
= 1;
370 * ptrace request handlers.
373 static inline int get_fpregs_fsave( struct user_i387_struct
*buf
,
374 struct task_struct
*tsk
)
376 return __copy_to_user( buf
, &tsk
->thread
.i387
.fsave
,
377 sizeof(struct user_i387_struct
) );
380 static inline int get_fpregs_fxsave( struct user_i387_struct
*buf
,
381 struct task_struct
*tsk
)
383 return convert_fxsr_to_user( (struct _fpstate
*)buf
,
384 &tsk
->thread
.i387
.fxsave
);
387 int get_fpregs( struct user_i387_struct
*buf
, struct task_struct
*tsk
)
391 return get_fpregs_fxsave( buf
, tsk
);
393 return get_fpregs_fsave( buf
, tsk
);
396 return save_i387_soft( &tsk
->thread
.i387
.soft
,
397 (struct _fpstate
*)buf
);
401 static inline int set_fpregs_fsave( struct task_struct
*tsk
,
402 struct user_i387_struct
*buf
)
404 return __copy_from_user( &tsk
->thread
.i387
.fsave
, buf
,
405 sizeof(struct user_i387_struct
) );
408 static inline int set_fpregs_fxsave( struct task_struct
*tsk
,
409 struct user_i387_struct
*buf
)
411 return convert_fxsr_from_user( &tsk
->thread
.i387
.fxsave
,
412 (struct _fpstate
*)buf
);
415 int set_fpregs( struct task_struct
*tsk
, struct user_i387_struct
*buf
)
419 return set_fpregs_fxsave( tsk
, buf
);
421 return set_fpregs_fsave( tsk
, buf
);
424 return restore_i387_soft( &tsk
->thread
.i387
.soft
,
425 (struct _fpstate
*)buf
);
429 int get_fpxregs( struct user_fxsr_struct
*buf
, struct task_struct
*tsk
)
432 __copy_to_user( (void *)buf
, &tsk
->thread
.i387
.fxsave
,
433 sizeof(struct user_fxsr_struct
) );
440 int set_fpxregs( struct task_struct
*tsk
, struct user_fxsr_struct
*buf
)
443 __copy_from_user( &tsk
->thread
.i387
.fxsave
, (void *)buf
,
444 sizeof(struct user_fxsr_struct
) );
445 /* mxcsr bit 6 and 31-16 must be zero for security reasons */
446 tsk
->thread
.i387
.fxsave
.mxcsr
&= 0xffbf;
454 * FPU state for core dumps.
457 static inline void copy_fpu_fsave( struct task_struct
*tsk
,
458 struct user_i387_struct
*fpu
)
460 memcpy( fpu
, &tsk
->thread
.i387
.fsave
,
461 sizeof(struct user_i387_struct
) );
464 static inline void copy_fpu_fxsave( struct task_struct
*tsk
,
465 struct user_i387_struct
*fpu
)
468 unsigned short *from
;
471 memcpy( fpu
, &tsk
->thread
.i387
.fxsave
, 7 * sizeof(long) );
473 to
= (unsigned short *)&fpu
->st_space
[0];
474 from
= (unsigned short *)&tsk
->thread
.i387
.fxsave
.st_space
[0];
475 for ( i
= 0 ; i
< 8 ; i
++, to
+= 5, from
+= 8 ) {
476 memcpy( to
, from
, 5 * sizeof(unsigned short) );
480 int dump_fpu( struct pt_regs
*regs
, struct user_i387_struct
*fpu
)
483 struct task_struct
*tsk
= current
;
485 fpvalid
= tsk
->used_math
;
489 copy_fpu_fxsave( tsk
, fpu
);
491 copy_fpu_fsave( tsk
, fpu
);
498 int dump_extended_fpu( struct pt_regs
*regs
, struct user_fxsr_struct
*fpu
)
501 struct task_struct
*tsk
= current
;
503 fpvalid
= tsk
->used_math
&& HAVE_FXSR
;
506 memcpy( fpu
, &tsk
->thread
.i387
.fxsave
,
507 sizeof(struct user_fxsr_struct
) );