From 7e6d37b51dc49b985095564a73c796ab81a301f1 Mon Sep 17 00:00:00 2001 From: Fabien Chouteau Date: Tue, 28 May 2019 18:31:38 -0400 Subject: [PATCH] RISC-V: fix single stepping over ret and other branching instructions This patch introduces wrappers around the tcg_gen_exit_tb() and tcg_gen_lookup_and_goto_ptr() functions that handle single stepping, i.e. call gen_exception_debug() when single stepping is enabled. Theses functions are then used instead of the originals, bringing single stepping handling in places where it was previously ignored such as jalr and system branch instructions (ecall, mret, sret, etc.). Backports commit 6e2716d8ca4edf3597307accef7af36e8ad966eb from qemu --- .../riscv/insn_trans/trans_privileged.inc.c | 11 +++--- qemu/target/riscv/insn_trans/trans_rvi.inc.c | 6 ++-- qemu/target/riscv/translate.c | 34 ++++++++++++++++--- 3 files changed, 36 insertions(+), 15 deletions(-) diff --git a/qemu/target/riscv/insn_trans/trans_privileged.inc.c b/qemu/target/riscv/insn_trans/trans_privileged.inc.c index e5c00cec..4c7a9600 100644 --- a/qemu/target/riscv/insn_trans/trans_privileged.inc.c +++ b/qemu/target/riscv/insn_trans/trans_privileged.inc.c @@ -20,20 +20,17 @@ static bool trans_ecall(DisasContext *ctx, arg_ecall *a) { - TCGContext *tcg_ctx = ctx->uc->tcg_ctx; - /* always generates U-level ECALL, fixed in do_interrupt handler */ generate_exception(ctx, RISCV_EXCP_U_ECALL); - tcg_gen_exit_tb(tcg_ctx, NULL, 0); /* no chaining */ + exit_tb(ctx); /* no chaining */ ctx->base.is_jmp = DISAS_NORETURN; return true; } static bool trans_ebreak(DisasContext *ctx, arg_ebreak *a) { - TCGContext *tcg_ctx = ctx->uc->tcg_ctx; generate_exception(ctx, RISCV_EXCP_BREAKPOINT); - tcg_gen_exit_tb(tcg_ctx, NULL, 0); /* no chaining */ + exit_tb(ctx); /* no chaining */ ctx->base.is_jmp = DISAS_NORETURN; return true; } @@ -51,7 +48,7 @@ static bool trans_sret(DisasContext *ctx, arg_sret *a) if (has_ext(ctx, RVS)) { gen_helper_sret(tcg_ctx, tcg_ctx->cpu_pc_risc, tcg_ctx->cpu_env, tcg_ctx->cpu_pc_risc); - tcg_gen_exit_tb(tcg_ctx, NULL, 0); /* no chaining */ + exit_tb(ctx); /* no chaining */ ctx->base.is_jmp = DISAS_NORETURN; } else { return false; @@ -73,7 +70,7 @@ static bool trans_mret(DisasContext *ctx, arg_mret *a) TCGContext *tcg_ctx = ctx->uc->tcg_ctx; tcg_gen_movi_tl(tcg_ctx, tcg_ctx->cpu_pc_risc, ctx->base.pc_next); gen_helper_mret(tcg_ctx, tcg_ctx->cpu_pc_risc, tcg_ctx->cpu_env, tcg_ctx->cpu_pc_risc); - tcg_gen_exit_tb(tcg_ctx, NULL, 0); /* no chaining */ + exit_tb(ctx); /* no chaining */ ctx->base.is_jmp = DISAS_NORETURN; return true; #else diff --git a/qemu/target/riscv/insn_trans/trans_rvi.inc.c b/qemu/target/riscv/insn_trans/trans_rvi.inc.c index 4cb798dc..7762fc12 100644 --- a/qemu/target/riscv/insn_trans/trans_rvi.inc.c +++ b/qemu/target/riscv/insn_trans/trans_rvi.inc.c @@ -63,7 +63,7 @@ static bool trans_jalr(DisasContext *ctx, arg_jalr *a) if (a->rd != 0) { tcg_gen_movi_tl(tcg_ctx, tcg_ctx->cpu_gpr_risc[a->rd], ctx->pc_succ_insn); } - tcg_gen_lookup_and_goto_ptr(tcg_ctx); + lookup_and_goto_ptr(ctx); if (misaligned) { gen_set_label(tcg_ctx, misaligned); @@ -503,7 +503,7 @@ static bool trans_fence_i(DisasContext *ctx, arg_fence_i *a) * however we need to end the translation block */ tcg_gen_movi_tl(tcg_ctx, tcg_ctx->cpu_pc_risc, ctx->pc_succ_insn); - tcg_gen_exit_tb(tcg_ctx, NULL, 0); + exit_tb(ctx); ctx->base.is_jmp = DISAS_NORETURN; return true; } @@ -522,7 +522,7 @@ static bool trans_fence_i(DisasContext *ctx, arg_fence_i *a) #define RISCV_OP_CSR_POST do {\ gen_set_gpr(ctx, a->rd, dest); \ tcg_gen_movi_tl(tcg_ctx, tcg_ctx->cpu_pc_risc, ctx->pc_succ_insn); \ - tcg_gen_exit_tb(tcg_ctx, NULL, 0); \ + exit_tb(ctx); \ ctx->base.is_jmp = DISAS_NORETURN; \ tcg_temp_free(tcg_ctx, source1); \ tcg_temp_free(tcg_ctx, csr_store); \ diff --git a/qemu/target/riscv/translate.c b/qemu/target/riscv/translate.c index c34a0c00..c1c8891b 100644 --- a/qemu/target/riscv/translate.c +++ b/qemu/target/riscv/translate.c @@ -116,6 +116,30 @@ static void gen_exception_debug(const DisasContext *ctx) tcg_temp_free_i32(tcg_ctx, helper_tmp); } +/* Wrapper around tcg_gen_exit_tb that handles single stepping */ +static void exit_tb(DisasContext *ctx) +{ + TCGContext *tcg_ctx = ctx->uc->tcg_ctx; + + if (ctx->base.singlestep_enabled) { + gen_exception_debug(ctx); + } else { + tcg_gen_exit_tb(tcg_ctx, NULL, 0); + } +} + +/* Wrapper around tcg_gen_lookup_and_goto_ptr that handles single stepping */ +static void lookup_and_goto_ptr(DisasContext *ctx) +{ + TCGContext *tcg_ctx = ctx->uc->tcg_ctx; + + if (ctx->base.singlestep_enabled) { + gen_exception_debug(ctx); + } else { + tcg_gen_lookup_and_goto_ptr(tcg_ctx); + } +} + static void gen_exception_illegal(DisasContext *ctx) { generate_exception(ctx, RISCV_EXCP_ILLEGAL_INST); @@ -147,14 +171,14 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) /* chaining is only allowed when the jump is to the same page */ tcg_gen_goto_tb(tcg_ctx, n); tcg_gen_movi_tl(tcg_ctx, tcg_ctx->cpu_pc_risc, dest); + + /* No need to check for single stepping here as use_goto_tb() will + * return false in case of single stepping. + */ tcg_gen_exit_tb(tcg_ctx, ctx->base.tb, n); } else { tcg_gen_movi_tl(tcg_ctx, tcg_ctx->cpu_pc_risc, dest); - if (ctx->base.singlestep_enabled) { - gen_exception_debug(ctx); - } else { - tcg_gen_lookup_and_goto_ptr(tcg_ctx); - } + lookup_and_goto_ptr(ctx); } }