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 <linux/init.h>
14 #include <linux/kernel_stat.h>
15 #include <asm/processor.h>
17 #include <asm/math_emu.h>
18 #include <asm/sigcontext.h>
20 #include <asm/ptrace.h>
21 #include <asm/uaccess.h>
23 #ifdef CONFIG_MATH_EMULATION
24 #define HAVE_HWFP (boot_cpu_data.hard_math)
29 static union i387_union empty_fpu_state;
31 void __init boot_init_fpu(void)
33 memset(&empty_fpu_state, 0, sizeof(union i387_union));
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;
41 empty_fpu_state.fxsave.cwd = 0x37f;
43 empty_fpu_state.fxsave.mxcsr = 0x1f80;
47 void load_empty_fpu(struct task_struct * tsk)
49 memcpy(&tsk->thread.i387, &empty_fpu_state, sizeof(union i387_union));
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.
61 asm volatile("fxrstor %0" : : "m" (empty_fpu_state.fxsave));
64 current->used_math = 1;
68 * FPU lazy state save handling.
71 static inline void __save_init_fpu( struct task_struct *tsk )
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));
81 asm volatile( "fnsave %0 ; fwait"
82 : "=m" (tsk->thread.i387.fsave) );
84 tsk->flags &= ~PF_USEDFPU;
87 void save_init_fpu( struct task_struct *tsk )
93 void kernel_fpu_begin(void)
95 struct task_struct *tsk = current;
97 if (tsk->flags & PF_USEDFPU) {
104 void restore_fpu( struct task_struct *tsk )
106 if ( cpu_has_fxsr ) {
107 asm volatile( "fxrstor %0"
108 : : "m" (tsk->thread.i387.fxsave) );
110 asm volatile( "frstor %0"
111 : : "m" (tsk->thread.i387.fsave) );
116 * FPU tag word conversions.
119 static inline unsigned short twd_i387_to_fxsr( unsigned short twd )
121 unsigned int tmp; /* to avoid 16 bit prefixes in the code */
123 /* Transform each pair of bits into 01 (valid) or 00 (empty) */
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 */
133 static inline unsigned long twd_fxsr_to_i387( struct i387_fxsave_struct *fxsave )
135 struct _fpxreg *st = NULL;
136 unsigned long tos = (fxsave->swd >> 11) & 7;
137 unsigned long twd = (unsigned long) fxsave->twd;
139 unsigned long ret = 0xffff0000;
142 #define FPREG_ADDR(f, n) ((void *)&(f)->st_space + (n) * 16);
144 for ( i = 0 ; i < 8 ; i++ ) {
146 st = FPREG_ADDR( fxsave, (i - tos) & 7 );
148 switch ( st->exponent & 0x7fff ) {
150 tag = 2; /* Special */
153 if ( !st->significand[0] &&
154 !st->significand[1] &&
155 !st->significand[2] &&
156 !st->significand[3] ) {
159 tag = 2; /* Special */
163 if ( st->significand[3] & 0x8000 ) {
166 tag = 2; /* Special */
173 ret |= (tag << (2 * i));
180 * FPU state interaction.
183 unsigned short get_fpu_cwd( struct task_struct *tsk )
185 if ( cpu_has_fxsr ) {
186 return tsk->thread.i387.fxsave.cwd;
188 return (unsigned short)tsk->thread.i387.fsave.cwd;
192 unsigned short get_fpu_swd( struct task_struct *tsk )
194 if ( cpu_has_fxsr ) {
195 return tsk->thread.i387.fxsave.swd;
197 return (unsigned short)tsk->thread.i387.fsave.swd;
201 unsigned short get_fpu_twd( struct task_struct *tsk )
203 if ( cpu_has_fxsr ) {
204 return tsk->thread.i387.fxsave.twd;
206 return (unsigned short)tsk->thread.i387.fsave.twd;
210 unsigned short get_fpu_mxcsr( struct task_struct *tsk )
213 return tsk->thread.i387.fxsave.mxcsr;
219 void set_fpu_cwd( struct task_struct *tsk, unsigned short cwd )
221 if ( cpu_has_fxsr ) {
222 tsk->thread.i387.fxsave.cwd = cwd;
224 tsk->thread.i387.fsave.cwd = ((long)cwd | 0xffff0000);
228 void set_fpu_swd( struct task_struct *tsk, unsigned short swd )
230 if ( cpu_has_fxsr ) {
231 tsk->thread.i387.fxsave.swd = swd;
233 tsk->thread.i387.fsave.swd = ((long)swd | 0xffff0000);
237 void set_fpu_twd( struct task_struct *tsk, unsigned short twd )
239 if ( cpu_has_fxsr ) {
240 tsk->thread.i387.fxsave.twd = twd_i387_to_fxsr(twd);
242 tsk->thread.i387.fsave.twd = ((long)twd | 0xffff0000);
246 void set_fpu_mxcsr( struct task_struct *tsk, unsigned short mxcsr )
249 tsk->thread.i387.fxsave.mxcsr = (mxcsr & 0xffbf);
254 * FXSR floating point environment conversions.
257 static inline int convert_fxsr_to_user( struct _fpstate *buf,
258 struct i387_fxsave_struct *fxsave )
260 unsigned long env[7];
262 struct _fpxreg *from;
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;
273 if ( __copy_to_user( buf, env, 7 * sizeof(unsigned long) ) )
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) ) )
285 static inline int convert_fxsr_from_user( struct i387_fxsave_struct *fxsave,
286 struct _fpstate *buf )
288 unsigned long env[7];
293 if ( __copy_from_user( env, buf, 7 * sizeof(long) ) )
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];
305 to = (struct _fpxreg *) &fxsave->st_space[0];
307 for ( i = 0 ; i < 8 ; i++, to++, from++ ) {
308 if ( __copy_from_user( to, from, sizeof(*from) ) )
315 * Signal frame handlers.
318 static inline int save_i387_fsave( struct _fpstate *buf )
320 struct task_struct *tsk = current;
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) ) )
330 static inline int save_i387_fxsave( struct _fpstate *buf )
332 struct task_struct *tsk = current;
337 if ( convert_fxsr_to_user( buf, &tsk->thread.i387.fxsave ) )
340 err |= __put_user( tsk->thread.i387.fxsave.swd, &buf->status );
341 err |= __put_user( X86_FXSR_MAGIC, &buf->magic );
345 if ( __copy_to_user( &buf->_fxsr_env[0], &tsk->thread.i387.fxsave,
346 sizeof(struct i387_fxsave_struct) ) )
351 int save_i387( struct _fpstate *buf )
353 if ( !current->used_math )
356 /* This will cause a "finit" to be triggered by the next
357 * attempted FPU operation by the 'current' process.
359 current->used_math = 0;
362 if ( cpu_has_fxsr ) {
363 return save_i387_fxsave( buf );
365 return save_i387_fsave( buf );
368 return save_i387_soft( ¤t->thread.i387.soft, buf );
372 static inline int restore_i387_fsave( struct _fpstate *buf )
374 struct task_struct *tsk = current;
376 return __copy_from_user( &tsk->thread.i387.fsave, buf,
377 sizeof(struct i387_fsave_struct) );
380 static inline int restore_i387_fxsave( struct _fpstate *buf )
383 struct task_struct *tsk = current;
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 );
392 int restore_i387( struct _fpstate *buf )
397 if ( cpu_has_fxsr ) {
398 err = restore_i387_fxsave( buf );
400 err = restore_i387_fsave( buf );
403 err = restore_i387_soft( ¤t->thread.i387.soft, buf );
405 current->used_math = 1;
410 * ptrace request handlers.
413 static inline int get_fpregs_fsave( struct user_i387_struct *buf,
414 struct task_struct *tsk )
416 return __copy_to_user( buf, &tsk->thread.i387.fsave,
417 sizeof(struct user_i387_struct) );
420 static inline int get_fpregs_fxsave( struct user_i387_struct *buf,
421 struct task_struct *tsk )
423 return convert_fxsr_to_user( (struct _fpstate *)buf,
424 &tsk->thread.i387.fxsave );
427 int get_fpregs( struct user_i387_struct *buf, struct task_struct *tsk )
430 if ( cpu_has_fxsr ) {
431 return get_fpregs_fxsave( buf, tsk );
433 return get_fpregs_fsave( buf, tsk );
436 return save_i387_soft( &tsk->thread.i387.soft,
437 (struct _fpstate *)buf );
441 static inline int set_fpregs_fsave( struct task_struct *tsk,
442 struct user_i387_struct *buf )
444 return __copy_from_user( &tsk->thread.i387.fsave, buf,
445 sizeof(struct user_i387_struct) );
448 static inline int set_fpregs_fxsave( struct task_struct *tsk,
449 struct user_i387_struct *buf )
451 return convert_fxsr_from_user( &tsk->thread.i387.fxsave,
452 (struct _fpstate *)buf );
455 int set_fpregs( struct task_struct *tsk, struct user_i387_struct *buf )
458 if ( cpu_has_fxsr ) {
459 return set_fpregs_fxsave( tsk, buf );
461 return set_fpregs_fsave( tsk, buf );
464 return restore_i387_soft( &tsk->thread.i387.soft,
465 (struct _fpstate *)buf );
469 int get_fpxregs( struct user_fxsr_struct *buf, struct task_struct *tsk )
471 if ( cpu_has_fxsr ) {
472 if (__copy_to_user( (void *)buf, &tsk->thread.i387.fxsave,
473 sizeof(struct user_fxsr_struct) ))
481 int set_fpxregs( struct task_struct *tsk, struct user_fxsr_struct *buf )
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;
495 * FPU state for core dumps.
498 static inline void copy_fpu_fsave( struct task_struct *tsk,
499 struct user_i387_struct *fpu )
501 memcpy( fpu, &tsk->thread.i387.fsave,
502 sizeof(struct user_i387_struct) );
505 static inline void copy_fpu_fxsave( struct task_struct *tsk,
506 struct user_i387_struct *fpu )
509 unsigned short *from;
512 memcpy( fpu, &tsk->thread.i387.fxsave, 7 * sizeof(long) );
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) );
521 int dump_fpu( struct pt_regs *regs, struct user_i387_struct *fpu )
524 struct task_struct *tsk = current;
526 fpvalid = tsk->used_math;
529 if ( cpu_has_fxsr ) {
530 copy_fpu_fxsave( tsk, fpu );
532 copy_fpu_fsave( tsk, fpu );
539 int dump_extended_fpu( struct pt_regs *regs, struct user_fxsr_struct *fpu )
542 struct task_struct *tsk = current;
544 fpvalid = tsk->used_math && cpu_has_fxsr;
547 memcpy( fpu, &tsk->thread.i387.fxsave,
548 sizeof(struct user_fxsr_struct) );