| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  *  linux/arch/x86_64/kernel/i387.c | 
 | 3 |  * | 
 | 4 |  *  Copyright (C) 1994 Linus Torvalds | 
 | 5 |  *  Copyright (C) 2002 Andi Kleen, SuSE Labs | 
 | 6 |  * | 
 | 7 |  *  Pentium III FXSR, SSE support | 
 | 8 |  *  General FPU state handling cleanups | 
 | 9 |  *	Gareth Hughes <gareth@valinux.com>, May 2000 | 
 | 10 |  *  | 
 | 11 |  *  x86-64 rework 2002 Andi Kleen.  | 
 | 12 |  *  Does direct fxsave in and out of user space now for signal handlers. | 
 | 13 |  *  All the FSAVE<->FXSAVE conversion code has been moved to the 32bit emulation, | 
 | 14 |  *  the 64bit user space sees a FXSAVE frame directly.  | 
 | 15 |  */ | 
 | 16 |  | 
 | 17 | #include <linux/config.h> | 
 | 18 | #include <linux/sched.h> | 
 | 19 | #include <linux/init.h> | 
 | 20 | #include <asm/processor.h> | 
 | 21 | #include <asm/i387.h> | 
 | 22 | #include <asm/sigcontext.h> | 
 | 23 | #include <asm/user.h> | 
 | 24 | #include <asm/ptrace.h> | 
 | 25 | #include <asm/uaccess.h> | 
 | 26 |  | 
 | 27 | unsigned int mxcsr_feature_mask = 0xffffffff; | 
 | 28 |  | 
 | 29 | void mxcsr_feature_mask_init(void) | 
 | 30 | { | 
 | 31 | 	unsigned int mask; | 
 | 32 | 	clts(); | 
 | 33 | 	memset(¤t->thread.i387.fxsave, 0, sizeof(struct i387_fxsave_struct)); | 
 | 34 | 	asm volatile("fxsave %0" : : "m" (current->thread.i387.fxsave)); | 
 | 35 | 	mask = current->thread.i387.fxsave.mxcsr_mask; | 
 | 36 | 	if (mask == 0) mask = 0x0000ffbf; | 
 | 37 | 	mxcsr_feature_mask &= mask; | 
 | 38 | 	stts(); | 
 | 39 | } | 
 | 40 |  | 
 | 41 | /* | 
 | 42 |  * Called at bootup to set up the initial FPU state that is later cloned | 
 | 43 |  * into all processes. | 
 | 44 |  */ | 
