diff mbox series

[v7,01/11] target/riscv: Combine set_mode and set_virt functions.

Message ID 20240626-smcntrpmf_v7-v7-1-bb0f10af7fa9@rivosinc.com
State New
Headers show
Series Add RISC-V ISA extension smcntrpmf support | expand

Commit Message

Atish Kumar Patra June 26, 2024, 11:57 p.m. UTC
From: Rajnesh Kanwal <rkanwal@rivosinc.com>

Combining riscv_cpu_set_virt_enabled() and riscv_cpu_set_mode()
functions. This is to make complete mode change information
available through a single function.

This allows to easily differentiate between HS->VS, VS->HS
and VS->VS transitions when executing state update codes.
For example: One use-case which inspired this change is
to update mode-specific instruction and cycle counters
which requires information of both prev mode and current
mode.

Signed-off-by: Rajnesh Kanwal <rkanwal@rivosinc.com>
---
 target/riscv/cpu.h        |  2 +-
 target/riscv/cpu_helper.c | 57 +++++++++++++++++++++++------------------------
 target/riscv/op_helper.c  | 17 +++++---------
 3 files changed, 35 insertions(+), 41 deletions(-)

Comments

Daniel Henrique Barboza July 1, 2024, 6:21 p.m. UTC | #1
On 6/26/24 8:57 PM, Atish Patra wrote:
> From: Rajnesh Kanwal <rkanwal@rivosinc.com>
> 
> Combining riscv_cpu_set_virt_enabled() and riscv_cpu_set_mode()
> functions. This is to make complete mode change information
> available through a single function.
> 
> This allows to easily differentiate between HS->VS, VS->HS
> and VS->VS transitions when executing state update codes.
> For example: One use-case which inspired this change is
> to update mode-specific instruction and cycle counters
> which requires information of both prev mode and current
> mode.
> 
> Signed-off-by: Rajnesh Kanwal <rkanwal@rivosinc.com>
> ---

Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>

