blob: 5f9c3fa19a85fb4459987789229387352cf1a809 [file] [log] [blame]
David Howellsb920de12008-02-08 04:19:31 -08001/* 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>
Akira Takeuchi278d91c2010-10-27 17:28:52 +010015#include <asm/system.h>
David Howellsb920de12008-02-08 04:19:31 -080016
Akira Takeuchi278d91c2010-10-27 17:28:52 +010017#ifdef CONFIG_LAZY_SAVE_FPU
David Howellsb920de12008-02-08 04:19:31 -080018struct task_struct *fpu_state_owner;
Akira Takeuchi278d91c2010-10-27 17:28:52 +010019#endif
David Howellsb920de12008-02-08 04:19:31 -080020
21/*
Akira Takeuchi278d91c2010-10-27 17:28:52 +010022 * error functions in FPU disabled exception
David Howellsb920de12008-02-08 04:19:31 -080023 */
Akira Takeuchi278d91c2010-10-27 17:28:52 +010024asmlinkage void fpu_disabled_in_kernel(struct pt_regs *regs)
David Howellsb920de12008-02-08 04:19:31 -080025{
Akira Takeuchi278d91c2010-10-27 17:28:52 +010026 die_if_no_fixup("An FPU Disabled exception happened in kernel space\n",
27 regs, EXCEP_FPU_DISABLED);
David Howellsb920de12008-02-08 04:19:31 -080028}
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 */
35asmlinkage void fpu_exception(struct pt_regs *regs, enum exception_code code)
36{
Akira Takeuchi278d91c2010-10-27 17:28:52 +010037 struct task_struct *tsk = current;
David Howellsb920de12008-02-08 04:19:31 -080038 siginfo_t info;
Akira Takeuchi278d91c2010-10-27 17:28:52 +010039 u32 fpcr;
David Howellsb920de12008-02-08 04:19:31 -080040
41 if (!user_mode(regs))
42 die_if_no_fixup("An FPU Operation exception happened in"
43 " kernel space\n",
44 regs, code);
45
Akira Takeuchi278d91c2010-10-27 17:28:52 +010046 if (!is_using_fpu(tsk))
David Howellsb920de12008-02-08 04:19:31 -080047 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
Akira Takeuchi278d91c2010-10-27 17:28:52 +010056 unlazy_fpu(tsk);
David Howellsb920de12008-02-08 04:19:31 -080057
Akira Takeuchi278d91c2010-10-27 17:28:52 +010058 fpcr = tsk->thread.fpu_state.fpcr;
David Howellsb920de12008-02-08 04:19:31 -080059
Akira Takeuchi278d91c2010-10-27 17:28:52 +010060 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;
David Howellsb920de12008-02-08 04:19:31 -080068
69 force_sig_info(SIGFPE, &info, tsk);
70}
71
72/*
Akira Takeuchi278d91c2010-10-27 17:28:52 +010073 * handle an FPU invalid_op exception
74 * - Derived from DO_EINFO() macro in arch/mn10300/kernel/traps.c
75 */
76asmlinkage 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/*
David Howellsb920de12008-02-08 04:19:31 -080091 * save the FPU state to a signal context
92 */
93int fpu_setup_sigcontext(struct fpucontext *fpucontext)
94{
David Howellsb920de12008-02-08 04:19:31 -080095 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
Akira Takeuchi278d91c2010-10-27 17:28:52 +0100106#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 */
David Howellsb920de12008-02-08 04:19:31 -0800113 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 }
Akira Takeuchi278d91c2010-10-27 17:28:52 +0100118#endif /* !CONFIG_LAZY_SAVE_FPU */
David Howellsb920de12008-02-08 04:19:31 -0800119
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;
David Howellsb920de12008-02-08 04:19:31 -0800133}
134
135/*
136 * kill a process's FPU state during restoration after signal handling
137 */
138void fpu_kill_state(struct task_struct *tsk)
139{
David Howellsb920de12008-02-08 04:19:31 -0800140 /* disown anything left in the FPU */
141 preempt_disable();
142
Akira Takeuchi278d91c2010-10-27 17:28:52 +0100143#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 */
David Howellsb920de12008-02-08 04:19:31 -0800149 if (fpu_state_owner == tsk) {
150 fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE;
151 fpu_state_owner = NULL;
152 }
Akira Takeuchi278d91c2010-10-27 17:28:52 +0100153#endif /* !CONFIG_LAZY_SAVE_FPU */
David Howellsb920de12008-02-08 04:19:31 -0800154
155 preempt_enable();
Akira Takeuchi278d91c2010-10-27 17:28:52 +0100156
David Howellsb920de12008-02-08 04:19:31 -0800157 /* 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 */
164int fpu_restore_sigcontext(struct fpucontext *fpucontext)
165{
166 struct task_struct *tsk = current;
167 int ret;
168
169 /* load up the old FPU state */
Akira Takeuchi278d91c2010-10-27 17:28:52 +0100170 ret = copy_from_user(&tsk->thread.fpu_state, fpucontext,
David Howellsb920de12008-02-08 04:19:31 -0800171 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 */
182int 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}