X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=cpu-exec.c;h=748cb66bca0e38bae44beb71fe72687d674d8a6f;hb=d1c136885ba5b302f9781b8927a8ea2ee38cccd2;hp=b240b9fa45a0d1567c23586e1fbdc6e638c14fc8;hpb=8212ff86f4405b6128d89dd1d97ff2d6cfcf9842;p=mirror_qemu.git diff --git a/cpu-exec.c b/cpu-exec.c index b240b9fa45..748cb66bca 100644 --- a/cpu-exec.c +++ b/cpu-exec.c @@ -18,7 +18,7 @@ */ #include "qemu/osdep.h" #include "cpu.h" -#include "trace.h" +#include "trace-root.h" #include "disas/disas.h" #include "exec/exec-all.h" #include "tcg.h" @@ -29,9 +29,11 @@ #include "qemu/rcu.h" #include "exec/tb-hash.h" #include "exec/log.h" +#include "qemu/main-loop.h" #if defined(TARGET_I386) && !defined(CONFIG_USER_ONLY) #include "hw/i386/apic.h" #endif +#include "sysemu/cpus.h" #include "sysemu/replay.h" /* -icount align implementation. */ @@ -143,22 +145,20 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb) uint8_t *tb_ptr = itb->tc_ptr; qemu_log_mask_and_addr(CPU_LOG_EXEC, itb->pc, - "Trace %p [" TARGET_FMT_lx "] %s\n", - itb->tc_ptr, itb->pc, lookup_symbol(itb->pc)); + "Trace %p [%d: " TARGET_FMT_lx "] %s\n", + itb->tc_ptr, cpu->cpu_index, itb->pc, + lookup_symbol(itb->pc)); #if defined(DEBUG_DISAS) - if (qemu_loglevel_mask(CPU_LOG_TB_CPU)) { + if (qemu_loglevel_mask(CPU_LOG_TB_CPU) + && qemu_log_in_addr_range(itb->pc)) { + qemu_log_lock(); #if defined(TARGET_I386) log_cpu_state(cpu, CPU_DUMP_CCOP); -#elif defined(TARGET_M68K) - /* ??? Should not modify env state for dumping. */ - cpu_m68k_flush_flags(env, env->cc_op); - env->cc_op = CC_OP_FLAGS; - env->sr = (env->sr & 0xffe0) | env->cc_dest | (env->cc_x << 4); - log_cpu_state(cpu, 0); #else log_cpu_state(cpu, 0); #endif + qemu_log_unlock(); } #endif /* DEBUG_DISAS */ @@ -187,12 +187,6 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb) cc->set_pc(cpu, last_tb->pc); } } - if (tb_exit == TB_EXIT_REQUESTED) { - /* We were asked to stop executing TBs (probably a pending - * interrupt. We've now stopped, so clear the flag. - */ - cpu->tcg_exit_req = 0; - } return ret; } @@ -203,28 +197,83 @@ static void cpu_exec_nocache(CPUState *cpu, int max_cycles, TranslationBlock *orig_tb, bool ignore_icount) { TranslationBlock *tb; - bool old_tb_flushed; /* Should never happen. We only end up here when an existing TB is too long. */ if (max_cycles > CF_COUNT_MASK) max_cycles = CF_COUNT_MASK; - old_tb_flushed = cpu->tb_flushed; - cpu->tb_flushed = false; + tb_lock(); tb = tb_gen_code(cpu, orig_tb->pc, orig_tb->cs_base, orig_tb->flags, max_cycles | CF_NOCACHE | (ignore_icount ? CF_IGNORE_ICOUNT : 0)); - tb->orig_tb = cpu->tb_flushed ? NULL : orig_tb; - cpu->tb_flushed |= old_tb_flushed; + tb->orig_tb = orig_tb; + tb_unlock(); + /* execute the generated code */ trace_exec_tb_nocache(tb, tb->pc); cpu_tb_exec(cpu, tb); + + tb_lock(); tb_phys_invalidate(tb, -1); tb_free(tb); + tb_unlock(); } #endif +static void cpu_exec_step(CPUState *cpu) +{ + CPUClass *cc = CPU_GET_CLASS(cpu); + CPUArchState *env = (CPUArchState *)cpu->env_ptr; + TranslationBlock *tb; + target_ulong cs_base, pc; + uint32_t flags; + + cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags); + if (sigsetjmp(cpu->jmp_env, 0) == 0) { + mmap_lock(); + tb_lock(); + tb = tb_gen_code(cpu, pc, cs_base, flags, + 1 | CF_NOCACHE | CF_IGNORE_ICOUNT); + tb->orig_tb = NULL; + tb_unlock(); + mmap_unlock(); + + cc->cpu_exec_enter(cpu); + /* execute the generated code */ + trace_exec_tb_nocache(tb, pc); + cpu_tb_exec(cpu, tb); + cc->cpu_exec_exit(cpu); + + tb_lock(); + tb_phys_invalidate(tb, -1); + tb_free(tb); + tb_unlock(); + } else { + /* We may have exited due to another problem here, so we need + * to reset any tb_locks we may have taken but didn't release. + * The mmap_lock is dropped by tb_gen_code if it runs out of + * memory. + */ +#ifndef CONFIG_SOFTMMU + tcg_debug_assert(!have_mmap_lock()); +#endif + tb_lock_reset(); + } +} + +void cpu_exec_step_atomic(CPUState *cpu) +{ + start_exclusive(); + + /* Since we got here, we know that parallel_cpus must be true. */ + parallel_cpus = false; + cpu_exec_step(cpu); + parallel_cpus = true; + + end_exclusive(); +} + struct tb_desc { target_ulong pc; target_ulong cs_base; @@ -337,10 +386,7 @@ static inline TranslationBlock *tb_find(CPUState *cpu, tb_lock(); have_tb_lock = true; } - /* Check if translation buffer has been flushed */ - if (cpu->tb_flushed) { - cpu->tb_flushed = false; - } else if (!tb->invalid) { + if (!tb->invalid) { tb_add_jump(last_tb, tb_exit, tb); } } @@ -357,12 +403,13 @@ static inline bool cpu_handle_halt(CPUState *cpu) if ((cpu->interrupt_request & CPU_INTERRUPT_POLL) && replay_interrupt()) { X86CPU *x86_cpu = X86_CPU(cpu); + qemu_mutex_lock_iothread(); apic_poll_irq(x86_cpu->apic_state); cpu_reset_interrupt(cpu, CPU_INTERRUPT_POLL); + qemu_mutex_unlock_iothread(); } #endif if (!cpu_has_work(cpu)) { - current_cpu = NULL; return true; } @@ -412,7 +459,9 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret) #else if (replay_exception()) { CPUClass *cc = CPU_GET_CLASS(cpu); + qemu_mutex_lock_iothread(); cc->do_interrupt(cpu); + qemu_mutex_unlock_iothread(); cpu->exception_index = -1; } else if (!replay_has_interrupt()) { /* give a chance to iothread in replay mode */ @@ -434,13 +483,15 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret) return false; } -static inline void cpu_handle_interrupt(CPUState *cpu, +static inline bool cpu_handle_interrupt(CPUState *cpu, TranslationBlock **last_tb) { CPUClass *cc = CPU_GET_CLASS(cpu); - int interrupt_request = cpu->interrupt_request; - if (unlikely(interrupt_request)) { + if (unlikely(atomic_read(&cpu->interrupt_request))) { + int interrupt_request; + qemu_mutex_lock_iothread(); + interrupt_request = cpu->interrupt_request; if (unlikely(cpu->singlestep_enabled & SSTEP_NOIRQ)) { /* Mask out external interrupts for this step. */ interrupt_request &= ~CPU_INTERRUPT_SSTEP_MASK; @@ -448,7 +499,8 @@ static inline void cpu_handle_interrupt(CPUState *cpu, if (interrupt_request & CPU_INTERRUPT_DEBUG) { cpu->interrupt_request &= ~CPU_INTERRUPT_DEBUG; cpu->exception_index = EXCP_DEBUG; - cpu_loop_exit(cpu); + qemu_mutex_unlock_iothread(); + return true; } if (replay_mode == REPLAY_MODE_PLAY && !replay_has_interrupt()) { /* Do nothing */ @@ -457,23 +509,26 @@ static inline void cpu_handle_interrupt(CPUState *cpu, cpu->interrupt_request &= ~CPU_INTERRUPT_HALT; cpu->halted = 1; cpu->exception_index = EXCP_HLT; - cpu_loop_exit(cpu); + qemu_mutex_unlock_iothread(); + return true; } #if defined(TARGET_I386) else if (interrupt_request & CPU_INTERRUPT_INIT) { X86CPU *x86_cpu = X86_CPU(cpu); CPUArchState *env = &x86_cpu->env; replay_interrupt(); - cpu_svm_check_intercept_param(env, SVM_EXIT_INIT, 0); + cpu_svm_check_intercept_param(env, SVM_EXIT_INIT, 0, 0); do_cpu_init(x86_cpu); cpu->exception_index = EXCP_HALTED; - cpu_loop_exit(cpu); + qemu_mutex_unlock_iothread(); + return true; } #else else if (interrupt_request & CPU_INTERRUPT_RESET) { replay_interrupt(); cpu_reset(cpu); - cpu_loop_exit(cpu); + qemu_mutex_unlock_iothread(); + return true; } #endif /* The target hook has 3 exit conditions: @@ -481,8 +536,8 @@ static inline void cpu_handle_interrupt(CPUState *cpu, True when it is, and we should restart on a new TB, and via longjmp via cpu_loop_exit. */ else { - replay_interrupt(); if (cc->cpu_exec_interrupt(cpu, interrupt_request)) { + replay_interrupt(); *last_tb = NULL; } /* The target hook may have updated the 'cpu->interrupt_request'; @@ -495,72 +550,71 @@ static inline void cpu_handle_interrupt(CPUState *cpu, the program flow was changed */ *last_tb = NULL; } + + /* If we exit via cpu_loop_exit/longjmp it is reset in cpu_exec */ + qemu_mutex_unlock_iothread(); } - if (unlikely(cpu->exit_request || replay_has_interrupt())) { - cpu->exit_request = 0; + + /* Finally, check if we need to exit to the main loop. */ + if (unlikely(atomic_read(&cpu->exit_request) + || (use_icount && cpu->icount_decr.u16.low + cpu->icount_extra == 0))) { + atomic_set(&cpu->exit_request, 0); cpu->exception_index = EXCP_INTERRUPT; - cpu_loop_exit(cpu); + return true; } + + return false; } static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb, - TranslationBlock **last_tb, int *tb_exit, - SyncClocks *sc) + TranslationBlock **last_tb, int *tb_exit) { uintptr_t ret; - - if (unlikely(cpu->exit_request)) { - return; - } + int32_t insns_left; trace_exec_tb(tb, tb->pc); ret = cpu_tb_exec(cpu, tb); - *last_tb = (TranslationBlock *)(ret & ~TB_EXIT_MASK); + tb = (TranslationBlock *)(ret & ~TB_EXIT_MASK); *tb_exit = ret & TB_EXIT_MASK; - switch (*tb_exit) { - case TB_EXIT_REQUESTED: - /* Something asked us to stop executing - * chained TBs; just continue round the main - * loop. Whatever requested the exit will also - * have set something else (eg exit_request or - * interrupt_request) which we will handle - * next time around the loop. But we need to - * ensure the tcg_exit_req read in generated code + if (*tb_exit != TB_EXIT_REQUESTED) { + *last_tb = tb; + return; + } + + *last_tb = NULL; + insns_left = atomic_read(&cpu->icount_decr.u32); + atomic_set(&cpu->icount_decr.u16.high, 0); + if (insns_left < 0) { + /* Something asked us to stop executing chained TBs; just + * continue round the main loop. Whatever requested the exit + * will also have set something else (eg exit_request or + * interrupt_request) which we will handle next time around + * the loop. But we need to ensure the zeroing of icount_decr * comes before the next read of cpu->exit_request * or cpu->interrupt_request. */ - smp_rmb(); - *last_tb = NULL; - break; - case TB_EXIT_ICOUNT_EXPIRED: - { - /* Instruction counter expired. */ -#ifdef CONFIG_USER_ONLY - abort(); -#else - int insns_left = cpu->icount_decr.u32; - if (cpu->icount_extra && insns_left >= 0) { - /* Refill decrementer and continue execution. */ - cpu->icount_extra += insns_left; - insns_left = MIN(0xffff, cpu->icount_extra); - cpu->icount_extra -= insns_left; - cpu->icount_decr.u16.low = insns_left; - } else { - if (insns_left > 0) { - /* Execute remaining instructions. */ - cpu_exec_nocache(cpu, insns_left, *last_tb, false); - align_clocks(sc, cpu); - } - cpu->exception_index = EXCP_INTERRUPT; - *last_tb = NULL; - cpu_loop_exit(cpu); - } - break; -#endif + smp_mb(); + return; } - default: - break; + + /* Instruction counter expired. */ + assert(use_icount); +#ifndef CONFIG_USER_ONLY + if (cpu->icount_extra) { + /* Refill decrementer and continue execution. */ + cpu->icount_extra += insns_left; + insns_left = MIN(0xffff, cpu->icount_extra); + cpu->icount_extra -= insns_left; + cpu->icount_decr.u16.low = insns_left; + } else { + /* Execute any remaining instructions, then let the main loop + * handle the next event. + */ + if (insns_left > 0) { + cpu_exec_nocache(cpu, insns_left, tb, false); + } } +#endif } /* main execution loop */ @@ -569,7 +623,7 @@ int cpu_exec(CPUState *cpu) { CPUClass *cc = CPU_GET_CLASS(cpu); int ret; - SyncClocks sc; + SyncClocks sc = { 0 }; /* replay_interrupt may need current_cpu */ current_cpu = cpu; @@ -578,13 +632,8 @@ int cpu_exec(CPUState *cpu) return EXCP_HALTED; } - atomic_mb_set(&tcg_current_cpu, cpu); rcu_read_lock(); - if (unlikely(atomic_mb_read(&exit_request))) { - cpu->exit_request = 1; - } - cc->cpu_exec_enter(cpu); /* Calculate difference between guest clock and host clock. @@ -594,51 +643,43 @@ int cpu_exec(CPUState *cpu) */ init_delay_params(&sc, cpu); - for(;;) { - /* prepare setjmp context for exception handling */ - if (sigsetjmp(cpu->jmp_env, 0) == 0) { - TranslationBlock *tb, *last_tb = NULL; - int tb_exit = 0; - - /* if an exception is pending, we execute it here */ - if (cpu_handle_exception(cpu, &ret)) { - break; - } - - atomic_mb_set(&cpu->tb_flushed, false); /* reset before first TB lookup */ - for(;;) { - cpu_handle_interrupt(cpu, &last_tb); - tb = tb_find(cpu, last_tb, tb_exit); - cpu_loop_exec_tb(cpu, tb, &last_tb, &tb_exit, &sc); - /* Try to align the host and virtual clocks - if the guest is in advance */ - align_clocks(&sc, cpu); - } /* for(;;) */ - } else { + /* prepare setjmp context for exception handling */ + if (sigsetjmp(cpu->jmp_env, 0) != 0) { #if defined(__clang__) || !QEMU_GNUC_PREREQ(4, 6) - /* Some compilers wrongly smash all local variables after - * siglongjmp. There were bug reports for gcc 4.5.0 and clang. - * Reload essential local variables here for those compilers. - * Newer versions of gcc would complain about this code (-Wclobbered). */ - cpu = current_cpu; - cc = CPU_GET_CLASS(cpu); + /* Some compilers wrongly smash all local variables after + * siglongjmp. There were bug reports for gcc 4.5.0 and clang. + * Reload essential local variables here for those compilers. + * Newer versions of gcc would complain about this code (-Wclobbered). */ + cpu = current_cpu; + cc = CPU_GET_CLASS(cpu); #else /* buggy compiler */ - /* Assert that the compiler does not smash local variables. */ - g_assert(cpu == current_cpu); - g_assert(cc == CPU_GET_CLASS(cpu)); + /* Assert that the compiler does not smash local variables. */ + g_assert(cpu == current_cpu); + g_assert(cc == CPU_GET_CLASS(cpu)); #endif /* buggy compiler */ - cpu->can_do_io = 1; - tb_lock_reset(); + cpu->can_do_io = 1; + tb_lock_reset(); + if (qemu_mutex_iothread_locked()) { + qemu_mutex_unlock_iothread(); + } + } + + /* if an exception is pending, we execute it here */ + while (!cpu_handle_exception(cpu, &ret)) { + TranslationBlock *last_tb = NULL; + int tb_exit = 0; + + while (!cpu_handle_interrupt(cpu, &last_tb)) { + TranslationBlock *tb = tb_find(cpu, last_tb, tb_exit); + cpu_loop_exec_tb(cpu, tb, &last_tb, &tb_exit); + /* Try to align the host and virtual clocks + if the guest is in advance */ + align_clocks(&sc, cpu); } - } /* for(;;) */ + } cc->cpu_exec_exit(cpu); rcu_read_unlock(); - /* fail safe : never use current_cpu outside cpu_exec() */ - current_cpu = NULL; - - /* Does not need atomic_mb_set because a spurious wakeup is okay. */ - atomic_set(&tcg_current_cpu, NULL); return ret; }