Message ID | 20230209062404.3582018-9-debug@rivosinc.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [v1,RFC,Zisslpcfi,1/9] target/riscv: adding zimops and zisslpcfi extension to RISCV cpu config | expand |
On 2023/2/9 14:24, Deepak Gupta wrote: > This patch implements instruction encodings for zisslpcfi instructions. > Additionally this patch implements zimops encodings as well. If Zisslpcfi > is supported by CPU but not enabled then all Zisslpcfi instructions > default to Zimops instuction behavior i.e. mov 0 to rd. > > zisslpcfi defines following instructions. > - Backward control flow > - sspush x1/x5 : Decrement shadow stack pointer and pushes x1 or x5 > on shadow stack. > - sspop x1/x5 : Pops from shadow stack into x1 or x5. Increments > shadow stack pointer. > - ssprr : Reads current shadow stack pointer into a destination > register. > - sscheckra : Compares x1 with x5. Raises illegal instr fault if > x1 != x5 > - ssamoswap : Atomically swaps value on top of shadow stack > > - Forward control flow > - lpsll, lpsml, lpsul : sets lower (9bit), mid (8bit) and upper > (8bit) label values in CSR_ULLP respectively. > - lpcll, lpcml, lpcul : checks lower (9bit), mid (8bit) and upper > (8bit) label values with CSR_ULLP > respectively. > Check label instructions raise illegal instruction fault when labels > mismatch. > > Signed-off-by: Deepak Gupta <debug@rivosinc.com> > Signed-off-by: Kip Walker <kip@rivosinc.com> > --- > target/riscv/cpu_bits.h | 10 + > target/riscv/helper.h | 7 + > target/riscv/insn32.decode | 29 ++ > target/riscv/insn_trans/trans_rvi.c.inc | 14 + > target/riscv/insn_trans/trans_zimops.c.inc | 53 +++ > target/riscv/insn_trans/trans_zisslpcfi.c.inc | 310 ++++++++++++++++++ > target/riscv/op_helper.c | 67 ++++ > target/riscv/translate.c | 2 + > 8 files changed, 492 insertions(+) > create mode 100644 target/riscv/insn_trans/trans_zimops.c.inc > create mode 100644 target/riscv/insn_trans/trans_zisslpcfi.c.inc > > diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h > index 37100ec8f6..b2d527c626 100644 > --- a/target/riscv/cpu_bits.h > +++ b/target/riscv/cpu_bits.h > @@ -600,6 +600,16 @@ typedef enum { > LP_EXPECTED = 1, > } cfi_elp; > > +#define LPLR_UL (((1 << 8) - 1) << 17) > +#define LPLR_ML (((1 << 8) - 1) << 9) > +#define LPLR_LL ((1 << 9) - 1) > + > +typedef enum { > + FCFI_LPLL = 0, > + FCFI_ML = 1, > + FCFI_UL = 2, > +} cfi_label_inst; > + > /* hstatus CSR bits */ > #define HSTATUS_VSBE 0x00000020 > #define HSTATUS_GVA 0x00000040 > diff --git a/target/riscv/helper.h b/target/riscv/helper.h > index 227c7122ef..6484415612 100644 > --- a/target/riscv/helper.h > +++ b/target/riscv/helper.h > @@ -97,6 +97,11 @@ DEF_HELPER_FLAGS_2(fcvt_h_l, TCG_CALL_NO_RWG, i64, env, tl) > DEF_HELPER_FLAGS_2(fcvt_h_lu, TCG_CALL_NO_RWG, i64, env, tl) > DEF_HELPER_FLAGS_2(fclass_h, TCG_CALL_NO_RWG_SE, tl, env, i64) > > +/* Forward CFI label checking */ > +DEF_HELPER_2(cfi_jalr, void, env, int) > +DEF_HELPER_3(cfi_check_landing_pad, void, env, int, int) > +DEF_HELPER_3(cfi_set_landing_pad, void, env, int, int) > + > /* Special functions */ > DEF_HELPER_2(csrr, tl, env, int) > DEF_HELPER_3(csrw, void, env, int, tl) > @@ -112,6 +117,8 @@ DEF_HELPER_1(tlb_flush, void, env) > /* Native Debug */ > DEF_HELPER_1(itrigger_match, void, env) > #endif > +/* helper for back cfi mismatch */ > +DEF_HELPER_1(sschkra_mismatch, void, env) > > /* Hypervisor functions */ > #ifndef CONFIG_USER_ONLY > diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode > index b7e7613ea2..cd734f03ae 100644 > --- a/target/riscv/insn32.decode > +++ b/target/riscv/insn32.decode > @@ -37,6 +37,8 @@ > %imm_u 12:s20 !function=ex_shift_12 > %imm_bs 30:2 !function=ex_shift_3 > %imm_rnum 20:4 > +%imm_cfi9 15:9 > +%imm_cfi8 15:8 > > # Argument sets: > &empty > @@ -163,6 +165,33 @@ csrrwi ............ ..... 101 ..... 1110011 @csr > csrrsi ............ ..... 110 ..... 1110011 @csr > csrrci ............ ..... 111 ..... 1110011 @csr > > +# zimops (unpriv integer may be operations) instructions with system opcode > +# These're superset of for cfi encodings. zimops_r and zimops_rr should be last > +# entry in below overlapping patterns so that it acts as final sink for overlapping patterns. > +# Any new encoding that can be used should be placed above mop.r and mop.rr > + > +# cfi instructions carved out of mop.r > +{ > + sspush 100000 0 11100 ..... 100 00000 1110011 %rs1 > + sspop 100000 0 11100 00000 100 ..... 1110011 %rd > + ssprr 100000 0 11101 00000 100 ..... 1110011 %rd > + zimops_r 1-00-- 0 111-- ----- 100 ..... 1110011 %rd > +} > + > +# cfi instructions carved out of mop.rr > +{ > + sschckra 100010 1 00001 00101 100 00000 1110011 > + ssamoswap 100000 1 ..... ..... 100 ..... 1110011 @r > + > + lpsll 100000 1 0 ......... 100 00000 1110011 %imm_cfi9 > + lpcll 100000 1 1 ......... 100 00000 1110011 %imm_cfi9 > + lpsml 100001 1 0 0........ 100 00000 1110011 %imm_cfi8 > + lpcml 100001 1 0 1........ 100 00000 1110011 %imm_cfi8 > + lpsul 100010 1 1 0........ 100 00000 1110011 %imm_cfi8 > + lpcul 100010 1 1 1........ 100 00000 1110011 %imm_cfi8 > + zimops_rr 1-00-- 1 - --------- 100 ..... 1110011 %rd > +} > + > # *** RV64I Base Instruction Set (in addition to RV32I) *** > lwu ............ ..... 110 ..... 0000011 @i > ld ............ ..... 011 ..... 0000011 @i > diff --git a/target/riscv/insn_trans/trans_rvi.c.inc b/target/riscv/insn_trans/trans_rvi.c.inc > index 5c69b88d1e..4a4f1ca778 100644 > --- a/target/riscv/insn_trans/trans_rvi.c.inc > +++ b/target/riscv/insn_trans/trans_rvi.c.inc > @@ -66,6 +66,20 @@ static bool trans_jalr(DisasContext *ctx, arg_jalr *a) > } > > gen_set_gpri(ctx, a->rd, ctx->pc_succ_insn); > + > + if (ctx->cfg_ptr->ext_cfi) { > + /* > + * Rely on a helper to check the forward CFI enable for the > + * current process mode. The alternatives would be (1) include > + * "fcfi enabled" in the cflags or (2) maintain a "fcfi > + * currently enabled" in cpu_env and emit TCG code to access > + * and test it. > + */ > + if (a->rd == xRA || a->rd == xT0 || (a->rs1 != xRA && a->rs1 != xT0)) { > + gen_helper_cfi_jalr(cpu_env, tcg_constant_i32(LP_EXPECTED)); > + } > + } > + > lookup_and_goto_ptr(ctx); > > if (misaligned) { > diff --git a/target/riscv/insn_trans/trans_zimops.c.inc b/target/riscv/insn_trans/trans_zimops.c.inc > new file mode 100644 > index 0000000000..51748637b9 > --- /dev/null > +++ b/target/riscv/insn_trans/trans_zimops.c.inc > @@ -0,0 +1,53 @@ > +/* > + * RISC-V translation routines for the Control-Flow Integrity Extension > + * > + * Copyright (c) 2022-2023 Rivos Inc. > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms and conditions of the GNU General Public License, > + * version 2 or later, as published by the Free Software Foundation. > + * > + * This program is distributed in the hope it will be useful, but WITHOUT > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for > + * more details. > + * > + * You should have received a copy of the GNU General Public License along with > + * this program. If not, see <http://www.gnu.org/licenses/>. > + */ > + > + static bool trans_zimops_r(DisasContext *ctx, arg_zimops_r * a) > + { > + /* zimops not implemented, raise illegal instruction & return true */ > + if (!ctx->cfg_ptr->ext_zimops) { > + gen_exception_illegal(ctx); > + return true; > + } > + > + /* > + * zimops implemented, simply grab destination and mov zero. > + * return true > + */ > + TCGv dest = dest_gpr(ctx, a->rd); > + dest = tcg_const_tl(0); > + gen_set_gpr(ctx, a->rd, dest); These three statements can be reduced to gen_set_gpr(ctx, a->rd, ctx->zero) > + return true; > + } > + > + static bool trans_zimops_rr(DisasContext *ctx, arg_zimops_rr * a) > + { > + /* zimops not implemented, raise illegal instruction & return true */ > + if (!ctx->cfg_ptr->ext_zimops) { > + gen_exception_illegal(ctx); > + return true; > + } > + > + /* > + * zimops implemented, simply grab destination and mov zero. > + * return true > + */ > + TCGv dest = dest_gpr(ctx, a->rd); > + dest = tcg_const_tl(0); > + gen_set_gpr(ctx, a->rd, dest); Same here. > + return true; > + } > diff --git a/target/riscv/insn_trans/trans_zisslpcfi.c.inc b/target/riscv/insn_trans/trans_zisslpcfi.c.inc > new file mode 100644 > index 0000000000..fe27bb73f6 > --- /dev/null > +++ b/target/riscv/insn_trans/trans_zisslpcfi.c.inc > @@ -0,0 +1,310 @@ > +/* > + * RISC-V translation routines for the Control-Flow Integrity Extension > + * > + * Copyright (c) 2022-2023 Rivos Inc. > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms and conditions of the GNU General Public License, > + * version 2 or later, as published by the Free Software Foundation. > + * > + * This program is distributed in the hope it will be useful, but WITHOUT > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for > + * more details. > + * > + * You should have received a copy of the GNU General Public License along with > + * this program. If not, see <http://www.gnu.org/licenses/>. > + */ > + > +static MemOp mxl_memop(DisasContext *ctx) > +{ > + switch (get_xl(ctx)) { > + case MXL_RV32: > + return MO_TEUL; > + > + case MXL_RV64: > + return MO_TEUQ; > + > + case MXL_RV128: > + return MO_TEUO; > + > + default: > + g_assert_not_reached(); > + } > +} > + > +static bool trans_sspop(DisasContext *ctx, arg_sspop *a) > +{ > + /* cfi only supported on 32bit and 64bit */ > + if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) { > + return false; > + } > + > + /* back cfi not enabled, should go to trans_zimops. return false */ > + if (!ctx->bcfi_enabled) { > + return false; > + } > + > + /* sspop can only load into x1 or x5. Everything else defaults to zimops */ > + if (a->rd != 1 && a->rd != 5) { > + return false; > + } > + rd can't be x0. > + /* > + * get data in TCGv using get_gpr > + * get addr in TCGv using gen_helper_csrr on CSR_SSP > + * use some tcg subtract arithmetic (subtract by XLEN) on addr > + * perform ss store on computed address > + */ > + > + TCGv addr = tcg_temp_new(); > + int tmp = (get_xl(ctx) == MXL_RV64) ? 8 : 4; > + TCGv_i32 ssp_csr = tcg_constant_i32(CSR_SSP); > + TCGv data = get_gpr(ctx, a->rd, EXT_NONE); > + gen_helper_csrr(addr, cpu_env, ssp_csr); tcg_gen_ld_tl can directly load env->ssp > + tcg_gen_qemu_ld_tl(data, addr, MMU_IDX_SS_ACCESS, > + mxl_memop(ctx) | MO_ALIGN); > + > + /* > + * add XLEN/bitwidth to addr, align to XLEN . How do i do that? Is below > + * the right way > + */ > + tcg_gen_addi_tl(addr, addr, tmp); > + gen_set_gpr(ctx, a->rd, data); > + gen_helper_csrw(cpu_env, ssp_csr, addr); tcg_gen_st_tl > + LeakĀ the addr tmp. Zhiwei > + return true; > +} > + > +static bool trans_sspush(DisasContext *ctx, arg_sspush *a) > +{ > + /* cfi only supported on 32bit and 64bit */ > + if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) { > + return false; > + } > + > + /* back cfi not enabled, should go to trans_zimops. return false */ > + if (!ctx->bcfi_enabled) { > + return false; > + } > + > + /* > + * sspush can only push from x1 or x5. Everything else defaults to zimops > + */ > + if (a->rs1 != 1 && a->rs1 != 5) { > + return false; > + } > + rs1 != 0 > + /* > + * get data in TCGv using get_gpr > + * get addr in TCGv using gen_helper_csrr on CSR_SSP > + * use some tcg subtract arithmetic (subtract by XLEN) on addr > + * perform ss store on computed address > + */ > + > + TCGv addr = tcg_temp_new(); > + int tmp = (get_xl(ctx) == MXL_RV64) ? -8 : -4; > + TCGv_i32 ssp_csr = tcg_constant_i32(CSR_SSP); > + TCGv data = get_gpr(ctx, a->rs1, EXT_NONE); > + gen_helper_csrr(addr, cpu_env, ssp_csr); > + > + /* > + * subtract XLEN from addr, align to XLEN . How do i do that? Is below the > + * right way > + */ > + tcg_gen_addi_tl(addr, addr, tmp); > + tcg_gen_qemu_st_tl(data, addr, MMU_IDX_SS_ACCESS, > + mxl_memop(ctx) | MO_ALIGN); > + > + gen_helper_csrw(cpu_env, ssp_csr, addr); > + > + return true; > +} > + > +static bool trans_sschckra(DisasContext *ctx, arg_sschckra *a) > +{ > + /* cfi only supported on 32bit and 64bit */ > + if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) { > + return false; > + } > + > + /* back cfi not enabled, should go to trans_zimops. return false */ > + if (!ctx->bcfi_enabled) { > + return false; > + } > + > + gen_helper_sschkra_mismatch(cpu_env); > + > + return true; > +} > + > +static bool trans_ssprr(DisasContext *ctx, arg_ssprr *a) > +{ > + /* cfi only supported on 32bit and 64bit */ > + if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) { > + return false; > + } > + > + /* back cfi not enabled, should go to trans_zimops. return false */ > + if (!ctx->bcfi_enabled) { > + return false; > + } > + > + TCGv dest = get_gpr(ctx, a->rd, EXT_NONE); > + TCGv_i32 ssp_csr = tcg_constant_i32(CSR_SSP); > + gen_helper_csrr(dest, cpu_env, ssp_csr); > + gen_set_gpr(ctx, a->rd, dest); > + > + return true; > +} > + > +static bool trans_ssamoswap(DisasContext *ctx, arg_ssamoswap *a) > +{ > + /* cfi only supported on 32bit and 64bit */ > + if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) { > + return false; > + } > + > + /* back cfi not enabled, should go to trans_zimops. return false */ > + if (!ctx->bcfi_enabled) { > + return false; > + } > + > + /* If cfi is enabled then, then rd must be != 0 */ > + > + if (a->rd == 0) { > + return false; > + } > + > + TCGv dest = dest_gpr(ctx, a->rd); > + TCGv src1 = get_address(ctx, a->rs1, 0); > + TCGv src2 = get_gpr(ctx, a->rs2, EXT_NONE); > + MemOp mop = (MO_ALIGN | ((get_xl(ctx) == MXL_RV32) ? MO_TESL : MO_TESQ)); > + > + tcg_gen_atomic_xchg_tl(dest, src1, src2, MMU_IDX_SS_ACCESS, mop); > + gen_set_gpr(ctx, a->rd, dest); > + return true; > +} > + > +static bool trans_lpcll(DisasContext *ctx, arg_lpcll *a) > +{ > + /* cfi only supported on 32bit and 64bit */ > + if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) { > + return false; > + } > + > + /* > + * If this is the first instruction of the TB, let the translator > + * know the landing pad requirement was satisfied. No need to bother > + * checking for CFI feature or enablement. > + */ > + > + if (ctx->base.pc_next == ctx->base.pc_first) { > + ctx->fcfi_lp_expected = false; > + /* PC must be 4 byte aligned */ > + if (ctx->fcfi_enabled && ((ctx->base.pc_next) & 0x3)) { > + /* > + * misaligned, according to spec we should raise illegal instead > + * of mis-aligned > + */ > + gen_exception_illegal(ctx); > + } > + } > + > + /* forward cfi not enabled, should go to trans_zimops. return false */ > + if (!ctx->fcfi_enabled) { > + return false; > + } > + > + gen_helper_cfi_check_landing_pad(cpu_env, tcg_constant_i32(a->imm_cfi9), > + tcg_constant_i32(FCFI_LPLL)); > + return true; > +} > + > +static bool trans_lpcml(DisasContext *ctx, arg_lpcml *a) > +{ > + /* cfi only supported on 32bit and 64bit */ > + if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) { > + return false; > + } > + > + /* forward cfi not enabled, should go to trans_zimops. return false */ > + if (!ctx->fcfi_enabled) { > + return false; > + } > + > + gen_helper_cfi_check_landing_pad(cpu_env, tcg_constant_i32(a->imm_cfi8), > + tcg_constant_i32(FCFI_ML)); > + return true; > +} > + > +static bool trans_lpcul(DisasContext *ctx, arg_lpcul *a) > +{ > + /* cfi only supported on 32bit and 64bit */ > + if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) { > + return false; > + } > + > + /* forward cfi not enabled, should go to trans_zimops. return false */ > + if (!ctx->fcfi_enabled) { > + return false; > + } > + > + gen_helper_cfi_check_landing_pad(cpu_env, tcg_constant_i32(a->imm_cfi8), > + tcg_constant_i32(FCFI_UL)); > + return true; > +} > + > +static bool trans_lpsll(DisasContext *ctx, arg_lpsll *a) > +{ > + /* cfi only supported on 32bit and 64bit */ > + if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) { > + return false; > + } > + > + /* forward cfi not enabled, should go to trans_zimops. return false */ > + if (!ctx->fcfi_enabled) { > + return false; > + } > + > + gen_helper_cfi_set_landing_pad(cpu_env, tcg_constant_i32(a->imm_cfi9), > + tcg_constant_i32(FCFI_LPLL)); > + > + return true; > +} > + > +static bool trans_lpsml(DisasContext *ctx, arg_lpsml *a) > +{ > + /* cfi only supported on 32bit and 64bit */ > + if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) { > + return false; > + } > + > + /* forward cfi not enabled, should go to trans_zimops. return false */ > + if (!ctx->fcfi_enabled) { > + return false; > + } > + > + gen_helper_cfi_set_landing_pad(cpu_env, tcg_constant_i32(a->imm_cfi8), > + tcg_constant_i32(FCFI_ML)); > + > + return true; > +} > + > +static bool trans_lpsul(DisasContext *ctx, arg_lpsul *a) > +{ > + /* cfi only supported on 32bit and 64bit */ > + if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) { > + return false; > + } > + > + /* forward cfi not enabled, should go to trans_zimops. return false */ > + if (!ctx->fcfi_enabled) { > + return false; > + } > + > + gen_helper_cfi_set_landing_pad(cpu_env, tcg_constant_i32(a->imm_cfi8), > + tcg_constant_i32(FCFI_UL)); > + > + return true; > +} > diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c > index d15893aa82..c14b76aabb 100644 > --- a/target/riscv/op_helper.c > +++ b/target/riscv/op_helper.c > @@ -123,6 +123,73 @@ target_ulong helper_csrrw_i128(CPURISCVState *env, int csr, > return int128_getlo(rv); > } > > +void helper_sschkra_mismatch(CPURISCVState *env) > +{ > + if (env->gpr[xRA] != env->gpr[xT0]) { > + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); > + } > +} > + > +void helper_cfi_jalr(CPURISCVState *env, int elp) > +{ > + /* > + * The translation routine doesn't know if forward CFI is enabled > + * in the current processor mode or not. It's not worth burning a > + * cflags bit to encode this, or tracking the current-mode-fcfi > + * enable in a dedicated member of 'env'. Just come out to a helper > + * for jump/call on a core with CFI. > + */ > + if (cpu_get_fcfien(env)) { > + env->elp = elp; > + } > +} > + > +void helper_cfi_check_landing_pad(CPURISCVState *env, int lbl, int inst_type) > +{ > + if (cpu_get_fcfien(env)) { > + switch (inst_type) { > + case FCFI_LPLL: > + /* > + * Check for a lower label match. We already checked 4 byte > + * alignment in tcg > + */ > + if (lbl != get_field(env->lplr, LPLR_LL)) { > + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); > + } > + env->elp = NO_LP_EXPECTED; > + break; > + case FCFI_ML: > + if (lbl != get_field(env->lplr, LPLR_ML)) { > + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); > + } > + break; > + case FCFI_UL: > + if (lbl != get_field(env->lplr, LPLR_UL)) { > + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); > + } > + } > + } > +} > + > +void helper_cfi_set_landing_pad(CPURISCVState *env, int lbl, int inst_type) > +{ > + if (cpu_get_fcfien(env)) { > + switch (inst_type) { > + case FCFI_LPLL: > + /* setting lower label always clears up entire field */ > + env->lplr = 0; > + env->lplr = set_field(env->lplr, LPLR_LL, lbl); > + break; > + case FCFI_ML: > + env->lplr = set_field(env->lplr, LPLR_ML, lbl); > + break; > + case FCFI_UL: > + env->lplr = set_field(env->lplr, LPLR_UL, lbl); > + break; > + } > + } > +} > + > #ifndef CONFIG_USER_ONLY > > target_ulong helper_sret(CPURISCVState *env) > diff --git a/target/riscv/translate.c b/target/riscv/translate.c > index 7d43d20fc3..b1a965d192 100644 > --- a/target/riscv/translate.c > +++ b/target/riscv/translate.c > @@ -1071,6 +1071,8 @@ static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc) > #include "insn_trans/trans_privileged.c.inc" > #include "insn_trans/trans_svinval.c.inc" > #include "insn_trans/trans_xventanacondops.c.inc" > +#include "insn_trans/trans_zisslpcfi.c.inc" > +#include "insn_trans/trans_zimops.c.inc" > > /* Include the auto-generated decoder for 16 bit insn */ > #include "decode-insn16.c.inc"
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 37100ec8f6..b2d527c626 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -600,6 +600,16 @@ typedef enum { LP_EXPECTED = 1, } cfi_elp; +#define LPLR_UL (((1 << 8) - 1) << 17) +#define LPLR_ML (((1 << 8) - 1) << 9) +#define LPLR_LL ((1 << 9) - 1) + +typedef enum { + FCFI_LPLL = 0, + FCFI_ML = 1, + FCFI_UL = 2, +} cfi_label_inst; + /* hstatus CSR bits */ #define HSTATUS_VSBE 0x00000020 #define HSTATUS_GVA 0x00000040 diff --git a/target/riscv/helper.h b/target/riscv/helper.h index 227c7122ef..6484415612 100644 --- a/target/riscv/helper.h +++ b/target/riscv/helper.h @@ -97,6 +97,11 @@ DEF_HELPER_FLAGS_2(fcvt_h_l, TCG_CALL_NO_RWG, i64, env, tl) DEF_HELPER_FLAGS_2(fcvt_h_lu, TCG_CALL_NO_RWG, i64, env, tl) DEF_HELPER_FLAGS_2(fclass_h, TCG_CALL_NO_RWG_SE, tl, env, i64) +/* Forward CFI label checking */ +DEF_HELPER_2(cfi_jalr, void, env, int) +DEF_HELPER_3(cfi_check_landing_pad, void, env, int, int) +DEF_HELPER_3(cfi_set_landing_pad, void, env, int, int) + /* Special functions */ DEF_HELPER_2(csrr, tl, env, int) DEF_HELPER_3(csrw, void, env, int, tl) @@ -112,6 +117,8 @@ DEF_HELPER_1(tlb_flush, void, env) /* Native Debug */ DEF_HELPER_1(itrigger_match, void, env) #endif +/* helper for back cfi mismatch */ +DEF_HELPER_1(sschkra_mismatch, void, env) /* Hypervisor functions */ #ifndef CONFIG_USER_ONLY diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode index b7e7613ea2..cd734f03ae 100644 --- a/target/riscv/insn32.decode +++ b/target/riscv/insn32.decode @@ -37,6 +37,8 @@ %imm_u 12:s20 !function=ex_shift_12 %imm_bs 30:2 !function=ex_shift_3 %imm_rnum 20:4 +%imm_cfi9 15:9 +%imm_cfi8 15:8 # Argument sets: &empty @@ -163,6 +165,33 @@ csrrwi ............ ..... 101 ..... 1110011 @csr csrrsi ............ ..... 110 ..... 1110011 @csr csrrci ............ ..... 111 ..... 1110011 @csr +# zimops (unpriv integer may be operations) instructions with system opcode +# These're superset of for cfi encodings. zimops_r and zimops_rr should be last +# entry in below overlapping patterns so that it acts as final sink for overlapping patterns. +# Any new encoding that can be used should be placed above mop.r and mop.rr + +# cfi instructions carved out of mop.r +{ + sspush 100000 0 11100 ..... 100 00000 1110011 %rs1 + sspop 100000 0 11100 00000 100 ..... 1110011 %rd + ssprr 100000 0 11101 00000 100 ..... 1110011 %rd + zimops_r 1-00-- 0 111-- ----- 100 ..... 1110011 %rd +} + +# cfi instructions carved out of mop.rr +{ + sschckra 100010 1 00001 00101 100 00000 1110011 + ssamoswap 100000 1 ..... ..... 100 ..... 1110011 @r + + lpsll 100000 1 0 ......... 100 00000 1110011 %imm_cfi9 + lpcll 100000 1 1 ......... 100 00000 1110011 %imm_cfi9 + lpsml 100001 1 0 0........ 100 00000 1110011 %imm_cfi8 + lpcml 100001 1 0 1........ 100 00000 1110011 %imm_cfi8 + lpsul 100010 1 1 0........ 100 00000 1110011 %imm_cfi8 + lpcul 100010 1 1 1........ 100 00000 1110011 %imm_cfi8 + zimops_rr 1-00-- 1 - --------- 100 ..... 1110011 %rd +} + # *** RV64I Base Instruction Set (in addition to RV32I) *** lwu ............ ..... 110 ..... 0000011 @i ld ............ ..... 011 ..... 0000011 @i diff --git a/target/riscv/insn_trans/trans_rvi.c.inc b/target/riscv/insn_trans/trans_rvi.c.inc index 5c69b88d1e..4a4f1ca778 100644 --- a/target/riscv/insn_trans/trans_rvi.c.inc +++ b/target/riscv/insn_trans/trans_rvi.c.inc @@ -66,6 +66,20 @@ static bool trans_jalr(DisasContext *ctx, arg_jalr *a) } gen_set_gpri(ctx, a->rd, ctx->pc_succ_insn); + + if (ctx->cfg_ptr->ext_cfi) { + /* + * Rely on a helper to check the forward CFI enable for the + * current process mode. The alternatives would be (1) include + * "fcfi enabled" in the cflags or (2) maintain a "fcfi + * currently enabled" in cpu_env and emit TCG code to access + * and test it. + */ + if (a->rd == xRA || a->rd == xT0 || (a->rs1 != xRA && a->rs1 != xT0)) { + gen_helper_cfi_jalr(cpu_env, tcg_constant_i32(LP_EXPECTED)); + } + } + lookup_and_goto_ptr(ctx); if (misaligned) { diff --git a/target/riscv/insn_trans/trans_zimops.c.inc b/target/riscv/insn_trans/trans_zimops.c.inc new file mode 100644 index 0000000000..51748637b9 --- /dev/null +++ b/target/riscv/insn_trans/trans_zimops.c.inc @@ -0,0 +1,53 @@ +/* + * RISC-V translation routines for the Control-Flow Integrity Extension + * + * Copyright (c) 2022-2023 Rivos Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + + static bool trans_zimops_r(DisasContext *ctx, arg_zimops_r * a) + { + /* zimops not implemented, raise illegal instruction & return true */ + if (!ctx->cfg_ptr->ext_zimops) { + gen_exception_illegal(ctx); + return true; + } + + /* + * zimops implemented, simply grab destination and mov zero. + * return true + */ + TCGv dest = dest_gpr(ctx, a->rd); + dest = tcg_const_tl(0); + gen_set_gpr(ctx, a->rd, dest); + return true; + } + + static bool trans_zimops_rr(DisasContext *ctx, arg_zimops_rr * a) + { + /* zimops not implemented, raise illegal instruction & return true */ + if (!ctx->cfg_ptr->ext_zimops) { + gen_exception_illegal(ctx); + return true; + } + + /* + * zimops implemented, simply grab destination and mov zero. + * return true + */ + TCGv dest = dest_gpr(ctx, a->rd); + dest = tcg_const_tl(0); + gen_set_gpr(ctx, a->rd, dest); + return true; + } diff --git a/target/riscv/insn_trans/trans_zisslpcfi.c.inc b/target/riscv/insn_trans/trans_zisslpcfi.c.inc new file mode 100644 index 0000000000..fe27bb73f6 --- /dev/null +++ b/target/riscv/insn_trans/trans_zisslpcfi.c.inc @@ -0,0 +1,310 @@ +/* + * RISC-V translation routines for the Control-Flow Integrity Extension + * + * Copyright (c) 2022-2023 Rivos Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +static MemOp mxl_memop(DisasContext *ctx) +{ + switch (get_xl(ctx)) { + case MXL_RV32: + return MO_TEUL; + + case MXL_RV64: + return MO_TEUQ; + + case MXL_RV128: + return MO_TEUO; + + default: + g_assert_not_reached(); + } +} + +static bool trans_sspop(DisasContext *ctx, arg_sspop *a) +{ + /* cfi only supported on 32bit and 64bit */ + if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) { + return false; + } + + /* back cfi not enabled, should go to trans_zimops. return false */ + if (!ctx->bcfi_enabled) { + return false; + } + + /* sspop can only load into x1 or x5. Everything else defaults to zimops */ + if (a->rd != 1 && a->rd != 5) { + return false; + } + + /* + * get data in TCGv using get_gpr + * get addr in TCGv using gen_helper_csrr on CSR_SSP + * use some tcg subtract arithmetic (subtract by XLEN) on addr + * perform ss store on computed address + */ + + TCGv addr = tcg_temp_new(); + int tmp = (get_xl(ctx) == MXL_RV64) ? 8 : 4; + TCGv_i32 ssp_csr = tcg_constant_i32(CSR_SSP); + TCGv data = get_gpr(ctx, a->rd, EXT_NONE); + gen_helper_csrr(addr, cpu_env, ssp_csr); + tcg_gen_qemu_ld_tl(data, addr, MMU_IDX_SS_ACCESS, + mxl_memop(ctx) | MO_ALIGN); + + /* + * add XLEN/bitwidth to addr, align to XLEN . How do i do that? Is below + * the right way + */ + tcg_gen_addi_tl(addr, addr, tmp); + gen_set_gpr(ctx, a->rd, data); + gen_helper_csrw(cpu_env, ssp_csr, addr); + + return true; +} + +static bool trans_sspush(DisasContext *ctx, arg_sspush *a) +{ + /* cfi only supported on 32bit and 64bit */ + if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) { + return false; + } + + /* back cfi not enabled, should go to trans_zimops. return false */ + if (!ctx->bcfi_enabled) { + return false; + } + + /* + * sspush can only push from x1 or x5. Everything else defaults to zimops + */ + if (a->rs1 != 1 && a->rs1 != 5) { + return false; + } + + /* + * get data in TCGv using get_gpr + * get addr in TCGv using gen_helper_csrr on CSR_SSP + * use some tcg subtract arithmetic (subtract by XLEN) on addr + * perform ss store on computed address + */ + + TCGv addr = tcg_temp_new(); + int tmp = (get_xl(ctx) == MXL_RV64) ? -8 : -4; + TCGv_i32 ssp_csr = tcg_constant_i32(CSR_SSP); + TCGv data = get_gpr(ctx, a->rs1, EXT_NONE); + gen_helper_csrr(addr, cpu_env, ssp_csr); + + /* + * subtract XLEN from addr, align to XLEN . How do i do that? Is below the + * right way + */ + tcg_gen_addi_tl(addr, addr, tmp); + tcg_gen_qemu_st_tl(data, addr, MMU_IDX_SS_ACCESS, + mxl_memop(ctx) | MO_ALIGN); + + gen_helper_csrw(cpu_env, ssp_csr, addr); + + return true; +} + +static bool trans_sschckra(DisasContext *ctx, arg_sschckra *a) +{ + /* cfi only supported on 32bit and 64bit */ + if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) { + return false; + } + + /* back cfi not enabled, should go to trans_zimops. return false */ + if (!ctx->bcfi_enabled) { + return false; + } + + gen_helper_sschkra_mismatch(cpu_env); + + return true; +} + +static bool trans_ssprr(DisasContext *ctx, arg_ssprr *a) +{ + /* cfi only supported on 32bit and 64bit */ + if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) { + return false; + } + + /* back cfi not enabled, should go to trans_zimops. return false */ + if (!ctx->bcfi_enabled) { + return false; + } + + TCGv dest = get_gpr(ctx, a->rd, EXT_NONE); + TCGv_i32 ssp_csr = tcg_constant_i32(CSR_SSP); + gen_helper_csrr(dest, cpu_env, ssp_csr); + gen_set_gpr(ctx, a->rd, dest); + + return true; +} + +static bool trans_ssamoswap(DisasContext *ctx, arg_ssamoswap *a) +{ + /* cfi only supported on 32bit and 64bit */ + if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) { + return false; + } + + /* back cfi not enabled, should go to trans_zimops. return false */ + if (!ctx->bcfi_enabled) { + return false; + } + + /* If cfi is enabled then, then rd must be != 0 */ + + if (a->rd == 0) { + return false; + } + + TCGv dest = dest_gpr(ctx, a->rd); + TCGv src1 = get_address(ctx, a->rs1, 0); + TCGv src2 = get_gpr(ctx, a->rs2, EXT_NONE); + MemOp mop = (MO_ALIGN | ((get_xl(ctx) == MXL_RV32) ? MO_TESL : MO_TESQ)); + + tcg_gen_atomic_xchg_tl(dest, src1, src2, MMU_IDX_SS_ACCESS, mop); + gen_set_gpr(ctx, a->rd, dest); + return true; +} + +static bool trans_lpcll(DisasContext *ctx, arg_lpcll *a) +{ + /* cfi only supported on 32bit and 64bit */ + if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) { + return false; + } + + /* + * If this is the first instruction of the TB, let the translator + * know the landing pad requirement was satisfied. No need to bother + * checking for CFI feature or enablement. + */ + + if (ctx->base.pc_next == ctx->base.pc_first) { + ctx->fcfi_lp_expected = false; + /* PC must be 4 byte aligned */ + if (ctx->fcfi_enabled && ((ctx->base.pc_next) & 0x3)) { + /* + * misaligned, according to spec we should raise illegal instead + * of mis-aligned + */ + gen_exception_illegal(ctx); + } + } + + /* forward cfi not enabled, should go to trans_zimops. return false */ + if (!ctx->fcfi_enabled) { + return false; + } + + gen_helper_cfi_check_landing_pad(cpu_env, tcg_constant_i32(a->imm_cfi9), + tcg_constant_i32(FCFI_LPLL)); + return true; +} + +static bool trans_lpcml(DisasContext *ctx, arg_lpcml *a) +{ + /* cfi only supported on 32bit and 64bit */ + if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) { + return false; + } + + /* forward cfi not enabled, should go to trans_zimops. return false */ + if (!ctx->fcfi_enabled) { + return false; + } + + gen_helper_cfi_check_landing_pad(cpu_env, tcg_constant_i32(a->imm_cfi8), + tcg_constant_i32(FCFI_ML)); + return true; +} + +static bool trans_lpcul(DisasContext *ctx, arg_lpcul *a) +{ + /* cfi only supported on 32bit and 64bit */ + if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) { + return false; + } + + /* forward cfi not enabled, should go to trans_zimops. return false */ + if (!ctx->fcfi_enabled) { + return false; + } + + gen_helper_cfi_check_landing_pad(cpu_env, tcg_constant_i32(a->imm_cfi8), + tcg_constant_i32(FCFI_UL)); + return true; +} + +static bool trans_lpsll(DisasContext *ctx, arg_lpsll *a) +{ + /* cfi only supported on 32bit and 64bit */ + if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) { + return false; + } + + /* forward cfi not enabled, should go to trans_zimops. return false */ + if (!ctx->fcfi_enabled) { + return false; + } + + gen_helper_cfi_set_landing_pad(cpu_env, tcg_constant_i32(a->imm_cfi9), + tcg_constant_i32(FCFI_LPLL)); + + return true; +} + +static bool trans_lpsml(DisasContext *ctx, arg_lpsml *a) +{ + /* cfi only supported on 32bit and 64bit */ + if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) { + return false; + } + + /* forward cfi not enabled, should go to trans_zimops. return false */ + if (!ctx->fcfi_enabled) { + return false; + } + + gen_helper_cfi_set_landing_pad(cpu_env, tcg_constant_i32(a->imm_cfi8), + tcg_constant_i32(FCFI_ML)); + + return true; +} + +static bool trans_lpsul(DisasContext *ctx, arg_lpsul *a) +{ + /* cfi only supported on 32bit and 64bit */ + if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) { + return false; + } + + /* forward cfi not enabled, should go to trans_zimops. return false */ + if (!ctx->fcfi_enabled) { + return false; + } + + gen_helper_cfi_set_landing_pad(cpu_env, tcg_constant_i32(a->imm_cfi8), + tcg_constant_i32(FCFI_UL)); + + return true; +} diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index d15893aa82..c14b76aabb 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -123,6 +123,73 @@ target_ulong helper_csrrw_i128(CPURISCVState *env, int csr, return int128_getlo(rv); } +void helper_sschkra_mismatch(CPURISCVState *env) +{ + if (env->gpr[xRA] != env->gpr[xT0]) { + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + } +} + +void helper_cfi_jalr(CPURISCVState *env, int elp) +{ + /* + * The translation routine doesn't know if forward CFI is enabled + * in the current processor mode or not. It's not worth burning a + * cflags bit to encode this, or tracking the current-mode-fcfi + * enable in a dedicated member of 'env'. Just come out to a helper + * for jump/call on a core with CFI. + */ + if (cpu_get_fcfien(env)) { + env->elp = elp; + } +} + +void helper_cfi_check_landing_pad(CPURISCVState *env, int lbl, int inst_type) +{ + if (cpu_get_fcfien(env)) { + switch (inst_type) { + case FCFI_LPLL: + /* + * Check for a lower label match. We already checked 4 byte + * alignment in tcg + */ + if (lbl != get_field(env->lplr, LPLR_LL)) { + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + } + env->elp = NO_LP_EXPECTED; + break; + case FCFI_ML: + if (lbl != get_field(env->lplr, LPLR_ML)) { + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + } + break; + case FCFI_UL: + if (lbl != get_field(env->lplr, LPLR_UL)) { + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + } + } + } +} + +void helper_cfi_set_landing_pad(CPURISCVState *env, int lbl, int inst_type) +{ + if (cpu_get_fcfien(env)) { + switch (inst_type) { + case FCFI_LPLL: + /* setting lower label always clears up entire field */ + env->lplr = 0; + env->lplr = set_field(env->lplr, LPLR_LL, lbl); + break; + case FCFI_ML: + env->lplr = set_field(env->lplr, LPLR_ML, lbl); + break; + case FCFI_UL: + env->lplr = set_field(env->lplr, LPLR_UL, lbl); + break; + } + } +} + #ifndef CONFIG_USER_ONLY target_ulong helper_sret(CPURISCVState *env) diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 7d43d20fc3..b1a965d192 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -1071,6 +1071,8 @@ static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc) #include "insn_trans/trans_privileged.c.inc" #include "insn_trans/trans_svinval.c.inc" #include "insn_trans/trans_xventanacondops.c.inc" +#include "insn_trans/trans_zisslpcfi.c.inc" +#include "insn_trans/trans_zimops.c.inc" /* Include the auto-generated decoder for 16 bit insn */ #include "decode-insn16.c.inc"