From da6bbf8513e621a8fc2fd315d77318f36547474d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 2 Apr 2019 14:37:51 +0700 Subject: tcg: Add CPUClass::tlb_fill MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This hook will replace the (user-only mode specific) handle_mmu_fault hook, and the (system mode specific) tlb_fill function. The handle_mmu_fault hook was written as if there was a valid way to recover from an mmu fault, and had 3 possible return states. In reality, the only valid action is to raise an exception, return to the main loop, and deliver the SIGSEGV to the guest. Note that all of the current implementations of handle_mmu_fault for guests which support linux-user do in fact only ever return 1, which is the signal to return to the main loop. Using the hook for system mode requires that all targets be converted, so for now the hook is (optionally) used only from user-only mode. Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- accel/tcg/user-exec.c | 39 ++++++++++++++------------------------- 1 file changed, 14 insertions(+), 25 deletions(-) (limited to 'accel') diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 0789984..199f88c 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -65,6 +65,7 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info, CPUClass *cc; int ret; unsigned long address = (unsigned long)info->si_addr; + MMUAccessType access_type; /* We must handle PC addresses from two different sources: * a call return address and a signal frame address. @@ -147,35 +148,23 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info, are still valid segv ones */ address = h2g_nocheck(address); - cc = CPU_GET_CLASS(cpu); - /* see if it is an MMU fault */ - g_assert(cc->handle_mmu_fault); - ret = cc->handle_mmu_fault(cpu, address, 0, is_write, MMU_USER_IDX); - - if (ret == 0) { - /* The MMU fault was handled without causing real CPU fault. - * Retain helper_retaddr for a possible second fault. - */ - return 1; - } - - /* All other paths lead to cpu_exit; clear helper_retaddr - * for next execution. + /* + * There is no way the target can handle this other than raising + * an exception. Undo signal and retaddr state prior to longjmp. */ + sigprocmask(SIG_SETMASK, old_set, NULL); helper_retaddr = 0; - if (ret < 0) { - return 0; /* not an MMU fault */ + cc = CPU_GET_CLASS(cpu); + if (cc->tlb_fill) { + access_type = is_write ? MMU_DATA_STORE : MMU_DATA_LOAD; + cc->tlb_fill(cpu, address, 0, access_type, MMU_USER_IDX, false, pc); + g_assert_not_reached(); + } else { + ret = cc->handle_mmu_fault(cpu, address, 0, is_write, MMU_USER_IDX); + g_assert(ret > 0); + cpu_loop_exit_restore(cpu, pc); } - - /* Now we have a real cpu fault. */ - cpu_restore_state(cpu, pc, true); - - sigprocmask(SIG_SETMASK, old_set, NULL); - cpu_loop_exit(cpu); - - /* never comes here */ - return 1; } #if defined(__i386__) -- cgit v1.1 From c319dc13579a92937bffe02ad2c9f1a550e73973 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 3 Apr 2019 09:07:11 +0700 Subject: tcg: Use CPUClass::tlb_fill in cputlb.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can now use the CPUClass hook instead of a named function. Create a static tlb_fill function to avoid other changes within cputlb.c. This also isolates the asserts within. Remove the named tlb_fill function from all of the targets. Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'accel') diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index f2f6182..dfcd9ae 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -855,6 +855,25 @@ static inline ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr) return ram_addr; } +/* + * Note: tlb_fill() can trigger a resize of the TLB. This means that all of the + * caller's prior references to the TLB table (e.g. CPUTLBEntry pointers) must + * be discarded and looked up again (e.g. via tlb_entry()). + */ +static void tlb_fill(CPUState *cpu, target_ulong addr, int size, + MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) +{ + CPUClass *cc = CPU_GET_CLASS(cpu); + bool ok; + + /* + * This is not a probe, so only valid return is success; failure + * should result in exception + longjmp to the cpu loop. + */ + ok = cc->tlb_fill(cpu, addr, size, access_type, mmu_idx, false, retaddr); + assert(ok); +} + static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry, int mmu_idx, target_ulong addr, uintptr_t retaddr, -- cgit v1.1 From 69963f5709a0645934c169784820d0bee22208ba Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 3 Apr 2019 10:37:13 +0700 Subject: tcg: Remove CPUClass::handle_mmu_fault MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This hook is now completely replaced by tlb_fill. Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/user-exec.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) (limited to 'accel') diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 199f88c..8cfbeb1 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -63,7 +63,6 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info, { CPUState *cpu = current_cpu; CPUClass *cc; - int ret; unsigned long address = (unsigned long)info->si_addr; MMUAccessType access_type; @@ -156,15 +155,9 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info, helper_retaddr = 0; cc = CPU_GET_CLASS(cpu); - if (cc->tlb_fill) { - access_type = is_write ? MMU_DATA_STORE : MMU_DATA_LOAD; - cc->tlb_fill(cpu, address, 0, access_type, MMU_USER_IDX, false, pc); - g_assert_not_reached(); - } else { - ret = cc->handle_mmu_fault(cpu, address, 0, is_write, MMU_USER_IDX); - g_assert(ret > 0); - cpu_loop_exit_restore(cpu, pc); - } + access_type = is_write ? MMU_DATA_STORE : MMU_DATA_LOAD; + cc->tlb_fill(cpu, address, 0, access_type, MMU_USER_IDX, false, pc); + g_assert_not_reached(); } #if defined(__i386__) -- cgit v1.1 From 4811e9095c0491bc6f5450e5012c9c4796b9e59d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 3 Apr 2019 10:16:56 +0700 Subject: tcg: Use tlb_fill probe from tlb_vaddr_to_host MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most of the existing users would continue around a loop which would fault the tlb entry in via a normal load/store. But for AArch64 SVE we have an existing emulation bug wherein we would mark the first element of a no-fault vector load as faulted (within the FFR, not via exception) just because we did not have its address in the TLB. Now we can properly only mark it as faulted if there really is no valid, readable translation, while still not raising an exception. (Note that beyond the first element of the vector, the hardware may report a fault for any reason whatsoever; with at least one element loaded, forward progress is guaranteed.) Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 61 insertions(+), 8 deletions(-) (limited to 'accel') diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index dfcd9ae..685e0f2 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -1007,6 +1007,16 @@ static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry, } } +static inline target_ulong tlb_read_ofs(CPUTLBEntry *entry, size_t ofs) +{ +#if TCG_OVERSIZED_GUEST + return *(target_ulong *)((uintptr_t)entry + ofs); +#else + /* ofs might correspond to .addr_write, so use atomic_read */ + return atomic_read((target_ulong *)((uintptr_t)entry + ofs)); +#endif +} + /* Return true if ADDR is present in the victim tlb, and has been copied back to the main tlb. */ static bool victim_tlb_hit(CPUArchState *env, size_t mmu_idx, size_t index, @@ -1017,14 +1027,7 @@ static bool victim_tlb_hit(CPUArchState *env, size_t mmu_idx, size_t index, assert_cpu_is_self(ENV_GET_CPU(env)); for (vidx = 0; vidx < CPU_VTLB_SIZE; ++vidx) { CPUTLBEntry *vtlb = &env->tlb_v_table[mmu_idx][vidx]; - target_ulong cmp; - - /* elt_ofs might correspond to .addr_write, so use atomic_read */ -#if TCG_OVERSIZED_GUEST - cmp = *(target_ulong *)((uintptr_t)vtlb + elt_ofs); -#else - cmp = atomic_read((target_ulong *)((uintptr_t)vtlb + elt_ofs)); -#endif + target_ulong cmp = tlb_read_ofs(vtlb, elt_ofs); if (cmp == page) { /* Found entry in victim tlb, swap tlb and iotlb. */ @@ -1108,6 +1111,56 @@ void probe_write(CPUArchState *env, target_ulong addr, int size, int mmu_idx, } } +void *tlb_vaddr_to_host(CPUArchState *env, abi_ptr addr, + MMUAccessType access_type, int mmu_idx) +{ + CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr); + uintptr_t tlb_addr, page; + size_t elt_ofs; + + switch (access_type) { + case MMU_DATA_LOAD: + elt_ofs = offsetof(CPUTLBEntry, addr_read); + break; + case MMU_DATA_STORE: + elt_ofs = offsetof(CPUTLBEntry, addr_write); + break; + case MMU_INST_FETCH: + elt_ofs = offsetof(CPUTLBEntry, addr_code); + break; + default: + g_assert_not_reached(); + } + + page = addr & TARGET_PAGE_MASK; + tlb_addr = tlb_read_ofs(entry, elt_ofs); + + if (!tlb_hit_page(tlb_addr, page)) { + uintptr_t index = tlb_index(env, mmu_idx, addr); + + if (!victim_tlb_hit(env, mmu_idx, index, elt_ofs, page)) { + CPUState *cs = ENV_GET_CPU(env); + CPUClass *cc = CPU_GET_CLASS(cs); + + if (!cc->tlb_fill(cs, addr, 0, access_type, mmu_idx, true, 0)) { + /* Non-faulting page table read failed. */ + return NULL; + } + + /* TLB resize via tlb_fill may have moved the entry. */ + entry = tlb_entry(env, mmu_idx, addr); + } + tlb_addr = tlb_read_ofs(entry, elt_ofs); + } + + if (tlb_addr & ~TARGET_PAGE_MASK) { + /* IO access */ + return NULL; + } + + return (void *)((uintptr_t)addr + entry->addend); +} + /* Probe for a read-modify-write atomic operation. Do not allow unaligned * operations, or io operations to proceed. Return the host address. */ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, -- cgit v1.1