From 64ea3d676d9447ecdb987deab5a1542ea088bd31 Mon Sep 17 00:00:00 2001 From: Stefan Markovic Date: Fri, 26 Oct 2018 13:17:43 +0200 Subject: [PATCH] linux-user: Add prctl() PR_SET_FP_MODE and PR_GET_FP_MODE implementations Implement MIPS specific prctl() PR_SET_FP_MODE and PR_GET_FP_MODE emulation. Reviewed-by: Aleksandar Markovic Signed-off-by: Stefan Markovic --- linux-user/mips/target_syscall.h | 2 + linux-user/mips64/target_syscall.h | 2 + linux-user/syscall.c | 62 ++++++++++++++++++++++++++++-- 3 files changed, 62 insertions(+), 4 deletions(-) diff --git a/linux-user/mips/target_syscall.h b/linux-user/mips/target_syscall.h index 33177af113..d5509a34a7 100644 --- a/linux-user/mips/target_syscall.h +++ b/linux-user/mips/target_syscall.h @@ -247,5 +247,7 @@ static inline abi_ulong target_shmlba(CPUMIPSState *env) /* MIPS-specific prctl() options */ #define TARGET_PR_SET_FP_MODE 45 #define TARGET_PR_GET_FP_MODE 46 +#define TARGET_PR_FP_MODE_FR (1 << 0) +#define TARGET_PR_FP_MODE_FRE (1 << 1) #endif /* MIPS_TARGET_SYSCALL_H */ diff --git a/linux-user/mips64/target_syscall.h b/linux-user/mips64/target_syscall.h index c1160e69f8..8ccc46822c 100644 --- a/linux-user/mips64/target_syscall.h +++ b/linux-user/mips64/target_syscall.h @@ -244,5 +244,7 @@ static inline abi_ulong target_shmlba(CPUMIPSState *env) /* MIPS-specific prctl() options */ #define TARGET_PR_SET_FP_MODE 45 #define TARGET_PR_GET_FP_MODE 46 +#define TARGET_PR_FP_MODE_FR (1 << 0) +#define TARGET_PR_FP_MODE_FRE (1 << 1) #endif /* MIPS64_TARGET_SYSCALL_H */ diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 15b03e17b9..810a58b704 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -9529,11 +9529,65 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, #endif #ifdef TARGET_MIPS case TARGET_PR_GET_FP_MODE: - /* TODO: Implement TARGET_PR_SET_FP_MODE handling.*/ - return -TARGET_EINVAL; + { + CPUMIPSState *env = ((CPUMIPSState *)cpu_env); + ret = 0; + if (env->CP0_Status & (1 << CP0St_FR)) { + ret |= TARGET_PR_FP_MODE_FR; + } + if (env->CP0_Config5 & (1 << CP0C5_FRE)) { + ret |= TARGET_PR_FP_MODE_FRE; + } + return ret; + } case TARGET_PR_SET_FP_MODE: - /* TODO: Implement TARGET_PR_GET_FP_MODE handling.*/ - return -TARGET_EINVAL; + { + CPUMIPSState *env = ((CPUMIPSState *)cpu_env); + bool old_fr = env->CP0_Status & (1 << CP0St_FR); + bool new_fr = arg2 & TARGET_PR_FP_MODE_FR; + bool new_fre = arg2 & TARGET_PR_FP_MODE_FRE; + + if (new_fr && !(env->active_fpu.fcr0 & (1 << FCR0_F64))) { + /* FR1 is not supported */ + return -TARGET_EOPNOTSUPP; + } + if (!new_fr && (env->active_fpu.fcr0 & (1 << FCR0_F64)) + && !(env->CP0_Status_rw_bitmask & (1 << CP0St_FR))) { + /* cannot set FR=0 */ + return -TARGET_EOPNOTSUPP; + } + if (new_fre && !(env->active_fpu.fcr0 & (1 << FCR0_FREP))) { + /* Cannot set FRE=1 */ + return -TARGET_EOPNOTSUPP; + } + + int i; + fpr_t *fpr = env->active_fpu.fpr; + for (i = 0; i < 32 ; i += 2) { + 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); + env->hflags |= MIPS_HFLAG_F64; + } else { + env->CP0_Status &= ~(1 << CP0St_FR); + } + if (new_fre) { + env->CP0_Config5 |= (1 << CP0C5_FRE); + if (env->active_fpu.fcr0 & (1 << FCR0_FREP)) { + env->hflags |= MIPS_HFLAG_FRE; + } + } else { + env->CP0_Config5 &= ~(1 << CP0C5_FRE); + } + + return 0; + } #endif /* MIPS */ #ifdef TARGET_AARCH64 case TARGET_PR_SVE_SET_VL: