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