X-Git-Url: http://git.maemo.org/git/?a=blobdiff_plain;f=exec.c;h=723de89bf4e25ebbadaf5d3ac387891eb02196fb;hb=b0a46a333acfd78da56cf6aebb95f4a5dfb3a4f2;hp=d9e959d69d21c034595679821a7c937216e4a8e7;hpb=5b2575789bf30630d6f1c185ddb239945df96fa5;p=qemu diff --git a/exec.c b/exec.c index d9e959d..723de89 100644 --- a/exec.c +++ b/exec.c @@ -15,11 +15,10 @@ * * 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 -#define WIN32_LEAN_AND_MEAN #include #else #include @@ -36,6 +35,10 @@ #include "cpu.h" #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 @@ -57,14 +60,8 @@ #undef DEBUG_TB_CHECK #endif -/* threshold to flush the translated code buffer */ -#define CODE_GEN_BUFFER_MAX_SIZE (CODE_GEN_BUFFER_SIZE - code_gen_max_block_size()) - #define SMC_BITMAP_USE_THRESHOLD 10 -#define MMAP_AREA_START 0x00000000 -#define MMAP_AREA_END 0xa8000000 - #if defined(TARGET_SPARC64) #define TARGET_PHYS_ADDR_SPACE_BITS 41 #elif defined(TARGET_SPARC) @@ -74,34 +71,71 @@ #define TARGET_VIRT_ADDR_SPACE_BITS 42 #elif defined(TARGET_PPC64) #define TARGET_PHYS_ADDR_SPACE_BITS 42 -#elif defined(TARGET_X86_64) && !defined(USE_KQEMU) +#elif defined(TARGET_X86_64) && !defined(CONFIG_KQEMU) #define TARGET_PHYS_ADDR_SPACE_BITS 42 -#elif defined(TARGET_I386) && !defined(USE_KQEMU) +#elif defined(TARGET_I386) && !defined(CONFIG_KQEMU) #define TARGET_PHYS_ADDR_SPACE_BITS 36 #else /* Note: for compatibility with kqemu, we use 32 bits for x86_64 */ #define TARGET_PHYS_ADDR_SPACE_BITS 32 #endif -TranslationBlock tbs[CODE_GEN_MAX_BLOCKS]; +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_buffer[CODE_GEN_BUFFER_SIZE] __attribute__((aligned (32))); +#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 */ +static unsigned long code_gen_buffer_max_size; uint8_t *code_gen_ptr; -ram_addr_t phys_ram_size; +#if !defined(CONFIG_USER_ONLY) int phys_ram_fd; -uint8_t *phys_ram_base; uint8_t *phys_ram_dirty; -static ram_addr_t phys_ram_alloc_offset = 0; +static int in_migration; + +typedef struct RAMBlock { + uint8_t *host; + ram_addr_t offset; + ram_addr_t length; + struct RAMBlock *next; +} RAMBlock; + +static RAMBlock *ram_blocks; +/* TODO: When we implement (and use) ram deallocation (e.g. for hotplug) + then we can no longer assume contiguous ram offsets, and external uses + of this variable will break. */ +ram_addr_t last_ram_offset; +#endif 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 */ @@ -116,8 +150,9 @@ typedef struct PageDesc { } PageDesc; typedef struct PhysPageDesc { - /* offset in host memory of the page + io_index in the low 12 bits */ + /* 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 @@ -134,8 +169,6 @@ typedef struct PhysPageDesc { #define L1_SIZE (1 << L1_BITS) #define L2_SIZE (1 << L2_BITS) -static void io_mem_init(void); - unsigned long qemu_real_host_page_size; unsigned long qemu_host_page_bits; unsigned long qemu_host_page_size; @@ -143,19 +176,21 @@ 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); /* io memory support */ CPUWriteMemoryFunc *io_mem_write[IO_MEM_NB_ENTRIES][4]; CPUReadMemoryFunc *io_mem_read[IO_MEM_NB_ENTRIES][4]; void *io_mem_opaque[IO_MEM_NB_ENTRIES]; -static int io_mem_nb; -#if defined(CONFIG_SOFTMMU) +static char io_mem_used[IO_MEM_NB_ENTRIES]; 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; @@ -171,8 +206,35 @@ 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 +static void map_exec(void *addr, long size) +{ + DWORD old_protect; + VirtualProtect(addr, size, + PAGE_EXECUTE_READWRITE, &old_protect); + +} +#else +static void map_exec(void *addr, long size) +{ + unsigned long start, end, page_size; + + page_size = getpagesize(); + start = (unsigned long)addr; + start &= ~(page_size - 1); + + end = (unsigned long)addr + size; + end += page_size - 1; + end &= ~(page_size - 1); + + mprotect((void *)start, end - start, + PROT_READ | PROT_WRITE | PROT_EXEC); +} +#endif + static void page_init(void) { /* NOTE: we can always suppose that qemu_host_page_size >= @@ -180,31 +242,13 @@ 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; - - VirtualProtect(code_gen_buffer, sizeof(code_gen_buffer), - PAGE_EXECUTE_READWRITE, &old_protect); } #else qemu_real_host_page_size = getpagesize(); - { - unsigned long start, end; - - start = (unsigned long)code_gen_buffer; - start &= ~(qemu_real_host_page_size - 1); - - end = (unsigned long)code_gen_buffer + sizeof(code_gen_buffer); - end += qemu_real_host_page_size - 1; - end &= ~(qemu_real_host_page_size - 1); - - mprotect((void *)start, end - start, - PROT_READ | PROT_WRITE | PROT_EXEC); - } #endif - if (qemu_host_page_size == 0) qemu_host_page_size = qemu_real_host_page_size; if (qemu_host_page_size < TARGET_PAGE_SIZE) @@ -222,42 +266,78 @@ static void page_init(void) FILE *f; int n; + mmap_lock(); + last_brk = (unsigned long)sbrk(0); f = fopen("/proc/self/maps", "r"); if (f) { do { n = fscanf (f, "%llx-%llx %*[^\n]\n", &startaddr, &endaddr); if (n == 2) { - page_set_flags(TARGET_PAGE_ALIGN(startaddr), + startaddr = MIN(startaddr, + (1ULL << TARGET_PHYS_ADDR_SPACE_BITS) - 1); + endaddr = MIN(endaddr, + (1ULL << TARGET_PHYS_ADDR_SPACE_BITS) - 1); + page_set_flags(startaddr & TARGET_PAGE_MASK, TARGET_PAGE_ALIGN(endaddr), PAGE_RESERVED); } } while (!feof(f)); fclose(f); } + mmap_unlock(); } #endif } +static inline PageDesc **page_l1_map(target_ulong index) +{ +#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)) + return NULL; +#endif + 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; - lp = &l1_map[index >> L2_BITS]; p = *lp; if (!p) { /* allocate if not found */ - p = qemu_malloc(sizeof(PageDesc) * L2_SIZE); - memset(p, 0, sizeof(PageDesc) * L2_SIZE); +#if defined(CONFIG_USER_ONLY) + 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; + if (h2g_valid(p)) { + unsigned long addr = h2g(p); + page_set_flags(addr & TARGET_PAGE_MASK, + TARGET_PAGE_ALIGN(addr + len), + PAGE_RESERVED); + } +#else + p = qemu_mallocz(sizeof(PageDesc) * L2_SIZE); *lp = p; +#endif } return p + (index & (L2_SIZE - 1)); } 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)); @@ -294,8 +374,10 @@ static PhysPageDesc *phys_page_find_alloc(target_phys_addr_t index, int alloc) return NULL; pd = qemu_vmalloc(sizeof(PhysPageDesc) * L2_SIZE); *lp = pd; - for (i = 0; i < L2_SIZE; i++) + for (i = 0; i < L2_SIZE; i++) { pd[i].phys_offset = IO_MEM_UNASSIGNED; + pd[i].region_offset = (index + i) << TARGET_PAGE_BITS; + } } return ((PhysPageDesc *)pd) + (index & (L2_SIZE - 1)); } @@ -309,6 +391,154 @@ static inline PhysPageDesc *phys_page_find(target_phys_addr_t index) static void tlb_protect_code(ram_addr_t ram_addr); static void tlb_unprotect_code_phys(CPUState *env, ram_addr_t ram_addr, target_ulong vaddr); +#define mmap_lock() do { } while(0) +#define mmap_unlock() do { } while(0) +#endif + +#define DEFAULT_CODE_GEN_BUFFER_SIZE (32 * 1024 * 1024) + +#if defined(CONFIG_USER_ONLY) +/* Currently it is not recommended to allocate big chunks of data in + user mode. It will change when a dedicated libc will be used */ +#define USE_STATIC_CODE_GEN_BUFFER +#endif + +#ifdef USE_STATIC_CODE_GEN_BUFFER +static uint8_t static_code_gen_buffer[DEFAULT_CODE_GEN_BUFFER_SIZE]; +#endif + +static void code_gen_alloc(unsigned long tb_size) +{ +#ifdef USE_STATIC_CODE_GEN_BUFFER + code_gen_buffer = static_code_gen_buffer; + code_gen_buffer_size = DEFAULT_CODE_GEN_BUFFER_SIZE; + map_exec(code_gen_buffer, code_gen_buffer_size); +#else + code_gen_buffer_size = tb_size; + if (code_gen_buffer_size == 0) { +#if defined(CONFIG_USER_ONLY) + /* in user mode, phys_ram_size is not meaningful */ + code_gen_buffer_size = DEFAULT_CODE_GEN_BUFFER_SIZE; +#else + /* XXX: needs adjustments */ + code_gen_buffer_size = (unsigned long)(ram_size / 4); +#endif + } + if (code_gen_buffer_size < MIN_CODE_GEN_BUFFER_SIZE) + code_gen_buffer_size = MIN_CODE_GEN_BUFFER_SIZE; + /* The code gen buffer location may have constraints depending on + the host cpu and OS */ +#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__) || defined(__DragonFly__) + { + 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(addr, 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); + } + } +#else + code_gen_buffer = qemu_malloc(code_gen_buffer_size); + map_exec(code_gen_buffer, code_gen_buffer_size); +#endif +#endif /* !USE_STATIC_CODE_GEN_BUFFER */ + map_exec(code_gen_prologue, sizeof(code_gen_prologue)); + code_gen_buffer_max_size = code_gen_buffer_size - + code_gen_max_block_size(); + code_gen_max_blocks = code_gen_buffer_size / CODE_GEN_AVG_BLOCK_SIZE; + tbs = qemu_malloc(code_gen_max_blocks * sizeof(TranslationBlock)); +} + +/* Must be called before using the QEMU cpus. 'tb_size' is the size + (in bytes) allocated to the translation buffer. Zero means default + size. */ +void cpu_exec_init_all(unsigned long tb_size) +{ + cpu_gen_init(); + code_gen_alloc(tb_size); + code_gen_ptr = code_gen_buffer; + page_init(); +#if !defined(CONFIG_USER_ONLY) + io_mem_init(); +#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; + + cpu_synchronize_state(env, 0); + + 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); + /* 0x01 was CPU_INTERRUPT_EXIT. This line can be removed when the + version_id is increased. */ + env->interrupt_request &= ~0x01; + tlb_flush(env, 1); + cpu_synchronize_state(env, 1); + + return 0; +} #endif void cpu_exec_init(CPUState *env) @@ -316,12 +546,9 @@ void cpu_exec_init(CPUState *env) CPUState **penv; int cpu_index; - if (!code_gen_ptr) { - cpu_gen_init(); - code_gen_ptr = code_gen_buffer; - page_init(); - io_mem_init(); - } +#if defined(CONFIG_USER_ONLY) + cpu_list_lock(); +#endif env->next_cpu = NULL; penv = &first_cpu; cpu_index = 0; @@ -330,8 +557,19 @@ void cpu_exec_init(CPUState *env) cpu_index++; } env->cpu_index = cpu_index; - env->nb_watchpoints = 0; + env->numa_node = 0; + TAILQ_INIT(&env->breakpoints); + TAILQ_INIT(&env->watchpoints); *penv = env; +#if defined(CONFIG_USER_ONLY) + cpu_list_unlock(); +#endif +#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) @@ -372,7 +610,7 @@ void tb_flush(CPUState *env1) nb_tbs, nb_tbs > 0 ? ((unsigned long)(code_gen_ptr - code_gen_buffer)) / nb_tbs : 0); #endif - if ((unsigned long)(code_gen_ptr - code_gen_buffer) > CODE_GEN_BUFFER_SIZE) + if ((unsigned long)(code_gen_ptr - code_gen_buffer) > code_gen_buffer_size) cpu_abort(env1, "Internal error: code buffer overflow\n"); nb_tbs = 0; @@ -426,7 +664,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; @@ -515,7 +753,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; @@ -603,10 +841,7 @@ static void build_page_bitmap(PageDesc *p) int n, tb_start, tb_end; TranslationBlock *tb; - p->code_bitmap = qemu_malloc(TARGET_PAGE_SIZE / 8); - if (!p->code_bitmap) - return; - memset(p->code_bitmap, 0, TARGET_PAGE_SIZE / 8); + p->code_bitmap = qemu_mallocz(TARGET_PAGE_SIZE / 8); tb = p->first_tb; while (tb != NULL) { @@ -629,11 +864,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; @@ -647,6 +880,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; @@ -663,8 +898,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 @@ -674,12 +909,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) @@ -693,12 +935,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; @@ -719,13 +955,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 @@ -734,15 +970,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 @@ -766,7 +996,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 @@ -776,8 +1006,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 @@ -790,12 +1019,10 @@ static inline void tb_invalidate_phys_page_fast(target_phys_addr_t start, int le int offset, b; #if 0 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->eip, - cpu_single_env->eip + (long)cpu_single_env->segs[R_CS].base); - } + qemu_log("modifying code at 0x%x size=%d EIP=%x PC=%08x\n", + cpu_single_env->mem_io_vaddr, len, + cpu_single_env->eip, + cpu_single_env->eip + (long)cpu_single_env->segs[R_CS].base); } #endif p = page_find(start >> TARGET_PAGE_BITS); @@ -816,12 +1043,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; @@ -829,11 +1060,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); @@ -844,7 +1070,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 @@ -853,14 +1079,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); @@ -873,8 +1093,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 @@ -942,8 +1161,8 @@ TranslationBlock *tb_alloc(target_ulong pc) { TranslationBlock *tb; - if (nb_tbs >= CODE_GEN_MAX_BLOCKS || - (code_gen_ptr - code_gen_buffer) >= CODE_GEN_BUFFER_MAX_SIZE) + if (nb_tbs >= code_gen_max_blocks || + (code_gen_ptr - code_gen_buffer) >= code_gen_buffer_max_size) return NULL; tb = &tbs[nb_tbs++]; tb->pc = pc; @@ -951,6 +1170,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, @@ -959,6 +1189,9 @@ void tb_link_phys(TranslationBlock *tb, unsigned int h; TranslationBlock **ptb; + /* Grab the mmap lock to stop another thread invalidating this TB + before we are done. */ + mmap_lock(); /* add in the physical hash table */ h = tb_phys_hash_func(phys_pc); ptb = &tb_phys_hash[h]; @@ -985,6 +1218,7 @@ void tb_link_phys(TranslationBlock *tb, #ifdef DEBUG_TB_CHECK tb_page_check(); #endif + mmap_unlock(); } /* find the TB 'tb' such that tb[0].tc_ptr <= tc_ptr < @@ -1086,85 +1320,143 @@ static void breakpoint_invalidate(CPUState *env, target_ulong pc) #endif /* Add a watchpoint. */ -int cpu_watchpoint_insert(CPUState *env, target_ulong addr) +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)); + + 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; 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; } -/* 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) +/* Remove a specific watchpoint by reference. */ +void cpu_watchpoint_remove_by_ref(CPUState *env, CPUWatchpoint *watchpoint) { -#if defined(TARGET_HAS_ICE) - int i; + TAILQ_REMOVE(&env->watchpoints, watchpoint, entry); - for(i = 0; i < env->nb_breakpoints; i++) { - if (env->breakpoints[i] == pc) - return 0; + tlb_flush_page(env, watchpoint->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); } +} - if (env->nb_breakpoints >= MAX_BREAKPOINTS) - return -1; - env->breakpoints[env->nb_breakpoints++] = pc; +/* Add a breakpoint. */ +int cpu_breakpoint_insert(CPUState *env, target_ulong pc, int flags, + CPUBreakpoint **breakpoint) +{ +#if defined(TARGET_HAS_ICE) + CPUBreakpoint *bp; + + bp = qemu_malloc(sizeof(*bp)); + + 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 a breakpoint */ -int cpu_breakpoint_remove(CPUState *env, target_ulong pc) +/* 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++) { - 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]; + CPUBreakpoint *bp; - breakpoint_invalidate(env, pc); - return 0; + TAILQ_FOREACH(bp, &env->breakpoints, entry) { + if (bp->pc == pc && bp->flags == flags) { + cpu_breakpoint_remove_by_ref(env, bp); + return 0; + } + } + return -ENOENT; #else - return -1; + return -ENOSYS; +#endif +} + +/* Remove a specific breakpoint by reference. */ +void cpu_breakpoint_remove_by_ref(CPUState *env, CPUBreakpoint *breakpoint) +{ +#if defined(TARGET_HAS_ICE) + TAILQ_REMOVE(&env->breakpoints, breakpoint, entry); + + 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 } @@ -1175,9 +1467,13 @@ void cpu_single_step(CPUState *env, int enabled) #if defined(TARGET_HAS_ICE) if (env->singlestep_enabled != enabled) { env->singlestep_enabled = enabled; - /* must flush all the translated code to avoid inconsistancies */ - /* XXX: only flush what is necessary */ - tb_flush(env); + if (kvm_enabled()) + kvm_update_guest_debug(env, 0); + else { + /* must flush all the translated code to avoid inconsistencies */ + /* XXX: only flush what is necessary */ + tb_flush(env); + } } #endif } @@ -1195,7 +1491,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 @@ -1219,21 +1515,58 @@ void cpu_set_log_filename(const char *filename) cpu_set_log(loglevel); } -/* mask must never be zero, except for A20 change call */ -void cpu_interrupt(CPUState *env, int mask) +static void cpu_unlink_tb(CPUState *env) { +#if defined(USE_NPTL) + /* FIXME: TB unchaining isn't SMP safe. For now just ignore the + problem and hope the cpu will stop of its own accord. For userspace + emulation this often isn't actually as bad as it sounds. Often + signals are used primarily to interrupt blocking syscalls. */ +#else TranslationBlock *tb; static spinlock_t interrupt_lock = SPIN_LOCK_UNLOCKED; - env->interrupt_request |= mask; + 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 +} + +/* mask must never be zero, except for A20 change call */ +void cpu_interrupt(CPUState *env, int mask) +{ + int old_mask; + + old_mask = env->interrupt_request; + env->interrupt_request |= mask; + +#ifndef CONFIG_USER_ONLY + /* + * If called from iothread context, wake the target cpu in + * case its halted. + */ + if (!qemu_cpu_self(env)) { + qemu_cpu_kick(env); + return; + } +#endif + + if (use_icount) { + env->icount_decr.u16.high = 0xffff; +#ifndef CONFIG_USER_ONLY + if (!can_do_io(env) + && (mask & ~old_mask) != 0) { + cpu_abort(env, "Raised interrupt while not in I/O function"); + } +#endif + } else { + cpu_unlink_tb(env); + } } void cpu_reset_interrupt(CPUState *env, int mask) @@ -1241,7 +1574,13 @@ void cpu_reset_interrupt(CPUState *env, int mask) env->interrupt_request &= ~mask; } -CPULogItem cpu_log_items[] = { +void cpu_exit(CPUState *env) +{ + env->exit_request = 1; + cpu_unlink_tb(env); +} + +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", @@ -1263,6 +1602,8 @@ CPULogItem cpu_log_items[] = { #ifdef TARGET_I386 { CPU_LOG_PCALL, "pcall", "show protected mode far calls/returns/exceptions" }, + { CPU_LOG_RESET, "cpu_reset", + "show CPU state before CPU resets" }, #endif #ifdef DEBUG_IOPORT { CPU_LOG_IOPORT, "ioport", @@ -1281,7 +1622,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; @@ -1322,26 +1663,21 @@ void cpu_abort(CPUState *env, const char *fmt, ...) vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); #ifdef TARGET_I386 - if(env->intercept & INTERCEPT_SVM_MASK) { - /* most probably the virtual machine should not - be shut down but rather caught by the VMM */ - vmexit(SVM_EXIT_SHUTDOWN, 0); - } cpu_dump_state(env, stderr, fprintf, X86_DUMP_FPU | X86_DUMP_CCOP); #else cpu_dump_state(env, stderr, fprintf, 0); #endif - if (logfile) { - fprintf(logfile, "qemu: fatal: "); - vfprintf(logfile, fmt, ap2); - fprintf(logfile, "\n"); + if (qemu_log_enabled()) { + qemu_log("qemu: fatal: "); + qemu_log_vprintf(fmt, ap2); + qemu_log("\n"); #ifdef TARGET_I386 - cpu_dump_state(env, logfile, fprintf, X86_DUMP_FPU | X86_DUMP_CCOP); + log_cpu_state(env, X86_DUMP_FPU | X86_DUMP_CCOP); #else - cpu_dump_state(env, logfile, fprintf, 0); + log_cpu_state(env, 0); #endif - fflush(logfile); - fclose(logfile); + qemu_log_flush(); + qemu_log_close(); } va_end(ap2); va_end(ap); @@ -1351,17 +1687,54 @@ 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; } #if !defined(CONFIG_USER_ONLY) +static inline void tlb_flush_jmp_cache(CPUState *env, target_ulong addr) +{ + unsigned int i; + + /* Discard jump cache entries for any tb which might potentially + overlap the flushed page. */ + i = tb_jmp_cache_hash_page(addr - TARGET_PAGE_SIZE); + memset (&env->tb_jmp_cache[i], 0, + TB_JMP_PAGE_SIZE * sizeof(TranslationBlock *)); + + i = tb_jmp_cache_hash_page(addr); + memset (&env->tb_jmp_cache[i], 0, + TB_JMP_PAGE_SIZE * sizeof(TranslationBlock *)); +} + /* NOTE: if flush_global is true, also flush global entries (not implemented yet) */ void tlb_flush(CPUState *env, int flush_global) @@ -1386,20 +1759,23 @@ void tlb_flush(CPUState *env, int flush_global) env->tlb_table[2][i].addr_read = -1; env->tlb_table[2][i].addr_write = -1; env->tlb_table[2][i].addr_code = -1; -#if (NB_MMU_MODES == 4) +#endif +#if (NB_MMU_MODES >= 4) env->tlb_table[3][i].addr_read = -1; env->tlb_table[3][i].addr_write = -1; env->tlb_table[3][i].addr_code = -1; #endif +#if (NB_MMU_MODES >= 5) + env->tlb_table[4][i].addr_read = -1; + env->tlb_table[4][i].addr_write = -1; + env->tlb_table[4][i].addr_code = -1; #endif + } memset (env->tb_jmp_cache, 0, TB_JMP_CACHE_SIZE * sizeof (void *)); -#if !defined(CONFIG_SOFTMMU) - munmap((void *)MMAP_AREA_START, MMAP_AREA_END - MMAP_AREA_START); -#endif -#ifdef USE_KQEMU +#ifdef CONFIG_KQEMU if (env->kqemu_enabled) { kqemu_flush(env, flush_global); } @@ -1424,7 +1800,6 @@ static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong addr) void tlb_flush_page(CPUState *env, target_ulong addr) { int i; - TranslationBlock *tb; #if defined(DEBUG_TLB) printf("tlb_flush_page: " TARGET_FMT_lx "\n", addr); @@ -1439,24 +1814,17 @@ void tlb_flush_page(CPUState *env, target_ulong addr) tlb_flush_entry(&env->tlb_table[1][i], addr); #if (NB_MMU_MODES >= 3) tlb_flush_entry(&env->tlb_table[2][i], addr); -#if (NB_MMU_MODES == 4) +#endif +#if (NB_MMU_MODES >= 4) tlb_flush_entry(&env->tlb_table[3][i], addr); #endif +#if (NB_MMU_MODES >= 5) + tlb_flush_entry(&env->tlb_table[4][i], addr); #endif - /* Discard jump cache entries for any tb which might potentially - overlap the flushed page. */ - i = tb_jmp_cache_hash_page(addr - TARGET_PAGE_SIZE); - memset (&env->tb_jmp_cache[i], 0, TB_JMP_PAGE_SIZE * sizeof(tb)); - - i = tb_jmp_cache_hash_page(addr); - memset (&env->tb_jmp_cache[i], 0, TB_JMP_PAGE_SIZE * sizeof(tb)); + tlb_flush_jmp_cache(env, addr); -#if !defined(CONFIG_SOFTMMU) - if (addr < MMAP_AREA_END) - munmap((void *)addr, TARGET_PAGE_SIZE); -#endif -#ifdef USE_KQEMU +#ifdef CONFIG_KQEMU if (env->kqemu_enabled) { kqemu_flush_page(env, addr); } @@ -1487,11 +1855,12 @@ static inline void tlb_reset_dirty_range(CPUTLBEntry *tlb_entry, if ((tlb_entry->addr_write & ~TARGET_PAGE_MASK) == IO_MEM_RAM) { addr = (tlb_entry->addr_write & TARGET_PAGE_MASK) + tlb_entry->addend; if ((addr - start) < length) { - tlb_entry->addr_write = (tlb_entry->addr_write & TARGET_PAGE_MASK) | IO_MEM_NOTDIRTY; + tlb_entry->addr_write = (tlb_entry->addr_write & TARGET_PAGE_MASK) | TLB_NOTDIRTY; } } } +/* Note: start and end must be within the same ram block. */ void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end, int dirty_flags) { @@ -1507,7 +1876,7 @@ void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end, if (length == 0) return; len = length >> TARGET_PAGE_BITS; -#ifdef USE_KQEMU +#ifdef CONFIG_KQEMU /* XXX: should not depend on cpu context */ env = first_cpu; if (env->kqemu_enabled) { @@ -1526,7 +1895,14 @@ void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end, /* we modify the TLB cache so that the dirty bit will be set again when accessing the range */ - start1 = start + (unsigned long)phys_ram_base; + start1 = (unsigned long)qemu_get_ram_ptr(start); + /* Chek that we don't span multiple blocks - this breaks the + address comparisons below. */ + if ((unsigned long)qemu_get_ram_ptr(end - 1) - start1 + != (end - 1) - start) { + abort(); + } + for(env = first_cpu; env != NULL; env = env->next_cpu) { for(i = 0; i < CPU_TLB_SIZE; i++) tlb_reset_dirty_range(&env->tlb_table[0][i], start1, length); @@ -1535,51 +1911,53 @@ void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end, #if (NB_MMU_MODES >= 3) for(i = 0; i < CPU_TLB_SIZE; i++) tlb_reset_dirty_range(&env->tlb_table[2][i], start1, length); -#if (NB_MMU_MODES == 4) +#endif +#if (NB_MMU_MODES >= 4) for(i = 0; i < CPU_TLB_SIZE; i++) tlb_reset_dirty_range(&env->tlb_table[3][i], start1, length); #endif +#if (NB_MMU_MODES >= 5) + for(i = 0; i < CPU_TLB_SIZE; i++) + tlb_reset_dirty_range(&env->tlb_table[4][i], start1, length); #endif } +} -#if !defined(CONFIG_SOFTMMU) - /* XXX: this is expensive */ - { - VirtPageDesc *p; - int j; - target_ulong addr; - - for(i = 0; i < L1_SIZE; i++) { - p = l1_virt_map[i]; - if (p) { - addr = i << (TARGET_PAGE_BITS + L2_BITS); - for(j = 0; j < L2_SIZE; j++) { - if (p->valid_tag == virt_valid_tag && - p->phys_addr >= start && p->phys_addr < end && - (p->prot & PROT_WRITE)) { - if (addr < MMAP_AREA_END) { - mprotect((void *)addr, TARGET_PAGE_SIZE, - p->prot & ~PROT_WRITE); - } - } - addr += TARGET_PAGE_SIZE; - p++; - } - } - } +int cpu_physical_memory_set_dirty_tracking(int enable) +{ + in_migration = enable; + if (kvm_enabled()) { + return kvm_set_migration_log(enable); } -#endif + return 0; +} + +int cpu_physical_memory_get_dirty_tracking(void) +{ + return in_migration; +} + +int cpu_physical_sync_dirty_bitmap(target_phys_addr_t start_addr, + target_phys_addr_t end_addr) +{ + int ret = 0; + + if (kvm_enabled()) + ret = kvm_physical_sync_dirty_bitmap(start_addr, end_addr); + return ret; } static inline void tlb_update_dirty(CPUTLBEntry *tlb_entry) { ram_addr_t ram_addr; + void *p; if ((tlb_entry->addr_write & ~TARGET_PAGE_MASK) == IO_MEM_RAM) { - ram_addr = (tlb_entry->addr_write & TARGET_PAGE_MASK) + - tlb_entry->addend - (unsigned long)phys_ram_base; + p = (void *)(unsigned long)((tlb_entry->addr_write & TARGET_PAGE_MASK) + + tlb_entry->addend); + ram_addr = qemu_ram_addr_from_host(p); if (!cpu_physical_memory_is_dirty(ram_addr)) { - tlb_entry->addr_write |= IO_MEM_NOTDIRTY; + tlb_entry->addr_write |= TLB_NOTDIRTY; } } } @@ -1595,41 +1973,41 @@ void cpu_tlb_update_dirty(CPUState *env) #if (NB_MMU_MODES >= 3) for(i = 0; i < CPU_TLB_SIZE; i++) tlb_update_dirty(&env->tlb_table[2][i]); -#if (NB_MMU_MODES == 4) +#endif +#if (NB_MMU_MODES >= 4) for(i = 0; i < CPU_TLB_SIZE; i++) tlb_update_dirty(&env->tlb_table[3][i]); #endif +#if (NB_MMU_MODES >= 5) + for(i = 0; i < CPU_TLB_SIZE; i++) + tlb_update_dirty(&env->tlb_table[4][i]); #endif } -static inline void tlb_set_dirty1(CPUTLBEntry *tlb_entry, - unsigned long start) +static inline void tlb_set_dirty1(CPUTLBEntry *tlb_entry, target_ulong vaddr) { - unsigned long addr; - if ((tlb_entry->addr_write & ~TARGET_PAGE_MASK) == IO_MEM_NOTDIRTY) { - addr = (tlb_entry->addr_write & TARGET_PAGE_MASK) + tlb_entry->addend; - if (addr == start) { - tlb_entry->addr_write = (tlb_entry->addr_write & TARGET_PAGE_MASK) | IO_MEM_RAM; - } - } + if (tlb_entry->addr_write == (vaddr | TLB_NOTDIRTY)) + tlb_entry->addr_write = vaddr; } -/* update the TLB corresponding to virtual page vaddr and phys addr - addr so that it is no longer dirty */ -static inline void tlb_set_dirty(CPUState *env, - unsigned long addr, target_ulong vaddr) +/* update the TLB corresponding to virtual page vaddr + so that it is no longer dirty */ +static inline void tlb_set_dirty(CPUState *env, target_ulong vaddr) { int i; - addr &= TARGET_PAGE_MASK; + vaddr &= TARGET_PAGE_MASK; i = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); - tlb_set_dirty1(&env->tlb_table[0][i], addr); - tlb_set_dirty1(&env->tlb_table[1][i], addr); + tlb_set_dirty1(&env->tlb_table[0][i], vaddr); + tlb_set_dirty1(&env->tlb_table[1][i], vaddr); #if (NB_MMU_MODES >= 3) - tlb_set_dirty1(&env->tlb_table[2][i], addr); -#if (NB_MMU_MODES == 4) - tlb_set_dirty1(&env->tlb_table[3][i], addr); + tlb_set_dirty1(&env->tlb_table[2][i], vaddr); #endif +#if (NB_MMU_MODES >= 4) + tlb_set_dirty1(&env->tlb_table[3][i], vaddr); +#endif +#if (NB_MMU_MODES >= 5) + tlb_set_dirty1(&env->tlb_table[4][i], vaddr); #endif } @@ -1645,10 +2023,12 @@ int tlb_set_page_exec(CPUState *env, target_ulong vaddr, unsigned long pd; unsigned int index; target_ulong address; + target_ulong code_address; 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); if (!p) { @@ -1662,153 +2042,78 @@ int tlb_set_page_exec(CPUState *env, target_ulong vaddr, #endif ret = 0; -#if !defined(CONFIG_SOFTMMU) - if (is_softmmu) -#endif - { - if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM && !(pd & IO_MEM_ROMD)) { - /* IO memory case */ - address = vaddr | pd; - addend = paddr; + address = vaddr; + if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM && !(pd & IO_MEM_ROMD)) { + /* IO memory case (romd handled later) */ + address |= TLB_MMIO; + } + addend = (unsigned long)qemu_get_ram_ptr(pd & TARGET_PAGE_MASK); + if ((pd & ~TARGET_PAGE_MASK) <= IO_MEM_ROM) { + /* Normal RAM. */ + iotlb = pd & TARGET_PAGE_MASK; + if ((pd & ~TARGET_PAGE_MASK) == IO_MEM_RAM) + iotlb |= IO_MEM_NOTDIRTY; + else + iotlb |= IO_MEM_ROM; + } else { + /* IO handlers are currently passed a physical address. + It would be nice to pass an offset from the base address + of that region. This would avoid having to special case RAM, + 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); + if (p) { + iotlb += p->region_offset; } else { - /* standard memory */ - address = vaddr; - addend = (unsigned long)phys_ram_base + (pd & TARGET_PAGE_MASK); + iotlb += paddr; } + } - /* 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)) { - if (address & ~TARGET_PAGE_MASK) { - env->watchpoint[i].addend = 0; - address = vaddr | io_mem_watch; - } else { - env->watchpoint[i].addend = pd - paddr + - (unsigned long) phys_ram_base; - /* TODO: Figure out how to make read watchpoints coexist - with code. */ - pd = (pd & TARGET_PAGE_MASK) | io_mem_watch | IO_MEM_ROMD; - } - } + code_address = address; + /* Make accesses to pages with watchpoints go via the + watchpoint trap routines. */ + 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. */ + address |= TLB_MMIO; } + } - index = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); - addend -= vaddr; - te = &env->tlb_table[mmu_idx][index]; - te->addend = addend; - if (prot & PAGE_READ) { - te->addr_read = address; - } else { - te->addr_read = -1; - } - if (prot & PAGE_EXEC) { - te->addr_code = address; - } else { - te->addr_code = -1; - } - if (prot & PAGE_WRITE) { - if ((pd & ~TARGET_PAGE_MASK) == IO_MEM_ROM || - (pd & IO_MEM_ROMD)) { - /* write access calls the I/O callback */ - te->addr_write = vaddr | - (pd & ~(TARGET_PAGE_MASK | IO_MEM_ROMD)); - } else if ((pd & ~TARGET_PAGE_MASK) == IO_MEM_RAM && - !cpu_physical_memory_is_dirty(pd)) { - te->addr_write = vaddr | IO_MEM_NOTDIRTY; - } else { - te->addr_write = address; - } - } else { - te->addr_write = -1; - } + index = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + env->iotlb[mmu_idx][index] = iotlb - vaddr; + te = &env->tlb_table[mmu_idx][index]; + te->addend = addend - vaddr; + if (prot & PAGE_READ) { + te->addr_read = address; + } else { + te->addr_read = -1; } -#if !defined(CONFIG_SOFTMMU) - else { - if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM) { - /* IO access: no mapping is done as it will be handled by the - soft MMU */ - if (!(env->hflags & HF_SOFTMMU_MASK)) - ret = 2; - } else { - void *map_addr; - if (vaddr >= MMAP_AREA_END) { - ret = 2; - } else { - if (prot & PROT_WRITE) { - if ((pd & ~TARGET_PAGE_MASK) == IO_MEM_ROM || -#if defined(TARGET_HAS_SMC) || 1 - first_tb || -#endif - ((pd & ~TARGET_PAGE_MASK) == IO_MEM_RAM && - !cpu_physical_memory_is_dirty(pd))) { - /* ROM: we do as if code was inside */ - /* if code is present, we only map as read only and save the - original mapping */ - VirtPageDesc *vp; - - vp = virt_page_find_alloc(vaddr >> TARGET_PAGE_BITS, 1); - vp->phys_addr = pd; - vp->prot = prot; - vp->valid_tag = virt_valid_tag; - prot &= ~PAGE_WRITE; - } - } - map_addr = mmap((void *)vaddr, TARGET_PAGE_SIZE, prot, - MAP_SHARED | MAP_FIXED, phys_ram_fd, (pd & TARGET_PAGE_MASK)); - if (map_addr == MAP_FAILED) { - cpu_abort(env, "mmap failed when mapped physical address 0x%08x to virtual address 0x%08x\n", - paddr, vaddr); - } - } + if (prot & PAGE_EXEC) { + te->addr_code = code_address; + } else { + te->addr_code = -1; + } + if (prot & PAGE_WRITE) { + if ((pd & ~TARGET_PAGE_MASK) == IO_MEM_ROM || + (pd & IO_MEM_ROMD)) { + /* Write access calls the I/O callback. */ + te->addr_write = address | TLB_MMIO; + } else if ((pd & ~TARGET_PAGE_MASK) == IO_MEM_RAM && + !cpu_physical_memory_is_dirty(pd)) { + te->addr_write = address | TLB_NOTDIRTY; + } else { + te->addr_write = address; } + } else { + te->addr_write = -1; } -#endif return ret; } -/* called from signal handler: invalidate the code and unprotect the - page. Return TRUE if the fault was succesfully handled. */ -int page_unprotect(target_ulong addr, unsigned long pc, void *puc) -{ -#if !defined(CONFIG_SOFTMMU) - VirtPageDesc *vp; - -#if defined(DEBUG_TLB) - printf("page_unprotect: addr=0x%08x\n", addr); -#endif - addr &= TARGET_PAGE_MASK; - - /* if it is not mapped, no need to worry here */ - if (addr >= MMAP_AREA_END) - return 0; - vp = virt_page_find(addr >> TARGET_PAGE_BITS); - if (!vp) - return 0; - /* NOTE: in this case, validate_tag is _not_ tested as it - validates only the code TLB */ - if (vp->valid_tag != virt_valid_tag) - return 0; - if (!(vp->prot & PAGE_WRITE)) - return 0; -#if defined(DEBUG_TLB) - printf("page_unprotect: addr=0x%08x phys_addr=0x%08x prot=%x\n", - addr, vp->phys_addr, vp->prot); -#endif - if (mprotect((void *)addr, TARGET_PAGE_SIZE, vp->prot) < 0) - cpu_abort(cpu_single_env, "error mprotect addr=0x%lx prot=%d\n", - (unsigned long)addr, vp->prot); - /* set the dirty bit */ - phys_ram_dirty[vp->phys_addr >> TARGET_PAGE_BITS] = 0xff; - /* flush the code inside */ - tb_invalidate_phys_page(vp->phys_addr, pc, puc); - return 1; -#else - return 0; -#endif -} - #else void tlb_flush(CPUState *env, int flush_global) @@ -1880,20 +2185,24 @@ int page_get_flags(target_ulong address) } /* modify the flags of a page and invalidate the code if - necessary. The flag PAGE_WRITE_ORG is positionned automatically + necessary. The flag PAGE_WRITE_ORG is positioned automatically depending on PAGE_WRITE */ void page_set_flags(target_ulong start, target_ulong end, int flags) { PageDesc *p; target_ulong addr; + /* mmap_lock should already be held. */ start = start & TARGET_PAGE_MASK; end = TARGET_PAGE_ALIGN(end); if (flags & PAGE_WRITE) flags |= PAGE_WRITE_ORG; - spin_lock(&tb_lock); for(addr = start; addr < end; addr += TARGET_PAGE_SIZE) { p = page_find_alloc(addr >> TARGET_PAGE_BITS); + /* We may be called for host regions that are outside guest + address space. */ + if (!p) + return; /* if the write protection is set, then we invalidate the code inside */ if (!(p->flags & PAGE_WRITE) && @@ -1903,7 +2212,6 @@ void page_set_flags(target_ulong start, target_ulong end, int flags) } p->flags = flags; } - spin_unlock(&tb_lock); } int page_check_range(target_ulong start, target_ulong len, int flags) @@ -1912,12 +2220,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 ) @@ -1943,18 +2252,25 @@ int page_check_range(target_ulong start, target_ulong len, int flags) } /* called from signal handler: invalidate the code and unprotect the - page. Return TRUE if the fault was succesfully handled. */ + page. Return TRUE if the fault was successfully handled. */ int page_unprotect(target_ulong address, unsigned long pc, void *puc) { unsigned int page_index, prot, pindex; PageDesc *p, *p1; target_ulong host_start, host_end, addr; + /* Technically this isn't safe inside a signal handler. However we + know this only ever happens in a synchronous SEGV handler, so in + practice it seems to be ok. */ + mmap_lock(); + host_start = address & qemu_host_page_mask; page_index = host_start >> TARGET_PAGE_BITS; p1 = page_find(page_index); - if (!p1) + if (!p1) { + mmap_unlock(); return 0; + } host_end = host_start + qemu_host_page_size; p = p1; prot = 0; @@ -1976,9 +2292,11 @@ int page_unprotect(target_ulong address, unsigned long pc, void *puc) #ifdef DEBUG_TB_CHECK tb_invalidate_check(address); #endif + mmap_unlock(); return 1; } } + mmap_unlock(); return 0; } @@ -1988,10 +2306,12 @@ 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 { \ @@ -2014,10 +2334,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_addr and region_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; @@ -2025,6 +2350,20 @@ void cpu_register_physical_memory(target_phys_addr_t start_addr, ram_addr_t orig_size = size; void *subpage; +#ifdef CONFIG_KQEMU + /* XXX: should not depend on cpu context */ + env = first_cpu; + if (env->kqemu_enabled) { + kqemu_set_phys_mem(start_addr, size, phys_offset); + } +#endif + if (kvm_enabled()) + kvm_set_phys_mem(start_addr, size, phys_offset); + + if (phys_offset == IO_MEM_UNASSIGNED) { + region_offset = start_addr; + } + 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) { @@ -2039,12 +2378,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 || @@ -2054,10 +2396,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; @@ -2066,12 +2409,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, + addr & TARGET_PAGE_MASK); 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 @@ -2093,22 +2439,145 @@ 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); +} + +#ifdef CONFIG_KQEMU /* XXX: better than nothing */ -ram_addr_t qemu_ram_alloc(ram_addr_t size) +static ram_addr_t kqemu_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 = %lu, max memory = %ld)\n", - size, phys_ram_size); + if ((last_ram_offset + size) > kqemu_phys_ram_size) { + fprintf(stderr, "Not enough memory (requested_size = %" PRIu64 ", max memory = %" PRIu64 ")\n", + (uint64_t)size, (uint64_t)kqemu_phys_ram_size); abort(); } - addr = phys_ram_alloc_offset; - phys_ram_alloc_offset = TARGET_PAGE_ALIGN(phys_ram_alloc_offset + size); + addr = last_ram_offset; + last_ram_offset = TARGET_PAGE_ALIGN(last_ram_offset + size); return addr; } +#endif + +ram_addr_t qemu_ram_alloc(ram_addr_t size) +{ + RAMBlock *new_block; + +#ifdef CONFIG_KQEMU + if (kqemu_phys_ram_base) { + return kqemu_ram_alloc(size); + } +#endif + + size = TARGET_PAGE_ALIGN(size); + new_block = qemu_malloc(sizeof(*new_block)); + + new_block->host = qemu_vmalloc(size); + new_block->offset = last_ram_offset; + new_block->length = size; + + new_block->next = ram_blocks; + ram_blocks = new_block; + + phys_ram_dirty = qemu_realloc(phys_ram_dirty, + (last_ram_offset + size) >> TARGET_PAGE_BITS); + memset(phys_ram_dirty + (last_ram_offset >> TARGET_PAGE_BITS), + 0xff, size >> TARGET_PAGE_BITS); + + last_ram_offset += size; + + if (kvm_enabled()) + kvm_setup_guest_memory(new_block->host, size); + + return new_block->offset; +} void qemu_ram_free(ram_addr_t addr) { + /* TODO: implement this. */ +} + +/* Return a host pointer to ram allocated with qemu_ram_alloc. + With the exception of the softmmu code in this file, this should + only be used for local memory (e.g. video ram) that the device owns, + and knows it isn't going to access beyond the end of the block. + + It should not be used for general purpose DMA. + Use cpu_physical_memory_map/cpu_physical_memory_rw instead. + */ +void *qemu_get_ram_ptr(ram_addr_t addr) +{ + RAMBlock *prev; + RAMBlock **prevp; + RAMBlock *block; + +#ifdef CONFIG_KQEMU + if (kqemu_phys_ram_base) { + return kqemu_phys_ram_base + addr; + } +#endif + + prev = NULL; + prevp = &ram_blocks; + block = ram_blocks; + while (block && (block->offset > addr + || block->offset + block->length <= addr)) { + if (prev) + prevp = &prev->next; + prev = block; + block = block->next; + } + if (!block) { + fprintf(stderr, "Bad ram offset %" PRIx64 "\n", (uint64_t)addr); + abort(); + } + /* Move this entry to to start of the list. */ + if (prev) { + prev->next = block->next; + block->next = *prevp; + *prevp = block; + } + return block->host + (addr - block->offset); +} + +/* Some of the softmmu routines need to translate from a host pointer + (typically a TLB entry) back to a ram offset. */ +ram_addr_t qemu_ram_addr_from_host(void *ptr) +{ + RAMBlock *prev; + RAMBlock **prevp; + RAMBlock *block; + uint8_t *host = ptr; + +#ifdef CONFIG_KQEMU + if (kqemu_phys_ram_base) { + return host - kqemu_phys_ram_base; + } +#endif + + prev = NULL; + prevp = &ram_blocks; + block = ram_blocks; + while (block && (block->host > host + || block->host + block->length <= host)) { + if (prev) + prevp = &prev->next; + prev = block; + block = block->next; + } + if (!block) { + fprintf(stderr, "Bad ram pointer %p\n", ptr); + abort(); + } + return block->offset + (host - block->host); } static uint32_t unassigned_mem_readb(void *opaque, target_phys_addr_t addr) @@ -2116,10 +2585,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; } @@ -2129,30 +2618,47 @@ 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 addr, uint32_t val) +static void notdirty_mem_writeb(void *opaque, target_phys_addr_t ram_addr, + uint32_t val) { - unsigned long ram_addr; int dirty_flags; - ram_addr = addr - (unsigned long)phys_ram_base; dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS]; if (!(dirty_flags & CODE_DIRTY_FLAG)) { #if !defined(CONFIG_USER_ONLY) @@ -2160,8 +2666,8 @@ static void notdirty_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS]; #endif } - stb_p((uint8_t *)(long)addr, val); -#ifdef USE_KQEMU + stb_p(qemu_get_ram_ptr(ram_addr), val); +#ifdef CONFIG_KQEMU if (cpu_single_env->kqemu_enabled && (dirty_flags & KQEMU_MODIFY_PAGE_MASK) != KQEMU_MODIFY_PAGE_MASK) kqemu_modify_page(cpu_single_env, ram_addr); @@ -2171,14 +2677,13 @@ static void notdirty_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t /* we remove the notdirty callback only if the code has been flushed */ if (dirty_flags == 0xff) - tlb_set_dirty(cpu_single_env, addr, 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 addr, uint32_t val) +static void notdirty_mem_writew(void *opaque, target_phys_addr_t ram_addr, + uint32_t val) { - unsigned long ram_addr; int dirty_flags; - ram_addr = addr - (unsigned long)phys_ram_base; dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS]; if (!(dirty_flags & CODE_DIRTY_FLAG)) { #if !defined(CONFIG_USER_ONLY) @@ -2186,8 +2691,8 @@ static void notdirty_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS]; #endif } - stw_p((uint8_t *)(long)addr, val); -#ifdef USE_KQEMU + stw_p(qemu_get_ram_ptr(ram_addr), val); +#ifdef CONFIG_KQEMU if (cpu_single_env->kqemu_enabled && (dirty_flags & KQEMU_MODIFY_PAGE_MASK) != KQEMU_MODIFY_PAGE_MASK) kqemu_modify_page(cpu_single_env, ram_addr); @@ -2197,14 +2702,13 @@ static void notdirty_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t /* we remove the notdirty callback only if the code has been flushed */ if (dirty_flags == 0xff) - tlb_set_dirty(cpu_single_env, addr, 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 addr, uint32_t val) +static void notdirty_mem_writel(void *opaque, target_phys_addr_t ram_addr, + uint32_t val) { - unsigned long ram_addr; int dirty_flags; - ram_addr = addr - (unsigned long)phys_ram_base; dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS]; if (!(dirty_flags & CODE_DIRTY_FLAG)) { #if !defined(CONFIG_USER_ONLY) @@ -2212,8 +2716,8 @@ static void notdirty_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS]; #endif } - stl_p((uint8_t *)(long)addr, val); -#ifdef USE_KQEMU + stl_p(qemu_get_ram_ptr(ram_addr), val); +#ifdef CONFIG_KQEMU if (cpu_single_env->kqemu_enabled && (dirty_flags & KQEMU_MODIFY_PAGE_MASK) != KQEMU_MODIFY_PAGE_MASK) kqemu_modify_page(cpu_single_env, ram_addr); @@ -2223,7 +2727,7 @@ static void notdirty_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t /* we remove the notdirty callback only if the code has been flushed */ if (dirty_flags == 0xff) - tlb_set_dirty(cpu_single_env, addr, cpu_single_env->mem_write_vaddr); + tlb_set_dirty(cpu_single_env, cpu_single_env->mem_io_vaddr); } static CPUReadMemoryFunc *error_mem_read[3] = { @@ -2238,68 +2742,90 @@ static CPUWriteMemoryFunc *notdirty_mem_write[3] = { notdirty_mem_writel, }; -#if defined(CONFIG_SOFTMMU) +/* Generate a debug exception if a watchpoint has been hit. */ +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; + 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; + } + } +} + /* Watchpoint access routines. Watchpoints are inserted using TLB tricks, so these check for a hit then pass through to the normal out-of-line phys routines. */ static uint32_t watch_mem_readb(void *opaque, target_phys_addr_t addr) { + 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, ~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, ~0x3, BP_MEM_READ); return ldl_phys(addr); } -/* Generate a debug exception if a watchpoint has been hit. - Returns the real physical address of the access. addr will be a host - address in case of a RAM location. */ -static target_ulong check_watchpoint(target_phys_addr_t addr) -{ - CPUState *env = cpu_single_env; - target_ulong watch; - target_ulong retaddr; - int i; - - retaddr = addr; - for (i = 0; i < env->nb_watchpoints; i++) { - watch = env->watchpoint[i].vaddr; - if (((env->mem_write_vaddr ^ watch) & TARGET_PAGE_MASK) == 0) { - retaddr = addr - env->watchpoint[i].addend; - if (((addr ^ watch) & ~TARGET_PAGE_MASK) == 0) { - cpu_single_env->watchpoint_hit = i + 1; - cpu_interrupt(cpu_single_env, CPU_INTERRUPT_DEBUG); - break; - } - } - } - return retaddr; -} - static void watch_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) { - addr = check_watchpoint(addr); + 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) { - addr = check_watchpoint(addr); + 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) { - addr = check_watchpoint(addr); + check_watchpoint(addr & ~TARGET_PAGE_MASK, ~0x3, BP_MEM_WRITE); stl_phys(addr, val); } @@ -2314,7 +2840,6 @@ static CPUWriteMemoryFunc *watch_mem_write[3] = { watch_mem_writew, watch_mem_writel, }; -#endif static inline uint32_t subpage_readlen (subpage_t *mmio, target_phys_addr_t addr, unsigned int len) @@ -2322,12 +2847,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; } @@ -2337,12 +2863,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) @@ -2412,7 +2940,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; @@ -2431,10 +2959,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; } } } @@ -2443,46 +2973,63 @@ 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; mmio = qemu_mallocz(sizeof(subpage_t)); - if (mmio != NULL) { - mmio->base = base; - subpage_memory = cpu_register_io_memory(0, subpage_read, subpage_write, mmio); + + mmio->base = base; + subpage_memory = cpu_register_io_memory(0, subpage_read, subpage_write, mmio); #if defined(DEBUG_SUBPAGE) - printf("%s: %p base " TARGET_FMT_plx " len %08x %d\n", __func__, - mmio, base, TARGET_PAGE_SIZE, subpage_memory); + printf("%s: %p base " TARGET_FMT_plx " len %08x %d\n", __func__, + mmio, base, TARGET_PAGE_SIZE, subpage_memory); #endif - *phys = subpage_memory | IO_MEM_SUBPAGE; - subpage_register(mmio, 0, TARGET_PAGE_SIZE - 1, orig_memory); - } + *phys = subpage_memory | IO_MEM_SUBPAGE; + subpage_register(mmio, 0, TARGET_PAGE_SIZE - 1, orig_memory, + region_offset); return mmio; } +static int get_free_io_mem_idx(void) +{ + int i; + + for (i = 0; i> IO_MEM_SHIFT, error_mem_read, unassigned_mem_write, NULL); cpu_register_io_memory(IO_MEM_UNASSIGNED >> IO_MEM_SHIFT, unassigned_mem_read, unassigned_mem_write, NULL); cpu_register_io_memory(IO_MEM_NOTDIRTY >> IO_MEM_SHIFT, error_mem_read, notdirty_mem_write, NULL); - io_mem_nb = 5; + for (i=0; i<5; i++) + io_mem_used[i] = 1; -#if defined(CONFIG_SOFTMMU) - io_mem_watch = cpu_register_io_memory(-1, watch_mem_read, + io_mem_watch = cpu_register_io_memory(0, watch_mem_read, watch_mem_write, NULL); +#ifdef CONFIG_KQEMU + if (kqemu_phys_ram_base) { + /* alloc dirty bits array */ + phys_ram_dirty = qemu_vmalloc(kqemu_phys_ram_size >> TARGET_PAGE_BITS); + memset(phys_ram_dirty, 0xff, kqemu_phys_ram_size >> TARGET_PAGE_BITS); + } #endif - /* alloc dirty bits array */ - phys_ram_dirty = qemu_vmalloc(phys_ram_size >> TARGET_PAGE_BITS); - memset(phys_ram_dirty, 0xff, phys_ram_size >> TARGET_PAGE_BITS); } /* mem_read and mem_write are arrays of functions containing the function to access byte (index 0), word (index 1) and dword (index - 2). Functions can be omitted with a NULL function pointer. The - registered functions may be modified dynamically later. + 2). Functions can be omitted with a NULL function pointer. If io_index is non zero, the corresponding io zone is modified. If it is zero, a new io zone is allocated. The return value can be used with cpu_register_physical_memory(). (-1) is @@ -2495,9 +3042,9 @@ int cpu_register_io_memory(int io_index, int i, subwidth = 0; if (io_index <= 0) { - if (io_mem_nb >= IO_MEM_NB_ENTRIES) - return -1; - io_index = io_mem_nb++; + io_index = get_free_io_mem_idx(); + if (io_index == -1) + return io_index; } else { if (io_index >= IO_MEM_NB_ENTRIES) return -1; @@ -2513,16 +3060,21 @@ int cpu_register_io_memory(int io_index, return (io_index << IO_MEM_SHIFT) | subwidth; } -CPUWriteMemoryFunc **cpu_get_io_memory_write(int io_index) +void cpu_unregister_io_memory(int io_table_address) { - return io_mem_write[io_index >> IO_MEM_SHIFT]; -} + int i; + int io_index = io_table_address >> IO_MEM_SHIFT; -CPUReadMemoryFunc **cpu_get_io_memory_read(int io_index) -{ - return io_mem_read[io_index >> IO_MEM_SHIFT]; + for (i=0;i < 3; i++) { + io_mem_read[io_index][i] = unassigned_mem_read[i]; + io_mem_write[io_index][i] = unassigned_mem_write[i]; + } + io_mem_opaque[io_index] = NULL; + io_mem_used[io_index] = 0; } +#endif /* !defined(CONFIG_USER_ONLY) */ + /* physical memory access (slow version, mainly for debug) */ #if defined(CONFIG_USER_ONLY) void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, @@ -2590,30 +3142,33 @@ void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, if (is_write) { if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM) { + target_phys_addr_t addr1 = addr; io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); + if (p) + addr1 = (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)) { + if (l >= 4 && ((addr1 & 3) == 0)) { /* 32 bit write access */ val = ldl_p(buf); - io_mem_write[io_index][2](io_mem_opaque[io_index], addr, val); + io_mem_write[io_index][2](io_mem_opaque[io_index], addr1, val); l = 4; - } else if (l >= 2 && ((addr & 1) == 0)) { + } else if (l >= 2 && ((addr1 & 1) == 0)) { /* 16 bit write access */ val = lduw_p(buf); - io_mem_write[io_index][1](io_mem_opaque[io_index], addr, val); + io_mem_write[io_index][1](io_mem_opaque[io_index], addr1, val); l = 2; } else { /* 8 bit write access */ val = ldub_p(buf); - io_mem_write[io_index][0](io_mem_opaque[io_index], addr, val); + io_mem_write[io_index][0](io_mem_opaque[io_index], addr1, val); l = 1; } } else { unsigned long addr1; addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); /* RAM case */ - ptr = phys_ram_base + addr1; + ptr = qemu_get_ram_ptr(addr1); memcpy(ptr, buf, l); if (!cpu_physical_memory_is_dirty(addr1)) { /* invalidate code */ @@ -2626,27 +3181,30 @@ void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, } else { if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM && !(pd & IO_MEM_ROMD)) { + target_phys_addr_t addr1 = addr; /* I/O case */ io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); - if (l >= 4 && ((addr & 3) == 0)) { + if (p) + addr1 = (addr & ~TARGET_PAGE_MASK) + p->region_offset; + if (l >= 4 && ((addr1 & 3) == 0)) { /* 32 bit read access */ - val = io_mem_read[io_index][2](io_mem_opaque[io_index], addr); + val = io_mem_read[io_index][2](io_mem_opaque[io_index], addr1); stl_p(buf, val); l = 4; - } else if (l >= 2 && ((addr & 1) == 0)) { + } else if (l >= 2 && ((addr1 & 1) == 0)) { /* 16 bit read access */ - val = io_mem_read[io_index][1](io_mem_opaque[io_index], addr); + val = io_mem_read[io_index][1](io_mem_opaque[io_index], addr1); stw_p(buf, val); l = 2; } else { /* 8 bit read access */ - val = io_mem_read[io_index][0](io_mem_opaque[io_index], addr); + val = io_mem_read[io_index][0](io_mem_opaque[io_index], addr1); stb_p(buf, val); l = 1; } } else { /* RAM case */ - ptr = phys_ram_base + (pd & TARGET_PAGE_MASK) + + ptr = qemu_get_ram_ptr(pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); memcpy(buf, ptr, l); } @@ -2687,7 +3245,7 @@ void cpu_physical_memory_write_rom(target_phys_addr_t addr, unsigned long addr1; addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); /* ROM/RAM case */ - ptr = phys_ram_base + addr1; + ptr = qemu_get_ram_ptr(addr1); memcpy(ptr, buf, l); } len -= l; @@ -2696,6 +3254,148 @@ void cpu_physical_memory_write_rom(target_phys_addr_t addr, } } +typedef struct { + void *buffer; + target_phys_addr_t addr; + target_phys_addr_t len; +} BounceBuffer; + +static BounceBuffer bounce; + +typedef struct MapClient { + void *opaque; + void (*callback)(void *opaque); + LIST_ENTRY(MapClient) link; +} MapClient; + +static LIST_HEAD(map_client_list, MapClient) map_client_list + = LIST_HEAD_INITIALIZER(map_client_list); + +void *cpu_register_map_client(void *opaque, void (*callback)(void *opaque)) +{ + MapClient *client = qemu_malloc(sizeof(*client)); + + client->opaque = opaque; + client->callback = callback; + LIST_INSERT_HEAD(&map_client_list, client, link); + return client; +} + +void cpu_unregister_map_client(void *_client) +{ + MapClient *client = (MapClient *)_client; + + LIST_REMOVE(client, link); +} + +static void cpu_notify_map_clients(void) +{ + MapClient *client; + + while (!LIST_EMPTY(&map_client_list)) { + client = LIST_FIRST(&map_client_list); + client->callback(client->opaque); + LIST_REMOVE(client, link); + } +} + +/* Map a physical memory region into a host virtual address. + * May map a subset of the requested range, given by and returned in *plen. + * May return NULL if resources needed to perform the mapping are exhausted. + * Use only for reads OR writes - not for read-modify-write operations. + * Use cpu_register_map_client() to know when retrying the map operation is + * likely to succeed. + */ +void *cpu_physical_memory_map(target_phys_addr_t addr, + target_phys_addr_t *plen, + int is_write) +{ + target_phys_addr_t len = *plen; + target_phys_addr_t done = 0; + int l; + uint8_t *ret = NULL; + uint8_t *ptr; + target_phys_addr_t page; + unsigned long pd; + PhysPageDesc *p; + unsigned long addr1; + + while (len > 0) { + page = addr & TARGET_PAGE_MASK; + l = (page + TARGET_PAGE_SIZE) - addr; + if (l > len) + l = len; + p = phys_page_find(page >> TARGET_PAGE_BITS); + if (!p) { + pd = IO_MEM_UNASSIGNED; + } else { + pd = p->phys_offset; + } + + if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM) { + if (done || bounce.buffer) { + break; + } + bounce.buffer = qemu_memalign(TARGET_PAGE_SIZE, TARGET_PAGE_SIZE); + bounce.addr = addr; + bounce.len = l; + if (!is_write) { + cpu_physical_memory_rw(addr, bounce.buffer, l, 0); + } + ptr = bounce.buffer; + } else { + addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); + ptr = qemu_get_ram_ptr(addr1); + } + if (!done) { + ret = ptr; + } else if (ret + done != ptr) { + break; + } + + len -= l; + addr += l; + done += l; + } + *plen = done; + return ret; +} + +/* Unmaps a memory region previously mapped by cpu_physical_memory_map(). + * Will also mark the memory as dirty if is_write == 1. access_len gives + * the amount of memory that was actually read or written by the caller. + */ +void cpu_physical_memory_unmap(void *buffer, target_phys_addr_t len, + int is_write, target_phys_addr_t access_len) +{ + if (buffer != bounce.buffer) { + if (is_write) { + ram_addr_t addr1 = qemu_ram_addr_from_host(buffer); + while (access_len) { + unsigned l; + l = TARGET_PAGE_SIZE; + if (l > access_len) + l = access_len; + if (!cpu_physical_memory_is_dirty(addr1)) { + /* invalidate code */ + tb_invalidate_phys_page_range(addr1, addr1 + l, 0); + /* set dirty bit */ + phys_ram_dirty[addr1 >> TARGET_PAGE_BITS] |= + (0xff & ~CODE_DIRTY_FLAG); + } + addr1 += l; + access_len -= l; + } + } + return; + } + if (is_write) { + cpu_physical_memory_write(bounce.addr, bounce.buffer, access_len); + } + qemu_free(bounce.buffer); + bounce.buffer = NULL; + cpu_notify_map_clients(); +} /* warning: addr must be aligned */ uint32_t ldl_phys(target_phys_addr_t addr) @@ -2717,10 +3417,12 @@ 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 */ - ptr = phys_ram_base + (pd & TARGET_PAGE_MASK) + + ptr = qemu_get_ram_ptr(pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); val = ldl_p(ptr); } @@ -2747,6 +3449,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); @@ -2756,7 +3460,7 @@ uint64_t ldq_phys(target_phys_addr_t addr) #endif } else { /* RAM case */ - ptr = phys_ram_base + (pd & TARGET_PAGE_MASK) + + ptr = qemu_get_ram_ptr(pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); val = ldq_p(ptr); } @@ -2798,11 +3502,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 = qemu_get_ram_ptr(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); + } + } } } @@ -2822,6 +3538,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); @@ -2830,7 +3548,7 @@ void stq_phys_notdirty(target_phys_addr_t addr, uint64_t val) io_mem_write[io_index][2](io_mem_opaque[io_index], addr + 4, val >> 32); #endif } else { - ptr = phys_ram_base + (pd & TARGET_PAGE_MASK) + + ptr = qemu_get_ram_ptr(pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); stq_p(ptr, val); } @@ -2853,12 +3571,14 @@ 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; addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); /* RAM case */ - ptr = phys_ram_base + addr1; + ptr = qemu_get_ram_ptr(addr1); stl_p(ptr, val); if (!cpu_physical_memory_is_dirty(addr1)) { /* invalidate code */ @@ -2893,7 +3613,7 @@ void stq_phys(target_phys_addr_t addr, uint64_t val) #endif -/* virtual memory access for debug */ +/* virtual memory access for debug (includes writing to ROM) */ int cpu_memory_rw_debug(CPUState *env, target_ulong addr, uint8_t *buf, int len, int is_write) { @@ -2910,8 +3630,13 @@ int cpu_memory_rw_debug(CPUState *env, target_ulong addr, l = (page + TARGET_PAGE_SIZE) - addr; if (l > len) l = len; - cpu_physical_memory_rw(phys_addr + (addr & ~TARGET_PAGE_MASK), - buf, l, is_write); + phys_addr += (addr & ~TARGET_PAGE_MASK); +#if !defined(CONFIG_USER_ONLY) + if (is_write) + cpu_physical_memory_write_rom(phys_addr, buf, l); + else +#endif + cpu_physical_memory_rw(phys_addr, buf, l, is_write); len -= l; buf += l; addr += l; @@ -2919,6 +3644,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, ...)) { @@ -2947,7 +3731,10 @@ void dump_exec_info(FILE *f, } /* XXX: avoid using doubles ? */ cpu_fprintf(f, "Translation buffer state:\n"); - cpu_fprintf(f, "TB count %d\n", nb_tbs); + cpu_fprintf(f, "gen code size %ld/%ld\n", + code_gen_ptr - code_gen_buffer, code_gen_buffer_max_size); + cpu_fprintf(f, "TB count %d/%d\n", + nb_tbs, code_gen_max_blocks); cpu_fprintf(f, "TB avg target size %d max=%d bytes\n", nb_tbs ? target_code_size / nb_tbs : 0, max_target_code_size); @@ -2966,45 +3753,7 @@ void dump_exec_info(FILE *f, cpu_fprintf(f, "TB flush count %d\n", tb_flush_count); cpu_fprintf(f, "TB invalidate count %d\n", tb_phys_invalidate_count); cpu_fprintf(f, "TLB flush count %d\n", tlb_flush_count); -#ifdef CONFIG_PROFILER - { - int64_t tot; - tot = dyngen_interm_time + dyngen_code_time; - cpu_fprintf(f, "JIT cycles %" PRId64 " (%0.3f s at 2.4 GHz)\n", - tot, tot / 2.4e9); - cpu_fprintf(f, "translated TBs %" PRId64 " (aborted=%" PRId64 " %0.1f%%)\n", - dyngen_tb_count, - dyngen_tb_count1 - dyngen_tb_count, - dyngen_tb_count1 ? (double)(dyngen_tb_count1 - dyngen_tb_count) / dyngen_tb_count1 * 100.0 : 0); - cpu_fprintf(f, "avg ops/TB %0.1f max=%d\n", - dyngen_tb_count ? (double)dyngen_op_count / dyngen_tb_count : 0, dyngen_op_count_max); - cpu_fprintf(f, "old ops/total ops %0.1f%%\n", - dyngen_op_count ? (double)dyngen_old_op_count / dyngen_op_count * 100.0 : 0); - cpu_fprintf(f, "deleted ops/TB %0.2f\n", - dyngen_tb_count ? - (double)dyngen_tcg_del_op_count / dyngen_tb_count : 0); - cpu_fprintf(f, "cycles/op %0.1f\n", - dyngen_op_count ? (double)tot / dyngen_op_count : 0); - cpu_fprintf(f, "cycles/in byte %0.1f\n", - dyngen_code_in_len ? (double)tot / dyngen_code_in_len : 0); - cpu_fprintf(f, "cycles/out byte %0.1f\n", - dyngen_code_out_len ? (double)tot / dyngen_code_out_len : 0); - if (tot == 0) - tot = 1; - cpu_fprintf(f, " gen_interm time %0.1f%%\n", - (double)dyngen_interm_time / tot * 100.0); - cpu_fprintf(f, " gen_code time %0.1f%%\n", - (double)dyngen_code_time / tot * 100.0); - cpu_fprintf(f, "cpu_restore count %" PRId64 "\n", - dyngen_restore_count); - cpu_fprintf(f, " avg cycles %0.1f\n", - dyngen_restore_count ? (double)dyngen_restore_time / dyngen_restore_count : 0); - { - extern void dump_op_count(void); - dump_op_count(); - } - } -#endif + tcg_dump_info(f, cpu_fprintf); } #if !defined(CONFIG_USER_ONLY)