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