| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * include/asm-i386/i387.h | 
|  | 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 | #ifndef __ASM_I386_I387_H | 
|  | 12 | #define __ASM_I386_I387_H | 
|  | 13 |  | 
|  | 14 | #include <linux/sched.h> | 
|  | 15 | #include <linux/init.h> | 
| Andi Kleen | 18bd057 | 2006-04-20 02:36:45 +0200 | [diff] [blame] | 16 | #include <linux/kernel_stat.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 17 | #include <asm/processor.h> | 
|  | 18 | #include <asm/sigcontext.h> | 
|  | 19 | #include <asm/user.h> | 
|  | 20 |  | 
|  | 21 | extern void mxcsr_feature_mask_init(void); | 
|  | 22 | extern void init_fpu(struct task_struct *); | 
| Linus Torvalds | 8ed1383 | 2005-07-22 16:06:16 -0400 | [diff] [blame] | 23 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 24 | /* | 
|  | 25 | * FPU lazy state save handling... | 
|  | 26 | */ | 
| Linus Torvalds | 8ed1383 | 2005-07-22 16:06:16 -0400 | [diff] [blame] | 27 |  | 
|  | 28 | /* | 
|  | 29 | * The "nop" is needed to make the instructions the same | 
|  | 30 | * length. | 
|  | 31 | */ | 
|  | 32 | #define restore_fpu(tsk)			\ | 
|  | 33 | alternative_input(			\ | 
|  | 34 | "nop ; frstor %1",		\ | 
|  | 35 | "fxrstor %1",			\ | 
|  | 36 | X86_FEATURE_FXSR,		\ | 
| Linus Torvalds | 2847e34 | 2005-07-22 18:19:20 -0400 | [diff] [blame] | 37 | "m" ((tsk)->thread.i387.fxsave)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 38 |  | 
|  | 39 | extern void kernel_fpu_begin(void); | 
|  | 40 | #define kernel_fpu_end() do { stts(); preempt_enable(); } while(0) | 
|  | 41 |  | 
| Andi Kleen | 18bd057 | 2006-04-20 02:36:45 +0200 | [diff] [blame] | 42 | /* We need a safe address that is cheap to find and that is already | 
|  | 43 | in L1 during context switch. The best choices are unfortunately | 
|  | 44 | different for UP and SMP */ | 
|  | 45 | #ifdef CONFIG_SMP | 
|  | 46 | #define safe_address (__per_cpu_offset[0]) | 
|  | 47 | #else | 
|  | 48 | #define safe_address (kstat_cpu(0).cpustat.user) | 
|  | 49 | #endif | 
|  | 50 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 51 | /* | 
|  | 52 | * These must be called with preempt disabled | 
|  | 53 | */ | 
|  | 54 | static inline void __save_init_fpu( struct task_struct *tsk ) | 
|  | 55 | { | 
| Andi Kleen | 18bd057 | 2006-04-20 02:36:45 +0200 | [diff] [blame] | 56 | /* Use more nops than strictly needed in case the compiler | 
|  | 57 | varies code */ | 
| Linus Torvalds | 2847e34 | 2005-07-22 18:19:20 -0400 | [diff] [blame] | 58 | alternative_input( | 
| Andi Kleen | 18bd057 | 2006-04-20 02:36:45 +0200 | [diff] [blame] | 59 | "fnsave %[fx] ;fwait;" GENERIC_NOP8 GENERIC_NOP4, | 
|  | 60 | "fxsave %[fx]\n" | 
| Chuck Ebbert | 543f2a3 | 2006-04-29 14:07:49 -0400 | [diff] [blame] | 61 | "bt $7,%[fsw] ; jnc 1f ; fnclex\n1:", | 
| Linus Torvalds | 2847e34 | 2005-07-22 18:19:20 -0400 | [diff] [blame] | 62 | X86_FEATURE_FXSR, | 
| Andi Kleen | 18bd057 | 2006-04-20 02:36:45 +0200 | [diff] [blame] | 63 | [fx] "m" (tsk->thread.i387.fxsave), | 
|  | 64 | [fsw] "m" (tsk->thread.i387.fxsave.swd) : "memory"); | 
|  | 65 | /* AMD K7/K8 CPUs don't save/restore FDP/FIP/FOP unless an exception | 
|  | 66 | is pending.  Clear the x87 state here by setting it to fixed | 
| Chuck Ebbert | 543f2a3 | 2006-04-29 14:07:49 -0400 | [diff] [blame] | 67 | values. safe_address is a random variable that should be in L1 */ | 
| Andi Kleen | 18bd057 | 2006-04-20 02:36:45 +0200 | [diff] [blame] | 68 | alternative_input( | 
|  | 69 | GENERIC_NOP8 GENERIC_NOP2, | 
|  | 70 | "emms\n\t"	  	/* clear stack tags */ | 
|  | 71 | "fildl %[addr]", 	/* set F?P to defined value */ | 
|  | 72 | X86_FEATURE_FXSAVE_LEAK, | 
|  | 73 | [addr] "m" (safe_address)); | 
| Al Viro | 06b425d | 2006-01-12 01:05:40 -0800 | [diff] [blame] | 74 | task_thread_info(tsk)->status &= ~TS_USEDFPU; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 75 | } | 
|  | 76 |  | 
|  | 77 | #define __unlazy_fpu( tsk ) do { \ | 
| Al Viro | 06b425d | 2006-01-12 01:05:40 -0800 | [diff] [blame] | 78 | if (task_thread_info(tsk)->status & TS_USEDFPU) \ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 79 | save_init_fpu( tsk ); \ | 
|  | 80 | } while (0) | 
|  | 81 |  | 
|  | 82 | #define __clear_fpu( tsk )					\ | 
|  | 83 | do {								\ | 
| Al Viro | 06b425d | 2006-01-12 01:05:40 -0800 | [diff] [blame] | 84 | if (task_thread_info(tsk)->status & TS_USEDFPU) {		\ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 85 | asm volatile("fnclex ; fwait");				\ | 
| Al Viro | 06b425d | 2006-01-12 01:05:40 -0800 | [diff] [blame] | 86 | task_thread_info(tsk)->status &= ~TS_USEDFPU;	\ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 87 | stts();						\ | 
|  | 88 | }							\ | 
|  | 89 | } while (0) | 
|  | 90 |  | 
|  | 91 |  | 
|  | 92 | /* | 
|  | 93 | * These disable preemption on their own and are safe | 
|  | 94 | */ | 
|  | 95 | static inline void save_init_fpu( struct task_struct *tsk ) | 
|  | 96 | { | 
|  | 97 | preempt_disable(); | 
|  | 98 | __save_init_fpu(tsk); | 
|  | 99 | stts(); | 
|  | 100 | preempt_enable(); | 
|  | 101 | } | 
|  | 102 |  | 
|  | 103 | #define unlazy_fpu( tsk ) do {	\ | 
|  | 104 | preempt_disable();	\ | 
|  | 105 | __unlazy_fpu(tsk);	\ | 
|  | 106 | preempt_enable();	\ | 
|  | 107 | } while (0) | 
|  | 108 |  | 
|  | 109 | #define clear_fpu( tsk ) do {	\ | 
|  | 110 | preempt_disable();	\ | 
|  | 111 | __clear_fpu( tsk );	\ | 
|  | 112 | preempt_enable();	\ | 
|  | 113 | } while (0) | 
|  | 114 | \ | 
|  | 115 | /* | 
|  | 116 | * FPU state interaction... | 
|  | 117 | */ | 
|  | 118 | extern unsigned short get_fpu_cwd( struct task_struct *tsk ); | 
|  | 119 | extern unsigned short get_fpu_swd( struct task_struct *tsk ); | 
|  | 120 | extern unsigned short get_fpu_mxcsr( struct task_struct *tsk ); | 
|  | 121 |  | 
|  | 122 | /* | 
|  | 123 | * Signal frame handlers... | 
|  | 124 | */ | 
|  | 125 | extern int save_i387( struct _fpstate __user *buf ); | 
|  | 126 | extern int restore_i387( struct _fpstate __user *buf ); | 
|  | 127 |  | 
|  | 128 | /* | 
|  | 129 | * ptrace request handers... | 
|  | 130 | */ | 
|  | 131 | extern int get_fpregs( struct user_i387_struct __user *buf, | 
|  | 132 | struct task_struct *tsk ); | 
|  | 133 | extern int set_fpregs( struct task_struct *tsk, | 
|  | 134 | struct user_i387_struct __user *buf ); | 
|  | 135 |  | 
|  | 136 | extern int get_fpxregs( struct user_fxsr_struct __user *buf, | 
|  | 137 | struct task_struct *tsk ); | 
|  | 138 | extern int set_fpxregs( struct task_struct *tsk, | 
|  | 139 | struct user_fxsr_struct __user *buf ); | 
|  | 140 |  | 
|  | 141 | /* | 
|  | 142 | * FPU state for core dumps... | 
|  | 143 | */ | 
|  | 144 | extern int dump_fpu( struct pt_regs *regs, | 
|  | 145 | struct user_i387_struct *fpu ); | 
|  | 146 |  | 
|  | 147 | #endif /* __ASM_I386_I387_H */ |