]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blob - arch/mn10300/kernel/fpu.c
efi/arm: Fix boot crash with CONFIG_CPUMASK_OFFSTACK=y
[mirror_ubuntu-artful-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 <linux/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 }