1/*
2 * RISC-V translation routines for the RVXI Base Integer Instruction Set.
3 *
4 * Copyright (c) 2020 Western Digital
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#ifndef CONFIG_USER_ONLY
20static void check_access(DisasContext *ctx) {
21    if (!ctx->hlsx) {
22        if (ctx->virt_enabled) {
23            generate_exception(ctx, RISCV_EXCP_VIRT_INSTRUCTION_FAULT);
24        } else {
25            generate_exception(ctx, RISCV_EXCP_ILLEGAL_INST);
26        }
27    }
28}
29#endif
30
31static bool trans_hlv_b(DisasContext *ctx, arg_hlv_b *a)
32{
33    REQUIRE_EXT(ctx, RVH);
34#ifndef CONFIG_USER_ONLY
35    TCGv t0 = tcg_temp_new();
36    TCGv t1 = tcg_temp_new();
37
38    check_access(ctx);
39
40    gen_get_gpr(t0, a->rs1);
41
42    tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx | TB_FLAGS_PRIV_HYP_ACCESS_MASK, MO_SB);
43    gen_set_gpr(a->rd, t1);
44
45    tcg_temp_free(t0);
46    tcg_temp_free(t1);
47    return true;
48#else
49    return false;
50#endif
51}
52
53static bool trans_hlv_h(DisasContext *ctx, arg_hlv_h *a)
54{
55    REQUIRE_EXT(ctx, RVH);
56#ifndef CONFIG_USER_ONLY
57    TCGv t0 = tcg_temp_new();
58    TCGv t1 = tcg_temp_new();
59
60    check_access(ctx);
61
62    gen_get_gpr(t0, a->rs1);
63
64    tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx | TB_FLAGS_PRIV_HYP_ACCESS_MASK, MO_TESW);
65    gen_set_gpr(a->rd, t1);
66
67    tcg_temp_free(t0);
68    tcg_temp_free(t1);
69    return true;
70#else
71    return false;
72#endif
73}
74
75static bool trans_hlv_w(DisasContext *ctx, arg_hlv_w *a)
76{
77    REQUIRE_EXT(ctx, RVH);
78#ifndef CONFIG_USER_ONLY
79    TCGv t0 = tcg_temp_new();
80    TCGv t1 = tcg_temp_new();
81
82    check_access(ctx);
83
84    gen_get_gpr(t0, a->rs1);
85
86    tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx | TB_FLAGS_PRIV_HYP_ACCESS_MASK, MO_TESL);
87    gen_set_gpr(a->rd, t1);
88
89    tcg_temp_free(t0);
90    tcg_temp_free(t1);
91    return true;
92#else
93    return false;
94#endif
95}
96
97static bool trans_hlv_bu(DisasContext *ctx, arg_hlv_bu *a)
98{
99    REQUIRE_EXT(ctx, RVH);
100#ifndef CONFIG_USER_ONLY
101    TCGv t0 = tcg_temp_new();
102    TCGv t1 = tcg_temp_new();
103
104    check_access(ctx);
105
106    gen_get_gpr(t0, a->rs1);
107
108    tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx | TB_FLAGS_PRIV_HYP_ACCESS_MASK, MO_UB);
109    gen_set_gpr(a->rd, t1);
110
111    tcg_temp_free(t0);
112    tcg_temp_free(t1);
113    return true;
114#else
115    return false;
116#endif
117}
118
119static bool trans_hlv_hu(DisasContext *ctx, arg_hlv_hu *a)
120{
121    REQUIRE_EXT(ctx, RVH);
122#ifndef CONFIG_USER_ONLY
123    TCGv t0 = tcg_temp_new();
124    TCGv t1 = tcg_temp_new();
125
126    check_access(ctx);
127
128    gen_get_gpr(t0, a->rs1);
129    tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx | TB_FLAGS_PRIV_HYP_ACCESS_MASK, MO_TEUW);
130    gen_set_gpr(a->rd, t1);
131
132    tcg_temp_free(t0);
133    tcg_temp_free(t1);
134    return true;
135#else
136    return false;
137#endif
138}
139
140static bool trans_hsv_b(DisasContext *ctx, arg_hsv_b *a)
141{
142    REQUIRE_EXT(ctx, RVH);
143#ifndef CONFIG_USER_ONLY
144    TCGv t0 = tcg_temp_new();
145    TCGv dat = tcg_temp_new();
146
147    check_access(ctx);
148
149    gen_get_gpr(t0, a->rs1);
150    gen_get_gpr(dat, a->rs2);
151
152    tcg_gen_qemu_st_tl(dat, t0, ctx->mem_idx | TB_FLAGS_PRIV_HYP_ACCESS_MASK, MO_SB);
153
154    tcg_temp_free(t0);
155    tcg_temp_free(dat);
156    return true;
157#else
158    return false;
159#endif
160}
161
162static bool trans_hsv_h(DisasContext *ctx, arg_hsv_h *a)
163{
164    REQUIRE_EXT(ctx, RVH);
165#ifndef CONFIG_USER_ONLY
166    TCGv t0 = tcg_temp_new();
167    TCGv dat = tcg_temp_new();
168
169    check_access(ctx);
170
171    gen_get_gpr(t0, a->rs1);
172    gen_get_gpr(dat, a->rs2);
173
174    tcg_gen_qemu_st_tl(dat, t0, ctx->mem_idx | TB_FLAGS_PRIV_HYP_ACCESS_MASK, MO_TESW);
175
176    tcg_temp_free(t0);
177    tcg_temp_free(dat);
178    return true;
179#else
180    return false;
181#endif
182}
183
184static bool trans_hsv_w(DisasContext *ctx, arg_hsv_w *a)
185{
186    REQUIRE_EXT(ctx, RVH);
187#ifndef CONFIG_USER_ONLY
188    TCGv t0 = tcg_temp_new();
189    TCGv dat = tcg_temp_new();
190
191    check_access(ctx);
192
193    gen_get_gpr(t0, a->rs1);
194    gen_get_gpr(dat, a->rs2);
195
196    tcg_gen_qemu_st_tl(dat, t0, ctx->mem_idx | TB_FLAGS_PRIV_HYP_ACCESS_MASK, MO_TESL);
197
198    tcg_temp_free(t0);
199    tcg_temp_free(dat);
200    return true;
201#else
202    return false;
203#endif
204}
205
206#ifdef TARGET_RISCV64
207static bool trans_hlv_wu(DisasContext *ctx, arg_hlv_wu *a)
208{
209    REQUIRE_EXT(ctx, RVH);
210#ifndef CONFIG_USER_ONLY
211    TCGv t0 = tcg_temp_new();
212    TCGv t1 = tcg_temp_new();
213
214    check_access(ctx);
215
216    gen_get_gpr(t0, a->rs1);
217
218    tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx | TB_FLAGS_PRIV_HYP_ACCESS_MASK, MO_TEUL);
219    gen_set_gpr(a->rd, t1);
220
221    tcg_temp_free(t0);
222    tcg_temp_free(t1);
223    return true;
224#else
225    return false;
226#endif
227}
228
229static bool trans_hlv_d(DisasContext *ctx, arg_hlv_d *a)
230{
231    REQUIRE_EXT(ctx, RVH);
232#ifndef CONFIG_USER_ONLY
233    TCGv t0 = tcg_temp_new();
234    TCGv t1 = tcg_temp_new();
235
236    check_access(ctx);
237
238    gen_get_gpr(t0, a->rs1);
239
240    tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx | TB_FLAGS_PRIV_HYP_ACCESS_MASK, MO_TEQ);
241    gen_set_gpr(a->rd, t1);
242
243    tcg_temp_free(t0);
244    tcg_temp_free(t1);
245    return true;
246#else
247    return false;
248#endif
249}
250
251static bool trans_hsv_d(DisasContext *ctx, arg_hsv_d *a)
252{
253    REQUIRE_EXT(ctx, RVH);
254#ifndef CONFIG_USER_ONLY
255    TCGv t0 = tcg_temp_new();
256    TCGv dat = tcg_temp_new();
257
258    check_access(ctx);
259
260    gen_get_gpr(t0, a->rs1);
261    gen_get_gpr(dat, a->rs2);
262
263    tcg_gen_qemu_st_tl(dat, t0, ctx->mem_idx | TB_FLAGS_PRIV_HYP_ACCESS_MASK, MO_TEQ);
264
265    tcg_temp_free(t0);
266    tcg_temp_free(dat);
267    return true;
268#else
269    return false;
270#endif
271}
272#endif
273
274static bool trans_hlvx_hu(DisasContext *ctx, arg_hlvx_hu *a)
275{
276    REQUIRE_EXT(ctx, RVH);
277#ifndef CONFIG_USER_ONLY
278    TCGv t0 = tcg_temp_new();
279    TCGv t1 = tcg_temp_new();
280    TCGv mem_idx = tcg_temp_new();
281    TCGv memop = tcg_temp_new();
282
283    gen_get_gpr(t0, a->rs1);
284    tcg_gen_movi_tl(mem_idx, ctx->mem_idx);
285    tcg_gen_movi_tl(memop, MO_TEUW);
286
287    gen_helper_hyp_x_load(t1, cpu_env, t0, mem_idx, memop);
288    gen_set_gpr(a->rd, t1);
289
290    tcg_temp_free(t0);
291    tcg_temp_free(t1);
292    tcg_temp_free(mem_idx);
293    tcg_temp_free(memop);
294    return true;
295#else
296    return false;
297#endif
298}
299
300static bool trans_hlvx_wu(DisasContext *ctx, arg_hlvx_wu *a)
301{
302    REQUIRE_EXT(ctx, RVH);
303#ifndef CONFIG_USER_ONLY
304    TCGv t0 = tcg_temp_new();
305    TCGv t1 = tcg_temp_new();
306    TCGv mem_idx = tcg_temp_new();
307    TCGv memop = tcg_temp_new();
308
309    gen_get_gpr(t0, a->rs1);
310    tcg_gen_movi_tl(mem_idx, ctx->mem_idx);
311    tcg_gen_movi_tl(memop, MO_TEUL);
312
313    gen_helper_hyp_x_load(t1, cpu_env, t0, mem_idx, memop);
314    gen_set_gpr(a->rd, t1);
315
316    tcg_temp_free(t0);
317    tcg_temp_free(t1);
318    tcg_temp_free(mem_idx);
319    tcg_temp_free(memop);
320    return true;
321#else
322    return false;
323#endif
324}
325
326static bool trans_hfence_gvma(DisasContext *ctx, arg_sfence_vma *a)
327{
328    REQUIRE_EXT(ctx, RVH);
329#ifndef CONFIG_USER_ONLY
330    gen_helper_hyp_gvma_tlb_flush(cpu_env);
331    return true;
332#endif
333    return false;
334}
335
336static bool trans_hfence_vvma(DisasContext *ctx, arg_sfence_vma *a)
337{
338    REQUIRE_EXT(ctx, RVH);
339#ifndef CONFIG_USER_ONLY
340    gen_helper_hyp_tlb_flush(cpu_env);
341    return true;
342#endif
343    return false;
344}
345