i387.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:12k
源码类别:

Linux/Unix编程

开发平台:

Unix_Linux

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