Add function loongarch_try_ptw() and use it in both vm level 0 and 1 address mapping.
Signed-off-by: SignKirigami <[email protected]> Signed-off-by: Hengyu Yu <[email protected]> --- target/loongarch/cpu-mmu.h | 2 +- target/loongarch/cpu_helper.c | 23 ++++++--- target/loongarch/tcg/tlb_helper.c | 82 +++++++++++++++++-------------- 3 files changed, 64 insertions(+), 43 deletions(-) diff --git a/target/loongarch/cpu-mmu.h b/target/loongarch/cpu-mmu.h index 29b0c109fb..bad05a5a9c 100644 --- a/target/loongarch/cpu-mmu.h +++ b/target/loongarch/cpu-mmu.h @@ -40,7 +40,7 @@ typedef struct MMUContext { static inline bool cpu_has_ptw(CPULoongArchState *env, int vm_level) { - CPUSysState *sys = env_sys(env); + CPUSysState *sys = get_sys(env, vm_level); return !!FIELD_EX64(sys->CSR_PWCH, CSR_PWCH, HPTW_EN); } diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c index 23d26385c6..4ba991aaff 100644 --- a/target/loongarch/cpu_helper.c +++ b/target/loongarch/cpu_helper.c @@ -160,7 +160,7 @@ TLBRet loongarch_ptw(CPULoongArchState *env, MMUContext *context, vaddr address; TLBRet ret; MemTxResult ret1; - CPUSysState *sys = env_sys(env); + CPUSysState *sys = get_sys(env, vm_level); address = context->addr; if ((address >> 63) & 0x1) { @@ -180,7 +180,10 @@ TLBRet loongarch_ptw(CPULoongArchState *env, MMUContext *context, /* get next level page directory */ index = (address >> dir_base) & ((1 << dir_width) - 1); phys = base | index << 3; - base = address_space_ldq_le(cs->as, phys, attrs, NULL); + base = address_space_ldq_le( + cs->as, + vm_level ? loongarch_get_host_address(env, phys, retaddr) : phys, + attrs, NULL); if (level) { if (FIELD_EX64(base, TLBENTRY, HUGE)) { /* base is a huge pte */ @@ -214,8 +217,11 @@ restart: index &= 1; context->pte_buddy[index] = base; - val = address_space_ldq_le(cs->as, phys + 8 * (1 - 2 * index), - attrs, NULL); + val = address_space_ldq_le( + cs->as, + (vm_level ? loongarch_get_host_address(env, phys, retaddr) : phys) + + 8 * (1 - 2 * index), + attrs, NULL); context->pte_buddy[1 - index] = val; } @@ -245,10 +251,15 @@ restart: if (access_type == MMU_DATA_STORE) { base = pte_mkdirty(base); } - ret1 = loongarch_cmpxchg_phys(cs, phys, pte, base); + ret1 = loongarch_cmpxchg_phys( + cs, vm_level ? loongarch_get_host_address(env, phys, retaddr) : phys, + pte, base); /* PTE updated by other CPU, reload PTE entry */ if (ret1 == MEMTX_DECODE_ERROR) { - base = address_space_ldq_le(cs->as, phys, attrs, NULL); + base = address_space_ldq_le( + cs->as, + vm_level ? loongarch_get_host_address(env, phys, retaddr) : phys, + attrs, NULL); goto restart; } diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index c02b7c2472..41d6138828 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -724,6 +724,46 @@ static TLBRet loongarch_map_host_address(CPULoongArchState *env, MMUContext *con return TLBRET_HOST_MATCH + ret; } +static void loongarch_try_ptw(CPULoongArchState *env, MMUContext *context, + MMUAccessType access_type, int mmu_index, + TLBRet *status, bool guest, uintptr_t retaddr) +{ + if ((*status == TLBRET_MATCH || *status == TLBRET_HOST_MATCH) && + context->mmu_index != MMU_DA_IDX && + context->mmu_index != MMU_GUEST_DA_IDX && cpu_has_ptw(env, guest)) { + bool need_update = true; + + if (access_type == MMU_DATA_STORE && pte_dirty(context->pte)) { + need_update = false; + } else if (access_type != MMU_DATA_STORE && pte_access(context->pte)) { + need_update = false; + } + + if (need_update) { + *status = (env_vm_level(env) == VM_LEVEL1 && + !guest) ? + TLBRET_HOST_NOMATCH : TLBRET_NOMATCH; + } + } + + if (*status != TLBRET_MATCH && *status != TLBRET_HOST_MATCH && + cpu_has_ptw(env, guest)) { + if (*status == TLBRET_NOMATCH || *status == TLBRET_INVALID || + *status == TLBRET_HOST_NOMATCH || *status == TLBRET_HOST_INVALID) { + *status = ((env_vm_level(env) == VM_LEVEL1 && + !guest) ? + TLBRET_HOST_MATCH : TLBRET_MATCH) + + loongarch_ptw(env, context, access_type, mmu_index, 0, + guest, retaddr); + if (*status == TLBRET_MATCH || *status == TLBRET_HOST_MATCH) { + ptw_update_tlb(env, context, guest); + } + } else if (context->tlb_index >= 0) { + invalidate_tlb(env, context->tlb_index, guest); + } + } +} + bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr) @@ -739,42 +779,8 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, context.addr = address; context.tlb_index = -1; ret = get_physical_address(env, &context, access_type, mmu_idx, 0, retaddr); - if (ret == TLBRET_MATCH && context.mmu_index != MMU_DA_IDX - && cpu_has_ptw(env, vm_level)) { - bool need_update = true; - - if (access_type == MMU_DATA_STORE && pte_dirty(context.pte)) { - need_update = false; - } else if (access_type != MMU_DATA_STORE && pte_access(context.pte)) { - need_update = false; - - /* - * FIXME: should context.prot be set without PAGE_WRITE with - * pte_write(context.pte) && !pte_dirty(context.pte)?? - * - * Otherwise there will be no loongarch_cpu_tlb_fill() function call - * for MMU_DATA_STORE access_type in future since QEMU TLB with - * prot PAGE_WRITE is added already - */ - } - - if (need_update) { - /* Need update bit A/D in PTE entry, take PTW again */ - ret = TLBRET_NOMATCH; - } - } - - if (ret != TLBRET_MATCH && cpu_has_ptw(env, vm_level)) { - /* Take HW PTW if TLB missed or bit P is zero */ - if (ret == TLBRET_NOMATCH || ret == TLBRET_INVALID) { - ret = loongarch_ptw(env, &context, access_type, mmu_idx, 0, vm_level, retaddr); - if (ret == TLBRET_MATCH) { - ptw_update_tlb(env, &context, vm_level); - } - } else if (context.tlb_index >= 0) { - invalidate_tlb(env, context.tlb_index, vm_level); - } - } + loongarch_try_ptw(env, &context, access_type, mmu_idx, &ret, vm_level, + retaddr); if (ret == TLBRET_MATCH) { physical = context.physical; @@ -784,6 +790,8 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, host_context.tlb_index = -1; ret = loongarch_map_host_address(env, &host_context, access_type, retaddr); + loongarch_try_ptw(env, &host_context, access_type, MMU_KERNEL_IDX, + &ret, false, retaddr); if (ret != TLBRET_HOST_MATCH) { if (probe) { return false; @@ -847,6 +855,8 @@ hwaddr loongarch_get_host_address(CPULoongArchState *env, hwaddr gpa, host_context.tlb_index = -1; ret = loongarch_map_host_address(env, &host_context, MMU_DATA_LOAD, retaddr); + loongarch_try_ptw(env, &host_context, MMU_DATA_LOAD, MMU_KERNEL_IDX, + &ret, false, retaddr); if (ret != TLBRET_HOST_MATCH) { raise_mmu_exception(env, gpa, MMU_DATA_LOAD, ret); -- 2.55.0
