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