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