>   target/riscv/cpu.h        |  2 +-
>   target/riscv/cpu_helper.c | 57 +++++++++++++++++++++++------------------------
>   target/riscv/op_helper.c  | 17 +++++---------
>   3 files changed, 35 insertions(+), 41 deletions(-)
> 
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index 90b8f1b08f83..46faefd24e09 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -544,7 +544,7 @@ void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, uint32_t priv,
>   RISCVException smstateen_acc_ok(CPURISCVState *env, int index, uint64_t bit);
>   #endif /* !CONFIG_USER_ONLY */
>   
> -void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv);
> +void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv, bool virt_en);
>   
>   void riscv_translate_init(void);
>   G_NORETURN void riscv_raise_exception(CPURISCVState *env,
> diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> index 6709622dd3ab..10d3fdaed376 100644
> --- a/target/riscv/cpu_helper.c
> +++ b/target/riscv/cpu_helper.c
> @@ -619,30 +619,6 @@ void riscv_cpu_set_geilen(CPURISCVState *env, target_ulong geilen)
>       env->geilen = geilen;
>   }
>   
> -/* This function can only be called to set virt when RVH is enabled */
> -void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable)
> -{
> -    /* Flush the TLB on all virt mode changes. */
> -    if (env->virt_enabled != enable) {
> -        tlb_flush(env_cpu(env));
> -    }
> -
> -    env->virt_enabled = enable;
> -
> -    if (enable) {
> -        /*
> -         * The guest external interrupts from an interrupt controller are
> -         * delivered only when the Guest/VM is running (i.e. V=1). This means
> -         * any guest external interrupt which is triggered while the Guest/VM
> -         * is not running (i.e. V=0) will be missed on QEMU resulting in guest
> -         * with sluggish response to serial console input and other I/O events.
> -         *
> -         * To solve this, we check and inject interrupt after setting V=1.
> -         */
> -        riscv_cpu_update_mip(env, 0, 0);
> -    }
> -}
> -
>   int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts)
>   {
>       CPURISCVState *env = &cpu->env;
> @@ -715,7 +691,7 @@ void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, uint32_t priv,
>       }
>   }
>   
> -void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv)
> +void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv, bool virt_en)
>   {
>       g_assert(newpriv <= PRV_M && newpriv != PRV_RESERVED);
>   
> @@ -736,6 +712,28 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv)
>        * preemptive context switch. As a result, do both.
>        */
>       env->load_res = -1;
> +
> +    if (riscv_has_ext(env, RVH)) {
> +        /* Flush the TLB on all virt mode changes. */
> +        if (env->virt_enabled != virt_en) {
> +            tlb_flush(env_cpu(env));
> +        }
> +
> +        env->virt_enabled = virt_en;
> +        if (virt_en) {
> +            /*
> +             * The guest external interrupts from an interrupt controller are
> +             * delivered only when the Guest/VM is running (i.e. V=1). This
> +             * means any guest external interrupt which is triggered while the
> +             * Guest/VM is not running (i.e. V=0) will be missed on QEMU
> +             * resulting in guest with sluggish response to serial console
> +             * input and other I/O events.
> +             *
> +             * To solve this, we check and inject interrupt after setting V=1.
> +             */
> +            riscv_cpu_update_mip(env, 0, 0);
> +        }
> +    }
>   }
>   
>   /*
> @@ -1648,6 +1646,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
>   {
>       RISCVCPU *cpu = RISCV_CPU(cs);
>       CPURISCVState *env = &cpu->env;
> +    bool virt = env->virt_enabled;
>       bool write_gva = false;
>       uint64_t s;
>   
> @@ -1778,7 +1777,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
>   
>                   htval = env->guest_phys_fault_addr;
>   
> -                riscv_cpu_set_virt_enabled(env, 0);
> +                virt = false;
>               } else {
>                   /* Trap into HS mode */
>                   env->hstatus = set_field(env->hstatus, HSTATUS_SPV, false);
> @@ -1799,7 +1798,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
>           env->htinst = tinst;
>           env->pc = (env->stvec >> 2 << 2) +
>                     ((async && (env->stvec & 3) == 1) ? cause * 4 : 0);
> -        riscv_cpu_set_mode(env, PRV_S);
> +        riscv_cpu_set_mode(env, PRV_S, virt);
>       } else {
>           /* handle the trap in M-mode */
>           if (riscv_has_ext(env, RVH)) {
> @@ -1815,7 +1814,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
>               mtval2 = env->guest_phys_fault_addr;
>   
>               /* Trapping to M mode, virt is disabled */
> -            riscv_cpu_set_virt_enabled(env, 0);
> +            virt = false;
>           }
>   
>           s = env->mstatus;
> @@ -1830,7 +1829,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
>           env->mtinst = tinst;
>           env->pc = (env->mtvec >> 2 << 2) +
>                     ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0);
> -        riscv_cpu_set_mode(env, PRV_M);
> +        riscv_cpu_set_mode(env, PRV_M, virt);
>       }
>   
>       /*
> diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
> index 2baf5bc3ca19..ec1408ba0fb1 100644
> --- a/target/riscv/op_helper.c
> +++ b/target/riscv/op_helper.c
> @@ -264,7 +264,7 @@ void helper_cbo_inval(CPURISCVState *env, target_ulong address)
>   target_ulong helper_sret(CPURISCVState *env)
>   {
>       uint64_t mstatus;
> -    target_ulong prev_priv, prev_virt;
> +    target_ulong prev_priv, prev_virt = env->virt_enabled;
>   
>       if (!(env->priv >= PRV_S)) {
>           riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
> @@ -307,11 +307,9 @@ target_ulong helper_sret(CPURISCVState *env)
>           if (prev_virt) {
>               riscv_cpu_swap_hypervisor_regs(env);
>           }
> -
> -        riscv_cpu_set_virt_enabled(env, prev_virt);
>       }
>   
> -    riscv_cpu_set_mode(env, prev_priv);
> +    riscv_cpu_set_mode(env, prev_priv, prev_virt);
>   
>       return retpc;
>   }
> @@ -347,16 +345,13 @@ target_ulong helper_mret(CPURISCVState *env)
>           mstatus = set_field(mstatus, MSTATUS_MPRV, 0);
>       }
>       env->mstatus = mstatus;
> -    riscv_cpu_set_mode(env, prev_priv);
> -
> -    if (riscv_has_ext(env, RVH)) {
> -        if (prev_virt) {
> -            riscv_cpu_swap_hypervisor_regs(env);
> -        }
>   
> -        riscv_cpu_set_virt_enabled(env, prev_virt);
> +    if (riscv_has_ext(env, RVH) && prev_virt) {
> +        riscv_cpu_swap_hypervisor_regs(env);
>       }
>   
> +    riscv_cpu_set_mode(env, prev_priv, prev_virt);
> +
>       return retpc;
>   }
>   
>
Alistair Francis July 3, 2024, 1:07 a.m. UTC | #2
On Thu, Jun 27, 2024 at 10:02 AM Atish Patra <atishp@rivosinc.com> wrote:
>
> From: Rajnesh Kanwal <rkanwal@rivosinc.com>
>
> Combining riscv_cpu_set_virt_enabled() and riscv_cpu_set_mode()
> functions. This is to make complete mode change information
> available through a single function.
>
> This allows to easily differentiate between HS->VS, VS->HS
> and VS->VS transitions when executing state update codes.
> For example: One use-case which inspired this change is
> to update mode-specific instruction and cycle counters
> which requires information of both prev mode and current
> mode.
>
> Signed-off-by: Rajnesh Kanwal <rkanwal@rivosinc.com>

Reviewed-by: Alistair Francis <alistair.francis@wdc.com>

Alistair

> ---
>  target/riscv/cpu.h        |  2 +-
>  target/riscv/cpu_helper.c | 57 +++++++++++++++++++++++------------------------
>  target/riscv/op_helper.c  | 17 +++++---------
>  3 files changed, 35 insertions(+), 41 deletions(-)
>
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index 90b8f1b08f83..46faefd24e09 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -544,7 +544,7 @@ void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, uint32_t priv,
>  RISCVException smstateen_acc_ok(CPURISCVState *env, int index, uint64_t bit);
>  #endif /* !CONFIG_USER_ONLY */
>
> -void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv);
> +void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv, bool virt_en);
>
>  void riscv_translate_init(void);
>  G_NORETURN void riscv_raise_exception(CPURISCVState *env,
> diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> index 6709622dd3ab..10d3fdaed376 100644
> --- a/target/riscv/cpu_helper.c
> +++ b/target/riscv/cpu_helper.c
> @@ -619,30 +619,6 @@ void riscv_cpu_set_geilen(CPURISCVState *env, target_ulong geilen)
>      env->geilen = geilen;
>  }
>
> -/* This function can only be called to set virt when RVH is enabled */
> -void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable)
> -{
> -    /* Flush the TLB on all virt mode changes. */
> -    if (env->virt_enabled != enable) {
> -        tlb_flush(env_cpu(env));
> -    }
> -
> -    env->virt_enabled = enable;
> -
> -    if (enable) {
> -        /*
> -         * The guest external interrupts from an interrupt controller are
> -         * delivered only when the Guest/VM is running (i.e. V=1). This means
> -         * any guest external interrupt which is triggered while the Guest/VM
> -         * is not running (i.e. V=0) will be missed on QEMU resulting in guest
> -         * with sluggish response to serial console input and other I/O events.
> -         *
> -         * To solve this, we check and inject interrupt after setting V=1.
> -         */
> -        riscv_cpu_update_mip(env, 0, 0);
> -    }
> -}
> -
>  int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts)
>  {
>      CPURISCVState *env = &cpu->env;
> @@ -715,7 +691,7 @@ void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, uint32_t priv,
>      }
>  }
>
> -void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv)
> +void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv, bool virt_en)
>  {
>      g_assert(newpriv <= PRV_M && newpriv != PRV_RESERVED);
>
> @@ -736,6 +712,28 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv)
>       * preemptive context switch. As a result, do both.
>       */
>      env->load_res = -1;
> +
> +    if (riscv_has_ext(env, RVH)) {
> +        /* Flush the TLB on all virt mode changes. */
> +        if (env->virt_enabled != virt_en) {
> +            tlb_flush(env_cpu(env));
> +        }
> +
> +        env->virt_enabled = virt_en;
> +        if (virt_en) {
> +            /*
> +             * The guest external interrupts from an interrupt controller are
> +             * delivered only when the Guest/VM is running (i.e. V=1). This
> +             * means any guest external interrupt which is triggered while the
> +             * Guest/VM is not running (i.e. V=0) will be missed on QEMU
> +             * resulting in guest with sluggish response to serial console
> +             * input and other I/O events.
> +             *
> +             * To solve this, we check and inject interrupt after setting V=1.
> +             */
> +            riscv_cpu_update_mip(env, 0, 0);
> +        }
> +    }
>  }
>
>  /*
> @@ -1648,6 +1646,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
>  {
>      RISCVCPU *cpu = RISCV_CPU(cs);
>      CPURISCVState *env = &cpu->env;
> +    bool virt = env->virt_enabled;
>      bool write_gva = false;
>      uint64_t s;
>
> @@ -1778,7 +1777,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
>
>                  htval = env->guest_phys_fault_addr;
>
> -                riscv_cpu_set_virt_enabled(env, 0);
> +                virt = false;
>              } else {
>                  /* Trap into HS mode */
>                  env->hstatus = set_field(env->hstatus, HSTATUS_SPV, false);
> @@ -1799,7 +1798,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
>          env->htinst = tinst;
>          env->pc = (env->stvec >> 2 << 2) +
>                    ((async && (env->stvec & 3) == 1) ? cause * 4 : 0);
> -        riscv_cpu_set_mode(env, PRV_S);
> +        riscv_cpu_set_mode(env, PRV_S, virt);
>      } else {
>          /* handle the trap in M-mode */
>          if (riscv_has_ext(env, RVH)) {
> @@ -1815,7 +1814,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
>              mtval2 = env->guest_phys_fault_addr;
>
>              /* Trapping to M mode, virt is disabled */
> -            riscv_cpu_set_virt_enabled(env, 0);
> +            virt = false;
>          }
>
>          s = env->mstatus;
> @@ -1830,7 +1829,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
>          env->mtinst = tinst;
>          env->pc = (env->mtvec >> 2 << 2) +
>                    ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0);
> -        riscv_cpu_set_mode(env, PRV_M);
> +        riscv_cpu_set_mode(env, PRV_M, virt);
>      }
>
>      /*
> diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
> index 2baf5bc3ca19..ec1408ba0fb1 100644
> --- a/target/riscv/op_helper.c
> +++ b/target/riscv/op_helper.c
> @@ -264,7 +264,7 @@ void helper_cbo_inval(CPURISCVState *env, target_ulong address)
>  target_ulong helper_sret(CPURISCVState *env)
>  {
>      uint64_t mstatus;
> -    target_ulong prev_priv, prev_virt;
> +    target_ulong prev_priv, prev_virt = env->virt_enabled;
>
>      if (!(env->priv >= PRV_S)) {
>          riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
> @@ -307,11 +307,9 @@ target_ulong helper_sret(CPURISCVState *env)
>          if (prev_virt) {
>              riscv_cpu_swap_hypervisor_regs(env);
>          }
> -
> -        riscv_cpu_set_virt_enabled(env, prev_virt);
>      }
>
> -    riscv_cpu_set_mode(env, prev_priv);
> +    riscv_cpu_set_mode(env, prev_priv, prev_virt);
>
>      return retpc;
>  }
> @@ -347,16 +345,13 @@ target_ulong helper_mret(CPURISCVState *env)
>          mstatus = set_field(mstatus, MSTATUS_MPRV, 0);
>      }
>      env->mstatus = mstatus;
> -    riscv_cpu_set_mode(env, prev_priv);
> -
> -    if (riscv_has_ext(env, RVH)) {
> -        if (prev_virt) {
> -            riscv_cpu_swap_hypervisor_regs(env);
> -        }
>
> -        riscv_cpu_set_virt_enabled(env, prev_virt);
> +    if (riscv_has_ext(env, RVH) && prev_virt) {
> +        riscv_cpu_swap_hypervisor_regs(env);
>      }
>
> +    riscv_cpu_set_mode(env, prev_priv, prev_virt);
> +
>      return retpc;
>  }
>
>
> --
> 2.34.1
>
>
diff mbox series

Patch

diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 90b8f1b08f83..46faefd24e09 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -544,7 +544,7 @@  void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, uint32_t priv,
 RISCVException smstateen_acc_ok(CPURISCVState *env, int index, uint64_t bit);
 #endif /* !CONFIG_USER_ONLY */
 
