aboutsummaryrefslogtreecommitdiff
path: root/linux-user
diff options
context:
space:
mode:
Diffstat (limited to 'linux-user')
-rw-r--r--linux-user/elfload.c37
-rw-r--r--linux-user/mips/cpu_loop.c75
-rw-r--r--linux-user/mips/target_syscall.h2
-rw-r--r--linux-user/mips64/target_syscall.h2
-rw-r--r--linux-user/qemu.h4
-rw-r--r--linux-user/syscall.c62
6 files changed, 178 insertions, 4 deletions
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 055f6a95ab..5bccd2e243 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -1517,11 +1517,25 @@ static void bswap_sym(struct elf_sym *sym)
bswaptls(&sym->st_size);
bswap16s(&sym->st_shndx);
}
+
+#ifdef TARGET_MIPS
+static void bswap_mips_abiflags(Mips_elf_abiflags_v0 *abiflags)
+{
+ bswap16s(&abiflags->version);
+ bswap32s(&abiflags->ases);
+ bswap32s(&abiflags->isa_ext);
+ bswap32s(&abiflags->flags1);
+ bswap32s(&abiflags->flags2);
+}
+#endif
#else
static inline void bswap_ehdr(struct elfhdr *ehdr) { }
static inline void bswap_phdr(struct elf_phdr *phdr, int phnum) { }
static inline void bswap_shdr(struct elf_shdr *shdr, int shnum) { }
static inline void bswap_sym(struct elf_sym *sym) { }
+#ifdef TARGET_MIPS
+static inline void bswap_mips_abiflags(Mips_elf_abiflags_v0 *abiflags) { }
+#endif
#endif
#ifdef USE_ELF_CORE_DUMP
@@ -2364,6 +2378,26 @@ static void load_elf_image(const char *image_name, int image_fd,
goto exit_errmsg;
}
*pinterp_name = interp_name;
+#ifdef TARGET_MIPS
+ } else if (eppnt->p_type == PT_MIPS_ABIFLAGS) {
+ Mips_elf_abiflags_v0 abiflags;
+ if (eppnt->p_filesz < sizeof(Mips_elf_abiflags_v0)) {
+ errmsg = "Invalid PT_MIPS_ABIFLAGS entry";
+ goto exit_errmsg;
+ }
+ if (eppnt->p_offset + eppnt->p_filesz <= BPRM_BUF_SIZE) {
+ memcpy(&abiflags, bprm_buf + eppnt->p_offset,
+ sizeof(Mips_elf_abiflags_v0));
+ } else {
+ retval = pread(image_fd, &abiflags, sizeof(Mips_elf_abiflags_v0),
+ eppnt->p_offset);
+ if (retval != sizeof(Mips_elf_abiflags_v0)) {
+ goto exit_perror;
+ }
+ }
+ bswap_mips_abiflags(&abiflags);
+ info->fp_abi = abiflags.fp_abi;
+#endif
}
}
@@ -2675,6 +2709,9 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info)
target_mmap(0, qemu_host_page_size, PROT_READ | PROT_EXEC,
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
}
+#ifdef TARGET_MIPS
+ info->interp_fp_abi = interp_info.fp_abi;
+#endif
}
bprm->p = create_elf_tables(bprm->p, bprm->argc, bprm->envc, &elf_ex,
diff --git a/linux-user/mips/cpu_loop.c b/linux-user/mips/cpu_loop.c
index c9c20cf8b7..97e495747f 100644
--- a/linux-user/mips/cpu_loop.c
+++ b/linux-user/mips/cpu_loop.c
@@ -740,6 +740,34 @@ void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs)
struct image_info *info = ts->info;
int i;
+ struct mode_req {
+ bool single;
+ bool soft;
+ bool fr1;
+ bool frdefault;
+ bool fre;
+ };
+
+ static const struct mode_req fpu_reqs[] = {
+ [MIPS_ABI_FP_ANY] = { true, true, true, true, true },
+ [MIPS_ABI_FP_DOUBLE] = { false, false, false, true, true },
+ [MIPS_ABI_FP_SINGLE] = { true, false, false, false, false },
+ [MIPS_ABI_FP_SOFT] = { false, true, false, false, false },
+ [MIPS_ABI_FP_OLD_64] = { false, false, false, false, false },
+ [MIPS_ABI_FP_XX] = { false, false, true, true, true },
+ [MIPS_ABI_FP_64] = { false, false, true, false, false },
+ [MIPS_ABI_FP_64A] = { false, false, true, false, true }
+ };
+
+ /*
+ * Mode requirements when .MIPS.abiflags is not present in the ELF.
+ * Not present means that everything is acceptable except FR1.
+ */
+ static struct mode_req none_req = { true, true, false, true, true };
+
+ struct mode_req prog_req;
+ struct mode_req interp_req;
+
for(i = 0; i < 32; i++) {
env->active_tc.gpr[i] = regs->regs[i];
}
@@ -747,6 +775,53 @@ void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs)
if (regs->cp0_epc & 1) {
env->hflags |= MIPS_HFLAG_M16;
}
+
+#ifdef TARGET_ABI_MIPSO32
+# define MAX_FP_ABI MIPS_ABI_FP_64A
+#else
+# define MAX_FP_ABI MIPS_ABI_FP_SOFT
+#endif
+ if ((info->fp_abi > MAX_FP_ABI && info->fp_abi != MIPS_ABI_FP_UNKNOWN)
+ || (info->interp_fp_abi > MAX_FP_ABI &&
+ info->interp_fp_abi != MIPS_ABI_FP_UNKNOWN)) {
+ fprintf(stderr, "qemu: Unexpected FPU mode\n");
+ exit(1);
+ }
+
+ prog_req = (info->fp_abi == MIPS_ABI_FP_UNKNOWN) ? none_req
+ : fpu_reqs[info->fp_abi];
+ interp_req = (info->interp_fp_abi == MIPS_ABI_FP_UNKNOWN) ? none_req
+ : fpu_reqs[info->interp_fp_abi];
+
+ prog_req.single &= interp_req.single;
+ prog_req.soft &= interp_req.soft;
+ prog_req.fr1 &= interp_req.fr1;
+ prog_req.frdefault &= interp_req.frdefault;
+ prog_req.fre &= interp_req.fre;
+
+ bool cpu_has_mips_r2_r6 = env->insn_flags & ISA_MIPS32R2 ||
+ env->insn_flags & ISA_MIPS64R2 ||
+ env->insn_flags & ISA_MIPS32R6 ||
+ env->insn_flags & ISA_MIPS64R6;
+
+ if (prog_req.fre && !prog_req.frdefault && !prog_req.fr1) {
+ env->CP0_Config5 |= (1 << CP0C5_FRE);
+ if (env->active_fpu.fcr0 & (1 << FCR0_FREP)) {
+ env->hflags |= MIPS_HFLAG_FRE;
+ }
+ } else if ((prog_req.fr1 && prog_req.frdefault) ||
+ (prog_req.single && !prog_req.frdefault)) {
+ if ((env->active_fpu.fcr0 & (1 << FCR0_F64)
+ && cpu_has_mips_r2_r6) || prog_req.fr1) {
+ env->CP0_Status |= (1 << CP0St_FR);
+ env->hflags |= MIPS_HFLAG_F64;
+ }
+ } else if (!prog_req.fre && !prog_req.frdefault &&
+ !prog_req.fr1 && !prog_req.single && !prog_req.soft) {
+ fprintf(stderr, "qemu: Can't find a matching FPU mode\n");
+ exit(1);
+ }
+
if (env->insn_flags & ISA_NANOMIPS32) {
return;
}
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/qemu.h b/linux-user/qemu.h
index dde3f26f5a..dd5771ce0c 100644
--- a/linux-user/qemu.h
+++ b/linux-user/qemu.h
@@ -61,6 +61,10 @@ struct image_info {
abi_ulong interpreter_loadmap_addr;
abi_ulong interpreter_pt_dynamic_addr;
struct image_info *other_info;
+#ifdef TARGET_MIPS
+ int fp_abi;
+ int interp_fp_abi;
+#endif
};
#ifdef TARGET_I386
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: