diff mbox series

[v1,RFC,Zisslpcfi,5/9] target/riscv: state save and restore of zisslppcfi state

Message ID 20230209062404.3582018-6-debug@rivosinc.com
State New
Headers show
Series [v1,RFC,Zisslpcfi,1/9] target/riscv: adding zimops and zisslpcfi extension to RISCV cpu config | expand

Commit Message

Deepak Gupta Feb. 9, 2023, 6:24 a.m. UTC
zisslpcfi's forward cfi if enabled on a hart, enables tracking of
indirect branches. CPU/hart internally keeps a state `elp` short
for expecting landing pad instruction. This state goes into
LP_EXPECTED on an indirect branch. But an interrupt/exception can occur
before target instruction is executed. In such a case this state must be
preserved so that it can be restored later. zisslpcfi saves elp state in
`sstatus` CSR. This patch saves elp state in sstatus CSR on trap delivery
while restores from sstatus CSR on trap return.

Additionally state in sstatus CSR must have save and restore zisslpcfi
state on exiting from hypervisor and entering into hypervisor.

Signed-off-by: Deepak Gupta <debug@rivosinc.com>
Signed-off-by: Kip Walker  <kip@rivosinc.com>
---
 target/riscv/cpu_bits.h   |  5 +++++
 target/riscv/cpu_helper.c | 26 ++++++++++++++++++++++++++
 target/riscv/op_helper.c  | 12 ++++++++++++
 3 files changed, 43 insertions(+)

Comments

LIU Zhiwei Feb. 15, 2023, 6:10 a.m. UTC | #1
On 2023/2/9 14:24, Deepak Gupta wrote:
> zisslpcfi's forward cfi if enabled on a hart, enables tracking of
> indirect branches. CPU/hart internally keeps a state `elp` short
> for expecting landing pad instruction. This state goes into
> LP_EXPECTED on an indirect branch. But an interrupt/exception can occur
> before target instruction is executed. In such a case this state must be
> preserved so that it can be restored later. zisslpcfi saves elp state in
> `sstatus` CSR.

And mstatus CSR.

Otherwise,

Reviewed-by: LIU Zhiwei <zhiwei_liu@linux.alibaba.com>

