xref: /openbmc/qemu/target/loongarch/tcg/insn_trans/trans_privileged.c.inc (revision 4748be5e9df56e13045c0f76fe9f60fa7655fed7)
1/* SPDX-License-Identifier: GPL-2.0-or-later */
2/*
3 * Copyright (c) 2021 Loongson Technology Corporation Limited
4 *
5 * LoongArch translation routines for the privileged instructions.
6 */
7
8#include "csr.h"
9
10#ifdef CONFIG_USER_ONLY
11
12#define GEN_FALSE_TRANS(name)   \
13static bool trans_##name(DisasContext *ctx, arg_##name * a)  \
14{   \
15    return false;   \
16}
17
18GEN_FALSE_TRANS(csrrd)
19GEN_FALSE_TRANS(csrwr)
20GEN_FALSE_TRANS(csrxchg)
21GEN_FALSE_TRANS(iocsrrd_b)
22GEN_FALSE_TRANS(iocsrrd_h)
23GEN_FALSE_TRANS(iocsrrd_w)
24GEN_FALSE_TRANS(iocsrrd_d)
25GEN_FALSE_TRANS(iocsrwr_b)
26GEN_FALSE_TRANS(iocsrwr_h)
27GEN_FALSE_TRANS(iocsrwr_w)
28GEN_FALSE_TRANS(iocsrwr_d)
29GEN_FALSE_TRANS(tlbsrch)
30GEN_FALSE_TRANS(tlbrd)
31GEN_FALSE_TRANS(tlbwr)
32GEN_FALSE_TRANS(tlbfill)
33GEN_FALSE_TRANS(tlbclr)
34GEN_FALSE_TRANS(tlbflush)
35GEN_FALSE_TRANS(invtlb)
36GEN_FALSE_TRANS(cacop)
37GEN_FALSE_TRANS(ldpte)
38GEN_FALSE_TRANS(lddir)
39GEN_FALSE_TRANS(ertn)
40GEN_FALSE_TRANS(dbcl)
41GEN_FALSE_TRANS(idle)
42
43#else
44
45typedef void (*GenCSRRead)(TCGv dest, TCGv_ptr env);
46typedef void (*GenCSRWrite)(TCGv dest, TCGv_ptr env, TCGv src);
47
48static bool check_plv(DisasContext *ctx)
49{
50    if (ctx->plv == MMU_PLV_USER) {
51        generate_exception(ctx, EXCCODE_IPE);
52        return true;
53    }
54    return false;
55}
56
57static bool set_csr_trans_func(unsigned int csr_num, GenCSRRead readfn,
58                               GenCSRWrite writefn)
59{
60    CSRInfo *csr;
61
62    csr = get_csr(csr_num);
63    if (!csr) {
64        return false;
65    }
66
67    csr->readfn = (GenCSRFunc)readfn;
68    csr->writefn = (GenCSRFunc)writefn;
69    return true;
70}
71
72#define SET_CSR_FUNC(NAME, read, write)                 \
73        set_csr_trans_func(LOONGARCH_CSR_##NAME, read, write)
74
75void loongarch_csr_translate_init(void)
76{
77    SET_CSR_FUNC(STLBPS, NULL, gen_helper_csrwr_stlbps);
78    SET_CSR_FUNC(ESTAT, NULL, gen_helper_csrwr_estat);
79    SET_CSR_FUNC(ASID,  NULL, gen_helper_csrwr_asid);
80    SET_CSR_FUNC(PGD,   gen_helper_csrrd_pgd, NULL);
81    SET_CSR_FUNC(PWCL,  NULL, gen_helper_csrwr_pwcl);
82    SET_CSR_FUNC(CPUID, gen_helper_csrrd_cpuid, NULL);
83    SET_CSR_FUNC(TCFG,  NULL, gen_helper_csrwr_tcfg);
84    SET_CSR_FUNC(TVAL,  gen_helper_csrrd_tval, NULL);
85    SET_CSR_FUNC(TICLR, NULL, gen_helper_csrwr_ticlr);
86}
87#undef SET_CSR_FUNC
88
89static bool check_csr_flags(DisasContext *ctx, const CSRInfo *csr, bool write)
90{
91    if ((csr->flags & CSRFL_READONLY) && write) {
92        return false;
93    }
94    if ((csr->flags & CSRFL_IO) && translator_io_start(&ctx->base)) {
95        ctx->base.is_jmp = DISAS_EXIT_UPDATE;
96    } else if ((csr->flags & CSRFL_EXITTB) && write) {
97        ctx->base.is_jmp = DISAS_EXIT_UPDATE;
98    }
99    return true;
100}
101
102static bool trans_csrrd(DisasContext *ctx, arg_csrrd *a)
103{
104    TCGv dest;
105    const CSRInfo *csr;
106    GenCSRRead readfn;
107
108    if (check_plv(ctx)) {
109        return false;
110    }
111    csr = get_csr(a->csr);
112    if (csr == NULL) {
113        /* CSR is undefined: read as 0. */
114        dest = tcg_constant_tl(0);
115    } else {
116        check_csr_flags(ctx, csr, false);
117        dest = gpr_dst(ctx, a->rd, EXT_NONE);
118        readfn = (GenCSRRead)csr->readfn;
119        if (readfn) {
120            readfn(dest, tcg_env);
121        } else {
122            tcg_gen_ld_tl(dest, tcg_env, csr->offset);
123        }
124    }
125    gen_set_gpr(a->rd, dest, EXT_NONE);
126    return true;
127}
128
129static bool trans_csrwr(DisasContext *ctx, arg_csrwr *a)
130{
131    TCGv dest, src1;
132    const CSRInfo *csr;
133    GenCSRWrite writefn;
134
135    if (check_plv(ctx)) {
136        return false;
137    }
138    csr = get_csr(a->csr);
139    if (csr == NULL) {
140        /* CSR is undefined: write ignored, read old_value as 0. */
141        gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE);
142        return true;
143    }
144    if (!check_csr_flags(ctx, csr, true)) {
145        /* CSR is readonly: trap. */
146        return false;
147    }
148    src1 = gpr_src(ctx, a->rd, EXT_NONE);
149    writefn = (GenCSRWrite)csr->writefn;
150    if (writefn) {
151        dest = gpr_dst(ctx, a->rd, EXT_NONE);
152        writefn(dest, tcg_env, src1);
153    } else {
154        dest = tcg_temp_new();
155        tcg_gen_ld_tl(dest, tcg_env, csr->offset);
156        tcg_gen_st_tl(src1, tcg_env, csr->offset);
157    }
158    gen_set_gpr(a->rd, dest, EXT_NONE);
159    return true;
160}
161
162static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg *a)
163{
164    TCGv src1, mask, oldv, newv, temp;
165    const CSRInfo *csr;
166    GenCSRWrite writefn;
167
168    if (check_plv(ctx)) {
169        return false;
170    }
171    csr = get_csr(a->csr);
172    if (csr == NULL) {
173        /* CSR is undefined: write ignored, read old_value as 0. */
174        gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE);
175        return true;
176    }
177
178    if (!check_csr_flags(ctx, csr, true)) {
179        /* CSR is readonly: trap. */
180        return false;
181    }
182
183    /* So far only readonly csrs have readfn. */
184    assert(csr->readfn == NULL);
185
186    src1 = gpr_src(ctx, a->rd, EXT_NONE);
187    mask = gpr_src(ctx, a->rj, EXT_NONE);
188    oldv = tcg_temp_new();
189    newv = tcg_temp_new();
190    temp = tcg_temp_new();
191
192    tcg_gen_ld_tl(oldv, tcg_env, csr->offset);
193    tcg_gen_and_tl(newv, src1, mask);
194    tcg_gen_andc_tl(temp, oldv, mask);
195    tcg_gen_or_tl(newv, newv, temp);
196
197    writefn = (GenCSRWrite)csr->writefn;
198    if (writefn) {
199        writefn(oldv, tcg_env, newv);
200    } else {
201        tcg_gen_st_tl(newv, tcg_env, csr->offset);
202    }
203    gen_set_gpr(a->rd, oldv, EXT_NONE);
204    return true;
205}
206
207static bool gen_iocsrrd(DisasContext *ctx, arg_rr *a,
208                        void (*func)(TCGv, TCGv_ptr, TCGv))
209{
210    TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
211    TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
212
213    if (check_plv(ctx)) {
214        return false;
215    }
216    func(dest, tcg_env, src1);
217    return true;
218}
219
220static bool gen_iocsrwr(DisasContext *ctx, arg_rr *a,
221                        void (*func)(TCGv_ptr, TCGv, TCGv))
222{
223    TCGv val = gpr_src(ctx, a->rd, EXT_NONE);
224    TCGv addr = gpr_src(ctx, a->rj, EXT_NONE);
225
226    if (check_plv(ctx)) {
227        return false;
228    }
229    func(tcg_env, addr, val);
230    return true;
231}
232
233TRANS(iocsrrd_b, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_b)
234TRANS(iocsrrd_h, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_h)
235TRANS(iocsrrd_w, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_w)
236TRANS(iocsrrd_d, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_d)
237TRANS(iocsrwr_b, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_b)
238TRANS(iocsrwr_h, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_h)
239TRANS(iocsrwr_w, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_w)
240TRANS(iocsrwr_d, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_d)
241
242static void check_mmu_idx(DisasContext *ctx)
243{
244    if (ctx->mem_idx != MMU_DA_IDX) {
245        tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4);
246        ctx->base.is_jmp = DISAS_EXIT;
247    }
248}
249
250static bool trans_tlbsrch(DisasContext *ctx, arg_tlbsrch *a)
251{
252    if (check_plv(ctx)) {
253        return false;
254    }
255    gen_helper_tlbsrch(tcg_env);
256    return true;
257}
258
259static bool trans_tlbrd(DisasContext *ctx, arg_tlbrd *a)
260{
261    if (check_plv(ctx)) {
262        return false;
263    }
264    gen_helper_tlbrd(tcg_env);
265    return true;
266}
267
268static bool trans_tlbwr(DisasContext *ctx, arg_tlbwr *a)
269{
270    if (check_plv(ctx)) {
271        return false;
272    }
273    gen_helper_tlbwr(tcg_env);
274    check_mmu_idx(ctx);
275    return true;
276}
277
278static bool trans_tlbfill(DisasContext *ctx, arg_tlbfill *a)
279{
280    if (check_plv(ctx)) {
281        return false;
282    }
283    gen_helper_tlbfill(tcg_env);
284    check_mmu_idx(ctx);
285    return true;
286}
287
288static bool trans_tlbclr(DisasContext *ctx, arg_tlbclr *a)
289{
290    if (check_plv(ctx)) {
291        return false;
292    }
293    gen_helper_tlbclr(tcg_env);
294    check_mmu_idx(ctx);
295    return true;
296}
297
298static bool trans_tlbflush(DisasContext *ctx, arg_tlbflush *a)
299{
300    if (check_plv(ctx)) {
301        return false;
302    }
303    gen_helper_tlbflush(tcg_env);
304    check_mmu_idx(ctx);
305    return true;
306}
307
308static bool trans_invtlb(DisasContext *ctx, arg_invtlb *a)
309{
310    TCGv rj = gpr_src(ctx, a->rj, EXT_NONE);
311    TCGv rk = gpr_src(ctx, a->rk, EXT_NONE);
312
313    if (check_plv(ctx)) {
314        return false;
315    }
316
317    switch (a->imm) {
318    case 0:
319    case 1:
320        gen_helper_invtlb_all(tcg_env);
321        break;
322    case 2:
323        gen_helper_invtlb_all_g(tcg_env, tcg_constant_i32(1));
324        break;
325    case 3:
326        gen_helper_invtlb_all_g(tcg_env, tcg_constant_i32(0));
327        break;
328    case 4:
329        gen_helper_invtlb_all_asid(tcg_env, rj);
330        break;
331    case 5:
332        gen_helper_invtlb_page_asid(tcg_env, rj, rk);
333        break;
334    case 6:
335        gen_helper_invtlb_page_asid_or_g(tcg_env, rj, rk);
336        break;
337    default:
338        return false;
339    }
340    ctx->base.is_jmp = DISAS_STOP;
341    return true;
342}
343
344static bool trans_cacop(DisasContext *ctx, arg_cacop *a)
345{
346    /* Treat the cacop as a nop */
347    if (check_plv(ctx)) {
348        return false;
349    }
350    return true;
351}
352
353static bool trans_ldpte(DisasContext *ctx, arg_ldpte *a)
354{
355    TCGv_i32 mem_idx = tcg_constant_i32(ctx->mem_idx);
356    TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
357
358    if (!avail_LSPW(ctx)) {
359        return true;
360    }
361
362    if (check_plv(ctx)) {
363        return false;
364    }
365    gen_helper_ldpte(tcg_env, src1, tcg_constant_tl(a->imm), mem_idx);
366    return true;
367}
368
369static bool trans_lddir(DisasContext *ctx, arg_lddir *a)
370{
371    TCGv_i32 mem_idx = tcg_constant_i32(ctx->mem_idx);
372    TCGv src = gpr_src(ctx, a->rj, EXT_NONE);
373    TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
374
375    if (!avail_LSPW(ctx)) {
376        return true;
377    }
378
379    if (check_plv(ctx)) {
380        return false;
381    }
382    gen_helper_lddir(dest, tcg_env, src, tcg_constant_tl(a->imm), mem_idx);
383    return true;
384}
385
386static bool trans_ertn(DisasContext *ctx, arg_ertn *a)
387{
388    if (check_plv(ctx)) {
389        return false;
390    }
391    gen_helper_ertn(tcg_env);
392    ctx->base.is_jmp = DISAS_EXIT;
393    return true;
394}
395
396static bool trans_dbcl(DisasContext *ctx, arg_dbcl *a)
397{
398    if (check_plv(ctx)) {
399        return false;
400    }
401    generate_exception(ctx, EXCCODE_DBP);
402    return true;
403}
404
405static bool trans_idle(DisasContext *ctx, arg_idle *a)
406{
407    if (check_plv(ctx)) {
408        return false;
409    }
410
411    tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4);
412    gen_helper_idle(tcg_env);
413    ctx->base.is_jmp = DISAS_NORETURN;
414    return true;
415}
416#endif
417