X-Git-Url: http://vcs.maemo.org/git/?a=blobdiff_plain;f=cpu-exec.c;h=8734337d6744fa78b69d17c82d5b6bd55d9ac95f;hb=HEAD;hp=d4a3dccbdcafc3b7250ccc534a3e6ad8b364f716;hpb=5d1d98ec4d4ef97d708e26674169ed3d1cae6413;p=qemu diff --git a/cpu-exec.c b/cpu-exec.c index d4a3dcc..8734337 100644 --- a/cpu-exec.c +++ b/cpu-exec.c @@ -15,11 +15,13 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA */ #include "config.h" #include "exec.h" #include "disas.h" +#include "tcg.h" +#include "kvm.h" #if !defined(CONFIG_SOFTMMU) #undef EAX @@ -32,59 +34,26 @@ #undef EDI #undef EIP #include +#ifdef __linux__ #include #endif - -int tb_invalidated_flag; - -//#define DEBUG_EXEC -//#define DEBUG_SIGNAL - -#define SAVE_GLOBALS() -#define RESTORE_GLOBALS() +#endif #if defined(__sparc__) && !defined(HOST_SOLARIS) -#include -#if defined(__GLIBC__) && ((__GLIBC__ < 2) || \ - ((__GLIBC__ == 2) && (__GLIBC_MINOR__ <= 90))) // Work around ugly bugs in glibc that mangle global register contents +#undef env +#define env cpu_single_env +#endif -static volatile void *saved_env; -static volatile unsigned long saved_t0, saved_i7; -#undef SAVE_GLOBALS -#define SAVE_GLOBALS() do { \ - saved_env = env; \ - saved_t0 = T0; \ - asm volatile ("st %%i7, [%0]" : : "r" (&saved_i7)); \ - } while(0) - -#undef RESTORE_GLOBALS -#define RESTORE_GLOBALS() do { \ - env = (void *)saved_env; \ - T0 = saved_t0; \ - asm volatile ("ld [%0], %%i7" : : "r" (&saved_i7)); \ - } while(0) - -static int sparc_setjmp(jmp_buf buf) -{ - int ret; +int tb_invalidated_flag; - SAVE_GLOBALS(); - ret = setjmp(buf); - RESTORE_GLOBALS(); - return ret; -} -#undef setjmp -#define setjmp(jmp_buf) sparc_setjmp(jmp_buf) +//#define DEBUG_EXEC +//#define DEBUG_SIGNAL -static void sparc_longjmp(jmp_buf buf, int val) +int qemu_cpu_has_work(CPUState *env) { - SAVE_GLOBALS(); - longjmp(buf, val); + return cpu_has_work(env); } -#define longjmp(jmp_buf, val) sparc_longjmp(jmp_buf, val) -#endif -#endif void cpu_loop_exit(void) { @@ -94,17 +63,17 @@ void cpu_loop_exit(void) 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; @@ -114,23 +83,51 @@ void cpu_resume_from_signal(CPUState *env1, void *puc) #if !defined(CONFIG_SOFTMMU) if (puc) { /* XXX: use siglongjmp ? */ +#ifdef __linux__ sigprocmask(SIG_SETMASK, &uc->uc_sigmask, NULL); +#elif defined(__OpenBSD__) + sigprocmask(SIG_SETMASK, &uc->sc_mask, NULL); +#endif } #endif + env->exception_index = -1; longjmp(env->jmp_env, 1); } +/* Execute the code without caching the generated code. An interpreter + could be used if available. */ +static void cpu_exec_nocache(int max_cycles, TranslationBlock *orig_tb) +{ + unsigned long next_tb; + TranslationBlock *tb; + + /* 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; + + tb = tb_gen_code(env, orig_tb->pc, orig_tb->cs_base, orig_tb->flags, + max_cycles); + env->current_tb = tb; + /* execute the generated code */ + next_tb = tcg_qemu_tb_exec(tb->tc_ptr); + + 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); + } + tb_phys_invalidate(tb, -1); + tb_free(tb); +} + static TranslationBlock *tb_find_slow(target_ulong pc, target_ulong cs_base, uint64_t flags) { TranslationBlock *tb, **ptb1; - int code_gen_size; unsigned int h; target_ulong phys_pc, phys_page1, phys_page2, virt_page2; - uint8_t *tc_ptr; - - spin_lock(&tb_lock); tb_invalidated_flag = 0; @@ -164,37 +161,12 @@ static TranslationBlock *tb_find_slow(target_ulong pc, ptb1 = &tb->phys_hash_next; } not_found: - /* if no translated code available, then translate it now */ - tb = tb_alloc(pc); - if (!tb) { - /* flush must be done */ - tb_flush(env); - /* cannot fail at this point */ - tb = tb_alloc(pc); - /* don't forget to invalidate previous TB info */ - tb_invalidated_flag = 1; - } - tc_ptr = code_gen_ptr; - tb->tc_ptr = tc_ptr; - tb->cs_base = cs_base; - tb->flags = flags; - SAVE_GLOBALS(); - cpu_gen_code(env, tb, &code_gen_size); - RESTORE_GLOBALS(); - code_gen_ptr = (void *)(((unsigned long)code_gen_ptr + code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1)); - - /* check next page if needed */ - virt_page2 = (pc + tb->size - 1) & TARGET_PAGE_MASK; - phys_page2 = -1; - if ((pc & TARGET_PAGE_MASK) != virt_page2) { - phys_page2 = get_phys_addr_code(env, virt_page2); - } - tb_link_phys(tb, phys_pc, phys_page2); + /* if no translated code available, then translate it now */ + tb = tb_gen_code(env, pc, cs_base, flags, 0); found: /* we add the TB in the virtual pc hash table */ env->tb_jmp_cache[tb_jmp_cache_hash_func(pc)] = tb; - spin_unlock(&tb_lock); return tb; } @@ -202,84 +174,41 @@ static inline TranslationBlock *tb_find_fast(void) { 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)); - flags |= env->intercept; - 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 - // Combined FPU enable bits . PRIV . DMMU enabled . IMMU enabled - flags = (((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->PC[env->current_tc]; -#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; - 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] & (U_FLAG | X_FLAG); - 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 (__builtin_expect(!tb || tb->pc != pc || tb->cs_base != cs_base || - tb->flags != flags, 0)) { + if (unlikely(!tb || tb->pc != pc || tb->cs_base != cs_base || + tb->flags != flags)) { tb = tb_find_slow(pc, cs_base, flags); - /* 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 */ - T0 = 0; - } } return tb; } -#define BREAK_CHAIN T0 = 0 +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) + TAILQ_FOREACH(wp, &env->watchpoints, entry) + wp->flags &= ~BP_WATCHPOINT_HIT; + + if (debug_excp_handler) + debug_excp_handler(env); +} /* main execution loop */ @@ -287,15 +216,10 @@ int cpu_exec(CPUState *env1) { #define DECLARE_HOST_REGS 1 #include "hostregs_helper.h" -#if defined(TARGET_SPARC) -#if defined(reg_REGWPTR) - uint32_t *saved_regwptr; -#endif -#endif int ret, interrupt_request; - long (*gen_func)(void); TranslationBlock *tb; uint8_t *tc_ptr; + unsigned long next_tb; if (cpu_halted(env1) == EXCP_HALTED) return EXCP_HALTED; @@ -306,7 +230,6 @@ int cpu_exec(CPUState *env1) #define SAVE_HOST_REGS 1 #include "hostregs_helper.h" env = env1; - SAVE_GLOBALS(); env_to_regs(); #if defined(TARGET_I386) @@ -316,9 +239,6 @@ int cpu_exec(CPUState *env1) 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) -#if defined(reg_REGWPTR) - saved_regwptr = REGWPTR; -#endif #elif defined(TARGET_M68K) env->cc_op = CC_OP_FLAGS; env->cc_dest = env->sr & 0xf; @@ -326,6 +246,7 @@ 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) @@ -338,14 +259,22 @@ int cpu_exec(CPUState *env1) /* prepare setjmp context for exception handling */ for(;;) { if (setjmp(env->jmp_env) == 0) { +#if defined(__sparc__) && !defined(HOST_SOLARIS) +#undef env + env = cpu_single_env; +#define env cpu_single_env +#endif 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; + 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 */ @@ -354,10 +283,12 @@ int cpu_exec(CPUState *env1) env->exception_is_int, env->error_code, env->exception_next_eip); + /* successfully delivered */ + env->old_exception = -1; #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 @@ -370,10 +301,12 @@ int cpu_exec(CPUState *env1) env->old_exception = -1; #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) - do_interrupt(env->exception_index); + do_interrupt(env); #elif defined(TARGET_ARM) do_interrupt(env); #elif defined(TARGET_SH4) @@ -385,13 +318,14 @@ int cpu_exec(CPUState *env1) #elif defined(TARGET_M68K) do_interrupt(0); #endif +#endif } env->exception_index = -1; } -#ifdef USE_KQEMU - if (kqemu_is_ok(env) && env->interrupt_request == 0) { +#ifdef CONFIG_KQEMU + if (kqemu_is_ok(env) && env->interrupt_request == 0 && env->exit_request == 0) { int ret; - 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); 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); @@ -404,7 +338,7 @@ int cpu_exec(CPUState *env1) } else if (ret == 2) { /* softmmu execution needed */ } else { - if (env->interrupt_request != 0) { + if (env->interrupt_request != 0 || env->exit_request != 0) { /* hardware interrupt will be executed just after */ } else { /* otherwise, we restart */ @@ -414,22 +348,30 @@ int cpu_exec(CPUState *env1) } #endif - T0 = 0; /* force lookup of first TB */ + if (kvm_enabled()) { + kvm_cpu_exec(env); + longjmp(env->jmp_env, 1); + } + + next_tb = 0; /* force lookup of first TB */ for(;;) { - SAVE_GLOBALS(); interrupt_request = env->interrupt_request; - if (__builtin_expect(interrupt_request, 0) -#if defined(TARGET_I386) - && env->hflags & HF_GIF_MASK -#endif - ) { + 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; @@ -438,47 +380,53 @@ int cpu_exec(CPUState *env1) } #endif #if defined(TARGET_I386) - if ((interrupt_request & CPU_INTERRUPT_SMI) && - !(env->hflags & HF_SMM_MASK)) { - svm_check_intercept(SVM_EXIT_SMI); - env->interrupt_request &= ~CPU_INTERRUPT_SMI; - do_smm_enter(); - BREAK_CHAIN; - } else if ((interrupt_request & CPU_INTERRUPT_NMI) && - !(env->hflags & HF_NMI_MASK)) { - env->interrupt_request &= ~CPU_INTERRUPT_NMI; - env->hflags |= HF_NMI_MASK; - do_interrupt(EXCP02_NMI, 0, 0, 0, 1); - BREAK_CHAIN; - } else if ((interrupt_request & CPU_INTERRUPT_HARD) && - (env->eflags & IF_MASK || env->hflags & HF_HIF_MASK) && - !(env->hflags & HF_INHIBIT_IRQ_MASK)) { - int intno; - 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); - } - do_interrupt(intno, 0, 0, 0, 1); - /* ensure that no TB jump will be modified as - the program flow was changed */ - BREAK_CHAIN; + if (env->hflags2 & HF2_GIF_MASK) { + if ((interrupt_request & CPU_INTERRUPT_SMI) && + !(env->hflags & HF_SMM_MASK)) { + svm_check_intercept(SVM_EXIT_SMI); + env->interrupt_request &= ~CPU_INTERRUPT_SMI; + do_smm_enter(); + next_tb = 0; + } else if ((interrupt_request & CPU_INTERRUPT_NMI) && + !(env->hflags2 & HF2_NMI_MASK)) { + env->interrupt_request &= ~CPU_INTERRUPT_NMI; + env->hflags2 |= HF2_NMI_MASK; + do_interrupt(EXCP02_NMI, 0, 0, 0, 1); + next_tb = 0; + } else if ((interrupt_request & CPU_INTERRUPT_HARD) && + (((env->hflags2 & HF2_VINTR_MASK) && + (env->hflags2 & HF2_HIF_MASK)) || + (!(env->hflags2 & HF2_VINTR_MASK) && + (env->eflags & IF_MASK && + !(env->hflags & HF_INHIBIT_IRQ_MASK))))) { + int intno; + svm_check_intercept(SVM_EXIT_INTR); + env->interrupt_request &= ~(CPU_INTERRUPT_HARD | CPU_INTERRUPT_VIRQ); + intno = cpu_get_pic_interrupt(env); + qemu_log_mask(CPU_LOG_TB_IN_ASM, "Servicing hardware INT=0x%02x\n", intno); +#if defined(__sparc__) && !defined(HOST_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 */ + next_tb = 0; #if !defined(CONFIG_USER_ONLY) - } else if ((interrupt_request & CPU_INTERRUPT_VIRQ) && - (env->eflags & IF_MASK) && !(env->hflags & HF_INHIBIT_IRQ_MASK)) { - int intno; - /* FIXME: this should respect TPR */ - env->interrupt_request &= ~CPU_INTERRUPT_VIRQ; - svm_check_intercept(SVM_EXIT_VINTR); - 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); - do_interrupt(intno, 0, 0, -1, 1); - stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_ctl), - ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_ctl)) & ~V_IRQ_MASK); - BREAK_CHAIN; + } else if ((interrupt_request & CPU_INTERRUPT_VIRQ) && + (env->eflags & IF_MASK) && + !(env->hflags & HF_INHIBIT_IRQ_MASK)) { + int intno; + /* FIXME: this should respect TPR */ + svm_check_intercept(SVM_EXIT_VINTR); + intno = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_vector)); + 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 + } } #elif defined(TARGET_PPC) #if 0 @@ -490,7 +438,16 @@ int cpu_exec(CPUState *env1) ppc_hw_interrupt(env); if (env->pending_interrupts == 0) env->interrupt_request &= ~CPU_INTERRUPT_HARD; - BREAK_CHAIN; + 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) && @@ -503,7 +460,7 @@ int cpu_exec(CPUState *env1) env->exception_index = EXCP_EXT_INTERRUPT; env->error_code = 0; do_interrupt(env); - BREAK_CHAIN; + next_tb = 0; } #elif defined(TARGET_SPARC) if ((interrupt_request & CPU_INTERRUPT_HARD) && @@ -515,12 +472,13 @@ int cpu_exec(CPUState *env1) (pil == 15 || pil > env->psrpil)) || type != TT_EXTINT) { env->interrupt_request &= ~CPU_INTERRUPT_HARD; - do_interrupt(env->interrupt_index); + 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 - BREAK_CHAIN; + next_tb = 0; } } else if (interrupt_request & CPU_INTERRUPT_TIMER) { //do_interrupt(0, 0, 0, 0, 0); @@ -531,7 +489,7 @@ int cpu_exec(CPUState *env1) && !(env->uncached_cpsr & CPSR_F)) { env->exception_index = EXCP_FIQ; do_interrupt(env); - BREAK_CHAIN; + next_tb = 0; } /* ARMv7-M interrupt return works by loading a magic value into the PC. On real hardware the load causes the @@ -547,22 +505,30 @@ int cpu_exec(CPUState *env1) || !(env->uncached_cpsr & CPSR_I))) { env->exception_index = EXCP_IRQ; do_interrupt(env); - BREAK_CHAIN; + next_tb = 0; } #elif defined(TARGET_SH4) if (interrupt_request & CPU_INTERRUPT_HARD) { do_interrupt(env); - BREAK_CHAIN; + next_tb = 0; } #elif defined(TARGET_ALPHA) if (interrupt_request & CPU_INTERRUPT_HARD) { do_interrupt(env); - BREAK_CHAIN; + next_tb = 0; } #elif defined(TARGET_CRIS) - if (interrupt_request & CPU_INTERRUPT_HARD) { + if (interrupt_request & CPU_INTERRUPT_HARD + && (env->pregs[PR_CCS] & I_FLAG)) { + env->exception_index = EXCP_IRQ; do_interrupt(env); - BREAK_CHAIN; + next_tb = 0; + } + if (interrupt_request & CPU_INTERRUPT_NMI + && (env->pregs[PR_CCS] & M_FLAG)) { + env->exception_index = EXCP_NMI; + do_interrupt(env); + next_tb = 0; } #elif defined(TARGET_M68K) if (interrupt_request & CPU_INTERRUPT_HARD @@ -575,7 +541,7 @@ int cpu_exec(CPUState *env1) first signalled. */ env->exception_index = env->pending_vector; do_interrupt(1); - BREAK_CHAIN; + next_tb = 0; } #endif /* Don't use the cached interupt_request value, @@ -584,126 +550,128 @@ int cpu_exec(CPUState *env1) env->interrupt_request &= ~CPU_INTERRUPT_EXITTB; /* ensure that no TB jump will be modified as the program flow was changed */ - BREAK_CHAIN; - } - if (interrupt_request & CPU_INTERRUPT_EXIT) { - env->interrupt_request &= ~CPU_INTERRUPT_EXIT; - env->exception_index = EXCP_INTERRUPT; - cpu_loop_exit(); + next_tb = 0; } } + 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)) { + 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); + log_cpu_state(env, 0); #elif defined(TARGET_SPARC) - REGWPTR = env->regbase + (env->cwp * 16); - env->regwptr = REGWPTR; - cpu_dump_state(env, logfile, fprintf, 0); + log_cpu_state(env, 0); #elif defined(TARGET_PPC) - cpu_dump_state(env, logfile, fprintf, 0); + log_cpu_state(env, 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); + log_cpu_state(env, 0); +#elif defined(TARGET_MICROBLAZE) + log_cpu_state(env, 0); #elif defined(TARGET_MIPS) - cpu_dump_state(env, logfile, fprintf, 0); + log_cpu_state(env, 0); #elif defined(TARGET_SH4) - cpu_dump_state(env, logfile, fprintf, 0); + log_cpu_state(env, 0); #elif defined(TARGET_ALPHA) - cpu_dump_state(env, logfile, fprintf, 0); + log_cpu_state(env, 0); #elif defined(TARGET_CRIS) - cpu_dump_state(env, logfile, fprintf, 0); + log_cpu_state(env, 0); #else #error unsupported target CPU #endif } #endif + spin_lock(&tb_lock); tb = tb_find_fast(); -#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)); + /* 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; } +#ifdef 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 - RESTORE_GLOBALS(); /* see if we can patch the calling TB. When the TB spans two pages, we cannot safely do a direct jump. */ { - if (T0 != 0 && -#if USE_KQEMU + if (next_tb != 0 && +#ifdef CONFIG_KQEMU (env->kqemu_enabled != 2) && #endif tb->page_addr[1] == -1) { - spin_lock(&tb_lock); - tb_add_jump((TranslationBlock *)(long)(T0 & ~3), T0 & 3, tb); - spin_unlock(&tb_lock); + tb_add_jump((TranslationBlock *)(next_tb & ~3), next_tb & 3, tb); } } - tc_ptr = tb->tc_ptr; + spin_unlock(&tb_lock); env->current_tb = tb; + + /* 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. */ + if (unlikely (env->exit_request)) + env->current_tb = NULL; + + while (env->current_tb) { + tc_ptr = tb->tc_ptr; /* execute the generated code */ - gen_func = (void *)tc_ptr; -#if defined(__sparc__) - __asm__ __volatile__("call %0\n\t" - "mov %%o7,%%i0" - : /* no outputs */ - : "r" (gen_func) - : "i0", "i1", "i2", "i3", "i4", "i5", - "o0", "o1", "o2", "o3", "o4", "o5", - "l0", "l1", "l2", "l3", "l4", "l5", - "l6", "l7"); -#elif defined(__hppa__) - asm volatile ("ble 0(%%sr4,%1)\n" - "copy %%r31,%%r18\n" - "copy %%r28,%0\n" - : "=r" (T0) - : "r" (gen_func) - : "r1", "r2", "r3", "r4", "r5", "r6", "r7", - "r8", "r9", "r10", "r11", "r12", "r13", - "r18", "r19", "r20", "r21", "r22", "r23", - "r24", "r25", "r26", "r27", "r28", "r29", - "r30", "r31"); -#elif defined(__arm__) - asm volatile ("mov pc, %0\n\t" - ".global exec_loop\n\t" - "exec_loop:\n\t" - : /* no outputs */ - : "r" (gen_func) - : "r1", "r2", "r3", "r8", "r9", "r10", "r12", "r14"); -#elif defined(__ia64) - struct fptr { - void *ip; - void *gp; - } fp; - - fp.ip = tc_ptr; - fp.gp = code_gen_buffer + 2 * (1 << 20); - (*(void (*)(void)) &fp)(); -#else - T0 = gen_func(); +#if defined(__sparc__) && !defined(HOST_SOLARIS) +#undef env + env = cpu_single_env; +#define env cpu_single_env #endif - env->current_tb = NULL; + next_tb = tcg_qemu_tb_exec(tc_ptr); + 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 */ - T0 = 0; - } -#endif -#if defined(USE_KQEMU) +#if defined(CONFIG_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) { @@ -719,19 +687,17 @@ int cpu_exec(CPUState *env1) #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) -#if defined(reg_REGWPTR) - REGWPTR = saved_regwptr; -#endif #elif defined(TARGET_PPC) #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); +#elif defined(TARGET_MICROBLAZE) #elif defined(TARGET_MIPS) #elif defined(TARGET_SH4) #elif defined(TARGET_ALPHA) @@ -742,7 +708,6 @@ int cpu_exec(CPUState *env1) #endif /* restore global registers */ - RESTORE_GLOBALS(); #include "hostregs_helper.h" /* fail safe : never use cpu_single_env outside cpu_exec() */ @@ -776,7 +741,7 @@ 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; } @@ -981,7 +946,7 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, /* 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); + cpu_loop_exit(); } else { /* activate soft MMU for this block */ cpu_resume_from_signal(env, puc); @@ -1070,7 +1035,57 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, /* 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); + cpu_loop_exit(); + } else { + /* activate soft MMU for this block */ + cpu_resume_from_signal(env, puc); + } + /* never comes here */ + return 1; +} + +#elif defined (TARGET_MICROBLAZE) +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_mb_handle_mmu_fault(env, address, is_write, MMU_USER_IDX, 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: PC=0x" TARGET_FMT_lx " error=0x%x %p\n", + env->PC, 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(); } else { /* activate soft MMU for this block */ cpu_resume_from_signal(env, puc); @@ -1221,17 +1236,28 @@ 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(__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(__OpenBSD__) + struct sigcontext *uc = puc; +#else struct ucontext *uc = puc; +#endif unsigned long pc; int trapno; @@ -1246,26 +1272,49 @@ int cpu_signal_handler(int host_signum, void *pinfo, return handle_cpu_signal(pc, (unsigned long)info->si_addr, trapno == 0xe ? (ERROR_sig(uc) >> 1) & 1 : 0, - &uc->uc_sigmask, puc); + &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) +#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; +#ifdef __NetBSD__ + 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 @@ -1374,26 +1423,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(HOST_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; } @@ -1412,7 +1484,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,