1 /* SPDX-License-Identifier: GPL-2.0-or-later */
3 * Copyright (c) 2021 Loongson Technology Corporation Limited
5 * LoongArch translation routines for the privileged instructions.
10 #ifdef CONFIG_USER_ONLY
12 #define GEN_FALSE_TRANS(name) \
13 static bool trans_##name(DisasContext *ctx, arg_##name * a) \
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)
45 typedef void (*GenCSRRead)(TCGv dest, TCGv_ptr env);
46 typedef void (*GenCSRWrite)(TCGv dest, TCGv_ptr env, TCGv src);
56 CSRFL_READONLY = (1 << 0),
57 CSRFL_EXITTB = (1 << 1),
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 \
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 \
73 #define CSR_OFF_FLAGS(NAME, FL) \
74 CSR_OFF_FUNCS(NAME, FL, NULL, NULL)
76 #define CSR_OFF(NAME) \
77 CSR_OFF_FLAGS(NAME, 0)
79 static const CSRInfo csr_info[] = {
80 CSR_OFF_FLAGS(CRMD, CSRFL_EXITTB),
82 CSR_OFF_FLAGS(EUEN, CSRFL_EXITTB),
83 CSR_OFF_FLAGS(MISC, CSRFL_READONLY),
85 CSR_OFF_FUNCS(ESTAT, CSRFL_EXITTB, NULL, gen_helper_csrwr_estat),
88 CSR_OFF_FLAGS(BADI, CSRFL_READONLY),
94 CSR_OFF_FUNCS(ASID, CSRFL_EXITTB, NULL, gen_helper_csrwr_asid),
97 CSR_OFF_FUNCS(PGD, CSRFL_READONLY, gen_helper_csrrd_pgd, NULL),
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),
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),
126 CSR_OFF_FUNCS(TICLR, CSRFL_IO, NULL, gen_helper_csrwr_ticlr),
145 CSR_OFF_ARRAY(DMW, 0),
146 CSR_OFF_ARRAY(DMW, 1),
147 CSR_OFF_ARRAY(DMW, 2),
148 CSR_OFF_ARRAY(DMW, 3),
154 static bool check_plv(DisasContext *ctx)
156 if (ctx->plv == MMU_PLV_USER) {
157 generate_exception(ctx, EXCCODE_IPE);
163 static const CSRInfo *get_csr(unsigned csr_num)
167 if (csr_num >= ARRAY_SIZE(csr_info)) {
170 csr = &csr_info[csr_num];
171 if (csr->offset == 0) {
177 static bool check_csr_flags(DisasContext *ctx, const CSRInfo *csr, bool write)
179 if ((csr->flags & CSRFL_READONLY) && write) {
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;
190 static bool trans_csrrd(DisasContext *ctx, arg_csrrd *a)
195 if (check_plv(ctx)) {
198 csr = get_csr(a->csr);
200 /* CSR is undefined: read as 0. */
201 dest = tcg_constant_tl(0);
203 check_csr_flags(ctx, csr, false);
204 dest = gpr_dst(ctx, a->rd, EXT_NONE);
206 csr->readfn(dest, tcg_env);
208 tcg_gen_ld_tl(dest, tcg_env, csr->offset);
211 gen_set_gpr(a->rd, dest, EXT_NONE);
215 static bool trans_csrwr(DisasContext *ctx, arg_csrwr *a)
220 if (check_plv(ctx)) {
223 csr = get_csr(a->csr);
225 /* CSR is undefined: write ignored, read old_value as 0. */
226 gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE);
229 if (!check_csr_flags(ctx, csr, true)) {
230 /* CSR is readonly: trap. */
233 src1 = gpr_src(ctx, a->rd, EXT_NONE);
235 dest = gpr_dst(ctx, a->rd, EXT_NONE);
236 csr->writefn(dest, tcg_env, src1);
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);
242 gen_set_gpr(a->rd, dest, EXT_NONE);
246 static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg *a)
248 TCGv src1, mask, oldv, newv, temp;
251 if (check_plv(ctx)) {
254 csr = get_csr(a->csr);
256 /* CSR is undefined: write ignored, read old_value as 0. */
257 gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE);
261 if (!check_csr_flags(ctx, csr, true)) {
262 /* CSR is readonly: trap. */
266 /* So far only readonly csrs have readfn. */
267 assert(csr->readfn == NULL);
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();
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);
281 csr->writefn(oldv, tcg_env, newv);
283 tcg_gen_st_tl(newv, tcg_env, csr->offset);
285 gen_set_gpr(a->rd, oldv, EXT_NONE);
289 static bool gen_iocsrrd(DisasContext *ctx, arg_rr *a,
290 void (*func)(TCGv, TCGv_ptr, TCGv))
292 TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
293 TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
295 if (check_plv(ctx)) {
298 func(dest, tcg_env, src1);
302 static bool gen_iocsrwr(DisasContext *ctx, arg_rr *a,
303 void (*func)(TCGv_ptr, TCGv, TCGv))
305 TCGv val = gpr_src(ctx, a->rd, EXT_NONE);
306 TCGv addr = gpr_src(ctx, a->rj, EXT_NONE);
308 if (check_plv(ctx)) {
311 func(tcg_env, addr, val);
315 TRANS(iocsrrd_b, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_b)
316 TRANS(iocsrrd_h, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_h)
317 TRANS(iocsrrd_w, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_w)
318 TRANS(iocsrrd_d, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_d)
319 TRANS(iocsrwr_b, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_b)
320 TRANS(iocsrwr_h, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_h)
321 TRANS(iocsrwr_w, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_w)
322 TRANS(iocsrwr_d, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_d)
324 static void check_mmu_idx(DisasContext *ctx)
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;
332 static bool trans_tlbsrch(DisasContext *ctx, arg_tlbsrch *a)
334 if (check_plv(ctx)) {
337 gen_helper_tlbsrch(tcg_env);
341 static bool trans_tlbrd(DisasContext *ctx, arg_tlbrd *a)
343 if (check_plv(ctx)) {
346 gen_helper_tlbrd(tcg_env);
350 static bool trans_tlbwr(DisasContext *ctx, arg_tlbwr *a)
352 if (check_plv(ctx)) {
355 gen_helper_tlbwr(tcg_env);
360 static bool trans_tlbfill(DisasContext *ctx, arg_tlbfill *a)
362 if (check_plv(ctx)) {
365 gen_helper_tlbfill(tcg_env);
370 static bool trans_tlbclr(DisasContext *ctx, arg_tlbclr *a)
372 if (check_plv(ctx)) {
375 gen_helper_tlbclr(tcg_env);
380 static bool trans_tlbflush(DisasContext *ctx, arg_tlbflush *a)
382 if (check_plv(ctx)) {
385 gen_helper_tlbflush(tcg_env);
390 static bool trans_invtlb(DisasContext *ctx, arg_invtlb *a)
392 TCGv rj = gpr_src(ctx, a->rj, EXT_NONE);
393 TCGv rk = gpr_src(ctx, a->rk, EXT_NONE);
395 if (check_plv(ctx)) {
402 gen_helper_invtlb_all(tcg_env);
405 gen_helper_invtlb_all_g(tcg_env, tcg_constant_i32(1));
408 gen_helper_invtlb_all_g(tcg_env, tcg_constant_i32(0));
411 gen_helper_invtlb_all_asid(tcg_env, rj);
414 gen_helper_invtlb_page_asid(tcg_env, rj, rk);
417 gen_helper_invtlb_page_asid_or_g(tcg_env, rj, rk);
422 ctx->base.is_jmp = DISAS_STOP;
426 static bool trans_cacop(DisasContext *ctx, arg_cacop *a)
428 /* Treat the cacop as a nop */
429 if (check_plv(ctx)) {
435 static bool trans_ldpte(DisasContext *ctx, arg_ldpte *a)
437 TCGv_i32 mem_idx = tcg_constant_i32(ctx->mem_idx);
438 TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
440 if (!avail_LSPW(ctx)) {
444 if (check_plv(ctx)) {
447 gen_helper_ldpte(tcg_env, src1, tcg_constant_tl(a->imm), mem_idx);
451 static bool trans_lddir(DisasContext *ctx, arg_lddir *a)
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);
457 if (!avail_LSPW(ctx)) {
461 if (check_plv(ctx)) {
464 gen_helper_lddir(dest, tcg_env, src, tcg_constant_tl(a->imm), mem_idx);
468 static bool trans_ertn(DisasContext *ctx, arg_ertn *a)
470 if (check_plv(ctx)) {
473 gen_helper_ertn(tcg_env);
474 ctx->base.is_jmp = DISAS_EXIT;
478 static bool trans_dbcl(DisasContext *ctx, arg_dbcl *a)
480 if (check_plv(ctx)) {
483 generate_exception(ctx, EXCCODE_DBP);
487 static bool trans_idle(DisasContext *ctx, arg_idle *a)
489 if (check_plv(ctx)) {
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;