Merge tag 'selinux-pr-20181224' of git://git.kernel.org/pub/scm/linux/kernel/git...
[sfrench/cifs-2.6.git] / arch / mips / include / asm / fpu.h
1 /*
2  * Copyright (C) 2002 MontaVista Software Inc.
3  * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation;  either version 2 of the  License, or (at your
8  * option) any later version.
9  */
10 #ifndef _ASM_FPU_H
11 #define _ASM_FPU_H
12
13 #include <linux/sched.h>
14 #include <linux/sched/task_stack.h>
15 #include <linux/ptrace.h>
16 #include <linux/thread_info.h>
17 #include <linux/bitops.h>
18
19 #include <asm/mipsregs.h>
20 #include <asm/cpu.h>
21 #include <asm/cpu-features.h>
22 #include <asm/fpu_emulator.h>
23 #include <asm/hazards.h>
24 #include <asm/ptrace.h>
25 #include <asm/processor.h>
26 #include <asm/current.h>
27 #include <asm/msa.h>
28
29 #ifdef CONFIG_MIPS_MT_FPAFF
30 #include <asm/mips_mt.h>
31 #endif
32
33 /*
34  * This enum specifies a mode in which we want the FPU to operate, for cores
35  * which implement the Status.FR bit. Note that the bottom bit of the value
36  * purposefully matches the desired value of the Status.FR bit.
37  */
38 enum fpu_mode {
39         FPU_32BIT = 0,          /* FR = 0 */
40         FPU_64BIT,              /* FR = 1, FRE = 0 */
41         FPU_AS_IS,
42         FPU_HYBRID,             /* FR = 1, FRE = 1 */
43
44 #define FPU_FR_MASK             0x1
45 };
46
47 #ifdef CONFIG_MIPS_FP_SUPPORT
48
49 extern void _save_fp(struct task_struct *);
50 extern void _restore_fp(struct task_struct *);
51
52 #define __disable_fpu()                                                 \
53 do {                                                                    \
54         clear_c0_status(ST0_CU1);                                       \
55         disable_fpu_hazard();                                           \
56 } while (0)
57
58 static inline int __enable_fpu(enum fpu_mode mode)
59 {
60         int fr;
61
62         switch (mode) {
63         case FPU_AS_IS:
64                 /* just enable the FPU in its current mode */
65                 set_c0_status(ST0_CU1);
66                 enable_fpu_hazard();
67                 return 0;
68
69         case FPU_HYBRID:
70                 if (!cpu_has_fre)
71                         return SIGFPE;
72
73                 /* set FRE */
74                 set_c0_config5(MIPS_CONF5_FRE);
75                 goto fr_common;
76
77         case FPU_64BIT:
78 #if !(defined(CONFIG_CPU_MIPSR2) || defined(CONFIG_CPU_MIPSR6) \
79       || defined(CONFIG_64BIT))
80                 /* we only have a 32-bit FPU */
81                 return SIGFPE;
82 #endif
83                 /* fall through */
84         case FPU_32BIT:
85                 if (cpu_has_fre) {
86                         /* clear FRE */
87                         clear_c0_config5(MIPS_CONF5_FRE);
88                 }
89 fr_common:
90                 /* set CU1 & change FR appropriately */
91                 fr = (int)mode & FPU_FR_MASK;
92                 change_c0_status(ST0_CU1 | ST0_FR, ST0_CU1 | (fr ? ST0_FR : 0));
93                 enable_fpu_hazard();
94
95                 /* check FR has the desired value */
96                 if (!!(read_c0_status() & ST0_FR) == !!fr)
97                         return 0;
98
99                 /* unsupported FR value */
100                 __disable_fpu();
101                 return SIGFPE;
102
103         default:
104                 BUG();
105         }
106
107         return SIGFPE;
108 }
109
110 #define clear_fpu_owner()       clear_thread_flag(TIF_USEDFPU)
111
112 static inline int __is_fpu_owner(void)
113 {
114         return test_thread_flag(TIF_USEDFPU);
115 }
116
117 static inline int is_fpu_owner(void)
118 {
119         return cpu_has_fpu && __is_fpu_owner();
120 }
121
122 static inline int __own_fpu(void)
123 {
124         enum fpu_mode mode;
125         int ret;
126
127         if (test_thread_flag(TIF_HYBRID_FPREGS))
128                 mode = FPU_HYBRID;
129         else
130                 mode = !test_thread_flag(TIF_32BIT_FPREGS);
131
132         ret = __enable_fpu(mode);
133         if (ret)
134                 return ret;
135
136         KSTK_STATUS(current) |= ST0_CU1;
137         if (mode == FPU_64BIT || mode == FPU_HYBRID)
138                 KSTK_STATUS(current) |= ST0_FR;
139         else /* mode == FPU_32BIT */
140                 KSTK_STATUS(current) &= ~ST0_FR;
141
142         set_thread_flag(TIF_USEDFPU);
143         return 0;
144 }
145
146 static inline int own_fpu_inatomic(int restore)
147 {
148         int ret = 0;
149
150         if (cpu_has_fpu && !__is_fpu_owner()) {
151                 ret = __own_fpu();
152                 if (restore && !ret)
153                         _restore_fp(current);
154         }
155         return ret;
156 }
157
158 static inline int own_fpu(int restore)
159 {
160         int ret;
161
162         preempt_disable();
163         ret = own_fpu_inatomic(restore);
164         preempt_enable();
165         return ret;
166 }
167
168 static inline void lose_fpu_inatomic(int save, struct task_struct *tsk)
169 {
170         if (is_msa_enabled()) {
171                 if (save) {
172                         save_msa(tsk);
173                         tsk->thread.fpu.fcr31 =
174                                         read_32bit_cp1_register(CP1_STATUS);
175                 }
176                 disable_msa();
177                 clear_tsk_thread_flag(tsk, TIF_USEDMSA);
178                 __disable_fpu();
179         } else if (is_fpu_owner()) {
180                 if (save)
181                         _save_fp(tsk);
182                 __disable_fpu();
183         } else {
184                 /* FPU should not have been left enabled with no owner */
185                 WARN(read_c0_status() & ST0_CU1,
186                      "Orphaned FPU left enabled");
187         }
188         KSTK_STATUS(tsk) &= ~ST0_CU1;
189         clear_tsk_thread_flag(tsk, TIF_USEDFPU);
190 }
191
192 static inline void lose_fpu(int save)
193 {
194         preempt_disable();
195         lose_fpu_inatomic(save, current);
196         preempt_enable();
197 }
198
199 /**
200  * init_fp_ctx() - Initialize task FP context
201  * @target: The task whose FP context should be initialized.
202  *
203  * Initializes the FP context of the target task to sane default values if that
204  * target task does not already have valid FP context. Once the context has
205  * been initialized, the task will be marked as having used FP & thus having
206  * valid FP context.
207  *
208  * Returns: true if context is initialized, else false.
209  */
210 static inline bool init_fp_ctx(struct task_struct *target)
211 {
212         /* If FP has been used then the target already has context */
213         if (tsk_used_math(target))
214                 return false;
215
216         /* Begin with data registers set to all 1s... */
217         memset(&target->thread.fpu.fpr, ~0, sizeof(target->thread.fpu.fpr));
218
219         /* FCSR has been preset by `mips_set_personality_nan'.  */
220
221         /*
222          * Record that the target has "used" math, such that the context
223          * just initialised, and any modifications made by the caller,
224          * aren't discarded.
225          */
226         set_stopped_child_used_math(target);
227
228         return true;
229 }
230
231 static inline void save_fp(struct task_struct *tsk)
232 {
233         if (cpu_has_fpu)
234                 _save_fp(tsk);
235 }
236
237 static inline void restore_fp(struct task_struct *tsk)
238 {
239         if (cpu_has_fpu)
240                 _restore_fp(tsk);
241 }
242
243 static inline union fpureg *get_fpu_regs(struct task_struct *tsk)
244 {
245         if (tsk == current) {
246                 preempt_disable();
247                 if (is_fpu_owner())
248                         _save_fp(current);
249                 preempt_enable();
250         }
251
252         return tsk->thread.fpu.fpr;
253 }
254
255 #else /* !CONFIG_MIPS_FP_SUPPORT */
256
257 /*
258  * When FP support is disabled we provide only a minimal set of stub functions
259  * to avoid callers needing to care too much about CONFIG_MIPS_FP_SUPPORT.
260  */
261
262 static inline int __enable_fpu(enum fpu_mode mode)
263 {
264         return SIGILL;
265 }
266
267 static inline void __disable_fpu(void)
268 {
269         /* no-op */
270 }
271
272
273 static inline int is_fpu_owner(void)
274 {
275         return 0;
276 }
277
278 static inline void clear_fpu_owner(void)
279 {
280         /* no-op */
281 }
282
283 static inline int own_fpu_inatomic(int restore)
284 {
285         return SIGILL;
286 }
287
288 static inline int own_fpu(int restore)
289 {
290         return SIGILL;
291 }
292
293 static inline void lose_fpu_inatomic(int save, struct task_struct *tsk)
294 {
295         /* no-op */
296 }
297
298 static inline void lose_fpu(int save)
299 {
300         /* no-op */
301 }
302
303 static inline bool init_fp_ctx(struct task_struct *target)
304 {
305         return false;
306 }
307
308 /*
309  * The following functions should only be called in paths where we know that FP
310  * support is enabled, typically a path where own_fpu() or __enable_fpu() have
311  * returned successfully. When CONFIG_MIPS_FP_SUPPORT=n it is known at compile
312  * time that this should never happen, so calls to these functions should be
313  * optimized away & never actually be emitted.
314  */
315
316 extern void save_fp(struct task_struct *tsk)
317         __compiletime_error("save_fp() should not be called when CONFIG_MIPS_FP_SUPPORT=n");
318
319 extern void _save_fp(struct task_struct *)
320         __compiletime_error("_save_fp() should not be called when CONFIG_MIPS_FP_SUPPORT=n");
321
322 extern void restore_fp(struct task_struct *tsk)
323         __compiletime_error("restore_fp() should not be called when CONFIG_MIPS_FP_SUPPORT=n");
324
325 extern void _restore_fp(struct task_struct *)
326         __compiletime_error("_restore_fp() should not be called when CONFIG_MIPS_FP_SUPPORT=n");
327
328 extern union fpureg *get_fpu_regs(struct task_struct *tsk)
329         __compiletime_error("get_fpu_regs() should not be called when CONFIG_MIPS_FP_SUPPORT=n");
330
331 #endif /* !CONFIG_MIPS_FP_SUPPORT */
332 #endif /* _ASM_FPU_H */