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_XTHEADBB(ctx) do { \ 26 if (!ctx->cfg_ptr->ext_xtheadbb) { \ 27 return false; \ 28 } \ 29} while (0) 30 31#define REQUIRE_XTHEADCMO(ctx) do { \ 32 if (!ctx->cfg_ptr->ext_xtheadcmo) { \ 33 return false; \ 34 } \ 35} while (0) 36 37#define REQUIRE_XTHEADSYNC(ctx) do { \ 38 if (!ctx->cfg_ptr->ext_xtheadsync) { \ 39 return false; \ 40 } \ 41} while (0) 42 43/* XTheadBa */ 44 45/* 46 * th.addsl is similar to sh[123]add (from Zba), but not an 47 * alternative encoding: while sh[123] applies the shift to rs1, 48 * th.addsl shifts rs2. 49 */ 50 51#define GEN_TH_ADDSL(SHAMT) \ 52static void gen_th_addsl##SHAMT(TCGv ret, TCGv arg1, TCGv arg2) \ 53{ \ 54 TCGv t = tcg_temp_new(); \ 55 tcg_gen_shli_tl(t, arg2, SHAMT); \ 56 tcg_gen_add_tl(ret, t, arg1); \ 57 tcg_temp_free(t); \ 58} 59 60GEN_TH_ADDSL(1) 61GEN_TH_ADDSL(2) 62GEN_TH_ADDSL(3) 63 64#define GEN_TRANS_TH_ADDSL(SHAMT) \ 65static bool trans_th_addsl##SHAMT(DisasContext *ctx, \ 66 arg_th_addsl##SHAMT * a) \ 67{ \ 68 REQUIRE_XTHEADBA(ctx); \ 69 return gen_arith(ctx, a, EXT_NONE, gen_th_addsl##SHAMT, NULL); \ 70} 71 72GEN_TRANS_TH_ADDSL(1) 73GEN_TRANS_TH_ADDSL(2) 74GEN_TRANS_TH_ADDSL(3) 75 76/* XTheadBb */ 77 78/* th.srri is an alternate encoding for rori (from Zbb) */ 79static bool trans_th_srri(DisasContext *ctx, arg_th_srri * a) 80{ 81 REQUIRE_XTHEADBB(ctx); 82 return gen_shift_imm_fn_per_ol(ctx, a, EXT_NONE, 83 tcg_gen_rotri_tl, gen_roriw, NULL); 84} 85 86/* th.srriw is an alternate encoding for roriw (from Zbb) */ 87static bool trans_th_srriw(DisasContext *ctx, arg_th_srriw *a) 88{ 89 REQUIRE_XTHEADBB(ctx); 90 REQUIRE_64BIT(ctx); 91 ctx->ol = MXL_RV32; 92 return gen_shift_imm_fn(ctx, a, EXT_NONE, gen_roriw, NULL); 93} 94 95/* th.ext and th.extu perform signed/unsigned bitfield extraction */ 96static bool gen_th_bfextract(DisasContext *ctx, arg_th_bfext *a, 97 void (*f)(TCGv, TCGv, unsigned int, unsigned int)) 98{ 99 TCGv dest = dest_gpr(ctx, a->rd); 100 TCGv source = get_gpr(ctx, a->rs1, EXT_ZERO); 101 102 if (a->lsb <= a->msb) { 103 f(dest, source, a->lsb, a->msb - a->lsb + 1); 104 gen_set_gpr(ctx, a->rd, dest); 105 } 106 return true; 107} 108 109static bool trans_th_ext(DisasContext *ctx, arg_th_ext *a) 110{ 111 REQUIRE_XTHEADBB(ctx); 112 return gen_th_bfextract(ctx, a, tcg_gen_sextract_tl); 113} 114 115static bool trans_th_extu(DisasContext *ctx, arg_th_extu *a) 116{ 117 REQUIRE_XTHEADBB(ctx); 118 return gen_th_bfextract(ctx, a, tcg_gen_extract_tl); 119} 120 121/* th.ff0: find first zero (clz on an inverted input) */ 122static bool gen_th_ff0(DisasContext *ctx, arg_th_ff0 *a, DisasExtend ext) 123{ 124 TCGv dest = dest_gpr(ctx, a->rd); 125 TCGv src1 = get_gpr(ctx, a->rs1, ext); 126 127 int olen = get_olen(ctx); 128 TCGv t = tcg_temp_new(); 129 130 tcg_gen_not_tl(t, src1); 131 if (olen != TARGET_LONG_BITS) { 132 if (olen == 32) { 133 gen_clzw(dest, t); 134 } else { 135 g_assert_not_reached(); 136 } 137 } else { 138 gen_clz(dest, t); 139 } 140 141 tcg_temp_free(t); 142 gen_set_gpr(ctx, a->rd, dest); 143 144 return true; 145} 146 147static bool trans_th_ff0(DisasContext *ctx, arg_th_ff0 *a) 148{ 149 REQUIRE_XTHEADBB(ctx); 150 return gen_th_ff0(ctx, a, EXT_NONE); 151} 152 153/* th.ff1 is an alternate encoding for clz (from Zbb) */ 154static bool trans_th_ff1(DisasContext *ctx, arg_th_ff1 *a) 155{ 156 REQUIRE_XTHEADBB(ctx); 157 return gen_unary_per_ol(ctx, a, EXT_NONE, gen_clz, gen_clzw); 158} 159 160static void gen_th_revw(TCGv ret, TCGv arg1) 161{ 162 tcg_gen_bswap32_tl(ret, arg1, TCG_BSWAP_OS); 163} 164 165/* th.rev is an alternate encoding for the RV64 rev8 (from Zbb) */ 166static bool trans_th_rev(DisasContext *ctx, arg_th_rev *a) 167{ 168 REQUIRE_XTHEADBB(ctx); 169 170 return gen_unary_per_ol(ctx, a, EXT_NONE, tcg_gen_bswap_tl, gen_th_revw); 171} 172 173/* th.revw is a sign-extended byte-swap of the lower word */ 174static bool trans_th_revw(DisasContext *ctx, arg_th_revw *a) 175{ 176 REQUIRE_XTHEADBB(ctx); 177 REQUIRE_64BIT(ctx); 178 return gen_unary(ctx, a, EXT_NONE, gen_th_revw); 179} 180 181/* th.tstnbz is equivalent to an orc.b (from Zbb) with inverted result */ 182static void gen_th_tstnbz(TCGv ret, TCGv source1) 183{ 184 gen_orc_b(ret, source1); 185 tcg_gen_not_tl(ret, ret); 186} 187 188static bool trans_th_tstnbz(DisasContext *ctx, arg_th_tstnbz *a) 189{ 190 REQUIRE_XTHEADBB(ctx); 191 return gen_unary(ctx, a, EXT_ZERO, gen_th_tstnbz); 192} 193 194/* XTheadCmo */ 195 196static inline int priv_level(DisasContext *ctx) 197{ 198#ifdef CONFIG_USER_ONLY 199 return PRV_U; 200#else 201 /* Priv level is part of mem_idx. */ 202 return ctx->mem_idx & TB_FLAGS_PRIV_MMU_MASK; 203#endif 204} 205 206/* Test if priv level is M, S, or U (cannot fail). */ 207#define REQUIRE_PRIV_MSU(ctx) 208 209/* Test if priv level is M or S. */ 210#define REQUIRE_PRIV_MS(ctx) \ 211do { \ 212 int priv = priv_level(ctx); \ 213 if (!(priv == PRV_M || \ 214 priv == PRV_S)) { \ 215 return false; \ 216 } \ 217} while (0) 218 219#define NOP_PRIVCHECK(insn, extcheck, privcheck) \ 220static bool trans_ ## insn(DisasContext *ctx, arg_ ## insn * a) \ 221{ \ 222 (void) a; \ 223 extcheck(ctx); \ 224 privcheck(ctx); \ 225 return true; \ 226} 227 228NOP_PRIVCHECK(th_dcache_call, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 229NOP_PRIVCHECK(th_dcache_ciall, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 230NOP_PRIVCHECK(th_dcache_iall, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 231NOP_PRIVCHECK(th_dcache_cpa, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 232NOP_PRIVCHECK(th_dcache_cipa, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 233NOP_PRIVCHECK(th_dcache_ipa, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 234NOP_PRIVCHECK(th_dcache_cva, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MSU) 235NOP_PRIVCHECK(th_dcache_civa, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MSU) 236NOP_PRIVCHECK(th_dcache_iva, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MSU) 237NOP_PRIVCHECK(th_dcache_csw, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 238NOP_PRIVCHECK(th_dcache_cisw, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 239NOP_PRIVCHECK(th_dcache_isw, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 240NOP_PRIVCHECK(th_dcache_cpal1, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 241NOP_PRIVCHECK(th_dcache_cval1, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 242 243NOP_PRIVCHECK(th_icache_iall, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 244NOP_PRIVCHECK(th_icache_ialls, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 245NOP_PRIVCHECK(th_icache_ipa, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 246NOP_PRIVCHECK(th_icache_iva, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MSU) 247 248NOP_PRIVCHECK(th_l2cache_call, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 249NOP_PRIVCHECK(th_l2cache_ciall, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 250NOP_PRIVCHECK(th_l2cache_iall, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) 251 252/* XTheadSync */ 253 254static bool trans_th_sfence_vmas(DisasContext *ctx, arg_th_sfence_vmas *a) 255{ 256 (void) a; 257 REQUIRE_XTHEADSYNC(ctx); 258 259#ifndef CONFIG_USER_ONLY 260 REQUIRE_PRIV_MS(ctx); 261 gen_helper_tlb_flush_all(cpu_env); 262 return true; 263#else 264 return false; 265#endif 266} 267 268#ifndef CONFIG_USER_ONLY 269static void gen_th_sync_local(DisasContext *ctx) 270{ 271 /* 272 * Emulate out-of-order barriers with pipeline flush 273 * by exiting the translation block. 274 */ 275 gen_set_pc_imm(ctx, ctx->pc_succ_insn); 276 tcg_gen_exit_tb(NULL, 0); 277 ctx->base.is_jmp = DISAS_NORETURN; 278} 279#endif 280 281static bool trans_th_sync(DisasContext *ctx, arg_th_sync *a) 282{ 283 (void) a; 284 REQUIRE_XTHEADSYNC(ctx); 285 286#ifndef CONFIG_USER_ONLY 287 REQUIRE_PRIV_MSU(ctx); 288 289 /* 290 * th.sync is an out-of-order barrier. 291 */ 292 gen_th_sync_local(ctx); 293 294 return true; 295#else 296 return false; 297#endif 298} 299 300static bool trans_th_sync_i(DisasContext *ctx, arg_th_sync_i *a) 301{ 302 (void) a; 303 REQUIRE_XTHEADSYNC(ctx); 304 305#ifndef CONFIG_USER_ONLY 306 REQUIRE_PRIV_MSU(ctx); 307 308 /* 309 * th.sync.i is th.sync plus pipeline flush. 310 */ 311 gen_th_sync_local(ctx); 312 313 return true; 314#else 315 return false; 316#endif 317} 318 319static bool trans_th_sync_is(DisasContext *ctx, arg_th_sync_is *a) 320{ 321 /* This instruction has the same behaviour like th.sync.i. */ 322 return trans_th_sync_i(ctx, a); 323} 324 325static bool trans_th_sync_s(DisasContext *ctx, arg_th_sync_s *a) 326{ 327 /* This instruction has the same behaviour like th.sync. */ 328 return trans_th_sync(ctx, a); 329} 330