Merge to Fedora kernel-2.6.7-1.494 and VServer 1.9.1.12. Fix some previous merge...
[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         struct _fpreg tmp[8]; /* 80 bytes scratch area */
231         unsigned long env[7];
232         struct _fpreg __user *to;
233         struct _fpxreg *from;
234         int i;
235
236         env[0] = (unsigned long)fxsave->cwd | 0xffff0000ul;
237         env[1] = (unsigned long)fxsave->swd | 0xffff0000ul;
238         env[2] = twd_fxsr_to_i387(fxsave);
239         env[3] = fxsave->fip;
240         env[4] = fxsave->fcs | ((unsigned long)fxsave->fop << 16);
241         env[5] = fxsave->foo;
242         env[6] = fxsave->fos;
243
244         if ( __copy_to_user( buf, env, 7 * sizeof(unsigned long) ) )
245                 return 1;
246
247         to = tmp;
248         from = (struct _fpxreg *) &fxsave->st_space[0];
249         for ( i = 0 ; i < 8 ; i++, to++, from++ ) {
250                 unsigned long __user *t = (unsigned long __user *)to;
251                 unsigned long *f = (unsigned long *)from;
252
253                 *t = *f;
254                 *(t + 1) = *(f+1);
255                 to->exponent = from->exponent;
256         }
257         if (copy_to_user(buf->_st, tmp, sizeof(struct _fpreg [8])))
258                 return 1;
259         return 0;
260 }
261
262 static int convert_fxsr_from_user( struct i387_fxsave_struct *fxsave,
263                                           struct _fpstate __user *buf )
264 {
265         struct _fpreg tmp[8]; /* 80 bytes scratch area */
266         unsigned long env[7];
267         struct _fpxreg *to;
268         struct _fpreg __user *from;
269         int i;
270
271         if ( __copy_from_user( env, buf, 7 * sizeof(long) ) )
272                 return 1;
273         if (copy_from_user(tmp, buf->_st, sizeof(struct _fpreg [8])))
274                 return 1;
275
276         fxsave->cwd = (unsigned short)(env[0] & 0xffff);
277         fxsave->swd = (unsigned short)(env[1] & 0xffff);
278         fxsave->twd = twd_i387_to_fxsr((unsigned short)(env[2] & 0xffff));
279         fxsave->fip = env[3];
280         fxsave->fop = (unsigned short)((env[4] & 0xffff0000ul) >> 16);
281         fxsave->fcs = (env[4] & 0xffff);
282         fxsave->foo = env[5];
283         fxsave->fos = env[6];
284
285         to = (struct _fpxreg *) &fxsave->st_space[0];
286         from = tmp;
287         for ( i = 0 ; i < 8 ; i++, to++, from++ ) {
288                 unsigned long *t = (unsigned long *)to;
289                 unsigned long __user *f = (unsigned long __user *)from;
290
291                 *t = *f;
292                 *(t + 1) = *(f + 1);
293                 to->exponent = from->exponent;
294         }
295         return 0;
296 }
297
298 /*
299  * Signal frame handlers.
300  */
301
302 static inline int save_i387_fsave( struct _fpstate __user *buf )
303 {
304         struct task_struct *tsk = current;
305
306         unlazy_fpu( tsk );
307         tsk->thread.i387.fsave.status = tsk->thread.i387.fsave.swd;
308         if ( __copy_to_user( buf, &tsk->thread.i387.fsave,
309                              sizeof(struct i387_fsave_struct) ) )
310                 return -1;
311         return 1;
312 }
313
314 static int save_i387_fxsave( struct _fpstate __user *buf )
315 {
316         struct task_struct *tsk = current;
317         int err = 0;
318
319         unlazy_fpu( tsk );
320
321         if ( convert_fxsr_to_user( buf, &tsk->thread.i387.fxsave ) )
322                 return -1;
323
324         err |= __put_user( tsk->thread.i387.fxsave.swd, &buf->status );
325         err |= __put_user( X86_FXSR_MAGIC, &buf->magic );
326         if ( err )
327                 return -1;
328
329         if ( __copy_to_user( &buf->_fxsr_env[0], &tsk->thread.i387.fxsave,
330                              sizeof(struct i387_fxsave_struct) ) )
331                 return -1;
332         return 1;
333 }
334
335 int save_i387( struct _fpstate __user *buf )
336 {
337         if ( !current->used_math )
338                 return 0;
339
340         /* This will cause a "finit" to be triggered by the next
341          * attempted FPU operation by the 'current' process.
342          */
343         current->used_math = 0;
344
345         if ( HAVE_HWFP ) {
346                 if ( cpu_has_fxsr ) {
347                         return save_i387_fxsave( buf );
348                 } else {
349                         return save_i387_fsave( buf );
350                 }
351         } else {
352                 return save_i387_soft( &current->thread.i387.soft, buf );
353         }
354 }
355
356 static inline int restore_i387_fsave( struct _fpstate __user *buf )
357 {
358         struct task_struct *tsk = current;
359         clear_fpu( tsk );
360         return __copy_from_user( &tsk->thread.i387.fsave, buf,
361                                  sizeof(struct i387_fsave_struct) );
362 }
363
364 static int restore_i387_fxsave( struct _fpstate __user *buf )
365 {
366         int err;
367         struct task_struct *tsk = current;
368         clear_fpu( tsk );
369         err = __copy_from_user( &tsk->thread.i387.fxsave, &buf->_fxsr_env[0],
370                                 sizeof(struct i387_fxsave_struct) );
371         /* mxcsr reserved bits must be masked to zero for security reasons */
372         tsk->thread.i387.fxsave.mxcsr &= mxcsr_feature_mask;
373         return err ? 1 : convert_fxsr_from_user( &tsk->thread.i387.fxsave, buf );
374 }
375
376 int restore_i387( struct _fpstate __user *buf )
377 {
378         int err;
379
380         if ( HAVE_HWFP ) {
381                 if ( cpu_has_fxsr ) {
382                         err = restore_i387_fxsave( buf );
383                 } else {
384                         err = restore_i387_fsave( buf );
385                 }
386         } else {
387                 err = restore_i387_soft( &current->thread.i387.soft, buf );
388         }
389         current->used_math = 1;
390         return err;
391 }
392
393 /*
394  * ptrace request handlers.
395  */
396
397 static inline int get_fpregs_fsave( struct user_i387_struct __user *buf,
398                                     struct task_struct *tsk )
399 {
400         return __copy_to_user( buf, &tsk->thread.i387.fsave,
401                                sizeof(struct user_i387_struct) );
402 }
403
404 static inline int get_fpregs_fxsave( struct user_i387_struct __user *buf,
405                                      struct task_struct *tsk )
406 {
407         return convert_fxsr_to_user( (struct _fpstate __user *)buf,
408                                      &tsk->thread.i387.fxsave );
409 }
410
411 int get_fpregs( struct user_i387_struct __user *buf, struct task_struct *tsk )
412 {
413         if ( HAVE_HWFP ) {
414                 if ( cpu_has_fxsr ) {
415                         return get_fpregs_fxsave( buf, tsk );
416                 } else {
417                         return get_fpregs_fsave( buf, tsk );
418                 }
419         } else {
420                 return save_i387_soft( &tsk->thread.i387.soft,
421                                        (struct _fpstate __user *)buf );
422         }
423 }
424
425 static inline int set_fpregs_fsave( struct task_struct *tsk,
426                                     struct user_i387_struct __user *buf )
427 {
428         return __copy_from_user( &tsk->thread.i387.fsave, buf,
429                                  sizeof(struct user_i387_struct) );
430 }
431
432 static inline int set_fpregs_fxsave( struct task_struct *tsk,
433                                      struct user_i387_struct __user *buf )
434 {
435         return convert_fxsr_from_user( &tsk->thread.i387.fxsave,
436                                        (struct _fpstate __user *)buf );
437 }
438
439 int set_fpregs( struct task_struct *tsk, struct user_i387_struct __user *buf )
440 {
441         if ( HAVE_HWFP ) {
442                 if ( cpu_has_fxsr ) {
443                         return set_fpregs_fxsave( tsk, buf );
444                 } else {
445                         return set_fpregs_fsave( tsk, buf );
446                 }
447         } else {
448                 return restore_i387_soft( &tsk->thread.i387.soft,
449                                           (struct _fpstate __user *)buf );
450         }
451 }
452
453 int get_fpxregs( struct user_fxsr_struct __user *buf, struct task_struct *tsk )
454 {
455         if ( cpu_has_fxsr ) {
456                 if (__copy_to_user( buf, &tsk->thread.i387.fxsave,
457                                     sizeof(struct user_fxsr_struct) ))
458                         return -EFAULT;
459                 return 0;
460         } else {
461                 return -EIO;
462         }
463 }
464
465 int set_fpxregs( struct task_struct *tsk, struct user_fxsr_struct __user *buf )
466 {
467         int ret = 0;
468
469         if ( cpu_has_fxsr ) {
470                 if (__copy_from_user( &tsk->thread.i387.fxsave, buf,
471                                   sizeof(struct user_fxsr_struct) ))
472                         ret = -EFAULT;
473                 /* mxcsr reserved bits must be masked to zero for security reasons */
474                 tsk->thread.i387.fxsave.mxcsr &= mxcsr_feature_mask;
475         } else {
476                 ret = -EIO;
477         }
478         return ret;
479 }
480
481 /*
482  * FPU state for core dumps.
483  */
484
485 static inline void copy_fpu_fsave( struct task_struct *tsk,
486                                    struct user_i387_struct *fpu )
487 {
488         memcpy( fpu, &tsk->thread.i387.fsave,
489                 sizeof(struct user_i387_struct) );
490 }
491
492 static inline void copy_fpu_fxsave( struct task_struct *tsk,
493                                    struct user_i387_struct *fpu )
494 {
495         unsigned short *to;
496         unsigned short *from;
497         int i;
498
499         memcpy( fpu, &tsk->thread.i387.fxsave, 7 * sizeof(long) );
500
501         to = (unsigned short *)&fpu->st_space[0];
502         from = (unsigned short *)&tsk->thread.i387.fxsave.st_space[0];
503         for ( i = 0 ; i < 8 ; i++, to += 5, from += 8 ) {
504                 memcpy( to, from, 5 * sizeof(unsigned short) );
505         }
506 }
507
508 int dump_fpu( struct pt_regs *regs, struct user_i387_struct *fpu )
509 {
510         int fpvalid;
511         struct task_struct *tsk = current;
512
513         fpvalid = tsk->used_math;
514         if ( fpvalid ) {
515                 unlazy_fpu( tsk );
516                 if ( cpu_has_fxsr ) {
517                         copy_fpu_fxsave( tsk, fpu );
518                 } else {
519                         copy_fpu_fsave( tsk, fpu );
520                 }
521         }
522
523         return fpvalid;
524 }
525
526 int dump_extended_fpu( struct pt_regs *regs, struct user_fxsr_struct *fpu )
527 {
528         int fpvalid;
529         struct task_struct *tsk = current;
530
531         fpvalid = tsk->used_math && cpu_has_fxsr;
532         if ( fpvalid ) {
533                 unlazy_fpu( tsk );
534                 memcpy( fpu, &tsk->thread.i387.fxsave,
535                         sizeof(struct user_fxsr_struct) );
536         }
537
538         return fpvalid;
539 }
540
541 int dump_task_fpu(struct task_struct *tsk, struct user_i387_struct *fpu)
542 {
543         int fpvalid = tsk->used_math;
544
545         if (fpvalid) {
546                 if (tsk == current)
547                         unlazy_fpu(tsk);
548                 if (cpu_has_fxsr)
549                         copy_fpu_fxsave(tsk, fpu);
550                 else
551                         copy_fpu_fsave(tsk, fpu);
552         }
553         return fpvalid;
554 }
555
556 int dump_task_extended_fpu(struct task_struct *tsk, struct user_fxsr_struct *fpu)
557 {
558         int fpvalid = tsk->used_math && cpu_has_fxsr;
559
560         if (fpvalid) {
561                 if (tsk == current)
562                        unlazy_fpu(tsk);
563                 memcpy(fpu, &tsk->thread.i387.fxsave, sizeof(*fpu));
564         }
565         return fpvalid;
566 }