Merge remote-tracking branch 'asoc/fix/core' into tmp
[platform/adaptation/renesas_rcar/renesas_kernel.git] / arch / mn10300 / kernel / fpu.c
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 #ifdef CONFIG_LAZY_SAVE_FPU
17 struct task_struct *fpu_state_owner;
18 #endif
19
20 /*
21  * error functions in FPU disabled exception
22  */
23 asmlinkage void fpu_disabled_in_kernel(struct pt_regs *regs)
24 {
25         die_if_no_fixup("An FPU Disabled exception happened in kernel space\n",
26                         regs, EXCEP_FPU_DISABLED);
27 }
28
29 /*
30  * handle an FPU operational exception
31  * - there's a possibility that if the FPU is asynchronous, the signal might
32  *   be meant for a process other than the current one
33  */
34 asmlinkage void fpu_exception(struct pt_regs *regs, enum exception_code code)
35 {
36         struct task_struct *tsk = current;
37         siginfo_t info;
38         u32 fpcr;
39
40         if (!user_mode(regs))
41                 die_if_no_fixup("An FPU Operation exception happened in"
42                                 " kernel space\n",
43                                 regs, code);
44
45         if (!is_using_fpu(tsk))
46                 die_if_no_fixup("An FPU Operation exception happened,"
47                                 " but the FPU is not in use",
48                                 regs, code);
49
50         info.si_signo = SIGFPE;
51         info.si_errno = 0;
52         info.si_addr = (void *) tsk->thread.uregs->pc;
53         info.si_code = FPE_FLTINV;
54
55         unlazy_fpu(tsk);
56
57         fpcr = tsk->thread.fpu_state.fpcr;
58
59         if (fpcr & FPCR_EC_Z)
60                 info.si_code = FPE_FLTDIV;
61         else if (fpcr & FPCR_EC_O)
62                 info.si_code = FPE_FLTOVF;
63         else if (fpcr & FPCR_EC_U)
64                 info.si_code = FPE_FLTUND;
65         else if (fpcr & FPCR_EC_I)
66                 info.si_code = FPE_FLTRES;
67
68         force_sig_info(SIGFPE, &info, tsk);
69 }
70
71 /*
72  * save the FPU state to a signal context
73  */
74 int fpu_setup_sigcontext(struct fpucontext *fpucontext)
75 {
76         struct task_struct *tsk = current;
77
78         if (!is_using_fpu(tsk))
79                 return 0;
80
81         /* transfer the current FPU state to memory and cause fpu_init() to be
82          * triggered by the next attempted FPU operation by the current
83          * process.
84          */
85         preempt_disable();
86
87 #ifndef CONFIG_LAZY_SAVE_FPU
88         if (tsk->thread.fpu_flags & THREAD_HAS_FPU) {
89                 fpu_save(&tsk->thread.fpu_state);
90                 tsk->thread.uregs->epsw &= ~EPSW_FE;
91                 tsk->thread.fpu_flags &= ~THREAD_HAS_FPU;
92         }
93 #else /* !CONFIG_LAZY_SAVE_FPU */
94         if (fpu_state_owner == tsk) {
95                 fpu_save(&tsk->thread.fpu_state);
96                 fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE;
97                 fpu_state_owner = NULL;
98         }
99 #endif /* !CONFIG_LAZY_SAVE_FPU */
100
101         preempt_enable();
102
103         /* we no longer have a valid current FPU state */
104         clear_using_fpu(tsk);
105
106         /* transfer the saved FPU state onto the userspace stack */
107         if (copy_to_user(fpucontext,
108                          &tsk->thread.fpu_state,
109                          min(sizeof(struct fpu_state_struct),
110                              sizeof(struct fpucontext))))
111                 return -1;
112
113         return 1;
114 }
115
116 /*
117  * kill a process's FPU state during restoration after signal handling
118  */
119 void fpu_kill_state(struct task_struct *tsk)
120 {
121         /* disown anything left in the FPU */
122         preempt_disable();
123
124 #ifndef CONFIG_LAZY_SAVE_FPU
125         if (tsk->thread.fpu_flags & THREAD_HAS_FPU) {
126                 tsk->thread.uregs->epsw &= ~EPSW_FE;
127                 tsk->thread.fpu_flags &= ~THREAD_HAS_FPU;
128         }
129 #else /* !CONFIG_LAZY_SAVE_FPU */
130         if (fpu_state_owner == tsk) {
131                 fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE;
132                 fpu_state_owner = NULL;
133         }
134 #endif /* !CONFIG_LAZY_SAVE_FPU */
135
136         preempt_enable();
137
138         /* we no longer have a valid current FPU state */
139         clear_using_fpu(tsk);
140 }
141
142 /*
143  * restore the FPU state from a signal context
144  */
145 int fpu_restore_sigcontext(struct fpucontext *fpucontext)
146 {
147         struct task_struct *tsk = current;
148         int ret;
149
150         /* load up the old FPU state */
151         ret = copy_from_user(&tsk->thread.fpu_state, fpucontext,
152                              min(sizeof(struct fpu_state_struct),
153                                  sizeof(struct fpucontext)));
154         if (!ret)
155                 set_using_fpu(tsk);
156
157         return ret;
158 }
159
160 /*
161  * fill in the FPU structure for a core dump
162  */
163 int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpreg)
164 {
165         struct task_struct *tsk = current;
166         int fpvalid;
167
168         fpvalid = is_using_fpu(tsk);
169         if (fpvalid) {
170                 unlazy_fpu(tsk);
171                 memcpy(fpreg, &tsk->thread.fpu_state, sizeof(*fpreg));
172         }
173
174         return fpvalid;
175 }