1cd71c089SLaurent Vivier /* 2cd71c089SLaurent Vivier * qemu user cpu loop 3cd71c089SLaurent Vivier * 4cd71c089SLaurent Vivier * Copyright (c) 2003-2008 Fabrice Bellard 5cd71c089SLaurent Vivier * 6cd71c089SLaurent Vivier * This program is free software; you can redistribute it and/or modify 7cd71c089SLaurent Vivier * it under the terms of the GNU General Public License as published by 8cd71c089SLaurent Vivier * the Free Software Foundation; either version 2 of the License, or 9cd71c089SLaurent Vivier * (at your option) any later version. 10cd71c089SLaurent Vivier * 11cd71c089SLaurent Vivier * This program is distributed in the hope that it will be useful, 12cd71c089SLaurent Vivier * but WITHOUT ANY WARRANTY; without even the implied warranty of 13cd71c089SLaurent Vivier * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14cd71c089SLaurent Vivier * GNU General Public License for more details. 15cd71c089SLaurent Vivier * 16cd71c089SLaurent Vivier * You should have received a copy of the GNU General Public License 17cd71c089SLaurent Vivier * along with this program; if not, see <http://www.gnu.org/licenses/>. 18cd71c089SLaurent Vivier */ 19cd71c089SLaurent Vivier 20cd71c089SLaurent Vivier #include "qemu/osdep.h" 21a8d25326SMarkus Armbruster #include "qemu-common.h" 22cd71c089SLaurent Vivier #include "qemu.h" 233b249d26SPeter Maydell #include "user-internals.h" 24cd71c089SLaurent Vivier #include "cpu_loop-common.h" 252113aed6SPeter Maydell #include "signal-common.h" 2658908ef6SLaurent Vivier #include "elf.h" 27502700d0SAlex Bennée #include "internal.h" 2881ddae7cSPhilippe Mathieu-Daudé #include "fpu_helper.h" 2958908ef6SLaurent Vivier 3058908ef6SLaurent Vivier # ifdef TARGET_ABI_MIPSO32 318d6d4c1bSAleksandar Markovic # define MIPS_SYSCALL_NUMBER_UNUSED -1 328d6d4c1bSAleksandar Markovic static const int8_t mips_syscall_args[] = { 33ac5d3c67SLaurent Vivier #include "syscall-args-o32.c.inc" 3458908ef6SLaurent Vivier }; 3558908ef6SLaurent Vivier # endif /* O32 */ 3658908ef6SLaurent Vivier 3758908ef6SLaurent Vivier /* Break codes */ 3858908ef6SLaurent Vivier enum { 3958908ef6SLaurent Vivier BRK_OVERFLOW = 6, 4058908ef6SLaurent Vivier BRK_DIVZERO = 7 4158908ef6SLaurent Vivier }; 4258908ef6SLaurent Vivier 4358908ef6SLaurent Vivier static int do_break(CPUMIPSState *env, target_siginfo_t *info, 4458908ef6SLaurent Vivier unsigned int code) 4558908ef6SLaurent Vivier { 4658908ef6SLaurent Vivier int ret = -1; 4758908ef6SLaurent Vivier 4858908ef6SLaurent Vivier switch (code) { 4958908ef6SLaurent Vivier case BRK_OVERFLOW: 5058908ef6SLaurent Vivier case BRK_DIVZERO: 5158908ef6SLaurent Vivier info->si_signo = TARGET_SIGFPE; 5258908ef6SLaurent Vivier info->si_errno = 0; 5358908ef6SLaurent Vivier info->si_code = (code == BRK_OVERFLOW) ? FPE_INTOVF : FPE_INTDIV; 5458908ef6SLaurent Vivier queue_signal(env, info->si_signo, QEMU_SI_FAULT, &*info); 5558908ef6SLaurent Vivier ret = 0; 5658908ef6SLaurent Vivier break; 5758908ef6SLaurent Vivier default: 5858908ef6SLaurent Vivier info->si_signo = TARGET_SIGTRAP; 5958908ef6SLaurent Vivier info->si_errno = 0; 6058908ef6SLaurent Vivier queue_signal(env, info->si_signo, QEMU_SI_FAULT, &*info); 6158908ef6SLaurent Vivier ret = 0; 6258908ef6SLaurent Vivier break; 6358908ef6SLaurent Vivier } 6458908ef6SLaurent Vivier 6558908ef6SLaurent Vivier return ret; 6658908ef6SLaurent Vivier } 6758908ef6SLaurent Vivier 6858908ef6SLaurent Vivier void cpu_loop(CPUMIPSState *env) 6958908ef6SLaurent Vivier { 705a7330b3SRichard Henderson CPUState *cs = env_cpu(env); 7158908ef6SLaurent Vivier target_siginfo_t info; 7258908ef6SLaurent Vivier int trapnr; 7358908ef6SLaurent Vivier abi_long ret; 7458908ef6SLaurent Vivier # ifdef TARGET_ABI_MIPSO32 7558908ef6SLaurent Vivier unsigned int syscall_num; 7658908ef6SLaurent Vivier # endif 7758908ef6SLaurent Vivier 7858908ef6SLaurent Vivier for(;;) { 7958908ef6SLaurent Vivier cpu_exec_start(cs); 8058908ef6SLaurent Vivier trapnr = cpu_exec(cs); 8158908ef6SLaurent Vivier cpu_exec_end(cs); 8258908ef6SLaurent Vivier process_queued_cpu_work(cs); 8358908ef6SLaurent Vivier 8458908ef6SLaurent Vivier switch(trapnr) { 8558908ef6SLaurent Vivier case EXCP_SYSCALL: 8658908ef6SLaurent Vivier env->active_tc.PC += 4; 8758908ef6SLaurent Vivier # ifdef TARGET_ABI_MIPSO32 8858908ef6SLaurent Vivier syscall_num = env->active_tc.gpr[2] - 4000; 8958908ef6SLaurent Vivier if (syscall_num >= sizeof(mips_syscall_args)) { 908d6d4c1bSAleksandar Markovic /* syscall_num is larger that any defined for MIPS O32 */ 918d6d4c1bSAleksandar Markovic ret = -TARGET_ENOSYS; 928d6d4c1bSAleksandar Markovic } else if (mips_syscall_args[syscall_num] == 938d6d4c1bSAleksandar Markovic MIPS_SYSCALL_NUMBER_UNUSED) { 948d6d4c1bSAleksandar Markovic /* syscall_num belongs to the range not defined for MIPS O32 */ 9558908ef6SLaurent Vivier ret = -TARGET_ENOSYS; 9658908ef6SLaurent Vivier } else { 978d6d4c1bSAleksandar Markovic /* syscall_num is valid */ 9858908ef6SLaurent Vivier int nb_args; 9958908ef6SLaurent Vivier abi_ulong sp_reg; 10058908ef6SLaurent Vivier abi_ulong arg5 = 0, arg6 = 0, arg7 = 0, arg8 = 0; 10158908ef6SLaurent Vivier 10258908ef6SLaurent Vivier nb_args = mips_syscall_args[syscall_num]; 10358908ef6SLaurent Vivier sp_reg = env->active_tc.gpr[29]; 10458908ef6SLaurent Vivier switch (nb_args) { 10558908ef6SLaurent Vivier /* these arguments are taken from the stack */ 10658908ef6SLaurent Vivier case 8: 10758908ef6SLaurent Vivier if ((ret = get_user_ual(arg8, sp_reg + 28)) != 0) { 10858908ef6SLaurent Vivier goto done_syscall; 10958908ef6SLaurent Vivier } 11081966c18SChen Qun /* fall through */ 11158908ef6SLaurent Vivier case 7: 11258908ef6SLaurent Vivier if ((ret = get_user_ual(arg7, sp_reg + 24)) != 0) { 11358908ef6SLaurent Vivier goto done_syscall; 11458908ef6SLaurent Vivier } 11581966c18SChen Qun /* fall through */ 11658908ef6SLaurent Vivier case 6: 11758908ef6SLaurent Vivier if ((ret = get_user_ual(arg6, sp_reg + 20)) != 0) { 11858908ef6SLaurent Vivier goto done_syscall; 11958908ef6SLaurent Vivier } 12081966c18SChen Qun /* fall through */ 12158908ef6SLaurent Vivier case 5: 12258908ef6SLaurent Vivier if ((ret = get_user_ual(arg5, sp_reg + 16)) != 0) { 12358908ef6SLaurent Vivier goto done_syscall; 12458908ef6SLaurent Vivier } 12581966c18SChen Qun /* fall through */ 12658908ef6SLaurent Vivier default: 12758908ef6SLaurent Vivier break; 12858908ef6SLaurent Vivier } 12958908ef6SLaurent Vivier ret = do_syscall(env, env->active_tc.gpr[2], 13058908ef6SLaurent Vivier env->active_tc.gpr[4], 13158908ef6SLaurent Vivier env->active_tc.gpr[5], 13258908ef6SLaurent Vivier env->active_tc.gpr[6], 13358908ef6SLaurent Vivier env->active_tc.gpr[7], 13458908ef6SLaurent Vivier arg5, arg6, arg7, arg8); 13558908ef6SLaurent Vivier } 13658908ef6SLaurent Vivier done_syscall: 13758908ef6SLaurent Vivier # else 13858908ef6SLaurent Vivier ret = do_syscall(env, env->active_tc.gpr[2], 13958908ef6SLaurent Vivier env->active_tc.gpr[4], env->active_tc.gpr[5], 14058908ef6SLaurent Vivier env->active_tc.gpr[6], env->active_tc.gpr[7], 14158908ef6SLaurent Vivier env->active_tc.gpr[8], env->active_tc.gpr[9], 14258908ef6SLaurent Vivier env->active_tc.gpr[10], env->active_tc.gpr[11]); 14358908ef6SLaurent Vivier # endif /* O32 */ 144af254a27SRichard Henderson if (ret == -QEMU_ERESTARTSYS) { 14558908ef6SLaurent Vivier env->active_tc.PC -= 4; 14658908ef6SLaurent Vivier break; 14758908ef6SLaurent Vivier } 148*57a0c938SRichard Henderson if (ret == -QEMU_ESIGRETURN) { 14958908ef6SLaurent Vivier /* Returning from a successful sigreturn syscall. 15058908ef6SLaurent Vivier Avoid clobbering register state. */ 15158908ef6SLaurent Vivier break; 15258908ef6SLaurent Vivier } 15358908ef6SLaurent Vivier if ((abi_ulong)ret >= (abi_ulong)-1133) { 15458908ef6SLaurent Vivier env->active_tc.gpr[7] = 1; /* error flag */ 15558908ef6SLaurent Vivier ret = -ret; 15658908ef6SLaurent Vivier } else { 15758908ef6SLaurent Vivier env->active_tc.gpr[7] = 0; /* error flag */ 15858908ef6SLaurent Vivier } 15958908ef6SLaurent Vivier env->active_tc.gpr[2] = ret; 16058908ef6SLaurent Vivier break; 16158908ef6SLaurent Vivier case EXCP_CpU: 16258908ef6SLaurent Vivier case EXCP_RI: 16358908ef6SLaurent Vivier info.si_signo = TARGET_SIGILL; 16458908ef6SLaurent Vivier info.si_errno = 0; 16558908ef6SLaurent Vivier info.si_code = 0; 16658908ef6SLaurent Vivier queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); 16758908ef6SLaurent Vivier break; 16858908ef6SLaurent Vivier case EXCP_INTERRUPT: 16958908ef6SLaurent Vivier /* just indicate that signals should be handled asap */ 17058908ef6SLaurent Vivier break; 17158908ef6SLaurent Vivier case EXCP_DEBUG: 172b10089a1SPeter Maydell info.si_signo = TARGET_SIGTRAP; 17358908ef6SLaurent Vivier info.si_errno = 0; 17458908ef6SLaurent Vivier info.si_code = TARGET_TRAP_BRKPT; 17558908ef6SLaurent Vivier queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); 17658908ef6SLaurent Vivier break; 17758908ef6SLaurent Vivier case EXCP_DSPDIS: 17858908ef6SLaurent Vivier info.si_signo = TARGET_SIGILL; 17958908ef6SLaurent Vivier info.si_errno = 0; 18058908ef6SLaurent Vivier info.si_code = TARGET_ILL_ILLOPC; 18158908ef6SLaurent Vivier queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); 18258908ef6SLaurent Vivier break; 18364ce541cSAleksandar Markovic case EXCP_FPE: 18464ce541cSAleksandar Markovic info.si_signo = TARGET_SIGFPE; 18564ce541cSAleksandar Markovic info.si_errno = 0; 18664ce541cSAleksandar Markovic info.si_code = TARGET_FPE_FLTUNK; 18764ce541cSAleksandar Markovic if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_INVALID) { 18864ce541cSAleksandar Markovic info.si_code = TARGET_FPE_FLTINV; 18964ce541cSAleksandar Markovic } else if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_DIV0) { 19064ce541cSAleksandar Markovic info.si_code = TARGET_FPE_FLTDIV; 19164ce541cSAleksandar Markovic } else if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_OVERFLOW) { 19264ce541cSAleksandar Markovic info.si_code = TARGET_FPE_FLTOVF; 19364ce541cSAleksandar Markovic } else if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_UNDERFLOW) { 19464ce541cSAleksandar Markovic info.si_code = TARGET_FPE_FLTUND; 19564ce541cSAleksandar Markovic } else if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_INEXACT) { 19664ce541cSAleksandar Markovic info.si_code = TARGET_FPE_FLTRES; 19764ce541cSAleksandar Markovic } 19864ce541cSAleksandar Markovic queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); 19964ce541cSAleksandar Markovic break; 20058908ef6SLaurent Vivier /* The code below was inspired by the MIPS Linux kernel trap 20158908ef6SLaurent Vivier * handling code in arch/mips/kernel/traps.c. 20258908ef6SLaurent Vivier */ 20358908ef6SLaurent Vivier case EXCP_BREAK: 20458908ef6SLaurent Vivier { 20558908ef6SLaurent Vivier abi_ulong trap_instr; 20658908ef6SLaurent Vivier unsigned int code; 20758908ef6SLaurent Vivier 20858908ef6SLaurent Vivier if (env->hflags & MIPS_HFLAG_M16) { 20958908ef6SLaurent Vivier if (env->insn_flags & ASE_MICROMIPS) { 21058908ef6SLaurent Vivier /* microMIPS mode */ 21158908ef6SLaurent Vivier ret = get_user_u16(trap_instr, env->active_tc.PC); 21258908ef6SLaurent Vivier if (ret != 0) { 21358908ef6SLaurent Vivier goto error; 21458908ef6SLaurent Vivier } 21558908ef6SLaurent Vivier 21658908ef6SLaurent Vivier if ((trap_instr >> 10) == 0x11) { 21758908ef6SLaurent Vivier /* 16-bit instruction */ 21858908ef6SLaurent Vivier code = trap_instr & 0xf; 21958908ef6SLaurent Vivier } else { 22058908ef6SLaurent Vivier /* 32-bit instruction */ 22158908ef6SLaurent Vivier abi_ulong instr_lo; 22258908ef6SLaurent Vivier 22358908ef6SLaurent Vivier ret = get_user_u16(instr_lo, 22458908ef6SLaurent Vivier env->active_tc.PC + 2); 22558908ef6SLaurent Vivier if (ret != 0) { 22658908ef6SLaurent Vivier goto error; 22758908ef6SLaurent Vivier } 22858908ef6SLaurent Vivier trap_instr = (trap_instr << 16) | instr_lo; 22958908ef6SLaurent Vivier code = ((trap_instr >> 6) & ((1 << 20) - 1)); 23058908ef6SLaurent Vivier /* Unfortunately, microMIPS also suffers from 23158908ef6SLaurent Vivier the old assembler bug... */ 23258908ef6SLaurent Vivier if (code >= (1 << 10)) { 23358908ef6SLaurent Vivier code >>= 10; 23458908ef6SLaurent Vivier } 23558908ef6SLaurent Vivier } 23658908ef6SLaurent Vivier } else { 23758908ef6SLaurent Vivier /* MIPS16e mode */ 23858908ef6SLaurent Vivier ret = get_user_u16(trap_instr, env->active_tc.PC); 23958908ef6SLaurent Vivier if (ret != 0) { 24058908ef6SLaurent Vivier goto error; 24158908ef6SLaurent Vivier } 24258908ef6SLaurent Vivier code = (trap_instr >> 6) & 0x3f; 24358908ef6SLaurent Vivier } 24458908ef6SLaurent Vivier } else { 24558908ef6SLaurent Vivier ret = get_user_u32(trap_instr, env->active_tc.PC); 24658908ef6SLaurent Vivier if (ret != 0) { 24758908ef6SLaurent Vivier goto error; 24858908ef6SLaurent Vivier } 24958908ef6SLaurent Vivier 25058908ef6SLaurent Vivier /* As described in the original Linux kernel code, the 25158908ef6SLaurent Vivier * below checks on 'code' are to work around an old 25258908ef6SLaurent Vivier * assembly bug. 25358908ef6SLaurent Vivier */ 25458908ef6SLaurent Vivier code = ((trap_instr >> 6) & ((1 << 20) - 1)); 25558908ef6SLaurent Vivier if (code >= (1 << 10)) { 25658908ef6SLaurent Vivier code >>= 10; 25758908ef6SLaurent Vivier } 25858908ef6SLaurent Vivier } 25958908ef6SLaurent Vivier 26058908ef6SLaurent Vivier if (do_break(env, &info, code) != 0) { 26158908ef6SLaurent Vivier goto error; 26258908ef6SLaurent Vivier } 26358908ef6SLaurent Vivier } 26458908ef6SLaurent Vivier break; 26558908ef6SLaurent Vivier case EXCP_TRAP: 26658908ef6SLaurent Vivier { 26758908ef6SLaurent Vivier abi_ulong trap_instr; 26858908ef6SLaurent Vivier unsigned int code = 0; 26958908ef6SLaurent Vivier 27058908ef6SLaurent Vivier if (env->hflags & MIPS_HFLAG_M16) { 27158908ef6SLaurent Vivier /* microMIPS mode */ 27258908ef6SLaurent Vivier abi_ulong instr[2]; 27358908ef6SLaurent Vivier 27458908ef6SLaurent Vivier ret = get_user_u16(instr[0], env->active_tc.PC) || 27558908ef6SLaurent Vivier get_user_u16(instr[1], env->active_tc.PC + 2); 27658908ef6SLaurent Vivier 27758908ef6SLaurent Vivier trap_instr = (instr[0] << 16) | instr[1]; 27858908ef6SLaurent Vivier } else { 27958908ef6SLaurent Vivier ret = get_user_u32(trap_instr, env->active_tc.PC); 28058908ef6SLaurent Vivier } 28158908ef6SLaurent Vivier 28258908ef6SLaurent Vivier if (ret != 0) { 28358908ef6SLaurent Vivier goto error; 28458908ef6SLaurent Vivier } 28558908ef6SLaurent Vivier 28658908ef6SLaurent Vivier /* The immediate versions don't provide a code. */ 28758908ef6SLaurent Vivier if (!(trap_instr & 0xFC000000)) { 28858908ef6SLaurent Vivier if (env->hflags & MIPS_HFLAG_M16) { 28958908ef6SLaurent Vivier /* microMIPS mode */ 29058908ef6SLaurent Vivier code = ((trap_instr >> 12) & ((1 << 4) - 1)); 29158908ef6SLaurent Vivier } else { 29258908ef6SLaurent Vivier code = ((trap_instr >> 6) & ((1 << 10) - 1)); 29358908ef6SLaurent Vivier } 29458908ef6SLaurent Vivier } 29558908ef6SLaurent Vivier 29658908ef6SLaurent Vivier if (do_break(env, &info, code) != 0) { 29758908ef6SLaurent Vivier goto error; 29858908ef6SLaurent Vivier } 29958908ef6SLaurent Vivier } 30058908ef6SLaurent Vivier break; 30158908ef6SLaurent Vivier case EXCP_ATOMIC: 30258908ef6SLaurent Vivier cpu_exec_step_atomic(cs); 30358908ef6SLaurent Vivier break; 30458908ef6SLaurent Vivier default: 30558908ef6SLaurent Vivier error: 30658908ef6SLaurent Vivier EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); 30758908ef6SLaurent Vivier abort(); 30858908ef6SLaurent Vivier } 30958908ef6SLaurent Vivier process_pending_signals(env); 31058908ef6SLaurent Vivier } 31158908ef6SLaurent Vivier } 312cd71c089SLaurent Vivier 313cd71c089SLaurent Vivier void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) 314cd71c089SLaurent Vivier { 31529a0af61SRichard Henderson CPUState *cpu = env_cpu(env); 31658908ef6SLaurent Vivier TaskState *ts = cpu->opaque; 31758908ef6SLaurent Vivier struct image_info *info = ts->info; 31858908ef6SLaurent Vivier int i; 31958908ef6SLaurent Vivier 3200c1bbedcSStefan Markovic struct mode_req { 3210c1bbedcSStefan Markovic bool single; 3220c1bbedcSStefan Markovic bool soft; 3230c1bbedcSStefan Markovic bool fr1; 3240c1bbedcSStefan Markovic bool frdefault; 3250c1bbedcSStefan Markovic bool fre; 3260c1bbedcSStefan Markovic }; 3270c1bbedcSStefan Markovic 3280c1bbedcSStefan Markovic static const struct mode_req fpu_reqs[] = { 3290c1bbedcSStefan Markovic [MIPS_ABI_FP_ANY] = { true, true, true, true, true }, 3300c1bbedcSStefan Markovic [MIPS_ABI_FP_DOUBLE] = { false, false, false, true, true }, 3310c1bbedcSStefan Markovic [MIPS_ABI_FP_SINGLE] = { true, false, false, false, false }, 3320c1bbedcSStefan Markovic [MIPS_ABI_FP_SOFT] = { false, true, false, false, false }, 3330c1bbedcSStefan Markovic [MIPS_ABI_FP_OLD_64] = { false, false, false, false, false }, 3340c1bbedcSStefan Markovic [MIPS_ABI_FP_XX] = { false, false, true, true, true }, 3350c1bbedcSStefan Markovic [MIPS_ABI_FP_64] = { false, false, true, false, false }, 3360c1bbedcSStefan Markovic [MIPS_ABI_FP_64A] = { false, false, true, false, true } 3370c1bbedcSStefan Markovic }; 3380c1bbedcSStefan Markovic 3390c1bbedcSStefan Markovic /* 3400c1bbedcSStefan Markovic * Mode requirements when .MIPS.abiflags is not present in the ELF. 3410c1bbedcSStefan Markovic * Not present means that everything is acceptable except FR1. 3420c1bbedcSStefan Markovic */ 3430c1bbedcSStefan Markovic static struct mode_req none_req = { true, true, false, true, true }; 3440c1bbedcSStefan Markovic 3450c1bbedcSStefan Markovic struct mode_req prog_req; 3460c1bbedcSStefan Markovic struct mode_req interp_req; 3470c1bbedcSStefan Markovic 34858908ef6SLaurent Vivier for(i = 0; i < 32; i++) { 34958908ef6SLaurent Vivier env->active_tc.gpr[i] = regs->regs[i]; 35058908ef6SLaurent Vivier } 35158908ef6SLaurent Vivier env->active_tc.PC = regs->cp0_epc & ~(target_ulong)1; 35258908ef6SLaurent Vivier if (regs->cp0_epc & 1) { 35358908ef6SLaurent Vivier env->hflags |= MIPS_HFLAG_M16; 35458908ef6SLaurent Vivier } 3550c1bbedcSStefan Markovic 3560c1bbedcSStefan Markovic #ifdef TARGET_ABI_MIPSO32 3570c1bbedcSStefan Markovic # define MAX_FP_ABI MIPS_ABI_FP_64A 3580c1bbedcSStefan Markovic #else 3590c1bbedcSStefan Markovic # define MAX_FP_ABI MIPS_ABI_FP_SOFT 3600c1bbedcSStefan Markovic #endif 3610c1bbedcSStefan Markovic if ((info->fp_abi > MAX_FP_ABI && info->fp_abi != MIPS_ABI_FP_UNKNOWN) 3620c1bbedcSStefan Markovic || (info->interp_fp_abi > MAX_FP_ABI && 3630c1bbedcSStefan Markovic info->interp_fp_abi != MIPS_ABI_FP_UNKNOWN)) { 3640c1bbedcSStefan Markovic fprintf(stderr, "qemu: Unexpected FPU mode\n"); 3650c1bbedcSStefan Markovic exit(1); 3660c1bbedcSStefan Markovic } 3670c1bbedcSStefan Markovic 3680c1bbedcSStefan Markovic prog_req = (info->fp_abi == MIPS_ABI_FP_UNKNOWN) ? none_req 3690c1bbedcSStefan Markovic : fpu_reqs[info->fp_abi]; 3700c1bbedcSStefan Markovic interp_req = (info->interp_fp_abi == MIPS_ABI_FP_UNKNOWN) ? none_req 3710c1bbedcSStefan Markovic : fpu_reqs[info->interp_fp_abi]; 3720c1bbedcSStefan Markovic 3730c1bbedcSStefan Markovic prog_req.single &= interp_req.single; 3740c1bbedcSStefan Markovic prog_req.soft &= interp_req.soft; 3750c1bbedcSStefan Markovic prog_req.fr1 &= interp_req.fr1; 3760c1bbedcSStefan Markovic prog_req.frdefault &= interp_req.frdefault; 3770c1bbedcSStefan Markovic prog_req.fre &= interp_req.fre; 3780c1bbedcSStefan Markovic 3797a47bae5SPhilippe Mathieu-Daudé bool cpu_has_mips_r2_r6 = env->insn_flags & ISA_MIPS_R2 || 3802e211e0aSPhilippe Mathieu-Daudé env->insn_flags & ISA_MIPS_R6; 3810c1bbedcSStefan Markovic 3820c1bbedcSStefan Markovic if (prog_req.fre && !prog_req.frdefault && !prog_req.fr1) { 3830c1bbedcSStefan Markovic env->CP0_Config5 |= (1 << CP0C5_FRE); 3840c1bbedcSStefan Markovic if (env->active_fpu.fcr0 & (1 << FCR0_FREP)) { 3850c1bbedcSStefan Markovic env->hflags |= MIPS_HFLAG_FRE; 3860c1bbedcSStefan Markovic } 3870c1bbedcSStefan Markovic } else if ((prog_req.fr1 && prog_req.frdefault) || 3880c1bbedcSStefan Markovic (prog_req.single && !prog_req.frdefault)) { 3890c1bbedcSStefan Markovic if ((env->active_fpu.fcr0 & (1 << FCR0_F64) 3900c1bbedcSStefan Markovic && cpu_has_mips_r2_r6) || prog_req.fr1) { 3910c1bbedcSStefan Markovic env->CP0_Status |= (1 << CP0St_FR); 3920c1bbedcSStefan Markovic env->hflags |= MIPS_HFLAG_F64; 3930c1bbedcSStefan Markovic } 3940c1bbedcSStefan Markovic } else if (!prog_req.fre && !prog_req.frdefault && 3950c1bbedcSStefan Markovic !prog_req.fr1 && !prog_req.single && !prog_req.soft) { 3960c1bbedcSStefan Markovic fprintf(stderr, "qemu: Can't find a matching FPU mode\n"); 3970c1bbedcSStefan Markovic exit(1); 3980c1bbedcSStefan Markovic } 3990c1bbedcSStefan Markovic 400722ac96cSAleksandar Markovic if (env->insn_flags & ISA_NANOMIPS32) { 401722ac96cSAleksandar Markovic return; 402722ac96cSAleksandar Markovic } 40358908ef6SLaurent Vivier if (((info->elf_flags & EF_MIPS_NAN2008) != 0) != 40458908ef6SLaurent Vivier ((env->active_fpu.fcr31 & (1 << FCR31_NAN2008)) != 0)) { 40558908ef6SLaurent Vivier if ((env->active_fpu.fcr31_rw_bitmask & 40658908ef6SLaurent Vivier (1 << FCR31_NAN2008)) == 0) { 40758908ef6SLaurent Vivier fprintf(stderr, "ELF binary's NaN mode not supported by CPU\n"); 40858908ef6SLaurent Vivier exit(1); 40958908ef6SLaurent Vivier } 41058908ef6SLaurent Vivier if ((info->elf_flags & EF_MIPS_NAN2008) != 0) { 41158908ef6SLaurent Vivier env->active_fpu.fcr31 |= (1 << FCR31_NAN2008); 41258908ef6SLaurent Vivier } else { 41358908ef6SLaurent Vivier env->active_fpu.fcr31 &= ~(1 << FCR31_NAN2008); 41458908ef6SLaurent Vivier } 41558908ef6SLaurent Vivier restore_snan_bit_mode(env); 41658908ef6SLaurent Vivier } 417cd71c089SLaurent Vivier } 418