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