| /* SPDX-License-Identifier: GPL-2.0-or-later */ | |
| /* | |
| * QEMU LoongArch user cpu_loop. | |
| * | |
| * Copyright (c) 2021 Loongson Technology Corporation Limited | |
| */ | |
| #include "qemu/osdep.h" | |
| #include "qemu.h" | |
| #include "user-internals.h" | |
| #include "user/cpu_loop.h" | |
| #include "signal-common.h" | |
| /* Break codes */ | |
| enum { | |
| BRK_OVERFLOW = 6, | |
| BRK_DIVZERO = 7 | |
| }; | |
| void cpu_loop(CPULoongArchState *env) | |
| { | |
| CPUSysState *sys = env_sys(env); | |
| CPUState *cs = env_cpu(env); | |
| int trapnr, si_code; | |
| abi_long ret; | |
| for (;;) { | |
| cpu_exec_start(cs); | |
| trapnr = cpu_exec(cs); | |
| cpu_exec_end(cs); | |
| qemu_process_cpu_events(cs); | |
| switch (trapnr) { | |
| case EXCP_INTERRUPT: | |
| /* just indicate that signals should be handled asap */ | |
| break; | |
| case EXCCODE_SYS: | |
| env->pc += 4; | |
| ret = do_syscall(env, env->gpr[11], | |
| env->gpr[4], env->gpr[5], | |
| env->gpr[6], env->gpr[7], | |
| env->gpr[8], env->gpr[9], | |
| -1, -1); | |
| if (ret == -QEMU_ERESTARTSYS) { | |
| env->pc -= 4; | |
| break; | |
| } | |
| if (ret == -QEMU_ESIGRETURN || ret == -QEMU_ESETPC) { | |
| /* | |
| * Returning from a successful sigreturn syscall or from | |
| * control flow diversion in a plugin callback. | |
| * Avoid clobbering register state. | |
| */ | |
| break; | |
| } | |
| env->gpr[4] = ret; | |
| break; | |
| case EXCCODE_INE: | |
| force_sig_fault(TARGET_SIGILL, 0, env->pc); | |
| break; | |
| case EXCCODE_FPE: | |
| si_code = TARGET_FPE_FLTUNK; | |
| if (GET_FP_CAUSE(env->fcsr0) & FP_INVALID) { | |
| si_code = TARGET_FPE_FLTINV; | |
| } else if (GET_FP_CAUSE(env->fcsr0) & FP_DIV0) { | |
| si_code = TARGET_FPE_FLTDIV; | |
| } else if (GET_FP_CAUSE(env->fcsr0) & FP_OVERFLOW) { | |
| si_code = TARGET_FPE_FLTOVF; | |
| } else if (GET_FP_CAUSE(env->fcsr0) & FP_UNDERFLOW) { | |
| si_code = TARGET_FPE_FLTUND; | |
| } else if (GET_FP_CAUSE(env->fcsr0) & FP_INEXACT) { | |
| si_code = TARGET_FPE_FLTRES; | |
| } | |
| force_sig_fault(TARGET_SIGFPE, si_code, env->pc); | |
| break; | |
| case EXCP_DEBUG: | |
| force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc); | |
| break; | |
| case EXCCODE_BRK: | |
| { | |
| unsigned int opcode; | |
| get_user_u32(opcode, env->pc); | |
| switch (opcode & 0x7fff) { | |
| case BRK_OVERFLOW: | |
| force_sig_fault(TARGET_SIGFPE, TARGET_FPE_INTOVF, env->pc); | |
| break; | |
| case BRK_DIVZERO: | |
| force_sig_fault(TARGET_SIGFPE, TARGET_FPE_INTDIV, env->pc); | |
| break; | |
| default: | |
| force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc); | |
| } | |
| } | |
| break; | |
| case EXCCODE_BCE: | |
| force_sig_fault(TARGET_SIGSYS, TARGET_SI_KERNEL, env->pc); | |
| break; | |
| /* | |
| * Begin with LSX and LASX disabled, then enable on the first trap. | |
| * In this way we can tell if the unit is in use. This is used to | |
| * choose the layout of any signal frame. | |
| */ | |
| case EXCCODE_SXD: | |
| sys->CSR_EUEN |= R_CSR_EUEN_SXE_MASK; | |
| break; | |
| case EXCCODE_ASXD: | |
| sys->CSR_EUEN |= R_CSR_EUEN_ASXE_MASK; | |
| break; | |
| case EXCP_ATOMIC: | |
| cpu_exec_step_atomic(cs); | |
| break; | |
| default: | |
| EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", | |
| trapnr); | |
| exit(EXIT_FAILURE); | |
| } | |
| process_pending_signals(env); | |
| } | |
| } | |
| void init_main_thread(CPUState *cs, struct image_info *info) | |
| { | |
| CPUArchState *env = cpu_env(cs); | |
| env->pc = info->entry; | |
| env->gpr[3] = info->start_stack; | |
| } |