target-lm32: stop VM on illegal or unknown instruction
Instead of translating the instruction to a no-op, pause the VM and display a message to the user. As a side effect, this also works for instructions where the operands are only known at runtime. Signed-off-by: Michael Walle <michael@walle.cc>
This commit is contained in:
		
							parent
							
								
									8c5edce5b7
								
							
						
					
					
						commit
						667ff9612b
					
				| 
						 | 
					@ -13,5 +13,6 @@ DEF_HELPER_1(rcsr_im, i32, env)
 | 
				
			||||||
DEF_HELPER_1(rcsr_ip, i32, env)
 | 
					DEF_HELPER_1(rcsr_ip, i32, env)
 | 
				
			||||||
DEF_HELPER_1(rcsr_jtx, i32, env)
 | 
					DEF_HELPER_1(rcsr_jtx, i32, env)
 | 
				
			||||||
DEF_HELPER_1(rcsr_jrx, i32, env)
 | 
					DEF_HELPER_1(rcsr_jrx, i32, env)
 | 
				
			||||||
 | 
					DEF_HELPER_1(ill, void, env)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "exec/def-helper.h"
 | 
					#include "exec/def-helper.h"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,6 +8,10 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "exec/softmmu_exec.h"
 | 
					#include "exec/softmmu_exec.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef CONFIG_USER_ONLY
 | 
				
			||||||
 | 
					#include "sysemu/sysemu.h"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if !defined(CONFIG_USER_ONLY)
 | 
					#if !defined(CONFIG_USER_ONLY)
 | 
				
			||||||
#define MMUSUFFIX _mmu
 | 
					#define MMUSUFFIX _mmu
 | 
				
			||||||
#define SHIFT 0
 | 
					#define SHIFT 0
 | 
				
			||||||
| 
						 | 
					@ -39,6 +43,19 @@ void HELPER(hlt)(CPULM32State *env)
 | 
				
			||||||
    cpu_loop_exit(env);
 | 
					    cpu_loop_exit(env);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void HELPER(ill)(CPULM32State *env)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					#ifndef CONFIG_USER_ONLY
 | 
				
			||||||
 | 
					    CPUState *cs = CPU(lm32_env_get_cpu(env));
 | 
				
			||||||
 | 
					    fprintf(stderr, "VM paused due to illegal instruction. "
 | 
				
			||||||
 | 
					            "Connect a debugger or switch to the monitor console "
 | 
				
			||||||
 | 
					            "to find out more.\n");
 | 
				
			||||||
 | 
					    qemu_system_vmstop_request(RUN_STATE_PAUSED);
 | 
				
			||||||
 | 
					    cs->halted = 1;
 | 
				
			||||||
 | 
					    raise_exception(env, EXCP_HALTED);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void HELPER(wcsr_bp)(CPULM32State *env, uint32_t bp, uint32_t idx)
 | 
					void HELPER(wcsr_bp)(CPULM32State *env, uint32_t bp, uint32_t idx)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    uint32_t addr = bp & ~1;
 | 
					    uint32_t addr = bp & ~1;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -122,6 +122,12 @@ static inline void t_gen_raise_exception(DisasContext *dc, uint32_t index)
 | 
				
			||||||
    tcg_temp_free_i32(tmp);
 | 
					    tcg_temp_free_i32(tmp);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline void t_gen_illegal_insn(DisasContext *dc)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    tcg_gen_movi_tl(cpu_pc, dc->pc);
 | 
				
			||||||
 | 
					    gen_helper_ill(cpu_env);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest)
 | 
					static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    TranslationBlock *tb;
 | 
					    TranslationBlock *tb;
 | 
				
			||||||
