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" 23cd71c089SLaurent Vivier #include "cpu_loop-common.h" 2458908ef6SLaurent Vivier #include "elf.h" 25502700d0SAlex Bennée #include "internal.h" 2658908ef6SLaurent Vivier 2758908ef6SLaurent Vivier # ifdef TARGET_ABI_MIPSO32 288d6d4c1bSAleksandar Markovic # define MIPS_SYSCALL_NUMBER_UNUSED -1 298d6d4c1bSAleksandar Markovic static const int8_t mips_syscall_args[] = { 30ac5d3c67SLaurent Vivier #include "syscall-args-o32.c.inc" 3158908ef6SLaurent Vivier }; 3258908ef6SLaurent Vivier # endif /* O32 */ 3358908ef6SLaurent Vivier 3458908ef6SLaurent Vivier /* Break codes */ 3558908ef6SLaurent Vivier enum { 3658908ef6SLaurent Vivier BRK_OVERFLOW = 6, 3758908ef6SLaurent Vivier BRK_DIVZERO = 7 3858908ef6SLaurent Vivier }; 3958908ef6SLaurent Vivier 4058908ef6SLaurent Vivier static int do_break(CPUMIPSState *env, target_siginfo_t *info, 4158908ef6SLaurent Vivier unsigned int code) 4258908ef6SLaurent Vivier { 4358908ef6SLaurent Vivier int ret = -1; 4458908ef6SLaurent Vivier 4558908ef6SLaurent Vivier switch (code) { 4658908ef6SLaurent Vivier case BRK_OVERFLOW: 4758908ef6SLaurent Vivier case BRK_DIVZERO: 4858908ef6SLaurent Vivier info->si_signo = TARGET_SIGFPE; 4958908ef6SLaurent Vivier info->si_errno = 0; 5058908ef6SLaurent Vivier info->si_code = (code == BRK_OVERFLOW) ? FPE_INTOVF : FPE_INTDIV; 5158908ef6SLaurent Vivier queue_signal(env, info->si_signo, QEMU_SI_FAULT, &*info); 5258908ef6SLaurent Vivier ret = 0; 5358908ef6SLaurent Vivier break; 5458908ef6SLaurent Vivier default: 5558908ef6SLaurent Vivier info->si_signo = TARGET_SIGTRAP; 5658908ef6SLaurent Vivier info->si_errno = 0; 5758908ef6SLaurent Vivier queue_signal(env, info->si_signo, QEMU_SI_FAULT, &*info); 5858908ef6SLaurent Vivier ret = 0; 5958908ef6SLaurent Vivier break; 6058908ef6SLaurent Vivier } 6158908ef6SLaurent Vivier 6258908ef6SLaurent Vivier return ret; 6358908ef6SLaurent Vivier } 6458908ef6SLaurent Vivier 6558908ef6SLaurent Vivier void cpu_loop(CPUMIPSState *env) 6658908ef6SLaurent Vivier { 675a7330b3SRichard Henderson CPUState *cs = env_cpu(env); 6858908ef6SLaurent Vivier target_siginfo_t info; 6958908ef6SLaurent Vivier int trapnr; 7058908ef6SLaurent Vivier abi_long ret; 7158908ef6SLaurent Vivier # ifdef TARGET_ABI_MIPSO32 7258908ef6SLaurent Vivier unsigned int syscall_num; 7358908ef6SLaurent Vivier # endif 7458908ef6SLaurent Vivier 7558908ef6SLaurent Vivier for(;;) { 7658908ef6SLaurent Vivier cpu_exec_start(cs); 7758908ef6SLaurent Vivier trapnr = cpu_exec(cs); 7858908ef6SLaurent Vivier cpu_exec_end(cs); 7958908ef6SLaurent Vivier process_queued_cpu_work(cs); 8058908ef6SLaurent Vivier 8158908ef6SLaurent Vivier switch(trapnr) { 8258908ef6SLaurent Vivier case EXCP_SYSCALL: 8358908ef6SLaurent Vivier env->active_tc.PC += 4; 8458908ef6SLaurent Vivier # ifdef TARGET_ABI_MIPSO32 8558908ef6SLaurent Vivier syscall_num = env->active_tc.gpr[2] - 4000; 8658908ef6SLaurent Vivier if (syscall_num >= sizeof(mips_syscall_args)) { 878d6d4c1bSAleksandar Markovic /* syscall_num is larger that any defined for MIPS O32 */ 888d6d4c1bSAleksandar Markovic ret = -TARGET_ENOSYS; 898d6d4c1bSAleksandar Markovic } else if (mips_syscall_args[syscall_num] == 908d6d4c1bSAleksandar Markovic MIPS_SYSCALL_NUMBER_UNUSED) { 918d6d4c1bSAleksandar Markovic /* syscall_num belongs to the range not defined for MIPS O32 */ 9258908ef6SLaurent Vivier ret = -TARGET_ENOSYS; 9358908ef6SLaurent Vivier } else { 948d6d4c1bSAleksandar Markovic /* syscall_num is valid */ 9558908ef6SLaurent Vivier int nb_args; 9658908ef6SLaurent Vivier abi_ulong sp_reg; 9758908ef6SLaurent Vivier abi_ulong arg5 = 0, arg6 = 0, arg7 = 0, arg8 = 0; 9858908ef6SLaurent Vivier 9958908ef6SLaurent Vivier nb_args = mips_syscall_args[syscall_num]; 10058908ef6SLaurent Vivier sp_reg = env->active_tc.gpr[29]; 10158908ef6SLaurent Vivier switch (nb_args) { 10258908ef6SLaurent Vivier /* these arguments are taken from the stack */ 10358908ef6SLaurent Vivier case 8: 10458908ef6SLaurent Vivier if ((ret = get_user_ual(arg8, sp_reg + 28)) != 0) { 10558908ef6SLaurent Vivier goto done_syscall; 10658908ef6SLaurent Vivier } 107*81966c18SChen Qun /* fall through */ 10858908ef6SLaurent Vivier case 7: 10958908ef6SLaurent Vivier if ((ret = get_user_ual(arg7, sp_reg + 24)) != 0) { 11058908ef6SLaurent Vivier goto done_syscall; 11158908ef6SLaurent Vivier } 112*81966c18SChen Qun /* fall through */ 11358908ef6SLaurent Vivier case 6: 11458908ef6SLaurent Vivier if ((ret = get_user_ual(arg6, sp_reg + 20)) != 0) { 11558908ef6SLaurent Vivier goto done_syscall; 11658908ef6SLaurent Vivier } 117*81966c18SChen Qun /* fall through */ 11858908ef6SLaurent Vivier case 5: 11958908ef6SLaurent Vivier if ((ret = get_user_ual(arg5, sp_reg + 16)) != 0) { 12058908ef6SLaurent Vivier goto done_syscall; 12158908ef6SLaurent Vivier } 122*81966c18SChen Qun /* fall through */ 12358908ef6SLaurent Vivier default: 12458908ef6SLaurent Vivier break; 12558908ef6SLaurent Vivier } 12658908ef6SLaurent Vivier ret = do_syscall(env, env->active_tc.gpr[2], 12758908ef6SLaurent Vivier env->active_tc.gpr[4], 12858908ef6SLaurent Vivier env->active_tc.gpr[5], 12958908ef6SLaurent Vivier env->active_tc.gpr[6], 13058908ef6SLaurent Vivier env->active_tc.gpr[7], 13158908ef6SLaurent Vivier arg5, arg6, arg7, arg8); 13258908ef6SLaurent Vivier } 13358908ef6SLaurent Vivier done_syscall: 13458908ef6SLaurent Vivier # else 13558908ef6SLaurent Vivier ret = do_syscall(env, env->active_tc.gpr[2], 13658908ef6SLaurent Vivier env->active_tc.gpr[4], env->active_tc.gpr[5], 13758908ef6SLaurent Vivier env->active_tc.gpr[6], env->active_tc.gpr[7], 13858908ef6SLaurent Vivier env->active_tc.gpr[8], env->active_tc.gpr[9], 13958908ef6SLaurent Vivier env->active_tc.gpr[10], env->active_tc.gpr[11]); 14058908ef6SLaurent Vivier # endif /* O32 */ 14158908ef6SLaurent Vivier if (ret == -TARGET_ERESTARTSYS) { 14258908ef6SLaurent Vivier env->active_tc.PC -= 4; 14358908ef6SLaurent Vivier break; 14458908ef6SLaurent Vivier } 14558908ef6SLaurent Vivier if (ret == -TARGET_QEMU_ESIGRETURN) { 14658908ef6SLaurent Vivier /* Returning from a successful sigreturn syscall. 14758908ef6SLaurent Vivier Avoid clobbering register state. */ 14858908ef6SLaurent Vivier break; 14958908ef6SLaurent Vivier } 15058908ef6SLaurent Vivier if ((abi_ulong)ret >= (abi_ulong)-1133) { 15158908ef6SLaurent Vivier env->active_tc.gpr[7] = 1; /* error flag */ 15258908ef6SLaurent Vivier ret = -ret; 15358908ef6SLaurent Vivier } else { 15458908ef6SLaurent Vivier env->active_tc.gpr[7] = 0; /* error flag */ 15558908ef6SLaurent Vivier } 15658908ef6SLaurent Vivier env->active_tc.gpr[2] = ret; 15758908ef6SLaurent Vivier break; 15858908ef6SLaurent Vivier case EXCP_TLBL: 15958908ef6SLaurent Vivier case EXCP_TLBS: 16058908ef6SLaurent Vivier case EXCP_AdEL: 16158908ef6SLaurent Vivier case EXCP_AdES: 16258908ef6SLaurent Vivier info.si_signo = TARGET_SIGSEGV; 16358908ef6SLaurent Vivier info.si_errno = 0; 16458908ef6SLaurent Vivier /* XXX: check env->error_code */ 16558908ef6SLaurent Vivier info.si_code = TARGET_SEGV_MAPERR; 16658908ef6SLaurent Vivier info._sifields._sigfault._addr = env->CP0_BadVAddr; 16758908ef6SLaurent Vivier queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); 16858908ef6SLaurent Vivier break; 16958908ef6SLaurent Vivier case EXCP_CpU: 17058908ef6SLaurent Vivier case EXCP_RI: 17158908ef6SLaurent Vivier info.si_signo = TARGET_SIGILL; 17258908ef6SLaurent Vivier info.si_errno = 0; 17358908ef6SLaurent Vivier info.si_code = 0; 17458908ef6SLaurent Vivier queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); 17558908ef6SLaurent Vivier break; 17658908ef6SLaurent Vivier case EXCP_INTERRUPT: 17758908ef6SLaurent Vivier /* just indicate that signals should be handled asap */ 17858908ef6SLaurent Vivier break; 17958908ef6SLaurent Vivier case EXCP_DEBUG: 180b10089a1SPeter Maydell info.si_signo = TARGET_SIGTRAP; 18158908ef6SLaurent Vivier info.si_errno = 0; 18258908ef6SLaurent Vivier info.si_code = TARGET_TRAP_BRKPT; 18358908ef6SLaurent Vivier queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); 18458908ef6SLaurent Vivier break; 18558908ef6SLaurent Vivier case EXCP_DSPDIS: 18658908ef6SLaurent Vivier info.si_signo = TARGET_SIGILL; 18758908ef6SLaurent Vivier info.si_errno = 0; 18858908ef6SLaurent Vivier info.si_code = TARGET_ILL_ILLOPC; 18958908ef6SLaurent Vivier queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); 19058908ef6SLaurent Vivier break; 19164ce541cSAleksandar Markovic case EXCP_FPE: 19264ce541cSAleksandar Markovic info.si_signo = TARGET_SIGFPE; 19364ce541cSAleksandar Markovic info.si_errno = 0; 19464ce541cSAleksandar Markovic info.si_code = TARGET_FPE_FLTUNK; 19564ce541cSAleksandar Markovic if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_INVALID) { 19664ce541cSAleksandar Markovic info.si_code = TARGET_FPE_FLTINV; 19764ce541cSAleksandar Markovic } else if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_DIV0) { 19864ce541cSAleksandar Markovic info.si_code = TARGET_FPE_FLTDIV; 19964ce541cSAleksandar Markovic } else if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_OVERFLOW) { 20064ce541cSAleksandar Markovic info.si_code = TARGET_FPE_FLTOVF; 20164ce541cSAleksandar Markovic } else if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_UNDERFLOW) { 20264ce541cSAleksandar Markovic info.si_code = TARGET_FPE_FLTUND; 20364ce541cSAleksandar Markovic } else if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_INEXACT) { 20464ce541cSAleksandar Markovic info.si_code = TARGET_FPE_FLTRES; 20564ce541cSAleksandar Markovic } 20664ce541cSAleksandar Markovic queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); 20764ce541cSAleksandar Markovic break; 20858908ef6SLaurent Vivier /* The code below was inspired by the MIPS Linux kernel trap 20958908ef6SLaurent Vivier * handling code in arch/mips/kernel/traps.c. 21058908ef6SLaurent Vivier */ 21158908ef6SLaurent Vivier case EXCP_BREAK: 21258908ef6SLaurent Vivier { 21358908ef6SLaurent Vivier abi_ulong trap_instr; 21458908ef6SLaurent Vivier unsigned int code; 21558908ef6SLaurent Vivier 21658908ef6SLaurent Vivier if (env->hflags & MIPS_HFLAG_M16) { 21758908ef6SLaurent Vivier if (env->insn_flags & ASE_MICROMIPS) { 21858908ef6SLaurent Vivier /* microMIPS mode */ 21958908ef6SLaurent Vivier ret = get_user_u16(trap_instr, env->active_tc.PC); 22058908ef6SLaurent Vivier if (ret != 0) { 22158908ef6SLaurent Vivier goto error; 22258908ef6SLaurent Vivier } 22358908ef6SLaurent Vivier 22458908ef6SLaurent Vivier if ((trap_instr >> 10) == 0x11) { 22558908ef6SLaurent Vivier /* 16-bit instruction */ 22658908ef6SLaurent Vivier code = trap_instr & 0xf; 22758908ef6SLaurent Vivier } else { 22858908ef6SLaurent Vivier /* 32-bit instruction */ 22958908ef6SLaurent Vivier abi_ulong instr_lo; 23058908ef6SLaurent Vivier 23158908ef6SLaurent Vivier ret = get_user_u16(instr_lo, 23258908ef6SLaurent Vivier env->active_tc.PC + 2); 23358908ef6SLaurent Vivier if (ret != 0) { 23458908ef6SLaurent Vivier goto error; 23558908ef6SLaurent Vivier } 23658908ef6SLaurent Vivier trap_instr = (trap_instr << 16) | instr_lo; 23758908ef6SLaurent Vivier code = ((trap_instr >> 6) & ((1 << 20) - 1)); 23858908ef6SLaurent Vivier /* Unfortunately, microMIPS also suffers from 23958908ef6SLaurent Vivier the old assembler bug... */ 24058908ef6SLaurent Vivier if (code >= (1 << 10)) { 24158908ef6SLaurent Vivier code >>= 10; 24258908ef6SLaurent Vivier } 24358908ef6SLaurent Vivier } 24458908ef6SLaurent Vivier } else { 24558908ef6SLaurent Vivier /* MIPS16e mode */ 24658908ef6SLaurent Vivier ret = get_user_u16(trap_instr, env->active_tc.PC); 24758908ef6SLaurent Vivier if (ret != 0) { 24858908ef6SLaurent Vivier goto error; 24958908ef6SLaurent Vivier } 25058908ef6SLaurent Vivier code = (trap_instr >> 6) & 0x3f; 25158908ef6SLaurent Vivier } 25258908ef6SLaurent Vivier } else { 25358908ef6SLaurent Vivier ret = get_user_u32(trap_instr, env->active_tc.PC); 25458908ef6SLaurent Vivier if (ret != 0) { 25558908ef6SLaurent Vivier goto error; 25658908ef6SLaurent Vivier } 25758908ef6SLaurent Vivier 25858908ef6SLaurent Vivier /* As described in the original Linux kernel code, the 25958908ef6SLaurent Vivier * below checks on 'code' are to work around an old 26058908ef6SLaurent Vivier * assembly bug. 26158908ef6SLaurent Vivier */ 26258908ef6SLaurent Vivier code = ((trap_instr >> 6) & ((1 << 20) - 1)); 26358908ef6SLaurent Vivier if (code >= (1 << 10)) { 26458908ef6SLaurent Vivier code >>= 10; 26558908ef6SLaurent Vivier } 26658908ef6SLaurent Vivier } 26758908ef6SLaurent Vivier 26858908ef6SLaurent Vivier if (do_break(env, &info, code) != 0) { 26958908ef6SLaurent Vivier goto error; 27058908ef6SLaurent Vivier } 27158908ef6SLaurent Vivier } 27258908ef6SLaurent Vivier break; 27358908ef6SLaurent Vivier case EXCP_TRAP: 27458908ef6SLaurent Vivier { 27558908ef6SLaurent Vivier abi_ulong trap_instr; 27658908ef6SLaurent Vivier unsigned int code = 0; 27758908ef6SLaurent Vivier 27858908ef6SLaurent Vivier if (env->hflags & MIPS_HFLAG_M16) { 27958908ef6SLaurent Vivier /* microMIPS mode */ 28058908ef6SLaurent Vivier abi_ulong instr[2]; 28158908ef6SLaurent Vivier 28258908ef6SLaurent Vivier ret = get_user_u16(instr[0], env->active_tc.PC) || 28358908ef6SLaurent Vivier get_user_u16(instr[1], env->active_tc.PC + 2); 28458908ef6SLaurent Vivier 28558908ef6SLaurent Vivier trap_instr = (instr[0] << 16) | instr[1]; 28658908ef6SLaurent Vivier } else { 28758908ef6SLaurent Vivier ret = get_user_u32(trap_instr, env->active_tc.PC); 28858908ef6SLaurent Vivier } 28958908ef6SLaurent Vivier 29058908ef6SLaurent Vivier if (ret != 0) { 29158908ef6SLaurent Vivier goto error; 29258908ef6SLaurent Vivier } 29358908ef6SLaurent Vivier 29458908ef6SLaurent Vivier /* The immediate versions don't provide a code. */ 29558908ef6SLaurent Vivier if (!(trap_instr & 0xFC000000)) { 29658908ef6SLaurent Vivier if (env->hflags & MIPS_HFLAG_M16) { 29758908ef6SLaurent Vivier /* microMIPS mode */ 29858908ef6SLaurent Vivier code = ((trap_instr >> 12) & ((1 << 4) - 1)); 29958908ef6SLaurent Vivier } else { 30058908ef6SLaurent Vivier code = ((trap_instr >> 6) & ((1 << 10) - 1)); 30158908ef6SLaurent Vivier } 30258908ef6SLaurent Vivier } 30358908ef6SLaurent Vivier 30458908ef6SLaurent Vivier if (do_break(env, &info, code) != 0) { 30558908ef6SLaurent Vivier goto error; 30658908ef6SLaurent Vivier } 30758908ef6SLaurent Vivier } 30858908ef6SLaurent Vivier break; 30958908ef6SLaurent Vivier case EXCP_ATOMIC: 31058908ef6SLaurent Vivier cpu_exec_step_atomic(cs); 31158908ef6SLaurent Vivier break; 31258908ef6SLaurent Vivier default: 31358908ef6SLaurent Vivier error: 31458908ef6SLaurent Vivier EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); 31558908ef6SLaurent Vivier abort(); 31658908ef6SLaurent Vivier } 31758908ef6SLaurent Vivier process_pending_signals(env); 31858908ef6SLaurent Vivier } 31958908ef6SLaurent Vivier } 320cd71c089SLaurent Vivier 321cd71c089SLaurent Vivier void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) 322cd71c089SLaurent Vivier { 32329a0af61SRichard Henderson CPUState *cpu = env_cpu(env); 32458908ef6SLaurent Vivier TaskState *ts = cpu->opaque; 32558908ef6SLaurent Vivier struct image_info *info = ts->info; 32658908ef6SLaurent Vivier int i; 32758908ef6SLaurent Vivier 3280c1bbedcSStefan Markovic struct mode_req { 3290c1bbedcSStefan Markovic bool single; 3300c1bbedcSStefan Markovic bool soft; 3310c1bbedcSStefan Markovic bool fr1; 3320c1bbedcSStefan Markovic bool frdefault; 3330c1bbedcSStefan Markovic bool fre; 3340c1bbedcSStefan Markovic }; 3350c1bbedcSStefan Markovic 3360c1bbedcSStefan Markovic static const struct mode_req fpu_reqs[] = { 3370c1bbedcSStefan Markovic [MIPS_ABI_FP_ANY] = { true, true, true, true, true }, 3380c1bbedcSStefan Markovic [MIPS_ABI_FP_DOUBLE] = { false, false, false, true, true }, 3390c1bbedcSStefan Markovic [MIPS_ABI_FP_SINGLE] = { true, false, false, false, false }, 3400c1bbedcSStefan Markovic [MIPS_ABI_FP_SOFT] = { false, true, false, false, false }, 3410c1bbedcSStefan Markovic [MIPS_ABI_FP_OLD_64] = { false, false, false, false, false }, 3420c1bbedcSStefan Markovic [MIPS_ABI_FP_XX] = { false, false, true, true, true }, 3430c1bbedcSStefan Markovic [MIPS_ABI_FP_64] = { false, false, true, false, false }, 3440c1bbedcSStefan Markovic [MIPS_ABI_FP_64A] = { false, false, true, false, true } 3450c1bbedcSStefan Markovic }; 3460c1bbedcSStefan Markovic 3470c1bbedcSStefan Markovic /* 3480c1bbedcSStefan Markovic * Mode requirements when .MIPS.abiflags is not present in the ELF. 3490c1bbedcSStefan Markovic * Not present means that everything is acceptable except FR1. 3500c1bbedcSStefan Markovic */ 3510c1bbedcSStefan Markovic static struct mode_req none_req = { true, true, false, true, true }; 3520c1bbedcSStefan Markovic 3530c1bbedcSStefan Markovic struct mode_req prog_req; 3540c1bbedcSStefan Markovic struct mode_req interp_req; 3550c1bbedcSStefan Markovic 35658908ef6SLaurent Vivier for(i = 0; i < 32; i++) { 35758908ef6SLaurent Vivier env->active_tc.gpr[i] = regs->regs[i]; 35858908ef6SLaurent Vivier } 35958908ef6SLaurent Vivier env->active_tc.PC = regs->cp0_epc & ~(target_ulong)1; 36058908ef6SLaurent Vivier if (regs->cp0_epc & 1) { 36158908ef6SLaurent Vivier env->hflags |= MIPS_HFLAG_M16; 36258908ef6SLaurent Vivier } 3630c1bbedcSStefan Markovic 3640c1bbedcSStefan Markovic #ifdef TARGET_ABI_MIPSO32 3650c1bbedcSStefan Markovic # define MAX_FP_ABI MIPS_ABI_FP_64A 3660c1bbedcSStefan Markovic #else 3670c1bbedcSStefan Markovic # define MAX_FP_ABI MIPS_ABI_FP_SOFT 3680c1bbedcSStefan Markovic #endif 3690c1bbedcSStefan Markovic if ((info->fp_abi > MAX_FP_ABI && info->fp_abi != MIPS_ABI_FP_UNKNOWN) 3700c1bbedcSStefan Markovic || (info->interp_fp_abi > MAX_FP_ABI && 3710c1bbedcSStefan Markovic info->interp_fp_abi != MIPS_ABI_FP_UNKNOWN)) { 3720c1bbedcSStefan Markovic fprintf(stderr, "qemu: Unexpected FPU mode\n"); 3730c1bbedcSStefan Markovic exit(1); 3740c1bbedcSStefan Markovic } 3750c1bbedcSStefan Markovic 3760c1bbedcSStefan Markovic prog_req = (info->fp_abi == MIPS_ABI_FP_UNKNOWN) ? none_req 3770c1bbedcSStefan Markovic : fpu_reqs[info->fp_abi]; 3780c1bbedcSStefan Markovic interp_req = (info->interp_fp_abi == MIPS_ABI_FP_UNKNOWN) ? none_req 3790c1bbedcSStefan Markovic : fpu_reqs[info->interp_fp_abi]; 3800c1bbedcSStefan Markovic 3810c1bbedcSStefan Markovic prog_req.single &= interp_req.single; 3820c1bbedcSStefan Markovic prog_req.soft &= interp_req.soft; 3830c1bbedcSStefan Markovic prog_req.fr1 &= interp_req.fr1; 3840c1bbedcSStefan Markovic prog_req.frdefault &= interp_req.frdefault; 3850c1bbedcSStefan Markovic prog_req.fre &= interp_req.fre; 3860c1bbedcSStefan Markovic 3870c1bbedcSStefan Markovic bool cpu_has_mips_r2_r6 = env->insn_flags & ISA_MIPS32R2 || 3880c1bbedcSStefan Markovic env->insn_flags & ISA_MIPS64R2 || 3890c1bbedcSStefan Markovic env->insn_flags & ISA_MIPS32R6 || 3900c1bbedcSStefan Markovic env->insn_flags & ISA_MIPS64R6; 3910c1bbedcSStefan Markovic 3920c1bbedcSStefan Markovic if (prog_req.fre && !prog_req.frdefault && !prog_req.fr1) { 3930c1bbedcSStefan Markovic env->CP0_Config5 |= (1 << CP0C5_FRE); 3940c1bbedcSStefan Markovic if (env->active_fpu.fcr0 & (1 << FCR0_FREP)) { 3950c1bbedcSStefan Markovic env->hflags |= MIPS_HFLAG_FRE; 3960c1bbedcSStefan Markovic } 3970c1bbedcSStefan Markovic } else if ((prog_req.fr1 && prog_req.frdefault) || 3980c1bbedcSStefan Markovic (prog_req.single && !prog_req.frdefault)) { 3990c1bbedcSStefan Markovic if ((env->active_fpu.fcr0 & (1 << FCR0_F64) 4000c1bbedcSStefan Markovic && cpu_has_mips_r2_r6) || prog_req.fr1) { 4010c1bbedcSStefan Markovic env->CP0_Status |= (1 << CP0St_FR); 4020c1bbedcSStefan Markovic env->hflags |= MIPS_HFLAG_F64; 4030c1bbedcSStefan Markovic } 4040c1bbedcSStefan Markovic } else if (!prog_req.fre && !prog_req.frdefault && 4050c1bbedcSStefan Markovic !prog_req.fr1 && !prog_req.single && !prog_req.soft) { 4060c1bbedcSStefan Markovic fprintf(stderr, "qemu: Can't find a matching FPU mode\n"); 4070c1bbedcSStefan Markovic exit(1); 4080c1bbedcSStefan Markovic } 4090c1bbedcSStefan Markovic 410722ac96cSAleksandar Markovic if (env->insn_flags & ISA_NANOMIPS32) { 411722ac96cSAleksandar Markovic return; 412722ac96cSAleksandar Markovic } 41358908ef6SLaurent Vivier if (((info->elf_flags & EF_MIPS_NAN2008) != 0) != 41458908ef6SLaurent Vivier ((env->active_fpu.fcr31 & (1 << FCR31_NAN2008)) != 0)) { 41558908ef6SLaurent Vivier if ((env->active_fpu.fcr31_rw_bitmask & 41658908ef6SLaurent Vivier (1 << FCR31_NAN2008)) == 0) { 41758908ef6SLaurent Vivier fprintf(stderr, "ELF binary's NaN mode not supported by CPU\n"); 41858908ef6SLaurent Vivier exit(1); 41958908ef6SLaurent Vivier } 42058908ef6SLaurent Vivier if ((info->elf_flags & EF_MIPS_NAN2008) != 0) { 42158908ef6SLaurent Vivier env->active_fpu.fcr31 |= (1 << FCR31_NAN2008); 42258908ef6SLaurent Vivier } else { 42358908ef6SLaurent Vivier env->active_fpu.fcr31 &= ~(1 << FCR31_NAN2008); 42458908ef6SLaurent Vivier } 42558908ef6SLaurent Vivier restore_snan_bit_mode(env); 42658908ef6SLaurent Vivier } 427cd71c089SLaurent Vivier } 428