zisslpcfi protects forward control flow (if enabled) by enforcing all
indirect call and jmp must land on a landing pad instruction `lpcll`
short for landing pad and check lower label value. If target of an
indirect call or jmp is not `lpcll` then cpu/hart must raise an illegal
instruction exception.
This patch implements the mechanism using TCG. Target architecture branch
instruction must define the end of a TB. Using this property, during
translation of branch instruction, TB flag = FCFI_LP_EXPECTED can be set.
Translation of target TB can check if FCFI_LP_EXPECTED flag is set and a
flag (fcfi_lp_expected) can be set in DisasContext. If `lpcll` gets
translated, fcfi_lp_expected flag in DisasContext can be cleared. Else
it'll fault.
This patch also also adds flag for forward and backward cfi in
DisasContext.
Signed-off-by: Deepak Gupta <de...@rivosinc.com>
Signed-off-by: Kip Walker <k...@rivosinc.com>
---
target/riscv/cpu.h | 3 +++
target/riscv/cpu_helper.c | 12 +++++++++
target/riscv/translate.c | 52 +++++++++++++++++++++++++++++++++++++++
3 files changed, 67 insertions(+)
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 8803ea6426..98b272bcad 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -644,6 +644,9 @@ FIELD(TB_FLAGS, VMA, 25, 1)
/* Native debug itrigger */
FIELD(TB_FLAGS, ITRIGGER, 26, 1)
+/* Zisslpcfi needs a TB flag to track indirect branches */
+FIELD(TB_FLAGS, FCFI_LP_EXPECTED, 27, 1)
+
#ifdef TARGET_RISCV32
#define riscv_cpu_mxl(env) ((void)(env), MXL_RV32)
#else
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index 63377abc2f..d15918f534 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -129,6 +129,18 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong
*pc,
flags = FIELD_DP32(flags, TB_FLAGS, VILL, 1);
}
+ if (cpu->cfg.ext_cfi) {
+ /*
+ * For Forward CFI, only the expectation of a lpcll at
+ * the start of the block is tracked (which can only happen
+ * when FCFI is enabled for the current processor mode). A jump
+ * or call at the end of the previous TB will have updated
+ * env->elp to indicate the expectation.
+ */
+ flags = FIELD_DP32(flags, TB_FLAGS, FCFI_LP_EXPECTED,
+ env->elp != NO_LP_EXPECTED);
+ }
+
#ifdef CONFIG_USER_ONLY
flags |= TB_FLAGS_MSTATUS_FS;
flags |= TB_FLAGS_MSTATUS_VS;
diff --git a/target/riscv/translate.c b/target/riscv/translate.c
index df38db7553..7d43d20fc3 100644
--- a/target/riscv/translate.c
+++ b/target/riscv/translate.c
@@ -41,6 +41,7 @@ static TCGv load_val;
/* globals for PM CSRs */
static TCGv pm_mask;
static TCGv pm_base;
+static TCGOp *cfi_lp_check;
#include "exec/gen-icount.h"
@@ -116,6 +117,10 @@ typedef struct DisasContext {
bool itrigger;
/* TCG of the current insn_start */
TCGOp *insn_start;
+ /* CFI extension */
+ bool bcfi_enabled;
+ bool fcfi_enabled;
+ bool fcfi_lp_expected;
} DisasContext;
static inline bool has_ext(DisasContext *ctx, uint32_t ext)
@@ -1166,11 +1171,44 @@ static void
riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
ctx->pm_mask_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_MASK_ENABLED);
ctx->pm_base_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_BASE_ENABLED);
ctx->itrigger = FIELD_EX32(tb_flags, TB_FLAGS, ITRIGGER);
+ ctx->bcfi_enabled = cpu_get_bcfien(env);
+ ctx->fcfi_enabled = cpu_get_fcfien(env);