blob: 9476e0498d59a8d9babf48d97443a41f6d6a3ccb [file] [log] [blame]
Thomas Gleixner2874c5f2019-05-27 08:55:01 +02001/* SPDX-License-Identifier: GPL-2.0-or-later */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
3 * Copyright (C) 2002 MontaVista Software Inc.
4 * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 */
6#ifndef _ASM_FPU_H
7#define _ASM_FPU_H
8
Linus Torvalds1da177e2005-04-16 15:20:36 -07009#include <linux/sched.h>
Ingo Molnar68db0cf2017-02-08 18:51:37 +010010#include <linux/sched/task_stack.h>
Arnd Bergmannfc699102017-03-08 08:29:31 +010011#include <linux/ptrace.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070012#include <linux/thread_info.h>
Jiri Slaby1977f032007-10-18 23:40:25 -070013#include <linux/bitops.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070014
15#include <asm/mipsregs.h>
16#include <asm/cpu.h>
17#include <asm/cpu-features.h>
Ralf Baechlee0cc3a42014-04-28 22:34:01 +020018#include <asm/fpu_emulator.h>
Chris Dearman0b624952007-05-08 16:09:13 +010019#include <asm/hazards.h>
James Hogan0c7e2bc2017-03-04 00:32:03 +000020#include <asm/ptrace.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070021#include <asm/processor.h>
22#include <asm/current.h>
Paul Burton33c771b2014-07-11 16:44:30 +010023#include <asm/msa.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070024
Ralf Baechlef088fc82006-04-05 09:45:47 +010025#ifdef CONFIG_MIPS_MT_FPAFF
26#include <asm/mips_mt.h>
27#endif
28
Paul Burton597ce172013-11-22 13:12:07 +000029/*
30 * This enum specifies a mode in which we want the FPU to operate, for cores
Paul Burton4227a2d2014-09-11 08:30:20 +010031 * which implement the Status.FR bit. Note that the bottom bit of the value
32 * purposefully matches the desired value of the Status.FR bit.
Paul Burton597ce172013-11-22 13:12:07 +000033 */
34enum fpu_mode {
35 FPU_32BIT = 0, /* FR = 0 */
Paul Burton4227a2d2014-09-11 08:30:20 +010036 FPU_64BIT, /* FR = 1, FRE = 0 */
Paul Burton597ce172013-11-22 13:12:07 +000037 FPU_AS_IS,
Paul Burton4227a2d2014-09-11 08:30:20 +010038 FPU_HYBRID, /* FR = 1, FRE = 1 */
39
40#define FPU_FR_MASK 0x1
Paul Burton597ce172013-11-22 13:12:07 +000041};
42
Paul Burton9ec55932018-11-07 23:14:04 +000043#ifdef CONFIG_MIPS_FP_SUPPORT
44
45extern void _save_fp(struct task_struct *);
46extern void _restore_fp(struct task_struct *);
47
Paul Burton84ab45b32015-01-30 12:09:37 +000048#define __disable_fpu() \
49do { \
50 clear_c0_status(ST0_CU1); \
51 disable_fpu_hazard(); \
52} while (0)
53
Paul Burton597ce172013-11-22 13:12:07 +000054static inline int __enable_fpu(enum fpu_mode mode)
55{
56 int fr;
57
58 switch (mode) {
59 case FPU_AS_IS:
60 /* just enable the FPU in its current mode */
61 set_c0_status(ST0_CU1);
62 enable_fpu_hazard();
63 return 0;
64
Paul Burton4227a2d2014-09-11 08:30:20 +010065 case FPU_HYBRID:
66 if (!cpu_has_fre)
67 return SIGFPE;
68
69 /* set FRE */
Ralf Baechled33e6fe2014-12-17 11:46:40 +010070 set_c0_config5(MIPS_CONF5_FRE);
Paul Burton4227a2d2014-09-11 08:30:20 +010071 goto fr_common;
72
Paul Burton597ce172013-11-22 13:12:07 +000073 case FPU_64BIT:
Markos Chandrasfcc53b52015-07-16 15:30:04 +010074#if !(defined(CONFIG_CPU_MIPSR2) || defined(CONFIG_CPU_MIPSR6) \
Markos Chandras6134d942015-01-30 10:20:28 +000075 || defined(CONFIG_64BIT))
Paul Burton597ce172013-11-22 13:12:07 +000076 /* we only have a 32-bit FPU */
77 return SIGFPE;
78#endif
79 /* fall through */
80 case FPU_32BIT:
Ralf Baechleb0c34f62014-12-17 11:39:30 +010081 if (cpu_has_fre) {
82 /* clear FRE */
Ralf Baechled33e6fe2014-12-17 11:46:40 +010083 clear_c0_config5(MIPS_CONF5_FRE);
Ralf Baechleb0c34f62014-12-17 11:39:30 +010084 }
Paul Burton4227a2d2014-09-11 08:30:20 +010085fr_common:
Paul Burton597ce172013-11-22 13:12:07 +000086 /* set CU1 & change FR appropriately */
Paul Burton4227a2d2014-09-11 08:30:20 +010087 fr = (int)mode & FPU_FR_MASK;
Paul Burton597ce172013-11-22 13:12:07 +000088 change_c0_status(ST0_CU1 | ST0_FR, ST0_CU1 | (fr ? ST0_FR : 0));
89 enable_fpu_hazard();
90
91 /* check FR has the desired value */
Paul Burton84ab45b32015-01-30 12:09:37 +000092 if (!!(read_c0_status() & ST0_FR) == !!fr)
93 return 0;
94
95 /* unsupported FR value */
96 __disable_fpu();
97 return SIGFPE;
Paul Burton597ce172013-11-22 13:12:07 +000098
99 default:
100 BUG();
101 }
Aaro Koskinen97b8b16b2014-02-05 22:05:44 +0200102
103 return SIGFPE;
Paul Burton597ce172013-11-22 13:12:07 +0000104}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106#define clear_fpu_owner() clear_thread_flag(TIF_USEDFPU)
107
Ralf Baechle1d74f6b2005-05-09 13:16:07 +0000108static inline int __is_fpu_owner(void)
109{
110 return test_thread_flag(TIF_USEDFPU);
111}
112
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113static inline int is_fpu_owner(void)
114{
Ralf Baechle1d74f6b2005-05-09 13:16:07 +0000115 return cpu_has_fpu && __is_fpu_owner();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116}
117
Paul Burton597ce172013-11-22 13:12:07 +0000118static inline int __own_fpu(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119{
Paul Burton597ce172013-11-22 13:12:07 +0000120 enum fpu_mode mode;
121 int ret;
122
Paul Burton4227a2d2014-09-11 08:30:20 +0100123 if (test_thread_flag(TIF_HYBRID_FPREGS))
124 mode = FPU_HYBRID;
125 else
126 mode = !test_thread_flag(TIF_32BIT_FPREGS);
127
Paul Burton597ce172013-11-22 13:12:07 +0000128 ret = __enable_fpu(mode);
129 if (ret)
130 return ret;
131
Atsushi Nemoto53dc8022007-03-10 01:07:45 +0900132 KSTK_STATUS(current) |= ST0_CU1;
Paul Burton4227a2d2014-09-11 08:30:20 +0100133 if (mode == FPU_64BIT || mode == FPU_HYBRID)
Paul Burton597ce172013-11-22 13:12:07 +0000134 KSTK_STATUS(current) |= ST0_FR;
135 else /* mode == FPU_32BIT */
136 KSTK_STATUS(current) &= ~ST0_FR;
137
Atsushi Nemoto53dc8022007-03-10 01:07:45 +0900138 set_thread_flag(TIF_USEDFPU);
Paul Burton597ce172013-11-22 13:12:07 +0000139 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140}
141
Paul Burton597ce172013-11-22 13:12:07 +0000142static inline int own_fpu_inatomic(int restore)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143{
Paul Burton597ce172013-11-22 13:12:07 +0000144 int ret = 0;
145
Atsushi Nemoto53dc8022007-03-10 01:07:45 +0900146 if (cpu_has_fpu && !__is_fpu_owner()) {
Paul Burton597ce172013-11-22 13:12:07 +0000147 ret = __own_fpu();
148 if (restore && !ret)
Atsushi Nemoto53dc8022007-03-10 01:07:45 +0900149 _restore_fp(current);
150 }
Paul Burton597ce172013-11-22 13:12:07 +0000151 return ret;
Atsushi Nemotofaea6232007-04-16 23:19:44 +0900152}
153
Paul Burton597ce172013-11-22 13:12:07 +0000154static inline int own_fpu(int restore)
Atsushi Nemotofaea6232007-04-16 23:19:44 +0900155{
Paul Burton597ce172013-11-22 13:12:07 +0000156 int ret;
157
Atsushi Nemotofaea6232007-04-16 23:19:44 +0900158 preempt_disable();
Paul Burton597ce172013-11-22 13:12:07 +0000159 ret = own_fpu_inatomic(restore);
Atsushi Nemoto53dc8022007-03-10 01:07:45 +0900160 preempt_enable();
Paul Burton597ce172013-11-22 13:12:07 +0000161 return ret;
Atsushi Nemoto53dc8022007-03-10 01:07:45 +0900162}
163
Paul Burton1a3d5952015-08-03 08:49:30 -0700164static inline void lose_fpu_inatomic(int save, struct task_struct *tsk)
Atsushi Nemoto53dc8022007-03-10 01:07:45 +0900165{
Paul Burton33c771b2014-07-11 16:44:30 +0100166 if (is_msa_enabled()) {
167 if (save) {
Paul Burton1a3d5952015-08-03 08:49:30 -0700168 save_msa(tsk);
169 tsk->thread.fpu.fcr31 =
Manuel Lauss842dfc12014-11-07 14:13:54 +0100170 read_32bit_cp1_register(CP1_STATUS);
Paul Burton33c771b2014-07-11 16:44:30 +0100171 }
172 disable_msa();
Paul Burton1a3d5952015-08-03 08:49:30 -0700173 clear_tsk_thread_flag(tsk, TIF_USEDMSA);
James Hoganacaf6a92015-02-25 13:08:05 +0000174 __disable_fpu();
Paul Burton33c771b2014-07-11 16:44:30 +0100175 } else if (is_fpu_owner()) {
Atsushi Nemoto53dc8022007-03-10 01:07:45 +0900176 if (save)
Paul Burton1a3d5952015-08-03 08:49:30 -0700177 _save_fp(tsk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178 __disable_fpu();
James Hogan00fe56d2016-02-01 13:50:37 +0000179 } else {
180 /* FPU should not have been left enabled with no owner */
181 WARN(read_c0_status() & ST0_CU1,
182 "Orphaned FPU left enabled");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 }
Paul Burton1a3d5952015-08-03 08:49:30 -0700184 KSTK_STATUS(tsk) &= ~ST0_CU1;
185 clear_tsk_thread_flag(tsk, TIF_USEDFPU);
186}
187
188static inline void lose_fpu(int save)
189{
190 preempt_disable();
191 lose_fpu_inatomic(save, current);
Atsushi Nemoto53dc8022007-03-10 01:07:45 +0900192 preempt_enable();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193}
194
Paul Burtoncc97ab22018-11-07 23:13:59 +0000195/**
196 * init_fp_ctx() - Initialize task FP context
197 * @target: The task whose FP context should be initialized.
198 *
199 * Initializes the FP context of the target task to sane default values if that
200 * target task does not already have valid FP context. Once the context has
201 * been initialized, the task will be marked as having used FP & thus having
202 * valid FP context.
203 *
204 * Returns: true if context is initialized, else false.
205 */
206static inline bool init_fp_ctx(struct task_struct *target)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207{
Paul Burtoncc97ab22018-11-07 23:13:59 +0000208 /* If FP has been used then the target already has context */
209 if (tsk_used_math(target))
210 return false;
Paul Burton597ce172013-11-22 13:12:07 +0000211
Paul Burtoncc97ab22018-11-07 23:13:59 +0000212 /* Begin with data registers set to all 1s... */
213 memset(&target->thread.fpu.fpr, ~0, sizeof(target->thread.fpu.fpr));
Ralf Baechleb0c34f62014-12-17 11:39:30 +0100214
Paul Burtoncc97ab22018-11-07 23:13:59 +0000215 /* FCSR has been preset by `mips_set_personality_nan'. */
Paul Burton4227a2d2014-09-11 08:30:20 +0100216
Paul Burtoncc97ab22018-11-07 23:13:59 +0000217 /*
218 * Record that the target has "used" math, such that the context
219 * just initialised, and any modifications made by the caller,
220 * aren't discarded.
221 */
222 set_stopped_child_used_math(target);
Paul Burton4227a2d2014-09-11 08:30:20 +0100223
Paul Burtoncc97ab22018-11-07 23:13:59 +0000224 return true;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225}
226
227static inline void save_fp(struct task_struct *tsk)
228{
229 if (cpu_has_fpu)
230 _save_fp(tsk);
231}
232
233static inline void restore_fp(struct task_struct *tsk)
234{
235 if (cpu_has_fpu)
236 _restore_fp(tsk);
237}
238
Paul Burtonbbd426f2014-02-13 11:26:41 +0000239static inline union fpureg *get_fpu_regs(struct task_struct *tsk)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240{
Atsushi Nemotoe04582b2006-10-09 00:10:01 +0900241 if (tsk == current) {
242 preempt_disable();
243 if (is_fpu_owner())
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 _save_fp(current);
Atsushi Nemotoe04582b2006-10-09 00:10:01 +0900245 preempt_enable();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246 }
247
Atsushi Nemotoeae89072006-05-16 01:26:03 +0900248 return tsk->thread.fpu.fpr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249}
250
Paul Burton9ec55932018-11-07 23:14:04 +0000251#else /* !CONFIG_MIPS_FP_SUPPORT */
252
253/*
254 * When FP support is disabled we provide only a minimal set of stub functions
255 * to avoid callers needing to care too much about CONFIG_MIPS_FP_SUPPORT.
256 */
257
258static inline int __enable_fpu(enum fpu_mode mode)
259{
260 return SIGILL;
261}
262
263static inline void __disable_fpu(void)
264{
265 /* no-op */
266}
267
268
269static inline int is_fpu_owner(void)
270{
271 return 0;
272}
273
274static inline void clear_fpu_owner(void)
275{
276 /* no-op */
277}
278
279static inline int own_fpu_inatomic(int restore)
280{
281 return SIGILL;
282}
283
284static inline int own_fpu(int restore)
285{
286 return SIGILL;
287}
288
289static inline void lose_fpu_inatomic(int save, struct task_struct *tsk)
290{
291 /* no-op */
292}
293
294static inline void lose_fpu(int save)
295{
296 /* no-op */
297}
298
299static inline bool init_fp_ctx(struct task_struct *target)
300{
301 return false;
302}
303
304/*
305 * The following functions should only be called in paths where we know that FP
306 * support is enabled, typically a path where own_fpu() or __enable_fpu() have
307 * returned successfully. When CONFIG_MIPS_FP_SUPPORT=n it is known at compile
308 * time that this should never happen, so calls to these functions should be
309 * optimized away & never actually be emitted.
310 */
311
312extern void save_fp(struct task_struct *tsk)
313 __compiletime_error("save_fp() should not be called when CONFIG_MIPS_FP_SUPPORT=n");
314
315extern void _save_fp(struct task_struct *)
316 __compiletime_error("_save_fp() should not be called when CONFIG_MIPS_FP_SUPPORT=n");
317
318extern void restore_fp(struct task_struct *tsk)
319 __compiletime_error("restore_fp() should not be called when CONFIG_MIPS_FP_SUPPORT=n");
320
321extern void _restore_fp(struct task_struct *)
322 __compiletime_error("_restore_fp() should not be called when CONFIG_MIPS_FP_SUPPORT=n");
323
324extern union fpureg *get_fpu_regs(struct task_struct *tsk)
325 __compiletime_error("get_fpu_regs() should not be called when CONFIG_MIPS_FP_SUPPORT=n");
326
327#endif /* !CONFIG_MIPS_FP_SUPPORT */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328#endif /* _ASM_FPU_H */