diff --git a/qemu/accel/tcg/cpu-exec.c b/qemu/accel/tcg/cpu-exec.c index 77168f35a7..0872fd2e69 100644 --- a/qemu/accel/tcg/cpu-exec.c +++ b/qemu/accel/tcg/cpu-exec.c @@ -390,10 +390,6 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret) CPUMIPSState *env = &(MIPS_CPU(cpu)->env); env->active_tc.PC = uc->next_pc; #endif -#if defined(TARGET_RISCV) - CPURISCVState *env = &(RISCV_CPU(uc->cpu)->env); - env->pc += 4; -#endif #if defined(TARGET_SPARC) CPUSPARCState *env = &(SPARC_CPU(uc->cpu)->env); env->pc = env->npc; @@ -423,6 +419,15 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret) return true; } +#if defined(TARGET_RISCV) + if (cpu->exception_index == RISCV_EXCP_U_ECALL || + cpu->exception_index == RISCV_EXCP_S_ECALL || + cpu->exception_index == RISCV_EXCP_VS_ECALL || + cpu->exception_index == RISCV_EXCP_M_ECALL) { + RISCV_CPU(uc->cpu)->env.pc = uc->next_pc; + } +#endif + cpu->exception_index = -1; } diff --git a/qemu/target/riscv/translate.c b/qemu/target/riscv/translate.c index 6aedcb86bc..c7c8002c8e 100644 --- a/qemu/target/riscv/translate.c +++ b/qemu/target/riscv/translate.c @@ -91,7 +91,8 @@ static inline bool has_ext(DisasContext *ctx, uint32_t ext) static void generate_exception(DisasContext *ctx, int excp) { TCGContext *tcg_ctx = ctx->uc->tcg_ctx; - + /* Save the post-exception PC so handled ecall hooks can resume execution. */ + ctx->uc->next_pc = ctx->pc_succ_insn; tcg_gen_movi_tl(tcg_ctx, tcg_ctx->cpu_pc, ctx->base.pc_next); TCGv_i32 helper_tmp = tcg_const_i32(tcg_ctx, excp); gen_helper_raise_exception(tcg_ctx, tcg_ctx->cpu_env, helper_tmp); diff --git a/tests/unit/test_riscv.c b/tests/unit/test_riscv.c index 86c633eb83..684b3bda3a 100644 --- a/tests/unit/test_riscv.c +++ b/tests/unit/test_riscv.c @@ -545,32 +545,105 @@ static void test_riscv64_code_patching_count(void) OK(uc_close(uc)); } -static void test_riscv64_ecall_cb(uc_engine *uc, uint32_t intno, void *data) +static void test_riscv_ecall_pc_cb(uc_engine *uc, uint32_t intno, void *data) { + uint64_t pc = 0; + + TEST_CHECK(intno == 8); // U-mode ecall + OK(uc_reg_read(uc, UC_RISCV_REG_PC, &pc)); + TEST_CHECK(pc == *(uint64_t *)data); + + uc_emu_stop(uc); +} + +static void test_riscv_illegal_pc_cb(uc_engine *uc, uint32_t intno, void *data) +{ + uint64_t pc = 0; + + TEST_CHECK(intno == 2); // illegal instruction + OK(uc_reg_read(uc, UC_RISCV_REG_PC, &pc)); + TEST_CHECK(pc == *(uint64_t *)data); + uc_emu_stop(uc); - return; } -static void test_riscv64_ecall(void) +static void test_riscv_ecall_pc(uc_mode mode) { uc_engine *uc; char code[] = "\x73\x00\x00\x00"; // ecall - uint64_t r_pc; + uint64_t expected_pc = code_start; + uint64_t pc = 0; uc_hook h; - uc_common_setup(&uc, UC_ARCH_RISCV, UC_MODE_RISCV64, code, - sizeof(code) - 1); + uc_common_setup(&uc, UC_ARCH_RISCV, mode, code, sizeof(code) - 1); - OK(uc_hook_add(uc, &h, UC_HOOK_INTR, test_riscv64_ecall_cb, NULL, 1, 0)); + OK(uc_hook_add(uc, &h, UC_HOOK_INTR, test_riscv_ecall_pc_cb, + &expected_pc, 1, 0)); OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); - OK(uc_reg_read(uc, UC_RISCV_REG_PC, &r_pc)); + OK(uc_reg_read(uc, UC_RISCV_REG_PC, &pc)); + TEST_CHECK(pc == code_start + 4); + + OK(uc_close(uc)); +} + +static void test_riscv_exception_pc(uc_mode mode, const char *code, size_t size) +{ + uc_engine *uc; + uint64_t expected_pc = code_start; + uint64_t pc = 0; + uc_hook h; + + uc_common_setup(&uc, UC_ARCH_RISCV, mode, code, size); + + OK(uc_hook_add(uc, &h, UC_HOOK_INTR, test_riscv_illegal_pc_cb, + &expected_pc, 1, 0)); + OK(uc_emu_start(uc, code_start, code_start + size, 0, 0)); - TEST_CHECK(r_pc == code_start + 4); + OK(uc_reg_read(uc, UC_RISCV_REG_PC, &pc)); + TEST_CHECK(pc == code_start); OK(uc_close(uc)); } +static void test_riscv32_ecall_pc(void) +{ + test_riscv_ecall_pc(UC_MODE_RISCV32); +} + +static void test_riscv64_ecall_pc(void) +{ + test_riscv_ecall_pc(UC_MODE_RISCV64); +} + +static void test_riscv32_illegal16_pc(void) +{ + char code[] = "\x00\x00"; // illegal compressed encoding + + test_riscv_exception_pc(UC_MODE_RISCV32, code, sizeof(code) - 1); +} + +static void test_riscv64_illegal16_pc(void) +{ + char code[] = "\x00\x00"; // illegal compressed encoding + + test_riscv_exception_pc(UC_MODE_RISCV64, code, sizeof(code) - 1); +} + +static void test_riscv32_illegal32_pc(void) +{ + char code[] = "\x7f\x00\x00\x00"; // illegal 32-bit encoding + + test_riscv_exception_pc(UC_MODE_RISCV32, code, sizeof(code) - 1); +} + +static void test_riscv64_illegal32_pc(void) +{ + char code[] = "\x7f\x00\x00\x00"; // illegal 32-bit encoding + + test_riscv_exception_pc(UC_MODE_RISCV64, code, sizeof(code) - 1); +} + static uint64_t test_riscv32_mmio_map_read_cb(uc_engine *uc, uint64_t offset, unsigned size, void *data) { @@ -926,7 +999,12 @@ TEST_LIST = { {"test_riscv64_fclass_s_produced_nanbox", test_riscv64_fclass_s_produced_nanbox}, {"test_riscv64_fmv_w_x_nanbox", test_riscv64_fmv_w_x_nanbox}, - {"test_riscv64_ecall", test_riscv64_ecall}, + {"test_riscv32_ecall_pc", test_riscv32_ecall_pc}, + {"test_riscv64_ecall_pc", test_riscv64_ecall_pc}, + {"test_riscv32_illegal16_pc", test_riscv32_illegal16_pc}, + {"test_riscv64_illegal16_pc", test_riscv64_illegal16_pc}, + {"test_riscv32_illegal32_pc", test_riscv32_illegal32_pc}, + {"test_riscv64_illegal32_pc", test_riscv64_illegal32_pc}, {"test_riscv32_mmio_map", test_riscv32_mmio_map}, {"test_riscv64_mmio_map", test_riscv64_mmio_map}, {"test_riscv32_map", test_riscv32_map},