]>
Commit | Line | Data |
---|---|---|
49a7f3aa CM |
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 | ||
c9410a68 CM |
19 | #define REQUIRE_XTHEADBA(ctx) do { \ |
20 | if (!ctx->cfg_ptr->ext_xtheadba) { \ | |
21 | return false; \ | |
22 | } \ | |
23 | } while (0) | |
24 | ||
49a7f3aa CM |
25 | #define REQUIRE_XTHEADCMO(ctx) do { \ |
26 | if (!ctx->cfg_ptr->ext_xtheadcmo) { \ | |
27 | return false; \ | |
28 | } \ | |
29 | } while (0) | |
30 | ||
134c3ffa CM |
31 | #define REQUIRE_XTHEADSYNC(ctx) do { \ |
32 | if (!ctx->cfg_ptr->ext_xtheadsync) { \ | |
33 | return false; \ | |
34 | } \ | |
35 | } while (0) | |
36 | ||
c9410a68 CM |
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) \ | |
46 | static 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 | ||
54 | GEN_TH_ADDSL(1) | |
55 | GEN_TH_ADDSL(2) | |
56 | GEN_TH_ADDSL(3) | |
57 | ||
58 | #define GEN_TRANS_TH_ADDSL(SHAMT) \ | |
59 | static 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 | ||
66 | GEN_TRANS_TH_ADDSL(1) | |
67 | GEN_TRANS_TH_ADDSL(2) | |
68 | GEN_TRANS_TH_ADDSL(3) | |
69 | ||
49a7f3aa CM |
70 | /* XTheadCmo */ |
71 | ||
72 | static 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) \ | |
87 | do { \ | |
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) \ | |
96 | static bool trans_ ## insn(DisasContext *ctx, arg_ ## insn * a) \ | |
97 | { \ | |
98 | (void) a; \ | |
99 | extcheck(ctx); \ | |
100 | privcheck(ctx); \ | |
101 | return true; \ | |
102 | } | |
103 | ||
104 | NOP_PRIVCHECK(th_dcache_call, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) | |
105 | NOP_PRIVCHECK(th_dcache_ciall, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) | |
106 | NOP_PRIVCHECK(th_dcache_iall, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) | |
107 | NOP_PRIVCHECK(th_dcache_cpa, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) | |
108 | NOP_PRIVCHECK(th_dcache_cipa, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) | |
109 | NOP_PRIVCHECK(th_dcache_ipa, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) | |
110 | NOP_PRIVCHECK(th_dcache_cva, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MSU) | |
111 | NOP_PRIVCHECK(th_dcache_civa, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MSU) | |
112 | NOP_PRIVCHECK(th_dcache_iva, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MSU) | |
113 | NOP_PRIVCHECK(th_dcache_csw, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) | |
114 | NOP_PRIVCHECK(th_dcache_cisw, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) | |
115 | NOP_PRIVCHECK(th_dcache_isw, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) | |
116 | NOP_PRIVCHECK(th_dcache_cpal1, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) | |
117 | NOP_PRIVCHECK(th_dcache_cval1, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) | |
118 | ||
119 | NOP_PRIVCHECK(th_icache_iall, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) | |
120 | NOP_PRIVCHECK(th_icache_ialls, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) | |
121 | NOP_PRIVCHECK(th_icache_ipa, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) | |
122 | NOP_PRIVCHECK(th_icache_iva, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MSU) | |
123 | ||
124 | NOP_PRIVCHECK(th_l2cache_call, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) | |
125 | NOP_PRIVCHECK(th_l2cache_ciall, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) | |
126 | NOP_PRIVCHECK(th_l2cache_iall, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) | |
134c3ffa CM |
127 | |
128 | /* XTheadSync */ | |
129 | ||
130 | static 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 | |
145 | static 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 | ||
157 | static 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 | ||
176 | static 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 | ||
195 | static 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 | ||
201 | static 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 | } |