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