162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-or-later */ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2002 MontaVista Software Inc. 462306a36Sopenharmony_ci * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#ifndef _ASM_FPU_H 762306a36Sopenharmony_ci#define _ASM_FPU_H 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/sched.h> 1062306a36Sopenharmony_ci#include <linux/sched/task_stack.h> 1162306a36Sopenharmony_ci#include <linux/ptrace.h> 1262306a36Sopenharmony_ci#include <linux/thread_info.h> 1362306a36Sopenharmony_ci#include <linux/bitops.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <asm/mipsregs.h> 1662306a36Sopenharmony_ci#include <asm/cpu.h> 1762306a36Sopenharmony_ci#include <asm/cpu-features.h> 1862306a36Sopenharmony_ci#include <asm/fpu_emulator.h> 1962306a36Sopenharmony_ci#include <asm/hazards.h> 2062306a36Sopenharmony_ci#include <asm/ptrace.h> 2162306a36Sopenharmony_ci#include <asm/processor.h> 2262306a36Sopenharmony_ci#include <asm/current.h> 2362306a36Sopenharmony_ci#include <asm/msa.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#ifdef CONFIG_MIPS_MT_FPAFF 2662306a36Sopenharmony_ci#include <asm/mips_mt.h> 2762306a36Sopenharmony_ci#endif 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* 3062306a36Sopenharmony_ci * This enum specifies a mode in which we want the FPU to operate, for cores 3162306a36Sopenharmony_ci * which implement the Status.FR bit. Note that the bottom bit of the value 3262306a36Sopenharmony_ci * purposefully matches the desired value of the Status.FR bit. 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_cienum fpu_mode { 3562306a36Sopenharmony_ci FPU_32BIT = 0, /* FR = 0 */ 3662306a36Sopenharmony_ci FPU_64BIT, /* FR = 1, FRE = 0 */ 3762306a36Sopenharmony_ci FPU_AS_IS, 3862306a36Sopenharmony_ci FPU_HYBRID, /* FR = 1, FRE = 1 */ 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define FPU_FR_MASK 0x1 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#ifdef CONFIG_MIPS_FP_SUPPORT 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ciextern void _save_fp(struct task_struct *); 4662306a36Sopenharmony_ciextern void _restore_fp(struct task_struct *); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#define __disable_fpu() \ 4962306a36Sopenharmony_cido { \ 5062306a36Sopenharmony_ci clear_c0_status(ST0_CU1); \ 5162306a36Sopenharmony_ci disable_fpu_hazard(); \ 5262306a36Sopenharmony_ci} while (0) 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic inline int __enable_fpu(enum fpu_mode mode) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci int fr; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci switch (mode) { 5962306a36Sopenharmony_ci case FPU_AS_IS: 6062306a36Sopenharmony_ci /* just enable the FPU in its current mode */ 6162306a36Sopenharmony_ci set_c0_status(ST0_CU1); 6262306a36Sopenharmony_ci enable_fpu_hazard(); 6362306a36Sopenharmony_ci return 0; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci case FPU_HYBRID: 6662306a36Sopenharmony_ci if (!cpu_has_fre) 6762306a36Sopenharmony_ci return SIGFPE; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* set FRE */ 7062306a36Sopenharmony_ci set_c0_config5(MIPS_CONF5_FRE); 7162306a36Sopenharmony_ci goto fr_common; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci case FPU_64BIT: 7462306a36Sopenharmony_ci#if !(defined(CONFIG_CPU_MIPSR2) || defined(CONFIG_CPU_MIPSR5) || \ 7562306a36Sopenharmony_ci defined(CONFIG_CPU_MIPSR6) || defined(CONFIG_64BIT)) 7662306a36Sopenharmony_ci /* we only have a 32-bit FPU */ 7762306a36Sopenharmony_ci return SIGFPE; 7862306a36Sopenharmony_ci#endif 7962306a36Sopenharmony_ci /* fallthrough */ 8062306a36Sopenharmony_ci case FPU_32BIT: 8162306a36Sopenharmony_ci if (cpu_has_fre) { 8262306a36Sopenharmony_ci /* clear FRE */ 8362306a36Sopenharmony_ci clear_c0_config5(MIPS_CONF5_FRE); 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_cifr_common: 8662306a36Sopenharmony_ci /* set CU1 & change FR appropriately */ 8762306a36Sopenharmony_ci fr = (int)mode & FPU_FR_MASK; 8862306a36Sopenharmony_ci change_c0_status(ST0_CU1 | ST0_FR, ST0_CU1 | (fr ? ST0_FR : 0)); 8962306a36Sopenharmony_ci enable_fpu_hazard(); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* check FR has the desired value */ 9262306a36Sopenharmony_ci if (!!(read_c0_status() & ST0_FR) == !!fr) 9362306a36Sopenharmony_ci return 0; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci /* unsupported FR value */ 9662306a36Sopenharmony_ci __disable_fpu(); 9762306a36Sopenharmony_ci return SIGFPE; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci default: 10062306a36Sopenharmony_ci BUG(); 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci return SIGFPE; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci#define clear_fpu_owner() clear_thread_flag(TIF_USEDFPU) 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic inline int __is_fpu_owner(void) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci return test_thread_flag(TIF_USEDFPU); 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic inline int is_fpu_owner(void) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci return cpu_has_fpu && __is_fpu_owner(); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic inline int __own_fpu(void) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci enum fpu_mode mode; 12162306a36Sopenharmony_ci int ret; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (test_thread_flag(TIF_HYBRID_FPREGS)) 12462306a36Sopenharmony_ci mode = FPU_HYBRID; 12562306a36Sopenharmony_ci else 12662306a36Sopenharmony_ci mode = !test_thread_flag(TIF_32BIT_FPREGS); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci ret = __enable_fpu(mode); 12962306a36Sopenharmony_ci if (ret) 13062306a36Sopenharmony_ci return ret; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci KSTK_STATUS(current) |= ST0_CU1; 13362306a36Sopenharmony_ci if (mode == FPU_64BIT || mode == FPU_HYBRID) 13462306a36Sopenharmony_ci KSTK_STATUS(current) |= ST0_FR; 13562306a36Sopenharmony_ci else /* mode == FPU_32BIT */ 13662306a36Sopenharmony_ci KSTK_STATUS(current) &= ~ST0_FR; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci set_thread_flag(TIF_USEDFPU); 13962306a36Sopenharmony_ci return 0; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic inline int own_fpu_inatomic(int restore) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci int ret = 0; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (cpu_has_fpu && !__is_fpu_owner()) { 14762306a36Sopenharmony_ci ret = __own_fpu(); 14862306a36Sopenharmony_ci if (restore && !ret) 14962306a36Sopenharmony_ci _restore_fp(current); 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci return ret; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic inline int own_fpu(int restore) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci int ret; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci preempt_disable(); 15962306a36Sopenharmony_ci ret = own_fpu_inatomic(restore); 16062306a36Sopenharmony_ci preempt_enable(); 16162306a36Sopenharmony_ci return ret; 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic inline void lose_fpu_inatomic(int save, struct task_struct *tsk) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci if (is_msa_enabled()) { 16762306a36Sopenharmony_ci if (save) { 16862306a36Sopenharmony_ci save_msa(tsk); 16962306a36Sopenharmony_ci tsk->thread.fpu.fcr31 = 17062306a36Sopenharmony_ci read_32bit_cp1_register(CP1_STATUS); 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci disable_msa(); 17362306a36Sopenharmony_ci clear_tsk_thread_flag(tsk, TIF_USEDMSA); 17462306a36Sopenharmony_ci __disable_fpu(); 17562306a36Sopenharmony_ci } else if (is_fpu_owner()) { 17662306a36Sopenharmony_ci if (save) 17762306a36Sopenharmony_ci _save_fp(tsk); 17862306a36Sopenharmony_ci __disable_fpu(); 17962306a36Sopenharmony_ci } else { 18062306a36Sopenharmony_ci /* FPU should not have been left enabled with no owner */ 18162306a36Sopenharmony_ci WARN(read_c0_status() & ST0_CU1, 18262306a36Sopenharmony_ci "Orphaned FPU left enabled"); 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci KSTK_STATUS(tsk) &= ~ST0_CU1; 18562306a36Sopenharmony_ci clear_tsk_thread_flag(tsk, TIF_USEDFPU); 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic inline void lose_fpu(int save) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci preempt_disable(); 19162306a36Sopenharmony_ci lose_fpu_inatomic(save, current); 19262306a36Sopenharmony_ci preempt_enable(); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci/** 19662306a36Sopenharmony_ci * init_fp_ctx() - Initialize task FP context 19762306a36Sopenharmony_ci * @target: The task whose FP context should be initialized. 19862306a36Sopenharmony_ci * 19962306a36Sopenharmony_ci * Initializes the FP context of the target task to sane default values if that 20062306a36Sopenharmony_ci * target task does not already have valid FP context. Once the context has 20162306a36Sopenharmony_ci * been initialized, the task will be marked as having used FP & thus having 20262306a36Sopenharmony_ci * valid FP context. 20362306a36Sopenharmony_ci * 20462306a36Sopenharmony_ci * Returns: true if context is initialized, else false. 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_cistatic inline bool init_fp_ctx(struct task_struct *target) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci /* If FP has been used then the target already has context */ 20962306a36Sopenharmony_ci if (tsk_used_math(target)) 21062306a36Sopenharmony_ci return false; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* Begin with data registers set to all 1s... */ 21362306a36Sopenharmony_ci memset(&target->thread.fpu.fpr, ~0, sizeof(target->thread.fpu.fpr)); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* FCSR has been preset by `mips_set_personality_nan'. */ 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* 21862306a36Sopenharmony_ci * Record that the target has "used" math, such that the context 21962306a36Sopenharmony_ci * just initialised, and any modifications made by the caller, 22062306a36Sopenharmony_ci * aren't discarded. 22162306a36Sopenharmony_ci */ 22262306a36Sopenharmony_ci set_stopped_child_used_math(target); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci return true; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic inline void save_fp(struct task_struct *tsk) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci if (cpu_has_fpu) 23062306a36Sopenharmony_ci _save_fp(tsk); 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic inline void restore_fp(struct task_struct *tsk) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci if (cpu_has_fpu) 23662306a36Sopenharmony_ci _restore_fp(tsk); 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic inline union fpureg *get_fpu_regs(struct task_struct *tsk) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci if (tsk == current) { 24262306a36Sopenharmony_ci preempt_disable(); 24362306a36Sopenharmony_ci if (is_fpu_owner()) 24462306a36Sopenharmony_ci _save_fp(current); 24562306a36Sopenharmony_ci preempt_enable(); 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci return tsk->thread.fpu.fpr; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci#else /* !CONFIG_MIPS_FP_SUPPORT */ 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci/* 25462306a36Sopenharmony_ci * When FP support is disabled we provide only a minimal set of stub functions 25562306a36Sopenharmony_ci * to avoid callers needing to care too much about CONFIG_MIPS_FP_SUPPORT. 25662306a36Sopenharmony_ci */ 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic inline int __enable_fpu(enum fpu_mode mode) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci return SIGILL; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic inline void __disable_fpu(void) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci /* no-op */ 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic inline int is_fpu_owner(void) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci return 0; 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic inline void clear_fpu_owner(void) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci /* no-op */ 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic inline int own_fpu_inatomic(int restore) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci return SIGILL; 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic inline int own_fpu(int restore) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci return SIGILL; 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic inline void lose_fpu_inatomic(int save, struct task_struct *tsk) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci /* no-op */ 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic inline void lose_fpu(int save) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci /* no-op */ 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic inline bool init_fp_ctx(struct task_struct *target) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci return false; 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci/* 30562306a36Sopenharmony_ci * The following functions should only be called in paths where we know that FP 30662306a36Sopenharmony_ci * support is enabled, typically a path where own_fpu() or __enable_fpu() have 30762306a36Sopenharmony_ci * returned successfully. When CONFIG_MIPS_FP_SUPPORT=n it is known at compile 30862306a36Sopenharmony_ci * time that this should never happen, so calls to these functions should be 30962306a36Sopenharmony_ci * optimized away & never actually be emitted. 31062306a36Sopenharmony_ci */ 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ciextern void save_fp(struct task_struct *tsk) 31362306a36Sopenharmony_ci __compiletime_error("save_fp() should not be called when CONFIG_MIPS_FP_SUPPORT=n"); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ciextern void _save_fp(struct task_struct *) 31662306a36Sopenharmony_ci __compiletime_error("_save_fp() should not be called when CONFIG_MIPS_FP_SUPPORT=n"); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ciextern void restore_fp(struct task_struct *tsk) 31962306a36Sopenharmony_ci __compiletime_error("restore_fp() should not be called when CONFIG_MIPS_FP_SUPPORT=n"); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ciextern void _restore_fp(struct task_struct *) 32262306a36Sopenharmony_ci __compiletime_error("_restore_fp() should not be called when CONFIG_MIPS_FP_SUPPORT=n"); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ciextern union fpureg *get_fpu_regs(struct task_struct *tsk) 32562306a36Sopenharmony_ci __compiletime_error("get_fpu_regs() should not be called when CONFIG_MIPS_FP_SUPPORT=n"); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci#endif /* !CONFIG_MIPS_FP_SUPPORT */ 32862306a36Sopenharmony_ci#endif /* _ASM_FPU_H */ 329