| 
						 | 
					@ -425,6 +431,7 @@ static void dec_divu(DisasContext *dc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!(dc->features & LM32_FEATURE_DIVIDE)) {
 | 
					    if (!(dc->features & LM32_FEATURE_DIVIDE)) {
 | 
				
			||||||
        qemu_log_mask(LOG_GUEST_ERROR, "hardware divider is not available\n");
 | 
					        qemu_log_mask(LOG_GUEST_ERROR, "hardware divider is not available\n");
 | 
				
			||||||
 | 
					        t_gen_illegal_insn(dc);
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -504,6 +511,7 @@ static void dec_modu(DisasContext *dc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!(dc->features & LM32_FEATURE_DIVIDE)) {
 | 
					    if (!(dc->features & LM32_FEATURE_DIVIDE)) {
 | 
				
			||||||
        qemu_log_mask(LOG_GUEST_ERROR, "hardware divider is not available\n");
 | 
					        qemu_log_mask(LOG_GUEST_ERROR, "hardware divider is not available\n");
 | 
				
			||||||
 | 
					        t_gen_illegal_insn(dc);
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -527,6 +535,7 @@ static void dec_mul(DisasContext *dc)
 | 
				
			||||||
    if (!(dc->features & LM32_FEATURE_MULTIPLY)) {
 | 
					    if (!(dc->features & LM32_FEATURE_MULTIPLY)) {
 | 
				
			||||||
        qemu_log_mask(LOG_GUEST_ERROR,
 | 
					        qemu_log_mask(LOG_GUEST_ERROR,
 | 
				
			||||||
                      "hardware multiplier is not available\n");
 | 
					                      "hardware multiplier is not available\n");
 | 
				
			||||||
 | 
					        t_gen_illegal_insn(dc);
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -591,21 +600,21 @@ static void dec_orhi(DisasContext *dc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void dec_scall(DisasContext *dc)
 | 
					static void dec_scall(DisasContext *dc)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    if (dc->imm5 == 7) {
 | 
					    switch (dc->imm5) {
 | 
				
			||||||
        LOG_DIS("scall\n");
 | 
					    case 2:
 | 
				
			||||||
    } else if (dc->imm5 == 2) {
 | 
					 | 
				
			||||||
        LOG_DIS("break\n");
 | 
					        LOG_DIS("break\n");
 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        qemu_log_mask(LOG_GUEST_ERROR, "invalid opcode @0x%x", dc->pc);
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (dc->imm5 == 7) {
 | 
					 | 
				
			||||||
        tcg_gen_movi_tl(cpu_pc, dc->pc);
 | 
					 | 
				
			||||||
        t_gen_raise_exception(dc, EXCP_SYSTEMCALL);
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        tcg_gen_movi_tl(cpu_pc, dc->pc);
 | 
					        tcg_gen_movi_tl(cpu_pc, dc->pc);
 | 
				
			||||||
        t_gen_raise_exception(dc, EXCP_BREAKPOINT);
 | 
					        t_gen_raise_exception(dc, EXCP_BREAKPOINT);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case 7:
 | 
				
			||||||
 | 
					        LOG_DIS("scall\n");
 | 
				
			||||||
 | 
					        tcg_gen_movi_tl(cpu_pc, dc->pc);
 | 
				
			||||||
 | 
					        t_gen_raise_exception(dc, EXCP_SYSTEMCALL);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					        qemu_log_mask(LOG_GUEST_ERROR, "invalid opcode @0x%x", dc->pc);
 | 
				
			||||||
 | 
					        t_gen_illegal_insn(dc);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -681,6 +690,7 @@ static void dec_sextb(DisasContext *dc)
 | 
				
			||||||
    if (!(dc->features & LM32_FEATURE_SIGN_EXTEND)) {
 | 
					    if (!(dc->features & LM32_FEATURE_SIGN_EXTEND)) {
 | 
				
			||||||
        qemu_log_mask(LOG_GUEST_ERROR,
 | 
					        qemu_log_mask(LOG_GUEST_ERROR,
 | 
				
			||||||
                      "hardware sign extender is not available\n");
 | 
					                      "hardware sign extender is not available\n");
 | 
				
			||||||
 | 
					        t_gen_illegal_insn(dc);
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -694,6 +704,7 @@ static void dec_sexth(DisasContext *dc)
 | 
				
			||||||
    if (!(dc->features & LM32_FEATURE_SIGN_EXTEND)) {
 | 
					    if (!(dc->features & LM32_FEATURE_SIGN_EXTEND)) {
 | 
				
			||||||
        qemu_log_mask(LOG_GUEST_ERROR,
 | 
					        qemu_log_mask(LOG_GUEST_ERROR,
 | 
				
			||||||
                      "hardware sign extender is not available\n");
 | 
					                      "hardware sign extender is not available\n");
 | 
				
			||||||
 | 
					        t_gen_illegal_insn(dc);
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -722,6 +733,7 @@ static void dec_sl(DisasContext *dc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!(dc->features & LM32_FEATURE_SHIFT)) {
 | 
					    if (!(dc->features & LM32_FEATURE_SHIFT)) {
 | 
				
			||||||
        qemu_log_mask(LOG_GUEST_ERROR, "hardware shifter is not available\n");
 | 
					        qemu_log_mask(LOG_GUEST_ERROR, "hardware shifter is not available\n");
 | 
				
			||||||
 | 
					        t_gen_illegal_insn(dc);
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -743,24 +755,32 @@ static void dec_sr(DisasContext *dc)
 | 
				
			||||||
        LOG_DIS("sr r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1);
 | 
					        LOG_DIS("sr r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!(dc->features & LM32_FEATURE_SHIFT)) {
 | 
					    /* The real CPU (w/o hardware shifter) only supports right shift by exactly
 | 
				
			||||||
        if (dc->format == OP_FMT_RI) {
 | 
					     * one bit */
 | 
				
			||||||
            /* TODO: check r1 == 1 during runtime */
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            if (dc->imm5 != 1) {
 | 
					 | 
				
			||||||
                qemu_log_mask(LOG_GUEST_ERROR,
 | 
					 | 
				
			||||||
                              "hardware shifter is not available\n");
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (dc->format == OP_FMT_RI) {
 | 
					    if (dc->format == OP_FMT_RI) {
 | 
				
			||||||
 | 
					        if (!(dc->features & LM32_FEATURE_SHIFT) && (dc->imm5 != 1)) {
 | 
				
			||||||
 | 
					            qemu_log_mask(LOG_GUEST_ERROR,
 | 
				
			||||||
 | 
					                    "hardware shifter is not available\n");
 | 
				
			||||||
 | 
					            t_gen_illegal_insn(dc);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        tcg_gen_sari_tl(cpu_R[dc->r1], cpu_R[dc->r0], dc->imm5);
 | 
					        tcg_gen_sari_tl(cpu_R[dc->r1], cpu_R[dc->r0], dc->imm5);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        TCGv t0 = tcg_temp_new();
 | 
					        int l1 = gen_new_label();
 | 
				
			||||||
 | 
					        int l2 = gen_new_label();
 | 
				
			||||||
 | 
					        TCGv t0 = tcg_temp_local_new();
 | 
				
			||||||
        tcg_gen_andi_tl(t0, cpu_R[dc->r1], 0x1f);
 | 
					        tcg_gen_andi_tl(t0, cpu_R[dc->r1], 0x1f);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!(dc->features & LM32_FEATURE_SHIFT)) {
 | 
				
			||||||
 | 
					            tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 1, l1);
 | 
				
			||||||
 | 
					            t_gen_illegal_insn(dc);
 | 
				
			||||||
 | 
					            tcg_gen_br(l2);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        gen_set_label(l1);
 | 
				
			||||||
        tcg_gen_sar_tl(cpu_R[dc->r2], cpu_R[dc->r0], t0);
 | 
					        tcg_gen_sar_tl(cpu_R[dc->r2], cpu_R[dc->r0], t0);
 | 
				
			||||||
 | 
					        gen_set_label(l2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        tcg_temp_free(t0);
 | 
					        tcg_temp_free(t0);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -773,24 +793,30 @@ static void dec_sru(DisasContext *dc)
 | 
				
			||||||
        LOG_DIS("sru r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1);
 | 
					        LOG_DIS("sru r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!(dc->features & LM32_FEATURE_SHIFT)) {
 | 
					 | 
				
			||||||
        if (dc->format == OP_FMT_RI) {
 | 
					 | 
				
			||||||
            /* TODO: check r1 == 1 during runtime */
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            if (dc->imm5 != 1) {
 | 
					 | 
				
			||||||
                qemu_log_mask(LOG_GUEST_ERROR,
 | 
					 | 
				
			||||||
                              "hardware shifter is not available\n");
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (dc->format == OP_FMT_RI) {
 | 
					    if (dc->format == OP_FMT_RI) {
 | 
				
			||||||
 | 
					        if (!(dc->features & LM32_FEATURE_SHIFT) && (dc->imm5 != 1)) {
 | 
				
			||||||
 | 
					            qemu_log_mask(LOG_GUEST_ERROR,
 | 
				
			||||||
 | 
					                    "hardware shifter is not available\n");
 | 
				
			||||||
 | 
					            t_gen_illegal_insn(dc);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        tcg_gen_shri_tl(cpu_R[dc->r1], cpu_R[dc->r0], dc->imm5);
 | 
					        tcg_gen_shri_tl(cpu_R[dc->r1], cpu_R[dc->r0], dc->imm5);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        TCGv t0 = tcg_temp_new();
 | 
					        int l1 = gen_new_label();
 | 
				
			||||||
 | 
					        int l2 = gen_new_label();
 | 
				
			||||||
 | 
					        TCGv t0 = tcg_temp_local_new();
 | 
				
			||||||
        tcg_gen_andi_tl(t0, cpu_R[dc->r1], 0x1f);
 | 
					        tcg_gen_andi_tl(t0, cpu_R[dc->r1], 0x1f);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!(dc->features & LM32_FEATURE_SHIFT)) {
 | 
				
			||||||
 | 
					            tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 1, l1);
 | 
				
			||||||
 | 
					            t_gen_illegal_insn(dc);
 | 
				
			||||||
 | 
					            tcg_gen_br(l2);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        gen_set_label(l1);
 | 
				
			||||||
        tcg_gen_shr_tl(cpu_R[dc->r2], cpu_R[dc->r0], t0);
 | 
					        tcg_gen_shr_tl(cpu_R[dc->r2], cpu_R[dc->r0], t0);
 | 
				
			||||||
 | 
					        gen_set_label(l2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        tcg_temp_free(t0);
 | 
					        tcg_temp_free(t0);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -819,6 +845,7 @@ static void dec_user(DisasContext *dc)
 | 
				
			||||||
    LOG_DIS("user");
 | 
					    LOG_DIS("user");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    qemu_log_mask(LOG_GUEST_ERROR, "user instruction undefined\n");
 | 
					    qemu_log_mask(LOG_GUEST_ERROR, "user instruction undefined\n");
 | 
				
			||||||
 | 
					    t_gen_illegal_insn(dc);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void dec_wcsr(DisasContext *dc)
 | 
					static void dec_wcsr(DisasContext *dc)
 | 
				
			||||||
| 
						 | 
					@ -886,6 +913,7 @@ static void dec_wcsr(DisasContext *dc)
 | 
				
			||||||
        if (dc->num_breakpoints <= no) {
 | 
					        if (dc->num_breakpoints <= no) {
 | 
				
			||||||
            qemu_log_mask(LOG_GUEST_ERROR,
 | 
					            qemu_log_mask(LOG_GUEST_ERROR,
 | 
				
			||||||
                          "breakpoint #%i is not available\n", no);
 | 
					                          "breakpoint #%i is not available\n", no);
 | 
				
			||||||
 | 
					            t_gen_illegal_insn(dc);
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        gen_helper_wcsr_bp(cpu_env, cpu_R[dc->r1], tcg_const_i32(no));
 | 
					        gen_helper_wcsr_bp(cpu_env, cpu_R[dc->r1], tcg_const_i32(no));
 | 
				
			||||||
| 
						 | 
					@ -898,6 +926,7 @@ static void dec_wcsr(DisasContext *dc)
 | 
				
			||||||
        if (dc->num_watchpoints <= no) {
 | 
					        if (dc->num_watchpoints <= no) {
 | 
				
			||||||
            qemu_log_mask(LOG_GUEST_ERROR,
 | 
					            qemu_log_mask(LOG_GUEST_ERROR,
 | 
				
			||||||
                          "watchpoint #%i is not available\n", no);
 | 
					                          "watchpoint #%i is not available\n", no);
 | 
				
			||||||
 | 
					            t_gen_illegal_insn(dc);
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        gen_helper_wcsr_wp(cpu_env, cpu_R[dc->r1], tcg_const_i32(no));
 | 
					        gen_helper_wcsr_wp(cpu_env, cpu_R[dc->r1], tcg_const_i32(no));
 | 
				
			||||||
| 
						 | 
					@ -956,6 +985,7 @@ static void dec_xor(DisasContext *dc)
 | 
				
			||||||
static void dec_ill(DisasContext *dc)
 | 
					static void dec_ill(DisasContext *dc)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    qemu_log_mask(LOG_GUEST_ERROR, "invalid opcode 0x%02x\n", dc->opcode);
 | 
					    qemu_log_mask(LOG_GUEST_ERROR, "invalid opcode 0x%02x\n", dc->opcode);
 | 
				
			||||||
 | 
					    t_gen_illegal_insn(dc);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef void (*DecoderInfo)(DisasContext *dc);
 | 
					typedef void (*DecoderInfo)(DisasContext *dc);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue