• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/arch/mn10300/kernel/
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
16struct task_struct *fpu_state_owner;
17
18/*
19 * handle an exception due to the FPU being disabled
20 */
21asmlinkage 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 */
72asmlinkage 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 */
131int 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 */
172void 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 */
192int 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 */
211int 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