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 return base; 529 } 530 531 if (FIELD_EX64(base, TLBENTRY, LEVEL)) { 532 return base; 533 } else { 534 return FIELD_DP64(base, TLBENTRY, LEVEL, level); 535 } 536 } 537 538 badvaddr = env->CSR_TLBRBADV; 539 base = base & TARGET_PHYS_MASK; 540 541 /* 0:64bit, 1:128bit, 2:192bit, 3:256bit */ 542 shift = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTEWIDTH); 543 shift = (shift + 1) * 3; 544 545 get_dir_base_width(env, &dir_base, &dir_width, level); 546 index = (badvaddr >> dir_base) & ((1 << dir_width) - 1); 547 phys = base | index << shift; 548 ret = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK; 549 return ret; 550 } 551 552 void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd, 553 uint32_t mem_idx) 554 { 555 CPUState *cs = env_cpu(env); 556 target_ulong phys, tmp0, ptindex, ptoffset0, ptoffset1, ps, badv; 557 int shift; 558 uint64_t ptbase = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTBASE); 559 uint64_t ptwidth = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTWIDTH); 560 uint64_t dir_base, dir_width; 561 562 /* 563 * The parameter "base" has only two types, 564 * one is the page table base address, 565 * whose bit 6 should be 0, 566 * and the other is the huge page entry, 567 * whose bit 6 should be 1. 568 */ 569 base = base & TARGET_PHYS_MASK; 570 if (FIELD_EX64(base, TLBENTRY, HUGE)) { 571 /* 572 * Gets the huge page level and Gets huge page size. 573 * Clears the huge page level information in the entry. 574 * Clears huge page bit. 575 * Move HGLOBAL bit to GLOBAL bit. 576 */ 577 get_dir_base_width(env, &dir_base, &dir_width, 578 FIELD_EX64(base, TLBENTRY, LEVEL)); 579 580 base = FIELD_DP64(base, TLBENTRY, LEVEL, 0); 581 base = FIELD_DP64(base, TLBENTRY, HUGE, 0); 582 if (FIELD_EX64(base, TLBENTRY, HGLOBAL)) { 583 base = FIELD_DP64(base, TLBENTRY, HGLOBAL, 0); 584 base = FIELD_DP64(base, TLBENTRY, G, 1); 585 } 586 587 ps = dir_base + dir_width - 1; 588 /* 589 * Huge pages are evenly split into parity pages 590 * when loaded into the tlb, 591 * so the tlb page size needs to be divided by 2. 592 */ 593 tmp0 = base; 594 if (odd) { 595 tmp0 += MAKE_64BIT_MASK(ps, 1); 596 } 597 } else { 598 /* 0:64bit, 1:128bit, 2:192bit, 3:256bit */ 599 shift = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTEWIDTH); 600 shift = (shift + 1) * 3; 601 badv = env->CSR_TLBRBADV; 602 603 ptindex = (badv >> ptbase) & ((1 << ptwidth) - 1); 604 ptindex = ptindex & ~0x1; /* clear bit 0 */ 605 ptoffset0 = ptindex << shift; 606 ptoffset1 = (ptindex + 1) << shift; 607 608 phys = base | (odd ? ptoffset1 : ptoffset0); 609 tmp0 = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK; 610 ps = ptbase; 611 } 612 613 if (odd) { 614 env->CSR_TLBRELO1 = tmp0; 615 } else { 616 env->CSR_TLBRELO0 = tmp0; 617 } 618 env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI, PS, ps); 619 } 620