+
+/* dump memory mappings */
+void page_dump(FILE *f)
+{
+ unsigned long start, end;
+ int i, j, prot, prot1;
+ PageDesc *p;
+
+ fprintf(f, "%-8s %-8s %-8s %s\n",
+ "start", "end", "size", "prot");
+ start = -1;
+ end = -1;
+ prot = 0;
+ for(i = 0; i <= L1_SIZE; i++) {
+ if (i < L1_SIZE)
+ p = l1_map[i];
+ else
+ p = NULL;
+ for(j = 0;j < L2_SIZE; j++) {
+ if (!p)
+ prot1 = 0;
+ else
+ prot1 = p[j].flags;
+ if (prot1 != prot) {
+ 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,
+ prot & PAGE_READ ? 'r' : '-',
+ prot & PAGE_WRITE ? 'w' : '-',
+ prot & PAGE_EXEC ? 'x' : '-');
+ }
+ if (prot1 != 0)
+ start = end;
+ else
+ start = -1;
+ prot = prot1;
+ }
+ if (!p)
+ break;
+ }
+ }
+}
+
+int page_get_flags(target_ulong address)
+{
+ PageDesc *p;
+
+ p = page_find(address >> TARGET_PAGE_BITS);
+ if (!p)
+ return 0;
+ return p->flags;
+}
+
+/* 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(target_ulong start, target_ulong end, int flags)
+{
+ PageDesc *p;
+ target_ulong addr;
+
+ 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);
+ /* if the write protection is set, then we invalidate the code
+ inside */
+ if (!(p->flags & PAGE_WRITE) &&
+ (flags & PAGE_WRITE) &&
+ p->first_tb) {
+ tb_invalidate_phys_page(addr, 0, NULL);
+ }
+ p->flags = flags;
+ }
+ spin_unlock(&tb_lock);
+}
+
+/* called from signal handler: invalidate the code and unprotect the
+ page. Return TRUE if the fault was succesfully 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;
+
+ 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 + qemu_host_page_size;
+ p = p1;
+ prot = 0;
+ for(addr = host_start;addr < host_end; addr += TARGET_PAGE_SIZE) {
+ prot |= p->flags;
+ p++;
+ }
+ /* if the page was really writable, then we change its
+ protection back to writable */
+ if (prot & PAGE_WRITE_ORG) {
+ pindex = (address - host_start) >> TARGET_PAGE_BITS;
+ if (!(p1[pindex].flags & PAGE_WRITE)) {
+ 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
+ the corresponding translated code. */
+ tb_invalidate_phys_page(address, pc, puc);
+#ifdef DEBUG_TB_CHECK
+ tb_invalidate_check(address);
+#endif
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* call this function when system calls directly modify a memory area */
+/* ??? This should be redundant now we have lock_user. */
+void page_unprotect_range(target_ulong data, target_ulong data_size)
+{
+ target_ulong start, end, addr;
+
+ start = data;
+ end = start + data_size;
+ start &= TARGET_PAGE_MASK;
+ end = TARGET_PAGE_ALIGN(end);
+ for(addr = start; addr < end; addr += TARGET_PAGE_SIZE) {
+ page_unprotect(addr, 0, NULL);
+ }
+}
+
+static inline void tlb_set_dirty(CPUState *env,
+ unsigned long addr, target_ulong vaddr)
+{
+}
+#endif /* defined(CONFIG_USER_ONLY) */
+
+/* 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,
+ unsigned long size,
+ unsigned long phys_offset)
+{
+ target_phys_addr_t addr, end_addr;
+ PhysPageDesc *p;
+
+ size = (size + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK;
+ end_addr = start_addr + size;
+ for(addr = start_addr; addr != end_addr; addr += TARGET_PAGE_SIZE) {
+ 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 += TARGET_PAGE_SIZE;
+ }
+}
+
+static uint32_t unassigned_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+ return 0;
+}
+
+static void unassigned_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+}
+
+static CPUReadMemoryFunc *unassigned_mem_read[3] = {
+ unassigned_mem_readb,
+ unassigned_mem_readb,
+ unassigned_mem_readb,
+};
+
+static CPUWriteMemoryFunc *unassigned_mem_write[3] = {
+ unassigned_mem_writeb,
+ unassigned_mem_writeb,
+ unassigned_mem_writeb,
+};
+
+static void notdirty_mem_writeb(void *opaque, target_phys_addr_t 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)
+ 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
+ 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 notdirty_mem_writew(void *opaque, target_phys_addr_t 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)
+ 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
+ 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 notdirty_mem_writel(void *opaque, target_phys_addr_t 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)
+ tb_invalidate_phys_page_fast(ram_addr, 4);
+ dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS];
+#endif
+ }
+ 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 *error_mem_read[3] = {
+ NULL, /* never used */
+ NULL, /* never used */
+ NULL, /* never used */
+};
+
+static CPUWriteMemoryFunc *notdirty_mem_write[3] = {
+ notdirty_mem_writeb,
+ notdirty_mem_writew,
+ notdirty_mem_writel,
+};
+
+static void io_mem_init(void)
+{
+ 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;
+
+ /* 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). All functions must be supplied. 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 returned if error. */
+int cpu_register_io_memory(int io_index,
+ CPUReadMemoryFunc **mem_read,
+ CPUWriteMemoryFunc **mem_write,
+ void *opaque)
+{
+ int i;
+
+ if (io_index <= 0) {
+ 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,
+ int len, int is_write)
+{
+ int l, flags;
+ target_ulong page;
+ void * p;
+
+ while (len > 0) {
+ page = addr & TARGET_PAGE_MASK;
+ l = (page + TARGET_PAGE_SIZE) - addr;
+ if (l > len)
+ l = len;
+ flags = page_get_flags(page);
+ if (!(flags & PAGE_VALID))
+ return;
+ if (is_write) {
+ if (!(flags & PAGE_WRITE))
+ return;
+ p = lock_user(addr, len, 0);
+ memcpy(p, buf, len);
+ unlock_user(p, addr, len);
+ } else {
+ if (!(flags & PAGE_READ))
+ return;
+ 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,
+ int len, int is_write)
+{
+ int l, io_index;
+ uint8_t *ptr;
+ uint32_t val;
+ 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 (is_write) {
+ 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 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 write access */
+ val = lduw_p(buf);
+ io_mem_write[io_index][1](io_mem_opaque[io_index], addr, 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);
+ l = 1;
+ }
+ } else {
+ unsigned long addr1;
+ addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK);
+ /* RAM case */
+ ptr = phys_ram_base + addr1;
+ memcpy(ptr, buf, l);
+ 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) {
+ /* 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](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](io_mem_opaque[io_index], addr);
+ stw_p(buf, val);
+ l = 2;
+ } else {
+ /* 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) +
+ (addr & ~TARGET_PAGE_MASK);
+ memcpy(buf, ptr, l);
+ }
+ }
+ len -= l;
+ buf += l;
+ 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) {
+ /* 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) {
+ /* 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) {
+ /* 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);
+ }
+}
+
+/* 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,
+ uint8_t *buf, int len, int is_write)
+{
+ int l;
+ target_ulong page, phys_addr;
+
+ while (len > 0) {
+ page = addr & TARGET_PAGE_MASK;
+ phys_addr = cpu_get_phys_page_debug(env, page);
+ /* if no physical page mapped, return an error */
+ if (phys_addr == -1)
+ return -1;
+ 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);
+ len -= l;
+ buf += l;
+ addr += l;
+ }
+ return 0;
+}
+
+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"
+
+#define SHIFT 1
+#include "softmmu_template.h"
+
+#define SHIFT 2
+#include "softmmu_template.h"
+
+#define SHIFT 3
+#include "softmmu_template.h"
+
+#undef env
+