fpu.h 7.2 KB

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