diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index 364f3ee212..c7af430f38 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -134,6 +134,19 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, vaddr *pc,
flags = FIELD_DP32(flags, TB_FLAGS, VILL, 1);
}
+ if (cpu_get_fcfien(env)) {
+ /*
+ * 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);
static void riscv_tr_tb_start(DisasContextBase *db, CPUState *cpu)
{
+ DisasContext *ctx = container_of(db, DisasContext, base);
+
+ if (ctx->fcfi_lp_expected) {
+ /*
+ * Since we can't look ahead to confirm that the first
+ * instruction is a legal landing pad instruction, emit
+ * compare-and-branch sequence that will be fixed-up in
+ * riscv_tr_tb_stop() to either statically hit or skip an
+ * illegal instruction exception depending on whether the
+ * flag was lowered by translation of a CJLP or JLP as
+ * the first instruction in the block.
+ */
+ TCGv_i32 immediate;
+ TCGLabel *l;
+ l = gen_new_label();
+ immediate = tcg_temp_new_i32();
+ tcg_gen_movi_i32(immediate, 0);
+ cfi_lp_check = tcg_last_op();
+ tcg_gen_brcondi_i32(TCG_COND_EQ, immediate, 0, l);
+ gen_helper_raise_sw_check_excep(tcg_env,
+ tcg_constant_tl(RISCV_EXCP_SW_CHECK_FCFI_TVAL),
+ tcg_constant_tl(MISSING_LPAD), tcg_constant_tl(0));
+ gen_set_label(l);
+ /*
+ * Despite the use of gen_exception_illegal(), the rest of
+ * the TB needs to be generated. The TCG optimizer will
+ * clean things up depending on which path ends up being
+ * active.
+ */
+ ctx->base.is_jmp = DISAS_NEXT;
+ }
}