-void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv);
+void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv, bool virt_en);
 
 void riscv_translate_init(void);
 G_NORETURN void riscv_raise_exception(CPURISCVState *env,
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index 6709622dd3ab..10d3fdaed376 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -619,30 +619,6 @@  void riscv_cpu_set_geilen(CPURISCVState *env, target_ulong geilen)
     env->geilen = geilen;
 }
 
-/* This function can only be called to set virt when RVH is enabled */
-void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable)
-{
-    /* Flush the TLB on all virt mode changes. */
-    if (env->virt_enabled != enable) {
-        tlb_flush(env_cpu(env));
-    }
-
-    env->virt_enabled = enable;
-
-    if (enable) {
-        /*
-         * The guest external interrupts from an interrupt controller are
-         * delivered only when the Guest/VM is running (i.e. V=1). This means
-         * any guest external interrupt which is triggered while the Guest/VM
-         * is not running (i.e. V=0) will be missed on QEMU resulting in guest
-         * with sluggish response to serial console input and other I/O events.
-         *
-         * To solve this, we check and inject interrupt after setting V=1.
-         */
-        riscv_cpu_update_mip(env, 0, 0);
-    }
-}
-
 int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts)
 {
     CPURISCVState *env = &cpu->env;
@@ -715,7 +691,7 @@  void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, uint32_t priv,
     }
 }
 
