master
inc 424 lines 10.2 KB
Raw
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 "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 static bool check_plv(DisasContext *ctx)
49 {
50 if (ctx->plv == MMU_PLV_USER) {
51 generate_exception(ctx, EXCCODE_IPE);
52 return true;
53 }
54 return false;
55 }
56
57 static bool set_csr_trans_func(unsigned int csr_num, GenCSRRead readfn,
58 GenCSRWrite writefn)
59 {
60 CSRInfo *csr;
61
62 csr = get_csr(csr_num);
63 if (!csr) {
64 return false;
65 }
66
67 csr->readfn = (GenCSRFunc)readfn;
68 csr->writefn = (GenCSRFunc)writefn;
69 return true;
70 }
71
72 #define SET_CSR_FUNC(NAME, read, write) \
73 set_csr_trans_func(LOONGARCH_CSR_##NAME, read, write)
74
75 void loongarch_csr_translate_init(void)
76 {
77 SET_CSR_FUNC(STLBPS, NULL, gen_helper_csrwr_stlbps);
78 SET_CSR_FUNC(ESTAT, NULL, gen_helper_csrwr_estat);
79 SET_CSR_FUNC(ASID, NULL, gen_helper_csrwr_asid);
80 SET_CSR_FUNC(PGD, gen_helper_csrrd_pgd, NULL);
81 SET_CSR_FUNC(PWCL, NULL, gen_helper_csrwr_pwcl);
82 SET_CSR_FUNC(PWCH, NULL, gen_helper_csrwr_pwch);
83 SET_CSR_FUNC(CPUID, gen_helper_csrrd_cpuid, NULL);
84 SET_CSR_FUNC(TCFG, NULL, gen_helper_csrwr_tcfg);
85 SET_CSR_FUNC(TVAL, gen_helper_csrrd_tval, NULL);
86 SET_CSR_FUNC(TICLR, NULL, gen_helper_csrwr_ticlr);
87 SET_CSR_FUNC(MSGIR, gen_helper_csrrd_msgir, NULL);
88 }
89 #undef SET_CSR_FUNC
90
91 static bool check_csr_flags(DisasContext *ctx, const CSRInfo *csr, bool write)
92 {
93 if ((csr->flags & CSRFL_READONLY) && write) {
94 return false;
95 }
96 if ((csr->flags & CSRFL_IO) && translator_io_start(&ctx->base)) {
97 ctx->base.is_jmp = DISAS_EXIT_UPDATE;
98 } else if ((csr->flags & CSRFL_EXITTB) && write) {
99 ctx->base.is_jmp = DISAS_EXIT_UPDATE;
100 }
101 return true;
102 }
103
104 static bool trans_csrrd(DisasContext *ctx, arg_csrrd *a)
105 {
106 TCGv dest;
107 const CSRInfo *csr;
108 GenCSRRead readfn;
109 tcg_target_long offset;
110
111 if (check_plv(ctx)) {
112 return false;
113 }
114 csr = get_csr(a->csr);
115 if (csr == NULL) {
116 /* CSR is undefined: read as 0. */
117 dest = tcg_constant_tl(0);
118 } else {
119 check_csr_flags(ctx, csr, false);
120 dest = gpr_dst(ctx, a->rd, EXT_NONE);
121 readfn = (GenCSRRead)csr->readfn;
122 if (readfn) {
123 readfn(dest, tcg_env);
124 } else {
125 offset = get_csr_offset(csr, 0);
126 tcg_gen_ld_tl(dest, tcg_env, offset);
127 }
128 }
129 gen_set_gpr(a->rd, dest, EXT_NONE);
130 return true;
131 }
132
133 static bool trans_csrwr(DisasContext *ctx, arg_csrwr *a)
134 {
135 TCGv dest, src1;
136 const CSRInfo *csr;
137 GenCSRWrite writefn;
138 tcg_target_long offset;
139
140 if (check_plv(ctx)) {
141 return false;
142 }
143 csr = get_csr(a->csr);
144 if (csr == NULL) {
145 /* CSR is undefined: write ignored, read old_value as 0. */
146 gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE);
147 return true;
148 }
149 if (!check_csr_flags(ctx, csr, true)) {
150 /* CSR is readonly: trap. */
151 return false;
152 }
153 src1 = gpr_src(ctx, a->rd, EXT_NONE);
154 writefn = (GenCSRWrite)csr->writefn;
155 if (writefn) {
156 dest = gpr_dst(ctx, a->rd, EXT_NONE);
157 writefn(dest, tcg_env, src1);
158 } else {
159 dest = tcg_temp_new();
160 offset = get_csr_offset(csr, 0);
161 tcg_gen_ld_tl(dest, tcg_env, offset);
162 tcg_gen_st_tl(src1, tcg_env, offset);
163 }
164 gen_set_gpr(a->rd, dest, EXT_NONE);
165 return true;
166 }
167
168 static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg *a)
169 {
170 TCGv src1, mask, oldv, newv, temp;
171 const CSRInfo *csr;
172 GenCSRWrite writefn;
173 tcg_target_long offset;
174
175 if (check_plv(ctx)) {
176 return false;
177 }
178 csr = get_csr(a->csr);
179 if (csr == NULL) {
180 /* CSR is undefined: write ignored, read old_value as 0. */
181 gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE);
182 return true;
183 }
184
185 if (!check_csr_flags(ctx, csr, true)) {
186 /* CSR is readonly: trap. */
187 return false;
188 }
189
190 /* So far only readonly csrs have readfn. */
191 assert(csr->readfn == NULL);
192
193 src1 = gpr_src(ctx, a->rd, EXT_NONE);
194 mask = gpr_src(ctx, a->rj, EXT_NONE);
195 oldv = tcg_temp_new();
196 newv = tcg_temp_new();
197 temp = tcg_temp_new();
198
199 offset = get_csr_offset(csr, 0);
200 tcg_gen_ld_tl(oldv, tcg_env, offset);
201 tcg_gen_and_tl(newv, src1, mask);
202 tcg_gen_andc_tl(temp, oldv, mask);
203 tcg_gen_or_tl(newv, newv, temp);
204
205 writefn = (GenCSRWrite)csr->writefn;
206 if (writefn) {
207 writefn(oldv, tcg_env, newv);
208 } else {
209 tcg_gen_st_tl(newv, tcg_env, offset);
210 }
211 gen_set_gpr(a->rd, oldv, EXT_NONE);
212 return true;
213 }
214
215 static bool gen_iocsrrd(DisasContext *ctx, arg_rr *a,
216 void (*func)(TCGv, TCGv_ptr, TCGv))
217 {
218 TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
219 TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
220
221 if (check_plv(ctx)) {
222 return false;
223 }
224 func(dest, tcg_env, src1);
225 return true;
226 }
227
228 static bool gen_iocsrwr(DisasContext *ctx, arg_rr *a,
229 void (*func)(TCGv_ptr, TCGv, TCGv))
230 {
231 TCGv val = gpr_src(ctx, a->rd, EXT_NONE);
232 TCGv addr = gpr_src(ctx, a->rj, EXT_NONE);
233
234 if (check_plv(ctx)) {
235 return false;
236 }
237 func(tcg_env, addr, val);
238 return true;
239 }
240
241 TRANS(iocsrrd_b, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_b)
242 TRANS(iocsrrd_h, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_h)
243 TRANS(iocsrrd_w, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_w)
244 TRANS64(iocsrrd_d, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_d)
245 TRANS(iocsrwr_b, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_b)
246 TRANS(iocsrwr_h, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_h)
247 TRANS(iocsrwr_w, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_w)
248 TRANS64(iocsrwr_d, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_d)
249
250 static void check_mmu_idx(DisasContext *ctx)
251 {
252 if (ctx->mem_idx != MMU_DA_IDX) {
253 tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4);
254 ctx->base.is_jmp = DISAS_EXIT;
255 }
256 }
257
258 static bool trans_tlbsrch(DisasContext *ctx, arg_tlbsrch *a)
259 {
260 if (check_plv(ctx)) {
261 return false;
262 }
263 gen_helper_tlbsrch(tcg_env);
264 return true;
265 }
266
267 static bool trans_tlbrd(DisasContext *ctx, arg_tlbrd *a)
268 {
269 if (check_plv(ctx)) {
270 return false;
271 }
272 gen_helper_tlbrd(tcg_env);
273 return true;
274 }
275
276 static bool trans_tlbwr(DisasContext *ctx, arg_tlbwr *a)
277 {
278 if (check_plv(ctx)) {
279 return false;
280 }
281 gen_helper_tlbwr(tcg_env);
282 check_mmu_idx(ctx);
283 return true;
284 }
285
286 static bool trans_tlbfill(DisasContext *ctx, arg_tlbfill *a)
287 {
288 if (check_plv(ctx)) {
289 return false;
290 }
291 gen_helper_tlbfill(tcg_env);
292 check_mmu_idx(ctx);
293 return true;
294 }
295
296 static bool trans_tlbclr(DisasContext *ctx, arg_tlbclr *a)
297 {
298 if (check_plv(ctx)) {
299 return false;
300 }
301 gen_helper_tlbclr(tcg_env);
302 check_mmu_idx(ctx);
303 return true;
304 }
305
306 static bool trans_tlbflush(DisasContext *ctx, arg_tlbflush *a)
307 {
308 if (check_plv(ctx)) {
309 return false;
310 }
311 gen_helper_tlbflush(tcg_env);
312 check_mmu_idx(ctx);
313 return true;
314 }
315
316 static bool trans_invtlb(DisasContext *ctx, arg_invtlb *a)
317 {
318 TCGv rj = gpr_src(ctx, a->rj, EXT_NONE);
319 TCGv rk = gpr_src(ctx, a->rk, EXT_NONE);
320
321 if (check_plv(ctx)) {
322 return false;
323 }
324
325 switch (a->imm) {
326 case 0:
327 case 1:
328 gen_helper_invtlb_all(tcg_env);
329 break;
330 case 2:
331 gen_helper_invtlb_all_g(tcg_env, tcg_constant_i32(1));
332 break;
333 case 3:
334 gen_helper_invtlb_all_g(tcg_env, tcg_constant_i32(0));
335 break;
336 case 4:
337 gen_helper_invtlb_all_asid(tcg_env, rj);
338 break;
339 case 5:
340 gen_helper_invtlb_page_asid(tcg_env, rj, rk);
341 break;
342 case 6:
343 gen_helper_invtlb_page_asid_or_g(tcg_env, rj, rk);
344 break;
345 default:
346 return false;
347 }
348 ctx->base.is_jmp = DISAS_STOP;
349 return true;
350 }
351
352 static bool trans_cacop(DisasContext *ctx, arg_cacop *a)
353 {
354 /* Treat the cacop as a nop */
355 if (check_plv(ctx)) {
356 return false;
357 }
358 return true;
359 }
360
361 static bool trans_ldpte(DisasContext *ctx, arg_ldpte *a)
362 {
363 TCGv_i32 mem_idx = tcg_constant_i32(ctx->mem_idx);
364 TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE);
365
366 if (!avail_LSPW(ctx)) {
367 return true;
368 }
369
370 if (check_plv(ctx)) {
371 return false;
372 }
373 gen_helper_ldpte(tcg_env, src1, tcg_constant_tl(a->imm), mem_idx);
374 return true;
375 }
376
377 static bool trans_lddir(DisasContext *ctx, arg_lddir *a)
378 {
379 TCGv_i32 mem_idx = tcg_constant_i32(ctx->mem_idx);
380 TCGv src = gpr_src(ctx, a->rj, EXT_NONE);
381 TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
382
383 if (!avail_LSPW(ctx)) {
384 return true;
385 }
386
387 if (check_plv(ctx)) {
388 return false;
389 }
390 gen_helper_lddir(dest, tcg_env, src, tcg_constant_i32(a->imm), mem_idx);
391 return true;
392 }
393
394 static bool trans_ertn(DisasContext *ctx, arg_ertn *a)
395 {
396 if (check_plv(ctx)) {
397 return false;
398 }
399 gen_helper_ertn(tcg_env);
400 ctx->base.is_jmp = DISAS_EXIT;
401 return true;
402 }
403
404 static bool trans_dbcl(DisasContext *ctx, arg_dbcl *a)
405 {
406 if (check_plv(ctx)) {
407 return false;
408 }
409 generate_exception(ctx, EXCCODE_DBP);
410 return true;
411 }
412
413 static bool trans_idle(DisasContext *ctx, arg_idle *a)
414 {
415 if (check_plv(ctx)) {
416 return false;
417 }
418
419 tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4);
420 gen_helper_idle(tcg_env);
421 ctx->base.is_jmp = DISAS_NORETURN;
422 return true;
423 }
424 #endif