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 "cpu-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
48typedef struct {
49    int offset;
50    int flags;
51    GenCSRRead readfn;
52    GenCSRWrite writefn;
53} CSRInfo;
54
55enum {
56    CSRFL_READONLY = (1 << 0),
57    CSRFL_EXITTB   = (1 << 1),
58    CSRFL_IO       = (1 << 2),
59};
60
61#define CSR_OFF_FUNCS(NAME, FL, RD, WR)                    \
62    [LOONGARCH_CSR_##NAME] = {                             \
63        .offset = offsetof(CPULoongArchState, CSR_##NAME), \
64        .flags = FL, .readfn = RD, .writefn = WR           \
65    }
66
67#define CSR_OFF_ARRAY(NAME, N)                                \
68    [LOONGARCH_CSR_##NAME(N)] = {                             \
69        .offset = offsetof(CPULoongArchState, CSR_##NAME[N]), \
70        .flags = 0, .readfn = NULL, .writefn = NULL           \
71    }
72
73#define CSR_OFF_FLAGS(NAME, FL) \
74    CSR_OFF_FUNCS(NAME, FL, NULL, NULL)
75
76#define CSR_OFF(NAME) \
77    CSR_OFF_FLAGS(NAME, 0)
78
79static const CSRInfo csr_info[] = {
80    CSR_OFF_FLAGS(CRMD, CSRFL_EXITTB),
81    CSR_OFF(PRMD),
82    CSR_OFF_FLAGS(EUEN, CSRFL_EXITTB),
83    CSR_OFF_FLAGS(MISC, CSRFL_READONLY),
84    CSR_OFF(ECFG),
85    CSR_OFF_FUNCS(ESTAT, CSRFL_EXITTB, NULL, gen_helper_csrwr_estat),
86    CSR_OFF(ERA),
87    CSR_OFF(BADV),
88    CSR_OFF_FLAGS(BADI, CSRFL_READONLY),
89    CSR_OFF(EENTRY),
90    CSR_OFF(TLBIDX),
91    CSR_OFF(TLBEHI),
92    CSR_OFF(TLBELO0),
93    CSR_OFF(TLBELO1),
94    CSR_OFF_FUNCS(ASID, CSRFL_EXITTB, NULL, gen_helper_csrwr_asid),
95    CSR_OFF(PGDL),
96    CSR_OFF(PGDH),
97    CSR_OFF_FUNCS(PGD, CSRFL_READONLY, gen_helper_csrrd_pgd, NULL),
98    CSR_OFF(PWCL),
99    CSR_OFF(PWCH),
100    CSR_OFF(STLBPS),
101    CSR_OFF(RVACFG),
102    CSR_OFF_FUNCS(CPUID, CSRFL_READONLY, gen_helper_csrrd_cpuid, NULL),
103    CSR_OFF_FLAGS(PRCFG1, CSRFL_READONLY),
104    CSR_OFF_FLAGS(PRCFG2, CSRFL_READONLY),
105    CSR_OFF_FLAGS(PRCFG3, CSRFL_READONLY),
106    CSR_OFF_ARRAY(SAVE, 0),
107    CSR_OFF_ARRAY(SAVE, 1),
108    CSR_OFF_ARRAY(SAVE, 2),
109    CSR_OFF_ARRAY(SAVE, 3),
110    CSR_OFF_ARRAY(SAVE, 4),
111    CSR_OFF_ARRAY(SAVE, 5),
112    CSR_OFF_ARRAY(SAVE, 6),
113    CSR_OFF_ARRAY(SAVE, 7),
114    CSR_OFF_ARRAY(SAVE, 8),
115    CSR_OFF_ARRAY(SAVE, 9),
116    CSR_OFF_ARRAY(SAVE, 10),
117    CSR_OFF_ARRAY(SAVE, 11),
118    CSR_OFF_ARRAY(SAVE, 12),
119    CSR_OFF_ARRAY(SAVE, 13),
120    CSR_OFF_ARRAY(SAVE, 14),
121    CSR_OFF_ARRAY(SAVE, 15),
122    CSR_OFF(TID),
123    CSR_OFF_FUNCS(TCFG, CSRFL_IO, NULL, gen_helper_csrwr_tcfg),
124    CSR_OFF_FUNCS(TVAL, CSRFL_READONLY | CSRFL_IO, gen_helper_csrrd_tval, NULL),
125    CSR_OFF(CNTC),
126    CSR_OFF_FUNCS(TICLR, CSRFL_IO, NULL, gen_helper_csrwr_ticlr),
127    CSR_OFF(LLBCTL),
128    CSR_OFF(IMPCTL1),
129    CSR_OFF(IMPCTL2),
130    CSR_OFF(TLBRENTRY),
131    CSR_OFF(TLBRBADV),
132    CSR_OFF(TLBRERA),
133    CSR_OFF(TLBRSAVE),
134    CSR_OFF(TLBRELO0),
135    CSR_OFF(TLBRELO1),
136    CSR_OFF(TLBREHI),
137    CSR_OFF(TLBRPRMD),
138    CSR_OFF(MERRCTL),
139    CSR_OFF(MERRINFO1),
140    CSR_OFF(MERRINFO2),
141    CSR_OFF(MERRENTRY),
142    CSR_OFF(MERRERA),
143    CSR_OFF(MERRSAVE),
144    CSR_OFF(CTAG),
145    CSR_OFF_ARRAY(DMW, 0),
146    CSR_OFF_ARRAY(DMW, 1),
147    CSR_OFF_ARRAY(DMW, 2),
148    CSR_OFF_ARRAY(DMW, 3),
149    CSR_OFF(DBG),
150    CSR_OFF(DERA),
151    CSR_OFF(DSAVE),
152};
153
154static bool check_plv(DisasContext *ctx)
155{
156    if (ctx->plv == MMU_PLV_USER) {
157        generate_exception(ctx, EXCCODE_IPE);
158        return true;
159    }
160    return false;
161}
162
163static const CSRInfo *get_csr(unsigned csr_num)
164{
165    const CSRInfo *csr;
166
167    if (csr_num >= ARRAY_SIZE(csr_info)) {
168        return NULL;
169    }
170    csr = &csr_info[csr_num];
171    if (csr->offset == 0) {
172        return NULL;
173    }
174    return csr;
175}
176
177static bool check_csr_flags(DisasContext *ctx, const CSRInfo *csr, bool write)
178{
179    if ((csr->flags & CSRFL_READONLY) && write) {
180        return false;
181    }
182    if ((csr->flags & CSRFL_IO) && translator_io_start(&ctx->base)) {
183        ctx->base.is_jmp = DISAS_EXIT_UPDATE;
184    } else if ((csr->flags & CSRFL_EXITTB) && write) {
185        ctx->base.is_jmp = DISAS_EXIT_UPDATE;
186    }
187    return true;
188}
189
190static bool trans_csrrd(DisasContext *ctx, arg_csrrd *a)
191{
192    TCGv dest;
193    const CSRInfo *csr;
194
195    if (check_plv(ctx)) {
196        return false;
197    }
198    csr = get_csr(a->csr);
199    if (csr == NULL) {
200        /* CSR is undefined: read as 0. */
201        dest = tcg_constant_tl(0);
202    } else {
203        check_csr_flags(ctx, csr, false);
204        dest = gpr_dst(ctx, a->rd, EXT_NONE);
205        if (csr->readfn) {
206            csr->readfn(dest, tcg_env);
207        } else {
208            tcg_gen_ld_tl(dest, tcg_env, csr->offset);
209        }
210    }
211    gen_set_gpr(a->rd, dest, EXT_NONE);
212    return true;
213}
214
215static bool trans_csrwr(DisasContext *ctx, arg_csrwr *a)
216{
217    TCGv dest, src1;
218    const CSRInfo *csr;
219
220    if (check_plv(ctx)) {
221        return false;
222    }
223    csr = get_csr(a->csr);
224    if (csr == NULL) {
225        /* CSR is undefined: write ignored, read old_value as 0. */
226        gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE);
227        return true;
228    }
229    if (!check_csr_flags(ctx, csr, true)) {
230        /* CSR is readonly: trap. */
231        return false;
232    }
233    src1 = gpr_src(ctx, a->rd, EXT_NONE);
234    if (csr->writefn) {
235        dest = gpr_dst(ctx, a->rd, EXT_NONE);
236        csr->writefn(dest, tcg_env, src1);
237    } else {
238        dest = tcg_temp_new();
239        tcg_gen_ld_tl(dest, tcg_env, csr->offset);
240        tcg_gen_st_tl(src1, tcg_env, csr->offset);
241    }
242    gen_set_gpr(a->rd, dest, EXT_NONE);
243    return true;
244}
245
246static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg *a)
247{
248    TCGv src1, mask, oldv, newv, temp;
249    const CSRInfo *csr;
250
251    if (check_plv(ctx)) {
252        return false;
253    }
254    csr = get_csr(a->csr);
255    if (csr == NULL) {
256        /* CSR is undefined: write ignored, read old_value as 0. */
257        gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE);
258        return true;
259    }
260
261    if (!check_csr_flags(ctx, csr, true)) {
262        /* CSR is readonly: trap. */
263        return false;
264    }
265
266    /* So far only readonly csrs have readfn. */
267    assert(csr->readfn == NULL);
268
269    src1 = gpr_src(ctx, a->rd, EXT_NONE);
270    mask = gpr_src(ctx, a->rj, EXT_NONE);
271    oldv = tcg_temp_new();
272    newv = tcg_temp_new();
273    temp = tcg_temp_new();
274
275    tcg_gen_ld_tl(oldv, tcg_env, csr->offset);
276    tcg_gen_and_tl(newv, src1, mask);
277    tcg_gen_andc_tl(temp, oldv, mask);
278    tcg_gen_or_tl(newv, newv, temp);
279
280    if (csr->writefn) {
281        csr->writefn(oldv, tcg_env, newv);
282    } else {
283        tcg_gen_st_tl(newv, tcg_env, csr->offset);
284    }
285    gen_set_gpr(a->rd, oldv, EXT_NONE);
286    return true;
287}
288
289static bool gen_iocsrrd(DisasContext *ctx, arg_rr *a,
290                        void (*func)(TCGv, TCGv_ptr, TCGv))
291{
292    TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
293    TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
294
295    if (check_plv(ctx)) {
296        return false;
297    }
298    func(dest, tcg_env, src1);
299    return true;
300}
301
302static bool gen_iocsrwr(DisasContext *ctx, arg_rr *a,
303                        void (*func)(TCGv_ptr, TCGv, TCGv))
304{
305    TCGv val = gpr_src(ctx, a->rd, EXT_NONE);
306    TCGv addr = gpr_src(ctx, a->rj, EXT_NONE);
307
308    if (check_plv(ctx)) {
309        return false;
310    }
311    func(tcg_env, addr, val);
312    return true;
313}
314
315TRANS(iocsrrd_b, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_b)
316TRANS(iocsrrd_h, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_h)
317TRANS(iocsrrd_w, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_w)
318TRANS(iocsrrd_d, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_d)
319TRANS(iocsrwr_b, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_b)
320TRANS(iocsrwr_h, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_h)
321TRANS(iocsrwr_w, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_w)
322TRANS(iocsrwr_d, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_d)
323
324static void check_mmu_idx(DisasContext *ctx)
325{
326    if (ctx->mem_idx != MMU_DA_IDX) {
327        tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4);
328        ctx->base.is_jmp = DISAS_EXIT;
329    }
330}
331
332static bool trans_tlbsrch(DisasContext *ctx, arg_tlbsrch *a)
333{
334    if (check_plv(ctx)) {
335        return false;
336    }
337    gen_helper_tlbsrch(tcg_env);
338    return true;
339}
340
341static bool trans_tlbrd(DisasContext *ctx, arg_tlbrd *a)
342{
343    if (check_plv(ctx)) {
344        return false;
345    }
346    gen_helper_tlbrd(tcg_env);
347    return true;
348}
349
350static bool trans_tlbwr(DisasContext *ctx, arg_tlbwr *a)
351{
352    if (check_plv(ctx)) {
353        return false;
354    }
355    gen_helper_tlbwr(tcg_env);
356    check_mmu_idx(ctx);
357    return true;
358}
359
360static bool trans_tlbfill(DisasContext *ctx, arg_tlbfill *a)
361{
362    if (check_plv(ctx)) {
363        return false;
364    }
365    gen_helper_tlbfill(tcg_env);
366    check_mmu_idx(ctx);
367    return true;
368}
369
370static bool trans_tlbclr(DisasContext *ctx, arg_tlbclr *a)
371{
372    if (check_plv(ctx)) {
373        return false;
374    }
375    gen_helper_tlbclr(tcg_env);
376    check_mmu_idx(ctx);
377    return true;
378}
379
380static bool trans_tlbflush(DisasContext *ctx, arg_tlbflush *a)
381{
382    if (check_plv(ctx)) {
383        return false;
384    }
385    gen_helper_tlbflush(tcg_env);
386    check_mmu_idx(ctx);
387    return true;
388}
389
390static bool trans_invtlb(DisasContext *ctx, arg_invtlb *a)
391{
392    TCGv rj = gpr_src(ctx, a->rj, EXT_NONE);
393    TCGv rk = gpr_src(ctx, a->rk, EXT_NONE);
394
395    if (check_plv(ctx)) {
396        return false;
397    }
398
399    switch (a->imm) {
400    case 0:
401    case 1:
402        gen_helper_invtlb_all(tcg_env);
403        break;
404    case 2:
405        gen_helper_invtlb_all_g(tcg_env, tcg_constant_i32(1));
406        break;
407    case 3:
408        gen_helper_invtlb_all_g(tcg_env, tcg_constant_i32(0));
409        break;
410    case 4:
411        gen_helper_invtlb_all_asid(tcg_env, rj);
412        break;
413    case 5:
414        gen_helper_invtlb_page_asid(tcg_env, rj, rk);
415        break;
416    case 6:
417        gen_helper_invtlb_page_asid_or_g(tcg_env, rj, rk);
418        break;
419    default:
420        return false;
421    }
422    ctx->base.is_jmp = DISAS_STOP;
423    return true;
424}
425
426static bool trans_cacop(DisasContext *ctx, arg_cacop *a)
427{
428    /* Treat the cacop as a nop */
429    if (check_plv(ctx)) {
430        return false;
431    }
432    return true;
433}
434
435static bool trans_ldpte(DisasContext *ctx, arg_ldpte *a)
436{
437    TCGv_i32 mem_idx = tcg_constant_i32(ctx->mem_idx);
438    TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
439
440    if (!avail_LSPW(ctx)) {
441        return true;
442    }
443
444    if (check_plv(ctx)) {
445        return false;
446    }
447    gen_helper_ldpte(tcg_env, src1, tcg_constant_tl(a->imm), mem_idx);
448    return true;
449}
450
451static bool trans_lddir(DisasContext *ctx, arg_lddir *a)
452{
453    TCGv_i32 mem_idx = tcg_constant_i32(ctx->mem_idx);
454    TCGv src = gpr_src(ctx, a->rj, EXT_NONE);
455    TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
456
457    if (!avail_LSPW(ctx)) {
458        return true;
459    }
460
461    if (check_plv(ctx)) {
462        return false;
463    }
464    gen_helper_lddir(dest, tcg_env, src, tcg_constant_tl(a->imm), mem_idx);
465    return true;
466}
467
468static bool trans_ertn(DisasContext *ctx, arg_ertn *a)
469{
470    if (check_plv(ctx)) {
471        return false;
472    }
473    gen_helper_ertn(tcg_env);
474    ctx->base.is_jmp = DISAS_EXIT;
475    return true;
476}
477
478static bool trans_dbcl(DisasContext *ctx, arg_dbcl *a)
479{
480    if (check_plv(ctx)) {
481        return false;
482    }
483    generate_exception(ctx, EXCCODE_DBP);
484    return true;
485}
486
487static bool trans_idle(DisasContext *ctx, arg_idle *a)
488{
489    if (check_plv(ctx)) {
490        return false;
491    }
492
493    tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4);
494    gen_helper_idle(tcg_env);
495    ctx->base.is_jmp = DISAS_NORETURN;
496    return true;
497}
498#endif
499