PageRenderTime 31ms CodeModel.GetById 20ms app.highlight 9ms RepoModel.GetById 0ms app.codeStats 0ms

/arch/mn10300/kernel/fpu.c

https://bitbucket.org/cresqo/cm7-p500-kernel
C | 223 lines | 144 code | 35 blank | 44 comment | 18 complexity | 27ecb500cbf336308b2c40826183e632 MD5 | raw file
Possible License(s): LGPL-2.0, AGPL-1.0, GPL-2.0
  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}