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 typedef void (*GenCSRRead)(TCGv dest, TCGv_ptr env);
11 typedef void (*GenCSRWrite)(TCGv dest, TCGv_ptr env, TCGv src);
21 CSRFL_READONLY = (1 << 0),
22 CSRFL_EXITTB = (1 << 1),
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 \
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 \
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),
47 CSR_OFF_FLAGS(EUEN, CSRFL_EXITTB),
48 CSR_OFF_FLAGS(MISC, CSRFL_READONLY),
50 CSR_OFF_FUNCS(ESTAT, CSRFL_EXITTB, NULL, gen_helper_csrwr_estat),
53 CSR_OFF_FLAGS(BADI, CSRFL_READONLY),
59 CSR_OFF_FUNCS(ASID, CSRFL_EXITTB, NULL, gen_helper_csrwr_asid),
62 CSR_OFF_FUNCS(PGD, CSRFL_READONLY, gen_helper_csrrd_pgd, NULL),
67 [LOONGARCH_CSR_CPUID] = {
68 .offset = (int)offsetof(CPUState, cpu_index)
69 - (int)offsetof(LoongArchCPU, env),
70 .flags = CSRFL_READONLY,
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),
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),
97 CSR_OFF_FUNCS(TICLR, CSRFL_IO, NULL, gen_helper_csrwr_ticlr),
116 CSR_OFF_ARRAY(DMW, 0),
117 CSR_OFF_ARRAY(DMW, 1),
118 CSR_OFF_ARRAY(DMW, 2),
119 CSR_OFF_ARRAY(DMW, 3),
125 static bool check_plv(DisasContext *ctx)
127 if (ctx->base.tb->flags == MMU_USER_IDX) {
128 generate_exception(ctx, EXCCODE_IPE);
134 static const CSRInfo *get_csr(unsigned csr_num)
138 if (csr_num >= ARRAY_SIZE(csr_info)) {
141 csr = &csr_info[csr_num];
142 if (csr->offset == 0) {
148 static bool check_csr_flags(DisasContext *ctx, const CSRInfo *csr, bool write)
150 if ((csr->flags & CSRFL_READONLY) && write) {
153 if ((csr->flags & CSRFL_IO) &&
154 (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT)) {
156 ctx->base.is_jmp = DISAS_EXIT_UPDATE;
157 } else if ((csr->flags & CSRFL_EXITTB) && write) {
158 ctx->base.is_jmp = DISAS_EXIT_UPDATE;
163 static bool trans_csrrd(DisasContext *ctx, arg_csrrd *a)
168 if (check_plv(ctx)) {
171 csr = get_csr(a->csr);
173 /* CSR is undefined: read as 0. */
174 dest = tcg_constant_tl(0);
176 check_csr_flags(ctx, csr, false);
177 dest = gpr_dst(ctx, a->rd, EXT_NONE);
179 csr->readfn(dest, cpu_env);
181 tcg_gen_ld_tl(dest, cpu_env, csr->offset);
184 gen_set_gpr(a->rd, dest, EXT_NONE);
188 static bool trans_csrwr(DisasContext *ctx, arg_csrwr *a)
193 if (check_plv(ctx)) {
196 csr = get_csr(a->csr);
198 /* CSR is undefined: write ignored, read old_value as 0. */
199 gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE);
202 if (!check_csr_flags(ctx, csr, true)) {
203 /* CSR is readonly: trap. */
206 src1 = gpr_src(ctx, a->rd, EXT_NONE);
208 dest = gpr_dst(ctx, a->rd, EXT_NONE);
209 csr->writefn(dest, cpu_env, src1);
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);
215 gen_set_gpr(a->rd, dest, EXT_NONE);
219 static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg *a)
221 TCGv src1, mask, oldv, newv, temp;
224 if (check_plv(ctx)) {
227 csr = get_csr(a->csr);
229 /* CSR is undefined: write ignored, read old_value as 0. */
230 gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE);
234 if (!check_csr_flags(ctx, csr, true)) {
235 /* CSR is readonly: trap. */
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);
254 csr->writefn(oldv, cpu_env, newv);
256 tcg_gen_st_tl(newv, cpu_env, csr->offset);
258 gen_set_gpr(a->rd, oldv, EXT_NONE);
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)) {
275 func(dest, cpu_env, src1);
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)) {
288 func(cpu_env, addr, val);
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;
309 static bool trans_tlbsrch(DisasContext *ctx, arg_tlbsrch *a)
311 if (check_plv(ctx)) {
314 gen_helper_tlbsrch(cpu_env);
318 static bool trans_tlbrd(DisasContext *ctx, arg_tlbrd *a)
320 if (check_plv(ctx)) {
323 gen_helper_tlbrd(cpu_env);
327 static bool trans_tlbwr(DisasContext *ctx, arg_tlbwr *a)
329 if (check_plv(ctx)) {
332 gen_helper_tlbwr(cpu_env);
337 static bool trans_tlbfill(DisasContext *ctx, arg_tlbfill *a)
339 if (check_plv(ctx)) {
342 gen_helper_tlbfill(cpu_env);
347 static bool trans_tlbclr(DisasContext *ctx, arg_tlbclr *a)
349 if (check_plv(ctx)) {
352 gen_helper_tlbclr(cpu_env);
357 static bool trans_tlbflush(DisasContext *ctx, arg_tlbflush *a)
359 if (check_plv(ctx)) {
362 gen_helper_tlbflush(cpu_env);
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)) {
379 gen_helper_invtlb_all(cpu_env);
382 gen_helper_invtlb_all_g(cpu_env, tcg_constant_i32(1));
385 gen_helper_invtlb_all_g(cpu_env, tcg_constant_i32(0));
388 gen_helper_invtlb_all_asid(cpu_env, rj);
391 gen_helper_invtlb_page_asid(cpu_env, rj, rk);
394 gen_helper_invtlb_page_asid_or_g(cpu_env, rj, rk);
399 ctx->base.is_jmp = DISAS_STOP;