]> git.proxmox.com Git - mirror_qemu.git/blob - target/loongarch/insn_trans/trans_privileged.c.inc
target/loongarch: Fix the CSRRD CPUID instruction on big endian hosts
[mirror_qemu.git] / target / loongarch / insn_trans / trans_privileged.c.inc
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) \
13 static bool trans_##name(DisasContext *ctx, arg_##name * a) \
14 { \
15 return false; \
16 }
17
18 GEN_FALSE_TRANS(csrrd)
19 GEN_FALSE_TRANS(csrwr)
20 GEN_FALSE_TRANS(csrxchg)
21 GEN_FALSE_TRANS(iocsrrd_b)
22 GEN_FALSE_TRANS(iocsrrd_h)
23 GEN_FALSE_TRANS(iocsrrd_w)
24 GEN_FALSE_TRANS(iocsrrd_d)
25 GEN_FALSE_TRANS(iocsrwr_b)
26 GEN_FALSE_TRANS(iocsrwr_h)
27 GEN_FALSE_TRANS(iocsrwr_w)
28 GEN_FALSE_TRANS(iocsrwr_d)
29 GEN_FALSE_TRANS(tlbsrch)
30 GEN_FALSE_TRANS(tlbrd)
31 GEN_FALSE_TRANS(tlbwr)
32 GEN_FALSE_TRANS(tlbfill)
33 GEN_FALSE_TRANS(tlbclr)
34 GEN_FALSE_TRANS(tlbflush)
35 GEN_FALSE_TRANS(invtlb)
36 GEN_FALSE_TRANS(cacop)
37 GEN_FALSE_TRANS(ldpte)
38 GEN_FALSE_TRANS(lddir)
39 GEN_FALSE_TRANS(ertn)
40 GEN_FALSE_TRANS(dbcl)
41 GEN_FALSE_TRANS(idle)
42
43 #else
44
45 typedef void (*GenCSRRead)(TCGv dest, TCGv_ptr env);
46 typedef void (*GenCSRWrite)(TCGv dest, TCGv_ptr env, TCGv src);
47
48 typedef struct {
49 int offset;
50 int flags;
51 GenCSRRead readfn;
52 GenCSRWrite writefn;
53 } CSRInfo;
54
55 enum {
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
79 static 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
154 static 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
163 static 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
177 static 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
190 static 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, cpu_env);
207 } else {
208 tcg_gen_ld_tl(dest, cpu_env, csr->offset);
209 }
210 }
211 gen_set_gpr(a->rd, dest, EXT_NONE);
212 return true;
213 }
214
215 static 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, cpu_env, src1);
237 } else {
238 dest = tcg_temp_new();
239 tcg_gen_ld_tl(dest, cpu_env, csr->offset);
240 tcg_gen_st_tl(src1, cpu_env, csr->offset);
241 }
242 gen_set_gpr(a->rd, dest, EXT_NONE);
243 return true;
244 }
245
246 static 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, cpu_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, cpu_env, newv);
282 } else {
283 tcg_gen_st_tl(newv, cpu_env, csr->offset);
284 }
285 gen_set_gpr(a->rd, oldv, EXT_NONE);
286 return true;
287 }
288
289 static 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, cpu_env, src1);
299 return true;
300 }
301
302 static 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(cpu_env, addr, val);
312 return true;
313 }
314
315 TRANS(iocsrrd_b, gen_iocsrrd, gen_helper_iocsrrd_b)
316 TRANS(iocsrrd_h, gen_iocsrrd, gen_helper_iocsrrd_h)
317 TRANS(iocsrrd_w, gen_iocsrrd, gen_helper_iocsrrd_w)
318 TRANS(iocsrrd_d, gen_iocsrrd, gen_helper_iocsrrd_d)
319 TRANS(iocsrwr_b, gen_iocsrwr, gen_helper_iocsrwr_b)
320 TRANS(iocsrwr_h, gen_iocsrwr, gen_helper_iocsrwr_h)
321 TRANS(iocsrwr_w, gen_iocsrwr, gen_helper_iocsrwr_w)
322 TRANS(iocsrwr_d, gen_iocsrwr, gen_helper_iocsrwr_d)
323
324 static void check_mmu_idx(DisasContext *ctx)
325 {
326 if (ctx->mem_idx != MMU_IDX_DA) {
327 tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4);
328 ctx->base.is_jmp = DISAS_EXIT;
329 }
330 }
331
332 static bool trans_tlbsrch(DisasContext *ctx, arg_tlbsrch *a)
333 {
334 if (check_plv(ctx)) {
335 return false;
336 }
337 gen_helper_tlbsrch(cpu_env);
338 return true;
339 }
340
341 static bool trans_tlbrd(DisasContext *ctx, arg_tlbrd *a)
342 {
343 if (check_plv(ctx)) {
344 return false;
345 }
346 gen_helper_tlbrd(cpu_env);
347 return true;
348 }
349
350 static bool trans_tlbwr(DisasContext *ctx, arg_tlbwr *a)
351 {
352 if (check_plv(ctx)) {
353 return false;
354 }
355 gen_helper_tlbwr(cpu_env);
356 check_mmu_idx(ctx);
357 return true;
358 }
359
360 static bool trans_tlbfill(DisasContext *ctx, arg_tlbfill *a)
361 {
362 if (check_plv(ctx)) {
363 return false;
364 }
365 gen_helper_tlbfill(cpu_env);
366 check_mmu_idx(ctx);
367 return true;
368 }
369
370 static bool trans_tlbclr(DisasContext *ctx, arg_tlbclr *a)
371 {
372 if (check_plv(ctx)) {
373 return false;
374 }
375 gen_helper_tlbclr(cpu_env);
376 check_mmu_idx(ctx);
377 return true;
378 }
379
380 static bool trans_tlbflush(DisasContext *ctx, arg_tlbflush *a)
381 {
382 if (check_plv(ctx)) {
383 return false;
384 }
385 gen_helper_tlbflush(cpu_env);
386 check_mmu_idx(ctx);
387 return true;
388 }
389
390 static 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(cpu_env);
403 break;
404 case 2:
405 gen_helper_invtlb_all_g(cpu_env, tcg_constant_i32(1));
406 break;
407 case 3:
408 gen_helper_invtlb_all_g(cpu_env, tcg_constant_i32(0));
409 break;
410 case 4:
411 gen_helper_invtlb_all_asid(cpu_env, rj);
412 break;
413 case 5:
414 gen_helper_invtlb_page_asid(cpu_env, rj, rk);
415 break;
416 case 6:
417 gen_helper_invtlb_page_asid_or_g(cpu_env, rj, rk);
418 break;
419 default:
420 return false;
421 }
422 ctx->base.is_jmp = DISAS_STOP;
423 return true;
424 }
425
426 static 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
435 static 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 (check_plv(ctx)) {
441 return false;
442 }
443 gen_helper_ldpte(cpu_env, src1, tcg_constant_tl(a->imm), mem_idx);
444 return true;
445 }
446
447 static bool trans_lddir(DisasContext *ctx, arg_lddir *a)
448 {
449 TCGv_i32 mem_idx = tcg_constant_i32(ctx->mem_idx);
450 TCGv src = gpr_src(ctx, a->rj, EXT_NONE);
451 TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
452
453 if (check_plv(ctx)) {
454 return false;
455 }
456 gen_helper_lddir(dest, cpu_env, src, tcg_constant_tl(a->imm), mem_idx);
457 return true;
458 }
459
460 static bool trans_ertn(DisasContext *ctx, arg_ertn *a)
461 {
462 if (check_plv(ctx)) {
463 return false;
464 }
465 gen_helper_ertn(cpu_env);
466 ctx->base.is_jmp = DISAS_EXIT;
467 return true;
468 }
469
470 static bool trans_dbcl(DisasContext *ctx, arg_dbcl *a)
471 {
472 if (check_plv(ctx)) {
473 return false;
474 }
475 generate_exception(ctx, EXCCODE_DBP);
476 return true;
477 }
478
479 static bool trans_idle(DisasContext *ctx, arg_idle *a)
480 {
481 if (check_plv(ctx)) {
482 return false;
483 }
484
485 tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4);
486 gen_helper_idle(cpu_env);
487 ctx->base.is_jmp = DISAS_NORETURN;
488 return true;
489 }
490 #endif