X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/54b949d27064a294304775e427f49d3706ed4a95..23120b13c6e316df44aeb63420121a1cf6dc49dc:/cputlb.c diff --git a/cputlb.c b/cputlb.c index 1230e9e8ff..3b271d44d9 100644 --- a/cputlb.c +++ b/cputlb.c @@ -22,10 +22,13 @@ #include "exec/exec-all.h" #include "exec/memory.h" #include "exec/address-spaces.h" +#include "exec/cpu_ldst.h" #include "exec/cputlb.h" #include "exec/memory-internal.h" +#include "exec/ram_addr.h" +#include "tcg/tcg.h" //#define DEBUG_TLB //#define DEBUG_TLB_CHECK @@ -33,13 +36,6 @@ /* statistics */ int tlb_flush_count; -static const CPUTLBEntry s_cputlb_empty_entry = { - .addr_read = -1, - .addr_write = -1, - .addr_code = -1, - .addend = -1, -}; - /* NOTE: * If flush_global is true (the usual case), flush all tlb entries. * If flush_global is false, flush (at least) all tlb entries not @@ -52,10 +48,9 @@ static const CPUTLBEntry s_cputlb_empty_entry = { * entries from the TLB at any time, so flushing more entries than * required is only an efficiency issue, not a correctness issue. */ -void tlb_flush(CPUArchState *env, int flush_global) +void tlb_flush(CPUState *cpu, int flush_global) { - CPUState *cpu = ENV_GET_CPU(env); - int i; + CPUArchState *env = cpu->env_ptr; #if defined(DEBUG_TLB) printf("tlb_flush:\n"); @@ -64,16 +59,11 @@ void tlb_flush(CPUArchState *env, int flush_global) links while we are modifying them */ cpu->current_tb = NULL; - for (i = 0; i < CPU_TLB_SIZE; i++) { - int mmu_idx; - - for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { - env->tlb_table[mmu_idx][i] = s_cputlb_empty_entry; - } - } - - memset(env->tb_jmp_cache, 0, TB_JMP_CACHE_SIZE * sizeof (void *)); + 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++; @@ -87,13 +77,13 @@ static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong addr) (TARGET_PAGE_MASK | TLB_INVALID_MASK)) || addr == (tlb_entry->addr_code & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { - *tlb_entry = s_cputlb_empty_entry; + memset(tlb_entry, -1, sizeof(*tlb_entry)); } } -void tlb_flush_page(CPUArchState *env, target_ulong addr) +void tlb_flush_page(CPUState *cpu, target_ulong addr) { - CPUState *cpu = ENV_GET_CPU(env); + CPUArchState *env = cpu->env_ptr; int i; int mmu_idx; @@ -107,7 +97,7 @@ void tlb_flush_page(CPUArchState *env, target_ulong addr) TARGET_FMT_lx "/" TARGET_FMT_lx ")\n", env->tlb_flush_addr, env->tlb_flush_mask); #endif - tlb_flush(env, 1); + tlb_flush(cpu, 1); return; } /* must reset current TB so that interrupts cannot modify the @@ -120,24 +110,31 @@ void tlb_flush_page(CPUArchState *env, target_ulong addr) tlb_flush_entry(&env->tlb_table[mmu_idx][i], addr); } - tb_flush_jmp_cache(env, 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); } /* update the TLBs so that writes to code in the virtual page 'addr' can be detected */ void tlb_protect_code(ram_addr_t ram_addr) { - cpu_physical_memory_reset_dirty(ram_addr, - ram_addr + TARGET_PAGE_SIZE, - CODE_DIRTY_FLAG); + cpu_physical_memory_reset_dirty(ram_addr, TARGET_PAGE_SIZE, + DIRTY_MEMORY_CODE); } /* update the TLB so that writes in physical page 'phys_addr' are no longer tested for self modifying code */ -void tlb_unprotect_code_phys(CPUArchState *env, ram_addr_t ram_addr, +void tlb_unprotect_code_phys(CPUState *cpu, ram_addr_t ram_addr, target_ulong vaddr) { - cpu_physical_memory_set_dirty_flags(ram_addr, CODE_DIRTY_FLAG); + cpu_physical_memory_set_dirty_flag(ram_addr, DIRTY_MEMORY_CODE); } static bool tlb_is_dirty_ram(CPUTLBEntry *tlbe) @@ -158,28 +155,26 @@ void tlb_reset_dirty_range(CPUTLBEntry *tlb_entry, uintptr_t start, } } -static inline void tlb_update_dirty(CPUTLBEntry *tlb_entry) +static inline ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr) { ram_addr_t ram_addr; - void *p; - if (tlb_is_dirty_ram(tlb_entry)) { - p = (void *)(uintptr_t)((tlb_entry->addr_write & TARGET_PAGE_MASK) - + tlb_entry->addend); - ram_addr = qemu_ram_addr_from_host_nofail(p); - if (!cpu_physical_memory_is_dirty(ram_addr)) { - tlb_entry->addr_write |= TLB_NOTDIRTY; - } + if (qemu_ram_addr_from_host(ptr, &ram_addr) == NULL) { + fprintf(stderr, "Bad ram pointer %p\n", ptr); + abort(); } + return ram_addr; } void cpu_tlb_reset_dirty_all(ram_addr_t start1, ram_addr_t length) { + CPUState *cpu; CPUArchState *env; - for (env = first_cpu; env != NULL; env = env->next_cpu) { + CPU_FOREACH(cpu) { int mmu_idx; + env = cpu->env_ptr; for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { unsigned int i; @@ -187,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); + } } } } @@ -210,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 @@ -238,10 +245,11 @@ static void tlb_add_large_page(CPUArchState *env, target_ulong vaddr, /* Add a new TLB entry. At most one entry for a given virtual address is permitted. Only a single TARGET_PAGE_SIZE region is mapped, the supplied size is only used by tlb_flush_page. */ -void tlb_set_page(CPUArchState *env, target_ulong vaddr, +void tlb_set_page(CPUState *cpu, target_ulong vaddr, hwaddr paddr, int prot, int mmu_idx, target_ulong size) { + CPUArchState *env = cpu->env_ptr; MemoryRegionSection *section; unsigned int index; target_ulong address; @@ -249,6 +257,7 @@ void tlb_set_page(CPUArchState *env, 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) { @@ -256,12 +265,13 @@ void tlb_set_page(CPUArchState *env, target_ulong vaddr, } sz = size; - section = address_space_translate(&address_space_memory, paddr, &xlat, &sz, - false); + section = address_space_translate_for_iotlb(cpu->as, paddr, + &xlat, &sz); assert(sz >= TARGET_PAGE_SIZE); #if defined(DEBUG_TLB) - printf("tlb_set_page: vaddr=" TARGET_FMT_lx " paddr=0x" TARGET_FMT_plx + qemu_log_mask(CPU_LOG_MMU, + "tlb_set_page: vaddr=" TARGET_FMT_lx " paddr=0x" TARGET_FMT_plx " prot=%x idx=%d\n", vaddr, paddr, prot, mmu_idx); #endif @@ -277,12 +287,18 @@ void tlb_set_page(CPUArchState *env, target_ulong vaddr, } code_address = address; - iotlb = memory_region_section_get_iotlb(env, section, vaddr, paddr, xlat, + iotlb = memory_region_section_get_iotlb(cpu, section, vaddr, paddr, xlat, 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; @@ -301,7 +317,8 @@ void tlb_set_page(CPUArchState *env, target_ulong vaddr, /* Write access calls the I/O callback. */ te->addr_write = address | TLB_MMIO; } else if (memory_region_is_ram(section->mr) - && !cpu_physical_memory_is_dirty(section->mr->ram_addr + xlat)) { + && cpu_physical_memory_is_clean(section->mr->ram_addr + + xlat)) { te->addr_write = address | TLB_NOTDIRTY; } else { te->addr_write = address; @@ -321,6 +338,7 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr) int mmu_idx, page_index, pd; void *p; MemoryRegion *mr; + CPUState *cpu = ENV_GET_CPU(env1); page_index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); mmu_idx = cpu_mmu_index(env1); @@ -329,34 +347,51 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr) cpu_ldub_code(env1, addr); } pd = env1->iotlb[mmu_idx][page_index] & ~TARGET_PAGE_MASK; - mr = iotlb_to_region(pd); + mr = iotlb_to_region(cpu->as, pd); if (memory_region_is_unassigned(mr)) { -#if defined(TARGET_ALPHA) || defined(TARGET_MIPS) || defined(TARGET_SPARC) - cpu_unassigned_access(env1, addr, 0, 1, 0, 4); -#else - cpu_abort(env1, "Trying to execute code outside RAM or ROM at 0x" - TARGET_FMT_lx "\n", addr); -#endif + CPUClass *cc = CPU_GET_CLASS(cpu); + + if (cc->do_unassigned_access) { + cc->do_unassigned_access(cpu, addr, false, true, 0, 4); + } else { + cpu_abort(cpu, "Trying to execute code outside RAM or ROM at 0x" + TARGET_FMT_lx "\n", addr); + } } p = (void *)((uintptr_t)addr + env1->tlb_table[mmu_idx][page_index].addend); return qemu_ram_addr_from_host_nofail(p); } +#define MMUSUFFIX _mmu + +#define SHIFT 0 +#include "softmmu_template.h" + +#define SHIFT 1 +#include "softmmu_template.h" + +#define SHIFT 2 +#include "softmmu_template.h" + +#define SHIFT 3 +#include "softmmu_template.h" +#undef MMUSUFFIX + #define MMUSUFFIX _cmmu -#undef GETPC -#define GETPC() ((uintptr_t)0) +#undef GETPC_ADJ +#define GETPC_ADJ 0 +#undef GETRA +#define GETRA() ((uintptr_t)0) #define SOFTMMU_CODE_ACCESS #define SHIFT 0 -#include "exec/softmmu_template.h" +#include "softmmu_template.h" #define SHIFT 1 -#include "exec/softmmu_template.h" +#include "softmmu_template.h" #define SHIFT 2 -#include "exec/softmmu_template.h" +#include "softmmu_template.h" #define SHIFT 3 -#include "exec/softmmu_template.h" - -#undef env +#include "softmmu_template.h"