-void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv)
+void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv, bool virt_en)
 {
     g_assert(newpriv <= PRV_M && newpriv != PRV_RESERVED);
 
@@ -736,6 +712,28 @@  void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv)
      * preemptive context switch. As a result, do both.
      */
     env->load_res = -1;
+
+    if (riscv_has_ext(env, RVH)) {
+        /* Flush the TLB on all virt mode changes. */
+        if (env->virt_enabled != virt_en) {
+            tlb_flush(env_cpu(env));
+        }
+
+        env->virt_enabled = virt_en;
+        if (virt_en) {
+            /*
+             * The guest external interrupts from an interrupt controller are
+             * delivered only when the Guest/VM is running (i.e. V=1). This
+             * means any guest external interrupt which is triggered while the
+             * Guest/VM is not running (i.e. V=0) will be missed on QEMU
+             * resulting in guest with sluggish response to serial console
+             * input and other I/O events.
+             *
+             * To solve this, we check and inject interrupt after setting V=1.
+             */
+            riscv_cpu_update_mip(env, 0, 0);
+        }
+    }
 }
 
 /*
@@ -1648,6 +1646,7 @@  void riscv_cpu_do_interrupt(CPUState *cs)
 {
     RISCVCPU *cpu = RISCV_CPU(cs);
     CPURISCVState *env = &cpu->env;
+    bool virt = env->virt_enabled;
     bool write_gva = false;
     uint64_t s;
 
@@ -1778,7 +1777,7 @@  void riscv_cpu_do_interrupt(CPUState *cs)
 
                 htval = env->guest_phys_fault_addr;
 
-                riscv_cpu_set_virt_enabled(env, 0);
+                virt = false;
             } else {
                 /* Trap into HS mode */
                 env->hstatus = set_field(env->hstatus, HSTATUS_SPV, false);
