Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[pandora-kernel.git] / arch / mn10300 / kernel / fpu.c
1 /* MN10300 FPU management
2  *
3  * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
4  * Written by David Howells (dhowells@redhat.com)
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public Licence
8  * as published by the Free Software Foundation; either version
9  * 2 of the Licence, or (at your option) any later version.
10  */
11 #include <asm/uaccess.h>
12 #include <asm/fpu.h>
13 #include <asm/elf.h>
14 #include <asm/exceptions.h>
15 #include <asm/system.h>
16
17 #ifdef CONFIG_LAZY_SAVE_FPU
18 struct task_struct *fpu_state_owner;
19 #endif
20
21 /*
22  * error functions in FPU disabled exception
23  */
24 asmlinkage void fpu_disabled_in_kernel(struct pt_regs *regs)
25 {
26         die_if_no_fixup("An FPU Disabled exception happened in kernel space\n",
27                         regs, EXCEP_FPU_DISABLED);
28 }
29
30 /*
31  * handle an FPU operational exception
32  * - there's a possibility that if the FPU is asynchronous, the signal might
33  *   be meant for a process other than the current one
34  */
35 asmlinkage void fpu_exception(struct pt_regs *regs, enum exception_code code)
36 {
37         struct task_struct *tsk = current;
38         siginfo_t info;
39         u32 fpcr;
40
41         if (!user_mode(regs))
42                 die_if_no_fixup("An FPU Operation exception happened in"
43                                 " kernel space\n",
44                                 regs, code);
45
46         if (!is_using_fpu(tsk))
47                 die_if_no_fixup("An FPU Operation exception happened,"
48                                 " but the FPU is not in use",
49                                 regs, code);
50
51         info.si_signo = SIGFPE;
52         info.si_errno = 0;
53         info.si_addr = (void *) tsk->thread.uregs->pc;
54         info.si_code = FPE_FLTINV;
55
56         unlazy_fpu(tsk);
57
58         fpcr = tsk->thread.fpu_state.fpcr;
59
60         if (fpcr & FPCR_EC_Z)
61                 info.si_code = FPE_FLTDIV;
62         else if (fpcr & FPCR_EC_O)
63                 info.si_code = FPE_FLTOVF;
64         else if (fpcr & FPCR_EC_U)
65                 info.si_code = FPE_FLTUND;
66         else if (fpcr & FPCR_EC_I)
67                 info.si_code = FPE_FLTRES;
68
69         force_sig_info(SIGFPE, &info, tsk);
70 }
71
72 /*
73  * handle an FPU invalid_op exception
74  * - Derived from DO_EINFO() macro in arch/mn10300/kernel/traps.c
75  */
76 asmlinkage void fpu_invalid_op(struct pt_regs *regs, enum exception_code code)
77 {
78         siginfo_t info;
79
80         if (!user_mode(regs))
81                 die_if_no_fixup("FPU invalid opcode", regs, code);
82
83         info.si_signo = SIGILL;
84         info.si_errno = 0;
85         info.si_code = ILL_COPROC;
86         info.si_addr = (void *) regs->pc;
87         force_sig_info(info.si_signo, &info, current);
88 }
89
90 /*
91  * save the FPU state to a signal context
92  */
93 int fpu_setup_sigcontext(struct fpucontext *fpucontext)
94 {
95         struct task_struct *tsk = current;
96
97         if (!is_using_fpu(tsk))
98                 return 0;
99
100         /* transfer the current FPU state to memory and cause fpu_init() to be
101          * triggered by the next attempted FPU operation by the current
102          * process.
103          */
104         preempt_disable();
105
106 #ifndef CONFIG_LAZY_SAVE_FPU
107         if (tsk->thread.fpu_flags & THREAD_HAS_FPU) {
108                 fpu_save(&tsk->thread.fpu_state);
109                 tsk->thread.uregs->epsw &= ~EPSW_FE;
110                 tsk->thread.fpu_flags &= ~THREAD_HAS_FPU;
111         }
112 #else /* !CONFIG_LAZY_SAVE_FPU */
113         if (fpu_state_owner == tsk) {
114                 fpu_save(&tsk->thread.fpu_state);
115                 fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE;
116                 fpu_state_owner = NULL;
117         }
118 #endif /* !CONFIG_LAZY_SAVE_FPU */
119
120         preempt_enable();
121
122         /* we no longer have a valid current FPU state */
123         clear_using_fpu(tsk);
124
125         /* transfer the saved FPU state onto the userspace stack */
126         if (copy_to_user(fpucontext,
127                          &tsk->thread.fpu_state,
128                          min(sizeof(struct fpu_state_struct),
129                              sizeof(struct fpucontext))))
130                 return -1;
131
132         return 1;
133 }
134
135 /*
136  * kill a process's FPU state during restoration after signal handling
137  */
138 void fpu_kill_state(struct task_struct *tsk)
139 {
140         /* disown anything left in the FPU */
141         preempt_disable();
142
143 #ifndef CONFIG_LAZY_SAVE_FPU
144         if (tsk->thread.fpu_flags & THREAD_HAS_FPU) {
145                 tsk->thread.uregs->epsw &= ~EPSW_FE;
146                 tsk->thread.fpu_flags &= ~THREAD_HAS_FPU;
147         }
148 #else /* !CONFIG_LAZY_SAVE_FPU */
149         if (fpu_state_owner == tsk) {
150                 fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE;
151                 fpu_state_owner = NULL;
152         }
153 #endif /* !CONFIG_LAZY_SAVE_FPU */
154
155         preempt_enable();
156
157         /* we no longer have a valid current FPU state */
158         clear_using_fpu(tsk);
159 }
160
161 /*
162  * restore the FPU state from a signal context
163  */
164 int fpu_restore_sigcontext(struct fpucontext *fpucontext)
165 {
166         struct task_struct *tsk = current;
167         int ret;
168
169         /* load up the old FPU state */
170         ret = copy_from_user(&tsk->thread.fpu_state, fpucontext,
171                              min(sizeof(struct fpu_state_struct),
172                                  sizeof(struct fpucontext)));
173         if (!ret)
174                 set_using_fpu(tsk);
175
176         return ret;
177 }
178
179 /*
180  * fill in the FPU structure for a core dump
181  */
182 int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpreg)
183 {
184         struct task_struct *tsk = current;
185         int fpvalid;
186
187         fpvalid = is_using_fpu(tsk);
188         if (fpvalid) {
189                 unlazy_fpu(tsk);
190                 memcpy(fpreg, &tsk->thread.fpu_state, sizeof(*fpreg));
191         }
192
193         return fpvalid;
194 }