Loading cpu-all.h +2 −0 Original line number Diff line number Diff line Loading @@ -782,6 +782,8 @@ void cpu_abort(CPUState *env, const char *fmt, ...) __attribute__ ((__noreturn__)); extern CPUState *first_cpu; extern CPUState *cpu_single_env; extern int64_t qemu_icount; extern int use_icount; #define CPU_INTERRUPT_EXIT 0x01 /* wants exit from main loop */ #define CPU_INTERRUPT_HARD 0x02 /* hardware interrupt pending */ Loading cpu-defs.h +28 −6 Original line number Diff line number Diff line Loading @@ -130,17 +130,29 @@ typedef struct CPUTLBEntry { sizeof(target_phys_addr_t))]; } CPUTLBEntry; #ifdef WORDS_BIGENDIAN typedef struct icount_decr_u16 { uint16_t high; uint16_t low; } icount_decr_u16; #else typedef struct icount_decr_u16 { uint16_t low; uint16_t high; } icount_decr_u16; #endif #define CPU_TEMP_BUF_NLONGS 128 #define CPU_COMMON \ struct TranslationBlock *current_tb; /* currently executing TB */ \ /* soft mmu support */ \ /* in order to avoid passing too many arguments to the memory \ write helpers, we store some rarely used information in the CPU \ /* in order to avoid passing too many arguments to the MMIO \ helpers, we store some rarely used information in the CPU \ context) */ \ unsigned long mem_write_pc; /* host pc at which the memory was \ written */ \ target_ulong mem_write_vaddr; /* target virtual addr at which the \ memory was written */ \ unsigned long mem_io_pc; /* host pc at which the memory was \ accessed */ \ target_ulong mem_io_vaddr; /* target virtual addr at which the \ memory was accessed */ \ int halted; /* TRUE if the CPU is in suspend state */ \ /* The meaning of the MMU modes is defined in the target code. */ \ CPUTLBEntry tlb_table[NB_MMU_MODES][CPU_TLB_SIZE]; \ Loading @@ -149,6 +161,16 @@ typedef struct CPUTLBEntry { /* buffer for temporaries in the code generator */ \ long temp_buf[CPU_TEMP_BUF_NLONGS]; \ \ int64_t icount_extra; /* Instructions until next timer event. */ \ /* Number of cycles left, with interrupt flag in high bit. \ This allows a single read-compare-cbranch-write sequence to test \ for both decrementer underflow and exceptions. */ \ union { \ uint32_t u32; \ icount_decr_u16 u16; \ } icount_decr; \ uint32_t can_do_io; /* nonzero if memory mapped IO is safe. */ \ \ /* from this point: preserved by CPU reset */ \ /* ice debug support */ \ target_ulong breakpoints[MAX_BREAKPOINTS]; \ Loading cpu-exec.c +63 −30 Original line number Diff line number Diff line Loading @@ -82,15 +82,40 @@ void cpu_resume_from_signal(CPUState *env1, void *puc) 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; tb_invalidated_flag = 0; Loading Loading @@ -125,29 +150,7 @@ static TranslationBlock *tb_find_slow(target_ulong pc, } 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_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); tb = tb_gen_code(env, pc, cs_base, flags, 0); found: /* we add the TB in the virtual pc hash table */ Loading Loading @@ -583,6 +586,7 @@ int cpu_exec(CPUState *env1) of memory exceptions while generating the code, we must recompute the hash index here */ next_tb = 0; tb_invalidated_flag = 0; } #ifdef DEBUG_EXEC if ((loglevel & CPU_LOG_EXEC)) { Loading @@ -604,8 +608,9 @@ int cpu_exec(CPUState *env1) } } spin_unlock(&tb_lock); tc_ptr = tb->tc_ptr; env->current_tb = tb; while (env->current_tb) { tc_ptr = tb->tc_ptr; /* execute the generated code */ #if defined(__sparc__) && !defined(HOST_SOLARIS) #undef env Loading @@ -614,6 +619,34 @@ int cpu_exec(CPUState *env1) #endif next_tb = tcg_qemu_tb_exec(tc_ptr); env->current_tb = NULL; if ((next_tb & 3) == 2) { /* Instruction counter exired. */ 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(USE_KQEMU) Loading exec-all.h +27 −6 Original line number Diff line number Diff line Loading @@ -27,7 +27,7 @@ #define DISAS_UPDATE 2 /* cpu state was modified dynamically */ #define DISAS_TB_JUMP 3 /* only pc was modified statically */ struct TranslationBlock; typedef struct TranslationBlock TranslationBlock; /* XXX: make safe guess about sizes */ #define MAX_OP_PER_INSTR 64 Loading @@ -48,6 +48,7 @@ extern target_ulong gen_opc_pc[OPC_BUF_SIZE]; extern target_ulong gen_opc_npc[OPC_BUF_SIZE]; extern uint8_t gen_opc_cc_op[OPC_BUF_SIZE]; extern uint8_t gen_opc_instr_start[OPC_BUF_SIZE]; extern uint16_t gen_opc_icount[OPC_BUF_SIZE]; extern target_ulong gen_opc_jump_pc[2]; extern uint32_t gen_opc_hflags[OPC_BUF_SIZE]; Loading Loading @@ -75,6 +76,10 @@ int cpu_restore_state_copy(struct TranslationBlock *tb, CPUState *env, unsigned long searched_pc, void *puc); void cpu_resume_from_signal(CPUState *env1, void *puc); void cpu_io_recompile(CPUState *env, void *retaddr); TranslationBlock *tb_gen_code(CPUState *env, target_ulong pc, target_ulong cs_base, int flags, int cflags); void cpu_exec_init(CPUState *env); int page_unprotect(target_ulong address, unsigned long pc, void *puc); void tb_invalidate_phys_page_range(target_phys_addr_t start, target_phys_addr_t end, Loading Loading @@ -117,16 +122,15 @@ static inline int tlb_set_page(CPUState *env1, target_ulong vaddr, #define USE_DIRECT_JUMP #endif typedef struct TranslationBlock { struct TranslationBlock { target_ulong pc; /* simulated PC corresponding to this block (EIP + CS base) */ target_ulong cs_base; /* CS base for this block */ uint64_t flags; /* flags defining in which context the code was generated */ uint16_t size; /* size of target code for this block (1 <= size <= TARGET_PAGE_SIZE) */ uint16_t cflags; /* compile flags */ #define CF_TB_FP_USED 0x0002 /* fp ops are used in the TB */ #define CF_FP_USED 0x0004 /* fp ops are used in the TB or in a chained TB */ #define CF_SINGLE_INSN 0x0008 /* compile only a single instruction */ #define CF_COUNT_MASK 0x7fff #define CF_LAST_IO 0x8000 /* Last insn may be an IO access. */ uint8_t *tc_ptr; /* pointer to the translated code */ /* next matching tb for physical address. */ Loading @@ -150,7 +154,8 @@ typedef struct TranslationBlock { jmp_first */ struct TranslationBlock *jmp_next[2]; struct TranslationBlock *jmp_first; } TranslationBlock; uint32_t icount; }; static inline unsigned int tb_jmp_cache_hash_page(target_ulong pc) { Loading @@ -173,9 +178,11 @@ static inline unsigned int tb_phys_hash_func(unsigned long pc) } TranslationBlock *tb_alloc(target_ulong pc); void tb_free(TranslationBlock *tb); void tb_flush(CPUState *env); void tb_link_phys(TranslationBlock *tb, target_ulong phys_pc, target_ulong phys_page2); void tb_phys_invalidate(TranslationBlock *tb, target_ulong page_addr); extern TranslationBlock *tb_phys_hash[CODE_GEN_PHYS_HASH_SIZE]; extern uint8_t *code_gen_ptr; Loading Loading @@ -364,6 +371,20 @@ static inline target_ulong get_phys_addr_code(CPUState *env1, target_ulong addr) } return addr + env1->tlb_table[mmu_idx][page_index].addend - (unsigned long)phys_ram_base; } /* Deterministic execution requires that IO only be performaed on the last instruction of a TB so that interrupts take effect immediately. */ static inline int can_do_io(CPUState *env) { if (!use_icount) return 1; /* If not executing code then assume we are ok. */ if (!env->current_tb) return 1; return env->can_do_io != 0; } #endif #ifdef USE_KQEMU Loading exec.c +118 −29 Original line number Diff line number Diff line Loading @@ -107,6 +107,13 @@ CPUState *first_cpu; /* current CPU in the current thread. It is only valid inside cpu_exec() */ CPUState *cpu_single_env; /* 0 = Do not count executed instructions. 1 = Precice instruction counting. 2 = Adaptive rate instruction counting. */ int use_icount = 0; /* Current instruction counter. While executing translated code this may include some instructions that have not yet been executed. */ int64_t qemu_icount; typedef struct PageDesc { /* list of TBs intersecting this ram page */ Loading Loading @@ -633,7 +640,7 @@ static inline void tb_reset_jump(TranslationBlock *tb, int n) tb_set_jmp_target(tb, n, (unsigned long)(tb->tc_ptr + tb->tb_next_offset[n])); } static inline void tb_phys_invalidate(TranslationBlock *tb, target_ulong page_addr) void tb_phys_invalidate(TranslationBlock *tb, target_ulong page_addr) { CPUState *env; PageDesc *p; Loading Loading @@ -746,11 +753,9 @@ static void build_page_bitmap(PageDesc *p) } } #ifdef TARGET_HAS_PRECISE_SMC static void tb_gen_code(CPUState *env, target_ulong pc, target_ulong cs_base, int flags, int cflags) TranslationBlock *tb_gen_code(CPUState *env, target_ulong pc, target_ulong cs_base, int flags, int cflags) { TranslationBlock *tb; uint8_t *tc_ptr; Loading @@ -764,6 +769,8 @@ static void tb_gen_code(CPUState *env, 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; Loading @@ -780,8 +787,8 @@ static void tb_gen_code(CPUState *env, phys_page2 = get_phys_addr_code(env, virt_page2); } tb_link_phys(tb, phys_pc, phys_page2); return tb; } #endif /* invalidate all TBs which intersect with the target physical page starting in range [start;end[. NOTE: start and end must refer to Loading Loading @@ -836,13 +843,13 @@ void tb_invalidate_phys_page_range(target_phys_addr_t start, target_phys_addr_t if (current_tb_not_found) { current_tb_not_found = 0; current_tb = NULL; if (env->mem_write_pc) { if (env->mem_io_pc) { /* now we have a real cpu fault */ current_tb = tb_find_pc(env->mem_write_pc); current_tb = tb_find_pc(env->mem_io_pc); } } if (current_tb == tb && !(current_tb->cflags & CF_SINGLE_INSN)) { (current_tb->cflags & CF_COUNT_MASK) != 1) { /* If we are modifying the current TB, we must stop its execution. We could be more precise by checking that the modification is after the current PC, but it Loading @@ -851,7 +858,7 @@ void tb_invalidate_phys_page_range(target_phys_addr_t start, target_phys_addr_t current_tb_modified = 1; cpu_restore_state(current_tb, env, env->mem_write_pc, NULL); env->mem_io_pc, NULL); #if defined(TARGET_I386) current_flags = env->hflags; current_flags |= (env->eflags & (IOPL_MASK | TF_MASK | VM_MASK)); Loading Loading @@ -883,7 +890,7 @@ void tb_invalidate_phys_page_range(target_phys_addr_t start, target_phys_addr_t if (!p->first_tb) { invalidate_page_bitmap(p); if (is_cpu_write_access) { tlb_unprotect_code_phys(env, start, env->mem_write_vaddr); tlb_unprotect_code_phys(env, start, env->mem_io_vaddr); } } #endif Loading @@ -893,8 +900,7 @@ void tb_invalidate_phys_page_range(target_phys_addr_t start, target_phys_addr_t modifying the memory. It will ensure that it cannot modify itself */ env->current_tb = NULL; tb_gen_code(env, current_pc, current_cs_base, current_flags, CF_SINGLE_INSN); tb_gen_code(env, current_pc, current_cs_base, current_flags, 1); cpu_resume_from_signal(env, NULL); } #endif Loading @@ -909,7 +915,7 @@ static inline void tb_invalidate_phys_page_fast(target_phys_addr_t start, int le if (1) { if (loglevel) { fprintf(logfile, "modifying code at 0x%x size=%d EIP=%x PC=%08x\n", cpu_single_env->mem_write_vaddr, len, cpu_single_env->mem_io_vaddr, len, cpu_single_env->eip, cpu_single_env->eip + (long)cpu_single_env->segs[R_CS].base); } Loading Loading @@ -961,7 +967,7 @@ static void tb_invalidate_phys_page(target_phys_addr_t addr, tb = (TranslationBlock *)((long)tb & ~3); #ifdef TARGET_HAS_PRECISE_SMC if (current_tb == tb && !(current_tb->cflags & CF_SINGLE_INSN)) { (current_tb->cflags & CF_COUNT_MASK) != 1) { /* If we are modifying the current TB, we must stop its execution. We could be more precise by checking that the modification is after the current PC, but it Loading Loading @@ -990,8 +996,7 @@ static void tb_invalidate_phys_page(target_phys_addr_t addr, modifying the memory. It will ensure that it cannot modify itself */ env->current_tb = NULL; tb_gen_code(env, current_pc, current_cs_base, current_flags, CF_SINGLE_INSN); tb_gen_code(env, current_pc, current_cs_base, current_flags, 1); cpu_resume_from_signal(env, puc); } #endif Loading Loading @@ -1068,6 +1073,17 @@ TranslationBlock *tb_alloc(target_ulong pc) return tb; } void tb_free(TranslationBlock *tb) { /* In practice this is mostly used for single use temorary TB Ignore the hard cases and just back up if this TB happens to be the last one generated. */ if (nb_tbs > 0 && tb == &tbs[nb_tbs - 1]) { code_gen_ptr = tb->tc_ptr; nb_tbs--; } } /* add a new TB and link it to the physical page tables. phys_page2 is (-1) to indicate that only one page contains the TB. */ void tb_link_phys(TranslationBlock *tb, Loading Loading @@ -1369,7 +1385,9 @@ void cpu_interrupt(CPUState *env, int mask) TranslationBlock *tb; static spinlock_t interrupt_lock = SPIN_LOCK_UNLOCKED; #endif int old_mask; old_mask = env->interrupt_request; /* FIXME: This is probably not threadsafe. A different thread could be in the mittle of a read-modify-write operation. */ env->interrupt_request |= mask; Loading @@ -1379,14 +1397,26 @@ void cpu_interrupt(CPUState *env, int mask) emulation this often isn't actually as bad as it sounds. Often signals are used primarily to interrupt blocking syscalls. */ #else if (use_icount) { env->icount_decr.u16.high = 0x8000; #ifndef CONFIG_USER_ONLY /* CPU_INTERRUPT_EXIT isn't a real interrupt. It just means an async event happened and we need to process it. */ if (!can_do_io(env) && (mask & ~(old_mask | CPU_INTERRUPT_EXIT)) != 0) { cpu_abort(env, "Raised interrupt while not in I/O function"); } #endif } else { tb = env->current_tb; /* if the cpu is currently executing code, we must unlink it and all the potentially executing TB */ tb = env->current_tb; if (tb && !testandset(&interrupt_lock)) { env->current_tb = NULL; tb_reset_jump_recursive(tb); resetlock(&interrupt_lock); } } #endif } Loading Loading @@ -2227,7 +2257,7 @@ static void notdirty_mem_writeb(void *opaque, target_phys_addr_t ram_addr, /* we remove the notdirty callback only if the code has been flushed */ if (dirty_flags == 0xff) tlb_set_dirty(cpu_single_env, cpu_single_env->mem_write_vaddr); tlb_set_dirty(cpu_single_env, cpu_single_env->mem_io_vaddr); } static void notdirty_mem_writew(void *opaque, target_phys_addr_t ram_addr, Loading @@ -2252,7 +2282,7 @@ static void notdirty_mem_writew(void *opaque, target_phys_addr_t ram_addr, /* we remove the notdirty callback only if the code has been flushed */ if (dirty_flags == 0xff) tlb_set_dirty(cpu_single_env, cpu_single_env->mem_write_vaddr); tlb_set_dirty(cpu_single_env, cpu_single_env->mem_io_vaddr); } static void notdirty_mem_writel(void *opaque, target_phys_addr_t ram_addr, Loading @@ -2277,7 +2307,7 @@ static void notdirty_mem_writel(void *opaque, target_phys_addr_t ram_addr, /* we remove the notdirty callback only if the code has been flushed */ if (dirty_flags == 0xff) tlb_set_dirty(cpu_single_env, cpu_single_env->mem_write_vaddr); tlb_set_dirty(cpu_single_env, cpu_single_env->mem_io_vaddr); } static CPUReadMemoryFunc *error_mem_read[3] = { Loading @@ -2299,7 +2329,7 @@ static void check_watchpoint(int offset, int flags) target_ulong vaddr; int i; vaddr = (env->mem_write_vaddr & TARGET_PAGE_MASK) + offset; vaddr = (env->mem_io_vaddr & TARGET_PAGE_MASK) + offset; for (i = 0; i < env->nb_watchpoints; i++) { if (vaddr == env->watchpoint[i].vaddr && (env->watchpoint[i].type & flags)) { Loading Loading @@ -2967,6 +2997,65 @@ int cpu_memory_rw_debug(CPUState *env, target_ulong addr, return 0; } /* in deterministic execution mode, instructions doing device I/Os must be at the end of the TB */ void cpu_io_recompile(CPUState *env, void *retaddr) { TranslationBlock *tb; uint32_t n, cflags; target_ulong pc, cs_base; uint64_t flags; tb = tb_find_pc((unsigned long)retaddr); if (!tb) { cpu_abort(env, "cpu_io_recompile: could not find TB for pc=%p", retaddr); } n = env->icount_decr.u16.low + tb->icount; cpu_restore_state(tb, env, (unsigned long)retaddr, NULL); /* Calculate how many instructions had been executed before the fault occured. */ n = n - env->icount_decr.u16.low; /* Generate a new TB ending on the I/O insn. */ n++; /* On MIPS and SH, delay slot instructions can only be restarted if they were already the first instruction in the TB. If this is not the first instruction in a TB then re-execute the preceeding branch. */ #if defined(TARGET_MIPS) if ((env->hflags & MIPS_HFLAG_BMASK) != 0 && n > 1) { env->active_tc.PC -= 4; env->icount_decr.u16.low++; env->hflags &= ~MIPS_HFLAG_BMASK; } #elif defined(TARGET_SH4) if ((env->flags & ((DELAY_SLOT | DELAY_SLOT_CONDITIONAL))) != 0 && n > 1) { env->pc -= 2; env->icount_decr.u16.low++; env->flags &= ~(DELAY_SLOT | DELAY_SLOT_CONDITIONAL); } #endif /* This should never happen. */ if (n > CF_COUNT_MASK) cpu_abort(env, "TB too big during recompile"); cflags = n | CF_LAST_IO; pc = tb->pc; cs_base = tb->cs_base; flags = tb->flags; tb_phys_invalidate(tb, -1); /* FIXME: In theory this could raise an exception. In practice we have already translated the block once so it's probably ok. */ tb_gen_code(env, pc, cs_base, flags, cflags); /* TODO: If env->pc != tb->pc (i.e. the failuting instruction was not the first in the TB) then we end up generating a whole new TB and repeating the fault, which is horribly inefficient. Better would be to execute just this insn uncached, or generate a second new TB. */ cpu_resume_from_signal(env, NULL); } void dump_exec_info(FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...)) { Loading Loading
cpu-all.h +2 −0 Original line number Diff line number Diff line Loading @@ -782,6 +782,8 @@ void cpu_abort(CPUState *env, const char *fmt, ...) __attribute__ ((__noreturn__)); extern CPUState *first_cpu; extern CPUState *cpu_single_env; extern int64_t qemu_icount; extern int use_icount; #define CPU_INTERRUPT_EXIT 0x01 /* wants exit from main loop */ #define CPU_INTERRUPT_HARD 0x02 /* hardware interrupt pending */ Loading
cpu-defs.h +28 −6 Original line number Diff line number Diff line Loading @@ -130,17 +130,29 @@ typedef struct CPUTLBEntry { sizeof(target_phys_addr_t))]; } CPUTLBEntry; #ifdef WORDS_BIGENDIAN typedef struct icount_decr_u16 { uint16_t high; uint16_t low; } icount_decr_u16; #else typedef struct icount_decr_u16 { uint16_t low; uint16_t high; } icount_decr_u16; #endif #define CPU_TEMP_BUF_NLONGS 128 #define CPU_COMMON \ struct TranslationBlock *current_tb; /* currently executing TB */ \ /* soft mmu support */ \ /* in order to avoid passing too many arguments to the memory \ write helpers, we store some rarely used information in the CPU \ /* in order to avoid passing too many arguments to the MMIO \ helpers, we store some rarely used information in the CPU \ context) */ \ unsigned long mem_write_pc; /* host pc at which the memory was \ written */ \ target_ulong mem_write_vaddr; /* target virtual addr at which the \ memory was written */ \ unsigned long mem_io_pc; /* host pc at which the memory was \ accessed */ \ target_ulong mem_io_vaddr; /* target virtual addr at which the \ memory was accessed */ \ int halted; /* TRUE if the CPU is in suspend state */ \ /* The meaning of the MMU modes is defined in the target code. */ \ CPUTLBEntry tlb_table[NB_MMU_MODES][CPU_TLB_SIZE]; \ Loading @@ -149,6 +161,16 @@ typedef struct CPUTLBEntry { /* buffer for temporaries in the code generator */ \ long temp_buf[CPU_TEMP_BUF_NLONGS]; \ \ int64_t icount_extra; /* Instructions until next timer event. */ \ /* Number of cycles left, with interrupt flag in high bit. \ This allows a single read-compare-cbranch-write sequence to test \ for both decrementer underflow and exceptions. */ \ union { \ uint32_t u32; \ icount_decr_u16 u16; \ } icount_decr; \ uint32_t can_do_io; /* nonzero if memory mapped IO is safe. */ \ \ /* from this point: preserved by CPU reset */ \ /* ice debug support */ \ target_ulong breakpoints[MAX_BREAKPOINTS]; \ Loading
cpu-exec.c +63 −30 Original line number Diff line number Diff line Loading @@ -82,15 +82,40 @@ void cpu_resume_from_signal(CPUState *env1, void *puc) 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; tb_invalidated_flag = 0; Loading Loading @@ -125,29 +150,7 @@ static TranslationBlock *tb_find_slow(target_ulong pc, } 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_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); tb = tb_gen_code(env, pc, cs_base, flags, 0); found: /* we add the TB in the virtual pc hash table */ Loading Loading @@ -583,6 +586,7 @@ int cpu_exec(CPUState *env1) of memory exceptions while generating the code, we must recompute the hash index here */ next_tb = 0; tb_invalidated_flag = 0; } #ifdef DEBUG_EXEC if ((loglevel & CPU_LOG_EXEC)) { Loading @@ -604,8 +608,9 @@ int cpu_exec(CPUState *env1) } } spin_unlock(&tb_lock); tc_ptr = tb->tc_ptr; env->current_tb = tb; while (env->current_tb) { tc_ptr = tb->tc_ptr; /* execute the generated code */ #if defined(__sparc__) && !defined(HOST_SOLARIS) #undef env Loading @@ -614,6 +619,34 @@ int cpu_exec(CPUState *env1) #endif next_tb = tcg_qemu_tb_exec(tc_ptr); env->current_tb = NULL; if ((next_tb & 3) == 2) { /* Instruction counter exired. */ 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(USE_KQEMU) Loading
exec-all.h +27 −6 Original line number Diff line number Diff line Loading @@ -27,7 +27,7 @@ #define DISAS_UPDATE 2 /* cpu state was modified dynamically */ #define DISAS_TB_JUMP 3 /* only pc was modified statically */ struct TranslationBlock; typedef struct TranslationBlock TranslationBlock; /* XXX: make safe guess about sizes */ #define MAX_OP_PER_INSTR 64 Loading @@ -48,6 +48,7 @@ extern target_ulong gen_opc_pc[OPC_BUF_SIZE]; extern target_ulong gen_opc_npc[OPC_BUF_SIZE]; extern uint8_t gen_opc_cc_op[OPC_BUF_SIZE]; extern uint8_t gen_opc_instr_start[OPC_BUF_SIZE]; extern uint16_t gen_opc_icount[OPC_BUF_SIZE]; extern target_ulong gen_opc_jump_pc[2]; extern uint32_t gen_opc_hflags[OPC_BUF_SIZE]; Loading Loading @@ -75,6 +76,10 @@ int cpu_restore_state_copy(struct TranslationBlock *tb, CPUState *env, unsigned long searched_pc, void *puc); void cpu_resume_from_signal(CPUState *env1, void *puc); void cpu_io_recompile(CPUState *env, void *retaddr); TranslationBlock *tb_gen_code(CPUState *env, target_ulong pc, target_ulong cs_base, int flags, int cflags); void cpu_exec_init(CPUState *env); int page_unprotect(target_ulong address, unsigned long pc, void *puc); void tb_invalidate_phys_page_range(target_phys_addr_t start, target_phys_addr_t end, Loading Loading @@ -117,16 +122,15 @@ static inline int tlb_set_page(CPUState *env1, target_ulong vaddr, #define USE_DIRECT_JUMP #endif typedef struct TranslationBlock { struct TranslationBlock { target_ulong pc; /* simulated PC corresponding to this block (EIP + CS base) */ target_ulong cs_base; /* CS base for this block */ uint64_t flags; /* flags defining in which context the code was generated */ uint16_t size; /* size of target code for this block (1 <= size <= TARGET_PAGE_SIZE) */ uint16_t cflags; /* compile flags */ #define CF_TB_FP_USED 0x0002 /* fp ops are used in the TB */ #define CF_FP_USED 0x0004 /* fp ops are used in the TB or in a chained TB */ #define CF_SINGLE_INSN 0x0008 /* compile only a single instruction */ #define CF_COUNT_MASK 0x7fff #define CF_LAST_IO 0x8000 /* Last insn may be an IO access. */ uint8_t *tc_ptr; /* pointer to the translated code */ /* next matching tb for physical address. */ Loading @@ -150,7 +154,8 @@ typedef struct TranslationBlock { jmp_first */ struct TranslationBlock *jmp_next[2]; struct TranslationBlock *jmp_first; } TranslationBlock; uint32_t icount; }; static inline unsigned int tb_jmp_cache_hash_page(target_ulong pc) { Loading @@ -173,9 +178,11 @@ static inline unsigned int tb_phys_hash_func(unsigned long pc) } TranslationBlock *tb_alloc(target_ulong pc); void tb_free(TranslationBlock *tb); void tb_flush(CPUState *env); void tb_link_phys(TranslationBlock *tb, target_ulong phys_pc, target_ulong phys_page2); void tb_phys_invalidate(TranslationBlock *tb, target_ulong page_addr); extern TranslationBlock *tb_phys_hash[CODE_GEN_PHYS_HASH_SIZE]; extern uint8_t *code_gen_ptr; Loading Loading @@ -364,6 +371,20 @@ static inline target_ulong get_phys_addr_code(CPUState *env1, target_ulong addr) } return addr + env1->tlb_table[mmu_idx][page_index].addend - (unsigned long)phys_ram_base; } /* Deterministic execution requires that IO only be performaed on the last instruction of a TB so that interrupts take effect immediately. */ static inline int can_do_io(CPUState *env) { if (!use_icount) return 1; /* If not executing code then assume we are ok. */ if (!env->current_tb) return 1; return env->can_do_io != 0; } #endif #ifdef USE_KQEMU Loading
exec.c +118 −29 Original line number Diff line number Diff line Loading @@ -107,6 +107,13 @@ CPUState *first_cpu; /* current CPU in the current thread. It is only valid inside cpu_exec() */ CPUState *cpu_single_env; /* 0 = Do not count executed instructions. 1 = Precice instruction counting. 2 = Adaptive rate instruction counting. */ int use_icount = 0; /* Current instruction counter. While executing translated code this may include some instructions that have not yet been executed. */ int64_t qemu_icount; typedef struct PageDesc { /* list of TBs intersecting this ram page */ Loading Loading @@ -633,7 +640,7 @@ static inline void tb_reset_jump(TranslationBlock *tb, int n) tb_set_jmp_target(tb, n, (unsigned long)(tb->tc_ptr + tb->tb_next_offset[n])); } static inline void tb_phys_invalidate(TranslationBlock *tb, target_ulong page_addr) void tb_phys_invalidate(TranslationBlock *tb, target_ulong page_addr) { CPUState *env; PageDesc *p; Loading Loading @@ -746,11 +753,9 @@ static void build_page_bitmap(PageDesc *p) } } #ifdef TARGET_HAS_PRECISE_SMC static void tb_gen_code(CPUState *env, target_ulong pc, target_ulong cs_base, int flags, int cflags) TranslationBlock *tb_gen_code(CPUState *env, target_ulong pc, target_ulong cs_base, int flags, int cflags) { TranslationBlock *tb; uint8_t *tc_ptr; Loading @@ -764,6 +769,8 @@ static void tb_gen_code(CPUState *env, 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; Loading @@ -780,8 +787,8 @@ static void tb_gen_code(CPUState *env, phys_page2 = get_phys_addr_code(env, virt_page2); } tb_link_phys(tb, phys_pc, phys_page2); return tb; } #endif /* invalidate all TBs which intersect with the target physical page starting in range [start;end[. NOTE: start and end must refer to Loading Loading @@ -836,13 +843,13 @@ void tb_invalidate_phys_page_range(target_phys_addr_t start, target_phys_addr_t if (current_tb_not_found) { current_tb_not_found = 0; current_tb = NULL; if (env->mem_write_pc) { if (env->mem_io_pc) { /* now we have a real cpu fault */ current_tb = tb_find_pc(env->mem_write_pc); current_tb = tb_find_pc(env->mem_io_pc); } } if (current_tb == tb && !(current_tb->cflags & CF_SINGLE_INSN)) { (current_tb->cflags & CF_COUNT_MASK) != 1) { /* If we are modifying the current TB, we must stop its execution. We could be more precise by checking that the modification is after the current PC, but it Loading @@ -851,7 +858,7 @@ void tb_invalidate_phys_page_range(target_phys_addr_t start, target_phys_addr_t current_tb_modified = 1; cpu_restore_state(current_tb, env, env->mem_write_pc, NULL); env->mem_io_pc, NULL); #if defined(TARGET_I386) current_flags = env->hflags; current_flags |= (env->eflags & (IOPL_MASK | TF_MASK | VM_MASK)); Loading Loading @@ -883,7 +890,7 @@ void tb_invalidate_phys_page_range(target_phys_addr_t start, target_phys_addr_t if (!p->first_tb) { invalidate_page_bitmap(p); if (is_cpu_write_access) { tlb_unprotect_code_phys(env, start, env->mem_write_vaddr); tlb_unprotect_code_phys(env, start, env->mem_io_vaddr); } } #endif Loading @@ -893,8 +900,7 @@ void tb_invalidate_phys_page_range(target_phys_addr_t start, target_phys_addr_t modifying the memory. It will ensure that it cannot modify itself */ env->current_tb = NULL; tb_gen_code(env, current_pc, current_cs_base, current_flags, CF_SINGLE_INSN); tb_gen_code(env, current_pc, current_cs_base, current_flags, 1); cpu_resume_from_signal(env, NULL); } #endif Loading @@ -909,7 +915,7 @@ static inline void tb_invalidate_phys_page_fast(target_phys_addr_t start, int le if (1) { if (loglevel) { fprintf(logfile, "modifying code at 0x%x size=%d EIP=%x PC=%08x\n", cpu_single_env->mem_write_vaddr, len, cpu_single_env->mem_io_vaddr, len, cpu_single_env->eip, cpu_single_env->eip + (long)cpu_single_env->segs[R_CS].base); } Loading Loading @@ -961,7 +967,7 @@ static void tb_invalidate_phys_page(target_phys_addr_t addr, tb = (TranslationBlock *)((long)tb & ~3); #ifdef TARGET_HAS_PRECISE_SMC if (current_tb == tb && !(current_tb->cflags & CF_SINGLE_INSN)) { (current_tb->cflags & CF_COUNT_MASK) != 1) { /* If we are modifying the current TB, we must stop its execution. We could be more precise by checking that the modification is after the current PC, but it Loading Loading @@ -990,8 +996,7 @@ static void tb_invalidate_phys_page(target_phys_addr_t addr, modifying the memory. It will ensure that it cannot modify itself */ env->current_tb = NULL; tb_gen_code(env, current_pc, current_cs_base, current_flags, CF_SINGLE_INSN); tb_gen_code(env, current_pc, current_cs_base, current_flags, 1); cpu_resume_from_signal(env, puc); } #endif Loading Loading @@ -1068,6 +1073,17 @@ TranslationBlock *tb_alloc(target_ulong pc) return tb; } void tb_free(TranslationBlock *tb) { /* In practice this is mostly used for single use temorary TB Ignore the hard cases and just back up if this TB happens to be the last one generated. */ if (nb_tbs > 0 && tb == &tbs[nb_tbs - 1]) { code_gen_ptr = tb->tc_ptr; nb_tbs--; } } /* add a new TB and link it to the physical page tables. phys_page2 is (-1) to indicate that only one page contains the TB. */ void tb_link_phys(TranslationBlock *tb, Loading Loading @@ -1369,7 +1385,9 @@ void cpu_interrupt(CPUState *env, int mask) TranslationBlock *tb; static spinlock_t interrupt_lock = SPIN_LOCK_UNLOCKED; #endif int old_mask; old_mask = env->interrupt_request; /* FIXME: This is probably not threadsafe. A different thread could be in the mittle of a read-modify-write operation. */ env->interrupt_request |= mask; Loading @@ -1379,14 +1397,26 @@ void cpu_interrupt(CPUState *env, int mask) emulation this often isn't actually as bad as it sounds. Often signals are used primarily to interrupt blocking syscalls. */ #else if (use_icount) { env->icount_decr.u16.high = 0x8000; #ifndef CONFIG_USER_ONLY /* CPU_INTERRUPT_EXIT isn't a real interrupt. It just means an async event happened and we need to process it. */ if (!can_do_io(env) && (mask & ~(old_mask | CPU_INTERRUPT_EXIT)) != 0) { cpu_abort(env, "Raised interrupt while not in I/O function"); } #endif } else { tb = env->current_tb; /* if the cpu is currently executing code, we must unlink it and all the potentially executing TB */ tb = env->current_tb; if (tb && !testandset(&interrupt_lock)) { env->current_tb = NULL; tb_reset_jump_recursive(tb); resetlock(&interrupt_lock); } } #endif } Loading Loading @@ -2227,7 +2257,7 @@ static void notdirty_mem_writeb(void *opaque, target_phys_addr_t ram_addr, /* we remove the notdirty callback only if the code has been flushed */ if (dirty_flags == 0xff) tlb_set_dirty(cpu_single_env, cpu_single_env->mem_write_vaddr); tlb_set_dirty(cpu_single_env, cpu_single_env->mem_io_vaddr); } static void notdirty_mem_writew(void *opaque, target_phys_addr_t ram_addr, Loading @@ -2252,7 +2282,7 @@ static void notdirty_mem_writew(void *opaque, target_phys_addr_t ram_addr, /* we remove the notdirty callback only if the code has been flushed */ if (dirty_flags == 0xff) tlb_set_dirty(cpu_single_env, cpu_single_env->mem_write_vaddr); tlb_set_dirty(cpu_single_env, cpu_single_env->mem_io_vaddr); } static void notdirty_mem_writel(void *opaque, target_phys_addr_t ram_addr, Loading @@ -2277,7 +2307,7 @@ static void notdirty_mem_writel(void *opaque, target_phys_addr_t ram_addr, /* we remove the notdirty callback only if the code has been flushed */ if (dirty_flags == 0xff) tlb_set_dirty(cpu_single_env, cpu_single_env->mem_write_vaddr); tlb_set_dirty(cpu_single_env, cpu_single_env->mem_io_vaddr); } static CPUReadMemoryFunc *error_mem_read[3] = { Loading @@ -2299,7 +2329,7 @@ static void check_watchpoint(int offset, int flags) target_ulong vaddr; int i; vaddr = (env->mem_write_vaddr & TARGET_PAGE_MASK) + offset; vaddr = (env->mem_io_vaddr & TARGET_PAGE_MASK) + offset; for (i = 0; i < env->nb_watchpoints; i++) { if (vaddr == env->watchpoint[i].vaddr && (env->watchpoint[i].type & flags)) { Loading Loading @@ -2967,6 +2997,65 @@ int cpu_memory_rw_debug(CPUState *env, target_ulong addr, return 0; } /* in deterministic execution mode, instructions doing device I/Os must be at the end of the TB */ void cpu_io_recompile(CPUState *env, void *retaddr) { TranslationBlock *tb; uint32_t n, cflags; target_ulong pc, cs_base; uint64_t flags; tb = tb_find_pc((unsigned long)retaddr); if (!tb) { cpu_abort(env, "cpu_io_recompile: could not find TB for pc=%p", retaddr); } n = env->icount_decr.u16.low + tb->icount; cpu_restore_state(tb, env, (unsigned long)retaddr, NULL); /* Calculate how many instructions had been executed before the fault occured. */ n = n - env->icount_decr.u16.low; /* Generate a new TB ending on the I/O insn. */ n++; /* On MIPS and SH, delay slot instructions can only be restarted if they were already the first instruction in the TB. If this is not the first instruction in a TB then re-execute the preceeding branch. */ #if defined(TARGET_MIPS) if ((env->hflags & MIPS_HFLAG_BMASK) != 0 && n > 1) { env->active_tc.PC -= 4; env->icount_decr.u16.low++; env->hflags &= ~MIPS_HFLAG_BMASK; } #elif defined(TARGET_SH4) if ((env->flags & ((DELAY_SLOT | DELAY_SLOT_CONDITIONAL))) != 0 && n > 1) { env->pc -= 2; env->icount_decr.u16.low++; env->flags &= ~(DELAY_SLOT | DELAY_SLOT_CONDITIONAL); } #endif /* This should never happen. */ if (n > CF_COUNT_MASK) cpu_abort(env, "TB too big during recompile"); cflags = n | CF_LAST_IO; pc = tb->pc; cs_base = tb->cs_base; flags = tb->flags; tb_phys_invalidate(tb, -1); /* FIXME: In theory this could raise an exception. In practice we have already translated the block once so it's probably ok. */ tb_gen_code(env, pc, cs_base, flags, cflags); /* TODO: If env->pc != tb->pc (i.e. the failuting instruction was not the first in the TB) then we end up generating a whole new TB and repeating the fault, which is horribly inefficient. Better would be to execute just this insn uncached, or generate a second new TB. */ cpu_resume_from_signal(env, NULL); } void dump_exec_info(FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...)) { Loading