| 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 */ | 
| Al Viro | e4f17c4 | 2006-01-12 01:05:38 -0800 | [diff] [blame] | 98 | if (task_thread_info(tsk)->status & TS_USEDFPU) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 | } |