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