@@ -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
@@ -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
@@ -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;
}