Back to home page

OSCL-LXR

 
 

    


0001 /* SPDX-License-Identifier: GPL-2.0-or-later */
0002 /*
0003  * Copyright (C) 2002 MontaVista Software Inc.
0004  * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
0005  */
0006 #ifndef _ASM_FPU_H
0007 #define _ASM_FPU_H
0008 
0009 #include <linux/sched.h>
0010 #include <linux/sched/task_stack.h>
0011 #include <linux/ptrace.h>
0012 #include <linux/thread_info.h>
0013 #include <linux/bitops.h>
0014 
0015 #include <asm/mipsregs.h>
0016 #include <asm/cpu.h>
0017 #include <asm/cpu-features.h>
0018 #include <asm/fpu_emulator.h>
0019 #include <asm/hazards.h>
0020 #include <asm/ptrace.h>
0021 #include <asm/processor.h>
0022 #include <asm/current.h>
0023 #include <asm/msa.h>
0024 
0025 #ifdef CONFIG_MIPS_MT_FPAFF
0026 #include <asm/mips_mt.h>
0027 #endif
0028 
0029 /*
0030  * This enum specifies a mode in which we want the FPU to operate, for cores
0031  * which implement the Status.FR bit. Note that the bottom bit of the value
0032  * purposefully matches the desired value of the Status.FR bit.
0033  */
0034 enum fpu_mode {
0035     FPU_32BIT = 0,      /* FR = 0 */
0036     FPU_64BIT,      /* FR = 1, FRE = 0 */
0037     FPU_AS_IS,
0038     FPU_HYBRID,     /* FR = 1, FRE = 1 */
0039 
0040 #define FPU_FR_MASK     0x1
0041 };
0042 
0043 #ifdef CONFIG_MIPS_FP_SUPPORT
0044 
0045 extern void _save_fp(struct task_struct *);
0046 extern void _restore_fp(struct task_struct *);
0047 
0048 #define __disable_fpu()                         \
0049 do {                                    \
0050     clear_c0_status(ST0_CU1);                   \
0051     disable_fpu_hazard();                       \
0052 } while (0)
0053 
0054 static inline int __enable_fpu(enum fpu_mode mode)
0055 {
0056     int fr;
0057 
0058     switch (mode) {
0059     case FPU_AS_IS:
0060         /* just enable the FPU in its current mode */
0061         set_c0_status(ST0_CU1);
0062         enable_fpu_hazard();
0063         return 0;
0064 
0065     case FPU_HYBRID:
0066         if (!cpu_has_fre)
0067             return SIGFPE;
0068 
0069         /* set FRE */
0070         set_c0_config5(MIPS_CONF5_FRE);
0071         goto fr_common;
0072 
0073     case FPU_64BIT:
0074 #if !(defined(CONFIG_CPU_MIPSR2) || defined(CONFIG_CPU_MIPSR5) || \
0075       defined(CONFIG_CPU_MIPSR6) || defined(CONFIG_64BIT))
0076         /* we only have a 32-bit FPU */
0077         return SIGFPE;
0078 #endif
0079         /* fallthrough */
0080     case FPU_32BIT:
0081         if (cpu_has_fre) {
0082             /* clear FRE */
0083             clear_c0_config5(MIPS_CONF5_FRE);
0084         }
0085 fr_common:
0086         /* set CU1 & change FR appropriately */
0087         fr = (int)mode & FPU_FR_MASK;
0088         change_c0_status(ST0_CU1 | ST0_FR, ST0_CU1 | (fr ? ST0_FR : 0));
0089         enable_fpu_hazard();
0090 
0091         /* check FR has the desired value */
0092         if (!!(read_c0_status() & ST0_FR) == !!fr)
0093             return 0;
0094 
0095         /* unsupported FR value */
0096         __disable_fpu();
0097         return SIGFPE;
0098 
0099     default:
0100         BUG();
0101     }
0102 
0103     return SIGFPE;
0104 }
0105 
0106 #define clear_fpu_owner()   clear_thread_flag(TIF_USEDFPU)
0107 
0108 static inline int __is_fpu_owner(void)
0109 {
0110     return test_thread_flag(TIF_USEDFPU);
0111 }
0112 
0113 static inline int is_fpu_owner(void)
0114 {
0115     return cpu_has_fpu && __is_fpu_owner();
0116 }
0117 
0118 static inline int __own_fpu(void)
0119 {
0120     enum fpu_mode mode;
0121     int ret;
0122 
0123     if (test_thread_flag(TIF_HYBRID_FPREGS))
0124         mode = FPU_HYBRID;
0125     else
0126         mode = !test_thread_flag(TIF_32BIT_FPREGS);
0127 
0128     ret = __enable_fpu(mode);
0129     if (ret)
0130         return ret;
0131 
0132     KSTK_STATUS(current) |= ST0_CU1;
0133     if (mode == FPU_64BIT || mode == FPU_HYBRID)
0134         KSTK_STATUS(current) |= ST0_FR;
0135     else /* mode == FPU_32BIT */
0136         KSTK_STATUS(current) &= ~ST0_FR;
0137 
0138     set_thread_flag(TIF_USEDFPU);
0139     return 0;
0140 }
0141 
0142 static inline int own_fpu_inatomic(int restore)
0143 {
0144     int ret = 0;
0145 
0146     if (cpu_has_fpu && !__is_fpu_owner()) {
0147         ret = __own_fpu();
0148         if (restore && !ret)
0149             _restore_fp(current);
0150     }
0151     return ret;
0152 }
0153 
0154 static inline int own_fpu(int restore)
0155 {
0156     int ret;
0157 
0158     preempt_disable();
0159     ret = own_fpu_inatomic(restore);
0160     preempt_enable();
0161     return ret;
0162 }
0163 
0164 static inline void lose_fpu_inatomic(int save, struct task_struct *tsk)
0165 {
0166     if (is_msa_enabled()) {
0167         if (save) {
0168             save_msa(tsk);
0169             tsk->thread.fpu.fcr31 =
0170                     read_32bit_cp1_register(CP1_STATUS);
0171         }
0172         disable_msa();
0173         clear_tsk_thread_flag(tsk, TIF_USEDMSA);
0174         __disable_fpu();
0175     } else if (is_fpu_owner()) {
0176         if (save)
0177             _save_fp(tsk);
0178         __disable_fpu();
0179     } else {
0180         /* FPU should not have been left enabled with no owner */
0181         WARN(read_c0_status() & ST0_CU1,
0182              "Orphaned FPU left enabled");
0183     }
0184     KSTK_STATUS(tsk) &= ~ST0_CU1;
0185     clear_tsk_thread_flag(tsk, TIF_USEDFPU);
0186 }
0187 
0188 static inline void lose_fpu(int save)
0189 {
0190     preempt_disable();
0191     lose_fpu_inatomic(save, current);
0192     preempt_enable();
0193 }
0194 
0195 /**
0196  * init_fp_ctx() - Initialize task FP context
0197  * @target: The task whose FP context should be initialized.
0198  *
0199  * Initializes the FP context of the target task to sane default values if that
0200  * target task does not already have valid FP context. Once the context has
0201  * been initialized, the task will be marked as having used FP & thus having
0202  * valid FP context.
0203  *
0204  * Returns: true if context is initialized, else false.
0205  */
0206 static inline bool init_fp_ctx(struct task_struct *target)
0207 {
0208     /* If FP has been used then the target already has context */
0209     if (tsk_used_math(target))
0210         return false;
0211 
0212     /* Begin with data registers set to all 1s... */
0213     memset(&target->thread.fpu.fpr, ~0, sizeof(target->thread.fpu.fpr));
0214 
0215     /* FCSR has been preset by `mips_set_personality_nan'.  */
0216 
0217     /*
0218      * Record that the target has "used" math, such that the context
0219      * just initialised, and any modifications made by the caller,
0220      * aren't discarded.
0221      */
0222     set_stopped_child_used_math(target);
0223 
0224     return true;
0225 }
0226 
0227 static inline void save_fp(struct task_struct *tsk)
0228 {
0229     if (cpu_has_fpu)
0230         _save_fp(tsk);
0231 }
0232 
0233 static inline void restore_fp(struct task_struct *tsk)
0234 {
0235     if (cpu_has_fpu)
0236         _restore_fp(tsk);
0237 }
0238 
0239 static inline union fpureg *get_fpu_regs(struct task_struct *tsk)
0240 {
0241     if (tsk == current) {
0242         preempt_disable();
0243         if (is_fpu_owner())
0244             _save_fp(current);
0245         preempt_enable();
0246     }
0247 
0248     return tsk->thread.fpu.fpr;
0249 }
0250 
0251 #else /* !CONFIG_MIPS_FP_SUPPORT */
0252 
0253 /*
0254  * When FP support is disabled we provide only a minimal set of stub functions
0255  * to avoid callers needing to care too much about CONFIG_MIPS_FP_SUPPORT.
0256  */
0257 
0258 static inline int __enable_fpu(enum fpu_mode mode)
0259 {
0260     return SIGILL;
0261 }
0262 
0263 static inline void __disable_fpu(void)
0264 {
0265     /* no-op */
0266 }
0267 
0268 
0269 static inline int is_fpu_owner(void)
0270 {
0271     return 0;
0272 }
0273 
0274 static inline void clear_fpu_owner(void)
0275 {
0276     /* no-op */
0277 }
0278 
0279 static inline int own_fpu_inatomic(int restore)
0280 {
0281     return SIGILL;
0282 }
0283 
0284 static inline int own_fpu(int restore)
0285 {
0286     return SIGILL;
0287 }
0288 
0289 static inline void lose_fpu_inatomic(int save, struct task_struct *tsk)
0290 {
0291     /* no-op */
0292 }
0293 
0294 static inline void lose_fpu(int save)
0295 {
0296     /* no-op */
0297 }
0298 
0299 static inline bool init_fp_ctx(struct task_struct *target)
0300 {
0301     return false;
0302 }
0303 
0304 /*
0305  * The following functions should only be called in paths where we know that FP
0306  * support is enabled, typically a path where own_fpu() or __enable_fpu() have
0307  * returned successfully. When CONFIG_MIPS_FP_SUPPORT=n it is known at compile
0308  * time that this should never happen, so calls to these functions should be
0309  * optimized away & never actually be emitted.
0310  */
0311 
0312 extern void save_fp(struct task_struct *tsk)
0313     __compiletime_error("save_fp() should not be called when CONFIG_MIPS_FP_SUPPORT=n");
0314 
0315 extern void _save_fp(struct task_struct *)
0316     __compiletime_error("_save_fp() should not be called when CONFIG_MIPS_FP_SUPPORT=n");
0317 
0318 extern void restore_fp(struct task_struct *tsk)
0319     __compiletime_error("restore_fp() should not be called when CONFIG_MIPS_FP_SUPPORT=n");
0320 
0321 extern void _restore_fp(struct task_struct *)
0322     __compiletime_error("_restore_fp() should not be called when CONFIG_MIPS_FP_SUPPORT=n");
0323 
0324 extern union fpureg *get_fpu_regs(struct task_struct *tsk)
0325     __compiletime_error("get_fpu_regs() should not be called when CONFIG_MIPS_FP_SUPPORT=n");
0326 
0327 #endif /* !CONFIG_MIPS_FP_SUPPORT */
0328 #endif /* _ASM_FPU_H */