@@ -1799,7 +1798,7 @@  void riscv_cpu_do_interrupt(CPUState *cs)
         env->htinst = tinst;
         env->pc = (env->stvec >> 2 << 2) +
                   ((async && (env->stvec & 3) == 1) ? cause * 4 : 0);
-        riscv_cpu_set_mode(env, PRV_S);
+        riscv_cpu_set_mode(env, PRV_S, virt);
     } else {
         /* handle the trap in M-mode */
         if (riscv_has_ext(env, RVH)) {
@@ -1815,7 +1814,7 @@  void riscv_cpu_do_interrupt(CPUState *cs)
             mtval2 = env->guest_phys_fault_addr;
 
             /* Trapping to M mode, virt is disabled */
-            riscv_cpu_set_virt_enabled(env, 0);
+            virt = false;
         }
 
         s = env->mstatus;
@@ -1830,7 +1829,7 @@  void riscv_cpu_do_interrupt(CPUState *cs)
         env->mtinst = tinst;
         env->pc = (env->mtvec >> 2 << 2) +
                   ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0);
-        riscv_cpu_set_mode(env, PRV_M);
+        riscv_cpu_set_mode(env, PRV_M, virt);
     }
 
     /*
diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
index 2baf5bc3ca19..ec1408ba0fb1 100644
--- a/target/riscv/op_helper.c
+++ b/target/riscv/op_helper.c
@@ -264,7 +264,7 @@  void helper_cbo_inval(CPURISCVState *env, target_ulong address)
 target_ulong helper_sret(CPURISCVState *env)
 {
     uint64_t mstatus;
-    target_ulong prev_priv, prev_virt;
+    target_ulong prev_priv, prev_virt = env->virt_enabled;
 
     if (!(env->priv >= PRV_S)) {
         riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
@@ -307,11 +307,9 @@  target_ulong helper_sret(CPURISCVState *env)
         if (prev_virt) {
             riscv_cpu_swap_hypervisor_regs(env);
         }
-
-        riscv_cpu_set_virt_enabled(env, prev_virt);
     }
 
-    riscv_cpu_set_mode(env, prev_priv);
+    riscv_cpu_set_mode(env, prev_priv, prev_virt);
 
     return retpc;
 }
@@ -347,16 +345,13 @@  target_ulong helper_mret(CPURISCVState *env)
         mstatus = set_field(mstatus, MSTATUS_MPRV, 0);
     }
     env->mstatus = mstatus;
-    riscv_cpu_set_mode(env, prev_priv);
-
-    if (riscv_has_ext(env, RVH)) {
-        if (prev_virt) {
-            riscv_cpu_swap_hypervisor_regs(env);
-        }
 
-        riscv_cpu_set_virt_enabled(env, prev_virt);
+    if (riscv_has_ext(env, RVH) && prev_virt) {
+        riscv_cpu_swap_hypervisor_regs(env);
     }
 
+    riscv_cpu_set_mode(env, prev_priv, prev_virt);
+
     return retpc;
 }