X-Git-Url: http://vcs.maemo.org/git/?a=blobdiff_plain;f=cpu-exec.c;h=8734337d6744fa78b69d17c82d5b6bd55d9ac95f;hb=HEAD;hp=51cd2fae22418d14a2103c1e10958f20981b8bbe;hpb=bfed01fc79cb2da1c6ca700bb0ce9238ce2974c7;p=qemu diff --git a/cpu-exec.c b/cpu-exec.c index 51cd2fa..8734337 100644 --- a/cpu-exec.c +++ b/cpu-exec.c @@ -1,6 +1,6 @@ /* * i386 emulator main execution loop - * + * * Copyright (c) 2003-2005 Fabrice Bellard * * This library is free software; you can redistribute it and/or @@ -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,14 +34,27 @@ #undef EDI #undef EIP #include +#ifdef __linux__ #include #endif +#endif + +#if defined(__sparc__) && !defined(HOST_SOLARIS) +// Work around ugly bugs in glibc that mangle global register contents +#undef env +#define env cpu_single_env +#endif int tb_invalidated_flag; //#define 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 @@ -48,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) +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; @@ -68,29 +83,56 @@ 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, - unsigned int flags) + 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; - + 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_page1 = phys_pc & TARGET_PAGE_MASK; @@ -101,13 +143,13 @@ static TranslationBlock *tb_find_slow(target_ulong pc, tb = *ptb1; if (!tb) goto not_found; - if (tb->pc == pc && + if (tb->pc == pc && tb->page_addr[0] == phys_page1 && - tb->cs_base == cs_base && + tb->cs_base == cs_base && tb->flags == flags) { /* check next page if needed */ if (tb->page_addr[1] != -1) { - virt_page2 = (pc & TARGET_PAGE_MASK) + + virt_page2 = (pc & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; phys_page2 = get_phys_addr_code(env, virt_page2); if (tb->page_addr[1] == phys_page2) @@ -119,35 +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; - cpu_gen_code(env, tb, CODE_GEN_MAX_SIZE, &code_gen_size); - 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; } @@ -155,79 +174,41 @@ static inline TranslationBlock *tb_find_fast(void) { TranslationBlock *tb; target_ulong cs_base, pc; - unsigned int 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); - 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 . MMU enabled . MMU no-fault . Supervisor - flags = (env->psref << 3) | ((env->mmuregs[0] & (MMU_E | MMU_NF)) << 1) - | env->psrs; -#endif - cs_base = env->npc; - pc = env->pc; -#elif defined(TARGET_PPC) - flags = (msr_pr << MSR_PR) | (msr_fp << MSR_FP) | - (msr_se << MSR_SE) | (msr_le << MSR_LE); - 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; -#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->sr & (SR_MD | SR_RB); - cs_base = 0; /* XXXXX */ - pc = env->pc; -#elif defined(TARGET_ALPHA) - flags = env->ps; - 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; } +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 */ @@ -235,56 +216,41 @@ 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 -#if defined(__sparc__) && !defined(HOST_SOLARIS) - int saved_i7; - target_ulong tmp_T0; -#endif int ret, interrupt_request; - void (*gen_func)(void); TranslationBlock *tb; uint8_t *tc_ptr; + unsigned long next_tb; if (cpu_halted(env1) == EXCP_HALTED) return EXCP_HALTED; - cpu_single_env = env1; + cpu_single_env = env1; /* first we save global registers */ #define SAVE_HOST_REGS 1 #include "hostregs_helper.h" env = env1; -#if defined(__sparc__) && !defined(HOST_SOLARIS) - /* we also save i7 because longjmp may not restore it */ - asm volatile ("mov %%i7, %0" : "=r" (saved_i7)); -#endif -#if defined(TARGET_I386) 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_ARM) #elif defined(TARGET_SPARC) -#if defined(reg_REGWPTR) - saved_regwptr = REGWPTR; -#endif -#elif defined(TARGET_PPC) #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_MICROBLAZE) #elif defined(TARGET_MIPS) #elif defined(TARGET_SH4) +#elif defined(TARGET_CRIS) /* XXXXX */ -#elif defined(TARGET_ALPHA) - env_to_regs(); #else #error unsupported target CPU #endif @@ -293,58 +259,73 @@ 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 */ #if defined(TARGET_I386) - do_interrupt_user(env->exception_index, - env->exception_is_int, - env->error_code, + do_interrupt_user(env->exception_index, + 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 double or triple faults yet. */ - do_interrupt(env->exception_index, - env->exception_is_int, - env->error_code, + do_interrupt(env->exception_index, + env->exception_is_int, + env->error_code, env->exception_next_eip, 0); /* successfully delivered */ 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) do_interrupt(env); #elif defined(TARGET_ALPHA) do_interrupt(env); +#elif defined(TARGET_CRIS) + do_interrupt(env); #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); @@ -357,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 */ @@ -367,21 +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(;;) { -#if defined(__sparc__) && !defined(HOST_SOLARIS) - /* g1 can be modified by some libc? functions */ - tmp_T0 = T0; -#endif interrupt_request = env->interrupt_request; - if (__builtin_expect(interrupt_request, 0)) { + 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_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; @@ -390,32 +380,53 @@ int cpu_exec(CPUState *env1) } #endif #if defined(TARGET_I386) - if ((interrupt_request & CPU_INTERRUPT_SMI) && - !(env->hflags & HF_SMM_MASK)) { - env->interrupt_request &= ~CPU_INTERRUPT_SMI; - do_smm_enter(); + 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) - tmp_T0 = 0; -#else - T0 = 0; +#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 */ + 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 - } else if ((interrupt_request & CPU_INTERRUPT_HARD) && - (env->eflags & IF_MASK) && - !(env->hflags & HF_INHIBIT_IRQ_MASK)) { - int intno; - env->interrupt_request &= ~CPU_INTERRUPT_HARD; - 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 */ -#if defined(__sparc__) && !defined(HOST_SOLARIS) - tmp_T0 = 0; -#else - T0 = 0; -#endif } #elif defined(TARGET_PPC) #if 0 @@ -427,11 +438,16 @@ int cpu_exec(CPUState *env1) ppc_hw_interrupt(env); if (env->pending_interrupts == 0) env->interrupt_request &= ~CPU_INTERRUPT_HARD; -#if defined(__sparc__) && !defined(HOST_SOLARIS) - tmp_T0 = 0; -#else - T0 = 0; -#endif + 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) && @@ -444,11 +460,7 @@ int cpu_exec(CPUState *env1) env->exception_index = EXCP_EXT_INTERRUPT; env->error_code = 0; do_interrupt(env); -#if defined(__sparc__) && !defined(HOST_SOLARIS) - tmp_T0 = 0; -#else - T0 = 0; -#endif + next_tb = 0; } #elif defined(TARGET_SPARC) if ((interrupt_request & CPU_INTERRUPT_HARD) && @@ -460,13 +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(__sparc__) && !defined(HOST_SOLARIS) - tmp_T0 = 0; -#else - T0 = 0; +#if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY) + cpu_check_irqs(env); #endif + next_tb = 0; } } else if (interrupt_request & CPU_INTERRUPT_TIMER) { //do_interrupt(0, 0, 0, 0, 0); @@ -477,17 +489,46 @@ int cpu_exec(CPUState *env1) && !(env->uncached_cpsr & CPSR_F)) { env->exception_index = EXCP_FIQ; do_interrupt(env); + next_tb = 0; } + /* ARMv7-M interrupt return works by loading a magic value + into the PC. On real hardware the load causes the + return to occur. The qemu implementation performs the + 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 occured at the wrong time. + We avoid this by disabling interrupts when + pc contains a magic address. */ if (interrupt_request & CPU_INTERRUPT_HARD - && !(env->uncached_cpsr & CPSR_I)) { + && ((IS_M(env) && env->regs[15] < 0xfffffff0) + || !(env->uncached_cpsr & CPSR_I))) { env->exception_index = EXCP_IRQ; do_interrupt(env); + next_tb = 0; } #elif defined(TARGET_SH4) - /* XXXXX */ + if (interrupt_request & CPU_INTERRUPT_HARD) { + do_interrupt(env); + next_tb = 0; + } #elif defined(TARGET_ALPHA) if (interrupt_request & CPU_INTERRUPT_HARD) { do_interrupt(env); + next_tb = 0; + } +#elif defined(TARGET_CRIS) + if (interrupt_request & CPU_INTERRUPT_HARD + && (env->pregs[PR_CCS] & I_FLAG)) { + env->exception_index = EXCP_IRQ; + do_interrupt(env); + 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 @@ -500,6 +541,7 @@ int cpu_exec(CPUState *env1) first signalled. */ env->exception_index = env->pending_vector; do_interrupt(1); + next_tb = 0; } #endif /* Don't use the cached interupt_request value, @@ -508,233 +550,135 @@ 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 */ -#if defined(__sparc__) && !defined(HOST_SOLARIS) - tmp_T0 = 0; -#else - T0 = 0; -#endif - } - 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 defined(TARGET_I386) + if (qemu_loglevel_mask(CPU_LOG_TB_CPU)) { /* restore flags in standard format */ -#ifdef reg_EAX - env->regs[R_EAX] = EAX; -#endif -#ifdef reg_EBX - env->regs[R_EBX] = EBX; -#endif -#ifdef reg_ECX - env->regs[R_ECX] = ECX; -#endif -#ifdef reg_EDX - env->regs[R_EDX] = EDX; -#endif -#ifdef reg_ESI - env->regs[R_ESI] = ESI; -#endif -#ifdef reg_EDI - env->regs[R_EDI] = EDI; -#endif -#ifdef reg_EBP - env->regs[R_EBP] = EBP; -#endif -#ifdef reg_ESP - env->regs[R_ESP] = ESP; -#endif - env->eflags = env->eflags | cc_table[CC_OP].compute_all() | (DF & DF_MASK); - cpu_dump_state(env, logfile, fprintf, X86_DUMP_CCOP); + regs_to_env(); +#if defined(TARGET_I386) + 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) + log_cpu_state(env, 0); #else -#error unsupported target CPU +#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 -#if defined(__sparc__) && !defined(HOST_SOLARIS) - T0 = tmp_T0; -#endif /* 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 -#if defined(TARGET_I386) && defined(USE_CODE_COPY) - && (tb->cflags & CF_CODE_COPY) == - (((TranslationBlock *)(T0 & ~3))->cflags & CF_CODE_COPY) -#endif - ) { - spin_lock(&tb_lock); - tb_add_jump((TranslationBlock *)(long)(T0 & ~3), T0 & 3, tb); -#if defined(USE_CODE_COPY) - /* propagates the FP use info */ - ((TranslationBlock *)(T0 & ~3))->cflags |= - (tb->cflags & CF_FP_USED); -#endif - spin_unlock(&tb_lock); + tb->page_addr[1] == -1) { + 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(__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(TARGET_I386) && defined(USE_CODE_COPY) -{ - if (!(tb->cflags & CF_CODE_COPY)) { - if ((tb->cflags & CF_FP_USED) && env->native_fp_regs) { - save_native_fp_state(env); - } - gen_func(); - } else { - if ((tb->cflags & CF_FP_USED) && !env->native_fp_regs) { - restore_native_fp_state(env); - } - /* we work with native eflags */ - CC_SRC = cc_table[CC_OP].compute_all(); - CC_OP = CC_OP_EFLAGS; - asm(".globl exec_loop\n" - "\n" - "debug1:\n" - " pushl %%ebp\n" - " fs movl %10, %9\n" - " fs movl %11, %%eax\n" - " andl $0x400, %%eax\n" - " fs orl %8, %%eax\n" - " pushl %%eax\n" - " popf\n" - " fs movl %%esp, %12\n" - " fs movl %0, %%eax\n" - " fs movl %1, %%ecx\n" - " fs movl %2, %%edx\n" - " fs movl %3, %%ebx\n" - " fs movl %4, %%esp\n" - " fs movl %5, %%ebp\n" - " fs movl %6, %%esi\n" - " fs movl %7, %%edi\n" - " fs jmp *%9\n" - "exec_loop:\n" - " fs movl %%esp, %4\n" - " fs movl %12, %%esp\n" - " fs movl %%eax, %0\n" - " fs movl %%ecx, %1\n" - " fs movl %%edx, %2\n" - " fs movl %%ebx, %3\n" - " fs movl %%ebp, %5\n" - " fs movl %%esi, %6\n" - " fs movl %%edi, %7\n" - " pushf\n" - " popl %%eax\n" - " movl %%eax, %%ecx\n" - " andl $0x400, %%ecx\n" - " shrl $9, %%ecx\n" - " andl $0x8d5, %%eax\n" - " fs movl %%eax, %8\n" - " movl $1, %%eax\n" - " subl %%ecx, %%eax\n" - " fs movl %%eax, %11\n" - " fs movl %9, %%ebx\n" /* get T0 value */ - " popl %%ebp\n" - : - : "m" (*(uint8_t *)offsetof(CPUState, regs[0])), - "m" (*(uint8_t *)offsetof(CPUState, regs[1])), - "m" (*(uint8_t *)offsetof(CPUState, regs[2])), - "m" (*(uint8_t *)offsetof(CPUState, regs[3])), - "m" (*(uint8_t *)offsetof(CPUState, regs[4])), - "m" (*(uint8_t *)offsetof(CPUState, regs[5])), - "m" (*(uint8_t *)offsetof(CPUState, regs[6])), - "m" (*(uint8_t *)offsetof(CPUState, regs[7])), - "m" (*(uint8_t *)offsetof(CPUState, cc_src)), - "m" (*(uint8_t *)offsetof(CPUState, tmp0)), - "a" (gen_func), - "m" (*(uint8_t *)offsetof(CPUState, df)), - "m" (*(uint8_t *)offsetof(CPUState, saved_esp)) - : "%ecx", "%edx" - ); - } -} -#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 - gen_func(); -#endif - env->current_tb = NULL; +#if defined(__sparc__) && !defined(HOST_SOLARIS) +#undef env + env = cpu_single_env; +#define env cpu_single_env +#endif + 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) { cpu_loop_exit(); } #endif - } + } /* for(;;) */ } else { env_to_regs(); } @@ -742,41 +686,32 @@ int cpu_exec(CPUState *env1) #if defined(TARGET_I386) -#if defined(USE_CODE_COPY) - if (env->native_fp_regs) { - save_native_fp_state(env); - } -#endif /* 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) +#elif defined(TARGET_CRIS) /* XXXXX */ #else #error unsupported target CPU #endif /* restore global registers */ -#if defined(__sparc__) && !defined(HOST_SOLARIS) - asm volatile ("mov %0, %%i7" : : "r" (saved_i7)); -#endif #include "hostregs_helper.h" /* fail safe : never use cpu_single_env outside cpu_exec() */ - cpu_single_env = NULL; + cpu_single_env = NULL; return ret; } @@ -803,34 +738,34 @@ void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector) env = s; if (!(env->cr[0] & CR0_PE_MASK) || (env->eflags & VM_MASK)) { selector &= 0xffff; - cpu_x86_load_seg_cache(env, seg_reg, 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; } @@ -846,7 +781,7 @@ void cpu_x86_frstor(CPUX86State *s, uint8_t *ptr, int data32) write caused the exception and otherwise 0'. 'old_set' is the signal set which should be restored */ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, - int is_write, sigset_t *old_set, + int is_write, sigset_t *old_set, void *puc) { TranslationBlock *tb; @@ -855,7 +790,7 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, if (cpu_single_env) env = cpu_single_env; /* XXX: find a correct solution for multithread */ #if defined(DEBUG_SIGNAL) - qemu_printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n", + qemu_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 */ @@ -864,8 +799,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); + ret = cpu_x86_handle_mmu_fault(env, address, is_write, MMU_USER_IDX, 0); if (ret < 0) return 0; /* not an MMU fault */ if (ret == 0) @@ -879,7 +813,7 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, } if (ret == 1) { #if 0 - printf("PF exception: EIP=0x%08x CR2=0x%08x error=0x%x\n", + printf("PF exception: EIP=0x%08x CR2=0x%08x error=0x%x\n", env->eip, env->cr[2], env->error_code); #endif /* we restore the process signal mask as the sigreturn should @@ -906,7 +840,7 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, 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", + 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 */ @@ -914,7 +848,7 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, return 1; } /* see if it is an MMU fault */ - ret = cpu_arm_handle_mmu_fault(env, address, is_write, 1, 0); + ret = cpu_arm_handle_mmu_fault(env, address, is_write, MMU_USER_IDX, 0); if (ret < 0) return 0; /* not an MMU fault */ if (ret == 0) @@ -930,6 +864,8 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, do it (XXX: use sigsetjmp) */ sigprocmask(SIG_SETMASK, old_set, NULL); cpu_loop_exit(); + /* never comes here */ + return 1; } #elif defined(TARGET_SPARC) static inline int handle_cpu_signal(unsigned long pc, unsigned long address, @@ -942,7 +878,7 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, 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", + 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 */ @@ -950,7 +886,7 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, return 1; } /* see if it is an MMU fault */ - ret = cpu_sparc_handle_mmu_fault(env, address, is_write, 1, 0); + ret = cpu_sparc_handle_mmu_fault(env, address, is_write, MMU_USER_IDX, 0); if (ret < 0) return 0; /* not an MMU fault */ if (ret == 0) @@ -966,6 +902,8 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, do it (XXX: use sigsetjmp) */ sigprocmask(SIG_SETMASK, old_set, NULL); cpu_loop_exit(); + /* never comes here */ + return 1; } #elif defined (TARGET_PPC) static inline int handle_cpu_signal(unsigned long pc, unsigned long address, @@ -974,11 +912,11 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, { 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", + 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 */ @@ -987,7 +925,7 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, } /* see if it is an MMU fault */ - ret = cpu_ppc_handle_mmu_fault(env, address, is_write, msr_pr, 0); + ret = cpu_ppc_handle_mmu_fault(env, address, is_write, MMU_USER_IDX, 0); if (ret < 0) return 0; /* not an MMU fault */ if (ret == 0) @@ -1002,13 +940,13 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, } if (ret == 1) { #if 0 - printf("PF exception: NIP=0x%08x error=0x%x %p\n", + 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); + cpu_loop_exit(); } else { /* activate soft MMU for this block */ cpu_resume_from_signal(env, puc); @@ -1028,7 +966,7 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, 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", + 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 */ @@ -1036,7 +974,7 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, return 1; } /* see if it is an MMU fault */ - ret = cpu_m68k_handle_mmu_fault(env, address, is_write, 1, 0); + ret = cpu_m68k_handle_mmu_fault(env, address, is_write, MMU_USER_IDX, 0); if (ret < 0) return 0; /* not an MMU fault */ if (ret == 0) @@ -1063,11 +1001,11 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, { 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", + 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 */ @@ -1076,7 +1014,7 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, } /* see if it is an MMU fault */ - ret = cpu_mips_handle_mmu_fault(env, address, is_write, 1, 0); + ret = cpu_mips_handle_mmu_fault(env, address, is_write, MMU_USER_IDX, 0); if (ret < 0) return 0; /* not an MMU fault */ if (ret == 0) @@ -1091,13 +1029,63 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, } if (ret == 1) { #if 0 - printf("PF exception: PC=0x" TARGET_FMT_lx " error=0x%x %p\n", + 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); - 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); @@ -1113,11 +1101,11 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, { 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", + 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 */ @@ -1126,7 +1114,7 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, } /* see if it is an MMU fault */ - ret = cpu_sh4_handle_mmu_fault(env, address, is_write, 1, 0); + ret = cpu_sh4_handle_mmu_fault(env, address, is_write, MMU_USER_IDX, 0); if (ret < 0) return 0; /* not an MMU fault */ if (ret == 0) @@ -1140,7 +1128,7 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, cpu_restore_state(tb, env, pc, puc); } #if 0 - printf("PF exception: NIP=0x%08x error=0x%x %p\n", + 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 @@ -1158,11 +1146,11 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, { 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", + 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 */ @@ -1171,7 +1159,7 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, } /* see if it is an MMU fault */ - ret = cpu_alpha_handle_mmu_fault(env, address, is_write, 1, 0); + ret = cpu_alpha_handle_mmu_fault(env, address, is_write, MMU_USER_IDX, 0); if (ret < 0) return 0; /* not an MMU fault */ if (ret == 0) @@ -1185,7 +1173,7 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, cpu_restore_state(tb, env, pc, puc); } #if 0 - printf("PF exception: NIP=0x%08x error=0x%x %p\n", + 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 @@ -1195,6 +1183,47 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, /* never comes here */ return 1; } +#elif defined (TARGET_CRIS) +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_cris_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); + } + /* 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 @@ -1207,37 +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 -#if defined(USE_CODE_COPY) -static void cpu_send_trap(unsigned long pc, int trap, - struct ucontext *uc) -{ - TranslationBlock *tb; - - if (cpu_single_env) - env = cpu_single_env; /* XXX: find a correct solution for multithread */ - /* 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, uc); - } - sigprocmask(SIG_SETMASK, &uc->uc_sigmask, NULL); - raise_exception_err(trap, env->error_code); -} -#endif - -int cpu_signal_handler(int host_signum, void *pinfo, +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; @@ -1249,36 +1269,52 @@ int cpu_signal_handler(int host_signum, void *pinfo, #endif pc = EIP_sig(uc); trapno = TRAP_sig(uc); -#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 ? - (ERROR_sig(uc) >> 1) & 1 : 0, - &uc->uc_sigmask, puc); + 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) +#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]; - 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); + pc = PC_sig(uc); + return handle_cpu_signal(pc, (unsigned long)info->si_addr, + 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 @@ -1329,7 +1365,7 @@ typedef struct ucontext SIGCONTEXT; # define TRAP_sig(context) EXCEPREG_sig(exception, context) /* number of powerpc exception taken */ #endif /* __APPLE__ */ -int cpu_signal_handler(int host_signum, void *pinfo, +int cpu_signal_handler(int host_signum, void *pinfo, void *puc) { siginfo_t *info = pinfo; @@ -1347,13 +1383,13 @@ int cpu_signal_handler(int host_signum, void *pinfo, if (TRAP_sig(uc) != 0x400 && (DSISR_sig(uc) & 0x02000000)) is_write = 1; #endif - return handle_cpu_signal(pc, (unsigned long)info->si_addr, + return handle_cpu_signal(pc, (unsigned long)info->si_addr, is_write, &uc->uc_sigmask, puc); } #elif defined(__alpha__) -int cpu_signal_handler(int host_signum, void *pinfo, +int cpu_signal_handler(int host_signum, void *pinfo, void *puc) { siginfo_t *info = pinfo; @@ -1378,75 +1414,102 @@ int cpu_signal_handler(int host_signum, void *pinfo, is_write = 1; } - return handle_cpu_signal(pc, (unsigned long)info->si_addr, + return handle_cpu_signal(pc, (unsigned long)info->si_addr, is_write, &uc->uc_sigmask, puc); } #elif defined(__sparc__) -int cpu_signal_handler(int host_signum, void *pinfo, +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; } } - return handle_cpu_signal(pc, (unsigned long)info->si_addr, + return handle_cpu_signal(pc, (unsigned long)info->si_addr, is_write, sigmask, NULL); } #elif defined(__arm__) -int cpu_signal_handler(int host_signum, void *pinfo, +int cpu_signal_handler(int host_signum, void *pinfo, void *puc) { siginfo_t *info = pinfo; struct ucontext *uc = puc; 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, + return handle_cpu_signal(pc, (unsigned long)info->si_addr, is_write, &uc->uc_sigmask, puc); } #elif defined(__mc68000) -int cpu_signal_handler(int host_signum, void *pinfo, +int cpu_signal_handler(int host_signum, void *pinfo, void *puc) { siginfo_t *info = pinfo; struct ucontext *uc = puc; unsigned long pc; int is_write; - + pc = uc->uc_mcontext.gregs[16]; /* XXX: compute is_write */ is_write = 0; - return handle_cpu_signal(pc, (unsigned long)info->si_addr, + return handle_cpu_signal(pc, (unsigned long)info->si_addr, is_write, &uc->uc_sigmask, puc); } @@ -1487,37 +1550,55 @@ int cpu_signal_handler(int host_signum, void *pinfo, void *puc) #elif defined(__s390__) -int cpu_signal_handler(int host_signum, void *pinfo, +int cpu_signal_handler(int host_signum, void *pinfo, void *puc) { siginfo_t *info = pinfo; struct ucontext *uc = puc; unsigned long pc; int is_write; - + pc = uc->uc_mcontext.psw.addr; /* XXX: compute is_write */ is_write = 0; - return handle_cpu_signal(pc, (unsigned long)info->si_addr, + 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, +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, + return handle_cpu_signal(pc, (unsigned long)info->si_addr, 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; + int is_write; + + pc = uc->uc_mcontext.sc_iaoq[0]; + /* FIXME: compute is_write */ + is_write = 0; + return handle_cpu_signal(pc, (unsigned long)info->si_addr, + is_write, + &uc->uc_sigmask, puc); +} + #else #error host CPU specific signal handler needed