target/loongarch: Add TLB instruction support
[qemu.git] / target / loongarch / insn_trans / trans_privileged.c.inc
blobd47918785bdc0b44a7fc7b882da46076e2e197e8
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  */
8 #include "cpu-csr.h"
10 typedef void (*GenCSRRead)(TCGv dest, TCGv_ptr env);
11 typedef void (*GenCSRWrite)(TCGv dest, TCGv_ptr env, TCGv src);
13 typedef struct {
14     int offset;
15     int flags;
16     GenCSRRead readfn;
17     GenCSRWrite writefn;
18 } CSRInfo;
20 enum {
21     CSRFL_READONLY = (1 << 0),
22     CSRFL_EXITTB   = (1 << 1),
23     CSRFL_IO       = (1 << 2),
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     }
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     }
38 #define CSR_OFF_FLAGS(NAME, FL) \
39     CSR_OFF_FUNCS(NAME, FL, NULL, NULL)
41 #define CSR_OFF(NAME) \
42     CSR_OFF_FLAGS(NAME, 0)
44 static 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),
125 static bool check_plv(DisasContext *ctx)
127     if (ctx->base.tb->flags == MMU_USER_IDX) {
128         generate_exception(ctx, EXCCODE_IPE);
129         return true;
130     }
131     return false;
134 static const CSRInfo *get_csr(unsigned csr_num)
136     const CSRInfo *csr;
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;
148 static bool check_csr_flags(DisasContext *ctx, const CSRInfo *csr, bool write)
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;
163 static bool trans_csrrd(DisasContext *ctx, arg_csrrd *a)
165     TCGv dest;
166     const CSRInfo *csr;
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;
188 static bool trans_csrwr(DisasContext *ctx, arg_csrwr *a)
190     TCGv dest, src1;
191     const CSRInfo *csr;
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;
219 static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg *a)
221     TCGv src1, mask, oldv, newv, temp;
222     const CSRInfo *csr;
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     }
234     if (!check_csr_flags(ctx, csr, true)) {
235         /* CSR is readonly: trap. */
236         return false;
237     }
239     /* So far only readonly csrs have readfn. */
240     assert(csr->readfn == NULL);
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();
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);
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);
260     tcg_temp_free(temp);
261     tcg_temp_free(newv);
262     tcg_temp_free(oldv);
263     return true;
266 static bool gen_iocsrrd(DisasContext *ctx, arg_rr *a,
267                         void (*func)(TCGv, TCGv_ptr, TCGv))
269     TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
270     TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
272     if (check_plv(ctx)) {
273         return false;
274     }
275     func(dest, cpu_env, src1);
276     return true;
279 static bool gen_iocsrwr(DisasContext *ctx, arg_rr *a,
280                         void (*func)(TCGv_ptr, TCGv, TCGv))
282     TCGv val = gpr_src(ctx, a->rd, EXT_NONE);
283     TCGv addr = gpr_src(ctx, a->rj, EXT_NONE);
285     if (check_plv(ctx)) {
286         return false;
287     }
288     func(cpu_env, addr, val);
289     return true;
292 TRANS(iocsrrd_b, gen_iocsrrd, gen_helper_iocsrrd_b)
293 TRANS(iocsrrd_h, gen_iocsrrd, gen_helper_iocsrrd_h)
294 TRANS(iocsrrd_w, gen_iocsrrd, gen_helper_iocsrrd_w)
295 TRANS(iocsrrd_d, gen_iocsrrd, gen_helper_iocsrrd_d)
296 TRANS(iocsrwr_b, gen_iocsrwr, gen_helper_iocsrwr_b)
297 TRANS(iocsrwr_h, gen_iocsrwr, gen_helper_iocsrwr_h)
298 TRANS(iocsrwr_w, gen_iocsrwr, gen_helper_iocsrwr_w)
299 TRANS(iocsrwr_d, gen_iocsrwr, gen_helper_iocsrwr_d)
301 static void check_mmu_idx(DisasContext *ctx)
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     }
309 static bool trans_tlbsrch(DisasContext *ctx, arg_tlbsrch *a)
311     if (check_plv(ctx)) {
312         return false;
313     }
314     gen_helper_tlbsrch(cpu_env);
315     return true;
318 static bool trans_tlbrd(DisasContext *ctx, arg_tlbrd *a)
320     if (check_plv(ctx)) {
321         return false;
322     }
323     gen_helper_tlbrd(cpu_env);
324     return true;
327 static bool trans_tlbwr(DisasContext *ctx, arg_tlbwr *a)
329     if (check_plv(ctx)) {
330         return false;
331     }
332     gen_helper_tlbwr(cpu_env);
333     check_mmu_idx(ctx);
334     return true;
337 static bool trans_tlbfill(DisasContext *ctx, arg_tlbfill *a)
339     if (check_plv(ctx)) {
340         return false;
341     }
342     gen_helper_tlbfill(cpu_env);
343     check_mmu_idx(ctx);
344     return true;
347 static bool trans_tlbclr(DisasContext *ctx, arg_tlbclr *a)
349     if (check_plv(ctx)) {
350         return false;
351     }
352     gen_helper_tlbclr(cpu_env);
353     check_mmu_idx(ctx);
354     return true;
357 static bool trans_tlbflush(DisasContext *ctx, arg_tlbflush *a)
359     if (check_plv(ctx)) {
360         return false;
361     }
362     gen_helper_tlbflush(cpu_env);
363     check_mmu_idx(ctx);
364     return true;
367 static bool trans_invtlb(DisasContext *ctx, arg_invtlb *a)
369     TCGv rj = gpr_src(ctx, a->rj, EXT_NONE);
370     TCGv rk = gpr_src(ctx, a->rk, EXT_NONE);
372     if (check_plv(ctx)) {
373         return false;
374     }
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;