1/* 2 * RISC-V translation routines for the T-Head vendor extensions (xthead*). 3 * 4 * Copyright (c) 2022 VRULL GmbH. 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_XTHEADBA(ctx) do { \ 20 if (!ctx->cfg_ptr->ext_xtheadba) { \ 21 return false; \ 22 } \ 23} while (0) 24 25#define REQUIRE_XTHEADCMO(ctx) do { \ 26 if (!ctx->cfg_ptr->ext_xtheadcmo) { \ 27 return false; \ 28 } \ 29} while (0) 30 31#define REQUIRE_XTHEADSYNC(ctx) do { \ 32 if (!ctx->cfg_ptr->ext_xtheadsync) { \ 33 return false; \ 34 } \ 35} while (0) 36 37/* XTheadBa */ 38 39/* 40 * th.addsl is similar to sh[123]add (from Zba), but not an 41 * alternative encoding: while sh[123] applies the shift to rs1, 42 * th.addsl shifts rs2. 43 */ 44 45#define GEN_TH_ADDSL(SHAMT) \ 46static void gen_th_addsl##SHAMT(TCGv ret, TCGv arg1, TCGv arg2) \ 47{ \ 48 TCGv t = tcg_temp_new(); \ 49 tcg_gen_shli_tl(t, arg2, SHAMT); \ 50 tcg_gen_add_tl(ret, t, arg1); \ 51 tcg_temp_free(t); \ 52} 53 54GEN_TH_ADDSL(1) 55GEN_TH_ADDSL(2) 56GEN_TH_ADDSL(3) 57 58#define GEN_TRANS_TH_ADDSL(SHAMT) \ 59static bool trans_th_addsl##SHAMT(DisasContext *ctx, \ 60 arg_th_addsl##SHAMT * a) \ 61{ \ 62 REQUIRE_XTHEADBA(ctx); \ 63 return gen_arith(ctx, a, EXT_NONE, gen_th_addsl##SHAMT, NULL); \ 64} 65 66GEN_TRANS_TH_ADDSL(1) 67GEN_TRANS_TH_ADDSL(2) 68GEN_TRANS_TH_ADDSL(3) 69 70/* XTheadCmo */ 71 72static inline int priv_level(DisasContext *ctx) 73{ 74#ifdef CONFIG_USER_ONLY 75 return PRV_U; 76#else 77 /* Priv level is part of mem_idx. */ 78 return ctx->mem_idx & TB_FLAGS_PRIV_MMU_MASK; 79#endif 80} 81 82/* Test if priv level is M, S, or U (cannot fail). */ 83#define REQUIRE_PRIV_MSU(ctx) 84 85/* Test if priv level is M or S. */ 86#define REQUIRE_PRIV_MS(ctx) \ 87do { \ 88 int priv = priv_level(ctx); \ 89 if (!(priv == PRV_M || \ 90 priv == PRV_S)) { \ 91 return false; \ 92 } \ 93} while (0) 94 95#define NOP_PRIVCHECK(insn, extcheck, privcheck) \ 96static bool trans_ ## insn(DisasContext *ctx, arg_ ## insn * a) \ 97{ \ 98 (void) a; \ 99 extcheck(ctx); \ 100 privcheck(ctx); \ 101 return true; \ 102} 103 104NOP_PRIVCHECK(th_dcache_call, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 105NOP_PRIVCHECK(th_dcache_ciall, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 106NOP_PRIVCHECK(th_dcache_iall, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 107NOP_PRIVCHECK(th_dcache_cpa, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 108NOP_PRIVCHECK(th_dcache_cipa, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 109NOP_PRIVCHECK(th_dcache_ipa, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 110NOP_PRIVCHECK(th_dcache_cva, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MSU) 111NOP_PRIVCHECK(th_dcache_civa, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MSU) 112NOP_PRIVCHECK(th_dcache_iva, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MSU) 113NOP_PRIVCHECK(th_dcache_csw, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 114NOP_PRIVCHECK(th_dcache_cisw, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 115NOP_PRIVCHECK(th_dcache_isw, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 116NOP_PRIVCHECK(th_dcache_cpal1, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 117NOP_PRIVCHECK(th_dcache_cval1, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 118 119NOP_PRIVCHECK(th_icache_iall, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 120NOP_PRIVCHECK(th_icache_ialls, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 121NOP_PRIVCHECK(th_icache_ipa, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 122NOP_PRIVCHECK(th_icache_iva, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MSU) 123 124NOP_PRIVCHECK(th_l2cache_call, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 125NOP_PRIVCHECK(th_l2cache_ciall, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 126NOP_PRIVCHECK(th_l2cache_iall, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 127 128/* XTheadSync */ 129 130static bool trans_th_sfence_vmas(DisasContext *ctx, arg_th_sfence_vmas *a) 131{ 132 (void) a; 133 REQUIRE_XTHEADSYNC(ctx); 134 135#ifndef CONFIG_USER_ONLY 136 REQUIRE_PRIV_MS(ctx); 137 gen_helper_tlb_flush_all(cpu_env); 138 return true; 139#else 140 return false; 141#endif 142} 143 144#ifndef CONFIG_USER_ONLY 145static void gen_th_sync_local(DisasContext *ctx) 146{ 147 /* 148 * Emulate out-of-order barriers with pipeline flush 149 * by exiting the translation block. 150 */ 151 gen_set_pc_imm(ctx, ctx->pc_succ_insn); 152 tcg_gen_exit_tb(NULL, 0); 153 ctx->base.is_jmp = DISAS_NORETURN; 154} 155#endif 156 157static bool trans_th_sync(DisasContext *ctx, arg_th_sync *a) 158{ 159 (void) a; 160 REQUIRE_XTHEADSYNC(ctx); 161 162#ifndef CONFIG_USER_ONLY 163 REQUIRE_PRIV_MSU(ctx); 164 165 /* 166 * th.sync is an out-of-order barrier. 167 */ 168 gen_th_sync_local(ctx); 169 170 return true; 171#else 172 return false; 173#endif 174} 175 176static bool trans_th_sync_i(DisasContext *ctx, arg_th_sync_i *a) 177{ 178 (void) a; 179 REQUIRE_XTHEADSYNC(ctx); 180 181#ifndef CONFIG_USER_ONLY 182 REQUIRE_PRIV_MSU(ctx); 183 184 /* 185 * th.sync.i is th.sync plus pipeline flush. 186 */ 187 gen_th_sync_local(ctx); 188 189 return true; 190#else 191 return false; 192#endif 193} 194 195static bool trans_th_sync_is(DisasContext *ctx, arg_th_sync_is *a) 196{ 197 /* This instruction has the same behaviour like th.sync.i. */ 198 return trans_th_sync_i(ctx, a); 199} 200 201static bool trans_th_sync_s(DisasContext *ctx, arg_th_sync_s *a) 202{ 203 /* This instruction has the same behaviour like th.sync. */ 204 return trans_th_sync(ctx, a); 205} 206