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"
21cd71c089SLaurent Vivier #include "qemu.h"
223b249d26SPeter Maydell #include "user-internals.h"
23cd71c089SLaurent Vivier #include "cpu_loop-common.h"
242113aed6SPeter Maydell #include "signal-common.h"
2558908ef6SLaurent Vivier #include "elf.h"
26502700d0SAlex Bennée #include "internal.h"
2781ddae7cSPhilippe Mathieu-Daudé #include "fpu_helper.h"
2858908ef6SLaurent Vivier
2958908ef6SLaurent Vivier # ifdef TARGET_ABI_MIPSO32
308d6d4c1bSAleksandar Markovic # define MIPS_SYSCALL_NUMBER_UNUSED -1
318d6d4c1bSAleksandar Markovic static const int8_t mips_syscall_args[] = {
32ac5d3c67SLaurent Vivier #include "syscall-args-o32.c.inc"
3358908ef6SLaurent Vivier };
3458908ef6SLaurent Vivier # endif /* O32 */
3558908ef6SLaurent Vivier
3658908ef6SLaurent Vivier /* Break codes */
3758908ef6SLaurent Vivier enum {
3858908ef6SLaurent Vivier BRK_OVERFLOW = 6,
3958908ef6SLaurent Vivier BRK_DIVZERO = 7
4058908ef6SLaurent Vivier };
4158908ef6SLaurent Vivier
do_tr_or_bp(CPUMIPSState * env,unsigned int code,bool trap)42bf19bdb8SRichard Henderson static void do_tr_or_bp(CPUMIPSState *env, unsigned int code, bool trap)
4358908ef6SLaurent Vivier {
44bf19bdb8SRichard Henderson target_ulong pc = env->active_tc.PC;
4558908ef6SLaurent Vivier
4658908ef6SLaurent Vivier switch (code) {
4758908ef6SLaurent Vivier case BRK_OVERFLOW:
48bf19bdb8SRichard Henderson force_sig_fault(TARGET_SIGFPE, TARGET_FPE_INTOVF, pc);
49bf19bdb8SRichard Henderson break;
5058908ef6SLaurent Vivier case BRK_DIVZERO:
51bf19bdb8SRichard Henderson force_sig_fault(TARGET_SIGFPE, TARGET_FPE_INTDIV, pc);
5258908ef6SLaurent Vivier break;
5358908ef6SLaurent Vivier default:
54bf19bdb8SRichard Henderson if (trap) {
55bf19bdb8SRichard Henderson force_sig(TARGET_SIGTRAP);
56bf19bdb8SRichard Henderson } else {
57bf19bdb8SRichard Henderson force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, pc);
58bf19bdb8SRichard Henderson }
5958908ef6SLaurent Vivier break;
6058908ef6SLaurent Vivier }
6158908ef6SLaurent Vivier }
6258908ef6SLaurent Vivier
cpu_loop(CPUMIPSState * env)6358908ef6SLaurent Vivier void cpu_loop(CPUMIPSState *env)
6458908ef6SLaurent Vivier {
655a7330b3SRichard Henderson CPUState *cs = env_cpu(env);
6673c0aa6aSRichard Henderson int trapnr, si_code;
676f3533ddSRichard Henderson unsigned int code;
6858908ef6SLaurent Vivier abi_long ret;
6958908ef6SLaurent Vivier # ifdef TARGET_ABI_MIPSO32
7058908ef6SLaurent Vivier unsigned int syscall_num;
7158908ef6SLaurent Vivier # endif
7258908ef6SLaurent Vivier
7358908ef6SLaurent Vivier for(;;) {
7458908ef6SLaurent Vivier cpu_exec_start(cs);
7558908ef6SLaurent Vivier trapnr = cpu_exec(cs);
7658908ef6SLaurent Vivier cpu_exec_end(cs);
7758908ef6SLaurent Vivier process_queued_cpu_work(cs);
7858908ef6SLaurent Vivier
7958908ef6SLaurent Vivier switch(trapnr) {
8058908ef6SLaurent Vivier case EXCP_SYSCALL:
8158908ef6SLaurent Vivier env->active_tc.PC += 4;
8258908ef6SLaurent Vivier # ifdef TARGET_ABI_MIPSO32
8358908ef6SLaurent Vivier syscall_num = env->active_tc.gpr[2] - 4000;
8458908ef6SLaurent Vivier if (syscall_num >= sizeof(mips_syscall_args)) {
858d6d4c1bSAleksandar Markovic /* syscall_num is larger that any defined for MIPS O32 */
868d6d4c1bSAleksandar Markovic ret = -TARGET_ENOSYS;
878d6d4c1bSAleksandar Markovic } else if (mips_syscall_args[syscall_num] ==
888d6d4c1bSAleksandar Markovic MIPS_SYSCALL_NUMBER_UNUSED) {
898d6d4c1bSAleksandar Markovic /* syscall_num belongs to the range not defined for MIPS O32 */
9058908ef6SLaurent Vivier ret = -TARGET_ENOSYS;
9158908ef6SLaurent Vivier } else {
928d6d4c1bSAleksandar Markovic /* syscall_num is valid */
9358908ef6SLaurent Vivier int nb_args;
9458908ef6SLaurent Vivier abi_ulong sp_reg;
9558908ef6SLaurent Vivier abi_ulong arg5 = 0, arg6 = 0, arg7 = 0, arg8 = 0;
9658908ef6SLaurent Vivier
9758908ef6SLaurent Vivier nb_args = mips_syscall_args[syscall_num];
9858908ef6SLaurent Vivier sp_reg = env->active_tc.gpr[29];
9958908ef6SLaurent Vivier switch (nb_args) {
10058908ef6SLaurent Vivier /* these arguments are taken from the stack */
10158908ef6SLaurent Vivier case 8:
10258908ef6SLaurent Vivier if ((ret = get_user_ual(arg8, sp_reg + 28)) != 0) {
10358908ef6SLaurent Vivier goto done_syscall;
10458908ef6SLaurent Vivier }
10581966c18SChen Qun /* fall through */
10658908ef6SLaurent Vivier case 7:
10758908ef6SLaurent Vivier if ((ret = get_user_ual(arg7, sp_reg + 24)) != 0) {
10858908ef6SLaurent Vivier goto done_syscall;
10958908ef6SLaurent Vivier }
11081966c18SChen Qun /* fall through */
11158908ef6SLaurent Vivier case 6:
11258908ef6SLaurent Vivier if ((ret = get_user_ual(arg6, sp_reg + 20)) != 0) {
11358908ef6SLaurent Vivier goto done_syscall;
11458908ef6SLaurent Vivier }
11581966c18SChen Qun /* fall through */
11658908ef6SLaurent Vivier case 5:
11758908ef6SLaurent Vivier if ((ret = get_user_ual(arg5, sp_reg + 16)) != 0) {
11858908ef6SLaurent Vivier goto done_syscall;
11958908ef6SLaurent Vivier }
12081966c18SChen Qun /* fall through */
12158908ef6SLaurent Vivier default:
12258908ef6SLaurent Vivier break;
12358908ef6SLaurent Vivier }
12458908ef6SLaurent Vivier ret = do_syscall(env, env->active_tc.gpr[2],
12558908ef6SLaurent Vivier env->active_tc.gpr[4],
12658908ef6SLaurent Vivier env->active_tc.gpr[5],
12758908ef6SLaurent Vivier env->active_tc.gpr[6],
12858908ef6SLaurent Vivier env->active_tc.gpr[7],
12958908ef6SLaurent Vivier arg5, arg6, arg7, arg8);
13058908ef6SLaurent Vivier }
13158908ef6SLaurent Vivier done_syscall:
13258908ef6SLaurent Vivier # else
13358908ef6SLaurent Vivier ret = do_syscall(env, env->active_tc.gpr[2],
13458908ef6SLaurent Vivier env->active_tc.gpr[4], env->active_tc.gpr[5],
13558908ef6SLaurent Vivier env->active_tc.gpr[6], env->active_tc.gpr[7],
13658908ef6SLaurent Vivier env->active_tc.gpr[8], env->active_tc.gpr[9],
13758908ef6SLaurent Vivier env->active_tc.gpr[10], env->active_tc.gpr[11]);
13858908ef6SLaurent Vivier # endif /* O32 */
139af254a27SRichard Henderson if (ret == -QEMU_ERESTARTSYS) {
14058908ef6SLaurent Vivier env->active_tc.PC -= 4;
14158908ef6SLaurent Vivier break;
14258908ef6SLaurent Vivier }
14357a0c938SRichard Henderson if (ret == -QEMU_ESIGRETURN) {
14458908ef6SLaurent Vivier /* Returning from a successful sigreturn syscall.
14558908ef6SLaurent Vivier Avoid clobbering register state. */
14658908ef6SLaurent Vivier break;
14758908ef6SLaurent Vivier }
14858908ef6SLaurent Vivier if ((abi_ulong)ret >= (abi_ulong)-1133) {
14958908ef6SLaurent Vivier env->active_tc.gpr[7] = 1; /* error flag */
15058908ef6SLaurent Vivier ret = -ret;
15158908ef6SLaurent Vivier } else {
15258908ef6SLaurent Vivier env->active_tc.gpr[7] = 0; /* error flag */
15358908ef6SLaurent Vivier }
15458908ef6SLaurent Vivier env->active_tc.gpr[2] = ret;
15558908ef6SLaurent Vivier break;
15658908ef6SLaurent Vivier case EXCP_CpU:
15758908ef6SLaurent Vivier case EXCP_RI:
15873c0aa6aSRichard Henderson case EXCP_DSPDIS:
15973c0aa6aSRichard Henderson force_sig(TARGET_SIGILL);
16058908ef6SLaurent Vivier break;
16158908ef6SLaurent Vivier case EXCP_INTERRUPT:
16258908ef6SLaurent Vivier /* just indicate that signals should be handled asap */
16358908ef6SLaurent Vivier break;
16458908ef6SLaurent Vivier case EXCP_DEBUG:
16573c0aa6aSRichard Henderson force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT,
16673c0aa6aSRichard Henderson env->active_tc.PC);
16758908ef6SLaurent Vivier break;
16864ce541cSAleksandar Markovic case EXCP_FPE:
16973c0aa6aSRichard Henderson si_code = TARGET_FPE_FLTUNK;
17064ce541cSAleksandar Markovic if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_INVALID) {
17173c0aa6aSRichard Henderson si_code = TARGET_FPE_FLTINV;
17264ce541cSAleksandar Markovic } else if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_DIV0) {
17373c0aa6aSRichard Henderson si_code = TARGET_FPE_FLTDIV;
17464ce541cSAleksandar Markovic } else if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_OVERFLOW) {
17573c0aa6aSRichard Henderson si_code = TARGET_FPE_FLTOVF;
17664ce541cSAleksandar Markovic } else if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_UNDERFLOW) {
17773c0aa6aSRichard Henderson si_code = TARGET_FPE_FLTUND;
17864ce541cSAleksandar Markovic } else if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_INEXACT) {
17973c0aa6aSRichard Henderson si_code = TARGET_FPE_FLTRES;
18064ce541cSAleksandar Markovic }
18173c0aa6aSRichard Henderson force_sig_fault(TARGET_SIGFPE, si_code, env->active_tc.PC);
18264ce541cSAleksandar Markovic break;
1836fad9b4bSMikulas Patocka case EXCP_OVERFLOW:
1846fad9b4bSMikulas Patocka force_sig_fault(TARGET_SIGFPE, TARGET_FPE_INTOVF, env->active_tc.PC);
1856fad9b4bSMikulas Patocka break;
18658908ef6SLaurent Vivier /* The code below was inspired by the MIPS Linux kernel trap
18758908ef6SLaurent Vivier * handling code in arch/mips/kernel/traps.c.
18858908ef6SLaurent Vivier */
18958908ef6SLaurent Vivier case EXCP_BREAK:
190bf19bdb8SRichard Henderson /*
1916f3533ddSRichard Henderson * As described in the original Linux kernel code, the below
1926f3533ddSRichard Henderson * checks on 'code' are to work around an old assembly bug.
193bf19bdb8SRichard Henderson */
1946f3533ddSRichard Henderson code = env->error_code;
19558908ef6SLaurent Vivier if (code >= (1 << 10)) {
19658908ef6SLaurent Vivier code >>= 10;
19758908ef6SLaurent Vivier }
198bf19bdb8SRichard Henderson do_tr_or_bp(env, code, false);
19958908ef6SLaurent Vivier break;
20058908ef6SLaurent Vivier case EXCP_TRAP:
2010a3336f6SRichard Henderson do_tr_or_bp(env, env->error_code, true);
20258908ef6SLaurent Vivier break;
20358908ef6SLaurent Vivier case EXCP_ATOMIC:
20458908ef6SLaurent Vivier cpu_exec_step_atomic(cs);
20558908ef6SLaurent Vivier break;
20658908ef6SLaurent Vivier default:
20758908ef6SLaurent Vivier EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr);
20858908ef6SLaurent Vivier abort();
20958908ef6SLaurent Vivier }
21058908ef6SLaurent Vivier process_pending_signals(env);
21158908ef6SLaurent Vivier }
21258908ef6SLaurent Vivier }
213cd71c089SLaurent Vivier
target_cpu_copy_regs(CPUArchState * env,struct target_pt_regs * regs)214cd71c089SLaurent Vivier void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs)
215cd71c089SLaurent Vivier {
21629a0af61SRichard Henderson CPUState *cpu = env_cpu(env);
217*e4e5cb4aSIlya Leoshkevich TaskState *ts = get_task_state(cpu);
21858908ef6SLaurent Vivier struct image_info *info = ts->info;
21958908ef6SLaurent Vivier int i;
22058908ef6SLaurent Vivier
2210c1bbedcSStefan Markovic struct mode_req {
2220c1bbedcSStefan Markovic bool single;
2230c1bbedcSStefan Markovic bool soft;
2240c1bbedcSStefan Markovic bool fr1;
2250c1bbedcSStefan Markovic bool frdefault;
2260c1bbedcSStefan Markovic bool fre;
2270c1bbedcSStefan Markovic };
2280c1bbedcSStefan Markovic
2290c1bbedcSStefan Markovic static const struct mode_req fpu_reqs[] = {
2300c1bbedcSStefan Markovic [MIPS_ABI_FP_ANY] = { true, true, true, true, true },
2310c1bbedcSStefan Markovic [MIPS_ABI_FP_DOUBLE] = { false, false, false, true, true },
2320c1bbedcSStefan Markovic [MIPS_ABI_FP_SINGLE] = { true, false, false, false, false },
2330c1bbedcSStefan Markovic [MIPS_ABI_FP_SOFT] = { false, true, false, false, false },
2340c1bbedcSStefan Markovic [MIPS_ABI_FP_OLD_64] = { false, false, false, false, false },
2350c1bbedcSStefan Markovic [MIPS_ABI_FP_XX] = { false, false, true, true, true },
2360c1bbedcSStefan Markovic [MIPS_ABI_FP_64] = { false, false, true, false, false },
2370c1bbedcSStefan Markovic [MIPS_ABI_FP_64A] = { false, false, true, false, true }
2380c1bbedcSStefan Markovic };
2390c1bbedcSStefan Markovic
2400c1bbedcSStefan Markovic /*
2410c1bbedcSStefan Markovic * Mode requirements when .MIPS.abiflags is not present in the ELF.
2420c1bbedcSStefan Markovic * Not present means that everything is acceptable except FR1.
2430c1bbedcSStefan Markovic */
2440c1bbedcSStefan Markovic static struct mode_req none_req = { true, true, false, true, true };
2450c1bbedcSStefan Markovic
2460c1bbedcSStefan Markovic struct mode_req prog_req;
2470c1bbedcSStefan Markovic struct mode_req interp_req;
2480c1bbedcSStefan Markovic
24958908ef6SLaurent Vivier for(i = 0; i < 32; i++) {
25058908ef6SLaurent Vivier env->active_tc.gpr[i] = regs->regs[i];
25158908ef6SLaurent Vivier }
25258908ef6SLaurent Vivier env->active_tc.PC = regs->cp0_epc & ~(target_ulong)1;
25358908ef6SLaurent Vivier if (regs->cp0_epc & 1) {
25458908ef6SLaurent Vivier env->hflags |= MIPS_HFLAG_M16;
25558908ef6SLaurent Vivier }
2560c1bbedcSStefan Markovic
2570c1bbedcSStefan Markovic #ifdef TARGET_ABI_MIPSO32
2580c1bbedcSStefan Markovic # define MAX_FP_ABI MIPS_ABI_FP_64A
2590c1bbedcSStefan Markovic #else
2600c1bbedcSStefan Markovic # define MAX_FP_ABI MIPS_ABI_FP_SOFT
2610c1bbedcSStefan Markovic #endif
2620c1bbedcSStefan Markovic if ((info->fp_abi > MAX_FP_ABI && info->fp_abi != MIPS_ABI_FP_UNKNOWN)
2630c1bbedcSStefan Markovic || (info->interp_fp_abi > MAX_FP_ABI &&
2640c1bbedcSStefan Markovic info->interp_fp_abi != MIPS_ABI_FP_UNKNOWN)) {
2650c1bbedcSStefan Markovic fprintf(stderr, "qemu: Unexpected FPU mode\n");
2660c1bbedcSStefan Markovic exit(1);
2670c1bbedcSStefan Markovic }
2680c1bbedcSStefan Markovic
2690c1bbedcSStefan Markovic prog_req = (info->fp_abi == MIPS_ABI_FP_UNKNOWN) ? none_req
2700c1bbedcSStefan Markovic : fpu_reqs[info->fp_abi];
2710c1bbedcSStefan Markovic interp_req = (info->interp_fp_abi == MIPS_ABI_FP_UNKNOWN) ? none_req
2720c1bbedcSStefan Markovic : fpu_reqs[info->interp_fp_abi];
2730c1bbedcSStefan Markovic
2740c1bbedcSStefan Markovic prog_req.single &= interp_req.single;
2750c1bbedcSStefan Markovic prog_req.soft &= interp_req.soft;
2760c1bbedcSStefan Markovic prog_req.fr1 &= interp_req.fr1;
2770c1bbedcSStefan Markovic prog_req.frdefault &= interp_req.frdefault;
2780c1bbedcSStefan Markovic prog_req.fre &= interp_req.fre;
2790c1bbedcSStefan Markovic
2807a47bae5SPhilippe Mathieu-Daudé bool cpu_has_mips_r2_r6 = env->insn_flags & ISA_MIPS_R2 ||
2812e211e0aSPhilippe Mathieu-Daudé env->insn_flags & ISA_MIPS_R6;
2820c1bbedcSStefan Markovic
2830c1bbedcSStefan Markovic if (prog_req.fre && !prog_req.frdefault && !prog_req.fr1) {
2840c1bbedcSStefan Markovic env->CP0_Config5 |= (1 << CP0C5_FRE);
2850c1bbedcSStefan Markovic if (env->active_fpu.fcr0 & (1 << FCR0_FREP)) {
2860c1bbedcSStefan Markovic env->hflags |= MIPS_HFLAG_FRE;
2870c1bbedcSStefan Markovic }
2880c1bbedcSStefan Markovic } else if ((prog_req.fr1 && prog_req.frdefault) ||
2890c1bbedcSStefan Markovic (prog_req.single && !prog_req.frdefault)) {
2900c1bbedcSStefan Markovic if ((env->active_fpu.fcr0 & (1 << FCR0_F64)
2910c1bbedcSStefan Markovic && cpu_has_mips_r2_r6) || prog_req.fr1) {
2920c1bbedcSStefan Markovic env->CP0_Status |= (1 << CP0St_FR);
2930c1bbedcSStefan Markovic env->hflags |= MIPS_HFLAG_F64;
2940c1bbedcSStefan Markovic }
295a0f8d270SDaniil Kovalev } else if (prog_req.fr1) {
296a0f8d270SDaniil Kovalev env->CP0_Status |= (1 << CP0St_FR);
297a0f8d270SDaniil Kovalev env->hflags |= MIPS_HFLAG_F64;
2980c1bbedcSStefan Markovic } else if (!prog_req.fre && !prog_req.frdefault &&
2990c1bbedcSStefan Markovic !prog_req.fr1 && !prog_req.single && !prog_req.soft) {
3000c1bbedcSStefan Markovic fprintf(stderr, "qemu: Can't find a matching FPU mode\n");
3010c1bbedcSStefan Markovic exit(1);
3020c1bbedcSStefan Markovic }
3030c1bbedcSStefan Markovic
304722ac96cSAleksandar Markovic if (env->insn_flags & ISA_NANOMIPS32) {
305722ac96cSAleksandar Markovic return;
306722ac96cSAleksandar Markovic }
30758908ef6SLaurent Vivier if (((info->elf_flags & EF_MIPS_NAN2008) != 0) !=
30858908ef6SLaurent Vivier ((env->active_fpu.fcr31 & (1 << FCR31_NAN2008)) != 0)) {
30958908ef6SLaurent Vivier if ((env->active_fpu.fcr31_rw_bitmask &
31058908ef6SLaurent Vivier (1 << FCR31_NAN2008)) == 0) {
31158908ef6SLaurent Vivier fprintf(stderr, "ELF binary's NaN mode not supported by CPU\n");
31258908ef6SLaurent Vivier exit(1);
31358908ef6SLaurent Vivier }
31458908ef6SLaurent Vivier if ((info->elf_flags & EF_MIPS_NAN2008) != 0) {
31558908ef6SLaurent Vivier env->active_fpu.fcr31 |= (1 << FCR31_NAN2008);
31658908ef6SLaurent Vivier } else {
31758908ef6SLaurent Vivier env->active_fpu.fcr31 &= ~(1 << FCR31_NAN2008);
31858908ef6SLaurent Vivier }
31958908ef6SLaurent Vivier restore_snan_bit_mode(env);
32058908ef6SLaurent Vivier }
321cd71c089SLaurent Vivier }
322