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_XTHEADCMO(ctx) do {              \
26    if (!ctx->cfg_ptr->ext_xtheadcmo) {          \
27        return false;                            \
28    }                                            \
29} while (0)
30
31#define REQUIRE_XTHEADSYNC(ctx) do {             \
32    if (!ctx->cfg_ptr->ext_xtheadsync) {         \
33        return false;                            \
34    }                                            \
35} while (0)
36
37/* XTheadBa */
38
39/*
40 * th.addsl is similar to sh[123]add (from Zba), but not an
41 * alternative encoding: while sh[123] applies the shift to rs1,
42 * th.addsl shifts rs2.
43 */
44
45#define GEN_TH_ADDSL(SHAMT)                                     \
46static void gen_th_addsl##SHAMT(TCGv ret, TCGv arg1, TCGv arg2) \
47{                                                               \
48    TCGv t = tcg_temp_new();                                    \
49    tcg_gen_shli_tl(t, arg2, SHAMT);                            \
50    tcg_gen_add_tl(ret, t, arg1);                               \
51    tcg_temp_free(t);                                           \
52}
53
54GEN_TH_ADDSL(1)
55GEN_TH_ADDSL(2)
56GEN_TH_ADDSL(3)
57
58#define GEN_TRANS_TH_ADDSL(SHAMT)                                       \
59static bool trans_th_addsl##SHAMT(DisasContext *ctx,                    \
60                                  arg_th_addsl##SHAMT * a)              \
61{                                                                       \
62    REQUIRE_XTHEADBA(ctx);                                              \
63    return gen_arith(ctx, a, EXT_NONE, gen_th_addsl##SHAMT, NULL);      \
64}
65
66GEN_TRANS_TH_ADDSL(1)
67GEN_TRANS_TH_ADDSL(2)
68GEN_TRANS_TH_ADDSL(3)
69
70/* XTheadCmo */
71
72static inline int priv_level(DisasContext *ctx)
73{
74#ifdef CONFIG_USER_ONLY
75    return PRV_U;
76#else
77     /* Priv level is part of mem_idx. */
78    return ctx->mem_idx & TB_FLAGS_PRIV_MMU_MASK;
79#endif
80}
81
82/* Test if priv level is M, S, or U (cannot fail). */
83#define REQUIRE_PRIV_MSU(ctx)
84
85/* Test if priv level is M or S. */
86#define REQUIRE_PRIV_MS(ctx)                                    \
87do {                                                            \
88    int priv = priv_level(ctx);                                 \
89    if (!(priv == PRV_M ||                                      \
90          priv == PRV_S)) {                                     \
91        return false;                                           \
92    }                                                           \
93} while (0)
94
95#define NOP_PRIVCHECK(insn, extcheck, privcheck)                \
96static bool trans_ ## insn(DisasContext *ctx, arg_ ## insn * a) \
97{                                                               \
98    (void) a;                                                   \
99    extcheck(ctx);                                              \
100    privcheck(ctx);                                             \
101    return true;                                                \
102}
103
104NOP_PRIVCHECK(th_dcache_call, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS)
105NOP_PRIVCHECK(th_dcache_ciall, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS)
106NOP_PRIVCHECK(th_dcache_iall, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS)
107NOP_PRIVCHECK(th_dcache_cpa, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS)
108NOP_PRIVCHECK(th_dcache_cipa, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS)
109NOP_PRIVCHECK(th_dcache_ipa, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS)
110NOP_PRIVCHECK(th_dcache_cva, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MSU)
111NOP_PRIVCHECK(th_dcache_civa, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MSU)
112NOP_PRIVCHECK(th_dcache_iva, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MSU)
113NOP_PRIVCHECK(th_dcache_csw, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS)
114NOP_PRIVCHECK(th_dcache_cisw, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS)
115NOP_PRIVCHECK(th_dcache_isw, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS)
116NOP_PRIVCHECK(th_dcache_cpal1, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS)
117NOP_PRIVCHECK(th_dcache_cval1, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS)
118
119NOP_PRIVCHECK(th_icache_iall, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS)
120NOP_PRIVCHECK(th_icache_ialls, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS)
121NOP_PRIVCHECK(th_icache_ipa, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS)
122NOP_PRIVCHECK(th_icache_iva, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MSU)
123
124NOP_PRIVCHECK(th_l2cache_call, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS)
125NOP_PRIVCHECK(th_l2cache_ciall, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS)
126NOP_PRIVCHECK(th_l2cache_iall, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS)
127
128/* XTheadSync */
129
130static bool trans_th_sfence_vmas(DisasContext *ctx, arg_th_sfence_vmas *a)
131{
132    (void) a;
133    REQUIRE_XTHEADSYNC(ctx);
134
135#ifndef CONFIG_USER_ONLY
136    REQUIRE_PRIV_MS(ctx);
137    gen_helper_tlb_flush_all(cpu_env);
138    return true;
139#else
140    return false;
141#endif
142}
143
144#ifndef CONFIG_USER_ONLY
145static void gen_th_sync_local(DisasContext *ctx)
146{
147    /*
148     * Emulate out-of-order barriers with pipeline flush
149     * by exiting the translation block.
150     */
151    gen_set_pc_imm(ctx, ctx->pc_succ_insn);
152    tcg_gen_exit_tb(NULL, 0);
153    ctx->base.is_jmp = DISAS_NORETURN;
154}
155#endif
156
157static bool trans_th_sync(DisasContext *ctx, arg_th_sync *a)
158{
159    (void) a;
160    REQUIRE_XTHEADSYNC(ctx);
161
162#ifndef CONFIG_USER_ONLY
163    REQUIRE_PRIV_MSU(ctx);
164
165    /*
166     * th.sync is an out-of-order barrier.
167     */
168    gen_th_sync_local(ctx);
169
170    return true;
171#else
172    return false;
173#endif
174}
175
176static bool trans_th_sync_i(DisasContext *ctx, arg_th_sync_i *a)
177{
178    (void) a;
179    REQUIRE_XTHEADSYNC(ctx);
180
181#ifndef CONFIG_USER_ONLY
182    REQUIRE_PRIV_MSU(ctx);
183
184    /*
185     * th.sync.i is th.sync plus pipeline flush.
186     */
187    gen_th_sync_local(ctx);
188
189    return true;
190#else
191    return false;
192#endif
193}
194
195static bool trans_th_sync_is(DisasContext *ctx, arg_th_sync_is *a)
196{
197    /* This instruction has the same behaviour like th.sync.i. */
198    return trans_th_sync_i(ctx, a);
199}
200
201static bool trans_th_sync_s(DisasContext *ctx, arg_th_sync_s *a)
202{
203    /* This instruction has the same behaviour like th.sync. */
204    return trans_th_sync(ctx, a);
205}
206