> This patch saves elp state in sstatus CSR on trap delivery
> while restores from sstatus CSR on trap return.
>
> Additionally state in sstatus CSR must have save and restore zisslpcfi
> state on exiting from hypervisor and entering into hypervisor.
>
> Signed-off-by: Deepak Gupta <debug@rivosinc.com>
> Signed-off-by: Kip Walker  <kip@rivosinc.com>
> ---
>   target/riscv/cpu_bits.h   |  5 +++++
>   target/riscv/cpu_helper.c | 26 ++++++++++++++++++++++++++
>   target/riscv/op_helper.c  | 12 ++++++++++++
>   3 files changed, 43 insertions(+)
>
> diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
> index 1663ba5775..37100ec8f6 100644
> --- a/target/riscv/cpu_bits.h
> +++ b/target/riscv/cpu_bits.h
> @@ -594,6 +594,11 @@ typedef enum {
>   
>   #define CFISTATUS_S_MASK    (SSTATUS_UFCFIEN | SSTATUS_UBCFIEN | \
>                                SSTATUS_SPELP)
> +/* enum for branch tracking state in cpu/hart */
> +typedef enum {
> +    NO_LP_EXPECTED = 0,
> +    LP_EXPECTED = 1,
> +} cfi_elp;
>   
>   /* hstatus CSR bits */
>   #define HSTATUS_VSBE         0x00000020
> diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> index a397023840..fc188683c9 100644
> --- a/target/riscv/cpu_helper.c
> +++ b/target/riscv/cpu_helper.c
> @@ -534,6 +534,16 @@ void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env)
>       if (riscv_has_ext(env, RVF)) {
>           mstatus_mask |= MSTATUS_FS;
>       }
> +
> +    /*
> +     * If cfi extension available, menvcfg.CFI = 1 and henvcfg.CFI = 1,
> +     * then apply CFI mask on mstatus
> +     */
> +    if (env_archcpu(env)->cfg.ext_cfi &&
> +        get_field(env->menvcfg, MENVCFG_CFI) &&
> +        get_field(env->henvcfg, HENVCFG_CFI)) {
> +        mstatus_mask |= CFISTATUS_S_MASK;
> +    }
>       bool current_virt = riscv_cpu_virt_enabled(env);
>   
>       g_assert(riscv_has_ext(env, RVH));
> @@ -1723,6 +1733,10 @@ void riscv_cpu_do_interrupt(CPUState *cs)
>       if (env->priv <= PRV_S &&
>               cause < TARGET_LONG_BITS && ((deleg >> cause) & 1)) {
>           /* handle the trap in S-mode */
> +        /* save elp status */
> +        if (cpu_get_fcfien(env)) {
> +            env->mstatus = set_field(env->mstatus, MSTATUS_SPELP, env->elp);
> +        }
>           if (riscv_has_ext(env, RVH)) {
>               uint64_t hdeleg = async ? env->hideleg : env->hedeleg;
>   
> @@ -1772,6 +1786,10 @@ void riscv_cpu_do_interrupt(CPUState *cs)
>           riscv_cpu_set_mode(env, PRV_S);
>       } else {
>           /* handle the trap in M-mode */
> +        /* save elp status */
> +        if (cpu_get_fcfien(env)) {
> +            env->mstatus = set_field(env->mstatus, MSTATUS_MPELP, env->elp);
> +        }
>           if (riscv_has_ext(env, RVH)) {
>               if (riscv_cpu_virt_enabled(env)) {
>                   riscv_cpu_swap_hypervisor_regs(env);
> @@ -1803,6 +1821,14 @@ void riscv_cpu_do_interrupt(CPUState *cs)
>           riscv_cpu_set_mode(env, PRV_M);
>       }
>   
> +    /*
> +     * Interrupt/exception/trap delivery is asynchronous event and as per
> +     * Zisslpcfi spec CPU should clear up the ELP state. If cfi extension is
> +     * available, clear ELP state.
> +     */
> +    if (cpu->cfg.ext_cfi) {
> +        env->elp = NO_LP_EXPECTED;
> +    }
>       /* NOTE: it is not necessary to yield load reservations here. It is only
>        * necessary for an SC from "another hart" to cause a load reservation
>        * to be yielded. Refer to the memory consistency model section of the
> diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
> index 878bcb03b8..d15893aa82 100644
> --- a/target/riscv/op_helper.c
> +++ b/target/riscv/op_helper.c
> @@ -176,6 +176,12 @@ target_ulong helper_sret(CPURISCVState *env)
>           riscv_cpu_set_virt_enabled(env, prev_virt);
>       }
>   
> +    /* If forward cfi enabled for target, restore elp status */
> +    if (cpu_get_fcfien(env)) {
> +        env->elp = get_field(env->mstatus, MSTATUS_SPELP);
> +        env->mstatus = set_field(env->mstatus, MSTATUS_SPELP, 0);
> +    }
> +
>       riscv_cpu_set_mode(env, prev_priv);
>   
>       return retpc;
> @@ -220,6 +226,12 @@ target_ulong helper_mret(CPURISCVState *env)
>           riscv_cpu_set_virt_enabled(env, prev_virt);
>       }
>   
> +    /* If forward cfi enabled for target, restore elp status */
> +    if (cpu_get_fcfien(env)) {
> +        env->elp = get_field(env->mstatus, MSTATUS_MPELP);
> +        env->mstatus = set_field(env->mstatus, MSTATUS_MPELP, 0);
> +    }
> +
>       return retpc;
>   }
>
Deepak Gupta Feb. 15, 2023, 11:13 p.m. UTC | #2
On Tue, Feb 14, 2023 at 10:11 PM LIU Zhiwei
<zhiwei_liu@linux.alibaba.com> wrote:
>
>
> On 2023/2/9 14:24, Deepak Gupta wrote:
> > zisslpcfi's forward cfi if enabled on a hart, enables tracking of
> > indirect branches. CPU/hart internally keeps a state `elp` short
> > for expecting landing pad instruction. This state goes into
> > LP_EXPECTED on an indirect branch. But an interrupt/exception can occur
> > before target instruction is executed. In such a case this state must be
> > preserved so that it can be restored later. zisslpcfi saves elp state in
> > `sstatus` CSR.
>
> And mstatus CSR.

Noted.

>
> Otherwise,
>
> Reviewed-by: LIU Zhiwei <zhiwei_liu@linux.alibaba.com>
>
> > This patch saves elp state in sstatus CSR on trap delivery
> > while restores from sstatus CSR on trap return.
> >
> > Additionally state in sstatus CSR must have save and restore zisslpcfi
> > state on exiting from hypervisor and entering into hypervisor.
> >
> > Signed-off-by: Deepak Gupta <debug@rivosinc.com>
> > Signed-off-by: Kip Walker  <kip@rivosinc.com>
> > ---
> >   target/riscv/cpu_bits.h   |  5 +++++
> >   target/riscv/cpu_helper.c | 26 ++++++++++++++++++++++++++
> >   target/riscv/op_helper.c  | 12 ++++++++++++
> >   3 files changed, 43 insertions(+)
> >
> > diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
> > index 1663ba5775..37100ec8f6 100644
> > --- a/target/riscv/cpu_bits.h
> > +++ b/target/riscv/cpu_bits.h
> > @@ -594,6 +594,11 @@ typedef enum {
> >
> >   #define CFISTATUS_S_MASK    (SSTATUS_UFCFIEN | SSTATUS_UBCFIEN | \
> >                                SSTATUS_SPELP)
> > +/* enum for branch tracking state in cpu/hart */
> > +typedef enum {
> > +    NO_LP_EXPECTED = 0,
> > +    LP_EXPECTED = 1,
> > +} cfi_elp;
> >
> >   /* hstatus CSR bits */
> >   #define HSTATUS_VSBE         0x00000020
> > diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> > index a397023840..fc188683c9 100644
> > --- a/target/riscv/cpu_helper.c
> > +++ b/target/riscv/cpu_helper.c
> > @@ -534,6 +534,16 @@ void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env)
> >       if (riscv_has_ext(env, RVF)) {
> >           mstatus_mask |= MSTATUS_FS;
> >       }
> > +
> > +    /*
> > +     * If cfi extension available, menvcfg.CFI = 1 and henvcfg.CFI = 1,
> > +     * then apply CFI mask on mstatus
> > +     */
> > +    if (env_archcpu(env)->cfg.ext_cfi &&
> > +        get_field(env->menvcfg, MENVCFG_CFI) &&
> > +        get_field(env->henvcfg, HENVCFG_CFI)) {
> > +        mstatus_mask |= CFISTATUS_S_MASK;
> > +    }
> >       bool current_virt = riscv_cpu_virt_enabled(env);
> >
> >       g_assert(riscv_has_ext(env, RVH));
> > @@ -1723,6 +1733,10 @@ void riscv_cpu_do_interrupt(CPUState *cs)
> >       if (env->priv <= PRV_S &&
> >               cause < TARGET_LONG_BITS && ((deleg >> cause) & 1)) {
> >           /* handle the trap in S-mode */
> > +        /* save elp status */
> > +        if (cpu_get_fcfien(env)) {
> > +            env->mstatus = set_field(env->mstatus, MSTATUS_SPELP, env->elp);
> > +        }
> >           if (riscv_has_ext(env, RVH)) {
> >               uint64_t hdeleg = async ? env->hideleg : env->hedeleg;
> >
> > @@ -1772,6 +1786,10 @@ void riscv_cpu_do_interrupt(CPUState *cs)
> >           riscv_cpu_set_mode(env, PRV_S);
> >       } else {
> >           /* handle the trap in M-mode */
> > +        /* save elp status */
> > +        if (cpu_get_fcfien(env)) {
> > +            env->mstatus = set_field(env->mstatus, MSTATUS_MPELP, env->elp);
> > +        }
> >           if (riscv_has_ext(env, RVH)) {
> >               if (riscv_cpu_virt_enabled(env)) {
> >                   riscv_cpu_swap_hypervisor_regs(env);
> > @@ -1803,6 +1821,14 @@ void riscv_cpu_do_interrupt(CPUState *cs)
> >           riscv_cpu_set_mode(env, PRV_M);
> >       }
> >
> > +    /*
> > +     * Interrupt/exception/trap delivery is asynchronous event and as per
> > +     * Zisslpcfi spec CPU should clear up the ELP state. If cfi extension is
> > +     * available, clear ELP state.
> > +     */
> > +    if (cpu->cfg.ext_cfi) {
> > +        env->elp = NO_LP_EXPECTED;
> > +    }
> >       /* NOTE: it is not necessary to yield load reservations here. It is only
> >        * necessary for an SC from "another hart" to cause a load reservation
> >        * to be yielded. Refer to the memory consistency model section of the
> > diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
> > index 878bcb03b8..d15893aa82 100644
> > --- a/target/riscv/op_helper.c
> > +++ b/target/riscv/op_helper.c
> > @@ -176,6 +176,12 @@ target_ulong helper_sret(CPURISCVState *env)
> >           riscv_cpu_set_virt_enabled(env, prev_virt);
> >       }
> >
> > +    /* If forward cfi enabled for target, restore elp status */
> > +    if (cpu_get_fcfien(env)) {
> > +        env->elp = get_field(env->mstatus, MSTATUS_SPELP);
> > +        env->mstatus = set_field(env->mstatus, MSTATUS_SPELP, 0);
> > +    }
> > +
> >       riscv_cpu_set_mode(env, prev_priv);
> >
> >       return retpc;
> > @@ -220,6 +226,12 @@ target_ulong helper_mret(CPURISCVState *env)
> >           riscv_cpu_set_virt_enabled(env, prev_virt);
> >       }
> >
> > +    /* If forward cfi enabled for target, restore elp status */
> > +    if (cpu_get_fcfien(env)) {
> > +        env->elp = get_field(env->mstatus, MSTATUS_MPELP);
> > +        env->mstatus = set_field(env->mstatus, MSTATUS_MPELP, 0);
> > +    }
> > +
> >       return retpc;
> >   }
> >
diff mbox series

Patch

diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index 1663ba5775..37100ec8f6 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -594,6 +594,11 @@  typedef enum {
 
 #define CFISTATUS_S_MASK    (SSTATUS_UFCFIEN | SSTATUS_UBCFIEN | \
                              SSTATUS_SPELP)
+/* enum for branch tracking state in cpu/hart */
+typedef enum {
+    NO_LP_EXPECTED = 0,
+    LP_EXPECTED = 1,
+} cfi_elp;
 
 /* hstatus CSR bits */
 #define HSTATUS_VSBE         0x00000020
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index a397023840..fc188683c9 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -534,6 +534,16 @@  void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env)
     if (riscv_has_ext(env, RVF)) {
         mstatus_mask |= MSTATUS_FS;
     }
