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