diff options
Diffstat (limited to 'linux-user')
-rw-r--r-- | linux-user/elfload.c | 37 | ||||
-rw-r--r-- | linux-user/mips/cpu_loop.c | 75 | ||||
-rw-r--r-- | linux-user/mips/target_syscall.h | 2 | ||||
-rw-r--r-- | linux-user/mips64/target_syscall.h | 2 | ||||
-rw-r--r-- | linux-user/qemu.h | 4 | ||||
-rw-r--r-- | linux-user/syscall.c | 62 |
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: |