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