From e35867b9ca57bfff3c9f03794231cb1831d17bda Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Thu, 22 Oct 2026 11:31:45 -0700 Subject: [PATCH] target/hexagon: implement control flow integrity (CFI) Implement v83 restricted indirect-control-flow instructions and validate their targets. Reviewed-by: Pierrick Bouvier Reviewed-by: Marco Liebel Signed-off-by: Brian Cain --- linux-user/hexagon/cpu_loop.c | 9 +++ linux-user/hexagon/signal.c | 8 ++ target/hexagon/attribs_def.h.inc | 1 + target/hexagon/cpu.c | 6 ++ target/hexagon/cpu.h | 3 + target/hexagon/cpu_bits.h | 1 + target/hexagon/gen_tcg.h | 90 +++++++++++++++------- target/hexagon/gen_tcg_sys.h | 6 ++ target/hexagon/genptr.c | 36 +++++++++ target/hexagon/hex_interrupts.c | 3 + target/hexagon/hexswi.c | 11 +++ target/hexagon/imported/alu.idef | 3 + target/hexagon/imported/branch.idef | 38 +++++++++ target/hexagon/imported/encode_pp.def | 18 ++++- target/hexagon/imported/encode_subinsn.def | 1 + target/hexagon/imported/ldst.idef | 2 + target/hexagon/imported/subinsns.idef | 2 + target/hexagon/imported/system.idef | 2 +- target/hexagon/machine.c | 3 +- target/hexagon/macros.h | 12 +++ target/hexagon/reg_fields_def.h.inc | 1 + target/hexagon/tag_rev_info.c.inc | 20 +++++ target/hexagon/translate.c | 59 ++++++++++++++ target/hexagon/translate.h | 3 + 24 files changed, 307 insertions(+), 31 deletions(-) diff --git a/linux-user/hexagon/cpu_loop.c b/linux-user/hexagon/cpu_loop.c index 22570a1264..32249f27b9 100644 --- a/linux-user/hexagon/cpu_loop.c +++ b/linux-user/hexagon/cpu_loop.c @@ -49,6 +49,9 @@ void cpu_loop(CPUHexagonState *env) break; case HEX_EVENT_TRAP0: case HEX_EVENT_TRAP2: + /* Exception entry ends any pending restricted-branch check. */ + env->cfi_active = 0; + env->cfi_updated = 0; syscallnum = env->gpr[6]; env->gpr[HEX_REG_PC] += 4; ret = do_syscall(env, @@ -78,6 +81,12 @@ void cpu_loop(CPUHexagonState *env) force_sig_fault(TARGET_SIGSEGV, TARGET_SEGV_BNDERR, env->gpr[HEX_REG_PC]); break; + case HEX_CAUSE_CFI_VIOLATION: + env->cfi_active = 0; + env->cfi_updated = 0; + force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, + env->gpr[HEX_REG_PC]); + break; case HEX_CAUSE_PRIV_USER_NO_GINSN: case HEX_CAUSE_PRIV_USER_NO_SINSN: case HEX_CAUSE_INVALID_PACKET: diff --git a/linux-user/hexagon/signal.c b/linux-user/hexagon/signal.c index 183ecfaa19..f8ba4a6b47 100644 --- a/linux-user/hexagon/signal.c +++ b/linux-user/hexagon/signal.c @@ -106,6 +106,10 @@ static void setup_sigcontext(struct target_sigcontext *sc, CPUHexagonState *env) __put_user(env->gpr[HEX_REG_CS0], &sc->sc_regs.cs0); __put_user(env->gpr[HEX_REG_CS1], &sc->sc_regs.cs1); + /* Preserve a pending restricted-branch landing pad across the signal. */ + __put_user(env->cfi_active, &sc->sc_regs.pad1); + env->cfi_active = 0; + env->cfi_updated = 0; } static void setup_ucontext(struct target_ucontext *uc, @@ -176,6 +180,7 @@ static void restore_sigcontext(CPUHexagonState *env, struct target_sigcontext *sc) { abi_ulong preds; + abi_ulong cfi; for (int i = 0; i < 32; i++) { __get_user(env->gpr[HEX_REG_R00 + i], &sc->sc_regs.gpr[i]); @@ -199,6 +204,9 @@ static void restore_sigcontext(CPUHexagonState *env, __get_user(env->gpr[HEX_REG_CS0], &sc->sc_regs.cs0); __get_user(env->gpr[HEX_REG_CS1], &sc->sc_regs.cs1); + __get_user(cfi, &sc->sc_regs.pad1); + env->cfi_active = cfi & 1; + env->cfi_updated = 0; } static void restore_ucontext(CPUHexagonState *env, struct target_ucontext *uc) diff --git a/target/hexagon/attribs_def.h.inc b/target/hexagon/attribs_def.h.inc index 8deef8e4cf..96ad1fd2f8 100644 --- a/target/hexagon/attribs_def.h.inc +++ b/target/hexagon/attribs_def.h.inc @@ -200,6 +200,7 @@ DEF_ATTRIB(NOTE_SHIFT_RESOURCE, "Uses the HVX shift resource.", "", "") /* Restrictions to make note of */ DEF_ATTRIB(RESTRICT_NOSLOT1_STORE, "Packet must not have slot 1 store", "", "") DEF_ATTRIB(RESTRICT_LATEPRED, "Predicate can not be used as a .new.", "", "") +DEF_ATTRIB(RESTRICT_TARGET, "Restricted branch target instruction", "", "") /* HVX IEEE FP extension attributes */ DEF_ATTRIB(HVX_IEEE_FP, "HVX IEEE FP extension instruction", "", "") diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index de6d8e3569..632ee3d60b 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -452,6 +452,9 @@ void hexagon_cpu_soft_reset(CPUHexagonState *env) BQL_LOCK_GUARD(); env->llsc_addr = ~0; + env->cfi_active = 0; + env->cfi_active_log = 0; + env->cfi_updated = 0; env->t_sreg[HEX_SREG_SSR] = 0; hexagon_ssr_set_cause(env, HEX_CAUSE_RESET); @@ -489,6 +492,9 @@ static void hexagon_cpu_reset_hold(Object *obj, ResetType type) set_float_default_nan_pattern(0b01111111, &env->hvx_fp_status); env->llsc_addr = ~0; + env->cfi_active = 0; + env->cfi_active_log = 0; + env->cfi_updated = 0; #ifndef CONFIG_USER_ONLY memset(env->t_sreg, 0, sizeof(uint32_t) * NUM_SREGS); memset(env->greg, 0, sizeof(uint32_t) * NUM_GREGS); diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h index 15ae5d1d00..ba155d4383 100644 --- a/target/hexagon/cpu.h +++ b/target/hexagon/cpu.h @@ -132,6 +132,9 @@ typedef struct CPUArchState { target_ulong gpr[TOTAL_PER_THREAD_REGS]; target_ulong pred[NUM_PREGS]; uint32_t cause_code; + uint32_t cfi_active; + uint32_t cfi_active_log; + uint32_t cfi_updated; /* For comparing with LLDB on target - see adjust_stack_ptrs function */ target_ulong last_pc_dumped; diff --git a/target/hexagon/cpu_bits.h b/target/hexagon/cpu_bits.h index f5f1349b07..c9ba09989e 100644 --- a/target/hexagon/cpu_bits.h +++ b/target/hexagon/cpu_bits.h @@ -74,6 +74,7 @@ enum hex_cause { HEX_CAUSE_TRAP1 = 0x009, HEX_CAUSE_FETCH_NO_XPAGE = 0x011, HEX_CAUSE_FETCH_NO_UPAGE = 0x012, + HEX_CAUSE_CFI_VIOLATION = 0x014, HEX_CAUSE_INVALID_PACKET = 0x015, HEX_CAUSE_INVALID_OPCODE = 0x015, /* alias: same cause as INVALID_PACKET */ HEX_CAUSE_NO_COPROC_ENABLE = 0x016, diff --git a/target/hexagon/gen_tcg.h b/target/hexagon/gen_tcg.h index ae94127e30..67691c9d9d 100644 --- a/target/hexagon/gen_tcg.h +++ b/target/hexagon/gen_tcg.h @@ -531,34 +531,8 @@ do { RsV = RsV; } while (0) #endif -#ifdef CONFIG_USER_ONLY #define fGEN_TCG_V2_CHECK_AND_WRITE_VSP(VSP) \ - do { \ - TCGLabel *write_vsp = gen_new_label(); \ - tcg_gen_brcond_tl(TCG_COND_GEU, VSP, \ - hex_gpr[HEX_REG_VFRAMELIMIT], write_vsp); \ - gen_precise_exception(HEX_CAUSE_VECTOR_STACK_OVERFLOW, ctx->pkt.pc); \ - gen_set_label(write_vsp); \ - tcg_gen_mov_tl(hex_gpr[HEX_REG_VSP], VSP); \ - } while (0) -#else -#define fGEN_TCG_V2_CHECK_AND_WRITE_VSP(VSP) \ - do { \ - TCGLabel *write_vsp = gen_new_label(); \ - TCGv mode = tcg_temp_new(); \ - tcg_gen_andi_tl(mode, hex_t_sreg[HEX_SREG_SSR], \ - 1 << reg_field_info[SSR_EX].offset); \ - tcg_gen_brcondi_tl(TCG_COND_NE, mode, 0, write_vsp); \ - tcg_gen_andi_tl(mode, hex_t_sreg[HEX_SREG_SSR], \ - 1 << reg_field_info[SSR_UM].offset); \ - tcg_gen_brcondi_tl(TCG_COND_EQ, mode, 0, write_vsp); \ - tcg_gen_brcond_tl(TCG_COND_GEU, VSP, \ - hex_gpr[HEX_REG_VFRAMELIMIT], write_vsp); \ - gen_precise_exception(HEX_CAUSE_VECTOR_STACK_OVERFLOW, ctx->pkt.pc); \ - gen_set_label(write_vsp); \ - tcg_gen_mov_tl(hex_gpr[HEX_REG_VSP], VSP); \ - } while (0) -#endif + gen_vframecheck(ctx, VSP) #define fGEN_TCG_V2_vallocframe_imm(SHORTCODE) \ do { \ TCGv vsp = tcg_temp_new(); \ @@ -589,6 +563,9 @@ #define fGEN_TCG_S2_allocframe(SHORTCODE) \ gen_allocframe(ctx, RxV, uiV) +#define fGEN_TCG_S2_allocframe_rb(SHORTCODE) \ + fGEN_TCG_S2_allocframe(SHORTCODE) + /* sub-instruction version (no RxV, so handle it manually) */ #define fGEN_TCG_SS2_allocframe(SHORTCODE) \ do { \ @@ -597,6 +574,9 @@ gen_allocframe(ctx, r29, uiV); \ } while (0) +#define fGEN_TCG_SS2_allocframe_rb(SHORTCODE) \ + fGEN_TCG_SS2_allocframe(SHORTCODE) + /* * Rdd32 = deallocframe(Rs32):raw * RddV == r31:30 @@ -777,6 +757,16 @@ gen_callr(ctx, RsV) #define fGEN_TCG_J2_callrh(SHORTCODE) \ gen_callr(ctx, RsV) +#define fGEN_TCG_J2_callr_rb(SHORTCODE) \ + do { \ + gen_callr(ctx, RsV); \ + gen_set_cfi(ctx); \ + } while (0) +#define fGEN_TCG_J2_callrh_rb(SHORTCODE) \ + do { \ + gen_callr(ctx, RsV); \ + gen_set_cfi(ctx); \ + } while (0) #define fGEN_TCG_J2_callt(SHORTCODE) \ gen_cond_call(ctx, PuV, TCG_COND_TSTEQ, riV) @@ -786,6 +776,16 @@ gen_cond_callr(ctx, TCG_COND_TSTEQ, PuV, RsV) #define fGEN_TCG_J2_callrf(SHORTCODE) \ gen_cond_callr(ctx, TCG_COND_TSTNE, PuV, RsV) +#define fGEN_TCG_J2_callrt_rb(SHORTCODE) \ + do { \ + gen_cond_callr(ctx, TCG_COND_TSTEQ, PuV, RsV); \ + gen_cond_set_cfi(ctx, true, PuV, -1); \ + } while (0) +#define fGEN_TCG_J2_callrf_rb(SHORTCODE) \ + do { \ + gen_cond_callr(ctx, TCG_COND_TSTNE, PuV, RsV); \ + gen_cond_set_cfi(ctx, false, PuV, -1); \ + } while (0) #define fGEN_TCG_J2_loop0r(SHORTCODE) \ gen_loop0r(ctx, RsV, riV) @@ -1006,6 +1006,39 @@ gen_jumpr(ctx, RsV) #define fGEN_TCG_J2_jumprh(SHORTCODE) \ gen_jumpr(ctx, RsV) +#define fGEN_TCG_J2_jumpr_rb(SHORTCODE) \ + do { \ + gen_jumpr(ctx, RsV); \ + gen_set_cfi_for_target(ctx, RsN); \ + } while (0) +#define fGEN_TCG_J2_jumprh_rb(SHORTCODE) \ + fGEN_TCG_J2_jumpr_rb(SHORTCODE) +#define fGEN_TCG_J2_jumprt_rb(SHORTCODE) \ + do { \ + gen_cond_jumpr(ctx, RsV, TCG_COND_TSTEQ, PuV); \ + gen_cond_set_cfi(ctx, true, PuV, RsN); \ + } while (0) +#define fGEN_TCG_J2_jumprf_rb(SHORTCODE) \ + do { \ + gen_cond_jumpr(ctx, RsV, TCG_COND_TSTNE, PuV); \ + gen_cond_set_cfi(ctx, false, PuV, RsN); \ + } while (0) +#define fGEN_TCG_J2_jumprtpt_rb(SHORTCODE) fGEN_TCG_J2_jumprt_rb(SHORTCODE) +#define fGEN_TCG_J2_jumprfpt_rb(SHORTCODE) fGEN_TCG_J2_jumprf_rb(SHORTCODE) +#define fGEN_TCG_J2_jumprtnew_rb(SHORTCODE) \ + do { \ + gen_cond_jumpr(ctx, RsV, TCG_COND_TSTEQ, PuN); \ + gen_cond_set_cfi(ctx, true, PuN, RsN); \ + } while (0) +#define fGEN_TCG_J2_jumprfnew_rb(SHORTCODE) \ + do { \ + gen_cond_jumpr(ctx, RsV, TCG_COND_TSTNE, PuN); \ + gen_cond_set_cfi(ctx, false, PuN, RsN); \ + } while (0) +#define fGEN_TCG_J2_jumprtnewpt_rb(SHORTCODE) \ + fGEN_TCG_J2_jumprtnew_rb(SHORTCODE) +#define fGEN_TCG_J2_jumprfnewpt_rb(SHORTCODE) \ + fGEN_TCG_J2_jumprfnew_rb(SHORTCODE) #define fGEN_TCG_J4_jumpseti(SHORTCODE) \ do { \ tcg_gen_movi_tl(RdV, UiV); \ @@ -1195,7 +1228,7 @@ RdV, tcg_constant_tl(0)); \ } while (0) -#define fGEN_TCG_Y2_break(SHORTCODE) +#define fGEN_TCG_Y2_break(SHORTCODE) gen_reset_cfi(ctx) #define fGEN_TCG_J2_unpause(SHORTCODE) #ifdef CONFIG_USER_ONLY @@ -1477,4 +1510,5 @@ #endif #define fGEN_TCG_A2_nop(SHORTCODE) do { } while (0) +#define fGEN_TCG_A2_nop_rb(SHORTCODE) gen_reset_cfi(ctx) #define fGEN_TCG_SA1_setin1(SHORTCODE) tcg_gen_movi_tl(RdV, -1) diff --git a/target/hexagon/gen_tcg_sys.h b/target/hexagon/gen_tcg_sys.h index 413ccc97f7..2486f9f0d6 100644 --- a/target/hexagon/gen_tcg_sys.h +++ b/target/hexagon/gen_tcg_sys.h @@ -131,11 +131,17 @@ */ #define fGEN_TCG_J2_rte(SHORTCODE) \ do { \ + TCGv_i32 cfi = tcg_temp_new_i32(); \ TCGv_i32 new_ssr = tcg_temp_new_i32(); \ + tcg_gen_extract_i32(cfi, hex_t_sreg[HEX_SREG_SSR], \ + reg_field_info[SSR_CFI].offset, 1); \ + fSET_CFI(cfi); \ tcg_gen_deposit_tl(new_ssr, hex_t_sreg[HEX_SREG_SSR], \ tcg_constant_tl(0), \ reg_field_info[SSR_EX].offset, \ reg_field_info[SSR_EX].width); \ + tcg_gen_deposit_tl(new_ssr, new_ssr, tcg_constant_tl(0), \ + reg_field_info[SSR_CFI].offset, 1); \ gen_log_sreg_write(ctx, HEX_SREG_SSR, new_ssr); \ gen_jumpr(ctx, hex_t_sreg[HEX_SREG_ELR]); \ } while (0) diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c index f1d0529d0c..7ef36e336f 100644 --- a/target/hexagon/genptr.c +++ b/target/hexagon/genptr.c @@ -1085,6 +1085,42 @@ static void gen_cond_callr(DisasContext *ctx, gen_set_label(skip); } +static void gen_set_cfi(DisasContext *ctx) +{ + fSET_CFI(tcg_constant_i32(1)); +} + +static void gen_reset_cfi(DisasContext *ctx) +{ + fSET_CFI(tcg_constant_i32(0)); +} + +/* Indirect transfers through LR are returns, which do not set the CFI state */ +static void gen_set_cfi_for_target(DisasContext *ctx, int rs_num) +{ + if (rs_num == HEX_REG_LR) { + gen_reset_cfi(ctx); + } else { + gen_set_cfi(ctx); + } +} + +/* + * Update the CFI state if the predicate (true if @sense, else false) holds. + * @rs_num is the jump target register, or -1 for calls. + */ +static void gen_cond_set_cfi(DisasContext *ctx, bool sense, TCGv pred, + int rs_num) +{ + TCGv lsb = tcg_temp_new(); + TCGLabel *skip = gen_new_label(); + + tcg_gen_andi_tl(lsb, pred, 1); + tcg_gen_brcondi_tl(sense ? TCG_COND_EQ : TCG_COND_NE, lsb, 0, skip); + gen_set_cfi_for_target(ctx, rs_num); + gen_set_label(skip); +} + #ifndef CONFIG_HEXAGON_IDEF_PARSER /* frame = ((LR << 32) | FP) ^ (FRAMEKEY << 32)) */ static TCGv_i64 gen_frame_scramble(void) diff --git a/target/hexagon/hex_interrupts.c b/target/hexagon/hex_interrupts.c index 03e1f28da9..81e7d016c3 100644 --- a/target/hexagon/hex_interrupts.c +++ b/target/hexagon/hex_interrupts.c @@ -48,6 +48,7 @@ static void set_ssr_ex_cause(CPUHexagonState *env, int ex, uint32_t cause) old = env->t_sreg[HEX_SREG_SSR]; SET_SYSTEM_FIELD(env, HEX_SREG_SSR, SSR_EX, ex); SET_SYSTEM_FIELD(env, HEX_SREG_SSR, SSR_CAUSE, cause); + SET_SYSTEM_FIELD(env, HEX_SREG_SSR, SSR_CFI, env->cfi_active); new = env->t_sreg[HEX_SREG_SSR]; hexagon_modify_ssr(env, new, old); } @@ -373,6 +374,8 @@ static void hex_accept_int(CPUHexagonState *env, int int_num) env->threadId); set_ssr_ex_cause(env, 1, HEX_CAUSE_INT0 | int_num); + env->cfi_active = 0; + env->cfi_updated = 0; set_elr(env, elr); env->gpr[HEX_REG_PC] = evb | (cs->exception_index << 2); } diff --git a/target/hexagon/hexswi.c b/target/hexagon/hexswi.c index 2074f13550..441fe37b7a 100644 --- a/target/hexagon/hexswi.c +++ b/target/hexagon/hexswi.c @@ -757,7 +757,17 @@ void hexagon_cpu_do_interrupt(CPUState *cs) env->llsc_addr = ~0; ssr = env->t_sreg[HEX_SREG_SSR]; + if (GET_SSR_FIELD(SSR_EX, ssr) == 0) { + SET_SSR_FIELD(env, SSR_CFI, env->cfi_active); + } + env->cfi_active = 0; + env->cfi_updated = 0; + if (GET_SSR_FIELD(SSR_EX, ssr) == 1) { + /* + * Double exception: keep the SSR.CFI value saved by the first + * exception, which a later rte would need to restore. + */ if (cpu->globalregs) { hexagon_globalreg_write(cpu->globalregs, HEX_SREG_DIAG, env->cause_code, env->threadId); @@ -861,6 +871,7 @@ void hexagon_cpu_do_interrupt(CPUState *cs) switch (env->cause_code) { case HEX_CAUSE_FETCH_NO_XPAGE: case HEX_CAUSE_FETCH_NO_UPAGE: + case HEX_CAUSE_CFI_VIOLATION: case HEX_CAUSE_PRIV_NO_READ: case HEX_CAUSE_PRIV_NO_UREAD: case HEX_CAUSE_PRIV_NO_WRITE: diff --git a/target/hexagon/imported/alu.idef b/target/hexagon/imported/alu.idef index b855676989..7c039ab018 100644 --- a/target/hexagon/imported/alu.idef +++ b/target/hexagon/imported/alu.idef @@ -241,6 +241,9 @@ Q6INSN(A2_minup,"Rdd32=minu(Rtt32,Rss32)",ATTRIBS(A_ARCHV3), Q6INSN(A2_nop,"nop",ATTRIBS(A_IT_NOP), "Nop (32-bit encoding)", fHIDE( { } )) +Q6INSN(A2_nop_rb,"nop_rb",ATTRIBS(A_IT_NOP,A_RESTRICT_TARGET), +"Restricted branch nop target", + fHIDE( { } )) Q6INSN(A4_ext,"immext(#u26:6)",ATTRIBS(A_IT_EXTENDER), diff --git a/target/hexagon/imported/branch.idef b/target/hexagon/imported/branch.idef index 93e2e375a5..b943a41a3f 100644 --- a/target/hexagon/imported/branch.idef +++ b/target/hexagon/imported/branch.idef @@ -37,6 +37,19 @@ Q6INSN(J2_jumpr,"jumpr Rs32",ATTRIBS(A_JINDIR), "indirect unconditional jump", Q6INSN(J2_jumprh,"jumprh Rs32",ATTRIBS(A_JINDIR, A_HINTED_COF), "indirect unconditional jump", {fJUMPR(RsN,RsV,COF_TYPE_JUMPR);}) +Q6INSN(J2_jumpr_rb,"jumpr_rb Rs32",ATTRIBS(A_JINDIR), + "restricted indirect unconditional jump", + {fJUMPR(RsN,RsV,COF_TYPE_JUMPR);}) +Q6INSN(J2_jumprh_rb,"jumprh_rb Rs32",ATTRIBS(A_JINDIR, A_HINTED_COF), + "restricted hinted indirect unconditional jump", + {fJUMPR(RsN,RsV,COF_TYPE_JUMPR);}) + +#define OLDCOND_JUMP_RB(TAG,OPER,OPER2,ATTRIB,DESCR,SEMANTICS) \ +Q6INSN(TAG##t_rb,"if (Pu4) "OPER":nt "OPER2,ATTRIB,DESCR,{fBRANCH_SPECULATE_STALL(fLSBOLD(PuV),,SPECULATE_NOT_TAKEN,12,0); if (fLSBOLD(PuV)) { SEMANTICS; }}) \ +Q6INSN(TAG##f_rb,"if (!Pu4) "OPER":nt "OPER2,ATTRIB,DESCR,{fBRANCH_SPECULATE_STALL(fLSBOLDNOT(PuV),,SPECULATE_NOT_TAKEN,12,0); if (fLSBOLDNOT(PuV)) { SEMANTICS; }}) \ +Q6INSN(TAG##tpt_rb,"if (Pu4) "OPER":t "OPER2,ATTRIB,DESCR,{fBRANCH_SPECULATE_STALL(fLSBOLD(PuV),,SPECULATE_TAKEN,12,0); if (fLSBOLD(PuV)) { SEMANTICS; }}) \ +Q6INSN(TAG##fpt_rb,"if (!Pu4) "OPER":t "OPER2,ATTRIB,DESCR,{fBRANCH_SPECULATE_STALL(fLSBOLDNOT(PuV),,SPECULATE_TAKEN,12,0); if (fLSBOLDNOT(PuV)) { SEMANTICS; }}) + #define OLDCOND_JUMP(TAG,OPER,OPER2,ATTRIB,DESCR,SEMANTICS) \ Q6INSN(TAG##t,"if (Pu4) "OPER":nt "OPER2,ATTRIB,DESCR,{fBRANCH_SPECULATE_STALL(fLSBOLD(PuV),,SPECULATE_NOT_TAKEN,12,0); if (fLSBOLD(PuV)) { SEMANTICS; }}) \ Q6INSN(TAG##f,"if (!Pu4) "OPER":nt "OPER2,ATTRIB,DESCR,{fBRANCH_SPECULATE_STALL(fLSBOLDNOT(PuV),,SPECULATE_NOT_TAKEN,12,0); if (fLSBOLDNOT(PuV)) { SEMANTICS; }}) \ @@ -48,6 +61,9 @@ fIMMEXT(riV);fPCALIGN(riV); fBRANCH(fREAD_PC()+riV,COF_TYPE_JUMP);) OLDCOND_JUMP(J2_jumpr,"jumpr","Rs32",ATTRIBS(A_JINDIROLD),"indirect conditional jump", fJUMPR(RsN,RsV,COF_TYPE_JUMPR);) +OLDCOND_JUMP_RB(J2_jumpr,"jumpr_rb","Rs32",ATTRIBS(A_JINDIROLD), + "restricted indirect conditional jump", + fJUMPR(RsN,RsV,COF_TYPE_JUMPR);) #define NEWCOND_JUMP(TAG,OPER,OPER2,ATTRIB,DESCR,SEMANTICS)\ Q6INSN(TAG##tnew,"if (Pu4.new) "OPER":nt "OPER2,ATTRIB,DESCR,{fBRANCH_SPECULATE_STALL(fLSBNEW(PuN),, SPECULATE_NOT_TAKEN , 12,0)} {if(fLSBNEW(PuN)){SEMANTICS;}})\ @@ -61,6 +77,17 @@ fIMMEXT(riV); fPCALIGN(riV); fBRANCH(fREAD_PC()+riV,COF_TYPE_JUMPNEW);) NEWCOND_JUMP(J2_jumpr,"jumpr","Rs32",ATTRIBS(A_JINDIRNEW,A_ARCHV3),"indirect conditional jump", fJUMPR(RsN,RsV,COF_TYPE_JUMPR);) +#define NEWCOND_JUMP_RB(TAG,OPER,OPER2,ATTRIB,DESCR,SEMANTICS) \ +Q6INSN(TAG##tnew_rb,"if (Pu4.new) "OPER":nt "OPER2,ATTRIB,DESCR,{fBRANCH_SPECULATE_STALL(fLSBNEW(PuN),, SPECULATE_NOT_TAKEN , 12,0)} {if(fLSBNEW(PuN)){SEMANTICS;}}) \ +Q6INSN(TAG##fnew_rb,"if (!Pu4.new) "OPER":nt "OPER2,ATTRIB,DESCR,{fBRANCH_SPECULATE_STALL(fLSBNEWNOT(PuN),, SPECULATE_NOT_TAKEN , 12,0)} {if(fLSBNEWNOT(PuN)){SEMANTICS;}}) \ +Q6INSN(TAG##tnewpt_rb,"if (Pu4.new) "OPER":t "OPER2,ATTRIB,DESCR,{fBRANCH_SPECULATE_STALL(fLSBNEW(PuN),, SPECULATE_TAKEN , 12,0)} {if(fLSBNEW(PuN)){SEMANTICS;}}) \ +Q6INSN(TAG##fnewpt_rb,"if (!Pu4.new) "OPER":t "OPER2,ATTRIB,DESCR,{fBRANCH_SPECULATE_STALL(fLSBNEWNOT(PuN),, SPECULATE_TAKEN , 12,0)} {if(fLSBNEWNOT(PuN)){SEMANTICS;}}) + +NEWCOND_JUMP_RB(J2_jumpr,"jumpr_rb","Rs32", + ATTRIBS(A_JINDIRNEW,A_ARCHV3), + "restricted indirect conditional jump", + fJUMPR(RsN,RsV,COF_TYPE_JUMPR);) + Q6INSN(J4_hintjumpr,"hintjr(Rs32)",ATTRIBS(A_JINDIR),"hint indirect conditional jump", @@ -202,6 +229,17 @@ Q6INSN(J2_callrf,"if (!Pu4) callr Rs32",ATTRIBS(CINDIR_STD),"indirect conditiona Q6INSN(J2_callrh,"callrh Rs32",ATTRIBS(CINDIR_STD, A_HINTED_COF), "hinted indirect unconditional call", { fCALLR(RsV); }) +Q6INSN(J2_callr_rb,"callr_rb Rs32",ATTRIBS(CINDIR_STD), + "restricted indirect unconditional call", { fCALLR(RsV); }) +Q6INSN(J2_callrh_rb,"callrh_rb Rs32",ATTRIBS(CINDIR_STD, A_HINTED_COF), + "restricted hinted indirect unconditional call", { fCALLR(RsV); }) +Q6INSN(J2_callrt_rb,"if (Pu4) callr_rb Rs32",ATTRIBS(CINDIR_STD), + "restricted indirect conditional call if true", + {if (fLSBOLD(PuV)) { fCALLR(RsV); }}) +Q6INSN(J2_callrf_rb,"if (!Pu4) callr_rb Rs32",ATTRIBS(CINDIR_STD), + "restricted indirect conditional call if false", + {if (fLSBOLDNOT(PuV)) { fCALLR(RsV); }}) + /*********************************************/ diff --git a/target/hexagon/imported/encode_pp.def b/target/hexagon/imported/encode_pp.def index ed1de09a40..97af0fcb39 100644 --- a/target/hexagon/imported/encode_pp.def +++ b/target/hexagon/imported/encode_pp.def @@ -413,6 +413,20 @@ DEF_ENC32(V2_vallocframe_imm,"01100000100-----PP001----iiiiiii") DEF_ENC32(V2_vallocframe_reg,"01100000100sssssPP010-----------") DEF_ENC32(V2_vdeallocframe_imm,"01101010100-----PP001----iiiiiii") DEF_ENC32(V2_vdeallocframe_reg,"01101001010sssssPP010-----------") +DEF_ENC32(J2_callr_rb,"01010000001sssssPP--------------") +DEF_ENC32(J2_callrh_rb,"01010000010sssssPP--------------") +DEF_ENC32(J2_callrt_rb,"01010001100sssssPP----uu--------") +DEF_ENC32(J2_callrf_rb,"01010001101sssssPP----uu--------") +DEF_ENC32(J2_jumpr_rb,"01010010000sssssPP--------------") +DEF_ENC32(J2_jumprh_rb,"01010010010sssssPP--------------") +DEF_ENC32(J2_jumprt_rb,"01010011110sssssPP-00-uu--------") +DEF_ENC32(J2_jumprf_rb,"01010011111sssssPP-00-uu--------") +DEF_ENC32(J2_jumprtpt_rb,"01010011110sssssPP-10-uu--------") +DEF_ENC32(J2_jumprfpt_rb,"01010011111sssssPP-10-uu--------") +DEF_ENC32(J2_jumprtnew_rb,"01010011110sssssPP-01-uu--------") +DEF_ENC32(J2_jumprfnew_rb,"01010011111sssssPP-01-uu--------") +DEF_ENC32(J2_jumprtnewpt_rb,"01010011110sssssPP-11-uu--------") +DEF_ENC32(J2_jumprfnewpt_rb,"01010011111sssssPP-11-uu--------") DEF_ENC32(J2_trap2,"0101010011------PP-iiiii---iii--") @@ -514,6 +528,7 @@ DEF_ENC32(S2_pstorerdf_pi_nt,"10101010110xxxxxPP1ttttt0iiii1vv") /* x bus/cache */ /* x store/cache */ DEF_ENC32(S2_allocframe, ICLASS_ST" 000 01 00xxxxx PP000iii iiiiiiii") +DEF_ENC32(S2_allocframe_rb,ICLASS_ST" 000 01 00xxxxx PP100iii iiiiiiii") DEF_ENC32(Y5_l2locka, ICLASS_ST" 000 01 11sssss PP1----- ------dd") DEF_ENC32(Y2_dczeroa, ICLASS_ST" 000 01 10sssss PP0----- --------") DEF_ENC32(Y2_dczeroa_nt,"10100000110sssssPP1-------------") @@ -1407,7 +1422,8 @@ DEF_ENC32(C2_cmoveif, ICLASS_ALU2op" 1110 1uu0iiii PP0iiiii iiiddddd") DEF_FIELDROW_DESC32( ICLASS_ALU2op" 1111 -------- PP------ --------","[#15] nop") -DEF_ENC32(A2_nop, ICLASS_ALU2op" 1111 -------- PP------ --------") +DEF_ENC32(A2_nop, ICLASS_ALU2op" 1111 0------- PP------ --------") +DEF_ENC32(A2_nop_rb, ICLASS_ALU2op" 1111 1------- PP------ --------") diff --git a/target/hexagon/imported/encode_subinsn.def b/target/hexagon/imported/encode_subinsn.def index 742fb50efb..4ff3e95a25 100644 --- a/target/hexagon/imported/encode_subinsn.def +++ b/target/hexagon/imported/encode_subinsn.def @@ -71,6 +71,7 @@ DEF_ENC_SUBINSN(SS2_storebi0, SUBINSN_S2, "100 10ssssiiii") DEF_ENC_SUBINSN(SS2_storebi1, SUBINSN_S2, "100 11ssssiiii") DEF_ENC_SUBINSN(SS2_allocframe, SUBINSN_S2, "111 0iiiii----") +DEF_ENC_SUBINSN(SS2_allocframe_rb,SUBINSN_S2, "111 1iiiii----") diff --git a/target/hexagon/imported/ldst.idef b/target/hexagon/imported/ldst.idef index 48cda64e0b..1e176ccc46 100644 --- a/target/hexagon/imported/ldst.idef +++ b/target/hexagon/imported/ldst.idef @@ -135,6 +135,8 @@ STD_ST_AMODES(storerhnew, "Nt8.new", "memh","Store Half integer",ATTRIBS(A_REGWR Q6INSN(S2_allocframe,"allocframe(Rx32,#u11:3):raw", ATTRIBS(A_REGWRSIZE_8B,A_MEMSIZE_8B,A_STORE,A_RESTRICT_SLOT0ONLY), "Allocate stack frame", { fEA_RI(RxV,-8); fSTORE(1,8,EA,fFRAME_SCRAMBLE((fCAST8_8u(fREAD_LR()) << 32) | fCAST4_4u(fREAD_FP()))); fWRITE_FP(EA); fFRAMECHECK(EA-uiV,EA); RxV = EA-uiV; }) +Q6INSN(S2_allocframe_rb,"allocframe_rb(Rx32,#u11:3):raw", ATTRIBS(A_REGWRSIZE_8B,A_MEMSIZE_8B,A_STORE,A_RESTRICT_SLOT0ONLY,A_RESTRICT_TARGET), "Allocate stack frame", +{ fEA_RI(RxV,-8); fSTORE(1,8,EA,fFRAME_SCRAMBLE((fCAST8_8u(fREAD_LR()) << 32) | fCAST4_4u(fREAD_FP()))); fWRITE_FP(EA); fFRAMECHECK(EA-uiV,EA); RxV = EA-uiV; }) #define A_RETURN A_RESTRICT_COF_MAX1,A_RESTRICT_SLOT0ONLY,A_RESTRICT_NOSLOT1_STORE,A_RET_TYPE,A_DEALLOCRET /**** Load Acquire Store Release Instructions****/ diff --git a/target/hexagon/imported/subinsns.idef b/target/hexagon/imported/subinsns.idef index be0ae8779d..a4be385f58 100644 --- a/target/hexagon/imported/subinsns.idef +++ b/target/hexagon/imported/subinsns.idef @@ -147,3 +147,5 @@ Q6INSN(SS2_storebi1, "memb(Rs16+#u4:0)=#1", ATTRIBS(A_MEMSIZE_1B,A_STORE,A_SUB Q6INSN(SS2_allocframe,"allocframe(#u5:3)", ATTRIBS(A_REGWRSIZE_8B,A_SUBINSN,A_MEMSIZE_8B,A_STORE,A_RESTRICT_SLOT0ONLY), "Allocate stack frame", { fEA_RI(fREAD_SP(),-8); fSTORE(1,8,EA,fFRAME_SCRAMBLE((fCAST8_8u(fREAD_LR()) << 32) | fCAST4_4u(fREAD_FP()))); fWRITE_FP(EA); fFRAMECHECK(EA-uiV,EA); fWRITE_SP(EA-uiV); }) +Q6INSN(SS2_allocframe_rb,"allocframe_rb(#u5:3)", ATTRIBS(A_REGWRSIZE_8B,A_SUBINSN,A_MEMSIZE_8B,A_STORE,A_RESTRICT_SLOT0ONLY,A_RESTRICT_TARGET), "Allocate stack frame", +{ fEA_RI(fREAD_SP(),-8); fSTORE(1,8,EA,fFRAME_SCRAMBLE((fCAST8_8u(fREAD_LR()) << 32) | fCAST4_4u(fREAD_FP()))); fWRITE_FP(EA); fFRAMECHECK(EA-uiV,EA); fWRITE_SP(EA-uiV); }) diff --git a/target/hexagon/imported/system.idef b/target/hexagon/imported/system.idef index 80fa0cd561..f279c52b7d 100644 --- a/target/hexagon/imported/system.idef +++ b/target/hexagon/imported/system.idef @@ -48,7 +48,7 @@ Q6INSN(J2_trap1,"trap1(Rx32,#u8)",ATTRIBS(A_COF,A_NOTE_NOPACKET,A_RESTRICT_NOPAC fVIRTINSN_SPSWAP(uiV,RxV); }) -Q6INSN(J2_trap2,"trap2(#u8)",ATTRIBS(A_COF,A_RESTRICT_NOPACKET), +Q6INSN(J2_trap2,"trap2(#u8)",ATTRIBS(A_COF,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET), "Trap to Operating System", { fTRAP(2,uiV); }) Q6INSN(J2_pause,"pause(#u8)",ATTRIBS(A_COF,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET), diff --git a/target/hexagon/machine.c b/target/hexagon/machine.c index 36fee33d33..6a41551d8f 100644 --- a/target/hexagon/machine.c +++ b/target/hexagon/machine.c @@ -21,7 +21,7 @@ static int hexagon_cpu_post_load(void *opaque, int version_id) const VMStateDescription vmstate_hexagon_cpu = { .name = "cpu", - .version_id = 2, + .version_id = 3, .minimum_version_id = 2, .post_load = hexagon_cpu_post_load, .fields = (const VMStateField[]) { @@ -37,6 +37,7 @@ const VMStateDescription vmstate_hexagon_cpu = { VMSTATE_UINT32(env.cause_code, HexagonCPU), VMSTATE_UNUSED(sizeof(uint64_t)), VMSTATE_UINT32(env.imprecise_exception, HexagonCPU), + VMSTATE_UINT32_V(env.cfi_active, HexagonCPU, 3), VMSTATE_END_OF_LIST() }, diff --git a/target/hexagon/macros.h b/target/hexagon/macros.h index a5b9c24c6b..195bd6a0c9 100644 --- a/target/hexagon/macros.h +++ b/target/hexagon/macros.h @@ -423,6 +423,18 @@ static inline TCGv gen_read_ireg(TCGv result, TCGv val, int shift) #define fCAST8S_16S(A) (int128_exts64(A)) #define fCAST16S_8S(A) (int128_getlo(A)) +#define fSET_CFI(CFI_VALUE) \ + do { \ + TCGLabel *skip_cfi_reset = gen_new_label(); \ + tcg_gen_movcond_i32(TCG_COND_NE, hex_cfi_active_log, hex_cfi_updated, \ + tcg_constant_i32(1), tcg_constant_i32(0), \ + hex_cfi_active_log); \ + tcg_gen_movi_i32(hex_cfi_updated, 1); \ + tcg_gen_brcondi_i32(TCG_COND_EQ, CFI_VALUE, 0, skip_cfi_reset); \ + tcg_gen_movi_i32(hex_cfi_active_log, 1); \ + gen_set_label(skip_cfi_reset); \ + } while (0) + #ifdef QEMU_GENERATE #define fEA_RI(REG, IMM) tcg_gen_addi_tl(EA, REG, IMM) #define fEA_RRs(REG, REG2, SCALE) \ diff --git a/target/hexagon/reg_fields_def.h.inc b/target/hexagon/reg_fields_def.h.inc index 553f6ffffb..21babbbb98 100644 --- a/target/hexagon/reg_fields_def.h.inc +++ b/target/hexagon/reg_fields_def.h.inc @@ -95,6 +95,7 @@ DEF_REG_FIELD(SYSCFG_L2GCA, 31, 1) /* SSR fields */ DEF_REG_FIELD(SSR_CAUSE, 0, 8) DEF_REG_FIELD(SSR_ASID, 8, 7) +DEF_REG_FIELD(SSR_CFI, 15, 1) DEF_REG_FIELD(SSR_UM, 16, 1) DEF_REG_FIELD(SSR_EX, 17, 1) DEF_REG_FIELD(SSR_IE, 18, 1) diff --git a/target/hexagon/tag_rev_info.c.inc b/target/hexagon/tag_rev_info.c.inc index c2d4886a84..88958b334d 100644 --- a/target/hexagon/tag_rev_info.c.inc +++ b/target/hexagon/tag_rev_info.c.inc @@ -707,6 +707,26 @@ static const struct tag_rev_info tag_rev_info[XX_LAST_OPCODE] = { .removed = HEX_VER_NONE }, [V2_vdeallocframe_reg] = { .introduced = HEX_VER_V83, .removed = HEX_VER_NONE }, + [J2_callr_rb] = { .introduced = HEX_VER_V83, .removed = HEX_VER_NONE }, + [J2_callrh_rb] = { .introduced = HEX_VER_V83, .removed = HEX_VER_NONE }, + [J2_callrt_rb] = { .introduced = HEX_VER_V83, .removed = HEX_VER_NONE }, + [J2_callrf_rb] = { .introduced = HEX_VER_V83, .removed = HEX_VER_NONE }, + [J2_jumpr_rb] = { .introduced = HEX_VER_V83, .removed = HEX_VER_NONE }, + [J2_jumprh_rb] = { .introduced = HEX_VER_V83, .removed = HEX_VER_NONE }, + [J2_jumprt_rb] = { .introduced = HEX_VER_V83, .removed = HEX_VER_NONE }, + [J2_jumprf_rb] = { .introduced = HEX_VER_V83, .removed = HEX_VER_NONE }, + [J2_jumprtpt_rb] = { .introduced = HEX_VER_V83, .removed = HEX_VER_NONE }, + [J2_jumprfpt_rb] = { .introduced = HEX_VER_V83, .removed = HEX_VER_NONE }, + [J2_jumprtnew_rb] = { .introduced = HEX_VER_V83, .removed = HEX_VER_NONE }, + [J2_jumprfnew_rb] = { .introduced = HEX_VER_V83, .removed = HEX_VER_NONE }, + [J2_jumprtnewpt_rb] = { .introduced = HEX_VER_V83, + .removed = HEX_VER_NONE }, + [J2_jumprfnewpt_rb] = { .introduced = HEX_VER_V83, + .removed = HEX_VER_NONE }, + [S2_allocframe_rb] = { .introduced = HEX_VER_V83, .removed = HEX_VER_NONE }, + [SS2_allocframe_rb] = { .introduced = HEX_VER_V83, + .removed = HEX_VER_NONE }, + [A2_nop_rb] = { .introduced = HEX_VER_V83, .removed = HEX_VER_NONE }, [J2_trap2] = { .introduced = HEX_VER_V83, .removed = HEX_VER_NONE }, [Y4_dcfetch_multi] = { .introduced = HEX_VER_V85, diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index 39ea6172b3..45cbddb6bb 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -68,6 +68,9 @@ TCGv hex_imprecise_exception; TCGv hex_vstore_addr[VSTORES_MAX]; TCGv hex_vstore_size[VSTORES_MAX]; TCGv hex_vstore_pending[VSTORES_MAX]; +TCGv_i32 hex_cfi_active; +TCGv_i32 hex_cfi_active_log; +TCGv_i32 hex_cfi_updated; #ifndef CONFIG_USER_ONLY TCGv_ptr hex_hvx_ptr; #endif @@ -480,6 +483,45 @@ static void mark_implicit_reg_write(DisasContext *ctx, int attrib, int rnum) } } +static bool pkt_legal_cfi_target(const Packet *pkt) +{ + int i; + + for (i = 0; i < pkt->num_insns; i++) { + if (GET_ATTRIB(pkt->insn[i].opcode, A_RESTRICT_TARGET) || + pkt->insn[i].opcode == Y2_break || + (pkt->insn[i].opcode == J2_trap0 && + pkt->insn[i].immed[0] == 0xdb)) { + return true; + } + } + return false; +} + +static void gen_cfi_check(DisasContext *ctx) +{ + TCGLabel *skip; + + if (ctx->hex_def->hex_version < HEX_VER_V83 || + pkt_legal_cfi_target(&ctx->pkt)) { + return; + } + + skip = gen_new_label(); + tcg_gen_brcondi_i32(TCG_COND_EQ, hex_cfi_active, 0, skip); + gen_precise_exception(HEX_CAUSE_CFI_VIOLATION, ctx->pkt.pc); + gen_set_label(skip); +} + +static void gen_cfi_target_update(DisasContext *ctx) +{ + if (ctx->hex_def->hex_version >= HEX_VER_V83 && + pkt_legal_cfi_target(&ctx->pkt)) { + tcg_gen_movi_i32(hex_cfi_active_log, 0); + tcg_gen_movi_i32(hex_cfi_updated, 1); + } +} + static void mark_implicit_usr_write(DisasContext *ctx, int attrib) { uint16_t opcode = ctx->insn->opcode; @@ -748,6 +790,9 @@ static void gen_start_packet(DisasContext *ctx) tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], ctx->next_PC); } + gen_cfi_target_update(ctx); + gen_cfi_check(ctx); + /* Preload the predicated registers into get_result_gpr(ctx, i) */ if (ctx->need_commit && !bitmap_empty(ctx->predicated_regs, TOTAL_PER_THREAD_REGS)) { @@ -1269,6 +1314,14 @@ static void gen_commit_packet(DisasContext *ctx) gen_helper_probe_pkt_scalar_store_s0(tcg_env, args_tcgv); } + if (ctx->hex_def->hex_version >= HEX_VER_V83) { + /* A restricted branch commits before a faulting store in its packet. */ + tcg_gen_movcond_i32(TCG_COND_NE, hex_cfi_active, hex_cfi_updated, + tcg_constant_i32(0), hex_cfi_active_log, + hex_cfi_active); + tcg_gen_movi_i32(hex_cfi_updated, 0); + } + process_store_log(ctx); gen_reg_writes(ctx); @@ -1518,6 +1571,12 @@ void hexagon_translate_init(void) } hex_new_value_usr = tcg_global_mem_new(tcg_env, offsetof(CPUHexagonState, new_value_usr), "new_value_usr"); + hex_cfi_active = tcg_global_mem_new_i32(tcg_env, + offsetof(CPUHexagonState, cfi_active), "cfi_active"); + hex_cfi_active_log = tcg_global_mem_new_i32(tcg_env, + offsetof(CPUHexagonState, cfi_active_log), "cfi_active_log"); + hex_cfi_updated = tcg_global_mem_new_i32(tcg_env, + offsetof(CPUHexagonState, cfi_updated), "cfi_updated"); for (i = 0; i < NUM_PREGS; i++) { hex_pred[i] = tcg_global_mem_new(tcg_env, diff --git a/target/hexagon/translate.h b/target/hexagon/translate.h index 54a44b14c6..bc5f202163 100644 --- a/target/hexagon/translate.h +++ b/target/hexagon/translate.h @@ -365,6 +365,9 @@ extern TCGv_i64 hex_llsc_val_i64; extern TCGv hex_vstore_addr[VSTORES_MAX]; extern TCGv hex_vstore_size[VSTORES_MAX]; extern TCGv hex_vstore_pending[VSTORES_MAX]; +extern TCGv_i32 hex_cfi_active; +extern TCGv_i32 hex_cfi_active_log; +extern TCGv_i32 hex_cfi_updated; void gen_precise_exception(int cause, uint32_t pc); #ifdef CONFIG_USER_ONLY #define hex_hvx_ptr tcg_env