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
16 struct task_struct *fpu_state_owner;
17
18 /*
19 * handle an exception due to the FPU being disabled
20 */
fpu_disabled(struct pt_regs * regs,enum exception_code code)21 asmlinkage void fpu_disabled(struct pt_regs *regs, enum exception_code code)
22 {
23 struct task_struct *tsk = current;
24
25 if (!user_mode(regs))
26 die_if_no_fixup("An FPU Disabled exception happened in"
27 " kernel space\n",
28 regs, code);
29
30 #ifdef CONFIG_FPU
31 preempt_disable();
32
33 /* transfer the last process's FPU state to memory */
34 if (fpu_state_owner) {
35 fpu_save(&fpu_state_owner->thread.fpu_state);
36 fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE;
37 }
38
39 /* the current process now owns the FPU state */
40 fpu_state_owner = tsk;
41 regs->epsw |= EPSW_FE;
42
43 /* load the FPU with the current process's FPU state or invent a new
44 * clean one if the process doesn't have one */
45 if (is_using_fpu(tsk)) {
46 fpu_restore(&tsk->thread.fpu_state);
47 } else {
48 fpu_init_state();
49 set_using_fpu(tsk);
50 }
51
52 preempt_enable();
53 #else
54 {
55 siginfo_t info;
56
57 info.si_signo = SIGFPE;
58 info.si_errno = 0;
59 info.si_addr = (void *) tsk->thread.uregs->pc;
60 info.si_code = FPE_FLTINV;
61
62 force_sig_info(SIGFPE, &info, tsk);
63 }
64 #endif /* CONFIG_FPU */
65 }
66
67 /*
68 * handle an FPU operational exception
69 * - there's a possibility that if the FPU is asynchronous, the signal might
70 * be meant for a process other than the current one
71 */
fpu_exception(struct pt_regs * regs,enum exception_code code)72 asmlinkage void fpu_exception(struct pt_regs *regs, enum exception_code code)
73 {
74 struct task_struct *tsk = fpu_state_owner;
75 siginfo_t info;
76
77 if (!user_mode(regs))
78 die_if_no_fixup("An FPU Operation exception happened in"
79 " kernel space\n",
80 regs, code);
81
82 if (!tsk)
83 die_if_no_fixup("An FPU Operation exception happened,"
84 " but the FPU is not in use",
85 regs, code);
86
87 info.si_signo = SIGFPE;
88 info.si_errno = 0;
89 info.si_addr = (void *) tsk->thread.uregs->pc;
90 info.si_code = FPE_FLTINV;
91
92 #ifdef CONFIG_FPU
93 {
94 u32 fpcr;
95
96 /* get FPCR (we need to enable the FPU whilst we do this) */
97 asm volatile(" or %1,epsw \n"
98 #ifdef CONFIG_MN10300_PROC_MN103E010
99 " nop \n"
100 " nop \n"
101 " nop \n"
102 #endif
103 " fmov fpcr,%0 \n"
104 #ifdef CONFIG_MN10300_PROC_MN103E010
105 " nop \n"
106 " nop \n"
107 " nop \n"
108 #endif
109 " and %2,epsw \n"
110 : "=&d"(fpcr)
111 : "i"(EPSW_FE), "i"(~EPSW_FE)
112 );
113
114 if (fpcr & FPCR_EC_Z)
115 info.si_code = FPE_FLTDIV;
116 else if (fpcr & FPCR_EC_O)
117 info.si_code = FPE_FLTOVF;
118 else if (fpcr & FPCR_EC_U)
119 info.si_code = FPE_FLTUND;
120 else if (fpcr & FPCR_EC_I)
121 info.si_code = FPE_FLTRES;
122 }
123 #endif
124
125 force_sig_info(SIGFPE, &info, tsk);
126 }
127
128 /*
129 * save the FPU state to a signal context
130 */
fpu_setup_sigcontext(struct fpucontext * fpucontext)131 int fpu_setup_sigcontext(struct fpucontext *fpucontext)
132 {
133 #ifdef CONFIG_FPU
134 struct task_struct *tsk = current;
135
136 if (!is_using_fpu(tsk))
137 return 0;
138
139 /* transfer the current FPU state to memory and cause fpu_init() to be
140 * triggered by the next attempted FPU operation by the current
141 * process.
142 */
143 preempt_disable();
144
145 if (fpu_state_owner == tsk) {
146 fpu_save(&tsk->thread.fpu_state);
147 fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE;
148 fpu_state_owner = NULL;
149 }
150
151 preempt_enable();
152
153 /* we no longer have a valid current FPU state */
154 clear_using_fpu(tsk);
155
156 /* transfer the saved FPU state onto the userspace stack */
157 if (copy_to_user(fpucontext,
158 &tsk->thread.fpu_state,
159 min(sizeof(struct fpu_state_struct),
160 sizeof(struct fpucontext))))
161 return -1;
162
163 return 1;
164 #else
165 return 0;
166 #endif
167 }
168
169 /*
170 * kill a process's FPU state during restoration after signal handling
171 */
fpu_kill_state(struct task_struct * tsk)172 void fpu_kill_state(struct task_struct *tsk)
173 {
174 #ifdef CONFIG_FPU
175 /* disown anything left in the FPU */
176 preempt_disable();
177
178 if (fpu_state_owner == tsk) {
179 fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE;
180 fpu_state_owner = NULL;
181 }
182
183 preempt_enable();
184 #endif
185 /* we no longer have a valid current FPU state */
186 clear_using_fpu(tsk);
187 }
188
189 /*
190 * restore the FPU state from a signal context
191 */
fpu_restore_sigcontext(struct fpucontext * fpucontext)192 int fpu_restore_sigcontext(struct fpucontext *fpucontext)
193 {
194 struct task_struct *tsk = current;
195 int ret;
196
197 /* load up the old FPU state */
198 ret = copy_from_user(&tsk->thread.fpu_state,
199 fpucontext,
200 min(sizeof(struct fpu_state_struct),
201 sizeof(struct fpucontext)));
202 if (!ret)
203 set_using_fpu(tsk);
204
205 return ret;
206 }
207
208 /*
209 * fill in the FPU structure for a core dump
210 */
dump_fpu(struct pt_regs * regs,elf_fpregset_t * fpreg)211 int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpreg)
212 {
213 struct task_struct *tsk = current;
214 int fpvalid;
215
216 fpvalid = is_using_fpu(tsk);
217 if (fpvalid) {
218 unlazy_fpu(tsk);
219 memcpy(fpreg, &tsk->thread.fpu_state, sizeof(*fpreg));
220 }
221
222 return fpvalid;
223 }
224