• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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