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