Commit 52ba13f0 authored by Richard Henderson's avatar Richard Henderson
Browse files

tcg: Release mmap_lock on translation fault

Turn helper_retaddr into a multi-state flag that may now also
indicate when we're performing a read on behalf of the translator.
In this case, release the mmap_lock before the longjmp back to
the main cpu loop, and thereby avoid a failing assert therein.

Fixes: https://bugs.launchpad.net/qemu/+bug/1832353


Tested-by: default avatarAlex Bennée <alex.bennee@linaro.org>
Reviewed-by: default avatarAlex Bennée <alex.bennee@linaro.org>
Signed-off-by: default avatarRichard Henderson <richard.henderson@linaro.org>
parent 2fbb2353
Loading
Loading
Loading
Loading
+47 −19
Original line number Diff line number Diff line
@@ -64,27 +64,56 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info,
    CPUState *cpu = current_cpu;
    CPUClass *cc;
    unsigned long address = (unsigned long)info->si_addr;
    MMUAccessType access_type;
    MMUAccessType access_type = is_write ? MMU_DATA_STORE : MMU_DATA_LOAD;

    /* We must handle PC addresses from two different sources:
     * a call return address and a signal frame address.
     *
     * Within cpu_restore_state_from_tb we assume the former and adjust
     * the address by -GETPC_ADJ so that the address is within the call
     * insn so that addr does not accidentally match the beginning of the
     * next guest insn.
    switch (helper_retaddr) {
    default:
        /*
         * Fault during host memory operation within a helper function.
         * The helper's host return address, saved here, gives us a
         * pointer into the generated code that will unwind to the
         * correct guest pc.
         */
        pc = helper_retaddr;
        break;

    case 0:
        /*
         * Fault during host memory operation within generated code.
         * (Or, a unrelated bug within qemu, but we can't tell from here).
         *
     * However, when the PC comes from the signal frame, it points to
     * the actual faulting host insn and not a call insn.  Subtracting
     * GETPC_ADJ in that case may accidentally match the previous guest insn.
         * We take the host pc from the signal frame.  However, we cannot
         * use that value directly.  Within cpu_restore_state_from_tb, we
         * assume PC comes from GETPC(), as used by the helper functions,
         * so we adjust the address by -GETPC_ADJ to form an address that
         * is within the call insn, so that the address does not accidentially
         * match the beginning of the next guest insn.  However, when the
         * pc comes from the signal frame it points to the actual faulting
         * host memory insn and not the return from a call insn.
         *
     * So for the later case, adjust forward to compensate for what
     * will be done later by cpu_restore_state_from_tb.
         * Therefore, adjust to compensate for what will be done later
         * by cpu_restore_state_from_tb.
         */
    if (helper_retaddr) {
        pc = helper_retaddr;
    } else {
        pc += GETPC_ADJ;
        break;

    case 1:
        /*
         * Fault during host read for translation, or loosely, "execution".
         *
         * The guest pc is already pointing to the start of the TB for which
         * code is being generated.  If the guest translator manages the
         * page crossings correctly, this is exactly the correct address
         * (and if the translator doesn't handle page boundaries correctly
         * there's little we can do about that here).  Therefore, do not
         * trigger the unwinder.
         *
         * Like tb_gen_code, release the memory lock before cpu_loop_exit.
         */
        pc = 0;
        access_type = MMU_INST_FETCH;
        mmap_unlock();
        break;
    }

    /* For synchronous signals we expect to be coming from the vCPU
@@ -155,7 +184,6 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info,
    clear_helper_retaddr();

    cc = CPU_GET_CLASS(cpu);
    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();
}
+16 −4
Original line number Diff line number Diff line
@@ -64,12 +64,18 @@
static inline RES_TYPE
glue(glue(cpu_ld, USUFFIX), MEMSUFFIX)(CPUArchState *env, abi_ptr ptr)
{
#if !defined(CODE_ACCESS)
#ifdef CODE_ACCESS
    RES_TYPE ret;
    set_helper_retaddr(1);
    ret = glue(glue(ld, USUFFIX), _p)(g2h(ptr));
    clear_helper_retaddr();
    return ret;
#else
    trace_guest_mem_before_exec(
        env_cpu(env), ptr,
        trace_mem_build_info(SHIFT, false, MO_TE, false));
#endif
    return glue(glue(ld, USUFFIX), _p)(g2h(ptr));
#endif
}

#ifndef CODE_ACCESS
@@ -90,12 +96,18 @@ glue(glue(glue(cpu_ld, USUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
static inline int
glue(glue(cpu_lds, SUFFIX), MEMSUFFIX)(CPUArchState *env, abi_ptr ptr)
{
#if !defined(CODE_ACCESS)
#ifdef CODE_ACCESS
    int ret;
    set_helper_retaddr(1);
    ret = glue(glue(lds, SUFFIX), _p)(g2h(ptr));
    clear_helper_retaddr();
    return ret;
#else
    trace_guest_mem_before_exec(
        env_cpu(env), ptr,
        trace_mem_build_info(SHIFT, true, MO_TE, false));
#endif
    return glue(glue(lds, SUFFIX), _p)(g2h(ptr));
#endif
}

#ifndef CODE_ACCESS