X-Git-Url: https://vcs.maemo.org/git/?a=blobdiff_plain;f=exec.c;h=0c0b6542eb067a6242d3e89b6d951529372b5ab2;hb=2857068eb1f7098d1af15d715c92fcd4e29d274f;hp=ae6165ba6114a9e443f2772011e8a5d2740bdc03;hpb=2e12669a4c06c7d26351b0c250db9b1ad72ba76f;p=qemu diff --git a/exec.c b/exec.c index ae6165b..0c0b654 100644 --- a/exec.c +++ b/exec.c @@ -1,6 +1,6 @@ /* * virtual page mapping and translated block handling - * + * * Copyright (c) 2003 Fabrice Bellard * * This library is free software; you can redistribute it and/or @@ -18,6 +18,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" +#ifdef _WIN32 +#include +#else +#include +#include +#endif #include #include #include @@ -25,20 +31,29 @@ #include #include #include -#if !defined(CONFIG_SOFTMMU) -#include -#endif #include "cpu.h" #include "exec-all.h" +#if defined(CONFIG_USER_ONLY) +#include +#endif //#define DEBUG_TB_INVALIDATE //#define DEBUG_FLUSH //#define DEBUG_TLB +//#define DEBUG_UNASSIGNED /* make various TB consistency checks */ -//#define DEBUG_TB_CHECK -//#define DEBUG_TLB_CHECK +//#define DEBUG_TB_CHECK +//#define DEBUG_TLB_CHECK + +//#define DEBUG_IOPORT +//#define DEBUG_SUBPAGE + +#if !defined(CONFIG_USER_ONLY) +/* TB consistency checks only implemented for usermode emulation. */ +#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_SIZE) @@ -48,25 +63,42 @@ #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) +#define TARGET_PHYS_ADDR_SPACE_BITS 36 +#elif defined(TARGET_ALPHA) +#define TARGET_PHYS_ADDR_SPACE_BITS 42 +#define TARGET_VIRT_ADDR_SPACE_BITS 42 +#elif defined(TARGET_PPC64) +#define TARGET_PHYS_ADDR_SPACE_BITS 42 +#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]; -TranslationBlock *tb_hash[CODE_GEN_HASH_SIZE]; TranslationBlock *tb_phys_hash[CODE_GEN_PHYS_HASH_SIZE]; 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]; +uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE] __attribute__((aligned (32))); uint8_t *code_gen_ptr; int phys_ram_size; int phys_ram_fd; uint8_t *phys_ram_base; uint8_t *phys_ram_dirty; +static ram_addr_t phys_ram_alloc_offset = 0; + +CPUState *first_cpu; +/* current CPU in the current thread. It is only valid inside + cpu_exec() */ +CPUState *cpu_single_env; typedef struct PageDesc { - /* offset in host memory of the page + io_index in the low 12 bits */ - unsigned long phys_offset; - /* list of TBs intersecting this physical page */ + /* list of TBs intersecting this ram page */ TranslationBlock *first_tb; /* in order to optimize self modifying code, we count the number of lookups we do to a given page to use a bitmap */ @@ -77,68 +109,106 @@ typedef struct PageDesc { #endif } PageDesc; -typedef struct VirtPageDesc { - /* physical address of code page. It is valid only if 'valid_tag' - matches 'virt_valid_tag' */ - target_ulong phys_addr; - unsigned int valid_tag; -#if !defined(CONFIG_SOFTMMU) - /* original page access rights. It is valid only if 'valid_tag' - matches 'virt_valid_tag' */ - unsigned int prot; -#endif -} VirtPageDesc; +typedef struct PhysPageDesc { + /* offset in host memory of the page + io_index in the low 12 bits */ + uint32_t phys_offset; +} PhysPageDesc; #define L2_BITS 10 +#if defined(CONFIG_USER_ONLY) && defined(TARGET_VIRT_ADDR_SPACE_BITS) +/* XXX: this is a temporary hack for alpha target. + * In the future, this is to be replaced by a multi-level table + * to actually be able to handle the complete 64 bits address space. + */ +#define L1_BITS (TARGET_VIRT_ADDR_SPACE_BITS - L2_BITS - TARGET_PAGE_BITS) +#else #define L1_BITS (32 - L2_BITS - TARGET_PAGE_BITS) +#endif #define L1_SIZE (1 << L1_BITS) #define L2_SIZE (1 << L2_BITS) static void io_mem_init(void); -unsigned long real_host_page_size; -unsigned long host_page_bits; -unsigned long host_page_size; -unsigned long host_page_mask; +unsigned long qemu_real_host_page_size; +unsigned long qemu_host_page_bits; +unsigned long qemu_host_page_size; +unsigned long qemu_host_page_mask; +/* XXX: for system emulation, it could just be an array */ static PageDesc *l1_map[L1_SIZE]; - -#if !defined(CONFIG_USER_ONLY) -static VirtPageDesc *l1_virt_map[L1_SIZE]; -static unsigned int virt_valid_tag; -#endif +PhysPageDesc **l1_phys_map; /* 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 int io_mem_watch; +#endif /* log support */ char *logfilename = "/tmp/qemu.log"; FILE *logfile; int loglevel; +static int log_append = 0; + +/* statistics */ +static int tlb_flush_count; +static int tb_flush_count; +static int tb_phys_invalidate_count; + +#define SUBPAGE_IDX(addr) ((addr) & ~TARGET_PAGE_MASK) +typedef struct subpage_t { + target_phys_addr_t base; + CPUReadMemoryFunc **mem_read[TARGET_PAGE_SIZE]; + CPUWriteMemoryFunc **mem_write[TARGET_PAGE_SIZE]; + void *opaque[TARGET_PAGE_SIZE]; +} subpage_t; static void page_init(void) { - /* NOTE: we can always suppose that host_page_size >= + /* NOTE: we can always suppose that qemu_host_page_size >= TARGET_PAGE_SIZE */ #ifdef _WIN32 - real_host_page_size = 4096; + { + 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 - real_host_page_size = getpagesize(); -#endif - if (host_page_size == 0) - host_page_size = real_host_page_size; - if (host_page_size < TARGET_PAGE_SIZE) - host_page_size = TARGET_PAGE_SIZE; - host_page_bits = 0; - while ((1 << host_page_bits) < host_page_size) - host_page_bits++; - host_page_mask = ~(host_page_size - 1); -#if !defined(CONFIG_USER_ONLY) - virt_valid_tag = 1; + 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) + qemu_host_page_size = TARGET_PAGE_SIZE; + qemu_host_page_bits = 0; + while ((1 << qemu_host_page_bits) < qemu_host_page_size) + qemu_host_page_bits++; + qemu_host_page_mask = ~(qemu_host_page_size - 1); + l1_phys_map = qemu_vmalloc(L1_SIZE * sizeof(void *)); + memset(l1_phys_map, 0, L1_SIZE * sizeof(void *)); } static inline PageDesc *page_find_alloc(unsigned int index) @@ -166,66 +236,74 @@ static inline PageDesc *page_find(unsigned int index) return p + (index & (L2_SIZE - 1)); } -#if !defined(CONFIG_USER_ONLY) -static void tlb_protect_code(CPUState *env, uint32_t addr); -static void tlb_unprotect_code_phys(CPUState *env, uint32_t phys_addr, target_ulong vaddr); - -static inline VirtPageDesc *virt_page_find_alloc(unsigned int index) +static PhysPageDesc *phys_page_find_alloc(target_phys_addr_t index, int alloc) { - VirtPageDesc **lp, *p; + void **lp, **p; + PhysPageDesc *pd; - lp = &l1_virt_map[index >> L2_BITS]; + p = (void **)l1_phys_map; +#if TARGET_PHYS_ADDR_SPACE_BITS > 32 + +#if TARGET_PHYS_ADDR_SPACE_BITS > (32 + L1_BITS) +#error unsupported TARGET_PHYS_ADDR_SPACE_BITS +#endif + lp = p + ((index >> (L1_BITS + L2_BITS)) & (L1_SIZE - 1)); p = *lp; if (!p) { /* allocate if not found */ - p = qemu_malloc(sizeof(VirtPageDesc) * L2_SIZE); - memset(p, 0, sizeof(VirtPageDesc) * L2_SIZE); + if (!alloc) + return NULL; + p = qemu_vmalloc(sizeof(void *) * L1_SIZE); + memset(p, 0, sizeof(void *) * L1_SIZE); *lp = p; } - return p + (index & (L2_SIZE - 1)); +#endif + lp = p + ((index >> L2_BITS) & (L1_SIZE - 1)); + pd = *lp; + if (!pd) { + int i; + /* allocate if not found */ + if (!alloc) + return NULL; + pd = qemu_vmalloc(sizeof(PhysPageDesc) * L2_SIZE); + *lp = pd; + for (i = 0; i < L2_SIZE; i++) + pd[i].phys_offset = IO_MEM_UNASSIGNED; + } + return ((PhysPageDesc *)pd) + (index & (L2_SIZE - 1)); } -static inline VirtPageDesc *virt_page_find(unsigned int index) +static inline PhysPageDesc *phys_page_find(target_phys_addr_t index) { - VirtPageDesc *p; - - p = l1_virt_map[index >> L2_BITS]; - if (!p) - return 0; - return p + (index & (L2_SIZE - 1)); + return phys_page_find_alloc(index, 0); } -static void virt_page_flush(void) -{ - int i, j; - VirtPageDesc *p; - - virt_valid_tag++; - - if (virt_valid_tag == 0) { - virt_valid_tag = 1; - for(i = 0; i < L1_SIZE; i++) { - p = l1_virt_map[i]; - if (p) { - for(j = 0; j < L2_SIZE; j++) - p[j].valid_tag = 0; - } - } - } -} -#else -static void virt_page_flush(void) -{ -} +#if !defined(CONFIG_USER_ONLY) +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); #endif -void cpu_exec_init(void) +void cpu_exec_init(CPUState *env) { + CPUState **penv; + int cpu_index; + if (!code_gen_ptr) { code_gen_ptr = code_gen_buffer; page_init(); io_mem_init(); } + env->next_cpu = NULL; + penv = &first_cpu; + cpu_index = 0; + while (*penv != NULL) { + penv = (CPUState **)&(*penv)->next_cpu; + cpu_index++; + } + env->cpu_index = cpu_index; + env->nb_watchpoints = 0; + *penv = env; } static inline void invalidate_page_bitmap(PageDesc *p) @@ -257,42 +335,43 @@ static void page_flush_tb(void) /* flush all the translation blocks */ /* XXX: tb_flush is currently not thread safe */ -void tb_flush(CPUState *env) +void tb_flush(CPUState *env1) { - int i; + CPUState *env; #if defined(DEBUG_FLUSH) - printf("qemu: flush code_size=%d nb_tbs=%d avg_tb_size=%d\n", - code_gen_ptr - code_gen_buffer, - nb_tbs, + printf("qemu: flush code_size=%d nb_tbs=%d avg_tb_size=%d\n", + code_gen_ptr - code_gen_buffer, + nb_tbs, nb_tbs > 0 ? (code_gen_ptr - code_gen_buffer) / nb_tbs : 0); #endif nb_tbs = 0; - for(i = 0;i < CODE_GEN_HASH_SIZE; i++) - tb_hash[i] = NULL; - virt_page_flush(); - for(i = 0;i < CODE_GEN_PHYS_HASH_SIZE; i++) - tb_phys_hash[i] = NULL; + for(env = first_cpu; env != NULL; env = env->next_cpu) { + memset (env->tb_jmp_cache, 0, TB_JMP_CACHE_SIZE * sizeof (void *)); + } + + memset (tb_phys_hash, 0, CODE_GEN_PHYS_HASH_SIZE * sizeof (void *)); page_flush_tb(); code_gen_ptr = code_gen_buffer; /* XXX: flush processor icache at this point if cache flush is expensive */ + tb_flush_count++; } #ifdef DEBUG_TB_CHECK -static void tb_invalidate_check(unsigned long address) +static void tb_invalidate_check(target_ulong address) { TranslationBlock *tb; int i; address &= TARGET_PAGE_MASK; - for(i = 0;i < CODE_GEN_HASH_SIZE; i++) { - for(tb = tb_hash[i]; tb != NULL; tb = tb->hash_next) { + for(i = 0;i < CODE_GEN_PHYS_HASH_SIZE; i++) { + for(tb = tb_phys_hash[i]; tb != NULL; tb = tb->phys_hash_next) { if (!(address + TARGET_PAGE_SIZE <= tb->pc || address >= tb->pc + tb->size)) { printf("ERROR invalidate: address=%08lx PC=%08lx size=%04x\n", - address, tb->pc, tb->size); + address, (long)tb->pc, tb->size); } } } @@ -303,14 +382,14 @@ static void tb_page_check(void) { TranslationBlock *tb; int i, flags1, flags2; - - for(i = 0;i < CODE_GEN_HASH_SIZE; i++) { - for(tb = tb_hash[i]; tb != NULL; tb = tb->hash_next) { + + for(i = 0;i < CODE_GEN_PHYS_HASH_SIZE; i++) { + for(tb = tb_phys_hash[i]; tb != NULL; tb = tb->phys_hash_next) { flags1 = page_get_flags(tb->pc); flags2 = page_get_flags(tb->pc + tb->size - 1); if ((flags1 & PAGE_WRITE) || (flags2 & PAGE_WRITE)) { printf("ERROR page flags: PC=%08lx size=%04x f1=%x f2=%x\n", - tb->pc, tb->size, flags1, flags2); + (long)tb->pc, tb->size, flags1, flags2); } } } @@ -405,27 +484,39 @@ 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_invalidate(TranslationBlock *tb) +static inline void tb_phys_invalidate(TranslationBlock *tb, unsigned int page_addr) { + CPUState *env; + PageDesc *p; unsigned int h, n1; - TranslationBlock *tb1, *tb2, **ptb; - + target_ulong phys_pc; + TranslationBlock *tb1, *tb2; + + /* remove the TB from the hash list */ + phys_pc = tb->page_addr[0] + (tb->pc & ~TARGET_PAGE_MASK); + h = tb_phys_hash_func(phys_pc); + tb_remove(&tb_phys_hash[h], tb, + offsetof(TranslationBlock, phys_hash_next)); + + /* remove the TB from the page list */ + if (tb->page_addr[0] != page_addr) { + p = page_find(tb->page_addr[0] >> TARGET_PAGE_BITS); + tb_page_remove(&p->first_tb, tb); + invalidate_page_bitmap(p); + } + if (tb->page_addr[1] != -1 && tb->page_addr[1] != page_addr) { + p = page_find(tb->page_addr[1] >> TARGET_PAGE_BITS); + tb_page_remove(&p->first_tb, tb); + invalidate_page_bitmap(p); + } + tb_invalidated_flag = 1; /* remove the TB from the hash list */ - h = tb_hash_func(tb->pc); - ptb = &tb_hash[h]; - for(;;) { - tb1 = *ptb; - /* NOTE: the TB is not necessarily linked in the hash. It - indicates that it is not currently used */ - if (tb1 == NULL) - return; - if (tb1 == tb) { - *ptb = tb1->hash_next; - break; - } - ptb = &tb1->hash_next; + h = tb_jmp_cache_hash_func(tb->pc); + for(env = first_cpu; env != NULL; env = env->next_cpu) { + if (env->tb_jmp_cache[h] == tb) + env->tb_jmp_cache[h] = NULL; } /* suppress this TB from the two jump lists */ @@ -445,33 +536,8 @@ static inline void tb_invalidate(TranslationBlock *tb) tb1 = tb2; } tb->jmp_first = (TranslationBlock *)((long)tb | 2); /* fail safe */ -} - -static inline void tb_phys_invalidate(TranslationBlock *tb, unsigned int page_addr) -{ - PageDesc *p; - unsigned int h; - target_ulong phys_pc; - - /* remove the TB from the hash list */ - phys_pc = tb->page_addr[0] + (tb->pc & ~TARGET_PAGE_MASK); - h = tb_phys_hash_func(phys_pc); - tb_remove(&tb_phys_hash[h], tb, - offsetof(TranslationBlock, phys_hash_next)); - - /* remove the TB from the page list */ - if (tb->page_addr[0] != page_addr) { - p = page_find(tb->page_addr[0] >> TARGET_PAGE_BITS); - tb_page_remove(&p->first_tb, tb); - invalidate_page_bitmap(p); - } - if (tb->page_addr[1] != -1 && tb->page_addr[1] != page_addr) { - p = page_find(tb->page_addr[1] >> TARGET_PAGE_BITS); - tb_page_remove(&p->first_tb, tb); - invalidate_page_bitmap(p); - } - tb_invalidate(tb); + tb_phys_invalidate_count++; } static inline void set_bits(uint8_t *tab, int start, int len) @@ -505,7 +571,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; @@ -534,7 +600,7 @@ static void build_page_bitmap(PageDesc *p) #ifdef TARGET_HAS_PRECISE_SMC -static void tb_gen_code(CPUState *env, +static void tb_gen_code(CPUState *env, target_ulong pc, target_ulong cs_base, int flags, int cflags) { @@ -543,13 +609,13 @@ static void tb_gen_code(CPUState *env, target_ulong phys_pc, phys_page2, virt_page2; int code_gen_size; - phys_pc = get_phys_addr_code(env, (unsigned long)pc); - tb = tb_alloc((unsigned long)pc); + phys_pc = get_phys_addr_code(env, pc); + tb = tb_alloc(pc); if (!tb) { /* flush must be done */ tb_flush(env); /* cannot fail at this point */ - tb = tb_alloc((unsigned long)pc); + tb = tb_alloc(pc); } tc_ptr = code_gen_ptr; tb->tc_ptr = tc_ptr; @@ -558,38 +624,36 @@ static void tb_gen_code(CPUState *env, tb->cflags = cflags; cpu_gen_code(env, tb, CODE_GEN_MAX_SIZE, &code_gen_size); code_gen_ptr = (void *)(((unsigned long)code_gen_ptr + code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1)); - + /* check next page if needed */ - virt_page2 = ((unsigned long)pc + tb->size - 1) & TARGET_PAGE_MASK; + virt_page2 = (pc + tb->size - 1) & TARGET_PAGE_MASK; phys_page2 = -1; - if (((unsigned long)pc & TARGET_PAGE_MASK) != virt_page2) { + if ((pc & TARGET_PAGE_MASK) != virt_page2) { phys_page2 = get_phys_addr_code(env, virt_page2); } tb_link_phys(tb, phys_pc, phys_page2); } #endif - + /* invalidate all TBs which intersect with the target physical page starting in range [start;end[. NOTE: start and end must refer to the same physical page. 'is_cpu_write_access' should be true if called from a real cpu write access: the virtual CPU will exit the current TB if code is modified inside this TB. */ -void tb_invalidate_phys_page_range(target_ulong start, target_ulong end, +void tb_invalidate_phys_page_range(target_ulong start, target_ulong end, int is_cpu_write_access) { int n, current_tb_modified, current_tb_not_found, current_flags; -#if defined(TARGET_HAS_PRECISE_SMC) || !defined(CONFIG_USER_ONLY) CPUState *env = cpu_single_env; -#endif PageDesc *p; - TranslationBlock *tb, *tb_next, *current_tb; + TranslationBlock *tb, *tb_next, *current_tb, *saved_tb; target_ulong tb_start, tb_end; target_ulong current_pc, current_cs_base; p = page_find(start >> TARGET_PAGE_BITS); - if (!p) + if (!p) return; - if (!p->code_bitmap && + if (!p->code_bitmap && ++p->code_write_count >= SMC_BITMAP_USE_THRESHOLD && is_cpu_write_access) { /* build code bitmap */ @@ -636,9 +700,9 @@ void tb_invalidate_phys_page_range(target_ulong start, target_ulong end, that the modification is after the current PC, but it would require a specialized function to partially restore the CPU state */ - + current_tb_modified = 1; - cpu_restore_state(current_tb, env, + cpu_restore_state(current_tb, env, env->mem_write_pc, NULL); #if defined(TARGET_I386) current_flags = env->hflags; @@ -650,7 +714,19 @@ void tb_invalidate_phys_page_range(target_ulong start, target_ulong end, #endif } #endif /* TARGET_HAS_PRECISE_SMC */ + /* we need to do that to handle the case where a signal + occurs while doing tb_phys_invalidate() */ + saved_tb = NULL; + if (env) { + saved_tb = env->current_tb; + env->current_tb = NULL; + } tb_phys_invalidate(tb, -1); + if (env) { + env->current_tb = saved_tb; + if (env->interrupt_request && env->current_tb) + cpu_interrupt(env, env->interrupt_request); + } } tb = tb_next; } @@ -668,7 +744,8 @@ void tb_invalidate_phys_page_range(target_ulong start, target_ulong end, /* we generate a block containing just the instruction modifying the memory. It will ensure that it cannot modify itself */ - tb_gen_code(env, current_pc, current_cs_base, current_flags, + env->current_tb = NULL; + tb_gen_code(env, current_pc, current_cs_base, current_flags, CF_SINGLE_INSN); cpu_resume_from_signal(env, NULL); } @@ -681,14 +758,17 @@ static inline void tb_invalidate_phys_page_fast(target_ulong start, int len) PageDesc *p; int offset, b; #if 0 - if (cpu_single_env->cr[0] & CR0_PE_MASK) { - printf("modifying code at 0x%x size=%d EIP=%x\n", - (vaddr & TARGET_PAGE_MASK) | (start & ~TARGET_PAGE_MASK), len, - cpu_single_env->eip); + 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); + } } #endif p = page_find(start >> TARGET_PAGE_BITS); - if (!p) + if (!p) return; if (p->code_bitmap) { offset = start & ~TARGET_PAGE_MASK; @@ -702,7 +782,7 @@ static inline void tb_invalidate_phys_page_fast(target_ulong start, int len) } #if !defined(CONFIG_SOFTMMU) -static void tb_invalidate_phys_page(target_ulong addr, +static void tb_invalidate_phys_page(target_ulong addr, unsigned long pc, void *puc) { int n, current_flags, current_tb_modified; @@ -715,7 +795,7 @@ static void tb_invalidate_phys_page(target_ulong addr, addr &= TARGET_PAGE_MASK; p = page_find(addr >> TARGET_PAGE_BITS); - if (!p) + if (!p) return; tb = p->first_tb; current_tb_modified = 0; @@ -739,7 +819,7 @@ static void tb_invalidate_phys_page(target_ulong addr, that the modification is after the current PC, but it would require a specialized function to partially restore the CPU state */ - + current_tb_modified = 1; cpu_restore_state(current_tb, env, pc, puc); #if defined(TARGET_I386) @@ -761,7 +841,8 @@ static void tb_invalidate_phys_page(target_ulong addr, /* we generate a block containing just the instruction modifying the memory. It will ensure that it cannot modify itself */ - tb_gen_code(env, current_pc, current_cs_base, current_flags, + env->current_tb = NULL; + tb_gen_code(env, current_pc, current_cs_base, current_flags, CF_SINGLE_INSN); cpu_resume_from_signal(env, puc); } @@ -770,50 +851,54 @@ static void tb_invalidate_phys_page(target_ulong addr, #endif /* add the tb in the target page and protect it if necessary */ -static inline void tb_alloc_page(TranslationBlock *tb, - unsigned int n, unsigned int page_addr) +static inline void tb_alloc_page(TranslationBlock *tb, + unsigned int n, target_ulong page_addr) { PageDesc *p; TranslationBlock *last_first_tb; tb->page_addr[n] = page_addr; - p = page_find(page_addr >> TARGET_PAGE_BITS); + p = page_find_alloc(page_addr >> TARGET_PAGE_BITS); tb->page_next[n] = p->first_tb; last_first_tb = p->first_tb; p->first_tb = (TranslationBlock *)((long)tb | n); invalidate_page_bitmap(p); -#ifdef TARGET_HAS_SMC +#if defined(TARGET_HAS_SMC) || 1 #if defined(CONFIG_USER_ONLY) if (p->flags & PAGE_WRITE) { - unsigned long host_start, host_end, addr; + target_ulong addr; + PageDesc *p2; int prot; /* force the host page as non writable (writes will have a page fault + mprotect overhead) */ - host_start = page_addr & host_page_mask; - host_end = host_start + host_page_size; + page_addr &= qemu_host_page_mask; prot = 0; - for(addr = host_start; addr < host_end; addr += TARGET_PAGE_SIZE) - prot |= page_get_flags(addr); - mprotect((void *)host_start, host_page_size, + for(addr = page_addr; addr < page_addr + qemu_host_page_size; + addr += TARGET_PAGE_SIZE) { + + p2 = page_find (addr >> TARGET_PAGE_BITS); + if (!p2) + continue; + prot |= p2->flags; + p2->flags &= ~PAGE_WRITE; + page_get_flags(addr); + } + mprotect(g2h(page_addr), qemu_host_page_size, (prot & PAGE_BITS) & ~PAGE_WRITE); #ifdef DEBUG_TB_INVALIDATE - printf("protecting code page: 0x%08lx\n", - host_start); + printf("protecting code page: 0x%08lx\n", + page_addr); #endif - p->flags &= ~PAGE_WRITE; } #else /* if some code is already present, then the pages are already protected. So we handle the case where only the first TB is allocated in a physical page */ if (!last_first_tb) { - target_ulong virt_addr; - - virt_addr = (tb->pc & TARGET_PAGE_MASK) + (n << TARGET_PAGE_BITS); - tlb_protect_code(cpu_single_env, virt_addr); + tlb_protect_code(page_addr); } #endif @@ -822,11 +907,11 @@ static inline void tb_alloc_page(TranslationBlock *tb, /* Allocate a new translation block. Flush the translation buffer if too many translation blocks or too much generated code. */ -TranslationBlock *tb_alloc(unsigned long pc) +TranslationBlock *tb_alloc(target_ulong pc) { TranslationBlock *tb; - if (nb_tbs >= CODE_GEN_MAX_BLOCKS || + if (nb_tbs >= CODE_GEN_MAX_BLOCKS || (code_gen_ptr - code_gen_buffer) >= CODE_GEN_BUFFER_MAX_SIZE) return NULL; tb = &tbs[nb_tbs++]; @@ -837,7 +922,7 @@ TranslationBlock *tb_alloc(unsigned long pc) /* 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, +void tb_link_phys(TranslationBlock *tb, target_ulong phys_pc, target_ulong phys_page2) { unsigned int h; @@ -855,57 +940,6 @@ void tb_link_phys(TranslationBlock *tb, tb_alloc_page(tb, 1, phys_page2); else tb->page_addr[1] = -1; -#ifdef DEBUG_TB_CHECK - tb_page_check(); -#endif -} - -/* link the tb with the other TBs */ -void tb_link(TranslationBlock *tb) -{ -#if !defined(CONFIG_USER_ONLY) - { - VirtPageDesc *vp; - target_ulong addr; - - /* save the code memory mappings (needed to invalidate the code) */ - addr = tb->pc & TARGET_PAGE_MASK; - vp = virt_page_find_alloc(addr >> TARGET_PAGE_BITS); -#ifdef DEBUG_TLB_CHECK - if (vp->valid_tag == virt_valid_tag && - vp->phys_addr != tb->page_addr[0]) { - printf("Error tb addr=0x%x phys=0x%x vp->phys_addr=0x%x\n", - addr, tb->page_addr[0], vp->phys_addr); - } -#endif - vp->phys_addr = tb->page_addr[0]; - if (vp->valid_tag != virt_valid_tag) { - vp->valid_tag = virt_valid_tag; -#if !defined(CONFIG_SOFTMMU) - vp->prot = 0; -#endif - } - - if (tb->page_addr[1] != -1) { - addr += TARGET_PAGE_SIZE; - vp = virt_page_find_alloc(addr >> TARGET_PAGE_BITS); -#ifdef DEBUG_TLB_CHECK - if (vp->valid_tag == virt_valid_tag && - vp->phys_addr != tb->page_addr[1]) { - printf("Error tb addr=0x%x phys=0x%x vp->phys_addr=0x%x\n", - addr, tb->page_addr[1], vp->phys_addr); - } -#endif - vp->phys_addr = tb->page_addr[1]; - if (vp->valid_tag != virt_valid_tag) { - vp->valid_tag = virt_valid_tag; -#if !defined(CONFIG_SOFTMMU) - vp->prot = 0; -#endif - } - } - } -#endif tb->jmp_first = (TranslationBlock *)((long)tb | 2); tb->jmp_next[0] = NULL; @@ -921,6 +955,10 @@ void tb_link(TranslationBlock *tb) tb_reset_jump(tb, 0); if (tb->tb_next_offset[1] != 0xffff) tb_reset_jump(tb, 1); + +#ifdef DEBUG_TB_CHECK + tb_page_check(); +#endif } /* find the TB 'tb' such that tb[0].tc_ptr <= tc_ptr < @@ -950,7 +988,7 @@ TranslationBlock *tb_find_pc(unsigned long tc_ptr) } else { m_min = m + 1; } - } + } return &tbs[m_max]; } @@ -986,7 +1024,7 @@ static inline void tb_reset_jump_recursive2(TranslationBlock *tb, int n) } *ptb = tb->jmp_next[n]; tb->jmp_next[n] = NULL; - + /* suppress the jump to next tb in generated code */ tb_reset_jump(tb, n); @@ -1001,21 +1039,71 @@ static void tb_reset_jump_recursive(TranslationBlock *tb) tb_reset_jump_recursive2(tb, 1); } +#if defined(TARGET_HAS_ICE) static void breakpoint_invalidate(CPUState *env, target_ulong pc) { - target_ulong phys_addr; + target_phys_addr_t addr; + target_ulong pd; + ram_addr_t ram_addr; + PhysPageDesc *p; + + addr = cpu_get_phys_page_debug(env, pc); + p = phys_page_find(addr >> TARGET_PAGE_BITS); + if (!p) { + pd = IO_MEM_UNASSIGNED; + } else { + pd = p->phys_offset; + } + ram_addr = (pd & TARGET_PAGE_MASK) | (pc & ~TARGET_PAGE_MASK); + tb_invalidate_phys_page_range(ram_addr, ram_addr + 1, 0); +} +#endif + +/* Add a watchpoint. */ +int cpu_watchpoint_insert(CPUState *env, target_ulong addr) +{ + int i; + + for (i = 0; i < env->nb_watchpoints; i++) { + if (addr == env->watchpoint[i].vaddr) + return 0; + } + if (env->nb_watchpoints >= MAX_WATCHPOINTS) + return -1; + + 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; +} + +/* Remove a watchpoint. */ +int cpu_watchpoint_remove(CPUState *env, target_ulong addr) +{ + int i; - phys_addr = cpu_get_phys_page_debug(env, pc); - tb_invalidate_phys_page_range(phys_addr, phys_addr + 1, 0); + 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); + return 0; + } + } + return -1; } /* 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) { -#if defined(TARGET_I386) || defined(TARGET_PPC) +#if defined(TARGET_HAS_ICE) int i; - + for(i = 0; i < env->nb_breakpoints; i++) { if (env->breakpoints[i] == pc) return 0; @@ -1024,7 +1112,7 @@ int cpu_breakpoint_insert(CPUState *env, target_ulong pc) if (env->nb_breakpoints >= MAX_BREAKPOINTS) return -1; env->breakpoints[env->nb_breakpoints++] = pc; - + breakpoint_invalidate(env, pc); return 0; #else @@ -1035,7 +1123,7 @@ int cpu_breakpoint_insert(CPUState *env, target_ulong pc) /* remove a breakpoint */ int cpu_breakpoint_remove(CPUState *env, target_ulong pc) { -#if defined(TARGET_I386) || defined(TARGET_PPC) +#if defined(TARGET_HAS_ICE) int i; for(i = 0; i < env->nb_breakpoints; i++) { if (env->breakpoints[i] == pc) @@ -1043,9 +1131,9 @@ int cpu_breakpoint_remove(CPUState *env, target_ulong pc) } return -1; found: - memmove(&env->breakpoints[i], &env->breakpoints[i + 1], - (env->nb_breakpoints - (i + 1)) * sizeof(env->breakpoints[0])); env->nb_breakpoints--; + if (i < env->nb_breakpoints) + env->breakpoints[i] = env->breakpoints[env->nb_breakpoints]; breakpoint_invalidate(env, pc); return 0; @@ -1058,7 +1146,7 @@ int cpu_breakpoint_remove(CPUState *env, target_ulong pc) CPU loop after each instruction */ void cpu_single_step(CPUState *env, int enabled) { -#if defined(TARGET_I386) || defined(TARGET_PPC) +#if defined(TARGET_HAS_ICE) if (env->singlestep_enabled != enabled) { env->singlestep_enabled = enabled; /* must flush all the translated code to avoid inconsistancies */ @@ -1073,7 +1161,7 @@ void cpu_set_log(int log_flags) { loglevel = log_flags; if (loglevel && !logfile) { - logfile = fopen(logfilename, "w"); + logfile = fopen(logfilename, log_append ? "a" : "w"); if (!logfile) { perror(logfilename); _exit(1); @@ -1087,12 +1175,22 @@ void cpu_set_log(int log_flags) #else setvbuf(logfile, NULL, _IOLBF, 0); #endif + log_append = 1; + } + if (!loglevel && logfile) { + fclose(logfile); + logfile = NULL; } } void cpu_set_log_filename(const char *filename) { logfilename = strdup(filename); + if (logfile) { + fclose(logfile); + logfile = NULL; + } + cpu_set_log(loglevel); } /* mask must never be zero, except for A20 change call */ @@ -1112,12 +1210,17 @@ void cpu_interrupt(CPUState *env, int mask) } } +void cpu_reset_interrupt(CPUState *env, int mask) +{ + env->interrupt_request &= ~mask; +} + CPULogItem cpu_log_items[] = { - { CPU_LOG_TB_OUT_ASM, "out_asm", + { CPU_LOG_TB_OUT_ASM, "out_asm", "show generated host assembly code for each compiled TB" }, { CPU_LOG_TB_IN_ASM, "in_asm", "show target assembly code for each compiled TB" }, - { CPU_LOG_TB_OP, "op", + { CPU_LOG_TB_OP, "op", "show micro ops for each compiled TB (only usable if 'in_asm' used)" }, #ifdef TARGET_I386 { CPU_LOG_TB_OP_OPT, "op_opt", @@ -1127,10 +1230,16 @@ CPULogItem cpu_log_items[] = { "show interrupts/exceptions in short format" }, { CPU_LOG_EXEC, "exec", "show trace before each executed TB (lots of logs)" }, + { CPU_LOG_TB_CPU, "cpu", + "show CPU state before block translation" }, #ifdef TARGET_I386 { CPU_LOG_PCALL, "pcall", "show protected mode far calls/returns/exceptions" }, #endif +#ifdef DEBUG_IOPORT + { CPU_LOG_IOPORT, "ioport", + "show all i/o ports accesses" }, +#endif { 0, NULL, NULL }, }; @@ -1140,7 +1249,7 @@ static int cmp1(const char *s1, int n, const char *s2) return 0; return memcmp(s1, s2, n) == 0; } - + /* takes a comma separated list of log masks. Return 0 if error. */ int cpu_str_to_log_mask(const char *str) { @@ -1154,11 +1263,17 @@ int cpu_str_to_log_mask(const char *str) p1 = strchr(p, ','); if (!p1) p1 = p + strlen(p); + if(cmp1(p,p1-p,"all")) { + for(item = cpu_log_items; item->mask != 0; item++) { + mask |= item->mask; + } + } else { for(item = cpu_log_items; item->mask != 0; item++) { if (cmp1(p, p1 - p, item->name)) goto found; } return 0; + } found: mask |= item->mask; if (*p1 != ',') @@ -1177,12 +1292,43 @@ void cpu_abort(CPUState *env, const char *fmt, ...) vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); #ifdef TARGET_I386 - cpu_x86_dump_state(env, stderr, X86_DUMP_FPU | X86_DUMP_CCOP); + 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, ap); + fprintf(logfile, "\n"); +#ifdef TARGET_I386 + cpu_dump_state(env, logfile, fprintf, X86_DUMP_FPU | X86_DUMP_CCOP); +#else + cpu_dump_state(env, logfile, fprintf, 0); #endif + fflush(logfile); + fclose(logfile); + } va_end(ap); abort(); } +CPUState *cpu_copy(CPUState *env) +{ + CPUState *new_env = cpu_init(); + /* preserve chaining and index */ + CPUState *next_cpu = new_env->next_cpu; + int cpu_index = new_env->cpu_index; + memcpy(new_env, env, sizeof(CPUState)); + new_env->next_cpu = next_cpu; + new_env->cpu_index = cpu_index; + return new_env; +} + #if !defined(CONFIG_USER_ONLY) /* NOTE: if flush_global is true, also flush global entries (not @@ -1199,37 +1345,58 @@ void tlb_flush(CPUState *env, int flush_global) env->current_tb = NULL; for(i = 0; i < CPU_TLB_SIZE; i++) { - env->tlb_read[0][i].address = -1; - env->tlb_write[0][i].address = -1; - env->tlb_read[1][i].address = -1; - env->tlb_write[1][i].address = -1; + env->tlb_table[0][i].addr_read = -1; + env->tlb_table[0][i].addr_write = -1; + env->tlb_table[0][i].addr_code = -1; + env->tlb_table[1][i].addr_read = -1; + env->tlb_table[1][i].addr_write = -1; + env->tlb_table[1][i].addr_code = -1; +#if (NB_MMU_MODES >= 3) + 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) + 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 +#endif } - virt_page_flush(); - for(i = 0;i < CODE_GEN_HASH_SIZE; i++) - tb_hash[i] = NULL; + 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 + if (env->kqemu_enabled) { + kqemu_flush(env, flush_global); + } +#endif + tlb_flush_count++; } -static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, uint32_t addr) +static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong addr) { - if (addr == (tlb_entry->address & - (TARGET_PAGE_MASK | TLB_INVALID_MASK))) - tlb_entry->address = -1; + if (addr == (tlb_entry->addr_read & + (TARGET_PAGE_MASK | TLB_INVALID_MASK)) || + addr == (tlb_entry->addr_write & + (TARGET_PAGE_MASK | TLB_INVALID_MASK)) || + addr == (tlb_entry->addr_code & + (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { + tlb_entry->addr_read = -1; + tlb_entry->addr_write = -1; + tlb_entry->addr_code = -1; + } } void tlb_flush_page(CPUState *env, target_ulong addr) { - int i, n; - VirtPageDesc *vp; - PageDesc *p; + int i; TranslationBlock *tb; #if defined(DEBUG_TLB) - printf("tlb_flush_page: 0x%08x\n", addr); + printf("tlb_flush_page: " TARGET_FMT_lx "\n", addr); #endif /* must reset current TB so that interrupts cannot modify the links while we are modifying them */ @@ -1237,106 +1404,70 @@ void tlb_flush_page(CPUState *env, target_ulong addr) addr &= TARGET_PAGE_MASK; i = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); - tlb_flush_entry(&env->tlb_read[0][i], addr); - tlb_flush_entry(&env->tlb_write[0][i], addr); - tlb_flush_entry(&env->tlb_read[1][i], addr); - tlb_flush_entry(&env->tlb_write[1][i], addr); - - /* remove from the virtual pc hash table all the TB at this - virtual address */ - - vp = virt_page_find(addr >> TARGET_PAGE_BITS); - if (vp && vp->valid_tag == virt_valid_tag) { - p = page_find(vp->phys_addr >> TARGET_PAGE_BITS); - if (p) { - /* we remove all the links to the TBs in this virtual page */ - tb = p->first_tb; - while (tb != NULL) { - n = (long)tb & 3; - tb = (TranslationBlock *)((long)tb & ~3); - if ((tb->pc & TARGET_PAGE_MASK) == addr || - ((tb->pc + tb->size - 1) & TARGET_PAGE_MASK) == addr) { - tb_invalidate(tb); - } - tb = tb->page_next[n]; - } - } - vp->valid_tag = 0; - } + tlb_flush_entry(&env->tlb_table[0][i], 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) + tlb_flush_entry(&env->tlb_table[3][i], addr); +#endif +#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)); #if !defined(CONFIG_SOFTMMU) if (addr < MMAP_AREA_END) munmap((void *)addr, TARGET_PAGE_SIZE); #endif -} - -static inline void tlb_protect_code1(CPUTLBEntry *tlb_entry, uint32_t addr) -{ - if (addr == (tlb_entry->address & - (TARGET_PAGE_MASK | TLB_INVALID_MASK)) && - (tlb_entry->address & ~TARGET_PAGE_MASK) != IO_MEM_CODE && - (tlb_entry->address & ~TARGET_PAGE_MASK) != IO_MEM_ROM) { - tlb_entry->address = (tlb_entry->address & TARGET_PAGE_MASK) | IO_MEM_CODE; +#ifdef USE_KQEMU + if (env->kqemu_enabled) { + kqemu_flush_page(env, addr); } +#endif } /* update the TLBs so that writes to code in the virtual page 'addr' can be detected */ -static void tlb_protect_code(CPUState *env, uint32_t addr) -{ - int i; - - addr &= TARGET_PAGE_MASK; - i = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); - tlb_protect_code1(&env->tlb_write[0][i], addr); - tlb_protect_code1(&env->tlb_write[1][i], addr); -#if !defined(CONFIG_SOFTMMU) - /* NOTE: as we generated the code for this page, it is already at - least readable */ - if (addr < MMAP_AREA_END) - mprotect((void *)addr, TARGET_PAGE_SIZE, PROT_READ); -#endif -} - -static inline void tlb_unprotect_code2(CPUTLBEntry *tlb_entry, - uint32_t phys_addr) +static void tlb_protect_code(ram_addr_t ram_addr) { - if ((tlb_entry->address & ~TARGET_PAGE_MASK) == IO_MEM_CODE && - ((tlb_entry->address & TARGET_PAGE_MASK) + tlb_entry->addend) == phys_addr) { - tlb_entry->address = (tlb_entry->address & TARGET_PAGE_MASK) | IO_MEM_NOTDIRTY; - } + cpu_physical_memory_reset_dirty(ram_addr, + ram_addr + TARGET_PAGE_SIZE, + CODE_DIRTY_FLAG); } /* update the TLB so that writes in physical page 'phys_addr' are no longer - tested self modifying code */ -static void tlb_unprotect_code_phys(CPUState *env, uint32_t phys_addr, target_ulong vaddr) + tested for self modifying code */ +static void tlb_unprotect_code_phys(CPUState *env, ram_addr_t ram_addr, + target_ulong vaddr) { - int i; - - phys_addr &= TARGET_PAGE_MASK; - phys_addr += (long)phys_ram_base; - i = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); - tlb_unprotect_code2(&env->tlb_write[0][i], phys_addr); - tlb_unprotect_code2(&env->tlb_write[1][i], phys_addr); + phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS] |= CODE_DIRTY_FLAG; } -static inline void tlb_reset_dirty_range(CPUTLBEntry *tlb_entry, +static inline void tlb_reset_dirty_range(CPUTLBEntry *tlb_entry, unsigned long start, unsigned long length) { unsigned long addr; - if ((tlb_entry->address & ~TARGET_PAGE_MASK) == IO_MEM_RAM) { - addr = (tlb_entry->address & TARGET_PAGE_MASK) + tlb_entry->addend; + 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->address = (tlb_entry->address & TARGET_PAGE_MASK) | IO_MEM_NOTDIRTY; + tlb_entry->addr_write = (tlb_entry->addr_write & TARGET_PAGE_MASK) | IO_MEM_NOTDIRTY; } } } -void cpu_physical_memory_reset_dirty(target_ulong start, target_ulong end) +void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end, + int dirty_flags) { CPUState *env; - target_ulong length, start1; - int i; + unsigned long length, start1; + int i, mask, len; + uint8_t *p; start &= TARGET_PAGE_MASK; end = TARGET_PAGE_ALIGN(end); @@ -1344,16 +1475,41 @@ void cpu_physical_memory_reset_dirty(target_ulong start, target_ulong end) length = end - start; if (length == 0) return; - memset(phys_ram_dirty + (start >> TARGET_PAGE_BITS), 0, length >> TARGET_PAGE_BITS); + len = length >> TARGET_PAGE_BITS; +#ifdef USE_KQEMU + /* XXX: should not depend on cpu context */ + env = first_cpu; + if (env->kqemu_enabled) { + ram_addr_t addr; + addr = start; + for(i = 0; i < len; i++) { + kqemu_set_notdirty(env, addr); + addr += TARGET_PAGE_SIZE; + } + } +#endif + mask = ~dirty_flags; + p = phys_ram_dirty + (start >> TARGET_PAGE_BITS); + for(i = 0; i < len; i++) + p[i] &= mask; - env = cpu_single_env; /* 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; - for(i = 0; i < CPU_TLB_SIZE; i++) - tlb_reset_dirty_range(&env->tlb_write[0][i], start1, length); - for(i = 0; i < CPU_TLB_SIZE; i++) - tlb_reset_dirty_range(&env->tlb_write[1][i], start1, length); + 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); + for(i = 0; i < CPU_TLB_SIZE; i++) + tlb_reset_dirty_range(&env->tlb_table[1][i], start1, length); +#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) + for(i = 0; i < CPU_TLB_SIZE; i++) + tlb_reset_dirty_range(&env->tlb_table[3][i], start1, length); +#endif +#endif + } #if !defined(CONFIG_SOFTMMU) /* XXX: this is expensive */ @@ -1371,7 +1527,7 @@ void cpu_physical_memory_reset_dirty(target_ulong start, target_ulong end) p->phys_addr >= start && p->phys_addr < end && (p->prot & PROT_WRITE)) { if (addr < MMAP_AREA_END) { - mprotect((void *)addr, TARGET_PAGE_SIZE, + mprotect((void *)addr, TARGET_PAGE_SIZE, p->prot & ~PROT_WRITE); } } @@ -1384,67 +1540,102 @@ void cpu_physical_memory_reset_dirty(target_ulong start, target_ulong end) #endif } -static inline void tlb_set_dirty1(CPUTLBEntry *tlb_entry, - unsigned long start) +static inline void tlb_update_dirty(CPUTLBEntry *tlb_entry) +{ + ram_addr_t ram_addr; + + 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; + if (!cpu_physical_memory_is_dirty(ram_addr)) { + tlb_entry->addr_write |= IO_MEM_NOTDIRTY; + } + } +} + +/* update the TLB according to the current state of the dirty bits */ +void cpu_tlb_update_dirty(CPUState *env) +{ + int i; + for(i = 0; i < CPU_TLB_SIZE; i++) + tlb_update_dirty(&env->tlb_table[0][i]); + for(i = 0; i < CPU_TLB_SIZE; i++) + tlb_update_dirty(&env->tlb_table[1][i]); +#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) + for(i = 0; i < CPU_TLB_SIZE; i++) + tlb_update_dirty(&env->tlb_table[3][i]); +#endif +#endif +} + +static inline void tlb_set_dirty1(CPUTLBEntry *tlb_entry, + unsigned long start) { unsigned long addr; - if ((tlb_entry->address & ~TARGET_PAGE_MASK) == IO_MEM_NOTDIRTY) { - addr = (tlb_entry->address & TARGET_PAGE_MASK) + tlb_entry->addend; + 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->address = (tlb_entry->address & TARGET_PAGE_MASK) | IO_MEM_RAM; + tlb_entry->addr_write = (tlb_entry->addr_write & TARGET_PAGE_MASK) | IO_MEM_RAM; } } } /* 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(unsigned long addr, target_ulong vaddr) +static inline void tlb_set_dirty(CPUState *env, + unsigned long addr, target_ulong vaddr) { - CPUState *env = cpu_single_env; int i; - phys_ram_dirty[(addr - (unsigned long)phys_ram_base) >> TARGET_PAGE_BITS] = 1; - addr &= TARGET_PAGE_MASK; i = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); - tlb_set_dirty1(&env->tlb_write[0][i], addr); - tlb_set_dirty1(&env->tlb_write[1][i], addr); + tlb_set_dirty1(&env->tlb_table[0][i], addr); + tlb_set_dirty1(&env->tlb_table[1][i], addr); +#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); +#endif +#endif } /* add a new TLB entry. At most one entry for a given virtual address is permitted. Return 0 if OK or 2 if the page could not be mapped (can only happen in non SOFTMMU mode for I/O pages or pages conflicting with the host address space). */ -int tlb_set_page(CPUState *env, target_ulong vaddr, - target_phys_addr_t paddr, int prot, - int is_user, int is_softmmu) +int tlb_set_page_exec(CPUState *env, target_ulong vaddr, + target_phys_addr_t paddr, int prot, + int is_user, int is_softmmu) { - PageDesc *p; - target_ulong pd; - TranslationBlock *first_tb; + PhysPageDesc *p; + unsigned long pd; unsigned int index; - target_ulong address, addend; + target_ulong address; + target_phys_addr_t addend; int ret; + CPUTLBEntry *te; + int i; - p = page_find(paddr >> TARGET_PAGE_BITS); + p = phys_page_find(paddr >> TARGET_PAGE_BITS); if (!p) { pd = IO_MEM_UNASSIGNED; - first_tb = NULL; } else { pd = p->phys_offset; - first_tb = p->first_tb; } #if defined(DEBUG_TLB) - printf("tlb_set_page: vaddr=0x%08x paddr=0x%08x prot=%x u=%d c=%d smmu=%d pd=0x%08x\n", - vaddr, paddr, prot, is_user, (first_tb != NULL), is_softmmu, pd); + printf("tlb_set_page: vaddr=" TARGET_FMT_lx " paddr=0x%08x prot=%x u=%d smmu=%d pd=0x%08lx\n", + vaddr, (int)paddr, prot, is_user, is_softmmu, pd); #endif ret = 0; #if !defined(CONFIG_SOFTMMU) - if (is_softmmu) + if (is_softmmu) #endif { - if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM) { + if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM && !(pd & IO_MEM_ROMD)) { /* IO memory case */ address = vaddr | pd; addend = paddr; @@ -1453,43 +1644,52 @@ int tlb_set_page(CPUState *env, target_ulong vaddr, address = vaddr; addend = (unsigned long)phys_ram_base + (pd & TARGET_PAGE_MASK); } - - index = (vaddr >> 12) & (CPU_TLB_SIZE - 1); + + /* 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; + } + } + } + + index = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); addend -= vaddr; + te = &env->tlb_table[is_user][index]; + te->addend = addend; if (prot & PAGE_READ) { - env->tlb_read[is_user][index].address = address; - env->tlb_read[is_user][index].addend = addend; + te->addr_read = address; } else { - env->tlb_read[is_user][index].address = -1; - env->tlb_read[is_user][index].addend = -1; + 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) { - /* ROM: access is ignored (same as unassigned) */ - env->tlb_write[is_user][index].address = vaddr | IO_MEM_ROM; - env->tlb_write[is_user][index].addend = addend; - } else - /* XXX: the PowerPC code seems not ready to handle - self modifying code with DCBI */ -#if defined(TARGET_HAS_SMC) || 1 - if (first_tb) { - /* if code is present, we use a specific memory - handler. It works only for physical memory access */ - env->tlb_write[is_user][index].address = vaddr | IO_MEM_CODE; - env->tlb_write[is_user][index].addend = addend; - } else -#endif - if ((pd & ~TARGET_PAGE_MASK) == IO_MEM_RAM && + 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)) { - env->tlb_write[is_user][index].address = vaddr | IO_MEM_NOTDIRTY; - env->tlb_write[is_user][index].addend = addend; + te->addr_write = vaddr | IO_MEM_NOTDIRTY; } else { - env->tlb_write[is_user][index].address = address; - env->tlb_write[is_user][index].addend = addend; + te->addr_write = address; } } else { - env->tlb_write[is_user][index].address = -1; - env->tlb_write[is_user][index].addend = -1; + te->addr_write = -1; } } #if !defined(CONFIG_SOFTMMU) @@ -1506,25 +1706,25 @@ int tlb_set_page(CPUState *env, target_ulong vaddr, ret = 2; } else { if (prot & PROT_WRITE) { - if ((pd & ~TARGET_PAGE_MASK) == IO_MEM_ROM || + if ((pd & ~TARGET_PAGE_MASK) == IO_MEM_ROM || #if defined(TARGET_HAS_SMC) || 1 first_tb || #endif - ((pd & ~TARGET_PAGE_MASK) == IO_MEM_RAM && + ((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); + + 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_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", @@ -1539,7 +1739,7 @@ int tlb_set_page(CPUState *env, target_ulong vaddr, /* called from signal handler: invalidate the code and unprotect the page. Return TRUE if the fault was succesfully handled. */ -int page_unprotect(unsigned long addr, unsigned long pc, void *puc) +int page_unprotect(target_ulong addr, unsigned long pc, void *puc) { #if !defined(CONFIG_SOFTMMU) VirtPageDesc *vp; @@ -1562,14 +1762,14 @@ int page_unprotect(unsigned long addr, unsigned long pc, void *puc) if (!(vp->prot & PAGE_WRITE)) return 0; #if defined(DEBUG_TLB) - printf("page_unprotect: addr=0x%08x phys_addr=0x%08x prot=%x\n", + 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] = 1; + 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; @@ -1588,9 +1788,9 @@ void tlb_flush_page(CPUState *env, target_ulong addr) { } -int tlb_set_page(CPUState *env, target_ulong vaddr, - target_phys_addr_t paddr, int prot, - int is_user, int is_softmmu) +int tlb_set_page_exec(CPUState *env, target_ulong vaddr, + target_phys_addr_t paddr, int prot, + int is_user, int is_softmmu) { return 0; } @@ -1621,7 +1821,7 @@ void page_dump(FILE *f) end = (i << (32 - L1_BITS)) | (j << TARGET_PAGE_BITS); if (start != -1) { fprintf(f, "%08lx-%08lx %08lx %c%c%c\n", - start, end, end - start, + start, end, end - start, prot & PAGE_READ ? 'r' : '-', prot & PAGE_WRITE ? 'w' : '-', prot & PAGE_EXEC ? 'x' : '-'); @@ -1638,7 +1838,7 @@ void page_dump(FILE *f) } } -int page_get_flags(unsigned long address) +int page_get_flags(target_ulong address) { PageDesc *p; @@ -1651,10 +1851,10 @@ int page_get_flags(unsigned long address) /* modify the flags of a page and invalidate the code if necessary. The flag PAGE_WRITE_ORG is positionned automatically depending on PAGE_WRITE */ -void page_set_flags(unsigned long start, unsigned long end, int flags) +void page_set_flags(target_ulong start, target_ulong end, int flags) { PageDesc *p; - unsigned long addr; + target_ulong addr; start = start & TARGET_PAGE_MASK; end = TARGET_PAGE_ALIGN(end); @@ -1665,7 +1865,7 @@ void page_set_flags(unsigned long start, unsigned long end, int flags) p = page_find_alloc(addr >> TARGET_PAGE_BITS); /* if the write protection is set, then we invalidate the code inside */ - if (!(p->flags & PAGE_WRITE) && + if (!(p->flags & PAGE_WRITE) && (flags & PAGE_WRITE) && p->first_tb) { tb_invalidate_phys_page(addr, 0, NULL); @@ -1677,18 +1877,18 @@ void page_set_flags(unsigned long start, unsigned long end, int flags) /* called from signal handler: invalidate the code and unprotect the page. Return TRUE if the fault was succesfully handled. */ -int page_unprotect(unsigned long address, unsigned long pc, void *puc) +int page_unprotect(target_ulong address, unsigned long pc, void *puc) { unsigned int page_index, prot, pindex; PageDesc *p, *p1; - unsigned long host_start, host_end, addr; + target_ulong host_start, host_end, addr; - host_start = address & host_page_mask; + host_start = address & qemu_host_page_mask; page_index = host_start >> TARGET_PAGE_BITS; p1 = page_find(page_index); if (!p1) return 0; - host_end = host_start + host_page_size; + host_end = host_start + qemu_host_page_size; p = p1; prot = 0; for(addr = host_start;addr < host_end; addr += TARGET_PAGE_SIZE) { @@ -1700,7 +1900,7 @@ int page_unprotect(unsigned long address, unsigned long pc, void *puc) if (prot & PAGE_WRITE_ORG) { pindex = (address - host_start) >> TARGET_PAGE_BITS; if (!(p1[pindex].flags & PAGE_WRITE)) { - mprotect((void *)host_start, host_page_size, + mprotect((void *)g2h(host_start), qemu_host_page_size, (prot & PAGE_BITS) | PAGE_WRITE); p1[pindex].flags |= PAGE_WRITE; /* and since the content will be modified, we must invalidate @@ -1716,11 +1916,12 @@ int page_unprotect(unsigned long address, unsigned long pc, void *puc) } /* call this function when system calls directly modify a memory area */ -void page_unprotect_range(uint8_t *data, unsigned long data_size) +/* ??? This should be redundant now we have lock_user. */ +void page_unprotect_range(target_ulong data, target_ulong data_size) { - unsigned long start, end, addr; + target_ulong start, end, addr; - start = (unsigned long)data; + start = data; end = start + data_size; start &= TARGET_PAGE_MASK; end = TARGET_PAGE_ALIGN(end); @@ -1729,37 +1930,154 @@ void page_unprotect_range(uint8_t *data, unsigned long data_size) } } -static inline void tlb_set_dirty(unsigned long addr, target_ulong vaddr) +static inline void tlb_set_dirty(CPUState *env, + unsigned long addr, target_ulong vaddr) { } #endif /* defined(CONFIG_USER_ONLY) */ +static int subpage_register (subpage_t *mmio, uint32_t start, uint32_t end, + int memory); +static void *subpage_init (target_phys_addr_t base, uint32_t *phys, + int orig_memory); +#define CHECK_SUBPAGE(addr, start_addr, start_addr2, end_addr, end_addr2, \ + need_subpage) \ + do { \ + if (addr > start_addr) \ + start_addr2 = 0; \ + else { \ + start_addr2 = start_addr & ~TARGET_PAGE_MASK; \ + if (start_addr2 > 0) \ + need_subpage = 1; \ + } \ + \ + if ((start_addr + orig_size) - addr >= TARGET_PAGE_SIZE) \ + end_addr2 = TARGET_PAGE_SIZE - 1; \ + else { \ + end_addr2 = (start_addr + orig_size - 1) & ~TARGET_PAGE_MASK; \ + if (end_addr2 < TARGET_PAGE_SIZE - 1) \ + need_subpage = 1; \ + } \ + } while (0) + /* 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, +void cpu_register_physical_memory(target_phys_addr_t start_addr, unsigned long size, unsigned long phys_offset) { - unsigned long addr, end_addr; - PageDesc *p; + target_phys_addr_t addr, end_addr; + PhysPageDesc *p; + CPUState *env; + unsigned long orig_size = size; + void *subpage; + + 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) { + p = phys_page_find(addr >> TARGET_PAGE_BITS); + if (p && p->phys_offset != IO_MEM_UNASSIGNED) { + unsigned long orig_memory = p->phys_offset; + target_phys_addr_t start_addr2, end_addr2; + int need_subpage = 0; + + CHECK_SUBPAGE(addr, start_addr, start_addr2, end_addr, end_addr2, + need_subpage); + if (need_subpage) { + if (!(orig_memory & IO_MEM_SUBPAGE)) { + subpage = subpage_init((addr & TARGET_PAGE_MASK), + &p->phys_offset, orig_memory); + } else { + subpage = io_mem_opaque[(orig_memory & ~TARGET_PAGE_MASK) + >> IO_MEM_SHIFT]; + } + subpage_register(subpage, start_addr2, end_addr2, phys_offset); + } else { + p->phys_offset = phys_offset; + if ((phys_offset & ~TARGET_PAGE_MASK) <= IO_MEM_ROM || + (phys_offset & IO_MEM_ROMD)) + phys_offset += TARGET_PAGE_SIZE; + } + } else { + p = phys_page_find_alloc(addr >> TARGET_PAGE_BITS, 1); + p->phys_offset = phys_offset; + if ((phys_offset & ~TARGET_PAGE_MASK) <= IO_MEM_ROM || + (phys_offset & IO_MEM_ROMD)) + phys_offset += TARGET_PAGE_SIZE; + else { + target_phys_addr_t start_addr2, end_addr2; + int need_subpage = 0; + + CHECK_SUBPAGE(addr, start_addr, start_addr2, end_addr, + end_addr2, need_subpage); + + if (need_subpage) { + subpage = subpage_init((addr & TARGET_PAGE_MASK), + &p->phys_offset, IO_MEM_UNASSIGNED); + subpage_register(subpage, start_addr2, end_addr2, + phys_offset); + } + } + } + } - end_addr = start_addr + size; - for(addr = start_addr; addr < end_addr; addr += TARGET_PAGE_SIZE) { - p = page_find_alloc(addr >> TARGET_PAGE_BITS); - p->phys_offset = phys_offset; - if ((phys_offset & ~TARGET_PAGE_MASK) <= IO_MEM_ROM) - phys_offset += TARGET_PAGE_SIZE; + /* since each CPU stores ram addresses in its TLB cache, we must + reset the modified entries */ + /* XXX: slow ! */ + for(env = first_cpu; env != NULL; env = env->next_cpu) { + tlb_flush(env, 1); } } -static uint32_t unassigned_mem_readb(target_phys_addr_t addr) +/* XXX: temporary until new memory mapping API */ +uint32_t cpu_get_physical_page_desc(target_phys_addr_t addr) +{ + PhysPageDesc *p; + + p = phys_page_find(addr >> TARGET_PAGE_BITS); + if (!p) + return IO_MEM_UNASSIGNED; + return p->phys_offset; +} + +/* XXX: better than nothing */ +ram_addr_t qemu_ram_alloc(unsigned int size) { + ram_addr_t addr; + if ((phys_ram_alloc_offset + size) >= phys_ram_size) { + fprintf(stderr, "Not enough memory (requested_size = %u, max memory = %d)\n", + size, phys_ram_size); + abort(); + } + addr = phys_ram_alloc_offset; + phys_ram_alloc_offset = TARGET_PAGE_ALIGN(phys_ram_alloc_offset + size); + return addr; +} + +void qemu_ram_free(ram_addr_t addr) +{ +} + +static uint32_t unassigned_mem_readb(void *opaque, target_phys_addr_t addr) +{ +#ifdef DEBUG_UNASSIGNED + printf("Unassigned mem read " TARGET_FMT_lx "\n", addr); +#endif +#ifdef TARGET_SPARC + do_unassigned_access(addr, 0, 0, 0); +#endif return 0; } -static void unassigned_mem_writeb(target_phys_addr_t addr, uint32_t val) +static void unassigned_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) { +#ifdef DEBUG_UNASSIGNED + printf("Unassigned mem write " TARGET_FMT_lx " = 0x%x\n", addr, val); +#endif +#ifdef TARGET_SPARC + do_unassigned_access(addr, 1, 0, 0); +#endif } static CPUReadMemoryFunc *unassigned_mem_read[3] = { @@ -1774,91 +2092,331 @@ static CPUWriteMemoryFunc *unassigned_mem_write[3] = { unassigned_mem_writeb, }; -/* self modifying code support in soft mmu mode : writing to a page - containing code comes to these functions */ - -static void code_mem_writeb(target_phys_addr_t addr, uint32_t val) +static void notdirty_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) { - unsigned long phys_addr; - - phys_addr = addr - (long)phys_ram_base; + 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) - tb_invalidate_phys_page_fast(phys_addr, 1); + tb_invalidate_phys_page_fast(ram_addr, 1); + dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS]; +#endif + } + stb_p((uint8_t *)(long)addr, val); +#ifdef USE_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); #endif - stb_raw((uint8_t *)addr, val); - phys_ram_dirty[phys_addr >> TARGET_PAGE_BITS] = 1; + dirty_flags |= (0xff & ~CODE_DIRTY_FLAG); + phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS] = dirty_flags; + /* 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); } -static void code_mem_writew(target_phys_addr_t addr, uint32_t val) +static void notdirty_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val) { - unsigned long phys_addr; - - phys_addr = addr - (long)phys_ram_base; + 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) - tb_invalidate_phys_page_fast(phys_addr, 2); + tb_invalidate_phys_page_fast(ram_addr, 2); + dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS]; +#endif + } + stw_p((uint8_t *)(long)addr, val); +#ifdef USE_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); #endif - stw_raw((uint8_t *)addr, val); - phys_ram_dirty[phys_addr >> TARGET_PAGE_BITS] = 1; + dirty_flags |= (0xff & ~CODE_DIRTY_FLAG); + phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS] = dirty_flags; + /* 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); } -static void code_mem_writel(target_phys_addr_t addr, uint32_t val) +static void notdirty_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) { - unsigned long phys_addr; - - phys_addr = addr - (long)phys_ram_base; + 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) - tb_invalidate_phys_page_fast(phys_addr, 4); + tb_invalidate_phys_page_fast(ram_addr, 4); + dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS]; #endif - stl_raw((uint8_t *)addr, val); - phys_ram_dirty[phys_addr >> TARGET_PAGE_BITS] = 1; + } + stl_p((uint8_t *)(long)addr, val); +#ifdef USE_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); +#endif + dirty_flags |= (0xff & ~CODE_DIRTY_FLAG); + phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS] = dirty_flags; + /* 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); } -static CPUReadMemoryFunc *code_mem_read[3] = { +static CPUReadMemoryFunc *error_mem_read[3] = { NULL, /* never used */ NULL, /* never used */ NULL, /* never used */ }; -static CPUWriteMemoryFunc *code_mem_write[3] = { - code_mem_writeb, - code_mem_writew, - code_mem_writel, +static CPUWriteMemoryFunc *notdirty_mem_write[3] = { + notdirty_mem_writeb, + notdirty_mem_writew, + notdirty_mem_writel, }; -static void notdirty_mem_writeb(uint32_t addr, uint32_t val) +#if defined(CONFIG_SOFTMMU) +/* 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) { - stb_raw((uint8_t *)addr, val); - tlb_set_dirty(addr, cpu_single_env->mem_write_vaddr); + return ldub_phys(addr); } -static void notdirty_mem_writew(uint32_t addr, uint32_t val) +static uint32_t watch_mem_readw(void *opaque, target_phys_addr_t addr) { - stw_raw((uint8_t *)addr, val); - tlb_set_dirty(addr, cpu_single_env->mem_write_vaddr); + return lduw_phys(addr); } -static void notdirty_mem_writel(uint32_t addr, uint32_t val) +static uint32_t watch_mem_readl(void *opaque, target_phys_addr_t addr) { - stl_raw((uint8_t *)addr, val); - tlb_set_dirty(addr, cpu_single_env->mem_write_vaddr); + return ldl_phys(addr); } -static CPUWriteMemoryFunc *notdirty_mem_write[3] = { - notdirty_mem_writeb, - notdirty_mem_writew, - notdirty_mem_writel, +/* 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); + stb_phys(addr, val); +} + +static void watch_mem_writew(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + addr = check_watchpoint(addr); + stw_phys(addr, val); +} + +static void watch_mem_writel(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + addr = check_watchpoint(addr); + stl_phys(addr, val); +} + +static CPUReadMemoryFunc *watch_mem_read[3] = { + watch_mem_readb, + watch_mem_readw, + watch_mem_readl, +}; + +static CPUWriteMemoryFunc *watch_mem_write[3] = { + watch_mem_writeb, + watch_mem_writew, + watch_mem_writel, +}; +#endif + +static inline uint32_t subpage_readlen (subpage_t *mmio, target_phys_addr_t addr, + unsigned int len) +{ + CPUReadMemoryFunc **mem_read; + uint32_t ret; + unsigned int idx; + + idx = SUBPAGE_IDX(addr - mmio->base); +#if defined(DEBUG_SUBPAGE) + printf("%s: subpage %p len %d addr " TARGET_FMT_plx " idx %d\n", __func__, + mmio, len, addr, idx); +#endif + mem_read = mmio->mem_read[idx]; + ret = (*mem_read[len])(mmio->opaque[idx], addr); + + return ret; +} + +static inline void subpage_writelen (subpage_t *mmio, target_phys_addr_t addr, + uint32_t value, unsigned int len) +{ + CPUWriteMemoryFunc **mem_write; + unsigned int idx; + + idx = SUBPAGE_IDX(addr - mmio->base); +#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 + mem_write = mmio->mem_write[idx]; + (*mem_write[len])(mmio->opaque[idx], addr, value); +} + +static uint32_t subpage_readb (void *opaque, target_phys_addr_t addr) +{ +#if defined(DEBUG_SUBPAGE) + printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr); +#endif + + return subpage_readlen(opaque, addr, 0); +} + +static void subpage_writeb (void *opaque, target_phys_addr_t addr, + uint32_t value) +{ +#if defined(DEBUG_SUBPAGE) + printf("%s: addr " TARGET_FMT_plx " val %08x\n", __func__, addr, value); +#endif + subpage_writelen(opaque, addr, value, 0); +} + +static uint32_t subpage_readw (void *opaque, target_phys_addr_t addr) +{ +#if defined(DEBUG_SUBPAGE) + printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr); +#endif + + return subpage_readlen(opaque, addr, 1); +} + +static void subpage_writew (void *opaque, target_phys_addr_t addr, + uint32_t value) +{ +#if defined(DEBUG_SUBPAGE) + printf("%s: addr " TARGET_FMT_plx " val %08x\n", __func__, addr, value); +#endif + subpage_writelen(opaque, addr, value, 1); +} + +static uint32_t subpage_readl (void *opaque, target_phys_addr_t addr) +{ +#if defined(DEBUG_SUBPAGE) + printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr); +#endif + + return subpage_readlen(opaque, addr, 2); +} + +static void subpage_writel (void *opaque, + target_phys_addr_t addr, uint32_t value) +{ +#if defined(DEBUG_SUBPAGE) + printf("%s: addr " TARGET_FMT_plx " val %08x\n", __func__, addr, value); +#endif + subpage_writelen(opaque, addr, value, 2); +} + +static CPUReadMemoryFunc *subpage_read[] = { + &subpage_readb, + &subpage_readw, + &subpage_readl, }; +static CPUWriteMemoryFunc *subpage_write[] = { + &subpage_writeb, + &subpage_writew, + &subpage_writel, +}; + +static int subpage_register (subpage_t *mmio, uint32_t start, uint32_t end, + int memory) +{ + int idx, eidx; + + if (start >= TARGET_PAGE_SIZE || end >= TARGET_PAGE_SIZE) + return -1; + idx = SUBPAGE_IDX(start); + eidx = SUBPAGE_IDX(end); +#if defined(DEBUG_SUBPAGE) + printf("%s: %p start %08x end %08x idx %08x eidx %08x mem %d\n", __func__, + mmio, start, end, idx, eidx, memory); +#endif + memory >>= IO_MEM_SHIFT; + for (; idx <= eidx; idx++) { + mmio->mem_read[idx] = io_mem_read[memory]; + mmio->mem_write[idx] = io_mem_write[memory]; + mmio->opaque[idx] = io_mem_opaque[memory]; + } + + return 0; +} + +static void *subpage_init (target_phys_addr_t base, uint32_t *phys, + int orig_memory) +{ + 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); +#if defined(DEBUG_SUBPAGE) + 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); + } + + return mmio; +} + static void io_mem_init(void) { - cpu_register_io_memory(IO_MEM_ROM >> IO_MEM_SHIFT, code_mem_read, unassigned_mem_write); - cpu_register_io_memory(IO_MEM_UNASSIGNED >> IO_MEM_SHIFT, unassigned_mem_read, unassigned_mem_write); - cpu_register_io_memory(IO_MEM_CODE >> IO_MEM_SHIFT, code_mem_read, code_mem_write); - cpu_register_io_memory(IO_MEM_NOTDIRTY >> IO_MEM_SHIFT, code_mem_read, notdirty_mem_write); + cpu_register_io_memory(IO_MEM_ROM >> 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; +#if defined(CONFIG_SOFTMMU) + io_mem_watch = cpu_register_io_memory(-1, watch_mem_read, + watch_mem_write, NULL); +#endif /* alloc dirty bits array */ - phys_ram_dirty = qemu_malloc(phys_ram_size >> TARGET_PAGE_BITS); + 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 @@ -1869,33 +2427,46 @@ static void io_mem_init(void) cpu_register_physical_memory(). (-1) is returned if error. */ int cpu_register_io_memory(int io_index, CPUReadMemoryFunc **mem_read, - CPUWriteMemoryFunc **mem_write) + CPUWriteMemoryFunc **mem_write, + void *opaque) { int i; if (io_index <= 0) { - if (io_index >= IO_MEM_NB_ENTRIES) + if (io_mem_nb >= IO_MEM_NB_ENTRIES) return -1; io_index = io_mem_nb++; } else { if (io_index >= IO_MEM_NB_ENTRIES) return -1; } - + for(i = 0;i < 3; i++) { io_mem_read[io_index][i] = mem_read[i]; io_mem_write[io_index][i] = mem_write[i]; } + io_mem_opaque[io_index] = opaque; return io_index << IO_MEM_SHIFT; } +CPUWriteMemoryFunc **cpu_get_io_memory_write(int io_index) +{ + return io_mem_write[io_index >> IO_MEM_SHIFT]; +} + +CPUReadMemoryFunc **cpu_get_io_memory_read(int io_index) +{ + return io_mem_read[io_index >> IO_MEM_SHIFT]; +} + /* 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, +void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, int len, int is_write) { int l, flags; target_ulong page; + void * p; while (len > 0) { page = addr & TARGET_PAGE_MASK; @@ -1908,19 +2479,24 @@ void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, if (is_write) { if (!(flags & PAGE_WRITE)) return; - memcpy((uint8_t *)addr, buf, len); + p = lock_user(addr, len, 0); + memcpy(p, buf, len); + unlock_user(p, addr, len); } else { if (!(flags & PAGE_READ)) return; - memcpy(buf, (uint8_t *)addr, len); + p = lock_user(addr, len, 1); + memcpy(buf, p, len); + unlock_user(p, addr, 0); } len -= l; buf += l; addr += l; } } + #else -void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, +void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, int len, int is_write) { int l, io_index; @@ -1928,37 +2504,39 @@ void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, uint32_t val; target_phys_addr_t page; unsigned long pd; - PageDesc *p; - + PhysPageDesc *p; + while (len > 0) { page = addr & TARGET_PAGE_MASK; l = (page + TARGET_PAGE_SIZE) - addr; if (l > len) l = len; - p = page_find(page >> TARGET_PAGE_BITS); + p = phys_page_find(page >> TARGET_PAGE_BITS); if (!p) { pd = IO_MEM_UNASSIGNED; } else { pd = p->phys_offset; } - + if (is_write) { - if ((pd & ~TARGET_PAGE_MASK) != 0) { + if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM) { io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); + /* XXX: could force cpu_single_env to NULL to avoid + potential bugs */ if (l >= 4 && ((addr & 3) == 0)) { - /* 32 bit read access */ - val = ldl_raw(buf); - io_mem_write[io_index][2](addr, val); + /* 32 bit write access */ + val = ldl_p(buf); + io_mem_write[io_index][2](io_mem_opaque[io_index], addr, val); l = 4; } else if (l >= 2 && ((addr & 1) == 0)) { - /* 16 bit read access */ - val = lduw_raw(buf); - io_mem_write[io_index][1](addr, val); + /* 16 bit write access */ + val = lduw_p(buf); + io_mem_write[io_index][1](io_mem_opaque[io_index], addr, val); l = 2; } else { - /* 8 bit access */ - val = ldub_raw(buf); - io_mem_write[io_index][0](addr, val); + /* 8 bit write access */ + val = ldub_p(buf); + io_mem_write[io_index][0](io_mem_opaque[io_index], addr, val); l = 1; } } else { @@ -1967,35 +2545,38 @@ void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, /* RAM case */ ptr = phys_ram_base + addr1; memcpy(ptr, buf, l); - /* invalidate code */ - tb_invalidate_phys_page_range(addr1, addr1 + l, 0); - /* set dirty bit */ - phys_ram_dirty[page >> TARGET_PAGE_BITS] = 1; + 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); + } } } else { if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM && - (pd & ~TARGET_PAGE_MASK) != IO_MEM_CODE) { + !(pd & IO_MEM_ROMD)) { /* I/O case */ io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); if (l >= 4 && ((addr & 3) == 0)) { /* 32 bit read access */ - val = io_mem_read[io_index][2](addr); - stl_raw(buf, val); + val = io_mem_read[io_index][2](io_mem_opaque[io_index], addr); + stl_p(buf, val); l = 4; } else if (l >= 2 && ((addr & 1) == 0)) { /* 16 bit read access */ - val = io_mem_read[io_index][1](addr); - stw_raw(buf, val); + val = io_mem_read[io_index][1](io_mem_opaque[io_index], addr); + stw_p(buf, val); l = 2; } else { - /* 8 bit access */ - val = io_mem_read[io_index][0](addr); - stb_raw(buf, val); + /* 8 bit read access */ + val = io_mem_read[io_index][0](io_mem_opaque[io_index], addr); + stb_p(buf, val); l = 1; } } else { /* RAM case */ - ptr = phys_ram_base + (pd & TARGET_PAGE_MASK) + + ptr = phys_ram_base + (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); memcpy(buf, ptr, l); } @@ -2005,14 +2586,250 @@ void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, addr += l; } } + +/* used for ROM loading : can write in RAM and ROM */ +void cpu_physical_memory_write_rom(target_phys_addr_t addr, + const uint8_t *buf, int len) +{ + int l; + uint8_t *ptr; + target_phys_addr_t page; + unsigned long pd; + PhysPageDesc *p; + + 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 && + (pd & ~TARGET_PAGE_MASK) != IO_MEM_ROM && + !(pd & IO_MEM_ROMD)) { + /* do nothing */ + } else { + unsigned long addr1; + addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); + /* ROM/RAM case */ + ptr = phys_ram_base + addr1; + memcpy(ptr, buf, l); + } + len -= l; + buf += l; + addr += l; + } +} + + +/* warning: addr must be aligned */ +uint32_t ldl_phys(target_phys_addr_t addr) +{ + int io_index; + uint8_t *ptr; + uint32_t val; + unsigned long pd; + PhysPageDesc *p; + + p = phys_page_find(addr >> TARGET_PAGE_BITS); + if (!p) { + pd = IO_MEM_UNASSIGNED; + } else { + pd = p->phys_offset; + } + + if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM && + !(pd & IO_MEM_ROMD)) { + /* I/O case */ + io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); + val = io_mem_read[io_index][2](io_mem_opaque[io_index], addr); + } else { + /* RAM case */ + ptr = phys_ram_base + (pd & TARGET_PAGE_MASK) + + (addr & ~TARGET_PAGE_MASK); + val = ldl_p(ptr); + } + return val; +} + +/* warning: addr must be aligned */ +uint64_t ldq_phys(target_phys_addr_t addr) +{ + int io_index; + uint8_t *ptr; + uint64_t val; + unsigned long pd; + PhysPageDesc *p; + + p = phys_page_find(addr >> TARGET_PAGE_BITS); + if (!p) { + pd = IO_MEM_UNASSIGNED; + } else { + pd = p->phys_offset; + } + + if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM && + !(pd & IO_MEM_ROMD)) { + /* I/O case */ + io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); +#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); +#else + val = io_mem_read[io_index][2](io_mem_opaque[io_index], addr); + val |= (uint64_t)io_mem_read[io_index][2](io_mem_opaque[io_index], addr + 4) << 32; +#endif + } else { + /* RAM case */ + ptr = phys_ram_base + (pd & TARGET_PAGE_MASK) + + (addr & ~TARGET_PAGE_MASK); + val = ldq_p(ptr); + } + return val; +} + +/* XXX: optimize */ +uint32_t ldub_phys(target_phys_addr_t addr) +{ + uint8_t val; + cpu_physical_memory_read(addr, &val, 1); + return val; +} + +/* XXX: optimize */ +uint32_t lduw_phys(target_phys_addr_t addr) +{ + uint16_t val; + cpu_physical_memory_read(addr, (uint8_t *)&val, 2); + return tswap16(val); +} + +/* warning: addr must be aligned. The ram page is not masked as dirty + and the code inside is not invalidated. It is useful if the dirty + bits are used to track modified PTEs */ +void stl_phys_notdirty(target_phys_addr_t addr, uint32_t val) +{ + int io_index; + uint8_t *ptr; + unsigned long pd; + PhysPageDesc *p; + + p = phys_page_find(addr >> TARGET_PAGE_BITS); + if (!p) { + pd = IO_MEM_UNASSIGNED; + } else { + pd = p->phys_offset; + } + + if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM) { + io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); + 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); + stl_p(ptr, val); + } +} + +void stq_phys_notdirty(target_phys_addr_t addr, uint64_t val) +{ + int io_index; + uint8_t *ptr; + unsigned long pd; + PhysPageDesc *p; + + p = phys_page_find(addr >> TARGET_PAGE_BITS); + if (!p) { + pd = IO_MEM_UNASSIGNED; + } else { + pd = p->phys_offset; + } + + if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM) { + io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); +#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); +#else + io_mem_write[io_index][2](io_mem_opaque[io_index], addr, 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) + + (addr & ~TARGET_PAGE_MASK); + stq_p(ptr, val); + } +} + +/* warning: addr must be aligned */ +void stl_phys(target_phys_addr_t addr, uint32_t val) +{ + int io_index; + uint8_t *ptr; + unsigned long pd; + PhysPageDesc *p; + + p = phys_page_find(addr >> TARGET_PAGE_BITS); + if (!p) { + pd = IO_MEM_UNASSIGNED; + } else { + pd = p->phys_offset; + } + + if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM) { + io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); + 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; + stl_p(ptr, val); + 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); + } + } +} + +/* XXX: optimize */ +void stb_phys(target_phys_addr_t addr, uint32_t val) +{ + uint8_t v = val; + cpu_physical_memory_write(addr, &v, 1); +} + +/* XXX: optimize */ +void stw_phys(target_phys_addr_t addr, uint32_t val) +{ + uint16_t v = tswap16(val); + cpu_physical_memory_write(addr, (const uint8_t *)&v, 2); +} + +/* XXX: optimize */ +void stq_phys(target_phys_addr_t addr, uint64_t val) +{ + val = tswap64(val); + cpu_physical_memory_write(addr, (const uint8_t *)&val, 8); +} + #endif /* virtual memory access for debug */ -int cpu_memory_rw_debug(CPUState *env, target_ulong addr, +int cpu_memory_rw_debug(CPUState *env, target_ulong addr, uint8_t *buf, int len, int is_write) { int l; - target_ulong page, phys_addr; + target_phys_addr_t phys_addr; + target_ulong page; while (len > 0) { page = addr & TARGET_PAGE_MASK; @@ -2023,7 +2840,7 @@ 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), + cpu_physical_memory_rw(phys_addr + (addr & ~TARGET_PAGE_MASK), buf, l, is_write); len -= l; buf += l; @@ -2032,11 +2849,59 @@ int cpu_memory_rw_debug(CPUState *env, target_ulong addr, return 0; } -#if !defined(CONFIG_USER_ONLY) +void dump_exec_info(FILE *f, + int (*cpu_fprintf)(FILE *f, const char *fmt, ...)) +{ + int i, target_code_size, max_target_code_size; + int direct_jmp_count, direct_jmp2_count, cross_page; + TranslationBlock *tb; + + target_code_size = 0; + max_target_code_size = 0; + cross_page = 0; + direct_jmp_count = 0; + direct_jmp2_count = 0; + for(i = 0; i < nb_tbs; i++) { + tb = &tbs[i]; + target_code_size += tb->size; + if (tb->size > max_target_code_size) + max_target_code_size = tb->size; + if (tb->page_addr[1] != -1) + cross_page++; + if (tb->tb_next_offset[0] != 0xffff) { + direct_jmp_count++; + if (tb->tb_next_offset[1] != 0xffff) { + direct_jmp2_count++; + } + } + } + /* XXX: avoid using doubles ? */ + cpu_fprintf(f, "TB count %d\n", nb_tbs); + cpu_fprintf(f, "TB avg target size %d max=%d bytes\n", + nb_tbs ? target_code_size / nb_tbs : 0, + max_target_code_size); + cpu_fprintf(f, "TB avg host size %d bytes (expansion ratio: %0.1f)\n", + nb_tbs ? (code_gen_ptr - code_gen_buffer) / nb_tbs : 0, + target_code_size ? (double) (code_gen_ptr - code_gen_buffer) / target_code_size : 0); + cpu_fprintf(f, "cross page TB count %d (%d%%)\n", + cross_page, + nb_tbs ? (cross_page * 100) / nb_tbs : 0); + cpu_fprintf(f, "direct jump count %d (%d%%) (2 jumps=%d %d%%)\n", + direct_jmp_count, + nb_tbs ? (direct_jmp_count * 100) / nb_tbs : 0, + direct_jmp2_count, + nb_tbs ? (direct_jmp2_count * 100) / nb_tbs : 0); + 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); +} + +#if !defined(CONFIG_USER_ONLY) #define MMUSUFFIX _cmmu #define GETPC() NULL #define env cpu_single_env +#define SOFTMMU_CODE_ACCESS #define SHIFT 0 #include "softmmu_template.h"