RISC-V Debug Specification:
https://github.com/riscv/riscv-debug-spec/releases/tag/1.0
Route EBREAK via helper_sdext_ebreak. If Sdext is enabled and the
matching dcsr.ebreak* bit is set, enter Debug Mode with cause=ebreak
and stop with EXCP_DEBUG. Otherwise keep the normal breakpoint trap.
Signed-off-by: Chao Liu <chao.liu.zevorn@gmail.com>
---
target/riscv/helper.h | 1 +
.../riscv/insn_trans/trans_privileged.c.inc | 6 ++++
target/riscv/op_helper.c | 34 +++++++++++++++++++
3 files changed, 41 insertions(+)
diff --git a/target/riscv/helper.h b/target/riscv/helper.h
index 6140b6340d..acff73051b 100644
--- a/target/riscv/helper.h
+++ b/target/riscv/helper.h
@@ -141,6 +141,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_2(sdext_ebreak, void, env, tl)
#endif
/* Hypervisor functions */
diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc
index f8641b1977..84f0c77513 100644
--- a/target/riscv/insn_trans/trans_privileged.c.inc
+++ b/target/riscv/insn_trans/trans_privileged.c.inc
@@ -68,9 +68,15 @@ static bool trans_ebreak(DisasContext *ctx, arg_ebreak *a)
if (pre == 0x01f01013 && ebreak == 0x00100073 && post == 0x40705013) {
generate_exception(ctx, RISCV_EXCP_SEMIHOST);
} else {
+#ifndef CONFIG_USER_ONLY
+ gen_update_pc(ctx, 0);
+ gen_helper_sdext_ebreak(tcg_env, tcg_constant_tl(ebreak_addr));
+ ctx->base.is_jmp = DISAS_NORETURN;
+#else
tcg_gen_st_tl(tcg_constant_tl(ebreak_addr), tcg_env,
offsetof(CPURISCVState, badaddr));
generate_exception(ctx, RISCV_EXCP_BREAKPOINT);
+#endif
}
return true;
}
diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
index 99736bbebb..b6417b4b0b 100644
--- a/target/riscv/op_helper.c
+++ b/target/riscv/op_helper.c
@@ -470,6 +470,40 @@ target_ulong helper_dret(CPURISCVState *env)
#endif
}
+void helper_sdext_ebreak(CPURISCVState *env, target_ulong pc)
+{
+ CPUState *cs = env_cpu(env);
+ bool enter_debug = false;
+
+ if (riscv_cpu_cfg(env)->ext_sdext && !env->debug_mode) {
+ if (env->virt_enabled) {
+ if (env->priv == PRV_S) {
+ enter_debug = env->dcsr & DCSR_EBREAKVS;
+ } else if (env->priv == PRV_U) {
+ enter_debug = env->dcsr & DCSR_EBREAKVU;
+ }
+ } else {
+ if (env->priv == PRV_M) {
+ enter_debug = env->dcsr & DCSR_EBREAKM;
+ } else if (env->priv == PRV_S) {
+ enter_debug = env->dcsr & DCSR_EBREAKS;
+ } else if (env->priv == PRV_U) {
+ enter_debug = env->dcsr & DCSR_EBREAKU;
+ }
+ }
+ }
+
+ env->badaddr = pc;
+
+ if (enter_debug) {
+ riscv_cpu_enter_debug_mode(env, pc, DCSR_CAUSE_EBREAK);
+ cs->exception_index = EXCP_DEBUG;
+ cpu_loop_exit_restore(cs, GETPC());
+ }
+
+ riscv_raise_exception(env, RISCV_EXCP_BREAKPOINT, GETPC());
+}
+
target_ulong helper_mnret(CPURISCVState *env)
{
target_ulong retpc = env->mnepc;
--
2.52.0
On 1/30/2026 3:00 AM, Chao Liu wrote:
> RISC-V Debug Specification:
> https://github.com/riscv/riscv-debug-spec/releases/tag/1.0
>
> Route EBREAK via helper_sdext_ebreak. If Sdext is enabled and the
> matching dcsr.ebreak* bit is set, enter Debug Mode with cause=ebreak
> and stop with EXCP_DEBUG. Otherwise keep the normal breakpoint trap.
>
> Signed-off-by: Chao Liu <chao.liu.zevorn@gmail.com>
> ---
Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>
> target/riscv/helper.h | 1 +
> .../riscv/insn_trans/trans_privileged.c.inc | 6 ++++
> target/riscv/op_helper.c | 34 +++++++++++++++++++
> 3 files changed, 41 insertions(+)
>
> diff --git a/target/riscv/helper.h b/target/riscv/helper.h
> index 6140b6340d..acff73051b 100644
> --- a/target/riscv/helper.h
> +++ b/target/riscv/helper.h
> @@ -141,6 +141,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_2(sdext_ebreak, void, env, tl)
> #endif
>
> /* Hypervisor functions */
> diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc
> index f8641b1977..84f0c77513 100644
> --- a/target/riscv/insn_trans/trans_privileged.c.inc
> +++ b/target/riscv/insn_trans/trans_privileged.c.inc
> @@ -68,9 +68,15 @@ static bool trans_ebreak(DisasContext *ctx, arg_ebreak *a)
> if (pre == 0x01f01013 && ebreak == 0x00100073 && post == 0x40705013) {
> generate_exception(ctx, RISCV_EXCP_SEMIHOST);
> } else {
> +#ifndef CONFIG_USER_ONLY
> + gen_update_pc(ctx, 0);
> + gen_helper_sdext_ebreak(tcg_env, tcg_constant_tl(ebreak_addr));
> + ctx->base.is_jmp = DISAS_NORETURN;
> +#else
> tcg_gen_st_tl(tcg_constant_tl(ebreak_addr), tcg_env,
> offsetof(CPURISCVState, badaddr));
> generate_exception(ctx, RISCV_EXCP_BREAKPOINT);
> +#endif
> }
> return true;
> }
> diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
> index 99736bbebb..b6417b4b0b 100644
> --- a/target/riscv/op_helper.c
> +++ b/target/riscv/op_helper.c
> @@ -470,6 +470,40 @@ target_ulong helper_dret(CPURISCVState *env)
> #endif
> }
>
> +void helper_sdext_ebreak(CPURISCVState *env, target_ulong pc)
> +{
> + CPUState *cs = env_cpu(env);
> + bool enter_debug = false;
> +
> + if (riscv_cpu_cfg(env)->ext_sdext && !env->debug_mode) {
> + if (env->virt_enabled) {
> + if (env->priv == PRV_S) {
> + enter_debug = env->dcsr & DCSR_EBREAKVS;
> + } else if (env->priv == PRV_U) {
> + enter_debug = env->dcsr & DCSR_EBREAKVU;
> + }
> + } else {
> + if (env->priv == PRV_M) {
> + enter_debug = env->dcsr & DCSR_EBREAKM;
> + } else if (env->priv == PRV_S) {
> + enter_debug = env->dcsr & DCSR_EBREAKS;
> + } else if (env->priv == PRV_U) {
> + enter_debug = env->dcsr & DCSR_EBREAKU;
> + }
> + }
> + }
> +
> + env->badaddr = pc;
> +
> + if (enter_debug) {
> + riscv_cpu_enter_debug_mode(env, pc, DCSR_CAUSE_EBREAK);
> + cs->exception_index = EXCP_DEBUG;
> + cpu_loop_exit_restore(cs, GETPC());
> + }
> +
> + riscv_raise_exception(env, RISCV_EXCP_BREAKPOINT, GETPC());
> +}
> +
> target_ulong helper_mnret(CPURISCVState *env)
> {
> target_ulong retpc = env->mnepc;
© 2016 - 2026 Red Hat, Inc.