TLBRet get_physical_address(CPULoongArchState *env, MMUContext *context,
MMUAccessType access_type, int mmu_idx,
int is_debug);
+TLBRet loongarch_ptw(CPULoongArchState *env, MMUContext *context,
+ int access_type, int mmu_idx, int debug);
void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base,
uint64_t *dir_width, unsigned int level);
hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
return TLBRET_MATCH;
}
-static TLBRet loongarch_ptw(CPULoongArchState *env, MMUContext *context,
- int access_type, int mmu_idx, int debug)
+TLBRet loongarch_ptw(CPULoongArchState *env, MMUContext *context,
+ int access_type, int mmu_idx, int debug)
{
CPUState *cs = env_cpu(env);
- target_ulong index, phys;
+ target_ulong index = 0, phys = 0;
uint64_t dir_base, dir_width;
uint64_t base;
int level;
if (level) {
if (FIELD_EX64(base, TLBENTRY, HUGE)) {
/* base is a huge pte */
+ index = 0;
+ dir_base -= 1;
break;
} else {
/* Discard high bits with page directory table */
base = FIELD_DP64(base, TLBENTRY, HGLOBAL, 0);
base = FIELD_DP64(base, TLBENTRY, G, 1);
}
+
+ context->pte_buddy[index] = base;
+ context->pte_buddy[1 - index] = base + BIT_ULL(dir_base);
+ base += (BIT_ULL(dir_base) & address);
+ } else 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;
}
}
+static void ptw_update_tlb(CPULoongArchState *env, MMUContext *context)
+{
+ int index;
+
+ index = context->tlb_index;
+ if (index < 0) {
+ index = get_tlb_random_index(env, context->addr, context->ps);
+ }
+
+ update_tlb_index(env, context, index);
+}
+
bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
MMUAccessType access_type, int mmu_idx,
bool probe, uintptr_t retaddr)
/* Data access */
context.addr = address;
+ context.tlb_index = -1;
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_ptw(env, &context, access_type, mmu_idx, 0);
+ if (ret == TLBRET_MATCH) {
+ ptw_update_tlb(env, &context);
+ }
+ } else if (context.tlb_index >= 0) {
+ invalidate_tlb(env, context.tlb_index);
+ }
+ }
+
if (ret == TLBRET_MATCH) {
physical = context.physical;
prot = context.prot;