diff --git a/cputlb.c b/cputlb.c index afd3705ff3..a55518a0d2 100644 --- a/cputlb.c +++ b/cputlb.c @@ -60,8 +60,10 @@ void tlb_flush(CPUState *cpu, int flush_global) cpu->current_tb = NULL; memset(env->tlb_table, -1, sizeof(env->tlb_table)); + memset(env->tlb_v_table, -1, sizeof(env->tlb_v_table)); memset(cpu->tb_jmp_cache, 0, sizeof(cpu->tb_jmp_cache)); + env->vtlb_index = 0; env->tlb_flush_addr = -1; env->tlb_flush_mask = 0; tlb_flush_count++; @@ -108,6 +110,14 @@ void tlb_flush_page(CPUState *cpu, target_ulong addr) tlb_flush_entry(&env->tlb_table[mmu_idx][i], addr); } + /* check whether there are entries that need to be flushed in the vtlb */ + for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { + int k; + for (k = 0; k < CPU_VTLB_SIZE; k++) { + tlb_flush_entry(&env->tlb_v_table[mmu_idx][k], addr); + } + } + tb_flush_jmp_cache(cpu, addr); } @@ -172,6 +182,11 @@ void cpu_tlb_reset_dirty_all(ram_addr_t start1, ram_addr_t length) tlb_reset_dirty_range(&env->tlb_table[mmu_idx][i], start1, length); } + + for (i = 0; i < CPU_VTLB_SIZE; i++) { + tlb_reset_dirty_range(&env->tlb_v_table[mmu_idx][i], + start1, length); + } } } } @@ -195,6 +210,13 @@ void tlb_set_dirty(CPUArchState *env, target_ulong vaddr) for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { tlb_set_dirty1(&env->tlb_table[mmu_idx][i], vaddr); } + + for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { + int k; + for (k = 0; k < CPU_VTLB_SIZE; k++) { + tlb_set_dirty1(&env->tlb_v_table[mmu_idx][k], vaddr); + } + } } /* Our TLB does not support large pages, so remember the area covered by @@ -235,6 +257,7 @@ void tlb_set_page(CPUState *cpu, target_ulong vaddr, uintptr_t addend; CPUTLBEntry *te; hwaddr iotlb, xlat, sz; + unsigned vidx = env->vtlb_index++ % CPU_VTLB_SIZE; assert(size >= TARGET_PAGE_SIZE); if (size != TARGET_PAGE_SIZE) { @@ -267,8 +290,14 @@ void tlb_set_page(CPUState *cpu, target_ulong vaddr, prot, &address); index = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); - env->iotlb[mmu_idx][index] = iotlb - vaddr; te = &env->tlb_table[mmu_idx][index]; + + /* do not discard the translation in te, evict it into a victim tlb */ + env->tlb_v_table[mmu_idx][vidx] = *te; + env->iotlb_v[mmu_idx][vidx] = env->iotlb[mmu_idx][index]; + + /* refill the tlb */ + env->iotlb[mmu_idx][index] = iotlb - vaddr; te->addend = addend - vaddr; if (prot & PAGE_READ) { te->addr_read = address; diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h index 2dd6206d4a..0ca6f0b953 100644 --- a/include/exec/cpu-defs.h +++ b/include/exec/cpu-defs.h @@ -71,6 +71,8 @@ typedef uint64_t target_ulong; #if !defined(CONFIG_USER_ONLY) #define CPU_TLB_BITS 8 #define CPU_TLB_SIZE (1 << CPU_TLB_BITS) +/* use a fully associative victim tlb of 8 entries */ +#define CPU_VTLB_SIZE 8 #if HOST_LONG_BITS == 32 && TARGET_LONG_BITS == 32 #define CPU_TLB_ENTRY_BITS 4 @@ -103,9 +105,12 @@ QEMU_BUILD_BUG_ON(sizeof(CPUTLBEntry) != (1 << CPU_TLB_ENTRY_BITS)); #define CPU_COMMON_TLB \ /* The meaning of the MMU modes is defined in the target code. */ \ CPUTLBEntry tlb_table[NB_MMU_MODES][CPU_TLB_SIZE]; \ - hwaddr iotlb[NB_MMU_MODES][CPU_TLB_SIZE]; \ + CPUTLBEntry tlb_v_table[NB_MMU_MODES][CPU_VTLB_SIZE]; \ + hwaddr iotlb[NB_MMU_MODES][CPU_TLB_SIZE]; \ + hwaddr iotlb_v[NB_MMU_MODES][CPU_VTLB_SIZE]; \ target_ulong tlb_flush_addr; \ - target_ulong tlb_flush_mask; + target_ulong tlb_flush_mask; \ + target_ulong vtlb_index; \ #else diff --git a/softmmu_template.h b/softmmu_template.h index 5a07f991a1..88e33900b6 100644 --- a/softmmu_template.h +++ b/softmmu_template.h @@ -116,6 +116,31 @@ # define helper_te_st_name helper_le_st_name #endif +/* macro to check the victim tlb */ +#define VICTIM_TLB_HIT(ty) \ +({ \ + /* we are about to do a page table walk. our last hope is the \ + * victim tlb. try to refill from the victim tlb before walking the \ + * page table. */ \ + int vidx; \ + hwaddr tmpiotlb; \ + CPUTLBEntry tmptlb; \ + for (vidx = CPU_VTLB_SIZE-1; vidx >= 0; --vidx) { \ + if (env->tlb_v_table[mmu_idx][vidx].ty == (addr & TARGET_PAGE_MASK)) {\ + /* found entry in victim tlb, swap tlb and iotlb */ \ + tmptlb = env->tlb_table[mmu_idx][index]; \ + env->tlb_table[mmu_idx][index] = env->tlb_v_table[mmu_idx][vidx]; \ + env->tlb_v_table[mmu_idx][vidx] = tmptlb; \ + tmpiotlb = env->iotlb[mmu_idx][index]; \ + env->iotlb[mmu_idx][index] = env->iotlb_v[mmu_idx][vidx]; \ + env->iotlb_v[mmu_idx][vidx] = tmpiotlb; \ + break; \ + } \ + } \ + /* return true when there is a vtlb hit, i.e. vidx >=0 */ \ + vidx >= 0; \ +}) + #ifndef SOFTMMU_CODE_ACCESS static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env, hwaddr physaddr, @@ -161,7 +186,10 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, mmu_idx, retaddr); } #endif - tlb_fill(ENV_GET_CPU(env), addr, READ_ACCESS_TYPE, mmu_idx, retaddr); + if (!VICTIM_TLB_HIT(ADDR_READ)) { + tlb_fill(ENV_GET_CPU(env), addr, READ_ACCESS_TYPE, + mmu_idx, retaddr); + } tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ; } @@ -246,7 +274,10 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, mmu_idx, retaddr); } #endif - tlb_fill(ENV_GET_CPU(env), addr, READ_ACCESS_TYPE, mmu_idx, retaddr); + if (!VICTIM_TLB_HIT(ADDR_READ)) { + tlb_fill(ENV_GET_CPU(env), addr, READ_ACCESS_TYPE, + mmu_idx, retaddr); + } tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ; } @@ -368,7 +399,9 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, cpu_unaligned_access(ENV_GET_CPU(env), addr, 1, mmu_idx, retaddr); } #endif - tlb_fill(ENV_GET_CPU(env), addr, 1, mmu_idx, retaddr); + if (!VICTIM_TLB_HIT(addr_write)) { + tlb_fill(ENV_GET_CPU(env), addr, 1, mmu_idx, retaddr); + } tlb_addr = env->tlb_table[mmu_idx][index].addr_write; } @@ -444,7 +477,9 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, cpu_unaligned_access(ENV_GET_CPU(env), addr, 1, mmu_idx, retaddr); } #endif - tlb_fill(ENV_GET_CPU(env), addr, 1, mmu_idx, retaddr); + if (!VICTIM_TLB_HIT(addr_write)) { + tlb_fill(ENV_GET_CPU(env), addr, 1, mmu_idx, retaddr); + } tlb_addr = env->tlb_table[mmu_idx][index].addr_write; }