With software PTW, there is TLB refill exception if there is TLB miss.

However with hardware PTW supported, hardware will search page table
with TLB miss. Also if Present bit is not set, hardware PTW will take,
it is used in odd/even TLB entry. For example in the first time odd TLB
entry is valid and even TLB entry is 0. When software accesses with
address from even page, there is no TLB miss only that TLB entry is 0.
In this condition, hardwre PTW will take also.

Signed-off-by: Bibo Mao <maob...@loongson.cn>
---
 target/loongarch/cpu-mmu.h        |  3 +++
 target/loongarch/cpu_helper.c     | 17 ++++++++++++++---
 target/loongarch/tcg/tlb_helper.c | 26 ++++++++++++++++++++++++++
 3 files changed, 43 insertions(+), 3 deletions(-)

diff --git a/target/loongarch/cpu-mmu.h b/target/loongarch/cpu-mmu.h
index c3e869234a..2e63324abe 100644
--- a/target/loongarch/cpu-mmu.h
+++ b/target/loongarch/cpu-mmu.h
@@ -67,6 +67,9 @@ TLBRet loongarch_check_pte(CPULoongArchState *env, MMUContext 
*context,
 TLBRet get_physical_address(CPULoongArchState *env, MMUContext *context,
                             MMUAccessType access_type, int mmu_idx,
                             int is_debug);
+TLBRet loongarch_page_table_walker(CPULoongArchState *env,
+                                   MMUContext *context,
+                                   int access_type, int mmu_idx);
 void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base,
                                uint64_t *dir_width, target_ulong level);
 hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c
index 2e07049425..79c994255f 100644
--- a/target/loongarch/cpu_helper.c
+++ b/target/loongarch/cpu_helper.c
@@ -106,9 +106,9 @@ TLBRet loongarch_check_pte(CPULoongArchState *env, 
MMUContext *context,
     return TLBRET_MATCH;
 }
 
-static TLBRet loongarch_page_table_walker(CPULoongArchState *env,
-                                          MMUContext *context,
-                                          int access_type, int mmu_idx)
+TLBRet loongarch_page_table_walker(CPULoongArchState *env,
+                                   MMUContext *context,
+                                   int access_type, int mmu_idx)
 {
     CPUState *cs = env_cpu(env);
     target_ulong index, phys;
@@ -151,12 +151,23 @@ static TLBRet 
loongarch_page_table_walker(CPULoongArchState *env,
             base = FIELD_DP64(base, TLBENTRY, HGLOBAL, 0);
             base = FIELD_DP64(base, TLBENTRY, G, 1);
         }
+
+        dir_base -= 1;
+        context->pte_buddy[0] = base;
+        context->pte_buddy[1] = base + BIT_ULL(dir_base);
+        base += (BIT_ULL(dir_base) & address);
     } 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 (cpu_has_ptw(env)) {
+            index &= 1;
+            context->pte_buddy[index] = base;
+            context->pte_buddy[1 - index] = ldq_phys(cs->as,
+                                            phys + 8 * (1 - 2 * index));
+        }
     }
 
     context->ps = dir_base;
diff --git a/target/loongarch/tcg/tlb_helper.c 
b/target/loongarch/tcg/tlb_helper.c
index 50c7583c6c..7fdac475b3 100644
--- a/target/loongarch/tcg/tlb_helper.c
+++ b/target/loongarch/tcg/tlb_helper.c
@@ -621,6 +621,20 @@ static inline void tlb_set_accessed(CPULoongArchState 
*env, vaddr address,
     tlb->tlb_misc |= TLB_MISC_KM_PTE(n);
 }
 
+static void ptw_update_tlb(CPULoongArchState *env, MMUContext *context)
+{
+    int index;
+    bool match;
+
+    match = loongarch_tlb_search(env, context->addr, &index);
+    if (!match) {
+        index = get_tlb_random_index(env, context->addr, context->ps);
+    }
+
+    invalidate_tlb(env, index);
+    fill_tlb_entry(env, env->tlb + index, context);
+}
+
 bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
                             MMUAccessType access_type, int mmu_idx,
                             bool probe, uintptr_t retaddr)
@@ -634,6 +648,18 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, 
int size,
     /* Data access */
     context.addr = address;
     ret = get_physical_address(env, &context, access_type, mmu_idx, 0);
+    if (ret != TLBRET_MATCH && cpu_has_ptw(env)) {
+        /* Take HW PTW if TLB missed or bit P is zero */
+        if (ret == TLBRET_NOMATCH || ret == TLBRET_INVALID) {
+            ret = loongarch_page_table_walker(env, &context, access_type, 
mmu_idx);
+            if (ret == TLBRET_MATCH) {
+                ptw_update_tlb(env, &context);
+            }
+        } else {
+            invalidate_tlb(env, context.tlb_index);
+        }
+    }
+
     if (ret == TLBRET_MATCH) {
         physical = context.physical;
         prot = context.prot;
-- 
2.39.3


Reply via email to