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