X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=exec.c;h=d6fa9778fc411edd8efd8d4baaec2f924093252e;hb=c2b3b41a0ba22b22e199f0a53830f337989db9fd;hp=48600e49a978556d2bdd1e57cef0faa3d542f6e6;hpb=b2a7081acb46b0cdcbec0346672d86fc77ebfeb5;p=mirror_qemu.git diff --git a/exec.c b/exec.c index 48600e49a9..d6fa9778fc 100644 --- a/exec.c +++ b/exec.c @@ -15,7 +15,7 @@ * * 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" #ifdef _WIN32 @@ -37,6 +37,9 @@ #include "exec-all.h" #include "qemu-common.h" #include "tcg.h" +#include "hw/hw.h" +#include "osdep.h" +#include "kvm.h" #if defined(CONFIG_USER_ONLY) #include #endif @@ -81,18 +84,30 @@ #define TARGET_PHYS_ADDR_SPACE_BITS 32 #endif -TranslationBlock *tbs; +static TranslationBlock *tbs; int code_gen_max_blocks; TranslationBlock *tb_phys_hash[CODE_GEN_PHYS_HASH_SIZE]; -int nb_tbs; +static int nb_tbs; /* any access to the tbs or the page table must use this lock */ spinlock_t tb_lock = SPIN_LOCK_UNLOCKED; -uint8_t code_gen_prologue[1024] __attribute__((aligned (32))); -uint8_t *code_gen_buffer; -unsigned long code_gen_buffer_size; +#if defined(__arm__) || defined(__sparc_v9__) +/* The prologue must be reachable with a direct jump. ARM and Sparc64 + have limited branch ranges (possibly also PPC) so place it in a + section close to code segment. */ +#define code_gen_section \ + __attribute__((__section__(".gen_code"))) \ + __attribute__((aligned (32))) +#else +#define code_gen_section \ + __attribute__((aligned (32))) +#endif + +uint8_t code_gen_prologue[1024] code_gen_section; +static uint8_t *code_gen_buffer; +static unsigned long code_gen_buffer_size; /* threshold to flush the translated code buffer */ -unsigned long code_gen_buffer_max_size; +static unsigned long code_gen_buffer_max_size; uint8_t *code_gen_ptr; #if !defined(CONFIG_USER_ONLY) @@ -100,6 +115,7 @@ ram_addr_t phys_ram_size; int phys_ram_fd; uint8_t *phys_ram_base; uint8_t *phys_ram_dirty; +static int in_migration; static ram_addr_t phys_ram_alloc_offset = 0; #endif @@ -107,6 +123,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 = Precise 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 */ @@ -123,6 +146,7 @@ typedef struct PageDesc { typedef struct PhysPageDesc { /* offset in host memory of the page + io_index in the low bits */ ram_addr_t phys_offset; + ram_addr_t region_offset; } PhysPageDesc; #define L2_BITS 10 @@ -146,7 +170,7 @@ unsigned long qemu_host_page_mask; /* XXX: for system emulation, it could just be an array */ static PageDesc *l1_map[L1_SIZE]; -PhysPageDesc **l1_phys_map; +static PhysPageDesc **l1_phys_map; #if !defined(CONFIG_USER_ONLY) static void io_mem_init(void); @@ -160,7 +184,7 @@ static int io_mem_watch; #endif /* log support */ -char *logfilename = "/tmp/qemu.log"; +static const char *logfilename = "/tmp/qemu.log"; FILE *logfile; int loglevel; static int log_append = 0; @@ -176,6 +200,7 @@ typedef struct subpage_t { CPUReadMemoryFunc **mem_read[TARGET_PAGE_SIZE][4]; CPUWriteMemoryFunc **mem_write[TARGET_PAGE_SIZE][4]; void *opaque[TARGET_PAGE_SIZE][2][4]; + ram_addr_t region_offset[TARGET_PAGE_SIZE][2][4]; } subpage_t; #ifdef _WIN32 @@ -211,7 +236,6 @@ static void page_init(void) #ifdef _WIN32 { SYSTEM_INFO system_info; - DWORD old_protect; GetSystemInfo(&system_info); qemu_real_host_page_size = system_info.dwPageSize; @@ -259,29 +283,35 @@ static void page_init(void) #endif } -static inline PageDesc *page_find_alloc(target_ulong index) +static inline PageDesc **page_l1_map(target_ulong index) { - PageDesc **lp, *p; - #if TARGET_LONG_BITS > 32 /* Host memory outside guest VM. For 32-bit targets we have already excluded high addresses. */ - if (index > ((target_ulong)L2_SIZE * L1_SIZE * TARGET_PAGE_SIZE)) + if (index > ((target_ulong)L2_SIZE * L1_SIZE)) return NULL; #endif - lp = &l1_map[index >> L2_BITS]; + return &l1_map[index >> L2_BITS]; +} + +static inline PageDesc *page_find_alloc(target_ulong index) +{ + PageDesc **lp, *p; + lp = page_l1_map(index); + if (!lp) + return NULL; + p = *lp; if (!p) { /* allocate if not found */ #if defined(CONFIG_USER_ONLY) - unsigned long addr; size_t len = sizeof(PageDesc) * L2_SIZE; /* Don't use qemu_malloc because it may recurse. */ p = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); *lp = p; - addr = h2g(p); - if (addr == (target_ulong)addr) { + if (h2g_valid(p)) { + unsigned long addr = h2g(p); page_set_flags(addr & TARGET_PAGE_MASK, TARGET_PAGE_ALIGN(addr + len), PAGE_RESERVED); @@ -296,9 +326,12 @@ static inline PageDesc *page_find_alloc(target_ulong index) static inline PageDesc *page_find(target_ulong index) { - PageDesc *p; + PageDesc **lp, *p; + lp = page_l1_map(index); + if (!lp) + return NULL; - p = l1_map[index >> L2_BITS]; + p = *lp; if (!p) return 0; return p + (index & (L2_SIZE - 1)); @@ -366,7 +399,7 @@ static void tlb_unprotect_code_phys(CPUState *env, ram_addr_t ram_addr, static uint8_t static_code_gen_buffer[DEFAULT_CODE_GEN_BUFFER_SIZE]; #endif -void code_gen_alloc(unsigned long tb_size) +static void code_gen_alloc(unsigned long tb_size) { #ifdef USE_STATIC_CODE_GEN_BUFFER code_gen_buffer = static_code_gen_buffer; @@ -380,7 +413,7 @@ void code_gen_alloc(unsigned long tb_size) code_gen_buffer_size = DEFAULT_CODE_GEN_BUFFER_SIZE; #else /* XXX: needs ajustments */ - code_gen_buffer_size = (int)(phys_ram_size / 4); + code_gen_buffer_size = (unsigned long)(phys_ram_size / 4); #endif } if (code_gen_buffer_size < MIN_CODE_GEN_BUFFER_SIZE) @@ -390,14 +423,50 @@ void code_gen_alloc(unsigned long tb_size) #if defined(__linux__) { int flags; + void *start = NULL; + flags = MAP_PRIVATE | MAP_ANONYMOUS; #if defined(__x86_64__) flags |= MAP_32BIT; /* Cannot map more than that */ if (code_gen_buffer_size > (800 * 1024 * 1024)) code_gen_buffer_size = (800 * 1024 * 1024); +#elif defined(__sparc_v9__) + // Map the buffer below 2G, so we can use direct calls and branches + flags |= MAP_FIXED; + start = (void *) 0x60000000UL; + if (code_gen_buffer_size > (512 * 1024 * 1024)) + code_gen_buffer_size = (512 * 1024 * 1024); +#elif defined(__arm__) + /* Map the buffer below 32M, so we can use direct calls and branches */ + flags |= MAP_FIXED; + start = (void *) 0x01000000UL; + if (code_gen_buffer_size > 16 * 1024 * 1024) + code_gen_buffer_size = 16 * 1024 * 1024; +#endif + code_gen_buffer = mmap(start, code_gen_buffer_size, + PROT_WRITE | PROT_READ | PROT_EXEC, + flags, -1, 0); + if (code_gen_buffer == MAP_FAILED) { + fprintf(stderr, "Could not allocate dynamic translator buffer\n"); + exit(1); + } + } +#elif defined(__FreeBSD__) + { + int flags; + void *addr = NULL; + flags = MAP_PRIVATE | MAP_ANONYMOUS; +#if defined(__x86_64__) + /* FreeBSD doesn't have MAP_32BIT, use MAP_FIXED and assume + * 0x40000000 is free */ + flags |= MAP_FIXED; + addr = (void *)0x40000000; + /* Cannot map more than that */ + if (code_gen_buffer_size > (800 * 1024 * 1024)) + code_gen_buffer_size = (800 * 1024 * 1024); #endif - code_gen_buffer = mmap(NULL, code_gen_buffer_size, + code_gen_buffer = mmap(addr, code_gen_buffer_size, PROT_WRITE | PROT_READ | PROT_EXEC, flags, -1, 0); if (code_gen_buffer == MAP_FAILED) { @@ -435,6 +504,33 @@ void cpu_exec_init_all(unsigned long tb_size) #endif } +#if defined(CPU_SAVE_VERSION) && !defined(CONFIG_USER_ONLY) + +#define CPU_COMMON_SAVE_VERSION 1 + +static void cpu_common_save(QEMUFile *f, void *opaque) +{ + CPUState *env = opaque; + + qemu_put_be32s(f, &env->halted); + qemu_put_be32s(f, &env->interrupt_request); +} + +static int cpu_common_load(QEMUFile *f, void *opaque, int version_id) +{ + CPUState *env = opaque; + + if (version_id != CPU_COMMON_SAVE_VERSION) + return -EINVAL; + + qemu_get_be32s(f, &env->halted); + qemu_get_be32s(f, &env->interrupt_request); + tlb_flush(env, 1); + + return 0; +} +#endif + void cpu_exec_init(CPUState *env) { CPUState **penv; @@ -448,8 +544,15 @@ void cpu_exec_init(CPUState *env) cpu_index++; } env->cpu_index = cpu_index; - env->nb_watchpoints = 0; + TAILQ_INIT(&env->breakpoints); + TAILQ_INIT(&env->watchpoints); *penv = env; +#if defined(CPU_SAVE_VERSION) && !defined(CONFIG_USER_ONLY) + register_savevm("cpu_common", cpu_index, CPU_COMMON_SAVE_VERSION, + cpu_common_save, cpu_common_load, env); + register_savevm("cpu", cpu_index, CPU_SAVE_VERSION, + cpu_save, cpu_load, env); +#endif } static inline void invalidate_page_bitmap(PageDesc *p) @@ -544,7 +647,7 @@ static void tb_page_check(void) } } -void tb_jmp_check(TranslationBlock *tb) +static void tb_jmp_check(TranslationBlock *tb) { TranslationBlock *tb1; unsigned int n1; @@ -633,7 +736,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; @@ -746,11 +849,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; @@ -764,6 +865,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; @@ -780,8 +883,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 @@ -791,12 +894,19 @@ static void tb_gen_code(CPUState *env, void tb_invalidate_phys_page_range(target_phys_addr_t start, target_phys_addr_t end, int is_cpu_write_access) { - int n, current_tb_modified, current_tb_not_found, current_flags; + TranslationBlock *tb, *tb_next, *saved_tb; CPUState *env = cpu_single_env; - PageDesc *p; - TranslationBlock *tb, *tb_next, *current_tb, *saved_tb; target_ulong tb_start, tb_end; - target_ulong current_pc, current_cs_base; + PageDesc *p; + int n; +#ifdef TARGET_HAS_PRECISE_SMC + int current_tb_not_found = is_cpu_write_access; + TranslationBlock *current_tb = NULL; + int current_tb_modified = 0; + target_ulong current_pc = 0; + target_ulong current_cs_base = 0; + int current_flags = 0; +#endif /* TARGET_HAS_PRECISE_SMC */ p = page_find(start >> TARGET_PAGE_BITS); if (!p) @@ -810,12 +920,6 @@ void tb_invalidate_phys_page_range(target_phys_addr_t start, target_phys_addr_t /* we remove all the TBs in the range [start, end[ */ /* XXX: see if in some cases it could be faster to invalidate all the code */ - current_tb_not_found = is_cpu_write_access; - current_tb_modified = 0; - current_tb = NULL; /* avoid warning */ - current_pc = 0; /* avoid warning */ - current_cs_base = 0; /* avoid warning */ - current_flags = 0; /* avoid warning */ tb = p->first_tb; while (tb != NULL) { n = (long)tb & 3; @@ -836,13 +940,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 @@ -851,15 +955,9 @@ 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); -#if defined(TARGET_I386) - current_flags = env->hflags; - current_flags |= (env->eflags & (IOPL_MASK | TF_MASK | VM_MASK)); - current_cs_base = (target_ulong)env->segs[R_CS].base; - current_pc = current_cs_base + env->eip; -#else -#error unsupported CPU -#endif + env->mem_io_pc, NULL); + cpu_get_tb_cpu_state(env, ¤t_pc, ¤t_cs_base, + ¤t_flags); } #endif /* TARGET_HAS_PRECISE_SMC */ /* we need to do that to handle the case where a signal @@ -883,7 +981,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 @@ -893,8 +991,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 @@ -909,7 +1006,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); } @@ -933,12 +1030,16 @@ static inline void tb_invalidate_phys_page_fast(target_phys_addr_t start, int le static void tb_invalidate_phys_page(target_phys_addr_t addr, unsigned long pc, void *puc) { - int n, current_flags, current_tb_modified; - target_ulong current_pc, current_cs_base; + TranslationBlock *tb; PageDesc *p; - TranslationBlock *tb, *current_tb; + int n; #ifdef TARGET_HAS_PRECISE_SMC + TranslationBlock *current_tb = NULL; CPUState *env = cpu_single_env; + int current_tb_modified = 0; + target_ulong current_pc = 0; + target_ulong current_cs_base = 0; + int current_flags = 0; #endif addr &= TARGET_PAGE_MASK; @@ -946,11 +1047,6 @@ static void tb_invalidate_phys_page(target_phys_addr_t addr, if (!p) return; tb = p->first_tb; - current_tb_modified = 0; - current_tb = NULL; - current_pc = 0; /* avoid warning */ - current_cs_base = 0; /* avoid warning */ - current_flags = 0; /* avoid warning */ #ifdef TARGET_HAS_PRECISE_SMC if (tb && pc != 0) { current_tb = tb_find_pc(pc); @@ -961,7 +1057,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 @@ -970,14 +1066,8 @@ static void tb_invalidate_phys_page(target_phys_addr_t addr, current_tb_modified = 1; cpu_restore_state(current_tb, env, pc, puc); -#if defined(TARGET_I386) - current_flags = env->hflags; - current_flags |= (env->eflags & (IOPL_MASK | TF_MASK | VM_MASK)); - current_cs_base = (target_ulong)env->segs[R_CS].base; - current_pc = current_cs_base + env->eip; -#else -#error unsupported CPU -#endif + cpu_get_tb_cpu_state(env, ¤t_pc, ¤t_cs_base, + ¤t_flags); } #endif /* TARGET_HAS_PRECISE_SMC */ tb_phys_invalidate(tb, addr); @@ -990,8 +1080,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 @@ -1068,6 +1157,17 @@ TranslationBlock *tb_alloc(target_ulong pc) return tb; } +void tb_free(TranslationBlock *tb) +{ + /* In practice this is mostly used for single use temporary 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, @@ -1207,107 +1307,147 @@ static void breakpoint_invalidate(CPUState *env, target_ulong pc) #endif /* Add a watchpoint. */ -int cpu_watchpoint_insert(CPUState *env, target_ulong addr, int type) +int cpu_watchpoint_insert(CPUState *env, target_ulong addr, target_ulong len, + int flags, CPUWatchpoint **watchpoint) { - int i; + target_ulong len_mask = ~(len - 1); + CPUWatchpoint *wp; - for (i = 0; i < env->nb_watchpoints; i++) { - if (addr == env->watchpoint[i].vaddr) - return 0; + /* sanity checks: allow power-of-2 lengths, deny unaligned watchpoints */ + if ((len != 1 && len != 2 && len != 4 && len != 8) || (addr & ~len_mask)) { + fprintf(stderr, "qemu: tried to set invalid watchpoint at " + TARGET_FMT_lx ", len=" TARGET_FMT_lu "\n", addr, len); + return -EINVAL; } - if (env->nb_watchpoints >= MAX_WATCHPOINTS) - return -1; + wp = qemu_malloc(sizeof(*wp)); + if (!wp) + return -ENOMEM; + + wp->vaddr = addr; + wp->len_mask = len_mask; + wp->flags = flags; + + /* keep all GDB-injected watchpoints in front */ + if (flags & BP_GDB) + TAILQ_INSERT_HEAD(&env->watchpoints, wp, entry); + else + TAILQ_INSERT_TAIL(&env->watchpoints, wp, entry); - i = env->nb_watchpoints++; - env->watchpoint[i].vaddr = addr; - env->watchpoint[i].type = type; tlb_flush_page(env, addr); - /* FIXME: This flush is needed because of the hack to make memory ops - terminate the TB. It can be removed once the proper IO trap and - re-execute bits are in. */ - tb_flush(env); - return i; + + if (watchpoint) + *watchpoint = wp; + return 0; } -/* Remove a watchpoint. */ -int cpu_watchpoint_remove(CPUState *env, target_ulong addr) +/* Remove a specific watchpoint. */ +int cpu_watchpoint_remove(CPUState *env, target_ulong addr, target_ulong len, + int flags) { - int i; + target_ulong len_mask = ~(len - 1); + CPUWatchpoint *wp; - for (i = 0; i < env->nb_watchpoints; i++) { - if (addr == env->watchpoint[i].vaddr) { - env->nb_watchpoints--; - env->watchpoint[i] = env->watchpoint[env->nb_watchpoints]; - tlb_flush_page(env, addr); + TAILQ_FOREACH(wp, &env->watchpoints, entry) { + if (addr == wp->vaddr && len_mask == wp->len_mask + && flags == (wp->flags & ~BP_WATCHPOINT_HIT)) { + cpu_watchpoint_remove_by_ref(env, wp); return 0; } } - return -1; + return -ENOENT; } -/* Remove all watchpoints. */ -void cpu_watchpoint_remove_all(CPUState *env) { - int i; +/* Remove a specific watchpoint by reference. */ +void cpu_watchpoint_remove_by_ref(CPUState *env, CPUWatchpoint *watchpoint) +{ + TAILQ_REMOVE(&env->watchpoints, watchpoint, entry); + + tlb_flush_page(env, watchpoint->vaddr); - for (i = 0; i < env->nb_watchpoints; i++) { - tlb_flush_page(env, env->watchpoint[i].vaddr); + qemu_free(watchpoint); +} + +/* Remove all matching watchpoints. */ +void cpu_watchpoint_remove_all(CPUState *env, int mask) +{ + CPUWatchpoint *wp, *next; + + TAILQ_FOREACH_SAFE(wp, &env->watchpoints, entry, next) { + if (wp->flags & mask) + cpu_watchpoint_remove_by_ref(env, wp); } - env->nb_watchpoints = 0; } -/* add a breakpoint. EXCP_DEBUG is returned by the CPU loop if a - breakpoint is reached */ -int cpu_breakpoint_insert(CPUState *env, target_ulong pc) +/* Add a breakpoint. */ +int cpu_breakpoint_insert(CPUState *env, target_ulong pc, int flags, + CPUBreakpoint **breakpoint) { #if defined(TARGET_HAS_ICE) - int i; + CPUBreakpoint *bp; - for(i = 0; i < env->nb_breakpoints; i++) { - if (env->breakpoints[i] == pc) - return 0; - } + bp = qemu_malloc(sizeof(*bp)); + if (!bp) + return -ENOMEM; - if (env->nb_breakpoints >= MAX_BREAKPOINTS) - return -1; - env->breakpoints[env->nb_breakpoints++] = pc; + bp->pc = pc; + bp->flags = flags; + + /* keep all GDB-injected breakpoints in front */ + if (flags & BP_GDB) + TAILQ_INSERT_HEAD(&env->breakpoints, bp, entry); + else + TAILQ_INSERT_TAIL(&env->breakpoints, bp, entry); breakpoint_invalidate(env, pc); + + if (breakpoint) + *breakpoint = bp; return 0; #else - return -1; + return -ENOSYS; #endif } -/* remove all breakpoints */ -void cpu_breakpoint_remove_all(CPUState *env) { +/* Remove a specific breakpoint. */ +int cpu_breakpoint_remove(CPUState *env, target_ulong pc, int flags) +{ #if defined(TARGET_HAS_ICE) - int i; - for(i = 0; i < env->nb_breakpoints; i++) { - breakpoint_invalidate(env, env->breakpoints[i]); + CPUBreakpoint *bp; + + TAILQ_FOREACH(bp, &env->breakpoints, entry) { + if (bp->pc == pc && bp->flags == flags) { + cpu_breakpoint_remove_by_ref(env, bp); + return 0; + } } - env->nb_breakpoints = 0; + return -ENOENT; +#else + return -ENOSYS; #endif } -/* remove a breakpoint */ -int cpu_breakpoint_remove(CPUState *env, target_ulong pc) +/* Remove a specific breakpoint by reference. */ +void cpu_breakpoint_remove_by_ref(CPUState *env, CPUBreakpoint *breakpoint) { #if defined(TARGET_HAS_ICE) - int i; - for(i = 0; i < env->nb_breakpoints; i++) { - if (env->breakpoints[i] == pc) - goto found; - } - return -1; - found: - env->nb_breakpoints--; - if (i < env->nb_breakpoints) - env->breakpoints[i] = env->breakpoints[env->nb_breakpoints]; + TAILQ_REMOVE(&env->breakpoints, breakpoint, entry); - breakpoint_invalidate(env, pc); - return 0; -#else - return -1; + breakpoint_invalidate(env, breakpoint->pc); + + qemu_free(breakpoint); +#endif +} + +/* Remove all matching breakpoints. */ +void cpu_breakpoint_remove_all(CPUState *env, int mask) +{ +#if defined(TARGET_HAS_ICE) + CPUBreakpoint *bp, *next; + + TAILQ_FOREACH_SAFE(bp, &env->breakpoints, entry, next) { + if (bp->flags & mask) + cpu_breakpoint_remove_by_ref(env, bp); + } #endif } @@ -1338,7 +1478,7 @@ void cpu_set_log(int log_flags) #if !defined(CONFIG_SOFTMMU) /* must avoid mmap() usage of glibc by setting a buffer "by hand" */ { - static uint8_t logfile_buf[4096]; + static char logfile_buf[4096]; setvbuf(logfile, logfile_buf, _IOLBF, sizeof(logfile_buf)); } #else @@ -1369,9 +1509,11 @@ 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. */ + be in the middle of a read-modify-write operation. */ env->interrupt_request |= mask; #if defined(USE_NPTL) /* FIXME: TB unchaining isn't SMP safe. For now just ignore the @@ -1379,13 +1521,25 @@ 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 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); + if (use_icount) { + env->icount_decr.u16.high = 0xffff; +#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 */ + if (tb && !testandset(&interrupt_lock)) { + env->current_tb = NULL; + tb_reset_jump_recursive(tb); + resetlock(&interrupt_lock); + } } #endif } @@ -1395,7 +1549,7 @@ void cpu_reset_interrupt(CPUState *env, int mask) env->interrupt_request &= ~mask; } -CPULogItem cpu_log_items[] = { +const CPULogItem cpu_log_items[] = { { CPU_LOG_TB_OUT_ASM, "out_asm", "show generated host assembly code for each compiled TB" }, { CPU_LOG_TB_IN_ASM, "in_asm", @@ -1435,7 +1589,7 @@ static int cmp1(const char *s1, int n, const char *s2) /* takes a comma separated list of log masks. Return 0 if error. */ int cpu_str_to_log_mask(const char *str) { - CPULogItem *item; + const CPULogItem *item; int mask; const char *p, *p1; @@ -1500,12 +1654,34 @@ void cpu_abort(CPUState *env, const char *fmt, ...) CPUState *cpu_copy(CPUState *env) { CPUState *new_env = cpu_init(env->cpu_model_str); - /* preserve chaining and index */ CPUState *next_cpu = new_env->next_cpu; int cpu_index = new_env->cpu_index; +#if defined(TARGET_HAS_ICE) + CPUBreakpoint *bp; + CPUWatchpoint *wp; +#endif + memcpy(new_env, env, sizeof(CPUState)); + + /* Preserve chaining and index. */ new_env->next_cpu = next_cpu; new_env->cpu_index = cpu_index; + + /* Clone all break/watchpoints. + Note: Once we support ptrace with hw-debug register access, make sure + BP_CPU break/watchpoints are handled correctly on clone. */ + TAILQ_INIT(&env->breakpoints); + TAILQ_INIT(&env->watchpoints); +#if defined(TARGET_HAS_ICE) + TAILQ_FOREACH(bp, &env->breakpoints, entry) { + cpu_breakpoint_insert(new_env, bp->pc, bp->flags, NULL); + } + TAILQ_FOREACH(wp, &env->watchpoints, entry) { + cpu_watchpoint_insert(new_env, wp->vaddr, (~wp->len_mask) + 1, + wp->flags, NULL); + } +#endif + return new_env; } @@ -1693,6 +1869,23 @@ void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end, } } +int cpu_physical_memory_set_dirty_tracking(int enable) +{ + in_migration = enable; + return 0; +} + +int cpu_physical_memory_get_dirty_tracking(void) +{ + return in_migration; +} + +void cpu_physical_sync_dirty_bitmap(target_phys_addr_t start_addr, target_phys_addr_t end_addr) +{ + if (kvm_enabled()) + kvm_physical_sync_dirty_bitmap(start_addr, end_addr); +} + static inline void tlb_update_dirty(CPUTLBEntry *tlb_entry) { ram_addr_t ram_addr; @@ -1764,7 +1957,7 @@ int tlb_set_page_exec(CPUState *env, target_ulong vaddr, target_phys_addr_t addend; int ret; CPUTLBEntry *te; - int i; + CPUWatchpoint *wp; target_phys_addr_t iotlb; p = phys_page_find(paddr >> TARGET_PAGE_BITS); @@ -1799,14 +1992,19 @@ int tlb_set_page_exec(CPUState *env, target_ulong vaddr, and avoid full address decoding in every device. We can't use the high bits of pd for this because IO_MEM_ROMD uses these as a ram address. */ - iotlb = (pd & ~TARGET_PAGE_MASK) + paddr; + iotlb = (pd & ~TARGET_PAGE_MASK); + if (p) { + iotlb += p->region_offset; + } else { + iotlb += paddr; + } } code_address = address; /* Make accesses to pages with watchpoints go via the watchpoint trap routines. */ - for (i = 0; i < env->nb_watchpoints; i++) { - if (vaddr == (env->watchpoint[i].vaddr & TARGET_PAGE_MASK)) { + TAILQ_FOREACH(wp, &env->watchpoints, entry) { + if (vaddr == (wp->vaddr & TARGET_PAGE_MASK)) { iotlb = io_mem_watch + paddr; /* TODO: The memory case can be optimized by not trapping reads of pages with a write breakpoint. */ @@ -1952,12 +2150,13 @@ int page_check_range(target_ulong start, target_ulong len, int flags) target_ulong end; target_ulong addr; + if (start + len < start) + /* we've wrapped around */ + return -1; + end = TARGET_PAGE_ALIGN(start+len); /* must do before we loose bits in the next step */ start = start & TARGET_PAGE_MASK; - if( end < start ) - /* we've wrapped around */ - return -1; for(addr = start; addr < end; addr += TARGET_PAGE_SIZE) { p = page_find(addr >> TARGET_PAGE_BITS); if( !p ) @@ -2038,10 +2237,11 @@ static inline void tlb_set_dirty(CPUState *env, #endif /* defined(CONFIG_USER_ONLY) */ #if !defined(CONFIG_USER_ONLY) + static int subpage_register (subpage_t *mmio, uint32_t start, uint32_t end, - ram_addr_t memory); + ram_addr_t memory, ram_addr_t region_offset); static void *subpage_init (target_phys_addr_t base, ram_addr_t *phys, - ram_addr_t orig_memory); + ram_addr_t orig_memory, ram_addr_t region_offset); #define CHECK_SUBPAGE(addr, start_addr, start_addr2, end_addr, end_addr2, \ need_subpage) \ do { \ @@ -2064,10 +2264,15 @@ static void *subpage_init (target_phys_addr_t base, ram_addr_t *phys, /* register physical memory. 'size' must be a multiple of the target page size. If (phys_offset & ~TARGET_PAGE_MASK) != 0, then it is an - io memory page */ -void cpu_register_physical_memory(target_phys_addr_t start_addr, - ram_addr_t size, - ram_addr_t phys_offset) + io memory page. The address used when calling the IO function is + the offset from the start of the region, plus region_offset. Both + start_region and regon_offset are rounded down to a page boundary + before calculating this offset. This should not be a problem unless + the low bits of start_addr and region_offset differ. */ +void cpu_register_physical_memory_offset(target_phys_addr_t start_addr, + ram_addr_t size, + ram_addr_t phys_offset, + ram_addr_t region_offset) { target_phys_addr_t addr, end_addr; PhysPageDesc *p; @@ -2082,6 +2287,10 @@ void cpu_register_physical_memory(target_phys_addr_t start_addr, kqemu_set_phys_mem(start_addr, size, phys_offset); } #endif + if (kvm_enabled()) + kvm_set_phys_mem(start_addr, size, phys_offset); + + region_offset &= TARGET_PAGE_MASK; size = (size + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK; end_addr = start_addr + (target_phys_addr_t)size; for(addr = start_addr; addr != end_addr; addr += TARGET_PAGE_SIZE) { @@ -2096,12 +2305,15 @@ void cpu_register_physical_memory(target_phys_addr_t start_addr, if (need_subpage || phys_offset & IO_MEM_SUBWIDTH) { if (!(orig_memory & IO_MEM_SUBPAGE)) { subpage = subpage_init((addr & TARGET_PAGE_MASK), - &p->phys_offset, orig_memory); + &p->phys_offset, orig_memory, + p->region_offset); } else { subpage = io_mem_opaque[(orig_memory & ~TARGET_PAGE_MASK) >> IO_MEM_SHIFT]; } - subpage_register(subpage, start_addr2, end_addr2, phys_offset); + subpage_register(subpage, start_addr2, end_addr2, phys_offset, + region_offset); + p->region_offset = 0; } else { p->phys_offset = phys_offset; if ((phys_offset & ~TARGET_PAGE_MASK) <= IO_MEM_ROM || @@ -2111,10 +2323,11 @@ void cpu_register_physical_memory(target_phys_addr_t start_addr, } else { p = phys_page_find_alloc(addr >> TARGET_PAGE_BITS, 1); p->phys_offset = phys_offset; + p->region_offset = region_offset; if ((phys_offset & ~TARGET_PAGE_MASK) <= IO_MEM_ROM || - (phys_offset & IO_MEM_ROMD)) + (phys_offset & IO_MEM_ROMD)) { phys_offset += TARGET_PAGE_SIZE; - else { + } else { target_phys_addr_t start_addr2, end_addr2; int need_subpage = 0; @@ -2123,12 +2336,15 @@ void cpu_register_physical_memory(target_phys_addr_t start_addr, if (need_subpage || phys_offset & IO_MEM_SUBWIDTH) { subpage = subpage_init((addr & TARGET_PAGE_MASK), - &p->phys_offset, IO_MEM_UNASSIGNED); + &p->phys_offset, IO_MEM_UNASSIGNED, + 0); subpage_register(subpage, start_addr2, end_addr2, - phys_offset); + phys_offset, region_offset); + p->region_offset = 0; } } } + region_offset += TARGET_PAGE_SIZE; } /* since each CPU stores ram addresses in its TLB cache, we must @@ -2150,12 +2366,24 @@ ram_addr_t cpu_get_physical_page_desc(target_phys_addr_t addr) return p->phys_offset; } +void qemu_register_coalesced_mmio(target_phys_addr_t addr, ram_addr_t size) +{ + if (kvm_enabled()) + kvm_coalesce_mmio_region(addr, size); +} + +void qemu_unregister_coalesced_mmio(target_phys_addr_t addr, ram_addr_t size) +{ + if (kvm_enabled()) + kvm_uncoalesce_mmio_region(addr, size); +} + /* XXX: better than nothing */ ram_addr_t qemu_ram_alloc(ram_addr_t size) { ram_addr_t addr; if ((phys_ram_alloc_offset + size) > phys_ram_size) { - fprintf(stderr, "Not enough memory (requested_size = %" PRIu64 ", max memory = %" PRIu64 "\n", + fprintf(stderr, "Not enough memory (requested_size = %" PRIu64 ", max memory = %" PRIu64 ")\n", (uint64_t)size, (uint64_t)phys_ram_size); abort(); } @@ -2173,10 +2401,30 @@ static uint32_t unassigned_mem_readb(void *opaque, target_phys_addr_t addr) #ifdef DEBUG_UNASSIGNED printf("Unassigned mem read " TARGET_FMT_plx "\n", addr); #endif -#ifdef TARGET_SPARC - do_unassigned_access(addr, 0, 0, 0); -#elif TARGET_CRIS - do_unassigned_access(addr, 0, 0, 0); +#if defined(TARGET_SPARC) + do_unassigned_access(addr, 0, 0, 0, 1); +#endif + return 0; +} + +static uint32_t unassigned_mem_readw(void *opaque, target_phys_addr_t addr) +{ +#ifdef DEBUG_UNASSIGNED + printf("Unassigned mem read " TARGET_FMT_plx "\n", addr); +#endif +#if defined(TARGET_SPARC) + do_unassigned_access(addr, 0, 0, 0, 2); +#endif + return 0; +} + +static uint32_t unassigned_mem_readl(void *opaque, target_phys_addr_t addr) +{ +#ifdef DEBUG_UNASSIGNED + printf("Unassigned mem read " TARGET_FMT_plx "\n", addr); +#endif +#if defined(TARGET_SPARC) + do_unassigned_access(addr, 0, 0, 0, 4); #endif return 0; } @@ -2186,23 +2434,41 @@ static void unassigned_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_ #ifdef DEBUG_UNASSIGNED printf("Unassigned mem write " TARGET_FMT_plx " = 0x%x\n", addr, val); #endif -#ifdef TARGET_SPARC - do_unassigned_access(addr, 1, 0, 0); -#elif TARGET_CRIS - do_unassigned_access(addr, 1, 0, 0); +#if defined(TARGET_SPARC) + do_unassigned_access(addr, 1, 0, 0, 1); +#endif +} + +static void unassigned_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val) +{ +#ifdef DEBUG_UNASSIGNED + printf("Unassigned mem write " TARGET_FMT_plx " = 0x%x\n", addr, val); +#endif +#if defined(TARGET_SPARC) + do_unassigned_access(addr, 1, 0, 0, 2); +#endif +} + +static void unassigned_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) +{ +#ifdef DEBUG_UNASSIGNED + printf("Unassigned mem write " TARGET_FMT_plx " = 0x%x\n", addr, val); +#endif +#if defined(TARGET_SPARC) + do_unassigned_access(addr, 1, 0, 0, 4); #endif } static CPUReadMemoryFunc *unassigned_mem_read[3] = { unassigned_mem_readb, - unassigned_mem_readb, - unassigned_mem_readb, + unassigned_mem_readw, + unassigned_mem_readl, }; static CPUWriteMemoryFunc *unassigned_mem_write[3] = { unassigned_mem_writeb, - unassigned_mem_writeb, - unassigned_mem_writeb, + unassigned_mem_writew, + unassigned_mem_writel, }; static void notdirty_mem_writeb(void *opaque, target_phys_addr_t ram_addr, @@ -2227,7 +2493,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, @@ -2252,7 +2518,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, @@ -2277,7 +2543,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] = { @@ -2293,19 +2559,46 @@ static CPUWriteMemoryFunc *notdirty_mem_write[3] = { }; /* Generate a debug exception if a watchpoint has been hit. */ -static void check_watchpoint(int offset, int flags) +static void check_watchpoint(int offset, int len_mask, int flags) { CPUState *env = cpu_single_env; + target_ulong pc, cs_base; + TranslationBlock *tb; target_ulong vaddr; - int i; - - vaddr = (env->mem_write_vaddr & TARGET_PAGE_MASK) + offset; - for (i = 0; i < env->nb_watchpoints; i++) { - if (vaddr == env->watchpoint[i].vaddr - && (env->watchpoint[i].type & flags)) { - env->watchpoint_hit = i + 1; - cpu_interrupt(env, CPU_INTERRUPT_DEBUG); - break; + CPUWatchpoint *wp; + int cpu_flags; + + if (env->watchpoint_hit) { + /* We re-entered the check after replacing the TB. Now raise + * the debug interrupt so that is will trigger after the + * current instruction. */ + cpu_interrupt(env, CPU_INTERRUPT_DEBUG); + return; + } + vaddr = (env->mem_io_vaddr & TARGET_PAGE_MASK) + offset; + TAILQ_FOREACH(wp, &env->watchpoints, entry) { + if ((vaddr == (wp->vaddr & len_mask) || + (vaddr & wp->len_mask) == wp->vaddr) && (wp->flags & flags)) { + wp->flags |= BP_WATCHPOINT_HIT; + if (!env->watchpoint_hit) { + env->watchpoint_hit = wp; + tb = tb_find_pc(env->mem_io_pc); + if (!tb) { + cpu_abort(env, "check_watchpoint: could not find TB for " + "pc=%p", (void *)env->mem_io_pc); + } + cpu_restore_state(tb, env, env->mem_io_pc, NULL); + tb_phys_invalidate(tb, -1); + if (wp->flags & BP_STOP_BEFORE_ACCESS) { + env->exception_index = EXCP_DEBUG; + } else { + cpu_get_tb_cpu_state(env, &pc, &cs_base, &cpu_flags); + tb_gen_code(env, pc, cs_base, cpu_flags, 1); + } + cpu_resume_from_signal(env, NULL); + } + } else { + wp->flags &= ~BP_WATCHPOINT_HIT; } } } @@ -2315,40 +2608,40 @@ static void check_watchpoint(int offset, int flags) phys routines. */ static uint32_t watch_mem_readb(void *opaque, target_phys_addr_t addr) { - check_watchpoint(addr & ~TARGET_PAGE_MASK, PAGE_READ); + check_watchpoint(addr & ~TARGET_PAGE_MASK, ~0x0, BP_MEM_READ); return ldub_phys(addr); } static uint32_t watch_mem_readw(void *opaque, target_phys_addr_t addr) { - check_watchpoint(addr & ~TARGET_PAGE_MASK, PAGE_READ); + check_watchpoint(addr & ~TARGET_PAGE_MASK, ~0x1, BP_MEM_READ); return lduw_phys(addr); } static uint32_t watch_mem_readl(void *opaque, target_phys_addr_t addr) { - check_watchpoint(addr & ~TARGET_PAGE_MASK, PAGE_READ); + check_watchpoint(addr & ~TARGET_PAGE_MASK, ~0x3, BP_MEM_READ); return ldl_phys(addr); } static void watch_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) { - check_watchpoint(addr & ~TARGET_PAGE_MASK, PAGE_WRITE); + check_watchpoint(addr & ~TARGET_PAGE_MASK, ~0x0, BP_MEM_WRITE); stb_phys(addr, val); } static void watch_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val) { - check_watchpoint(addr & ~TARGET_PAGE_MASK, PAGE_WRITE); + check_watchpoint(addr & ~TARGET_PAGE_MASK, ~0x1, BP_MEM_WRITE); stw_phys(addr, val); } static void watch_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) { - check_watchpoint(addr & ~TARGET_PAGE_MASK, PAGE_WRITE); + check_watchpoint(addr & ~TARGET_PAGE_MASK, ~0x3, BP_MEM_WRITE); stl_phys(addr, val); } @@ -2370,12 +2663,13 @@ static inline uint32_t subpage_readlen (subpage_t *mmio, target_phys_addr_t addr uint32_t ret; unsigned int idx; - idx = SUBPAGE_IDX(addr - mmio->base); + idx = SUBPAGE_IDX(addr); #if defined(DEBUG_SUBPAGE) printf("%s: subpage %p len %d addr " TARGET_FMT_plx " idx %d\n", __func__, mmio, len, addr, idx); #endif - ret = (**mmio->mem_read[idx][len])(mmio->opaque[idx][0][len], addr); + ret = (**mmio->mem_read[idx][len])(mmio->opaque[idx][0][len], + addr + mmio->region_offset[idx][0][len]); return ret; } @@ -2385,12 +2679,14 @@ static inline void subpage_writelen (subpage_t *mmio, target_phys_addr_t addr, { unsigned int idx; - idx = SUBPAGE_IDX(addr - mmio->base); + idx = SUBPAGE_IDX(addr); #if defined(DEBUG_SUBPAGE) printf("%s: subpage %p len %d addr " TARGET_FMT_plx " idx %d value %08x\n", __func__, mmio, len, addr, idx, value); #endif - (**mmio->mem_write[idx][len])(mmio->opaque[idx][1][len], addr, value); + (**mmio->mem_write[idx][len])(mmio->opaque[idx][1][len], + addr + mmio->region_offset[idx][1][len], + value); } static uint32_t subpage_readb (void *opaque, target_phys_addr_t addr) @@ -2460,7 +2756,7 @@ static CPUWriteMemoryFunc *subpage_write[] = { }; static int subpage_register (subpage_t *mmio, uint32_t start, uint32_t end, - ram_addr_t memory) + ram_addr_t memory, ram_addr_t region_offset) { int idx, eidx; unsigned int i; @@ -2479,10 +2775,12 @@ static int subpage_register (subpage_t *mmio, uint32_t start, uint32_t end, if (io_mem_read[memory][i]) { mmio->mem_read[idx][i] = &io_mem_read[memory][i]; mmio->opaque[idx][0][i] = io_mem_opaque[memory]; + mmio->region_offset[idx][0][i] = region_offset; } if (io_mem_write[memory][i]) { mmio->mem_write[idx][i] = &io_mem_write[memory][i]; mmio->opaque[idx][1][i] = io_mem_opaque[memory]; + mmio->region_offset[idx][1][i] = region_offset; } } } @@ -2491,7 +2789,7 @@ static int subpage_register (subpage_t *mmio, uint32_t start, uint32_t end, } static void *subpage_init (target_phys_addr_t base, ram_addr_t *phys, - ram_addr_t orig_memory) + ram_addr_t orig_memory, ram_addr_t region_offset) { subpage_t *mmio; int subpage_memory; @@ -2505,7 +2803,8 @@ static void *subpage_init (target_phys_addr_t base, ram_addr_t *phys, mmio, base, TARGET_PAGE_SIZE, subpage_memory); #endif *phys = subpage_memory | IO_MEM_SUBPAGE; - subpage_register(mmio, 0, TARGET_PAGE_SIZE - 1, orig_memory); + subpage_register(mmio, 0, TARGET_PAGE_SIZE - 1, orig_memory, + region_offset); } return mmio; @@ -2639,6 +2938,8 @@ void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, if (is_write) { if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM) { io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); + if (p) + addr = (addr & ~TARGET_PAGE_MASK) + p->region_offset; /* XXX: could force cpu_single_env to NULL to avoid potential bugs */ if (l >= 4 && ((addr & 3) == 0)) { @@ -2676,6 +2977,8 @@ void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, !(pd & IO_MEM_ROMD)) { /* I/O case */ io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); + if (p) + addr = (addr & ~TARGET_PAGE_MASK) + p->region_offset; if (l >= 4 && ((addr & 3) == 0)) { /* 32 bit read access */ val = io_mem_read[io_index][2](io_mem_opaque[io_index], addr); @@ -2765,6 +3068,8 @@ uint32_t ldl_phys(target_phys_addr_t addr) !(pd & IO_MEM_ROMD)) { /* I/O case */ io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); + if (p) + addr = (addr & ~TARGET_PAGE_MASK) + p->region_offset; val = io_mem_read[io_index][2](io_mem_opaque[io_index], addr); } else { /* RAM case */ @@ -2795,6 +3100,8 @@ uint64_t ldq_phys(target_phys_addr_t addr) !(pd & IO_MEM_ROMD)) { /* I/O case */ io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); + if (p) + addr = (addr & ~TARGET_PAGE_MASK) + p->region_offset; #ifdef TARGET_WORDS_BIGENDIAN val = (uint64_t)io_mem_read[io_index][2](io_mem_opaque[io_index], addr) << 32; val |= io_mem_read[io_index][2](io_mem_opaque[io_index], addr + 4); @@ -2846,11 +3153,23 @@ void stl_phys_notdirty(target_phys_addr_t addr, uint32_t val) if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM) { io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); + if (p) + addr = (addr & ~TARGET_PAGE_MASK) + p->region_offset; io_mem_write[io_index][2](io_mem_opaque[io_index], addr, val); } else { - ptr = phys_ram_base + (pd & TARGET_PAGE_MASK) + - (addr & ~TARGET_PAGE_MASK); + unsigned long addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); + ptr = phys_ram_base + addr1; stl_p(ptr, val); + + if (unlikely(in_migration)) { + if (!cpu_physical_memory_is_dirty(addr1)) { + /* invalidate code */ + tb_invalidate_phys_page_range(addr1, addr1 + 4, 0); + /* set dirty bit */ + phys_ram_dirty[addr1 >> TARGET_PAGE_BITS] |= + (0xff & ~CODE_DIRTY_FLAG); + } + } } } @@ -2870,6 +3189,8 @@ void stq_phys_notdirty(target_phys_addr_t addr, uint64_t val) if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM) { io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); + if (p) + addr = (addr & ~TARGET_PAGE_MASK) + p->region_offset; #ifdef TARGET_WORDS_BIGENDIAN io_mem_write[io_index][2](io_mem_opaque[io_index], addr, val >> 32); io_mem_write[io_index][2](io_mem_opaque[io_index], addr + 4, val); @@ -2901,6 +3222,8 @@ void stl_phys(target_phys_addr_t addr, uint32_t val) if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM) { io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); + if (p) + addr = (addr & ~TARGET_PAGE_MASK) + p->region_offset; io_mem_write[io_index][2](io_mem_opaque[io_index], addr, val); } else { unsigned long addr1; @@ -2967,6 +3290,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 + occurred. */ + 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 preceding + 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 faulting 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, ...)) {