+
+    /*
+     * If cfi extension available, menvcfg.CFI = 1 and henvcfg.CFI = 1,
+     * then apply CFI mask on mstatus
+     */
+    if (env_archcpu(env)->cfg.ext_cfi &&
+        get_field(env->menvcfg, MENVCFG_CFI) &&
+        get_field(env->henvcfg, HENVCFG_CFI)) {
+        mstatus_mask |= CFISTATUS_S_MASK;
+    }
     bool current_virt = riscv_cpu_virt_enabled(env);
 
     g_assert(riscv_has_ext(env, RVH));
@@ -1723,6 +1733,10 @@  void riscv_cpu_do_interrupt(CPUState *cs)
     if (env->priv <= PRV_S &&
             cause < TARGET_LONG_BITS && ((deleg >> cause) & 1)) {
         /* handle the trap in S-mode */
+        /* save elp status */
+        if (cpu_get_fcfien(env)) {
+            env->mstatus = set_field(env->mstatus, MSTATUS_SPELP, env->elp);
+        }
         if (riscv_has_ext(env, RVH)) {
             uint64_t hdeleg = async ? env->hideleg : env->hedeleg;
 
@@ -1772,6 +1786,10 @@  void riscv_cpu_do_interrupt(CPUState *cs)
         riscv_cpu_set_mode(env, PRV_S);
     } else {
         /* handle the trap in M-mode */
+        /* save elp status */
+        if (cpu_get_fcfien(env)) {
+            env->mstatus = set_field(env->mstatus, MSTATUS_MPELP, env->elp);
+        }
         if (riscv_has_ext(env, RVH)) {
             if (riscv_cpu_virt_enabled(env)) {
                 riscv_cpu_swap_hypervisor_regs(env);
@@ -1803,6 +1821,14 @@  void riscv_cpu_do_interrupt(CPUState *cs)
         riscv_cpu_set_mode(env, PRV_M);
     }
 
+    /*
+     * Interrupt/exception/trap delivery is asynchronous event and as per
+     * Zisslpcfi spec CPU should clear up the ELP state. If cfi extension is
+     * available, clear ELP state.
+     */
+    if (cpu->cfg.ext_cfi) {
+        env->elp = NO_LP_EXPECTED;
+    }
     /* NOTE: it is not necessary to yield load reservations here. It is only
      * necessary for an SC from "another hart" to cause a load reservation
      * to be yielded. Refer to the memory consistency model section of the
diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
index 878bcb03b8..d15893aa82 100644
--- a/target/riscv/op_helper.c
+++ b/target/riscv/op_helper.c
@@ -176,6 +176,12 @@  target_ulong helper_sret(CPURISCVState *env)
         riscv_cpu_set_virt_enabled(env, prev_virt);
     }
 
+    /* If forward cfi enabled for target, restore elp status */
+    if (cpu_get_fcfien(env)) {
+        env->elp = get_field(env->mstatus, MSTATUS_SPELP);
+        env->mstatus = set_field(env->mstatus, MSTATUS_SPELP, 0);
+    }
+
     riscv_cpu_set_mode(env, prev_priv);
 
     return retpc;
@@ -220,6 +226,12 @@  target_ulong helper_mret(CPURISCVState *env)
         riscv_cpu_set_virt_enabled(env, prev_virt);
     }
 
+    /* If forward cfi enabled for target, restore elp status */
+    if (cpu_get_fcfien(env)) {
+        env->elp = get_field(env->mstatus, MSTATUS_MPELP);
+        env->mstatus = set_field(env->mstatus, MSTATUS_MPELP, 0);
+    }
+
     return retpc;
 }