summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cputlb.c31
-rw-r--r--include/exec/cpu-defs.h9
-rw-r--r--softmmu_template.h43
3 files changed, 76 insertions, 7 deletions
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;
}