diff options
Diffstat (limited to 'target/loongarch/cpu_helper.c')
-rw-r--r-- | target/loongarch/cpu_helper.c | 235 |
1 files changed, 108 insertions, 127 deletions
diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c index 580362a..e172b11 100644 --- a/target/loongarch/cpu_helper.c +++ b/target/loongarch/cpu_helper.c @@ -7,162 +7,143 @@ */ #include "qemu/osdep.h" +#include "system/tcg.h" #include "cpu.h" +#include "accel/tcg/cpu-mmu-index.h" +#include "exec/target_page.h" #include "internals.h" #include "cpu-csr.h" +#include "tcg/tcg_loongarch.h" -#ifdef CONFIG_TCG -static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - int access_type, int index, int mmu_idx) +void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, + uint64_t *dir_width, target_ulong level) { - LoongArchTLB *tlb = &env->tlb[index]; - uint64_t plv = mmu_idx; - uint64_t tlb_entry, tlb_ppn; - uint8_t tlb_ps, n, tlb_v, tlb_d, tlb_plv, tlb_nx, tlb_nr, tlb_rplv; - - if (index >= LOONGARCH_STLB) { - tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); - } else { - tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); + switch (level) { + case 1: + *dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_BASE); + *dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_WIDTH); + break; + case 2: + *dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_BASE); + *dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_WIDTH); + break; + case 3: + *dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_BASE); + *dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_WIDTH); + break; + case 4: + *dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_BASE); + *dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_WIDTH); + break; + default: + /* level may be zero for ldpte */ + *dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTBASE); + *dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTWIDTH); + break; } - n = (address >> tlb_ps) & 0x1;/* Odd or even */ +} - tlb_entry = n ? tlb->tlb_entry1 : tlb->tlb_entry0; - tlb_v = FIELD_EX64(tlb_entry, TLBENTRY, V); - tlb_d = FIELD_EX64(tlb_entry, TLBENTRY, D); - tlb_plv = FIELD_EX64(tlb_entry, TLBENTRY, PLV); - if (is_la64(env)) { - tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_64, PPN); - tlb_nx = FIELD_EX64(tlb_entry, TLBENTRY_64, NX); - tlb_nr = FIELD_EX64(tlb_entry, TLBENTRY_64, NR); - tlb_rplv = FIELD_EX64(tlb_entry, TLBENTRY_64, RPLV); +static int loongarch_page_table_walker(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address) +{ + CPUState *cs = env_cpu(env); + target_ulong index, phys; + uint64_t dir_base, dir_width; + uint64_t base; + int level; + + if ((address >> 63) & 0x1) { + base = env->CSR_PGDH; } else { - tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_32, PPN); - tlb_nx = 0; - tlb_nr = 0; - tlb_rplv = 0; + base = env->CSR_PGDL; } + base &= TARGET_PHYS_MASK; - /* Remove sw bit between bit12 -- bit PS*/ - tlb_ppn = tlb_ppn & ~(((0x1UL << (tlb_ps - 12)) -1)); + for (level = 4; level > 0; level--) { + get_dir_base_width(env, &dir_base, &dir_width, level); - /* Check access rights */ - if (!tlb_v) { - return TLBRET_INVALID; - } + if (dir_width == 0) { + continue; + } - if (access_type == MMU_INST_FETCH && tlb_nx) { - return TLBRET_XI; + /* get next level page directory */ + index = (address >> dir_base) & ((1 << dir_width) - 1); + phys = base | index << 3; + base = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK; + if (FIELD_EX64(base, TLBENTRY, HUGE)) { + /* base is a huge pte */ + break; + } } - if (access_type == MMU_DATA_LOAD && tlb_nr) { - return TLBRET_RI; + /* pte */ + if (FIELD_EX64(base, TLBENTRY, HUGE)) { + /* Huge Page. base is pte */ + base = FIELD_DP64(base, TLBENTRY, LEVEL, 0); + base = FIELD_DP64(base, TLBENTRY, HUGE, 0); + if (FIELD_EX64(base, TLBENTRY, HGLOBAL)) { + base = FIELD_DP64(base, TLBENTRY, HGLOBAL, 0); + base = FIELD_DP64(base, TLBENTRY, G, 1); + } + } else { + /* Normal Page. base points to pte */ + get_dir_base_width(env, &dir_base, &dir_width, 0); + index = (address >> dir_base) & ((1 << dir_width) - 1); + phys = base | index << 3; + base = ldq_phys(cs->as, phys); } - if (((tlb_rplv == 0) && (plv > tlb_plv)) || - ((tlb_rplv == 1) && (plv != tlb_plv))) { - return TLBRET_PE; - } + /* TODO: check plv and other bits? */ - if ((access_type == MMU_DATA_STORE) && !tlb_d) { - return TLBRET_DIRTY; + /* base is pte, in normal pte format */ + if (!FIELD_EX64(base, TLBENTRY, V)) { + return TLBRET_NOMATCH; } - *physical = (tlb_ppn << R_TLBENTRY_64_PPN_SHIFT) | - (address & MAKE_64BIT_MASK(0, tlb_ps)); - *prot = PAGE_READ; - if (tlb_d) { - *prot |= PAGE_WRITE; - } - if (!tlb_nx) { - *prot |= PAGE_EXEC; + if (!FIELD_EX64(base, TLBENTRY, D)) { + *prot = PAGE_READ; + } else { + *prot = PAGE_READ | PAGE_WRITE; } - return TLBRET_MATCH; -} -/* - * One tlb entry holds an adjacent odd/even pair, the vpn is the - * content of the virtual page number divided by 2. So the - * compare vpn is bit[47:15] for 16KiB page. while the vppn - * field in tlb entry contains bit[47:13], so need adjust. - * virt_vpn = vaddr[47:13] - */ -bool loongarch_tlb_search(CPULoongArchState *env, target_ulong vaddr, - int *index) -{ - LoongArchTLB *tlb; - uint16_t csr_asid, tlb_asid, stlb_idx; - uint8_t tlb_e, tlb_ps, tlb_g, stlb_ps; - int i, compare_shift; - uint64_t vpn, tlb_vppn; - - csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID); - stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); - vpn = (vaddr & TARGET_VIRT_MASK) >> (stlb_ps + 1); - stlb_idx = vpn & 0xff; /* VA[25:15] <==> TLBIDX.index for 16KiB Page */ - compare_shift = stlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; - - /* Search STLB */ - for (i = 0; i < 8; ++i) { - tlb = &env->tlb[i * 256 + stlb_idx]; - tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); - if (tlb_e) { - tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); - tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); - tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); - - if ((tlb_g == 1 || tlb_asid == csr_asid) && - (vpn == (tlb_vppn >> compare_shift))) { - *index = i * 256 + stlb_idx; - return true; - } - } - } + /* get TARGET_PAGE_SIZE aligned physical address */ + base += (address & TARGET_PHYS_MASK) & ((1 << dir_base) - 1); + /* mask RPLV, NX, NR bits */ + base = FIELD_DP64(base, TLBENTRY_64, RPLV, 0); + base = FIELD_DP64(base, TLBENTRY_64, NX, 0); + base = FIELD_DP64(base, TLBENTRY_64, NR, 0); + /* mask other attribute bits */ + *physical = base & TARGET_PAGE_MASK; - /* Search MTLB */ - for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; ++i) { - tlb = &env->tlb[i]; - tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); - if (tlb_e) { - tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); - tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); - tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); - tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); - compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; - vpn = (vaddr & TARGET_VIRT_MASK) >> (tlb_ps + 1); - if ((tlb_g == 1 || tlb_asid == csr_asid) && - (vpn == (tlb_vppn >> compare_shift))) { - *index = i; - return true; - } - } - } - return false; + return 0; } static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical, int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx) + MMUAccessType access_type, int mmu_idx, + int is_debug) { - int index, match; + int ret; + + if (tcg_enabled()) { + ret = loongarch_get_addr_from_tlb(env, physical, prot, address, + access_type, mmu_idx); + if (ret != TLBRET_NOMATCH) { + return ret; + } + } - match = loongarch_tlb_search(env, address, &index); - if (match) { - return loongarch_map_tlb_entry(env, physical, prot, - address, access_type, index, mmu_idx); + if (is_debug) { + /* + * For debugger memory access, we want to do the map when there is a + * legal mapping, even if the mapping is not yet in TLB. return 0 if + * there is a valid map, else none zero. + */ + return loongarch_page_table_walker(env, physical, prot, address); } return TLBRET_NOMATCH; } -#else -static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx) -{ - return TLBRET_NOMATCH; -} -#endif static hwaddr dmw_va2pa(CPULoongArchState *env, target_ulong va, target_ulong dmw) @@ -178,7 +159,7 @@ static hwaddr dmw_va2pa(CPULoongArchState *env, target_ulong va, int get_physical_address(CPULoongArchState *env, hwaddr *physical, int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx) + MMUAccessType access_type, int mmu_idx, int is_debug) { int user_mode = mmu_idx == MMU_USER_IDX; int kernel_mode = mmu_idx == MMU_KERNEL_IDX; @@ -222,7 +203,7 @@ int get_physical_address(CPULoongArchState *env, hwaddr *physical, /* Mapped address */ return loongarch_map_address(env, physical, prot, address, - access_type, mmu_idx); + access_type, mmu_idx, is_debug); } hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) @@ -232,7 +213,7 @@ hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) int prot; if (get_physical_address(env, &phys_addr, &prot, addr, MMU_DATA_LOAD, - cpu_mmu_index(cs, false)) != 0) { + cpu_mmu_index(cs, false), 1) != 0) { return -1; } return phys_addr; |