xref: /openbmc/qemu/target/loongarch/tcg/tlb_helper.c (revision a158c63b)
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3  * QEMU LoongArch TLB helpers
4  *
5  * Copyright (c) 2021 Loongson Technology Corporation Limited
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 
20 static void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base,
21                                uint64_t *dir_width, target_ulong level)
22 {
23     switch (level) {
24     case 1:
25         *dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_BASE);
26         *dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_WIDTH);
27         break;
28     case 2:
29         *dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_BASE);
30         *dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_WIDTH);
31         break;
32     case 3:
33         *dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_BASE);
34         *dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_WIDTH);
35         break;
36     case 4:
37         *dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_BASE);
38         *dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_WIDTH);
39         break;
40     default:
41         /* level may be zero for ldpte */
42         *dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTBASE);
43         *dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTWIDTH);
44         break;
45     }
46 }
47 
48 static void raise_mmu_exception(CPULoongArchState *env, target_ulong address,
49                                 MMUAccessType access_type, int tlb_error)
50 {
51     CPUState *cs = env_cpu(env);
52 
53     switch (tlb_error) {
54     default:
55     case TLBRET_BADADDR:
56         cs->exception_index = access_type == MMU_INST_FETCH
57                               ? EXCCODE_ADEF : EXCCODE_ADEM;
58         break;
59     case TLBRET_NOMATCH:
60         /* No TLB match for a mapped address */
61         if (access_type == MMU_DATA_LOAD) {
62             cs->exception_index = EXCCODE_PIL;
63         } else if (access_type == MMU_DATA_STORE) {
64             cs->exception_index = EXCCODE_PIS;
65         } else if (access_type == MMU_INST_FETCH) {
66             cs->exception_index = EXCCODE_PIF;
67         }
68         env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR, 1);
69         break;
70     case TLBRET_INVALID:
71         /* TLB match with no valid bit */
72         if (access_type == MMU_DATA_LOAD) {
73             cs->exception_index = EXCCODE_PIL;
74         } else if (access_type == MMU_DATA_STORE) {
75             cs->exception_index = EXCCODE_PIS;
76         } else if (access_type == MMU_INST_FETCH) {
77             cs->exception_index = EXCCODE_PIF;
78         }
79         break;
80     case TLBRET_DIRTY:
81         /* TLB match but 'D' bit is cleared */
82         cs->exception_index = EXCCODE_PME;
83         break;
84     case TLBRET_XI:
85         /* Execute-Inhibit Exception */
86         cs->exception_index = EXCCODE_PNX;
87         break;
88     case TLBRET_RI:
89         /* Read-Inhibit Exception */
90         cs->exception_index = EXCCODE_PNR;
91         break;
92     case TLBRET_PE:
93         /* Privileged Exception */
94         cs->exception_index = EXCCODE_PPI;
95         break;
96     }
97 
98     if (tlb_error == TLBRET_NOMATCH) {
99         env->CSR_TLBRBADV = address;
100         if (is_la64(env)) {
101             env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI_64,
102                                         VPPN, extract64(address, 13, 35));
103         } else {
104             env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI_32,
105                                         VPPN, extract64(address, 13, 19));
106         }
107     } else {
108         if (!FIELD_EX64(env->CSR_DBG, CSR_DBG, DST)) {
109             env->CSR_BADV = address;
110         }
111         env->CSR_TLBEHI = address & (TARGET_PAGE_MASK << 1);
112    }
113 }
114 
115 static void invalidate_tlb_entry(CPULoongArchState *env, int index)
116 {
117     target_ulong addr, mask, pagesize;
118     uint8_t tlb_ps;
119     LoongArchTLB *tlb = &env->tlb[index];
120 
121     int mmu_idx = cpu_mmu_index(env_cpu(env), false);
122     uint8_t tlb_v0 = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, V);
123     uint8_t tlb_v1 = FIELD_EX64(tlb->tlb_entry1, TLBENTRY, V);
124     uint64_t tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
125 
126     if (index >= LOONGARCH_STLB) {
127         tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
128     } else {
129         tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS);
130     }
131     pagesize = MAKE_64BIT_MASK(tlb_ps, 1);
132     mask = MAKE_64BIT_MASK(0, tlb_ps + 1);
133 
134     if (tlb_v0) {
135         addr = (tlb_vppn << R_TLB_MISC_VPPN_SHIFT) & ~mask;    /* even */
136         tlb_flush_range_by_mmuidx(env_cpu(env), addr, pagesize,
137                                   mmu_idx, TARGET_LONG_BITS);
138     }
139 
140     if (tlb_v1) {
141         addr = (tlb_vppn << R_TLB_MISC_VPPN_SHIFT) & pagesize;    /* odd */
142         tlb_flush_range_by_mmuidx(env_cpu(env), addr, pagesize,
143                                   mmu_idx, TARGET_LONG_BITS);
144     }
145 }
146 
147 static void invalidate_tlb(CPULoongArchState *env, int index)
148 {
149     LoongArchTLB *tlb;
150     uint16_t csr_asid, tlb_asid, tlb_g;
151 
152     csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
153     tlb = &env->tlb[index];
154     tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
155     tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
156     if (tlb_g == 0 && tlb_asid != csr_asid) {
157         return;
158     }
159     invalidate_tlb_entry(env, index);
160 }
161 
162 static void fill_tlb_entry(CPULoongArchState *env, int index)
163 {
164     LoongArchTLB *tlb = &env->tlb[index];
165     uint64_t lo0, lo1, csr_vppn;
166     uint16_t csr_asid;
167     uint8_t csr_ps;
168 
169     if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) {
170         csr_ps = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI, PS);
171         if (is_la64(env)) {
172             csr_vppn = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI_64, VPPN);
173         } else {
174             csr_vppn = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI_32, VPPN);
175         }
176         lo0 = env->CSR_TLBRELO0;
177         lo1 = env->CSR_TLBRELO1;
178     } else {
179         csr_ps = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, PS);
180         if (is_la64(env)) {
181             csr_vppn = FIELD_EX64(env->CSR_TLBEHI, CSR_TLBEHI_64, VPPN);
182         } else {
183             csr_vppn = FIELD_EX64(env->CSR_TLBEHI, CSR_TLBEHI_32, VPPN);
184         }
185         lo0 = env->CSR_TLBELO0;
186         lo1 = env->CSR_TLBELO1;
187     }
188 
189     if (csr_ps == 0) {
190         qemu_log_mask(CPU_LOG_MMU, "page size is 0\n");
191     }
192 
193     /* Only MTLB has the ps fields */
194     if (index >= LOONGARCH_STLB) {
195         tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, PS, csr_ps);
196     }
197 
198     tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, VPPN, csr_vppn);
199     tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 1);
200     csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
201     tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, ASID, csr_asid);
202 
203     tlb->tlb_entry0 = lo0;
204     tlb->tlb_entry1 = lo1;
205 }
206 
207 /* Return an random value between low and high */
208 static uint32_t get_random_tlb(uint32_t low, uint32_t high)
209 {
210     uint32_t val;
211 
212     qemu_guest_getrandom_nofail(&val, sizeof(val));
213     return val % (high - low + 1) + low;
214 }
215 
216 void helper_tlbsrch(CPULoongArchState *env)
217 {
218     int index, match;
219 
220     if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) {
221         match = loongarch_tlb_search(env, env->CSR_TLBREHI, &index);
222     } else {
223         match = loongarch_tlb_search(env, env->CSR_TLBEHI, &index);
224     }
225 
226     if (match) {
227         env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX, index);
228         env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 0);
229         return;
230     }
231 
232     env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 1);
233 }
234 
235 void helper_tlbrd(CPULoongArchState *env)
236 {
237     LoongArchTLB *tlb;
238     int index;
239     uint8_t tlb_ps, tlb_e;
240 
241     index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX);
242     tlb = &env->tlb[index];
243 
244     if (index >= LOONGARCH_STLB) {
245         tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
246     } else {
247         tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS);
248     }
249     tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
250 
251     if (!tlb_e) {
252         /* Invalid TLB entry */
253         env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 1);
254         env->CSR_ASID  = FIELD_DP64(env->CSR_ASID, CSR_ASID, ASID, 0);
255         env->CSR_TLBEHI = 0;
256         env->CSR_TLBELO0 = 0;
257         env->CSR_TLBELO1 = 0;
258         env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, PS, 0);
259     } else {
260         /* Valid TLB entry */
261         env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 0);
262         env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX,
263                                      PS, (tlb_ps & 0x3f));
264         env->CSR_TLBEHI = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN) <<
265                                      R_TLB_MISC_VPPN_SHIFT;
266         env->CSR_TLBELO0 = tlb->tlb_entry0;
267         env->CSR_TLBELO1 = tlb->tlb_entry1;
268     }
269 }
270 
271 void helper_tlbwr(CPULoongArchState *env)
272 {
273     int index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX);
274 
275     invalidate_tlb(env, index);
276 
277     if (FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, NE)) {
278         env->tlb[index].tlb_misc = FIELD_DP64(env->tlb[index].tlb_misc,
279                                               TLB_MISC, E, 0);
280         return;
281     }
282 
283     fill_tlb_entry(env, index);
284 }
285 
286 void helper_tlbfill(CPULoongArchState *env)
287 {
288     uint64_t address, entryhi;
289     int index, set, stlb_idx;
290     uint16_t pagesize, stlb_ps;
291 
292     if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) {
293         entryhi = env->CSR_TLBREHI;
294         pagesize = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI, PS);
295     } else {
296         entryhi = env->CSR_TLBEHI;
297         pagesize = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, PS);
298     }
299 
300     stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS);
301 
302     if (pagesize == stlb_ps) {
303         /* Only write into STLB bits [47:13] */
304         address = entryhi & ~MAKE_64BIT_MASK(0, R_CSR_TLBEHI_64_VPPN_SHIFT);
305 
306         /* Choose one set ramdomly */
307         set = get_random_tlb(0, 7);
308 
309         /* Index in one set */
310         stlb_idx = (address >> (stlb_ps + 1)) & 0xff; /* [0,255] */
311 
312         index = set * 256 + stlb_idx;
313     } else {
314         /* Only write into MTLB */
315         index = get_random_tlb(LOONGARCH_STLB, LOONGARCH_TLB_MAX - 1);
316     }
317 
318     invalidate_tlb(env, index);
319     fill_tlb_entry(env, index);
320 }
321 
322 void helper_tlbclr(CPULoongArchState *env)
323 {
324     LoongArchTLB *tlb;
325     int i, index;
326     uint16_t csr_asid, tlb_asid, tlb_g;
327 
328     csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
329     index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX);
330 
331     if (index < LOONGARCH_STLB) {
332         /* STLB. One line per operation */
333         for (i = 0; i < 8; i++) {
334             tlb = &env->tlb[i * 256 + (index % 256)];
335             tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
336             tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
337             if (!tlb_g && tlb_asid == csr_asid) {
338                 tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
339             }
340         }
341     } else if (index < LOONGARCH_TLB_MAX) {
342         /* All MTLB entries */
343         for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; i++) {
344             tlb = &env->tlb[i];
345             tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
346             tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
347             if (!tlb_g && tlb_asid == csr_asid) {
348                 tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
349             }
350         }
351     }
352 
353     tlb_flush(env_cpu(env));
354 }
355 
356 void helper_tlbflush(CPULoongArchState *env)
357 {
358     int i, index;
359 
360     index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX);
361 
362     if (index < LOONGARCH_STLB) {
363         /* STLB. One line per operation */
364         for (i = 0; i < 8; i++) {
365             int s_idx = i * 256 + (index % 256);
366             env->tlb[s_idx].tlb_misc = FIELD_DP64(env->tlb[s_idx].tlb_misc,
367                                                   TLB_MISC, E, 0);
368         }
369     } else if (index < LOONGARCH_TLB_MAX) {
370         /* All MTLB entries */
371         for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; i++) {
372             env->tlb[i].tlb_misc = FIELD_DP64(env->tlb[i].tlb_misc,
373                                               TLB_MISC, E, 0);
374         }
375     }
376 
377     tlb_flush(env_cpu(env));
378 }
379 
380 void helper_invtlb_all(CPULoongArchState *env)
381 {
382     for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
383         env->tlb[i].tlb_misc = FIELD_DP64(env->tlb[i].tlb_misc,
384                                           TLB_MISC, E, 0);
385     }
386     tlb_flush(env_cpu(env));
387 }
388 
389 void helper_invtlb_all_g(CPULoongArchState *env, uint32_t g)
390 {
391     for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
392         LoongArchTLB *tlb = &env->tlb[i];
393         uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
394 
395         if (tlb_g == g) {
396             tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
397         }
398     }
399     tlb_flush(env_cpu(env));
400 }
401 
402 void helper_invtlb_all_asid(CPULoongArchState *env, target_ulong info)
403 {
404     uint16_t asid = info & R_CSR_ASID_ASID_MASK;
405 
406     for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
407         LoongArchTLB *tlb = &env->tlb[i];
408         uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
409         uint16_t tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
410 
411         if (!tlb_g && (tlb_asid == asid)) {
412             tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
413         }
414     }
415     tlb_flush(env_cpu(env));
416 }
417 
418 void helper_invtlb_page_asid(CPULoongArchState *env, target_ulong info,
419                              target_ulong addr)
420 {
421     uint16_t asid = info & 0x3ff;
422 
423     for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
424         LoongArchTLB *tlb = &env->tlb[i];
425         uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
426         uint16_t tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
427         uint64_t vpn, tlb_vppn;
428         uint8_t tlb_ps, compare_shift;
429 
430         if (i >= LOONGARCH_STLB) {
431             tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
432         } else {
433             tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS);
434         }
435         tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
436         vpn = (addr & TARGET_VIRT_MASK) >> (tlb_ps + 1);
437         compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT;
438 
439         if (!tlb_g && (tlb_asid == asid) &&
440            (vpn == (tlb_vppn >> compare_shift))) {
441             tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
442         }
443     }
444     tlb_flush(env_cpu(env));
445 }
446 
447 void helper_invtlb_page_asid_or_g(CPULoongArchState *env,
448                                   target_ulong info, target_ulong addr)
449 {
450     uint16_t asid = info & 0x3ff;
451 
452     for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
453         LoongArchTLB *tlb = &env->tlb[i];
454         uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
455         uint16_t tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
456         uint64_t vpn, tlb_vppn;
457         uint8_t tlb_ps, compare_shift;
458 
459         if (i >= LOONGARCH_STLB) {
460             tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
461         } else {
462             tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS);
463         }
464         tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
465         vpn = (addr & TARGET_VIRT_MASK) >> (tlb_ps + 1);
466         compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT;
467 
468         if ((tlb_g || (tlb_asid == asid)) &&
469             (vpn == (tlb_vppn >> compare_shift))) {
470             tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
471         }
472     }
473     tlb_flush(env_cpu(env));
474 }
475 
476 bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
477                             MMUAccessType access_type, int mmu_idx,
478                             bool probe, uintptr_t retaddr)
479 {
480     CPULoongArchState *env = cpu_env(cs);
481     hwaddr physical;
482     int prot;
483     int ret;
484 
485     /* Data access */
486     ret = get_physical_address(env, &physical, &prot, address,
487                                access_type, mmu_idx);
488 
489     if (ret == TLBRET_MATCH) {
490         tlb_set_page(cs, address & TARGET_PAGE_MASK,
491                      physical & TARGET_PAGE_MASK, prot,
492                      mmu_idx, TARGET_PAGE_SIZE);
493         qemu_log_mask(CPU_LOG_MMU,
494                       "%s address=%" VADDR_PRIx " physical " HWADDR_FMT_plx
495                       " prot %d\n", __func__, address, physical, prot);
496         return true;
497     } else {
498         qemu_log_mask(CPU_LOG_MMU,
499                       "%s address=%" VADDR_PRIx " ret %d\n", __func__, address,
500                       ret);
501     }
502     if (probe) {
503         return false;
504     }
505     raise_mmu_exception(env, address, access_type, ret);
506     cpu_loop_exit_restore(cs, retaddr);
507 }
508 
509 target_ulong helper_lddir(CPULoongArchState *env, target_ulong base,
510                           target_ulong level, uint32_t mem_idx)
511 {
512     CPUState *cs = env_cpu(env);
513     target_ulong badvaddr, index, phys, ret;
514     int shift;
515     uint64_t dir_base, dir_width;
516 
517     if (unlikely((level == 0) || (level > 4))) {
518         qemu_log_mask(LOG_GUEST_ERROR,
519                       "Attepted LDDIR with level %"PRId64"\n", level);
520         return base;
521     }
522 
523     if (FIELD_EX64(base, TLBENTRY, HUGE)) {
524         if (unlikely(level == 4)) {
525             qemu_log_mask(LOG_GUEST_ERROR,
526                           "Attempted use of level 4 huge page\n");
527         }
528 
529         if (FIELD_EX64(base, TLBENTRY, LEVEL)) {
530             return base;
531         } else {
532             return FIELD_DP64(base, TLBENTRY, LEVEL, level);
533         }
534     }
535 
536     badvaddr = env->CSR_TLBRBADV;
537     base = base & TARGET_PHYS_MASK;
538 
539     /* 0:64bit, 1:128bit, 2:192bit, 3:256bit */
540     shift = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTEWIDTH);
541     shift = (shift + 1) * 3;
542 
543     get_dir_base_width(env, &dir_base, &dir_width, level);
544     index = (badvaddr >> dir_base) & ((1 << dir_width) - 1);
545     phys = base | index << shift;
546     ret = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK;
547     return ret;
548 }
549 
550 void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd,
551                   uint32_t mem_idx)
552 {
553     CPUState *cs = env_cpu(env);
554     target_ulong phys, tmp0, ptindex, ptoffset0, ptoffset1, ps, badv;
555     int shift;
556     uint64_t ptbase = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTBASE);
557     uint64_t ptwidth = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTWIDTH);
558     uint64_t dir_base, dir_width;
559 
560     /*
561      * The parameter "base" has only two types,
562      * one is the page table base address,
563      * whose bit 6 should be 0,
564      * and the other is the huge page entry,
565      * whose bit 6 should be 1.
566      */
567     base = base & TARGET_PHYS_MASK;
568     if (FIELD_EX64(base, TLBENTRY, HUGE)) {
569         /*
570          * Gets the huge page level and Gets huge page size.
571          * Clears the huge page level information in the entry.
572          * Clears huge page bit.
573          * Move HGLOBAL bit to GLOBAL bit.
574          */
575         get_dir_base_width(env, &dir_base, &dir_width,
576                            FIELD_EX64(base, TLBENTRY, LEVEL));
577 
578         base = FIELD_DP64(base, TLBENTRY, LEVEL, 0);
579         base = FIELD_DP64(base, TLBENTRY, HUGE, 0);
580         if (FIELD_EX64(base, TLBENTRY, HGLOBAL)) {
581             base = FIELD_DP64(base, TLBENTRY, HGLOBAL, 0);
582             base = FIELD_DP64(base, TLBENTRY, G, 1);
583         }
584 
585         ps = dir_base + dir_width - 1;
586         /*
587          * Huge pages are evenly split into parity pages
588          * when loaded into the tlb,
589          * so the tlb page size needs to be divided by 2.
590          */
591         tmp0 = base;
592         if (odd) {
593             tmp0 += MAKE_64BIT_MASK(ps, 1);
594         }
595     } else {
596         /* 0:64bit, 1:128bit, 2:192bit, 3:256bit */
597         shift = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTEWIDTH);
598         shift = (shift + 1) * 3;
599         badv = env->CSR_TLBRBADV;
600 
601         ptindex = (badv >> ptbase) & ((1 << ptwidth) - 1);
602         ptindex = ptindex & ~0x1;   /* clear bit 0 */
603         ptoffset0 = ptindex << shift;
604         ptoffset1 = (ptindex + 1) << shift;
605 
606         phys = base | (odd ? ptoffset1 : ptoffset0);
607         tmp0 = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK;
608         ps = ptbase;
609     }
610 
611     if (odd) {
612         env->CSR_TLBRELO1 = tmp0;
613     } else {
614         env->CSR_TLBRELO0 = tmp0;
615     }
616     env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI, PS, ps);
617 }
618