xref: /openbmc/linux/arch/mips/include/asm/fpu.h (revision 31af04cd)
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 */
333