| Ashok Raj | e6982c6 | 2005-06-25 14:54:58 -0700 | [diff] [blame] | 45 | void __cpuinit fpu_init(void) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 46 | { | 
 | 47 | 	unsigned long oldcr0 = read_cr0(); | 
 | 48 | 	extern void __bad_fxsave_alignment(void); | 
 | 49 | 		 | 
 | 50 | 	if (offsetof(struct task_struct, thread.i387.fxsave) & 15) | 
 | 51 | 		__bad_fxsave_alignment(); | 
 | 52 | 	set_in_cr4(X86_CR4_OSFXSR); | 
 | 53 | 	set_in_cr4(X86_CR4_OSXMMEXCPT); | 
 | 54 |  | 
 | 55 | 	write_cr0(oldcr0 & ~((1UL<<3)|(1UL<<2))); /* clear TS and EM */ | 
 | 56 |  | 
 | 57 | 	mxcsr_feature_mask_init(); | 
 | 58 | 	/* clean state in init */ | 
 | 59 | 	current_thread_info()->status = 0; | 
 | 60 | 	clear_used_math(); | 
 | 61 | } | 
 | 62 |  | 
 | 63 | void init_fpu(struct task_struct *child) | 
 | 64 | { | 
 | 65 | 	if (tsk_used_math(child)) { | 
 | 66 | 		if (child == current) | 
 | 67 | 			unlazy_fpu(child); | 
 | 68 | 		return; | 
 | 69 | 	}	 | 
 | 70 | 	memset(&child->thread.i387.fxsave, 0, sizeof(struct i387_fxsave_struct)); | 
 | 71 | 	child->thread.i387.fxsave.cwd = 0x37f; | 
 | 72 | 	child->thread.i387.fxsave.mxcsr = 0x1f80; | 
 | 73 | 	/* only the device not available exception or ptrace can call init_fpu */ | 
 | 74 | 	set_stopped_child_used_math(child); | 
 | 75 | } | 
 | 76 |  | 
 | 77 | /* | 
 | 78 |  * Signal frame handlers. | 
 | 79 |  */ | 
 | 80 |  | 
 | 81 | int save_i387(struct _fpstate __user *buf) | 
 | 82 | { | 
 | 83 | 	struct task_struct *tsk = current; | 
 | 84 | 	int err = 0; | 
 | 85 |  | 
 | 86 | 	{  | 
 | 87 | 		extern void bad_user_i387_struct(void);  | 
 | 88 | 		if (sizeof(struct user_i387_struct) != sizeof(tsk->thread.i387.fxsave)) | 
 | 89 | 			bad_user_i387_struct(); | 
 | 90 | 	}  | 
 | 91 |  | 
 | 92 | 	if ((unsigned long)buf % 16)  | 
 | 93 | 		printk("save_i387: bad fpstate %p\n",buf);  | 
 | 94 |  | 
 | 95 | 	if (!used_math()) | 
 | 96 | 		return 0; | 
 | 97 | 	clear_used_math(); /* trigger finit */ | 
 | 98 | 	if (tsk->thread_info->status & TS_USEDFPU) { | 
 | 99 | 		err = save_i387_checking((struct i387_fxsave_struct __user *)buf); | 
 | 100 | 		if (err) return err; | 
 | 101 | 		stts(); | 
 | 102 | 		} else { | 
 | 103 | 		if (__copy_to_user(buf, &tsk->thread.i387.fxsave,  | 
 | 104 | 				   sizeof(struct i387_fxsave_struct))) | 
 | 105 | 			return -1; | 
 | 106 | 	}  | 
 | 107 | 		return 1; | 
 | 108 | } | 
 | 109 |  | 
 | 110 | /* | 
 | 111 |  * ptrace request handlers. | 
 | 112 |  */ | 
 | 113 |  | 
 | 114 | int get_fpregs(struct user_i387_struct __user *buf, struct task_struct *tsk) | 
 | 115 | { | 
 | 116 | 	init_fpu(tsk); | 
 | 117 | 	return __copy_to_user(buf, &tsk->thread.i387.fxsave, | 
 | 118 | 			       sizeof(struct user_i387_struct)) ? -EFAULT : 0; | 
 | 119 | } | 
 | 120 |  | 
 | 121 | int set_fpregs(struct task_struct *tsk, struct user_i387_struct __user *buf) | 
 | 122 | { | 
 | 123 | 	if (__copy_from_user(&tsk->thread.i387.fxsave, buf,  | 
 | 124 | 			     sizeof(struct user_i387_struct))) | 
 | 125 | 		return -EFAULT; | 
 | 126 | 		return 0; | 
 | 127 | } | 
 | 128 |  | 
 | 129 | /* | 
 | 130 |  * FPU state for core dumps. | 
 | 131 |  */ | 
 | 132 |  | 
 | 133 | int dump_fpu( struct pt_regs *regs, struct user_i387_struct *fpu ) | 
 | 134 | { | 
 | 135 | 	struct task_struct *tsk = current; | 
 | 136 |  | 
 | 137 | 	if (!used_math()) | 
 | 138 | 		return 0; | 
 | 139 |  | 
 | 140 | 	unlazy_fpu(tsk); | 
 | 141 | 	memcpy(fpu, &tsk->thread.i387.fxsave, sizeof(struct user_i387_struct));  | 
 | 142 | 	return 1;  | 
 | 143 | } | 
 | 144 |  | 
 | 145 | int dump_task_fpu(struct task_struct *tsk, struct user_i387_struct *fpu) | 
 | 146 | { | 
 | 147 | 	int fpvalid = !!tsk_used_math(tsk); | 
 | 148 |  | 
 | 149 | 	if (fpvalid) { | 
 | 150 | 		if (tsk == current) | 
 | 151 | 			unlazy_fpu(tsk); | 
 | 152 | 		memcpy(fpu, &tsk->thread.i387.fxsave, sizeof(struct user_i387_struct)); 	 | 
 | 153 | } | 
 | 154 | 	return fpvalid; | 
 | 155 | } |