| 1 | /* |
| 2 | * RISC-V translation routines for the Control-Flow Integrity Extension |
| 3 | * |
| 4 | * Copyright (c) 2024 Rivos Inc. |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or modify it |
| 7 | * under the terms and conditions of the GNU General Public License, |
| 8 | * version 2 or later, as published by the Free Software Foundation. |
| 9 | * |
| 10 | * This program is distributed in the hope it will be useful, but WITHOUT |
| 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 13 | * more details. |
| 14 | * |
| 15 | * You should have received a copy of the GNU General Public License along with |
| 16 | * this program. If not, see <http://www.gnu.org/licenses/>. |
| 17 | */ |
| 18 | |
| 19 | #define REQUIRE_ZICFISS(ctx) do { \ |
| 20 | if (!ctx->cfg_ptr->ext_zicfiss) { \ |
| 21 | return false; \ |
| 22 | } \ |
| 23 | } while (0) |
| 24 | |
| 25 | static bool trans_sspopchk(DisasContext *ctx, arg_sspopchk *a) |
| 26 | { |
| 27 | if (!ctx->bcfi_enabled) { |
| 28 | return false; |
| 29 | } |
| 30 | |
| 31 | TCGv addr = tcg_temp_new(); |
| 32 | TCGLabel *skip = gen_new_label(); |
| 33 | uint32_t tmp = (get_xl(ctx) == MXL_RV64) ? 8 : 4; |
| 34 | TCGv data = tcg_temp_new(); |
| 35 | gen_update_pc(ctx, 0); |
| 36 | TCGv_i64 wide_addr = tcg_temp_new_i64(); |
| 37 | tcg_gen_ld_i64(wide_addr, tcg_env, offsetof(CPURISCVState, ssp)); |
| 38 | tcg_gen_trunc_i64_tl(addr, wide_addr); |
| 39 | decode_save_opc(ctx, RISCV_UW2_ALWAYS_STORE_AMO); |
| 40 | tcg_gen_qemu_ld_tl(data, addr, SS_MMU_INDEX(ctx), |
| 41 | mxl_memop(ctx) | MO_ALIGN); |
| 42 | TCGv rs1 = get_gpr(ctx, a->rs1, EXT_NONE); |
| 43 | tcg_gen_brcond_tl(TCG_COND_EQ, data, rs1, skip); |
| 44 | tcg_gen_st8_i32(tcg_constant_i32(RISCV_EXCP_SW_CHECK_BCFI_TVAL), |
| 45 | tcg_env, offsetof(CPURISCVState, sw_check_code)); |
| 46 | gen_helper_raise_exception(tcg_env, |
| 47 | tcg_constant_i32(RISCV_EXCP_SW_CHECK)); |
| 48 | gen_set_label(skip); |
| 49 | tcg_gen_addi_tl(addr, addr, tmp); |
| 50 | tcg_gen_ext_tl_i64(wide_addr, addr); |
| 51 | tcg_gen_st_i64(wide_addr, tcg_env, offsetof(CPURISCVState, ssp)); |
| 52 | |
| 53 | return true; |
| 54 | } |
| 55 | |
| 56 | static bool trans_sspush(DisasContext *ctx, arg_sspush *a) |
| 57 | { |
| 58 | if (!ctx->bcfi_enabled) { |
| 59 | return false; |
| 60 | } |
| 61 | |
| 62 | TCGv addr = tcg_temp_new(); |
| 63 | int tmp = (get_xl(ctx) == MXL_RV64) ? -8 : -4; |
| 64 | TCGv data = get_gpr(ctx, a->rs2, EXT_NONE); |
| 65 | TCGv_i64 wide_addr = tcg_temp_new_i64(); |
| 66 | decode_save_opc(ctx, RISCV_UW2_ALWAYS_STORE_AMO); |
| 67 | tcg_gen_ld_i64(wide_addr, tcg_env, offsetof(CPURISCVState, ssp)); |
| 68 | tcg_gen_trunc_i64_tl(addr, wide_addr); |
| 69 | tcg_gen_addi_tl(addr, addr, tmp); |
| 70 | tcg_gen_qemu_st_tl(data, addr, SS_MMU_INDEX(ctx), |
| 71 | mxl_memop(ctx) | MO_ALIGN); |
| 72 | tcg_gen_ext_tl_i64(wide_addr, addr); |
| 73 | tcg_gen_st_i64(wide_addr, tcg_env, offsetof(CPURISCVState, ssp)); |
| 74 | |
| 75 | return true; |
| 76 | } |
| 77 | |
| 78 | static bool trans_ssrdp(DisasContext *ctx, arg_ssrdp *a) |
| 79 | { |
| 80 | if (!ctx->bcfi_enabled || a->rd == 0) { |
| 81 | return false; |
| 82 | } |
| 83 | |
| 84 | TCGv dest = dest_gpr(ctx, a->rd); |
| 85 | TCGv_i64 wide_addr = tcg_temp_new_i64(); |
| 86 | tcg_gen_ld_i64(wide_addr, tcg_env, offsetof(CPURISCVState, ssp)); |
| 87 | tcg_gen_trunc_i64_tl(dest, wide_addr); |
| 88 | gen_set_gpr(ctx, a->rd, dest); |
| 89 | |
| 90 | return true; |
| 91 | } |
| 92 | |
| 93 | static bool trans_ssamoswap_w(DisasContext *ctx, arg_amoswap_w *a) |
| 94 | { |
| 95 | REQUIRE_A_OR_ZAAMO(ctx); |
| 96 | REQUIRE_ZICFISS(ctx); |
| 97 | if (ctx->priv == PRV_M) { |
| 98 | generate_exception(ctx, RISCV_EXCP_STORE_AMO_ACCESS_FAULT); |
| 99 | } |
| 100 | |
| 101 | if (!ctx->bcfi_enabled) { |
| 102 | #ifndef CONFIG_USER_ONLY |
| 103 | gen_helper_ssamoswap_disabled(tcg_env); |
| 104 | #else |
| 105 | return false; |
| 106 | #endif |
| 107 | } |
| 108 | |
| 109 | TCGv dest = dest_gpr(ctx, a->rd); |
| 110 | TCGv src1, src2 = get_gpr(ctx, a->rs2, EXT_NONE); |
| 111 | MemOp memop = MO_ALIGN | MO_SL; |
| 112 | |
| 113 | decode_save_opc(ctx, RISCV_UW2_ALWAYS_STORE_AMO); |
| 114 | src1 = get_address(ctx, a->rs1, 0); |
| 115 | |
| 116 | memop |= ctx->mo_endianness; |
| 117 | tcg_gen_atomic_xchg_tl(dest, src1, src2, SS_MMU_INDEX(ctx), memop); |
| 118 | gen_set_gpr(ctx, a->rd, dest); |
| 119 | return true; |
| 120 | } |
| 121 | |
| 122 | static bool trans_ssamoswap_d(DisasContext *ctx, arg_amoswap_w *a) |
| 123 | { |
| 124 | REQUIRE_64BIT(ctx); |
| 125 | REQUIRE_A_OR_ZAAMO(ctx); |
| 126 | REQUIRE_ZICFISS(ctx); |
| 127 | if (ctx->priv == PRV_M) { |
| 128 | generate_exception(ctx, RISCV_EXCP_STORE_AMO_ACCESS_FAULT); |
| 129 | } |
| 130 | |
| 131 | if (!ctx->bcfi_enabled) { |
| 132 | #ifndef CONFIG_USER_ONLY |
| 133 | gen_helper_ssamoswap_disabled(tcg_env); |
| 134 | #else |
| 135 | return false; |
| 136 | #endif |
| 137 | } |
| 138 | |
| 139 | TCGv dest = dest_gpr(ctx, a->rd); |
| 140 | TCGv src1, src2 = get_gpr(ctx, a->rs2, EXT_NONE); |
| 141 | MemOp memop = MO_ALIGN | MO_SQ; |
| 142 | |
| 143 | decode_save_opc(ctx, RISCV_UW2_ALWAYS_STORE_AMO); |
| 144 | src1 = get_address(ctx, a->rs1, 0); |
| 145 | |
| 146 | memop |= ctx->mo_endianness; |
| 147 | tcg_gen_atomic_xchg_tl(dest, src1, src2, SS_MMU_INDEX(ctx), memop); |
| 148 | gen_set_gpr(ctx, a->rd, dest); |
| 149 | return true; |
| 150 | } |