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 | } |