diff options
Diffstat (limited to 'src/recompiler/cpu-exec.c')
-rw-r--r-- | src/recompiler/cpu-exec.c | 1377 |
1 files changed, 578 insertions, 799 deletions
diff --git a/src/recompiler/cpu-exec.c b/src/recompiler/cpu-exec.c index 47fa445af..d6b8c00cc 100644 --- a/src/recompiler/cpu-exec.c +++ b/src/recompiler/cpu-exec.c @@ -14,8 +14,7 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -28,10 +27,11 @@ */ #include "config.h" -#define CPU_NO_GLOBAL_REGS #include "exec.h" #include "disas.h" #include "tcg.h" +#include "kvm.h" +#include "qemu-barrier.h" #if !defined(CONFIG_SOFTMMU) #undef EAX @@ -44,10 +44,12 @@ #undef EDI #undef EIP #include <signal.h> +#ifdef __linux__ #include <sys/ucontext.h> #endif +#endif -#if defined(__sparc__) && !defined(HOST_SOLARIS) +#if defined(__sparc__) && !defined(CONFIG_SOLARIS) // Work around ugly bugs in glibc that mangle global register contents #undef env #define env cpu_single_env @@ -55,29 +57,31 @@ int tb_invalidated_flag; -//#define DEBUG_EXEC +//#define CONFIG_DEBUG_EXEC //#define DEBUG_SIGNAL +int qemu_cpu_has_work(CPUState *env) +{ + return cpu_has_work(env); +} void cpu_loop_exit(void) { - /* NOTE: the register at this point must be saved by hand because - longjmp restore them */ - regs_to_env(); + env->current_tb = NULL; longjmp(env->jmp_env, 1); } -#if !(defined(TARGET_SPARC) || defined(TARGET_SH4) || defined(TARGET_M68K)) -#define reg_T2 -#endif - /* exit the current TB from a signal handler. The host registers are restored in a state compatible with the CPU emulator */ void cpu_resume_from_signal(CPUState *env1, void *puc) { #if !defined(CONFIG_SOFTMMU) +#ifdef __linux__ struct ucontext *uc = puc; +#elif defined(__OpenBSD__) + struct sigcontext *uc = puc; +#endif #endif env = env1; @@ -87,9 +91,18 @@ void cpu_resume_from_signal(CPUState *env1, void *puc) #if !defined(CONFIG_SOFTMMU) if (puc) { /* XXX: use siglongjmp ? */ +#ifdef __linux__ +#ifdef __ia64 + sigprocmask(SIG_SETMASK, (sigset_t *)&uc->uc_sigmask, NULL); +#else sigprocmask(SIG_SETMASK, &uc->uc_sigmask, NULL); +#endif +#elif defined(__OpenBSD__) + sigprocmask(SIG_SETMASK, &uc->sc_mask, NULL); +#endif } #endif + env->exception_index = -1; longjmp(env->jmp_env, 1); } @@ -114,11 +127,12 @@ static void cpu_exec_nocache(int max_cycles, TranslationBlock *orig_tb) #else next_tb = tcg_qemu_tb_exec(tb->tc_ptr); #endif + env->current_tb = NULL; if ((next_tb & 3) == 2) { /* Restore PC. This may happen if async event occurs before the TB starts executing. */ - CPU_PC_FROM_TB(env, tb); + cpu_pc_from_tb(env, tb); } tb_phys_invalidate(tb, -1); tb_free(tb); @@ -130,14 +144,13 @@ static TranslationBlock *tb_find_slow(target_ulong pc, { TranslationBlock *tb, **ptb1; unsigned int h; - target_ulong phys_pc, phys_page1, phys_page2, virt_page2; + tb_page_addr_t phys_pc, phys_page1, phys_page2; + target_ulong virt_page2; tb_invalidated_flag = 0; - regs_to_env(); /* XXX: do it just before cpu_gen_code() */ - /* find translated block using physical mappings */ - phys_pc = get_phys_addr_code(env, pc); + phys_pc = get_page_addr_code(env, pc); phys_page1 = phys_pc & TARGET_PAGE_MASK; phys_page2 = -1; h = tb_phys_hash_func(phys_pc); @@ -154,7 +167,7 @@ static TranslationBlock *tb_find_slow(target_ulong pc, if (tb->page_addr[1] != -1) { virt_page2 = (pc & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; - phys_page2 = get_phys_addr_code(env, virt_page2); + phys_page2 = get_page_addr_code(env, virt_page2); if (tb->page_addr[1] == phys_page2) goto found; } else { @@ -173,79 +186,16 @@ static TranslationBlock *tb_find_slow(target_ulong pc, return tb; } -#ifndef VBOX static inline TranslationBlock *tb_find_fast(void) -#else -DECLINLINE(TranslationBlock *) tb_find_fast(void) -#endif { TranslationBlock *tb; target_ulong cs_base, pc; - uint64_t flags; + int flags; /* we record a subset of the CPU state. It will always be the same before a given translated block is executed. */ -#if defined(TARGET_I386) - flags = env->hflags; - flags |= (env->eflags & (IOPL_MASK | TF_MASK | VM_MASK)); - cs_base = env->segs[R_CS].base; - pc = cs_base + env->eip; -#elif defined(TARGET_ARM) - flags = env->thumb | (env->vfp.vec_len << 1) - | (env->vfp.vec_stride << 4); - if ((env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR) - flags |= (1 << 6); - if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30)) - flags |= (1 << 7); - flags |= (env->condexec_bits << 8); - cs_base = 0; - pc = env->regs[15]; -#elif defined(TARGET_SPARC) -#ifdef TARGET_SPARC64 - // AM . Combined FPU enable bits . PRIV . DMMU enabled . IMMU enabled - flags = ((env->pstate & PS_AM) << 2) - | (((env->pstate & PS_PEF) >> 1) | ((env->fprs & FPRS_FEF) << 2)) - | (env->pstate & PS_PRIV) | ((env->lsu & (DMMU_E | IMMU_E)) >> 2); -#else - // FPU enable . Supervisor - flags = (env->psref << 4) | env->psrs; -#endif - cs_base = env->npc; - pc = env->pc; -#elif defined(TARGET_PPC) - flags = env->hflags; - cs_base = 0; - pc = env->nip; -#elif defined(TARGET_MIPS) - flags = env->hflags & (MIPS_HFLAG_TMASK | MIPS_HFLAG_BMASK); - cs_base = 0; - pc = env->active_tc.PC; -#elif defined(TARGET_M68K) - flags = (env->fpcr & M68K_FPCR_PREC) /* Bit 6 */ - | (env->sr & SR_S) /* Bit 13 */ - | ((env->macsr >> 4) & 0xf); /* Bits 0-3 */ - cs_base = 0; - pc = env->pc; -#elif defined(TARGET_SH4) - flags = (env->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL - | DELAY_SLOT_TRUE | DELAY_SLOT_CLEARME)) /* Bits 0- 3 */ - | (env->fpscr & (FPSCR_FR | FPSCR_SZ | FPSCR_PR)) /* Bits 19-21 */ - | (env->sr & (SR_MD | SR_RB)); /* Bits 29-30 */ - cs_base = 0; - pc = env->pc; -#elif defined(TARGET_ALPHA) - flags = env->ps; - cs_base = 0; - pc = env->pc; -#elif defined(TARGET_CRIS) - flags = env->pregs[PR_CCS] & (S_FLAG | P_FLAG | U_FLAG | X_FLAG); - flags |= env->dslot; - cs_base = 0; - pc = env->pc; -#else -#error unsupported CPU -#endif + cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags); tb = env->tb_jmp_cache[tb_jmp_cache_hash_func(pc)]; if (unlikely(!tb || tb->pc != pc || tb->cs_base != cs_base || tb->flags != flags)) { @@ -254,33 +204,71 @@ DECLINLINE(TranslationBlock *) tb_find_fast(void) return tb; } +static CPUDebugExcpHandler *debug_excp_handler; + +CPUDebugExcpHandler *cpu_set_debug_excp_handler(CPUDebugExcpHandler *handler) +{ + CPUDebugExcpHandler *old_handler = debug_excp_handler; + + debug_excp_handler = handler; + return old_handler; +} + +static void cpu_handle_debug_exception(CPUState *env) +{ + CPUWatchpoint *wp; + + if (!env->watchpoint_hit) + QTAILQ_FOREACH(wp, &env->watchpoints, entry) + wp->flags &= ~BP_WATCHPOINT_HIT; + + if (debug_excp_handler) + debug_excp_handler(env); +} + /* main execution loop */ -#ifdef VBOX +volatile sig_atomic_t exit_request; int cpu_exec(CPUState *env1) { -#define DECLARE_HOST_REGS 1 -#include "hostregs_helper.h" - int ret = 0, interrupt_request; + volatile host_reg_t saved_env_reg; + int ret VBOX_ONLY(= 0), interrupt_request; TranslationBlock *tb; uint8_t *tc_ptr; +#ifndef VBOX + uintptr_t next_tb; +#else /* VBOX */ unsigned long next_tb; +#endif /* VBOX */ + +# ifndef VBOX + if (cpu_halted(env1) == EXCP_HALTED) + return EXCP_HALTED; +# endif /* !VBOX */ cpu_single_env = env1; - /* first we save global registers */ -#define SAVE_HOST_REGS 1 -#include "hostregs_helper.h" + /* the access to env below is actually saving the global register's + value, so that files not including target-xyz/exec.h are free to + use it. */ + QEMU_BUILD_BUG_ON (sizeof (saved_env_reg) != sizeof (env)); + saved_env_reg = (host_reg_t) env; + barrier(); env = env1; - env_to_regs(); + if (unlikely(exit_request)) { + env->exit_request = 1; + } + #if defined(TARGET_I386) - /* put eflags in CPU temporary format */ - CC_SRC = env->eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); - DF = 1 - (2 * ((env->eflags >> 10) & 1)); - CC_OP = CC_OP_EFLAGS; - env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); + if (!kvm_enabled()) { + /* put eflags in CPU temporary format */ + CC_SRC = env->eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); + DF = 1 - (2 * ((env->eflags >> 10) & 1)); + CC_OP = CC_OP_EFLAGS; + env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); + } #elif defined(TARGET_SPARC) #elif defined(TARGET_M68K) env->cc_op = CC_OP_FLAGS; @@ -289,22 +277,29 @@ int cpu_exec(CPUState *env1) #elif defined(TARGET_ALPHA) #elif defined(TARGET_ARM) #elif defined(TARGET_PPC) +#elif defined(TARGET_MICROBLAZE) #elif defined(TARGET_MIPS) #elif defined(TARGET_SH4) #elif defined(TARGET_CRIS) +#elif defined(TARGET_S390X) /* XXXXX */ #else #error unsupported target CPU #endif #ifndef VBOX /* VBOX: We need to raise traps and suchlike from the outside. */ env->exception_index = -1; -#endif +#endif /* !VBOX */ /* prepare setjmp context for exception handling */ for(;;) { - if (setjmp(env->jmp_env) == 0) - { - env->current_tb = NULL; + if (setjmp(env->jmp_env) == 0) { +#if defined(__sparc__) && !defined(CONFIG_SOLARIS) +#undef env + env = cpu_single_env; +#define env cpu_single_env +#endif +#ifdef VBOX + env->current_tb = NULL; /* probably not needed, but whatever... */ /* * Check for fatal errors first @@ -315,298 +310,21 @@ int cpu_exec(CPUState *env1) ret = env->exception_index; cpu_loop_exit(); } - - /* if an exception is pending, we execute it here */ - if (env->exception_index >= 0) { - Assert(!env->user_mode_only); - if (env->exception_index >= EXCP_INTERRUPT) { - /* exit request from the cpu execution loop */ - ret = env->exception_index; - break; - } else { - /* simulate a real cpu exception. On i386, it can - trigger new exceptions, but we do not handle - double or triple faults yet. */ - RAWEx_ProfileStart(env, STATS_IRQ_HANDLING); - Log(("do_interrupt %d %d %RGv\n", env->exception_index, env->exception_is_int, (RTGCPTR)env->exception_next_eip)); - do_interrupt(env->exception_index, - env->exception_is_int, - env->error_code, - env->exception_next_eip, 0); - /* successfully delivered */ - env->old_exception = -1; - RAWEx_ProfileStop(env, STATS_IRQ_HANDLING); - } - env->exception_index = -1; - } - - next_tb = 0; /* force lookup of first TB */ - for(;;) - { - interrupt_request = env->interrupt_request; -#ifndef VBOX - if (__builtin_expect(interrupt_request, 0)) -#else - if (RT_UNLIKELY(interrupt_request != 0)) -#endif - { - /** @todo: reconcile with what QEMU really does */ - - /* Single instruction exec request, we execute it and return (one way or the other). - The caller will always reschedule after doing this operation! */ - if (interrupt_request & CPU_INTERRUPT_SINGLE_INSTR) - { - /* not in flight are we? (if we are, we trapped) */ - if (!(env->interrupt_request & CPU_INTERRUPT_SINGLE_INSTR_IN_FLIGHT)) - { - ASMAtomicOrS32((int32_t volatile *)&env->interrupt_request, CPU_INTERRUPT_SINGLE_INSTR_IN_FLIGHT); - env->exception_index = EXCP_SINGLE_INSTR; - if (emulate_single_instr(env) == -1) - AssertMsgFailed(("REM: emulate_single_instr failed for EIP=%RGv!!\n", (RTGCPTR)env->eip)); - - /* When we receive an external interrupt during execution of this single - instruction, then we should stay here. We will leave when we're ready - for raw-mode or when interrupted by pending EMT requests. */ - interrupt_request = env->interrupt_request; /* reload this! */ - if ( !(interrupt_request & CPU_INTERRUPT_HARD) - || !(env->eflags & IF_MASK) - || (env->hflags & HF_INHIBIT_IRQ_MASK) - || (env->state & CPU_RAW_HWACC) - ) - { - env->exception_index = ret = EXCP_SINGLE_INSTR; - cpu_loop_exit(); - } - } - /* Clear CPU_INTERRUPT_SINGLE_INSTR and leave CPU_INTERRUPT_SINGLE_INSTR_IN_FLIGHT set. */ - ASMAtomicAndS32((int32_t volatile *)&env->interrupt_request, ~CPU_INTERRUPT_SINGLE_INSTR); - } - - RAWEx_ProfileStart(env, STATS_IRQ_HANDLING); - if ((interrupt_request & CPU_INTERRUPT_SMI) && - !(env->hflags & HF_SMM_MASK)) { - env->interrupt_request &= ~CPU_INTERRUPT_SMI; - do_smm_enter(); - next_tb = 0; - } - else if ((interrupt_request & CPU_INTERRUPT_HARD) && - (env->eflags & IF_MASK) && - !(env->hflags & HF_INHIBIT_IRQ_MASK)) - { - /* if hardware interrupt pending, we execute it */ - int intno; - ASMAtomicAndS32((int32_t volatile *)&env->interrupt_request, ~CPU_INTERRUPT_HARD); - intno = cpu_get_pic_interrupt(env); - if (intno >= 0) - { - Log(("do_interrupt %d\n", intno)); - do_interrupt(intno, 0, 0, 0, 1); - } - /* ensure that no TB jump will be modified as - the program flow was changed */ - next_tb = 0; - } - if (env->interrupt_request & CPU_INTERRUPT_EXITTB) - { - ASMAtomicAndS32((int32_t volatile *)&env->interrupt_request, ~CPU_INTERRUPT_EXITTB); - /* ensure that no TB jump will be modified as - the program flow was changed */ - next_tb = 0; - } - RAWEx_ProfileStop(env, STATS_IRQ_HANDLING); - if (interrupt_request & CPU_INTERRUPT_EXIT) - { - env->exception_index = EXCP_INTERRUPT; - ASMAtomicAndS32((int32_t volatile *)&env->interrupt_request, ~CPU_INTERRUPT_EXIT); - ret = env->exception_index; - cpu_loop_exit(); - } - if (interrupt_request & CPU_INTERRUPT_RC) - { - env->exception_index = EXCP_RC; - ASMAtomicAndS32((int32_t volatile *)&env->interrupt_request, ~CPU_INTERRUPT_RC); - ret = env->exception_index; - cpu_loop_exit(); - } - } - - /* - * Check if we the CPU state allows us to execute the code in raw-mode. - */ - RAWEx_ProfileStart(env, STATS_RAW_CHECK); - if (remR3CanExecuteRaw(env, - env->eip + env->segs[R_CS].base, - env->hflags | (env->eflags & (IOPL_MASK | TF_MASK | VM_MASK)), - &env->exception_index)) - { - RAWEx_ProfileStop(env, STATS_RAW_CHECK); - ret = env->exception_index; - cpu_loop_exit(); - } - RAWEx_ProfileStop(env, STATS_RAW_CHECK); - - RAWEx_ProfileStart(env, STATS_TLB_LOOKUP); - spin_lock(&tb_lock); - tb = tb_find_fast(); - /* Note: we do it here to avoid a gcc bug on Mac OS X when - doing it in tb_find_slow */ - if (tb_invalidated_flag) { - /* as some TB could have been invalidated because - of memory exceptions while generating the code, we - must recompute the hash index here */ - next_tb = 0; - tb_invalidated_flag = 0; - } - - /* see if we can patch the calling TB. When the TB - spans two pages, we cannot safely do a direct - jump. */ - if (next_tb != 0 - && !(tb->cflags & CF_RAW_MODE) - && tb->page_addr[1] == -1) - { - tb_add_jump((TranslationBlock *)(long)(next_tb & ~3), next_tb & 3, tb); - } - spin_unlock(&tb_lock); - RAWEx_ProfileStop(env, STATS_TLB_LOOKUP); - - env->current_tb = tb; - while (env->current_tb) { - tc_ptr = tb->tc_ptr; - /* execute the generated code */ - RAWEx_ProfileStart(env, STATS_QEMU_RUN_EMULATED_CODE); -#if defined(VBOX) && defined(GCC_WITH_BUGGY_REGPARM) - tcg_qemu_tb_exec(tc_ptr, next_tb); -#else - next_tb = tcg_qemu_tb_exec(tc_ptr); -#endif - RAWEx_ProfileStop(env, STATS_QEMU_RUN_EMULATED_CODE); - env->current_tb = NULL; - if ((next_tb & 3) == 2) { - /* Instruction counter expired. */ - int insns_left; - tb = (TranslationBlock *)(long)(next_tb & ~3); - /* Restore PC. */ - CPU_PC_FROM_TB(env, tb); - insns_left = env->icount_decr.u32; - if (env->icount_extra && insns_left >= 0) { - /* Refill decrementer and continue execution. */ - env->icount_extra += insns_left; - if (env->icount_extra > 0xffff) { - insns_left = 0xffff; - } else { - insns_left = env->icount_extra; - } - env->icount_extra -= insns_left; - env->icount_decr.u16.low = insns_left; - } else { - if (insns_left > 0) { - /* Execute remaining instructions. */ - cpu_exec_nocache(insns_left, tb); - } - env->exception_index = EXCP_INTERRUPT; - next_tb = 0; - cpu_loop_exit(); - } - } - } - - /* reset soft MMU for next block (it can currently - only be set by a memory fault) */ -#if defined(TARGET_I386) && !defined(CONFIG_SOFTMMU) - if (env->hflags & HF_SOFTMMU_MASK) { - env->hflags &= ~HF_SOFTMMU_MASK; - /* do not allow linking to another block */ - next_tb = 0; - } -#endif - } /* for(;;) */ - } else { - env_to_regs(); - } -#ifdef VBOX_HIGH_RES_TIMERS_HACK - /* NULL the current_tb here so cpu_interrupt() doesn't do anything - unnecessary (like crashing during emulate single instruction). - Note! Don't use env1->pVM here, the code wouldn't run with - gcc-4.4/amd64 anymore, see #3883. */ - env->current_tb = NULL; - if ( !(env->interrupt_request & ( CPU_INTERRUPT_EXIT | CPU_INTERRUPT_DEBUG | CPU_INTERRUPT_EXTERNAL_EXIT | CPU_INTERRUPT_RC - | CPU_INTERRUPT_SINGLE_INSTR | CPU_INTERRUPT_SINGLE_INSTR_IN_FLIGHT)) - && ( (env->interrupt_request & CPU_INTERRUPT_EXTERNAL_TIMER) - || TMTimerPollBool(env->pVM, env->pVCpu)) ) { - ASMAtomicAndS32((int32_t volatile *)&env->interrupt_request, ~CPU_INTERRUPT_EXTERNAL_TIMER); - remR3ProfileStart(STATS_QEMU_RUN_TIMERS); - TMR3TimerQueuesDo(env->pVM); - remR3ProfileStop(STATS_QEMU_RUN_TIMERS); - } -#endif - } /* for(;;) */ - -#if defined(TARGET_I386) - /* restore flags in standard format */ - env->eflags = env->eflags | cc_table[CC_OP].compute_all() | (DF & DF_MASK); -#else -#error unsupported target CPU -#endif -#include "hostregs_helper.h" - return ret; -} - -#else /* !VBOX */ -int cpu_exec(CPUState *env1) -{ -#define DECLARE_HOST_REGS 1 -#include "hostregs_helper.h" - int ret, interrupt_request; - TranslationBlock *tb; - uint8_t *tc_ptr; - unsigned long next_tb; - - if (cpu_halted(env1) == EXCP_HALTED) - return EXCP_HALTED; - - cpu_single_env = env1; - - /* first we save global registers */ -#define SAVE_HOST_REGS 1 -#include "hostregs_helper.h" - env = env1; - - env_to_regs(); -#if defined(TARGET_I386) - /* put eflags in CPU temporary format */ - CC_SRC = env->eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); - DF = 1 - (2 * ((env->eflags >> 10) & 1)); - CC_OP = CC_OP_EFLAGS; - env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); -#elif defined(TARGET_SPARC) -#elif defined(TARGET_M68K) - env->cc_op = CC_OP_FLAGS; - env->cc_dest = env->sr & 0xf; - env->cc_x = (env->sr >> 4) & 1; -#elif defined(TARGET_ALPHA) -#elif defined(TARGET_ARM) -#elif defined(TARGET_PPC) -#elif defined(TARGET_MIPS) -#elif defined(TARGET_SH4) -#elif defined(TARGET_CRIS) - /* XXXXX */ -#else -#error unsupported target CPU #endif - env->exception_index = -1; - /* prepare setjmp context for exception handling */ - for(;;) { - if (setjmp(env->jmp_env) == 0) { - env->current_tb = NULL; /* if an exception is pending, we execute it here */ if (env->exception_index >= 0) { if (env->exception_index >= EXCP_INTERRUPT) { /* exit request from the cpu execution loop */ ret = env->exception_index; +#ifdef VBOX /* because of the above stuff */ + env->exception_index = -1; +#endif + if (ret == EXCP_DEBUG) + cpu_handle_debug_exception(env); break; - } else if (env->user_mode_only) { + } else { +#if defined(CONFIG_USER_ONLY) /* if user mode only, we simulate a fake exception which will be handled outside the cpu execution loop */ @@ -620,19 +338,29 @@ int cpu_exec(CPUState *env1) #endif ret = env->exception_index; break; - } else { +#else #if defined(TARGET_I386) /* simulate a real cpu exception. On i386, it can trigger new exceptions, but we do not handle double or triple faults yet. */ +# ifdef VBOX + RAWEx_ProfileStart(env, STATS_IRQ_HANDLING); + Log(("do_interrupt: vec=%#x int=%d pc=%04x:%RGv\n", env->exception_index, env->exception_is_int, + env->segs[R_CS].selector, (RTGCPTR)env->exception_next_eip)); +# endif /* VBOX */ do_interrupt(env->exception_index, env->exception_is_int, env->error_code, env->exception_next_eip, 0); /* successfully delivered */ env->old_exception = -1; +# ifdef VBOX + RAWEx_ProfileStop(env, STATS_IRQ_HANDLING); +# endif /* VBOX */ #elif defined(TARGET_PPC) do_interrupt(env); +#elif defined(TARGET_MICROBLAZE) + do_interrupt(env); #elif defined(TARGET_MIPS) do_interrupt(env); #elif defined(TARGET_SPARC) @@ -640,7 +368,7 @@ int cpu_exec(CPUState *env1) #elif defined(TARGET_ARM) do_interrupt(env); #elif defined(TARGET_SH4) - do_interrupt(env); + do_interrupt(env); #elif defined(TARGET_ALPHA) do_interrupt(env); #elif defined(TARGET_CRIS) @@ -648,47 +376,37 @@ int cpu_exec(CPUState *env1) #elif defined(TARGET_M68K) do_interrupt(0); #endif + env->exception_index = -1; +#endif } - env->exception_index = -1; } -#ifdef USE_KQEMU - if (kqemu_is_ok(env) && env->interrupt_request == 0) { - int ret; - env->eflags = env->eflags | cc_table[CC_OP].compute_all() | (DF & DF_MASK); - ret = kqemu_cpu_exec(env); - /* put eflags in CPU temporary format */ - CC_SRC = env->eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); - DF = 1 - (2 * ((env->eflags >> 10) & 1)); - CC_OP = CC_OP_EFLAGS; - env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); - if (ret == 1) { - /* exception */ - longjmp(env->jmp_env, 1); - } else if (ret == 2) { - /* softmmu execution needed */ - } else { - if (env->interrupt_request != 0) { - /* hardware interrupt will be executed just after */ - } else { - /* otherwise, we restart */ - longjmp(env->jmp_env, 1); - } - } + +# ifndef VBOX + if (kvm_enabled()) { + kvm_cpu_exec(env); + longjmp(env->jmp_env, 1); } -#endif +# endif /* !VBOX */ next_tb = 0; /* force lookup of first TB */ for(;;) { interrupt_request = env->interrupt_request; - if (unlikely(interrupt_request) && - likely(!(env->singlestep_enabled & SSTEP_NOIRQ))) { + if (unlikely(interrupt_request)) { + if (unlikely(env->singlestep_enabled & SSTEP_NOIRQ)) { + /* Mask out external interrupts for this step. */ + interrupt_request &= ~(CPU_INTERRUPT_HARD | + CPU_INTERRUPT_FIQ | + CPU_INTERRUPT_SMI | + CPU_INTERRUPT_NMI); + } if (interrupt_request & CPU_INTERRUPT_DEBUG) { env->interrupt_request &= ~CPU_INTERRUPT_DEBUG; env->exception_index = EXCP_DEBUG; cpu_loop_exit(); } #if defined(TARGET_ARM) || defined(TARGET_SPARC) || defined(TARGET_MIPS) || \ - defined(TARGET_PPC) || defined(TARGET_ALPHA) || defined(TARGET_CRIS) + defined(TARGET_PPC) || defined(TARGET_ALPHA) || defined(TARGET_CRIS) || \ + defined(TARGET_MICROBLAZE) if (interrupt_request & CPU_INTERRUPT_HALT) { env->interrupt_request &= ~CPU_INTERRUPT_HALT; env->halted = 1; @@ -697,7 +415,56 @@ int cpu_exec(CPUState *env1) } #endif #if defined(TARGET_I386) - if (env->hflags2 & HF2_GIF_MASK) { +# ifdef VBOX + /* Memory registration may post a tlb flush request, process it ASAP. */ + if (interrupt_request & (CPU_INTERRUPT_EXTERNAL_FLUSH_TLB)) { + tlb_flush(env, true); /* (clears the flush flag) */ + } + + /* Single instruction exec request, we execute it and return (one way or the other). + The caller will always reschedule after doing this operation! */ + if (interrupt_request & CPU_INTERRUPT_SINGLE_INSTR) + { + /* not in flight are we? (if we are, we trapped) */ + if (!(env->interrupt_request & CPU_INTERRUPT_SINGLE_INSTR_IN_FLIGHT)) + { + ASMAtomicOrS32((int32_t volatile *)&env->interrupt_request, CPU_INTERRUPT_SINGLE_INSTR_IN_FLIGHT); + env->exception_index = EXCP_SINGLE_INSTR; + if (emulate_single_instr(env) == -1) + AssertMsgFailed(("REM: emulate_single_instr failed for EIP=%RGv!!\n", (RTGCPTR)env->eip)); + + /* When we receive an external interrupt during execution of this single + instruction, then we should stay here. We will leave when we're ready + for raw-mode or when interrupted by pending EMT requests. */ + interrupt_request = env->interrupt_request; /* reload this! */ + if ( !(interrupt_request & CPU_INTERRUPT_HARD) + || !(env->eflags & IF_MASK) + || (env->hflags & HF_INHIBIT_IRQ_MASK) + || (env->state & CPU_RAW_HWACC) + ) + { + env->exception_index = ret = EXCP_SINGLE_INSTR; + cpu_loop_exit(); + } + } + /* Clear CPU_INTERRUPT_SINGLE_INSTR and leave CPU_INTERRUPT_SINGLE_INSTR_IN_FLIGHT set. */ + ASMAtomicAndS32((int32_t volatile *)&env->interrupt_request, ~CPU_INTERRUPT_SINGLE_INSTR); +# ifdef IEM_VERIFICATION_MODE + env->exception_index = ret = EXCP_SINGLE_INSTR; + cpu_loop_exit(); +# endif + } +# endif /* VBOX */ + +# ifndef VBOX /** @todo reconcile our code with the following... */ + if (interrupt_request & CPU_INTERRUPT_INIT) { + svm_check_intercept(SVM_EXIT_INIT); + do_cpu_init(env); + env->exception_index = EXCP_HALTED; + cpu_loop_exit(); + } else if (interrupt_request & CPU_INTERRUPT_SIPI) { + do_cpu_sipi(env); + } else if (env->hflags2 & HF2_GIF_MASK) { if ((interrupt_request & CPU_INTERRUPT_SMI) && !(env->hflags & HF_SMM_MASK)) { svm_check_intercept(SVM_EXIT_SMI); @@ -710,6 +477,10 @@ int cpu_exec(CPUState *env1) env->hflags2 |= HF2_NMI_MASK; do_interrupt(EXCP02_NMI, 0, 0, 0, 1); next_tb = 0; + } else if (interrupt_request & CPU_INTERRUPT_MCE) { + env->interrupt_request &= ~CPU_INTERRUPT_MCE; + do_interrupt(EXCP12_MCHK, 0, 0, 0, 0); + next_tb = 0; } else if ((interrupt_request & CPU_INTERRUPT_HARD) && (((env->hflags2 & HF2_VINTR_MASK) && (env->hflags2 & HF2_HIF_MASK)) || @@ -720,9 +491,12 @@ int cpu_exec(CPUState *env1) svm_check_intercept(SVM_EXIT_INTR); env->interrupt_request &= ~(CPU_INTERRUPT_HARD | CPU_INTERRUPT_VIRQ); intno = cpu_get_pic_interrupt(env); - if (loglevel & CPU_LOG_TB_IN_ASM) { - fprintf(logfile, "Servicing hardware INT=0x%02x\n", intno); - } + qemu_log_mask(CPU_LOG_TB_IN_ASM, "Servicing hardware INT=0x%02x\n", intno); +#if defined(__sparc__) && !defined(CONFIG_SOLARIS) +#undef env + env = cpu_single_env; +#define env cpu_single_env +#endif do_interrupt(intno, 0, 0, 0, 1); /* ensure that no TB jump will be modified as the program flow was changed */ @@ -734,19 +508,44 @@ int cpu_exec(CPUState *env1) int intno; /* FIXME: this should respect TPR */ svm_check_intercept(SVM_EXIT_VINTR); - env->interrupt_request &= ~CPU_INTERRUPT_VIRQ; intno = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_vector)); - if (loglevel & CPU_LOG_TB_IN_ASM) - fprintf(logfile, "Servicing virtual hardware INT=0x%02x\n", intno); + qemu_log_mask(CPU_LOG_TB_IN_ASM, "Servicing virtual hardware INT=0x%02x\n", intno); do_interrupt(intno, 0, 0, 0, 1); + env->interrupt_request &= ~CPU_INTERRUPT_VIRQ; next_tb = 0; #endif } } +# else /* VBOX */ + RAWEx_ProfileStart(env, STATS_IRQ_HANDLING); + if ((interrupt_request & CPU_INTERRUPT_SMI) && + !(env->hflags & HF_SMM_MASK)) { + env->interrupt_request &= ~CPU_INTERRUPT_SMI; + do_smm_enter(); + next_tb = 0; + } + else if ((interrupt_request & CPU_INTERRUPT_HARD) && + (env->eflags & IF_MASK) && + !(env->hflags & HF_INHIBIT_IRQ_MASK)) + { + /* if hardware interrupt pending, we execute it */ + int intno; + ASMAtomicAndS32((int32_t volatile *)&env->interrupt_request, ~CPU_INTERRUPT_HARD); + intno = cpu_get_pic_interrupt(env); + if (intno >= 0) + { + Log(("do_interrupt %d\n", intno)); + do_interrupt(intno, 0, 0, 0, 1); + } + /* ensure that no TB jump will be modified as + the program flow was changed */ + next_tb = 0; + } +# endif /* VBOX */ #elif defined(TARGET_PPC) #if 0 if ((interrupt_request & CPU_INTERRUPT_RESET)) { - cpu_ppc_reset(env); + cpu_reset(env); } #endif if (interrupt_request & CPU_INTERRUPT_HARD) { @@ -755,6 +554,15 @@ int cpu_exec(CPUState *env1) env->interrupt_request &= ~CPU_INTERRUPT_HARD; next_tb = 0; } +#elif defined(TARGET_MICROBLAZE) + if ((interrupt_request & CPU_INTERRUPT_HARD) + && (env->sregs[SR_MSR] & MSR_IE) + && !(env->sregs[SR_MSR] & (MSR_EIP | MSR_BIP)) + && !(env->iflags & (D_FLAG | IMM_FLAG))) { + env->exception_index = EXCP_IRQ; + do_interrupt(env); + next_tb = 0; + } #elif defined(TARGET_MIPS) if ((interrupt_request & CPU_INTERRUPT_HARD) && (env->CP0_Status & env->CP0_Cause & CP0Ca_IP_mask) && @@ -769,23 +577,20 @@ int cpu_exec(CPUState *env1) next_tb = 0; } #elif defined(TARGET_SPARC) - if ((interrupt_request & CPU_INTERRUPT_HARD) && - (env->psret != 0)) { - int pil = env->interrupt_index & 15; - int type = env->interrupt_index & 0xf0; - - if (((type == TT_EXTINT) && - (pil == 15 || pil > env->psrpil)) || - type != TT_EXTINT) { - env->interrupt_request &= ~CPU_INTERRUPT_HARD; - env->exception_index = env->interrupt_index; - do_interrupt(env); - env->interrupt_index = 0; -#if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY) - cpu_check_irqs(env); -#endif - next_tb = 0; - } + if (interrupt_request & CPU_INTERRUPT_HARD) { + if (cpu_interrupts_enabled(env) && + env->interrupt_index > 0) { + int pil = env->interrupt_index & 0xf; + int type = env->interrupt_index & 0xf0; + + if (((type == TT_EXTINT) && + cpu_pil_allowed(env, pil)) || + type != TT_EXTINT) { + env->exception_index = env->interrupt_index; + do_interrupt(env); + next_tb = 0; + } + } } else if (interrupt_request & CPU_INTERRUPT_TIMER) { //do_interrupt(0, 0, 0, 0, 0); env->interrupt_request &= ~CPU_INTERRUPT_TIMER; @@ -803,7 +608,7 @@ int cpu_exec(CPUState *env1) jump normally, then does the exception return when the CPU tries to execute code at the magic address. This will cause the magic PC value to be pushed to - the stack if an interrupt occurred at the wrong time. + the stack if an interrupt occured at the wrong time. We avoid this by disabling interrupts when pc contains a magic address. */ if (interrupt_request & CPU_INTERRUPT_HARD @@ -825,7 +630,8 @@ int cpu_exec(CPUState *env1) } #elif defined(TARGET_CRIS) if (interrupt_request & CPU_INTERRUPT_HARD - && (env->pregs[PR_CCS] & I_FLAG)) { + && (env->pregs[PR_CCS] & I_FLAG) + && !env->locked_irq) { env->exception_index = EXCP_IRQ; do_interrupt(env); next_tb = 0; @@ -850,53 +656,76 @@ int cpu_exec(CPUState *env1) next_tb = 0; } #endif - /* Don't use the cached interrupt_request value, + /* Don't use the cached interupt_request value, do_interrupt may have updated the EXITTB flag. */ if (env->interrupt_request & CPU_INTERRUPT_EXITTB) { +#ifndef VBOX env->interrupt_request &= ~CPU_INTERRUPT_EXITTB; +#else /* VBOX */ + ASMAtomicAndS32((int32_t volatile *)&env->interrupt_request, ~CPU_INTERRUPT_EXITTB); +#endif /* VBOX */ /* ensure that no TB jump will be modified as the program flow was changed */ next_tb = 0; } - if (interrupt_request & CPU_INTERRUPT_EXIT) { - env->interrupt_request &= ~CPU_INTERRUPT_EXIT; - env->exception_index = EXCP_INTERRUPT; +#ifdef VBOX + RAWEx_ProfileStop(env, STATS_IRQ_HANDLING); + if (interrupt_request & CPU_INTERRUPT_RC) { + env->exception_index = EXCP_RC; + ASMAtomicAndS32((int32_t volatile *)&env->interrupt_request, ~CPU_INTERRUPT_RC); + ret = env->exception_index; cpu_loop_exit(); } + if (interrupt_request & (CPU_INTERRUPT_EXTERNAL_EXIT)) { + ASMAtomicAndS32((int32_t volatile *)&env->interrupt_request, ~(CPU_INTERRUPT_EXTERNAL_EXIT)); + env->exit_request = 1; + } +#endif + } + if (unlikely(env->exit_request)) { + env->exit_request = 0; + env->exception_index = EXCP_INTERRUPT; + cpu_loop_exit(); } -#ifdef DEBUG_EXEC - if ((loglevel & CPU_LOG_TB_CPU)) { + +#ifdef VBOX + /* + * Check if we the CPU state allows us to execute the code in raw-mode. + */ + RAWEx_ProfileStart(env, STATS_RAW_CHECK); + if (remR3CanExecuteRaw(env, + env->eip + env->segs[R_CS].base, + env->hflags | (env->eflags & (IOPL_MASK | TF_MASK | VM_MASK)), + &env->exception_index)) + { + RAWEx_ProfileStop(env, STATS_RAW_CHECK); + ret = env->exception_index; + cpu_loop_exit(); + } + RAWEx_ProfileStop(env, STATS_RAW_CHECK); +#endif /* VBOX */ + +#if defined(DEBUG_DISAS) || defined(CONFIG_DEBUG_EXEC) + if (qemu_loglevel_mask(CPU_LOG_TB_CPU)) { /* restore flags in standard format */ - regs_to_env(); #if defined(TARGET_I386) - env->eflags = env->eflags | cc_table[CC_OP].compute_all() | (DF & DF_MASK); - cpu_dump_state(env, logfile, fprintf, X86_DUMP_CCOP); + env->eflags = env->eflags | helper_cc_compute_all(CC_OP) | (DF & DF_MASK); + log_cpu_state(env, X86_DUMP_CCOP); env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); -#elif defined(TARGET_ARM) - cpu_dump_state(env, logfile, fprintf, 0); -#elif defined(TARGET_SPARC) - cpu_dump_state(env, logfile, fprintf, 0); -#elif defined(TARGET_PPC) - cpu_dump_state(env, logfile, fprintf, 0); #elif defined(TARGET_M68K) 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); - cpu_dump_state(env, logfile, fprintf, 0); -#elif defined(TARGET_MIPS) - cpu_dump_state(env, logfile, fprintf, 0); -#elif defined(TARGET_SH4) - cpu_dump_state(env, logfile, fprintf, 0); -#elif defined(TARGET_ALPHA) - cpu_dump_state(env, logfile, fprintf, 0); -#elif defined(TARGET_CRIS) - cpu_dump_state(env, logfile, fprintf, 0); + log_cpu_state(env, 0); #else -#error unsupported target CPU + log_cpu_state(env, 0); #endif } -#endif +#endif /* DEBUG_DISAS || CONFIG_DEBUG_EXEC */ +#ifdef VBOX + RAWEx_ProfileStart(env, STATS_TLB_LOOKUP); +#endif /*VBOX*/ spin_lock(&tb_lock); tb = tb_find_fast(); /* Note: we do it here to avoid a gcc bug on Mac OS X when @@ -908,43 +737,57 @@ int cpu_exec(CPUState *env1) next_tb = 0; tb_invalidated_flag = 0; } -#ifdef DEBUG_EXEC - if ((loglevel & CPU_LOG_EXEC)) { - fprintf(logfile, "Trace 0x%08lx [" TARGET_FMT_lx "] %s\n", - (long)tb->tc_ptr, tb->pc, - lookup_symbol(tb->pc)); - } +#ifdef CONFIG_DEBUG_EXEC + qemu_log_mask(CPU_LOG_EXEC, "Trace 0x%08lx [" TARGET_FMT_lx "] %s\n", + (long)tb->tc_ptr, tb->pc, + lookup_symbol(tb->pc)); #endif /* see if we can patch the calling TB. When the TB spans two pages, we cannot safely do a direct jump. */ - { - if (next_tb != 0 && -#ifdef USE_KQEMU - (env->kqemu_enabled != 2) && -#endif - tb->page_addr[1] == -1) { +#ifndef VBOX + if (next_tb != 0 && tb->page_addr[1] == -1) { +#else /* VBOX */ + if (next_tb != 0 && !(tb->cflags & CF_RAW_MODE) && tb->page_addr[1] == -1) { +#endif /* VBOX */ tb_add_jump((TranslationBlock *)(next_tb & ~3), next_tb & 3, tb); } - } spin_unlock(&tb_lock); +#ifdef VBOX + RAWEx_ProfileStop(env, STATS_TLB_LOOKUP); +#endif + + /* cpu_interrupt might be called while translating the + TB, but before it is linked into a potentially + infinite loop and becomes env->current_tb. Avoid + starting execution if there is a pending interrupt. */ env->current_tb = tb; - while (env->current_tb) { + barrier(); + if (likely(!env->exit_request)) { tc_ptr = tb->tc_ptr; /* execute the generated code */ -#if defined(__sparc__) && !defined(HOST_SOLARIS) +#ifdef VBOX + RAWEx_ProfileStart(env, STATS_QEMU_RUN_EMULATED_CODE); +#endif +#if defined(__sparc__) && !defined(CONFIG_SOLARIS) #undef env env = cpu_single_env; #define env cpu_single_env #endif +#if defined(VBOX) && defined(GCC_WITH_BUGGY_REGPARM) + tcg_qemu_tb_exec(tc_ptr, next_tb); +#else next_tb = tcg_qemu_tb_exec(tc_ptr); - env->current_tb = NULL; +#endif +#ifdef VBOX + RAWEx_ProfileStop(env, STATS_QEMU_RUN_EMULATED_CODE); +#endif if ((next_tb & 3) == 2) { /* Instruction counter expired. */ int insns_left; tb = (TranslationBlock *)(long)(next_tb & ~3); /* Restore PC. */ - CPU_PC_FROM_TB(env, tb); + cpu_pc_from_tb(env, tb); insns_left = env->icount_decr.u32; if (env->icount_extra && insns_left >= 0) { /* Refill decrementer and continue execution. */ @@ -967,25 +810,33 @@ int cpu_exec(CPUState *env1) } } } + env->current_tb = NULL; /* reset soft MMU for next block (it can currently only be set by a memory fault) */ -#if defined(USE_KQEMU) -#define MIN_CYCLE_BEFORE_SWITCH (100 * 1000) - if (kqemu_is_ok(env) && - (cpu_get_time_fast() - env->last_io_time) >= MIN_CYCLE_BEFORE_SWITCH) { - cpu_loop_exit(); - } -#endif } /* for(;;) */ - } else { - env_to_regs(); } +#ifdef VBOX_HIGH_RES_TIMERS_HACK + /* NULL the current_tb here so cpu_interrupt() doesn't do anything + unnecessary (like crashing during emulate single instruction). + Note! Don't use env1->pVM here, the code wouldn't run with + gcc-4.4/amd64 anymore, see #3883. */ + env->current_tb = NULL; + if ( !(env->interrupt_request & ( CPU_INTERRUPT_DEBUG | CPU_INTERRUPT_EXTERNAL_EXIT | CPU_INTERRUPT_RC + | CPU_INTERRUPT_SINGLE_INSTR | CPU_INTERRUPT_SINGLE_INSTR_IN_FLIGHT)) + && ( (env->interrupt_request & CPU_INTERRUPT_EXTERNAL_TIMER) + || TMTimerPollBool(env->pVM, env->pVCpu)) ) { + ASMAtomicAndS32((int32_t volatile *)&env->interrupt_request, ~CPU_INTERRUPT_EXTERNAL_TIMER); + remR3ProfileStart(STATS_QEMU_RUN_TIMERS); + TMR3TimerQueuesDo(env->pVM); + remR3ProfileStop(STATS_QEMU_RUN_TIMERS); + } +#endif } /* for(;;) */ #if defined(TARGET_I386) /* restore flags in standard format */ - env->eflags = env->eflags | cc_table[CC_OP].compute_all() | (DF & DF_MASK); + env->eflags = env->eflags | helper_cc_compute_all(CC_OP) | (DF & DF_MASK); #elif defined(TARGET_ARM) /* XXX: Save/restore host fpu exception state?. */ #elif defined(TARGET_SPARC) @@ -995,23 +846,27 @@ int cpu_exec(CPUState *env1) env->cc_op = CC_OP_FLAGS; env->sr = (env->sr & 0xffe0) | env->cc_dest | (env->cc_x << 4); +#elif defined(TARGET_MICROBLAZE) #elif defined(TARGET_MIPS) #elif defined(TARGET_SH4) #elif defined(TARGET_ALPHA) #elif defined(TARGET_CRIS) +#elif defined(TARGET_S390X) /* XXXXX */ #else #error unsupported target CPU #endif /* restore global registers */ -#include "hostregs_helper.h" + barrier(); + env = (void *) saved_env_reg; +# ifndef VBOX /* we might be using elsewhere, we only have one. */ /* fail safe : never use cpu_single_env outside cpu_exec() */ cpu_single_env = NULL; +# endif return ret; } -#endif /* !VBOX */ /* must only be called from the generated code as an exception can be generated */ @@ -1039,31 +894,31 @@ void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector) cpu_x86_load_seg_cache(env, seg_reg, selector, (selector << 4), 0xffff, 0); } else { - load_seg(seg_reg, selector); + helper_load_seg(seg_reg, selector); } env = saved_env; } -void cpu_x86_fsave(CPUX86State *s, uint8_t *ptr, int data32) +void cpu_x86_fsave(CPUX86State *s, target_ulong ptr, int data32) { CPUX86State *saved_env; saved_env = env; env = s; - helper_fsave((target_ulong)ptr, data32); + helper_fsave(ptr, data32); env = saved_env; } -void cpu_x86_frstor(CPUX86State *s, uint8_t *ptr, int data32) +void cpu_x86_frstor(CPUX86State *s, target_ulong ptr, int data32) { CPUX86State *saved_env; saved_env = env; env = s; - helper_frstor((target_ulong)ptr, data32); + helper_frstor(ptr, data32); env = saved_env; } @@ -1073,6 +928,10 @@ void cpu_x86_frstor(CPUX86State *s, uint8_t *ptr, int data32) #if !defined(CONFIG_SOFTMMU) #if defined(TARGET_I386) +#define EXCEPTION_ACTION raise_exception_err(env->exception_index, env->error_code) +#else +#define EXCEPTION_ACTION cpu_loop_exit() +#endif /* 'pc' is the host PC at which the exception was raised. 'address' is the effective address of the memory exception. 'is_write' is 1 if a @@ -1097,57 +956,7 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, } /* see if it is an MMU fault */ - ret = cpu_x86_handle_mmu_fault(env, address, is_write, - ((env->hflags & HF_CPL_MASK) == 3), 0); - if (ret < 0) - return 0; /* not an MMU fault */ - if (ret == 0) - return 1; /* the MMU fault was handled without causing real CPU fault */ - /* now we have a real cpu fault */ - tb = tb_find_pc(pc); - if (tb) { - /* the PC is inside the translated code. It means that we have - a virtual CPU fault */ - cpu_restore_state(tb, env, pc, puc); - } - if (ret == 1) { -#if 0 - printf("PF exception: EIP=0x%RGv CR2=0x%RGv error=0x%x\n", - env->eip, env->cr[2], env->error_code); -#endif - /* we restore the process signal mask as the sigreturn should - do it (XXX: use sigsetjmp) */ - sigprocmask(SIG_SETMASK, old_set, NULL); - raise_exception_err(env->exception_index, env->error_code); - } else { - /* activate soft MMU for this block */ - env->hflags |= HF_SOFTMMU_MASK; - cpu_resume_from_signal(env, puc); - } - /* never comes here */ - return 1; -} - -#elif defined(TARGET_ARM) -static inline int handle_cpu_signal(unsigned long pc, unsigned long address, - int is_write, sigset_t *old_set, - void *puc) -{ - TranslationBlock *tb; - int ret; - - if (cpu_single_env) - env = cpu_single_env; /* XXX: find a correct solution for multithread */ -#if defined(DEBUG_SIGNAL) - printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n", - pc, address, is_write, *(unsigned long *)old_set); -#endif - /* XXX: locking issue */ - if (is_write && page_unprotect(h2g(address), pc, puc)) { - return 1; - } - /* see if it is an MMU fault */ - ret = cpu_arm_handle_mmu_fault(env, address, is_write, 1, 0); + ret = cpu_handle_mmu_fault(env, address, is_write, MMU_USER_IDX, 0); if (ret < 0) return 0; /* not an MMU fault */ if (ret == 0) @@ -1159,234 +968,16 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, a virtual CPU fault */ cpu_restore_state(tb, env, pc, puc); } - /* we restore the process signal mask as the sigreturn should - do it (XXX: use sigsetjmp) */ - sigprocmask(SIG_SETMASK, old_set, NULL); - cpu_loop_exit(); -} -#elif defined(TARGET_SPARC) -static inline int handle_cpu_signal(unsigned long pc, unsigned long address, - int is_write, sigset_t *old_set, - void *puc) -{ - TranslationBlock *tb; - int ret; - if (cpu_single_env) - env = cpu_single_env; /* XXX: find a correct solution for multithread */ -#if defined(DEBUG_SIGNAL) - printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n", - pc, address, is_write, *(unsigned long *)old_set); -#endif - /* XXX: locking issue */ - if (is_write && page_unprotect(h2g(address), pc, puc)) { - return 1; - } - /* see if it is an MMU fault */ - ret = cpu_sparc_handle_mmu_fault(env, address, is_write, 1, 0); - if (ret < 0) - return 0; /* not an MMU fault */ - if (ret == 0) - return 1; /* the MMU fault was handled without causing real CPU fault */ - /* now we have a real cpu fault */ - tb = tb_find_pc(pc); - if (tb) { - /* the PC is inside the translated code. It means that we have - a virtual CPU fault */ - cpu_restore_state(tb, env, pc, puc); - } /* we restore the process signal mask as the sigreturn should do it (XXX: use sigsetjmp) */ sigprocmask(SIG_SETMASK, old_set, NULL); - cpu_loop_exit(); -} -#elif defined (TARGET_PPC) -static inline int handle_cpu_signal(unsigned long pc, unsigned long address, - int is_write, sigset_t *old_set, - void *puc) -{ - TranslationBlock *tb; - int ret; - - if (cpu_single_env) - env = cpu_single_env; /* XXX: find a correct solution for multithread */ -#if defined(DEBUG_SIGNAL) - printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n", - pc, address, is_write, *(unsigned long *)old_set); -#endif - /* XXX: locking issue */ - if (is_write && page_unprotect(h2g(address), pc, puc)) { - return 1; - } + EXCEPTION_ACTION; - /* see if it is an MMU fault */ - ret = cpu_ppc_handle_mmu_fault(env, address, is_write, msr_pr, 0); - if (ret < 0) - return 0; /* not an MMU fault */ - if (ret == 0) - return 1; /* the MMU fault was handled without causing real CPU fault */ - - /* now we have a real cpu fault */ - tb = tb_find_pc(pc); - if (tb) { - /* the PC is inside the translated code. It means that we have - a virtual CPU fault */ - cpu_restore_state(tb, env, pc, puc); - } - if (ret == 1) { -#if 0 - printf("PF exception: NIP=0x%08x error=0x%x %p\n", - env->nip, env->error_code, tb); -#endif - /* we restore the process signal mask as the sigreturn should - do it (XXX: use sigsetjmp) */ - sigprocmask(SIG_SETMASK, old_set, NULL); - do_raise_exception_err(env->exception_index, env->error_code); - } else { - /* activate soft MMU for this block */ - cpu_resume_from_signal(env, puc); - } /* never comes here */ return 1; } -#elif defined(TARGET_M68K) -static inline int handle_cpu_signal(unsigned long pc, unsigned long address, - int is_write, sigset_t *old_set, - void *puc) -{ - TranslationBlock *tb; - int ret; - - if (cpu_single_env) - env = cpu_single_env; /* XXX: find a correct solution for multithread */ -#if defined(DEBUG_SIGNAL) - printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n", - pc, address, is_write, *(unsigned long *)old_set); -#endif - /* XXX: locking issue */ - if (is_write && page_unprotect(address, pc, puc)) { - return 1; - } - /* see if it is an MMU fault */ - ret = cpu_m68k_handle_mmu_fault(env, address, is_write, 1, 0); - if (ret < 0) - return 0; /* not an MMU fault */ - if (ret == 0) - return 1; /* the MMU fault was handled without causing real CPU fault */ - /* now we have a real cpu fault */ - tb = tb_find_pc(pc); - if (tb) { - /* the PC is inside the translated code. It means that we have - a virtual CPU fault */ - cpu_restore_state(tb, env, pc, puc); - } - /* we restore the process signal mask as the sigreturn should - do it (XXX: use sigsetjmp) */ - sigprocmask(SIG_SETMASK, old_set, NULL); - cpu_loop_exit(); - /* never comes here */ - return 1; -} - -#elif defined (TARGET_MIPS) -static inline int handle_cpu_signal(unsigned long pc, unsigned long address, - int is_write, sigset_t *old_set, - void *puc) -{ - TranslationBlock *tb; - int ret; - - if (cpu_single_env) - env = cpu_single_env; /* XXX: find a correct solution for multithread */ -#if defined(DEBUG_SIGNAL) - printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n", - pc, address, is_write, *(unsigned long *)old_set); -#endif - /* XXX: locking issue */ - if (is_write && page_unprotect(h2g(address), pc, puc)) { - return 1; - } - - /* see if it is an MMU fault */ - ret = cpu_mips_handle_mmu_fault(env, address, is_write, 1, 0); - if (ret < 0) - return 0; /* not an MMU fault */ - if (ret == 0) - return 1; /* the MMU fault was handled without causing real CPU fault */ - - /* now we have a real cpu fault */ - tb = tb_find_pc(pc); - if (tb) { - /* the PC is inside the translated code. It means that we have - a virtual CPU fault */ - cpu_restore_state(tb, env, pc, puc); - } - if (ret == 1) { -#if 0 - printf("PF exception: NIP=0x%08x error=0x%x %p\n", - env->nip, env->error_code, tb); -#endif - /* we restore the process signal mask as the sigreturn should - do it (XXX: use sigsetjmp) */ - sigprocmask(SIG_SETMASK, old_set, NULL); - do_raise_exception_err(env->exception_index, env->error_code); - } else { - /* activate soft MMU for this block */ - cpu_resume_from_signal(env, puc); - } - /* never comes here */ - return 1; -} - -#elif defined (TARGET_SH4) -static inline int handle_cpu_signal(unsigned long pc, unsigned long address, - int is_write, sigset_t *old_set, - void *puc) -{ - TranslationBlock *tb; - int ret; - - if (cpu_single_env) - env = cpu_single_env; /* XXX: find a correct solution for multithread */ -#if defined(DEBUG_SIGNAL) - printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n", - pc, address, is_write, *(unsigned long *)old_set); -#endif - /* XXX: locking issue */ - if (is_write && page_unprotect(h2g(address), pc, puc)) { - return 1; - } - - /* see if it is an MMU fault */ - ret = cpu_sh4_handle_mmu_fault(env, address, is_write, 1, 0); - if (ret < 0) - return 0; /* not an MMU fault */ - if (ret == 0) - return 1; /* the MMU fault was handled without causing real CPU fault */ - - /* now we have a real cpu fault */ - tb = tb_find_pc(pc); - if (tb) { - /* the PC is inside the translated code. It means that we have - a virtual CPU fault */ - cpu_restore_state(tb, env, pc, puc); - } -#if 0 - printf("PF exception: NIP=0x%08x error=0x%x %p\n", - env->nip, env->error_code, tb); -#endif - /* we restore the process signal mask as the sigreturn should - do it (XXX: use sigsetjmp) */ - sigprocmask(SIG_SETMASK, old_set, NULL); - cpu_loop_exit(); - /* never comes here */ - return 1; -} -#else -#error unsupported target CPU -#endif - #if defined(__i386__) #if defined(__APPLE__) @@ -1395,17 +986,44 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, # define EIP_sig(context) (*((unsigned long*)&(context)->uc_mcontext->ss.eip)) # define TRAP_sig(context) ((context)->uc_mcontext->es.trapno) # define ERROR_sig(context) ((context)->uc_mcontext->es.err) +# define MASK_sig(context) ((context)->uc_sigmask) +#elif defined (__NetBSD__) +# include <ucontext.h> + +# define EIP_sig(context) ((context)->uc_mcontext.__gregs[_REG_EIP]) +# define TRAP_sig(context) ((context)->uc_mcontext.__gregs[_REG_TRAPNO]) +# define ERROR_sig(context) ((context)->uc_mcontext.__gregs[_REG_ERR]) +# define MASK_sig(context) ((context)->uc_sigmask) +#elif defined (__FreeBSD__) || defined(__DragonFly__) +# include <ucontext.h> + +# define EIP_sig(context) (*((unsigned long*)&(context)->uc_mcontext.mc_eip)) +# define TRAP_sig(context) ((context)->uc_mcontext.mc_trapno) +# define ERROR_sig(context) ((context)->uc_mcontext.mc_err) +# define MASK_sig(context) ((context)->uc_sigmask) +#elif defined(__OpenBSD__) +# define EIP_sig(context) ((context)->sc_eip) +# define TRAP_sig(context) ((context)->sc_trapno) +# define ERROR_sig(context) ((context)->sc_err) +# define MASK_sig(context) ((context)->sc_mask) #else # define EIP_sig(context) ((context)->uc_mcontext.gregs[REG_EIP]) # define TRAP_sig(context) ((context)->uc_mcontext.gregs[REG_TRAPNO]) # define ERROR_sig(context) ((context)->uc_mcontext.gregs[REG_ERR]) +# define MASK_sig(context) ((context)->uc_sigmask) #endif int cpu_signal_handler(int host_signum, void *pinfo, void *puc) { siginfo_t *info = pinfo; +#if defined(__NetBSD__) || defined (__FreeBSD__) || defined(__DragonFly__) + ucontext_t *uc = puc; +#elif defined(__OpenBSD__) + struct sigcontext *uc = puc; +#else struct ucontext *uc = puc; +#endif unsigned long pc; int trapno; @@ -1415,38 +1033,61 @@ int cpu_signal_handler(int host_signum, void *pinfo, #define REG_ERR ERR #define REG_TRAPNO TRAPNO #endif - pc = uc->uc_mcontext.gregs[REG_EIP]; - trapno = uc->uc_mcontext.gregs[REG_TRAPNO]; -#if defined(TARGET_I386) && defined(USE_CODE_COPY) - if (trapno == 0x00 || trapno == 0x05) { - /* send division by zero or bound exception */ - cpu_send_trap(pc, trapno, uc); - return 1; - } else -#endif - return handle_cpu_signal(pc, (unsigned long)info->si_addr, - trapno == 0xe ? - (uc->uc_mcontext.gregs[REG_ERR] >> 1) & 1 : 0, - &uc->uc_sigmask, puc); + pc = EIP_sig(uc); + trapno = TRAP_sig(uc); + return handle_cpu_signal(pc, (unsigned long)info->si_addr, + trapno == 0xe ? + (ERROR_sig(uc) >> 1) & 1 : 0, + &MASK_sig(uc), puc); } #elif defined(__x86_64__) +#ifdef __NetBSD__ +#define PC_sig(context) _UC_MACHINE_PC(context) +#define TRAP_sig(context) ((context)->uc_mcontext.__gregs[_REG_TRAPNO]) +#define ERROR_sig(context) ((context)->uc_mcontext.__gregs[_REG_ERR]) +#define MASK_sig(context) ((context)->uc_sigmask) +#elif defined(__OpenBSD__) +#define PC_sig(context) ((context)->sc_rip) +#define TRAP_sig(context) ((context)->sc_trapno) +#define ERROR_sig(context) ((context)->sc_err) +#define MASK_sig(context) ((context)->sc_mask) +#elif defined (__FreeBSD__) || defined(__DragonFly__) +#include <ucontext.h> + +#define PC_sig(context) (*((unsigned long*)&(context)->uc_mcontext.mc_rip)) +#define TRAP_sig(context) ((context)->uc_mcontext.mc_trapno) +#define ERROR_sig(context) ((context)->uc_mcontext.mc_err) +#define MASK_sig(context) ((context)->uc_sigmask) +#else +#define PC_sig(context) ((context)->uc_mcontext.gregs[REG_RIP]) +#define TRAP_sig(context) ((context)->uc_mcontext.gregs[REG_TRAPNO]) +#define ERROR_sig(context) ((context)->uc_mcontext.gregs[REG_ERR]) +#define MASK_sig(context) ((context)->uc_sigmask) +#endif + int cpu_signal_handler(int host_signum, void *pinfo, void *puc) { siginfo_t *info = pinfo; - struct ucontext *uc = puc; unsigned long pc; +#if defined(__NetBSD__) || defined (__FreeBSD__) || defined(__DragonFly__) + ucontext_t *uc = puc; +#elif defined(__OpenBSD__) + struct sigcontext *uc = puc; +#else + struct ucontext *uc = puc; +#endif - pc = uc->uc_mcontext.gregs[REG_RIP]; + pc = PC_sig(uc); return handle_cpu_signal(pc, (unsigned long)info->si_addr, - uc->uc_mcontext.gregs[REG_TRAPNO] == 0xe ? - (uc->uc_mcontext.gregs[REG_ERR] >> 1) & 1 : 0, - &uc->uc_sigmask, puc); + TRAP_sig(uc) == 0xe ? + (ERROR_sig(uc) >> 1) & 1 : 0, + &MASK_sig(uc), puc); } -#elif defined(__powerpc__) +#elif defined(_ARCH_PPC) /*********************************************************************** * signal context platform-specific definitions @@ -1472,6 +1113,20 @@ int cpu_signal_handler(int host_signum, void *pinfo, # define TRAP_sig(context) REG_sig(trap, context) #endif /* linux */ +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#include <ucontext.h> +# define IAR_sig(context) ((context)->uc_mcontext.mc_srr0) +# define MSR_sig(context) ((context)->uc_mcontext.mc_srr1) +# define CTR_sig(context) ((context)->uc_mcontext.mc_ctr) +# define XER_sig(context) ((context)->uc_mcontext.mc_xer) +# define LR_sig(context) ((context)->uc_mcontext.mc_lr) +# define CR_sig(context) ((context)->uc_mcontext.mc_cr) +/* Exception Registers access */ +# define DAR_sig(context) ((context)->uc_mcontext.mc_dar) +# define DSISR_sig(context) ((context)->uc_mcontext.mc_dsisr) +# define TRAP_sig(context) ((context)->uc_mcontext.mc_exc) +#endif /* __FreeBSD__|| __FreeBSD_kernel__ */ + #ifdef __APPLE__ # include <sys/ucontext.h> typedef struct ucontext SIGCONTEXT; @@ -1501,7 +1156,11 @@ int cpu_signal_handler(int host_signum, void *pinfo, void *puc) { siginfo_t *info = pinfo; +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + ucontext_t *uc = puc; +#else struct ucontext *uc = puc; +#endif unsigned long pc; int is_write; @@ -1555,26 +1214,49 @@ int cpu_signal_handler(int host_signum, void *pinfo, void *puc) { siginfo_t *info = pinfo; - uint32_t *regs = (uint32_t *)(info + 1); - void *sigmask = (regs + 20); - unsigned long pc; int is_write; uint32_t insn; - +#if !defined(__arch64__) || defined(CONFIG_SOLARIS) + uint32_t *regs = (uint32_t *)(info + 1); + void *sigmask = (regs + 20); /* XXX: is there a standard glibc define ? */ - pc = regs[1]; + unsigned long pc = regs[1]; +#else +#ifdef __linux__ + struct sigcontext *sc = puc; + unsigned long pc = sc->sigc_regs.tpc; + void *sigmask = (void *)sc->sigc_mask; +#elif defined(__OpenBSD__) + struct sigcontext *uc = puc; + unsigned long pc = uc->sc_pc; + void *sigmask = (void *)(long)uc->sc_mask; +#endif +#endif + /* XXX: need kernel patch to get write flag faster */ is_write = 0; insn = *(uint32_t *)pc; if ((insn >> 30) == 3) { switch((insn >> 19) & 0x3f) { case 0x05: // stb + case 0x15: // stba case 0x06: // sth + case 0x16: // stha case 0x04: // st + case 0x14: // sta case 0x07: // std + case 0x17: // stda + case 0x0e: // stx + case 0x1e: // stxa case 0x24: // stf + case 0x34: // stfa case 0x27: // stdf + case 0x37: // stdfa + case 0x26: // stqf + case 0x36: // stqfa case 0x25: // stfsr + case 0x3c: // casa + case 0x3e: // casxa is_write = 1; break; } @@ -1593,7 +1275,11 @@ int cpu_signal_handler(int host_signum, void *pinfo, unsigned long pc; int is_write; +#if (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3)) pc = uc->uc_mcontext.gregs[R15]; +#else + pc = uc->uc_mcontext.arm_pc; +#endif /* XXX: compute is_write */ is_write = 0; return handle_cpu_signal(pc, (unsigned long)info->si_addr, @@ -1650,7 +1336,7 @@ int cpu_signal_handler(int host_signum, void *pinfo, void *puc) } return handle_cpu_signal(ip, (unsigned long)info->si_addr, is_write, - &uc->uc_sigmask, puc); + (sigset_t *)&uc->uc_sigmask, puc); } #elif defined(__s390__) @@ -1661,14 +1347,107 @@ int cpu_signal_handler(int host_signum, void *pinfo, siginfo_t *info = pinfo; struct ucontext *uc = puc; unsigned long pc; - int is_write; + uint16_t *pinsn; + int is_write = 0; pc = uc->uc_mcontext.psw.addr; + + /* ??? On linux, the non-rt signal handler has 4 (!) arguments instead + of the normal 2 arguments. The 3rd argument contains the "int_code" + from the hardware which does in fact contain the is_write value. + The rt signal handler, as far as I can tell, does not give this value + at all. Not that we could get to it from here even if it were. */ + /* ??? This is not even close to complete, since it ignores all + of the read-modify-write instructions. */ + pinsn = (uint16_t *)pc; + switch (pinsn[0] >> 8) { + case 0x50: /* ST */ + case 0x42: /* STC */ + case 0x40: /* STH */ + is_write = 1; + break; + case 0xc4: /* RIL format insns */ + switch (pinsn[0] & 0xf) { + case 0xf: /* STRL */ + case 0xb: /* STGRL */ + case 0x7: /* STHRL */ + is_write = 1; + } + break; + case 0xe3: /* RXY format insns */ + switch (pinsn[2] & 0xff) { + case 0x50: /* STY */ + case 0x24: /* STG */ + case 0x72: /* STCY */ + case 0x70: /* STHY */ + case 0x8e: /* STPQ */ + case 0x3f: /* STRVH */ + case 0x3e: /* STRV */ + case 0x2f: /* STRVG */ + is_write = 1; + } + break; + } + return handle_cpu_signal(pc, (unsigned long)info->si_addr, + is_write, &uc->uc_sigmask, puc); +} + +#elif defined(__mips__) + +int cpu_signal_handler(int host_signum, void *pinfo, + void *puc) +{ + siginfo_t *info = pinfo; + struct ucontext *uc = puc; + greg_t pc = uc->uc_mcontext.pc; + int is_write; + /* XXX: compute is_write */ is_write = 0; return handle_cpu_signal(pc, (unsigned long)info->si_addr, - is_write, - &uc->uc_sigmask, puc); + is_write, &uc->uc_sigmask, puc); +} + +#elif defined(__hppa__) + +int cpu_signal_handler(int host_signum, void *pinfo, + void *puc) +{ + struct siginfo *info = pinfo; + struct ucontext *uc = puc; + unsigned long pc = uc->uc_mcontext.sc_iaoq[0]; + uint32_t insn = *(uint32_t *)pc; + int is_write = 0; + + /* XXX: need kernel patch to get write flag faster. */ + switch (insn >> 26) { + case 0x1a: /* STW */ + case 0x19: /* STH */ + case 0x18: /* STB */ + case 0x1b: /* STWM */ + is_write = 1; + break; + + case 0x09: /* CSTWX, FSTWX, FSTWS */ + case 0x0b: /* CSTDX, FSTDX, FSTDS */ + /* Distinguish from coprocessor load ... */ + is_write = (insn >> 9) & 1; + break; + + case 0x03: + switch ((insn >> 6) & 15) { + case 0xa: /* STWS */ + case 0x9: /* STHS */ + case 0x8: /* STBS */ + case 0xe: /* STWAS */ + case 0xc: /* STBYS */ + is_write = 1; + } + break; + } + + return handle_cpu_signal(pc, (unsigned long)info->si_addr, + is_write, &uc->uc_sigmask, puc); } #else |