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