@@ -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)
{
@@ -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));
@@ -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)
@@ -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 */
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 <syq@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(+)