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