| 1 | /* |
| 2 | * MIPS Exceptions processing helpers for QEMU. |
| 3 | * |
| 4 | * Copyright (c) 2004-2005 Jocelyn Mayer |
| 5 | * |
| 6 | * This library is free software; you can redistribute it and/or |
| 7 | * modify it under the terms of the GNU Lesser General Public |
| 8 | * License as published by the Free Software Foundation; either |
| 9 | * version 2.1 of the License, or (at your option) any later version. |
| 10 | * |
| 11 | * This library is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 14 | * Lesser General Public License for more details. |
| 15 | * |
| 16 | * You should have received a copy of the GNU Lesser General Public |
| 17 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
| 18 | * |
| 19 | */ |
| 20 | |
| 21 | #include "qemu/osdep.h" |
| 22 | #include "qemu/log.h" |
| 23 | #include "cpu.h" |
| 24 | #include "internal.h" |
| 25 | #include "exec/helper-proto.h" |
| 26 | #include "exec/translation-block.h" |
| 27 | #include "accel/tcg/cpu-loop.h" |
| 28 | |
| 29 | target_ulong exception_resume_pc(CPUMIPSState *env) |
| 30 | { |
| 31 | target_ulong bad_pc; |
| 32 | target_ulong isa_mode; |
| 33 | |
| 34 | isa_mode = !!(env->hflags & MIPS_HFLAG_M16); |
| 35 | bad_pc = env->active_tc.PC | isa_mode; |
| 36 | if (env->hflags & MIPS_HFLAG_BMASK) { |
| 37 | /* |
| 38 | * If the exception was raised from a delay slot, come back to |
| 39 | * the jump. |
| 40 | */ |
| 41 | bad_pc -= (env->hflags & MIPS_HFLAG_B16 ? 2 : 4); |
| 42 | } |
| 43 | |
| 44 | return bad_pc; |
| 45 | } |
| 46 | |
| 47 | void helper_raise_exception_err(CPUMIPSState *env, uint32_t exception, |
| 48 | int error_code) |
| 49 | { |
| 50 | do_raise_exception_err(env, exception, error_code, 0); |
| 51 | } |
| 52 | |
| 53 | void helper_raise_exception(CPUMIPSState *env, uint32_t exception) |
| 54 | { |
| 55 | do_raise_exception(env, exception, GETPC()); |
| 56 | } |
| 57 | |
| 58 | void helper_raise_exception_debug(CPUMIPSState *env) |
| 59 | { |
| 60 | do_raise_exception(env, EXCP_DEBUG, 0); |
| 61 | } |
| 62 | |
| 63 | static void raise_exception(CPUMIPSState *env, uint32_t exception) |
| 64 | { |
| 65 | do_raise_exception(env, exception, 0); |
| 66 | } |
| 67 | |
| 68 | void helper_wait(CPUMIPSState *env) |
| 69 | { |
| 70 | CPUState *cs = env_cpu(env); |
| 71 | |
| 72 | cs->halted = 1; |
| 73 | cpu_reset_interrupt(cs, CPU_INTERRUPT_WAKE); |
| 74 | /* |
| 75 | * Last instruction in the block, PC was updated before |
| 76 | * - no need to recover PC and icount. |
| 77 | */ |
| 78 | raise_exception(env, EXCP_HLT); |
| 79 | } |
| 80 | |
| 81 | void mips_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) |
| 82 | { |
| 83 | CPUMIPSState *env = cpu_env(cs); |
| 84 | |
| 85 | tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL)); |
| 86 | env->active_tc.PC = tb->pc; |
| 87 | env->hflags &= ~MIPS_HFLAG_BMASK; |
| 88 | env->hflags |= tb->flags & MIPS_HFLAG_BMASK; |
| 89 | } |
| 90 | |
| 91 | static const char * const excp_names[EXCP_LAST + 1] = { |
| 92 | [EXCP_RESET] = "reset", |
| 93 | [EXCP_SRESET] = "soft reset", |
| 94 | [EXCP_DSS] = "debug single step", |
| 95 | [EXCP_DINT] = "debug interrupt", |
| 96 | [EXCP_NMI] = "non-maskable interrupt", |
| 97 | [EXCP_MCHECK] = "machine check", |
| 98 | [EXCP_EXT_INTERRUPT] = "interrupt", |
| 99 | [EXCP_DFWATCH] = "deferred watchpoint", |
| 100 | [EXCP_DIB] = "debug instruction breakpoint", |
| 101 | [EXCP_IWATCH] = "instruction fetch watchpoint", |
| 102 | [EXCP_AdEL] = "address error load", |
| 103 | [EXCP_AdES] = "address error store", |
| 104 | [EXCP_TLBF] = "TLB refill", |
| 105 | [EXCP_IBE] = "instruction bus error", |
| 106 | [EXCP_DBp] = "debug breakpoint", |
| 107 | [EXCP_SYSCALL] = "syscall", |
| 108 | [EXCP_BREAK] = "break", |
| 109 | [EXCP_CpU] = "coprocessor unusable", |
| 110 | [EXCP_RI] = "reserved instruction", |
| 111 | [EXCP_OVERFLOW] = "arithmetic overflow", |
| 112 | [EXCP_TRAP] = "trap", |
| 113 | [EXCP_FPE] = "floating point", |
| 114 | [EXCP_DDBS] = "debug data break store", |
| 115 | [EXCP_DWATCH] = "data watchpoint", |
| 116 | [EXCP_LTLBL] = "TLB modify", |
| 117 | [EXCP_TLBL] = "TLB load", |
| 118 | [EXCP_TLBS] = "TLB store", |
| 119 | [EXCP_DBE] = "data bus error", |
| 120 | [EXCP_DDBL] = "debug data break load", |
| 121 | [EXCP_THREAD] = "thread", |
| 122 | [EXCP_MDMX] = "MDMX", |
| 123 | [EXCP_C2E] = "precise coprocessor 2", |
| 124 | [EXCP_CACHE] = "cache error", |
| 125 | [EXCP_TLBXI] = "TLB execute-inhibit", |
| 126 | [EXCP_TLBRI] = "TLB read-inhibit", |
| 127 | [EXCP_MSADIS] = "MSA disabled", |
| 128 | [EXCP_MSAFPE] = "MSA floating point", |
| 129 | [EXCP_SEMIHOST] = "Semihosting", |
| 130 | }; |
| 131 | |
| 132 | const char *mips_exception_name(int32_t exception) |
| 133 | { |
| 134 | if (exception < 0 || exception > EXCP_LAST) { |
| 135 | return "unknown"; |
| 136 | } |
| 137 | return excp_names[exception]; |
| 138 | } |
| 139 | |
| 140 | void do_raise_exception_err(CPUMIPSState *env, uint32_t exception, |
| 141 | int error_code, uintptr_t pc) |
| 142 | { |
| 143 | CPUState *cs = env_cpu(env); |
| 144 | |
| 145 | qemu_log_mask(CPU_LOG_INT, "%s: %d (%s) %d\n", |
| 146 | __func__, exception, mips_exception_name(exception), |
| 147 | error_code); |
| 148 | cs->exception_index = exception; |
| 149 | env->error_code = error_code; |
| 150 | |
| 151 | cpu_loop_exit_restore(cs, pc); |
| 152 | } |