--- zzzz-none-000/linux-3.10.107/arch/mips/include/asm/fpu.h 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/arch/mips/include/asm/fpu.h 2021-02-04 17:41:59.000000000 +0000 @@ -17,9 +17,11 @@ #include #include #include +#include #include #include #include +#include #ifdef CONFIG_MIPS_MT_FPAFF #include @@ -28,16 +30,23 @@ struct sigcontext; struct sigcontext32; -extern void fpu_emulator_init_fpu(void); -extern void _init_fpu(void); +extern void _init_fpu(unsigned int); extern void _save_fp(struct task_struct *); extern void _restore_fp(struct task_struct *); -#define __enable_fpu() \ -do { \ - set_c0_status(ST0_CU1); \ - enable_fpu_hazard(); \ -} while (0) +/* + * This enum specifies a mode in which we want the FPU to operate, for cores + * which implement the Status.FR bit. Note that the bottom bit of the value + * purposefully matches the desired value of the Status.FR bit. + */ +enum fpu_mode { + FPU_32BIT = 0, /* FR = 0 */ + FPU_64BIT, /* FR = 1, FRE = 0 */ + FPU_AS_IS, + FPU_HYBRID, /* FR = 1, FRE = 1 */ + +#define FPU_FR_MASK 0x1 +}; #define __disable_fpu() \ do { \ @@ -45,18 +54,57 @@ disable_fpu_hazard(); \ } while (0) -#define enable_fpu() \ -do { \ - if (cpu_has_fpu) \ - __enable_fpu(); \ -} while (0) +static inline int __enable_fpu(enum fpu_mode mode) +{ + int fr; -#define disable_fpu() \ -do { \ - if (cpu_has_fpu) \ - __disable_fpu(); \ -} while (0) + switch (mode) { + case FPU_AS_IS: + /* just enable the FPU in its current mode */ + set_c0_status(ST0_CU1); + enable_fpu_hazard(); + return 0; + + case FPU_HYBRID: + if (!cpu_has_fre) + return SIGFPE; + + /* set FRE */ + set_c0_config5(MIPS_CONF5_FRE); + goto fr_common; + + case FPU_64BIT: +#if !(defined(CONFIG_CPU_MIPSR2) || defined(CONFIG_CPU_MIPSR6) \ + || defined(CONFIG_64BIT)) + /* we only have a 32-bit FPU */ + return SIGFPE; +#endif + /* fall through */ + case FPU_32BIT: + if (cpu_has_fre) { + /* clear FRE */ + clear_c0_config5(MIPS_CONF5_FRE); + } +fr_common: + /* set CU1 & change FR appropriately */ + fr = (int)mode & FPU_FR_MASK; + change_c0_status(ST0_CU1 | ST0_FR, ST0_CU1 | (fr ? ST0_FR : 0)); + enable_fpu_hazard(); + + /* check FR has the desired value */ + if (!!(read_c0_status() & ST0_FR) == !!fr) + return 0; + /* unsupported FR value */ + __disable_fpu(); + return SIGFPE; + + default: + BUG(); + } + + return SIGFPE; +} #define clear_fpu_owner() clear_thread_flag(TIF_USEDFPU) @@ -70,52 +118,117 @@ return cpu_has_fpu && __is_fpu_owner(); } -static inline void __own_fpu(void) +static inline int __own_fpu(void) { - __enable_fpu(); + enum fpu_mode mode; + int ret; + + if (test_thread_flag(TIF_HYBRID_FPREGS)) + mode = FPU_HYBRID; + else + mode = !test_thread_flag(TIF_32BIT_FPREGS); + + ret = __enable_fpu(mode); + if (ret) + return ret; + KSTK_STATUS(current) |= ST0_CU1; + if (mode == FPU_64BIT || mode == FPU_HYBRID) + KSTK_STATUS(current) |= ST0_FR; + else /* mode == FPU_32BIT */ + KSTK_STATUS(current) &= ~ST0_FR; + set_thread_flag(TIF_USEDFPU); + return 0; } -static inline void own_fpu_inatomic(int restore) +static inline int own_fpu_inatomic(int restore) { + int ret = 0; + if (cpu_has_fpu && !__is_fpu_owner()) { - __own_fpu(); - if (restore) + ret = __own_fpu(); + if (restore && !ret) _restore_fp(current); } + return ret; } -static inline void own_fpu(int restore) +static inline int own_fpu(int restore) { + int ret; + preempt_disable(); - own_fpu_inatomic(restore); + ret = own_fpu_inatomic(restore); preempt_enable(); + return ret; } -static inline void lose_fpu(int save) +static inline void lose_fpu_inatomic(int save, struct task_struct *tsk) { - preempt_disable(); - if (is_fpu_owner()) { + if (is_msa_enabled()) { + if (save) { + save_msa(tsk); + tsk->thread.fpu.fcr31 = + read_32bit_cp1_register(CP1_STATUS); + } + disable_msa(); + clear_tsk_thread_flag(tsk, TIF_USEDMSA); + __disable_fpu(); + } else if (is_fpu_owner()) { if (save) - _save_fp(current); - KSTK_STATUS(current) &= ~ST0_CU1; - clear_thread_flag(TIF_USEDFPU); + _save_fp(tsk); __disable_fpu(); } - preempt_enable(); + KSTK_STATUS(tsk) &= ~ST0_CU1; + clear_tsk_thread_flag(tsk, TIF_USEDFPU); } -static inline void init_fpu(void) +static inline void lose_fpu(int save) { preempt_disable(); + lose_fpu_inatomic(save, current); + preempt_enable(); +} + +static inline int init_fpu(void) +{ + unsigned int fcr31 = current->thread.fpu.fcr31; + int ret = 0; + if (cpu_has_fpu) { - __own_fpu(); - _init_fpu(); - } else { + unsigned int config5; + + ret = __own_fpu(); + if (ret) + return ret; + + if (!cpu_has_fre) { + _init_fpu(fcr31); + + return 0; + } + + /* + * Ensure FRE is clear whilst running _init_fpu, since + * single precision FP instructions are used. If FRE + * was set then we'll just end up initialising all 32 + * 64b registers. + */ + config5 = clear_c0_config5(MIPS_CONF5_FRE); + enable_fpu_hazard(); + + _init_fpu(fcr31); + + /* Restore FRE */ + write_c0_config5(config5); + enable_fpu_hazard(); + } else if (IS_ENABLED(CONFIG_MIPS_FPU_EMULATOR)) fpu_emulator_init_fpu(); - } - preempt_enable(); + else + ret = SIGILL; + + return ret; } static inline void save_fp(struct task_struct *tsk) @@ -130,7 +243,7 @@ _restore_fp(tsk); } -static inline fpureg_t *get_fpu_regs(struct task_struct *tsk) +static inline union fpureg *get_fpu_regs(struct task_struct *tsk) { if (tsk == current) { preempt_disable();