~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/i386/kernel/i387.c

Version: ~ [ linux-5.8 ] ~ [ linux-5.7.12 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.55 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.136 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.191 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.232 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.232 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.85 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp