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