MIPS o32 has a mode called FP64, who has 32 64Bit FPU registers. To use it: add PR_GET_FP_MODE and PR_SET_FP_MODE options for prctl set CP0St_FR when binary as EF_MIPS_FP64
https://dmz-portal.mips.com/wiki/MIPS_O32_ABI_-_FR0_and_FR1_Interlinking Signed-off-by: YunQiang Su <s...@debian.org> --- linux-user/main.c | 3 +++ linux-user/syscall.c | 24 +++++++++++++++++++++ target/mips/cpu.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++ target/mips/cpu.h | 9 ++++++++ 4 files changed, 95 insertions(+) diff --git a/linux-user/main.c b/linux-user/main.c index 2fd2a143ed..f2b02a99a2 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -4750,6 +4750,9 @@ int main(int argc, char **argv, char **envp) } restore_snan_bit_mode(env); } + if ((info->elf_flags & EF_MIPS_FP64) != 0) { + env->CP0_Status |= (1 << CP0St_FR); + } } #elif defined(TARGET_NIOS2) { diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 11c9116c4a..664dacda33 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -10530,6 +10530,30 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, * need. */ ret = -TARGET_EINVAL; break; +#ifdef TARGET_MIPS + case PR_GET_FP_MODE: + { + CPUMIPSState *env = ((CPUMIPSState *)cpu_env); + ret = 0; + if (env->CP0_Status & (1 << CP0St_FR)) { + ret |= PR_FP_MODE_FR; + } + if (env->CP0_Config5 & (1 << CP0C5_FRE)) { + ret |= PR_FP_MODE_FRE; + } + break; + } + case PR_SET_FP_MODE: + { + CPUMIPSState *env = ((CPUMIPSState *)cpu_env); + ret = mips_prctl_set_fp_mode(env, arg2); + if (ret < 0) { + ret = -TARGET_EOPNOTSUPP; + goto fail; + } + break; + } +#endif default: /* Most prctl options have no pointer arguments */ ret = get_errno(prctl(arg1, arg2, arg3, arg4, arg5)); diff --git a/target/mips/cpu.c b/target/mips/cpu.c index 069f93560e..6cf1907f9c 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -246,4 +246,63 @@ static void mips_cpu_register_types(void) } } +#ifdef CONFIG_USER_ONLY +abi_long mips_prctl_set_fp_mode(CPUMIPSState *env, abi_long arg2) +{ + abi_long ret; + CPUState *other_cpu; + bool old_fr = env->CP0_Status & (1 << CP0St_FR); + bool new_fr = arg2 & PR_FP_MODE_FR; + bool new_fre = arg2 & PR_FP_MODE_FRE; + + if (new_fr && !(env->active_fpu.fcr0 & (1 << FCR0_F64))) { + /* FR1 is not supported */ + ret = -1; + return ret; + } + + if (!new_fr && (env->active_fpu.fcr0 & (1 << FCR0_F64)) + && !(env->CP0_Status_rw_bitmask & (1 << CP0St_FR))) { + /* cannot set FR=0 */ + ret = -1; + return ret; + } + + if (new_fre && !(env->active_fpu.fcr0 & (1 << FCR0_FREP))) { + /* Cannot set FRE=1 */ + ret = -1; + return ret; + } + + start_exclusive(); + CPU_FOREACH(other_cpu) { + int i; + MIPSCPU *cpu = MIPS_CPU(other_cpu); + env = &cpu->env; + for (i = 0; i < 32 ; i += 2) { + fpr_t *fpr = env->active_fpu.fpr; + if (!old_fr && new_fr) { + fpr[i].w[!FP_ENDIAN_IDX] = fpr[i + 1].w[FP_ENDIAN_IDX]; + } else if (old_fr && !new_fr) { + fpr[i + 1].w[FP_ENDIAN_IDX] = fpr[i].w[!FP_ENDIAN_IDX]; + } + } + if (new_fr) { + env->CP0_Status |= (1 << CP0St_FR); + } else { + env->CP0_Status &= ~(1 << CP0St_FR); + } + if (new_fre) { + env->CP0_Config5 |= (1 << CP0C5_FRE); + } else { + env->CP0_Config5 &= ~(1 << CP0C5_FRE); + } + compute_hflags(env); + } + end_exclusive(); + ret = 0; + return ret; +} +#endif + type_init(mips_cpu_register_types) diff --git a/target/mips/cpu.h b/target/mips/cpu.h index 7f8ba5ff3e..b5d9aac6d0 100644 --- a/target/mips/cpu.h +++ b/target/mips/cpu.h @@ -769,4 +769,13 @@ static inline void cpu_get_tb_cpu_state(CPUMIPSState *env, target_ulong *pc, MIPS_HFLAG_HWRENA_ULR); } +/* prctl interface. */ +#if defined(CONFIG_USER_ONLY) +#define PR_SET_FP_MODE 45 +#define PR_GET_FP_MODE 46 +#define PR_FP_MODE_FR (1 << 0) +#define PR_FP_MODE_FRE (1 << 1) +abi_long mips_prctl_set_fp_mode(CPUMIPSState *env, abi_long arg2); +#endif + #endif /* MIPS_CPU_H */ -- 2.15.1