On Wed, 2026-09-09 at 11:48 -0300, Daniel Henrique Barboza wrote: > From: Chao Liu <[email protected]> > > RISC-V Debug Specification: > https://github.com/riscv/riscv-debug-spec/releases/tag/1.0 > > Use a TB flag when dcsr.step is set (and we are not in Debug Mode). > When the flag is on, build 1-insn TBs and do not chain to the next > TB. > Add a TB-exit helper that enters Debug Mode with cause=step and sets > dpc to the next pc, then stops with EXCP_DEBUG. > > If dcsr.stepie is 0, do not take interrupts while stepping. Treat WFI > as a nop so the hart does not sleep during a step. > > PS: This patch references Max Chou's handling of ext_tb_flags. > https://lore.kernel.org/qemu-devel/[email protected]/ > > Signed-off-by: Chao Liu <[email protected]> > Reviewed-by: Daniel Henrique Barboza > <[email protected]> > Tested-by: Tao Tang <[email protected]> > Signed-off-by: Daniel Henrique Barboza > <[email protected]>
Reviewed-by: Alistair Francis <[email protected]> Alistair > --- > include/exec/translation-block.h | 4 ++-- > target/riscv/cpu.h | 2 ++ > target/riscv/helper.h | 1 + > target/riscv/tcg/cpu_helper.c | 6 ++++++ > target/riscv/tcg/op_helper.c | 20 ++++++++++++++++++++ > target/riscv/tcg/tcg-cpu.c | 5 +++++ > target/riscv/tcg/translate.c | 15 +++++++++++++-- > 7 files changed, 49 insertions(+), 4 deletions(-) > > diff --git a/include/exec/translation-block.h > b/include/exec/translation-block.h > index 40cc699031..ee15608c89 100644 > --- a/include/exec/translation-block.h > +++ b/include/exec/translation-block.h > @@ -64,8 +64,8 @@ struct TranslationBlock { > * x86: the original user, the Code Segment virtual base, > * arm: an extension of tb->flags, > * s390x: instruction data for EXECUTE, > - * sparc: the next pc of the instruction queue (for delay > slots). > - * riscv: an extension of tb->flags, > + * sparc: the next pc of the instruction queue (for delay > slots), > + * riscv: an extension of tb->flags. > */ > uint64_t cs_base; > > diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h > index 82eb61a18c..db49ad7682 100644 > --- a/target/riscv/cpu.h > +++ b/target/riscv/cpu.h > @@ -755,6 +755,8 @@ FIELD(TB_FLAGS, PM_SIGNEXTEND, 31, 1) > FIELD(EXT_TB_FLAGS, MISA_EXT, 0, 32) > FIELD(EXT_TB_FLAGS, ALTFMT, 32, 1) > FIELD(EXT_TB_FLAGS, BIG_ENDIAN, 33, 1) > +/* sdext single-step needs a TB flag to build 1-insn TBs */ > +FIELD(EXT_TB_FLAGS, SDEXT_STEP, 34, 1) > > #ifdef TARGET_RISCV32 > #define riscv_cpu_mxl(env) ((void)(env), MXL_RV32) > diff --git a/target/riscv/helper.h b/target/riscv/helper.h > index 0d9ae15e3e..5193440e1c 100644 > --- a/target/riscv/helper.h > +++ b/target/riscv/helper.h > @@ -142,6 +142,7 @@ DEF_HELPER_1(tlb_flush_all, void, env) > DEF_HELPER_4(ctr_add_entry, void, env, tl, tl, tl) > /* Native Debug */ > DEF_HELPER_1(itrigger_match, void, env) > +DEF_HELPER_1(sdext_step, void, env) > DEF_HELPER_2(sdext_ebreak, void, env, tl) > #endif > > diff --git a/target/riscv/tcg/cpu_helper.c > b/target/riscv/tcg/cpu_helper.c > index e6d948991c..a7344b1be2 100644 > --- a/target/riscv/tcg/cpu_helper.c > +++ b/target/riscv/tcg/cpu_helper.c > @@ -500,6 +500,12 @@ bool riscv_cpu_exec_interrupt(CPUState *cs, int > interrupt_request) > if (interrupt_request & mask) { > RISCVCPU *cpu = RISCV_CPU(cs); > CPURISCVState *env = &cpu->env; > + > + if (cpu->cfg.ext_sdext && !env->debug_mode && > + (env->dcsr & DCSR_STEP) && !(env->dcsr & DCSR_STEPIE)) { > + return false; > + } > + > int interruptno = riscv_cpu_local_irq_pending(env); > if (interruptno >= 0) { > cs->exception_index = RISCV_EXCP_INT_FLAG | interruptno; > diff --git a/target/riscv/tcg/op_helper.c > b/target/riscv/tcg/op_helper.c > index 5db4f6aacd..489aac28e7 100644 > --- a/target/riscv/tcg/op_helper.c > +++ b/target/riscv/tcg/op_helper.c > @@ -489,6 +489,22 @@ target_ulong helper_dret(CPURISCVState *env) > #endif > } > > +void helper_sdext_step(CPURISCVState *env) > +{ > +#ifndef CONFIG_USER_ONLY > + CPUState *cs = env_cpu(env); > + > + if (!riscv_cpu_cfg(env)->ext_sdext || env->debug_mode || > + !(env->dcsr & DCSR_STEP)) { > + return; > + } > + > + riscv_cpu_enter_debug_mode(env, env->pc, DCSR_CAUSE_STEP); > + cs->exception_index = EXCP_DEBUG; > + cpu_loop_exit_restore(cs, GETPC()); > +#endif > +} > + > void helper_sdext_ebreak(CPURISCVState *env, target_ulong pc) > { > CPUState *cs = env_cpu(env); > @@ -621,6 +637,10 @@ void helper_wfi(CPURISCVState *env) > (prv_u || (prv_s && get_field(env->hstatus, > HSTATUS_VTW)))) { > riscv_raise_exception(env, > RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); > } else { > + if (riscv_cpu_cfg(env)->ext_sdext && !env->debug_mode && > + (env->dcsr & DCSR_STEP)) { > + return; > + } > cs->halted = 1; > cs->exception_index = EXCP_HLT; > cpu_loop_exit(cs); > diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c > index 959a1e89c8..16d26cee5c 100644 > --- a/target/riscv/tcg/tcg-cpu.c > +++ b/target/riscv/tcg/tcg-cpu.c > @@ -196,6 +196,11 @@ static TCGTBCPUState > riscv_get_tb_cpu_state(CPUState *cs) > ext_flags = FIELD_DP64(ext_flags, EXT_TB_FLAGS, MISA_EXT, env- > >misa_ext); > ext_flags = FIELD_DP64(ext_flags, EXT_TB_FLAGS, BIG_ENDIAN, > mo_endian_env(env) == MO_BE); > +#ifndef CONFIG_USER_ONLY > + if (cpu->cfg.ext_sdext && !env->debug_mode && (env->dcsr & > DCSR_STEP)) { > + ext_flags = FIELD_DP64(ext_flags, EXT_TB_FLAGS, SDEXT_STEP, > 1); > + } > +#endif > > return (TCGTBCPUState){ > .pc = env->xl == MXL_RV32 ? env->pc & UINT32_MAX : env->pc, > diff --git a/target/riscv/tcg/translate.c > b/target/riscv/tcg/translate.c > index 9684dbe752..1a38d6aeb5 100644 > --- a/target/riscv/tcg/translate.c > +++ b/target/riscv/tcg/translate.c > @@ -112,6 +112,8 @@ typedef struct DisasContext { > bool ztso; > /* Use icount trigger for native debug */ > bool itrigger; > + /* Enter Debug Mode after next instruction (sdext single-step). > */ > + bool sdext_step; > /* FRM is known to contain a valid value. */ > bool frm_valid; > bool insn_start_updated; > @@ -286,6 +288,9 @@ static void lookup_and_goto_ptr(DisasContext > *ctx) > if (ctx->itrigger) { > gen_helper_itrigger_match(tcg_env); > } > + if (ctx->sdext_step) { > + gen_helper_sdext_step(tcg_env); > + } > #endif > tcg_gen_lookup_and_goto_ptr(); > } > @@ -296,6 +301,9 @@ static void exit_tb(DisasContext *ctx) > if (ctx->itrigger) { > gen_helper_itrigger_match(tcg_env); > } > + if (ctx->sdext_step) { > + gen_helper_sdext_step(tcg_env); > + } > #endif > tcg_gen_exit_tb(NULL, 0); > } > @@ -309,7 +317,8 @@ static void gen_goto_tb(DisasContext *ctx, > unsigned tb_slot_idx, > * Under itrigger, instruction executes one by one like > singlestep, > * direct block chain benefits will be small. > */ > - if (translator_use_goto_tb(&ctx->base, dest) && !ctx->itrigger) > { > + if (translator_use_goto_tb(&ctx->base, dest) && > + !ctx->itrigger && !ctx->sdext_step) { > /* > * For pcrel, the pc must always be up-to-date on entry to > * the linked TB, so that it can use simple additions for > all > @@ -1350,6 +1359,7 @@ static void > riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) > ctx->bcfi_enabled = FIELD_EX32(tb_flags, TB_FLAGS, > BCFI_ENABLED); > ctx->fcfi_lp_expected = FIELD_EX32(tb_flags, TB_FLAGS, > FCFI_LP_EXPECTED); > ctx->fcfi_enabled = FIELD_EX32(tb_flags, TB_FLAGS, > FCFI_ENABLED); > + ctx->sdext_step = FIELD_EX64(ext_tb_flags, EXT_TB_FLAGS, > SDEXT_STEP); > ctx->zero = tcg_constant_tl(0); > ctx->virt_inst_excp = false; > ctx->decoders = cpu->decoders; > @@ -1402,7 +1412,8 @@ static void > riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) > > /* Only the first insn within a TB is allowed to cross a page > boundary. */ > if (ctx->base.is_jmp == DISAS_NEXT) { > - if (ctx->itrigger || !translator_is_same_page(&ctx->base, > ctx->base.pc_next)) { > + if (ctx->itrigger || ctx->sdext_step || > + !translator_is_same_page(&ctx->base, ctx->base.pc_next)) > { > ctx->base.is_jmp = DISAS_TOO_MANY; > } else { > unsigned page_ofs = ctx->base.pc_next & > ~TARGET_PAGE_MASK;
