aboutsummaryrefslogtreecommitdiff
path: root/target-mips/op_helper.c
diff options
context:
space:
mode:
authorPavel Dovgaluk <Pavel.Dovgaluk@ispras.ru>2015-07-10 12:57:08 +0300
committerLeon Alrae <leon.alrae@imgtec.com>2015-09-18 12:07:24 +0100
commit9c708c7f9fbb813a3fac02f2728e51e62f2f5ffc (patch)
tree30c2e51a383243a8d135465729a43b0c2745d94f /target-mips/op_helper.c
parentd54a299b83a07642c85a22bfe19b69ca4def9ec4 (diff)
downloadqemu-9c708c7f9fbb813a3fac02f2728e51e62f2f5ffc.zip
qemu-9c708c7f9fbb813a3fac02f2728e51e62f2f5ffc.tar.gz
qemu-9c708c7f9fbb813a3fac02f2728e51e62f2f5ffc.tar.bz2
target-mips: improve exception handling
This patch improves exception handling in MIPS. Instructions generate several types of exceptions. When exception is generated, it breaks the execution of the current translation block. Implementation of the exceptions handling does not correctly restore icount for the instruction which caused the exception. In most cases icount will be decreased by the value equal to the size of TB. This patch passes pointer to the translation block internals to the exception handler. It allows correct restoring of the icount value. Signed-off-by: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru> Reviewed-by: Aurelien Jarno <aurelien@aurel32.net> Reviewed-by: Leon Alrae <leon.alrae@imgtec.com> [leon.alrae@imgtec.com: avoid retranslation in linux-user SC, break lines which are over 80 chars, remove v3 changelog from the commit message] Signed-off-by: Leon Alrae <leon.alrae@imgtec.com>
Diffstat (limited to 'target-mips/op_helper.c')
-rw-r--r--target-mips/op_helper.c254
1 files changed, 141 insertions, 113 deletions
diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c
index 94de108..6739fff 100644
--- a/target-mips/op_helper.c
+++ b/target-mips/op_helper.c
@@ -30,42 +30,23 @@ static inline void cpu_mips_tlb_flush (CPUMIPSState *env, int flush_global);
/*****************************************************************************/
/* Exceptions processing helpers */
-static inline void QEMU_NORETURN do_raise_exception_err(CPUMIPSState *env,
- uint32_t exception,
- int error_code,
- uintptr_t pc)
+void helper_raise_exception_err(CPUMIPSState *env, uint32_t exception,
+ int error_code)
{
- CPUState *cs = CPU(mips_env_get_cpu(env));
-
- if (exception < EXCP_SC) {
- qemu_log_mask(CPU_LOG_INT, "%s: %d %d\n",
- __func__, exception, error_code);
- }
- cs->exception_index = exception;
- env->error_code = error_code;
-
- if (pc) {
- /* now we have a real cpu fault */
- cpu_restore_state(cs, pc);
- }
-
- cpu_loop_exit(cs);
+ do_raise_exception_err(env, exception, error_code, 0);
}
-static inline void QEMU_NORETURN do_raise_exception(CPUMIPSState *env,
- uint32_t exception,
- uintptr_t pc)
+void helper_raise_exception(CPUMIPSState *env, uint32_t exception)
{
- do_raise_exception_err(env, exception, 0, pc);
+ do_raise_exception(env, exception, GETPC());
}
-void helper_raise_exception_err(CPUMIPSState *env, uint32_t exception,
- int error_code)
+void helper_raise_exception_debug(CPUMIPSState *env)
{
- do_raise_exception_err(env, exception, error_code, 0);
+ do_raise_exception(env, EXCP_DEBUG, 0);
}
-void helper_raise_exception(CPUMIPSState *env, uint32_t exception)
+static void raise_exception(CPUMIPSState *env, uint32_t exception)
{
do_raise_exception(env, exception, 0);
}
@@ -73,21 +54,21 @@ void helper_raise_exception(CPUMIPSState *env, uint32_t exception)
#if defined(CONFIG_USER_ONLY)
#define HELPER_LD(name, insn, type) \
static inline type do_##name(CPUMIPSState *env, target_ulong addr, \
- int mem_idx) \
+ int mem_idx, uintptr_t retaddr) \
{ \
- return (type) cpu_##insn##_data(env, addr); \
+ return (type) cpu_##insn##_data_ra(env, addr, retaddr); \
}
#else
#define HELPER_LD(name, insn, type) \
static inline type do_##name(CPUMIPSState *env, target_ulong addr, \
- int mem_idx) \
+ int mem_idx, uintptr_t retaddr) \
{ \
switch (mem_idx) \
{ \
- case 0: return (type) cpu_##insn##_kernel(env, addr); break; \
- case 1: return (type) cpu_##insn##_super(env, addr); break; \
+ case 0: return (type) cpu_##insn##_kernel_ra(env, addr, retaddr); \
+ case 1: return (type) cpu_##insn##_super_ra(env, addr, retaddr); \
default: \
- case 2: return (type) cpu_##insn##_user(env, addr); break; \
+ case 2: return (type) cpu_##insn##_user_ra(env, addr, retaddr); \
} \
}
#endif
@@ -100,21 +81,21 @@ HELPER_LD(ld, ldq, int64_t)
#if defined(CONFIG_USER_ONLY)
#define HELPER_ST(name, insn, type) \
static inline void do_##name(CPUMIPSState *env, target_ulong addr, \
- type val, int mem_idx) \
+ type val, int mem_idx, uintptr_t retaddr) \
{ \
- cpu_##insn##_data(env, addr, val); \
+ cpu_##insn##_data_ra(env, addr, val, retaddr); \
}
#else
#define HELPER_ST(name, insn, type) \
static inline void do_##name(CPUMIPSState *env, target_ulong addr, \
- type val, int mem_idx) \
+ type val, int mem_idx, uintptr_t retaddr) \
{ \
switch (mem_idx) \
{ \
- case 0: cpu_##insn##_kernel(env, addr, val); break; \
- case 1: cpu_##insn##_super(env, addr, val); break; \
+ case 0: cpu_##insn##_kernel_ra(env, addr, val, retaddr); break; \
+ case 1: cpu_##insn##_super_ra(env, addr, val, retaddr); break; \
default: \
- case 2: cpu_##insn##_user(env, addr, val); break; \
+ case 2: cpu_##insn##_user_ra(env, addr, val, retaddr); break; \
} \
}
#endif
@@ -293,14 +274,15 @@ target_ulong helper_bitswap(target_ulong rt)
static inline hwaddr do_translate_address(CPUMIPSState *env,
target_ulong address,
- int rw)
+ int rw, uintptr_t retaddr)
{
hwaddr lladdr;
+ CPUState *cs = CPU(mips_env_get_cpu(env));
lladdr = cpu_mips_translate_address(env, address, rw);
if (lladdr == -1LL) {
- cpu_loop_exit(CPU(mips_env_get_cpu(env)));
+ cpu_loop_exit_restore(cs, retaddr);
} else {
return lladdr;
}
@@ -311,10 +293,10 @@ target_ulong helper_##name(CPUMIPSState *env, target_ulong arg, int mem_idx) \
{ \
if (arg & almask) { \
env->CP0_BadVAddr = arg; \
- helper_raise_exception(env, EXCP_AdEL); \
+ do_raise_exception(env, EXCP_AdEL, GETPC()); \
} \
- env->lladdr = do_translate_address(env, arg, 0); \
- env->llval = do_##insn(env, arg, mem_idx); \
+ env->lladdr = do_translate_address(env, arg, 0, GETPC()); \
+ env->llval = do_##insn(env, arg, mem_idx, GETPC()); \
return env->llval; \
}
HELPER_LD_ATOMIC(ll, lw, 0x3)
@@ -331,12 +313,12 @@ target_ulong helper_##name(CPUMIPSState *env, target_ulong arg1, \
\
if (arg2 & almask) { \
env->CP0_BadVAddr = arg2; \
- helper_raise_exception(env, EXCP_AdES); \
+ do_raise_exception(env, EXCP_AdES, GETPC()); \
} \
- if (do_translate_address(env, arg2, 1) == env->lladdr) { \
- tmp = do_##ld_insn(env, arg2, mem_idx); \
+ if (do_translate_address(env, arg2, 1, GETPC()) == env->lladdr) { \
+ tmp = do_##ld_insn(env, arg2, mem_idx, GETPC()); \
if (tmp == env->llval) { \
- do_##st_insn(env, arg2, arg1, mem_idx); \
+ do_##st_insn(env, arg2, arg1, mem_idx, GETPC()); \
return 1; \
} \
} \
@@ -360,31 +342,43 @@ HELPER_ST_ATOMIC(scd, ld, sd, 0x7)
void helper_swl(CPUMIPSState *env, target_ulong arg1, target_ulong arg2,
int mem_idx)
{
- do_sb(env, arg2, (uint8_t)(arg1 >> 24), mem_idx);
+ do_sb(env, arg2, (uint8_t)(arg1 >> 24), mem_idx, GETPC());
- if (GET_LMASK(arg2) <= 2)
- do_sb(env, GET_OFFSET(arg2, 1), (uint8_t)(arg1 >> 16), mem_idx);
+ if (GET_LMASK(arg2) <= 2) {
+ do_sb(env, GET_OFFSET(arg2, 1), (uint8_t)(arg1 >> 16), mem_idx,
+ GETPC());
+ }
- if (GET_LMASK(arg2) <= 1)
- do_sb(env, GET_OFFSET(arg2, 2), (uint8_t)(arg1 >> 8), mem_idx);
+ if (GET_LMASK(arg2) <= 1) {
+ do_sb(env, GET_OFFSET(arg2, 2), (uint8_t)(arg1 >> 8), mem_idx,
+ GETPC());
+ }
- if (GET_LMASK(arg2) == 0)
- do_sb(env, GET_OFFSET(arg2, 3), (uint8_t)arg1, mem_idx);
+ if (GET_LMASK(arg2) == 0) {
+ do_sb(env, GET_OFFSET(arg2, 3), (uint8_t)arg1, mem_idx,
+ GETPC());
+ }
}
void helper_swr(CPUMIPSState *env, target_ulong arg1, target_ulong arg2,
int mem_idx)
{
- do_sb(env, arg2, (uint8_t)arg1, mem_idx);
+ do_sb(env, arg2, (uint8_t)arg1, mem_idx, GETPC());
- if (GET_LMASK(arg2) >= 1)
- do_sb(env, GET_OFFSET(arg2, -1), (uint8_t)(arg1 >> 8), mem_idx);
+ if (GET_LMASK(arg2) >= 1) {
+ do_sb(env, GET_OFFSET(arg2, -1), (uint8_t)(arg1 >> 8), mem_idx,
+ GETPC());
+ }
- if (GET_LMASK(arg2) >= 2)
- do_sb(env, GET_OFFSET(arg2, -2), (uint8_t)(arg1 >> 16), mem_idx);
+ if (GET_LMASK(arg2) >= 2) {
+ do_sb(env, GET_OFFSET(arg2, -2), (uint8_t)(arg1 >> 16), mem_idx,
+ GETPC());
+ }
- if (GET_LMASK(arg2) == 3)
- do_sb(env, GET_OFFSET(arg2, -3), (uint8_t)(arg1 >> 24), mem_idx);
+ if (GET_LMASK(arg2) == 3) {
+ do_sb(env, GET_OFFSET(arg2, -3), (uint8_t)(arg1 >> 24), mem_idx,
+ GETPC());
+ }
}
#if defined(TARGET_MIPS64)
@@ -400,55 +394,83 @@ void helper_swr(CPUMIPSState *env, target_ulong arg1, target_ulong arg2,
void helper_sdl(CPUMIPSState *env, target_ulong arg1, target_ulong arg2,
int mem_idx)
{
- do_sb(env, arg2, (uint8_t)(arg1 >> 56), mem_idx);
+ do_sb(env, arg2, (uint8_t)(arg1 >> 56), mem_idx, GETPC());
- if (GET_LMASK64(arg2) <= 6)
- do_sb(env, GET_OFFSET(arg2, 1), (uint8_t)(arg1 >> 48), mem_idx);
+ if (GET_LMASK64(arg2) <= 6) {
+ do_sb(env, GET_OFFSET(arg2, 1), (uint8_t)(arg1 >> 48), mem_idx,
+ GETPC());
+ }
- if (GET_LMASK64(arg2) <= 5)
- do_sb(env, GET_OFFSET(arg2, 2), (uint8_t)(arg1 >> 40), mem_idx);
+ if (GET_LMASK64(arg2) <= 5) {
+ do_sb(env, GET_OFFSET(arg2, 2), (uint8_t)(arg1 >> 40), mem_idx,
+ GETPC());
+ }
- if (GET_LMASK64(arg2) <= 4)
- do_sb(env, GET_OFFSET(arg2, 3), (uint8_t)(arg1 >> 32), mem_idx);
+ if (GET_LMASK64(arg2) <= 4) {
+ do_sb(env, GET_OFFSET(arg2, 3), (uint8_t)(arg1 >> 32), mem_idx,
+ GETPC());
+ }
- if (GET_LMASK64(arg2) <= 3)
- do_sb(env, GET_OFFSET(arg2, 4), (uint8_t)(arg1 >> 24), mem_idx);
+ if (GET_LMASK64(arg2) <= 3) {
+ do_sb(env, GET_OFFSET(arg2, 4), (uint8_t)(arg1 >> 24), mem_idx,
+ GETPC());
+ }
- if (GET_LMASK64(arg2) <= 2)
- do_sb(env, GET_OFFSET(arg2, 5), (uint8_t)(arg1 >> 16), mem_idx);
+ if (GET_LMASK64(arg2) <= 2) {
+ do_sb(env, GET_OFFSET(arg2, 5), (uint8_t)(arg1 >> 16), mem_idx,
+ GETPC());
+ }
- if (GET_LMASK64(arg2) <= 1)
- do_sb(env, GET_OFFSET(arg2, 6), (uint8_t)(arg1 >> 8), mem_idx);
+ if (GET_LMASK64(arg2) <= 1) {
+ do_sb(env, GET_OFFSET(arg2, 6), (uint8_t)(arg1 >> 8), mem_idx,
+ GETPC());
+ }
- if (GET_LMASK64(arg2) <= 0)
- do_sb(env, GET_OFFSET(arg2, 7), (uint8_t)arg1, mem_idx);
+ if (GET_LMASK64(arg2) <= 0) {
+ do_sb(env, GET_OFFSET(arg2, 7), (uint8_t)arg1, mem_idx,
+ GETPC());
+ }
}
void helper_sdr(CPUMIPSState *env, target_ulong arg1, target_ulong arg2,
int mem_idx)
{
- do_sb(env, arg2, (uint8_t)arg1, mem_idx);
+ do_sb(env, arg2, (uint8_t)arg1, mem_idx, GETPC());
- if (GET_LMASK64(arg2) >= 1)
- do_sb(env, GET_OFFSET(arg2, -1), (uint8_t)(arg1 >> 8), mem_idx);
+ if (GET_LMASK64(arg2) >= 1) {
+ do_sb(env, GET_OFFSET(arg2, -1), (uint8_t)(arg1 >> 8), mem_idx,
+ GETPC());
+ }
- if (GET_LMASK64(arg2) >= 2)
- do_sb(env, GET_OFFSET(arg2, -2), (uint8_t)(arg1 >> 16), mem_idx);
+ if (GET_LMASK64(arg2) >= 2) {
+ do_sb(env, GET_OFFSET(arg2, -2), (uint8_t)(arg1 >> 16), mem_idx,
+ GETPC());
+ }
- if (GET_LMASK64(arg2) >= 3)
- do_sb(env, GET_OFFSET(arg2, -3), (uint8_t)(arg1 >> 24), mem_idx);
+ if (GET_LMASK64(arg2) >= 3) {
+ do_sb(env, GET_OFFSET(arg2, -3), (uint8_t)(arg1 >> 24), mem_idx,
+ GETPC());
+ }
- if (GET_LMASK64(arg2) >= 4)
- do_sb(env, GET_OFFSET(arg2, -4), (uint8_t)(arg1 >> 32), mem_idx);
+ if (GET_LMASK64(arg2) >= 4) {
+ do_sb(env, GET_OFFSET(arg2, -4), (uint8_t)(arg1 >> 32), mem_idx,
+ GETPC());
+ }
- if (GET_LMASK64(arg2) >= 5)
- do_sb(env, GET_OFFSET(arg2, -5), (uint8_t)(arg1 >> 40), mem_idx);
+ if (GET_LMASK64(arg2) >= 5) {
+ do_sb(env, GET_OFFSET(arg2, -5), (uint8_t)(arg1 >> 40), mem_idx,
+ GETPC());
+ }
- if (GET_LMASK64(arg2) >= 6)
- do_sb(env, GET_OFFSET(arg2, -6), (uint8_t)(arg1 >> 48), mem_idx);
+ if (GET_LMASK64(arg2) >= 6) {
+ do_sb(env, GET_OFFSET(arg2, -6), (uint8_t)(arg1 >> 48), mem_idx,
+ GETPC());
+ }
- if (GET_LMASK64(arg2) == 7)
- do_sb(env, GET_OFFSET(arg2, -7), (uint8_t)(arg1 >> 56), mem_idx);
+ if (GET_LMASK64(arg2) == 7) {
+ do_sb(env, GET_OFFSET(arg2, -7), (uint8_t)(arg1 >> 56), mem_idx,
+ GETPC());
+ }
}
#endif /* TARGET_MIPS64 */
@@ -465,13 +487,14 @@ void helper_lwm(CPUMIPSState *env, target_ulong addr, target_ulong reglist,
for (i = 0; i < base_reglist; i++) {
env->active_tc.gpr[multiple_regs[i]] =
- (target_long)do_lw(env, addr, mem_idx);
+ (target_long)do_lw(env, addr, mem_idx, GETPC());
addr += 4;
}
}
if (do_r31) {
- env->active_tc.gpr[31] = (target_long)do_lw(env, addr, mem_idx);
+ env->active_tc.gpr[31] = (target_long)do_lw(env, addr, mem_idx,
+ GETPC());
}
}
@@ -485,13 +508,14 @@ void helper_swm(CPUMIPSState *env, target_ulong addr, target_ulong reglist,
target_ulong i;
for (i = 0; i < base_reglist; i++) {
- do_sw(env, addr, env->active_tc.gpr[multiple_regs[i]], mem_idx);
+ do_sw(env, addr, env->active_tc.gpr[multiple_regs[i]], mem_idx,
+ GETPC());
addr += 4;
}
}
if (do_r31) {
- do_sw(env, addr, env->active_tc.gpr[31], mem_idx);
+ do_sw(env, addr, env->active_tc.gpr[31], mem_idx, GETPC());
}
}
@@ -506,13 +530,14 @@ void helper_ldm(CPUMIPSState *env, target_ulong addr, target_ulong reglist,
target_ulong i;
for (i = 0; i < base_reglist; i++) {
- env->active_tc.gpr[multiple_regs[i]] = do_ld(env, addr, mem_idx);
+ env->active_tc.gpr[multiple_regs[i]] = do_ld(env, addr, mem_idx,
+ GETPC());
addr += 8;
}
}
if (do_r31) {
- env->active_tc.gpr[31] = do_ld(env, addr, mem_idx);
+ env->active_tc.gpr[31] = do_ld(env, addr, mem_idx, GETPC());
}
}
@@ -526,13 +551,14 @@ void helper_sdm(CPUMIPSState *env, target_ulong addr, target_ulong reglist,
target_ulong i;
for (i = 0; i < base_reglist; i++) {
- do_sd(env, addr, env->active_tc.gpr[multiple_regs[i]], mem_idx);
+ do_sd(env, addr, env->active_tc.gpr[multiple_regs[i]], mem_idx,
+ GETPC());
addr += 8;
}
}
if (do_r31) {
- do_sd(env, addr, env->active_tc.gpr[31], mem_idx);
+ do_sd(env, addr, env->active_tc.gpr[31], mem_idx, GETPC());
}
}
#endif
@@ -1792,13 +1818,13 @@ target_ulong helper_yield(CPUMIPSState *env, target_ulong arg)
env->active_tc.CP0_TCStatus & (1 << CP0TCSt_DT)) {
env->CP0_VPEControl &= ~(0x7 << CP0VPECo_EXCPT);
env->CP0_VPEControl |= 4 << CP0VPECo_EXCPT;
- helper_raise_exception(env, EXCP_THREAD);
+ do_raise_exception(env, EXCP_THREAD, GETPC());
}
}
} else if (arg1 == 0) {
if (0 /* TODO: TC underflow */) {
env->CP0_VPEControl &= ~(0x7 << CP0VPECo_EXCPT);
- helper_raise_exception(env, EXCP_THREAD);
+ do_raise_exception(env, EXCP_THREAD, GETPC());
} else {
// TODO: Deallocate TC
}
@@ -1806,7 +1832,7 @@ target_ulong helper_yield(CPUMIPSState *env, target_ulong arg)
/* Yield qualifier inputs not implemented. */
env->CP0_VPEControl &= ~(0x7 << CP0VPECo_EXCPT);
env->CP0_VPEControl |= 2 << CP0VPECo_EXCPT;
- helper_raise_exception(env, EXCP_THREAD);
+ do_raise_exception(env, EXCP_THREAD, GETPC());
}
return env->CP0_YQMask;
}
@@ -2165,7 +2191,7 @@ target_ulong helper_rdhwr_cpunum(CPUMIPSState *env)
(env->CP0_HWREna & (1 << 0)))
return env->CP0_EBase & 0x3ff;
else
- helper_raise_exception(env, EXCP_RI);
+ do_raise_exception(env, EXCP_RI, GETPC());
return 0;
}
@@ -2176,7 +2202,7 @@ target_ulong helper_rdhwr_synci_step(CPUMIPSState *env)
(env->CP0_HWREna & (1 << 1)))
return env->SYNCI_Step;
else
- helper_raise_exception(env, EXCP_RI);
+ do_raise_exception(env, EXCP_RI, GETPC());
return 0;
}
@@ -2191,7 +2217,7 @@ target_ulong helper_rdhwr_cc(CPUMIPSState *env)
return (int32_t)cpu_mips_get_count(env);
#endif
} else {
- helper_raise_exception(env, EXCP_RI);
+ do_raise_exception(env, EXCP_RI, GETPC());
}
return 0;
@@ -2203,7 +2229,7 @@ target_ulong helper_rdhwr_ccres(CPUMIPSState *env)
(env->CP0_HWREna & (1 << 3)))
return env->CCRes;
else
- helper_raise_exception(env, EXCP_RI);
+ do_raise_exception(env, EXCP_RI, GETPC());
return 0;
}
@@ -2240,7 +2266,9 @@ void helper_wait(CPUMIPSState *env)
cs->halted = 1;
cpu_reset_interrupt(cs, CPU_INTERRUPT_WAKE);
- helper_raise_exception(env, EXCP_HLT);
+ /* Last instruction in the block, PC was updated before
+ - no need to recover PC and icount */
+ raise_exception(env, EXCP_HLT);
}
#if !defined(CONFIG_USER_ONLY)
@@ -2301,9 +2329,9 @@ void mips_cpu_unassigned_access(CPUState *cs, hwaddr addr,
}
if (is_exec) {
- helper_raise_exception(env, EXCP_IBE);
+ raise_exception(env, EXCP_IBE);
} else {
- helper_raise_exception(env, EXCP_DBE);
+ raise_exception(env, EXCP_DBE);
}
}
#endif /* !CONFIG_USER_ONLY */
@@ -2338,7 +2366,7 @@ target_ulong helper_cfc1(CPUMIPSState *env, uint32_t reg)
arg1 = (int32_t)
((env->CP0_Status & (1 << CP0St_FR)) >> CP0St_FR);
} else {
- helper_raise_exception(env, EXCP_RI);
+ do_raise_exception(env, EXCP_RI, GETPC());
}
}
break;
@@ -2381,7 +2409,7 @@ void helper_ctc1(CPUMIPSState *env, target_ulong arg1, uint32_t fs, uint32_t rt)
env->CP0_Status &= ~(1 << CP0St_FR);
compute_hflags(env);
} else {
- helper_raise_exception(env, EXCP_RI);
+ do_raise_exception(env, EXCP_RI, GETPC());
}
break;
case 4:
@@ -2393,7 +2421,7 @@ void helper_ctc1(CPUMIPSState *env, target_ulong arg1, uint32_t fs, uint32_t rt)
env->CP0_Status |= (1 << CP0St_FR);
compute_hflags(env);
} else {
- helper_raise_exception(env, EXCP_RI);
+ do_raise_exception(env, EXCP_RI, GETPC());
}
break;
case 5: