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