RISC-V Debug Specification:
https://github.com/riscv/riscv-debug-spec/releases/tag/1.0
Add DRET decode/translate and a helper to leave Debug Mode and return
to dpc. Executing DRET outside Debug Mode raises illegal instruction.
Signed-off-by: Chao Liu <chao.liu.zevorn@gmail.com>
---
target/riscv/helper.h | 1 +
target/riscv/insn32.decode | 1 +
target/riscv/insn_trans/trans_privileged.c.inc | 18 ++++++++++++++++++
target/riscv/op_helper.c | 16 ++++++++++++++++
4 files changed, 36 insertions(+)
diff --git a/target/riscv/helper.h b/target/riscv/helper.h
index b785456ee0..6140b6340d 100644
--- a/target/riscv/helper.h
+++ b/target/riscv/helper.h
@@ -131,6 +131,7 @@ DEF_HELPER_6(csrrw_i128, tl, env, int, tl, tl, tl, tl)
#ifndef CONFIG_USER_ONLY
DEF_HELPER_1(sret, tl, env)
DEF_HELPER_1(mret, tl, env)
+DEF_HELPER_1(dret, tl, env)
DEF_HELPER_1(mnret, tl, env)
DEF_HELPER_1(ctr_clear, void, env)
DEF_HELPER_1(wfi, void, env)
diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode
index 6e35c4b1e6..4db842d5d9 100644
--- a/target/riscv/insn32.decode
+++ b/target/riscv/insn32.decode
@@ -118,6 +118,7 @@ sctrclr 000100000100 00000 000 00000 1110011
uret 0000000 00010 00000 000 00000 1110011
sret 0001000 00010 00000 000 00000 1110011
mret 0011000 00010 00000 000 00000 1110011
+dret 0111101 10010 00000 000 00000 1110011
wfi 0001000 00101 00000 000 00000 1110011
sfence_vma 0001001 ..... ..... 000 00000 1110011 @sfence_vma
diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc
index 8a62b4cfcd..f8641b1977 100644
--- a/target/riscv/insn_trans/trans_privileged.c.inc
+++ b/target/riscv/insn_trans/trans_privileged.c.inc
@@ -125,6 +125,24 @@ static bool trans_mret(DisasContext *ctx, arg_mret *a)
#endif
}
+static bool trans_dret(DisasContext *ctx, arg_dret *a)
+{
+#ifndef CONFIG_USER_ONLY
+ if (!ctx->cfg_ptr->ext_sdext) {
+ return false;
+ }
+ decode_save_opc(ctx, 0);
+ translator_io_start(&ctx->base);
+ gen_update_pc(ctx, 0);
+ gen_helper_dret(cpu_pc, tcg_env);
+ exit_tb(ctx); /* no chaining */
+ ctx->base.is_jmp = DISAS_NORETURN;
+ return true;
+#else
+ return false;
+#endif
+}
+
static bool trans_mnret(DisasContext *ctx, arg_mnret *a)
{
#ifndef CONFIG_USER_ONLY
diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
index 6ccc127c30..99736bbebb 100644
--- a/target/riscv/op_helper.c
+++ b/target/riscv/op_helper.c
@@ -454,6 +454,22 @@ target_ulong helper_mret(CPURISCVState *env)
return retpc;
}
+target_ulong helper_dret(CPURISCVState *env)
+{
+ uintptr_t ra = GETPC();
+#ifdef CONFIG_USER_ONLY
+ riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, ra);
+ return 0;
+#else
+ if (!riscv_cpu_cfg(env)->ext_sdext || !env->debug_mode) {
+ riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, ra);
+ }
+ target_ulong retpc = env->dpc & get_xepc_mask(env);
+ riscv_cpu_leave_debug_mode(env);
+ return retpc;
+#endif
+}
+
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
>
> Add DRET decode/translate and a helper to leave Debug Mode and return
> to dpc. Executing DRET outside Debug Mode raises illegal instruction.
>
> 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 +
> target/riscv/insn32.decode | 1 +
> target/riscv/insn_trans/trans_privileged.c.inc | 18 ++++++++++++++++++
> target/riscv/op_helper.c | 16 ++++++++++++++++
> 4 files changed, 36 insertions(+)
>
> diff --git a/target/riscv/helper.h b/target/riscv/helper.h
> index b785456ee0..6140b6340d 100644
> --- a/target/riscv/helper.h
> +++ b/target/riscv/helper.h
> @@ -131,6 +131,7 @@ DEF_HELPER_6(csrrw_i128, tl, env, int, tl, tl, tl, tl)
> #ifndef CONFIG_USER_ONLY
> DEF_HELPER_1(sret, tl, env)
> DEF_HELPER_1(mret, tl, env)
> +DEF_HELPER_1(dret, tl, env)
> DEF_HELPER_1(mnret, tl, env)
> DEF_HELPER_1(ctr_clear, void, env)
> DEF_HELPER_1(wfi, void, env)
> diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode
> index 6e35c4b1e6..4db842d5d9 100644
> --- a/target/riscv/insn32.decode
> +++ b/target/riscv/insn32.decode
> @@ -118,6 +118,7 @@ sctrclr 000100000100 00000 000 00000 1110011
> uret 0000000 00010 00000 000 00000 1110011
> sret 0001000 00010 00000 000 00000 1110011
> mret 0011000 00010 00000 000 00000 1110011
> +dret 0111101 10010 00000 000 00000 1110011
> wfi 0001000 00101 00000 000 00000 1110011
> sfence_vma 0001001 ..... ..... 000 00000 1110011 @sfence_vma
>
> diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc
> index 8a62b4cfcd..f8641b1977 100644
> --- a/target/riscv/insn_trans/trans_privileged.c.inc
> +++ b/target/riscv/insn_trans/trans_privileged.c.inc
> @@ -125,6 +125,24 @@ static bool trans_mret(DisasContext *ctx, arg_mret *a)
> #endif
> }
>
> +static bool trans_dret(DisasContext *ctx, arg_dret *a)
> +{
> +#ifndef CONFIG_USER_ONLY
> + if (!ctx->cfg_ptr->ext_sdext) {
> + return false;
> + }
> + decode_save_opc(ctx, 0);
> + translator_io_start(&ctx->base);
> + gen_update_pc(ctx, 0);
> + gen_helper_dret(cpu_pc, tcg_env);
> + exit_tb(ctx); /* no chaining */
> + ctx->base.is_jmp = DISAS_NORETURN;
> + return true;
> +#else
> + return false;
> +#endif
> +}
> +
> static bool trans_mnret(DisasContext *ctx, arg_mnret *a)
> {
> #ifndef CONFIG_USER_ONLY
> diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
> index 6ccc127c30..99736bbebb 100644
> --- a/target/riscv/op_helper.c
> +++ b/target/riscv/op_helper.c
> @@ -454,6 +454,22 @@ target_ulong helper_mret(CPURISCVState *env)
> return retpc;
> }
>
> +target_ulong helper_dret(CPURISCVState *env)
> +{
> + uintptr_t ra = GETPC();
> +#ifdef CONFIG_USER_ONLY
> + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, ra);
> + return 0;
> +#else
> + if (!riscv_cpu_cfg(env)->ext_sdext || !env->debug_mode) {
> + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, ra);
> + }
> + target_ulong retpc = env->dpc & get_xepc_mask(env);
> + riscv_cpu_leave_debug_mode(env);
> + return retpc;
> +#endif
> +}
> +
> target_ulong helper_mnret(CPURISCVState *env)
> {
> target_ulong retpc = env->mnepc;
© 2016 - 2026 Red Hat, Inc.