1
2
3
4
5
6
7
8
9#include "qemu/osdep.h"
10#include "qemu/guest-random.h"
11
12#include "cpu.h"
13#include "internals.h"
14#include "exec/helper-proto.h"
15#include "exec/exec-all.h"
16#include "exec/cpu_ldst.h"
17#include "exec/log.h"
18#include "cpu-csr.h"
19
20enum {
21 TLBRET_MATCH = 0,
22 TLBRET_BADADDR = 1,
23 TLBRET_NOMATCH = 2,
24 TLBRET_INVALID = 3,
25 TLBRET_DIRTY = 4,
26 TLBRET_RI = 5,
27 TLBRET_XI = 6,
28 TLBRET_PE = 7,
29};
30
31static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical,
32 int *prot, target_ulong address,
33 int access_type, int index, int mmu_idx)
34{
35 LoongArchTLB *tlb = &env->tlb[index];
36 uint64_t plv = mmu_idx;
37 uint64_t tlb_entry, tlb_ppn;
38 uint8_t tlb_ps, n, tlb_v, tlb_d, tlb_plv, tlb_nx, tlb_nr, tlb_rplv;
39
40 if (index >= LOONGARCH_STLB) {
41 tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
42 } else {
43 tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS);
44 }
45 n = (address >> tlb_ps) & 0x1;
46
47 tlb_entry = n ? tlb->tlb_entry1 : tlb->tlb_entry0;
48 tlb_v = FIELD_EX64(tlb_entry, TLBENTRY, V);
49 tlb_d = FIELD_EX64(tlb_entry, TLBENTRY, D);
50 tlb_plv = FIELD_EX64(tlb_entry, TLBENTRY, PLV);
51 tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY, PPN);
52 tlb_nx = FIELD_EX64(tlb_entry, TLBENTRY, NX);
53 tlb_nr = FIELD_EX64(tlb_entry, TLBENTRY, NR);
54 tlb_rplv = FIELD_EX64(tlb_entry, TLBENTRY, RPLV);
55
56
57 if (!tlb_v) {
58 return TLBRET_INVALID;
59 }
60
61 if (access_type == MMU_INST_FETCH && tlb_nx) {
62 return TLBRET_XI;
63 }
64
65 if (access_type == MMU_DATA_LOAD && tlb_nr) {
66 return TLBRET_RI;
67 }
68
69 if (((tlb_rplv == 0) && (plv > tlb_plv)) ||
70 ((tlb_rplv == 1) && (plv != tlb_plv))) {
71 return TLBRET_PE;
72 }
73
74 if ((access_type == MMU_DATA_STORE) && !tlb_d) {
75 return TLBRET_DIRTY;
76 }
77
78
79
80
81
82 *physical = (tlb_ppn << R_TLBENTRY_PPN_SHIFT) |
83 (address & MAKE_64BIT_MASK(0, tlb_ps));
84 *prot = PAGE_READ;
85 if (tlb_d) {
86 *prot |= PAGE_WRITE;
87 }
88 if (!tlb_nx) {
89 *prot |= PAGE_EXEC;
90 }
91 return TLBRET_MATCH;
92}
93
94
95
96
97
98
99
100
101static bool loongarch_tlb_search(CPULoongArchState *env, target_ulong vaddr,
102 int *index)
103{
104 LoongArchTLB *tlb;
105 uint16_t csr_asid, tlb_asid, stlb_idx;
106 uint8_t tlb_e, tlb_ps, tlb_g, stlb_ps;
107 int i, compare_shift;
108 uint64_t vpn, tlb_vppn;
109
110 csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
111 stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS);
112 vpn = (vaddr & TARGET_VIRT_MASK) >> (stlb_ps + 1);
113 stlb_idx = vpn & 0xff;
114 compare_shift = stlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT;
115
116
117 for (i = 0; i < 8; ++i) {
118 tlb = &env->tlb[i * 256 + stlb_idx];
119 tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
120 if (tlb_e) {
121 tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
122 tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
123 tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
124
125 if ((tlb_g == 1 || tlb_asid == csr_asid) &&
126 (vpn == (tlb_vppn >> compare_shift))) {
127 *index = i * 256 + stlb_idx;
128 return true;
129 }
130 }
131 }
132
133
134 for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; ++i) {
135 tlb = &env->tlb[i];
136 tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
137 if (tlb_e) {
138 tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
139 tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
140 tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
141 tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
142 compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT;
143 vpn = (vaddr & TARGET_VIRT_MASK) >> (tlb_ps + 1);
144 if ((tlb_g == 1 || tlb_asid == csr_asid) &&
145 (vpn == (tlb_vppn >> compare_shift))) {
146 *index = i;
147 return true;
148 }
149 }
150 }
151 return false;
152}
153
154static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical,
155 int *prot, target_ulong address,
156 MMUAccessType access_type, int mmu_idx)
157{
158 int index, match;
159
160 match = loongarch_tlb_search(env, address, &index);
161 if (match) {
162 return loongarch_map_tlb_entry(env, physical, prot,
163 address, access_type, index, mmu_idx);
164 }
165
166 return TLBRET_NOMATCH;
167}
168
169static int get_physical_address(CPULoongArchState *env, hwaddr *physical,
170 int *prot, target_ulong address,
171 MMUAccessType access_type, int mmu_idx)
172{
173 int user_mode = mmu_idx == MMU_IDX_USER;
174 int kernel_mode = mmu_idx == MMU_IDX_KERNEL;
175 uint32_t plv, base_c, base_v;
176 int64_t addr_high;
177 uint8_t da = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, DA);
178 uint8_t pg = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PG);
179
180
181 if (da & !pg) {
182 *physical = address & TARGET_PHYS_MASK;
183 *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
184 return TLBRET_MATCH;
185 }
186
187 plv = kernel_mode | (user_mode << R_CSR_DMW_PLV3_SHIFT);
188 base_v = address >> TARGET_VIRT_ADDR_SPACE_BITS;
189
190 for (int i = 0; i < 4; i++) {
191 base_c = env->CSR_DMW[i] >> TARGET_VIRT_ADDR_SPACE_BITS;
192 if ((plv & env->CSR_DMW[i]) && (base_c == base_v)) {
193 *physical = dmw_va2pa(address);
194 *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
195 return TLBRET_MATCH;
196 }
197 }
198
199
200 addr_high = sextract64(address, TARGET_VIRT_ADDR_SPACE_BITS, 16);
201 if (!(addr_high == 0 || addr_high == -1)) {
202 return TLBRET_BADADDR;
203 }
204
205
206 return loongarch_map_address(env, physical, prot, address,
207 access_type, mmu_idx);
208}
209
210hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
211{
212 LoongArchCPU *cpu = LOONGARCH_CPU(cs);
213 CPULoongArchState *env = &cpu->env;
214 hwaddr phys_addr;
215 int prot;
216
217 if (get_physical_address(env, &phys_addr, &prot, addr, MMU_DATA_LOAD,
218 cpu_mmu_index(env, false)) != 0) {
219 return -1;
220 }
221 return phys_addr;
222}
223
224static void raise_mmu_exception(CPULoongArchState *env, target_ulong address,
225 MMUAccessType access_type, int tlb_error)
226{
227 CPUState *cs = env_cpu(env);
228
229 switch (tlb_error) {
230 default:
231 case TLBRET_BADADDR:
232 cs->exception_index = access_type == MMU_INST_FETCH
233 ? EXCCODE_ADEF : EXCCODE_ADEM;
234 break;
235 case TLBRET_NOMATCH:
236
237 if (access_type == MMU_DATA_LOAD) {
238 cs->exception_index = EXCCODE_PIL;
239 } else if (access_type == MMU_DATA_STORE) {
240 cs->exception_index = EXCCODE_PIS;
241 } else if (access_type == MMU_INST_FETCH) {
242 cs->exception_index = EXCCODE_PIF;
243 }
244 env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR, 1);
245 break;
246 case TLBRET_INVALID:
247
248 if (access_type == MMU_DATA_LOAD) {
249 cs->exception_index = EXCCODE_PIL;
250 } else if (access_type == MMU_DATA_STORE) {
251 cs->exception_index = EXCCODE_PIS;
252 } else if (access_type == MMU_INST_FETCH) {
253 cs->exception_index = EXCCODE_PIF;
254 }
255 break;
256 case TLBRET_DIRTY:
257
258 cs->exception_index = EXCCODE_PME;
259 break;
260 case TLBRET_XI:
261
262 cs->exception_index = EXCCODE_PNX;
263 break;
264 case TLBRET_RI:
265
266 cs->exception_index = EXCCODE_PNR;
267 break;
268 case TLBRET_PE:
269
270 cs->exception_index = EXCCODE_PPI;
271 break;
272 }
273
274 if (tlb_error == TLBRET_NOMATCH) {
275 env->CSR_TLBRBADV = address;
276 env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI, VPPN,
277 extract64(address, 13, 35));
278 } else {
279 if (!FIELD_EX64(env->CSR_DBG, CSR_DBG, DST)) {
280 env->CSR_BADV = address;
281 }
282 env->CSR_TLBEHI = address & (TARGET_PAGE_MASK << 1);
283 }
284}
285
286static void invalidate_tlb_entry(CPULoongArchState *env, int index)
287{
288 target_ulong addr, mask, pagesize;
289 uint8_t tlb_ps;
290 LoongArchTLB *tlb = &env->tlb[index];
291
292 int mmu_idx = cpu_mmu_index(env, false);
293 uint8_t tlb_v0 = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, V);
294 uint8_t tlb_v1 = FIELD_EX64(tlb->tlb_entry1, TLBENTRY, V);
295 uint64_t tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
296
297 if (index >= LOONGARCH_STLB) {
298 tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
299 } else {
300 tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS);
301 }
302 pagesize = MAKE_64BIT_MASK(tlb_ps, 1);
303 mask = MAKE_64BIT_MASK(0, tlb_ps + 1);
304
305 if (tlb_v0) {
306 addr = (tlb_vppn << R_TLB_MISC_VPPN_SHIFT) & ~mask;
307 tlb_flush_range_by_mmuidx(env_cpu(env), addr, pagesize,
308 mmu_idx, TARGET_LONG_BITS);
309 }
310
311 if (tlb_v1) {
312 addr = (tlb_vppn << R_TLB_MISC_VPPN_SHIFT) & pagesize;
313 tlb_flush_range_by_mmuidx(env_cpu(env), addr, pagesize,
314 mmu_idx, TARGET_LONG_BITS);
315 }
316}
317
318static void invalidate_tlb(CPULoongArchState *env, int index)
319{
320 LoongArchTLB *tlb;
321 uint16_t csr_asid, tlb_asid, tlb_g;
322
323 csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
324 tlb = &env->tlb[index];
325 tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
326 tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
327 if (tlb_g == 0 && tlb_asid != csr_asid) {
328 return;
329 }
330 invalidate_tlb_entry(env, index);
331}
332
333static void fill_tlb_entry(CPULoongArchState *env, int index)
334{
335 LoongArchTLB *tlb = &env->tlb[index];
336 uint64_t lo0, lo1, csr_vppn;
337 uint16_t csr_asid;
338 uint8_t csr_ps;
339
340 if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) {
341 csr_ps = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI, PS);
342 csr_vppn = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI, VPPN);
343 lo0 = env->CSR_TLBRELO0;
344 lo1 = env->CSR_TLBRELO1;
345 } else {
346 csr_ps = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, PS);
347 csr_vppn = FIELD_EX64(env->CSR_TLBEHI, CSR_TLBEHI, VPPN);
348 lo0 = env->CSR_TLBELO0;
349 lo1 = env->CSR_TLBELO1;
350 }
351
352 if (csr_ps == 0) {
353 qemu_log_mask(CPU_LOG_MMU, "page size is 0\n");
354 }
355
356
357 if (index >= LOONGARCH_STLB) {
358 tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, PS, csr_ps);
359 }
360
361 tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, VPPN, csr_vppn);
362 tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 1);
363 csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
364 tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, ASID, csr_asid);
365
366 tlb->tlb_entry0 = lo0;
367 tlb->tlb_entry1 = lo1;
368}
369
370
371static uint32_t get_random_tlb(uint32_t low, uint32_t high)
372{
373 uint32_t val;
374
375 qemu_guest_getrandom_nofail(&val, sizeof(val));
376 return val % (high - low + 1) + low;
377}
378
379void helper_tlbsrch(CPULoongArchState *env)
380{
381 int index, match;
382
383 if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) {
384 match = loongarch_tlb_search(env, env->CSR_TLBREHI, &index);
385 } else {
386 match = loongarch_tlb_search(env, env->CSR_TLBEHI, &index);
387 }
388
389 if (match) {
390 env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX, index);
391 env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 0);
392 return;
393 }
394
395 env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 1);
396}
397
398void helper_tlbrd(CPULoongArchState *env)
399{
400 LoongArchTLB *tlb;
401 int index;
402 uint8_t tlb_ps, tlb_e;
403
404 index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX);
405 tlb = &env->tlb[index];
406
407 if (index >= LOONGARCH_STLB) {
408 tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
409 } else {
410 tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS);
411 }
412 tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
413
414 if (!tlb_e) {
415
416 env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 1);
417 env->CSR_ASID = FIELD_DP64(env->CSR_ASID, CSR_ASID, ASID, 0);
418 env->CSR_TLBEHI = 0;
419 env->CSR_TLBELO0 = 0;
420 env->CSR_TLBELO1 = 0;
421 env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, PS, 0);
422 } else {
423
424 env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 0);
425 env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX,
426 PS, (tlb_ps & 0x3f));
427 env->CSR_TLBEHI = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN) <<
428 R_TLB_MISC_VPPN_SHIFT;
429 env->CSR_TLBELO0 = tlb->tlb_entry0;
430 env->CSR_TLBELO1 = tlb->tlb_entry1;
431 }
432}
433
434void helper_tlbwr(CPULoongArchState *env)
435{
436 int index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX);
437
438 invalidate_tlb(env, index);
439
440 if (FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, NE)) {
441 env->tlb[index].tlb_misc = FIELD_DP64(env->tlb[index].tlb_misc,
442 TLB_MISC, E, 0);
443 return;
444 }
445
446 fill_tlb_entry(env, index);
447}
448
449void helper_tlbfill(CPULoongArchState *env)
450{
451 uint64_t address, entryhi;
452 int index, set, stlb_idx;
453 uint16_t pagesize, stlb_ps;
454
455 if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) {
456 entryhi = env->CSR_TLBREHI;
457 pagesize = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI, PS);
458 } else {
459 entryhi = env->CSR_TLBEHI;
460 pagesize = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, PS);
461 }
462
463 stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS);
464
465 if (pagesize == stlb_ps) {
466
467 address = entryhi & ~MAKE_64BIT_MASK(0, R_CSR_TLBEHI_VPPN_SHIFT);
468
469
470 set = get_random_tlb(0, 7);
471
472
473 stlb_idx = (address >> (stlb_ps + 1)) & 0xff;
474
475 index = set * 256 + stlb_idx;
476 } else {
477
478 index = get_random_tlb(LOONGARCH_STLB, LOONGARCH_TLB_MAX - 1);
479 }
480
481 invalidate_tlb(env, index);
482 fill_tlb_entry(env, index);
483}
484
485void helper_tlbclr(CPULoongArchState *env)
486{
487 LoongArchTLB *tlb;
488 int i, index;
489 uint16_t csr_asid, tlb_asid, tlb_g;
490
491 csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
492 index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX);
493
494 if (index < LOONGARCH_STLB) {
495
496 for (i = 0; i < 8; i++) {
497 tlb = &env->tlb[i * 256 + (index % 256)];
498 tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
499 tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
500 if (!tlb_g && tlb_asid == csr_asid) {
501 tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
502 }
503 }
504 } else if (index < LOONGARCH_TLB_MAX) {
505
506 for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; i++) {
507 tlb = &env->tlb[i];
508 tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
509 tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
510 if (!tlb_g && tlb_asid == csr_asid) {
511 tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
512 }
513 }
514 }
515
516 tlb_flush(env_cpu(env));
517}
518
519void helper_tlbflush(CPULoongArchState *env)
520{
521 int i, index;
522
523 index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX);
524
525 if (index < LOONGARCH_STLB) {
526
527 for (i = 0; i < 8; i++) {
528 int s_idx = i * 256 + (index % 256);
529 env->tlb[s_idx].tlb_misc = FIELD_DP64(env->tlb[s_idx].tlb_misc,
530 TLB_MISC, E, 0);
531 }
532 } else if (index < LOONGARCH_TLB_MAX) {
533
534 for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; i++) {
535 env->tlb[i].tlb_misc = FIELD_DP64(env->tlb[i].tlb_misc,
536 TLB_MISC, E, 0);
537 }
538 }
539
540 tlb_flush(env_cpu(env));
541}
542
543void helper_invtlb_all(CPULoongArchState *env)
544{
545 for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
546 env->tlb[i].tlb_misc = FIELD_DP64(env->tlb[i].tlb_misc,
547 TLB_MISC, E, 0);
548 }
549 tlb_flush(env_cpu(env));
550}
551
552void helper_invtlb_all_g(CPULoongArchState *env, uint32_t g)
553{
554 for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
555 LoongArchTLB *tlb = &env->tlb[i];
556 uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
557
558 if (tlb_g == g) {
559 tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
560 }
561 }
562 tlb_flush(env_cpu(env));
563}
564
565void helper_invtlb_all_asid(CPULoongArchState *env, target_ulong info)
566{
567 uint16_t asid = info & R_CSR_ASID_ASID_MASK;
568
569 for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
570 LoongArchTLB *tlb = &env->tlb[i];
571 uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
572 uint16_t tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
573
574 if (!tlb_g && (tlb_asid == asid)) {
575 tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
576 }
577 }
578 tlb_flush(env_cpu(env));
579}
580
581void helper_invtlb_page_asid(CPULoongArchState *env, target_ulong info,
582 target_ulong addr)
583{
584 uint16_t asid = info & 0x3ff;
585
586 for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
587 LoongArchTLB *tlb = &env->tlb[i];
588 uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
589 uint16_t tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
590 uint64_t vpn, tlb_vppn;
591 uint8_t tlb_ps, compare_shift;
592
593 if (i >= LOONGARCH_STLB) {
594 tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
595 } else {
596 tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS);
597 }
598 tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
599 vpn = (addr & TARGET_VIRT_MASK) >> (tlb_ps + 1);
600 compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT;
601
602 if (!tlb_g && (tlb_asid == asid) &&
603 (vpn == (tlb_vppn >> compare_shift))) {
604 tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
605 }
606 }
607 tlb_flush(env_cpu(env));
608}
609
610void helper_invtlb_page_asid_or_g(CPULoongArchState *env,
611 target_ulong info, target_ulong addr)
612{
613 uint16_t asid = info & 0x3ff;
614
615 for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
616 LoongArchTLB *tlb = &env->tlb[i];
617 uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
618 uint16_t tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
619 uint64_t vpn, tlb_vppn;
620 uint8_t tlb_ps, compare_shift;
621
622 if (i >= LOONGARCH_STLB) {
623 tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
624 } else {
625 tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS);
626 }
627 tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
628 vpn = (addr & TARGET_VIRT_MASK) >> (tlb_ps + 1);
629 compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT;
630
631 if ((tlb_g || (tlb_asid == asid)) &&
632 (vpn == (tlb_vppn >> compare_shift))) {
633 tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
634 }
635 }
636 tlb_flush(env_cpu(env));
637}
638
639bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
640 MMUAccessType access_type, int mmu_idx,
641 bool probe, uintptr_t retaddr)
642{
643 LoongArchCPU *cpu = LOONGARCH_CPU(cs);
644 CPULoongArchState *env = &cpu->env;
645 hwaddr physical;
646 int prot;
647 int ret;
648
649
650 ret = get_physical_address(env, &physical, &prot, address,
651 access_type, mmu_idx);
652
653 if (ret == TLBRET_MATCH) {
654 tlb_set_page(cs, address & TARGET_PAGE_MASK,
655 physical & TARGET_PAGE_MASK, prot,
656 mmu_idx, TARGET_PAGE_SIZE);
657 qemu_log_mask(CPU_LOG_MMU,
658 "%s address=%" VADDR_PRIx " physical " HWADDR_FMT_plx
659 " prot %d\n", __func__, address, physical, prot);
660 return true;
661 } else {
662 qemu_log_mask(CPU_LOG_MMU,
663 "%s address=%" VADDR_PRIx " ret %d\n", __func__, address,
664 ret);
665 }
666 if (probe) {
667 return false;
668 }
669 raise_mmu_exception(env, address, access_type, ret);
670 cpu_loop_exit_restore(cs, retaddr);
671}
672
673target_ulong helper_lddir(CPULoongArchState *env, target_ulong base,
674 target_ulong level, uint32_t mem_idx)
675{
676 CPUState *cs = env_cpu(env);
677 target_ulong badvaddr, index, phys, ret;
678 int shift;
679 uint64_t dir_base, dir_width;
680 bool huge = (base >> LOONGARCH_PAGE_HUGE_SHIFT) & 0x1;
681
682 badvaddr = env->CSR_TLBRBADV;
683 base = base & TARGET_PHYS_MASK;
684
685
686 shift = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTEWIDTH);
687 shift = (shift + 1) * 3;
688
689 if (huge) {
690 return base;
691 }
692 switch (level) {
693 case 1:
694 dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_BASE);
695 dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_WIDTH);
696 break;
697 case 2:
698 dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_BASE);
699 dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_WIDTH);
700 break;
701 case 3:
702 dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_BASE);
703 dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_WIDTH);
704 break;
705 case 4:
706 dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_BASE);
707 dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_WIDTH);
708 break;
709 default:
710 do_raise_exception(env, EXCCODE_INE, GETPC());
711 return 0;
712 }
713 index = (badvaddr >> dir_base) & ((1 << dir_width) - 1);
714 phys = base | index << shift;
715 ret = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK;
716 return ret;
717}
718
719void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd,
720 uint32_t mem_idx)
721{
722 CPUState *cs = env_cpu(env);
723 target_ulong phys, tmp0, ptindex, ptoffset0, ptoffset1, ps, badv;
724 int shift;
725 bool huge = (base >> LOONGARCH_PAGE_HUGE_SHIFT) & 0x1;
726 uint64_t ptbase = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTBASE);
727 uint64_t ptwidth = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTWIDTH);
728
729 base = base & TARGET_PHYS_MASK;
730
731 if (huge) {
732
733 tmp0 = base ^ (1 << LOONGARCH_PAGE_HUGE_SHIFT);
734
735 tmp0 = ((tmp0 & (1 << LOONGARCH_HGLOBAL_SHIFT)) >>
736 LOONGARCH_HGLOBAL_SHIFT) << R_TLBENTRY_G_SHIFT |
737 (tmp0 & (~(1 << R_TLBENTRY_G_SHIFT)));
738 ps = ptbase + ptwidth - 1;
739 if (odd) {
740 tmp0 += MAKE_64BIT_MASK(ps, 1);
741 }
742 } else {
743
744 shift = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTEWIDTH);
745 shift = (shift + 1) * 3;
746 badv = env->CSR_TLBRBADV;
747
748 ptindex = (badv >> ptbase) & ((1 << ptwidth) - 1);
749 ptindex = ptindex & ~0x1;
750 ptoffset0 = ptindex << shift;
751 ptoffset1 = (ptindex + 1) << shift;
752
753 phys = base | (odd ? ptoffset1 : ptoffset0);
754 tmp0 = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK;
755 ps = ptbase;
756 }
757
758 if (odd) {
759 env->CSR_TLBRELO1 = tmp0;
760 } else {
761 env->CSR_TLBRELO0 = tmp0;
762 }
763 env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI, PS, ps);
764}
765