diff options
author | Junfeng Dong <junfeng.dong@intel.com> | 2013-11-19 17:45:23 +0800 |
---|---|---|
committer | Junfeng Dong <junfeng.dong@intel.com> | 2013-11-19 17:45:23 +0800 |
commit | 340f06c9eaee097e626c251bf7a013350649c091 (patch) | |
tree | 107e5705050a12da68fc80a56ae37afd50a2cc94 /exec.c | |
parent | 42bf3037d458a330856a0be584200c1e41c3f417 (diff) | |
download | qemu-340f06c9eaee097e626c251bf7a013350649c091.tar.gz qemu-340f06c9eaee097e626c251bf7a013350649c091.tar.bz2 qemu-340f06c9eaee097e626c251bf7a013350649c091.zip |
Import upstream 1.6.0.upstream/1.6.0
Change-Id: Icf52b556470cac8677297f2ef14ded16684f7887
Signed-off-by: Junfeng Dong <junfeng.dong@intel.com>
Diffstat (limited to 'exec.c')
-rw-r--r-- | exec.c | 3134 |
1 files changed, 793 insertions, 2341 deletions
@@ -1,5 +1,5 @@ /* - * virtual page mapping and translated block handling + * Virtual page mapping * * Copyright (c) 2003 Fabrice Bellard * @@ -29,162 +29,101 @@ #include "tcg.h" #include "hw/hw.h" #include "hw/qdev.h" -#include "osdep.h" -#include "kvm.h" -#include "hw/xen.h" -#include "qemu-timer.h" -#include "memory.h" -#include "dma.h" -#include "exec-memory.h" +#include "qemu/osdep.h" +#include "sysemu/kvm.h" +#include "sysemu/sysemu.h" +#include "hw/xen/xen.h" +#include "qemu/timer.h" +#include "qemu/config-file.h" +#include "exec/memory.h" +#include "sysemu/dma.h" +#include "exec/address-spaces.h" #if defined(CONFIG_USER_ONLY) #include <qemu.h> -#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) -#include <sys/param.h> -#if __FreeBSD_version >= 700104 -#define HAVE_KINFO_GETVMMAP -#define sigqueue sigqueue_freebsd /* avoid redefinition */ -#include <sys/time.h> -#include <sys/proc.h> -#include <machine/profile.h> -#define _KERNEL -#include <sys/user.h> -#undef _KERNEL -#undef sigqueue -#include <libutil.h> -#endif -#endif #else /* !CONFIG_USER_ONLY */ -#include "xen-mapcache.h" +#include "sysemu/xen-mapcache.h" #include "trace.h" #endif +#include "exec/cpu-all.h" -#include "cputlb.h" - -#include "memory-internal.h" - -//#define DEBUG_TB_INVALIDATE -//#define DEBUG_FLUSH -//#define DEBUG_UNASSIGNED +#include "exec/cputlb.h" +#include "translate-all.h" -/* make various TB consistency checks */ -//#define DEBUG_TB_CHECK +#include "exec/memory-internal.h" -//#define DEBUG_IOPORT //#define DEBUG_SUBPAGE #if !defined(CONFIG_USER_ONLY) -/* TB consistency checks only implemented for usermode emulation. */ -#undef DEBUG_TB_CHECK -#endif - -#define SMC_BITMAP_USE_THRESHOLD 10 - -static TranslationBlock *tbs; -static int code_gen_max_blocks; -TranslationBlock *tb_phys_hash[CODE_GEN_PHYS_HASH_SIZE]; -static int nb_tbs; -/* any access to the tbs or the page table must use this lock */ -spinlock_t tb_lock = SPIN_LOCK_UNLOCKED; - -uint8_t *code_gen_prologue; -static uint8_t *code_gen_buffer; -static size_t code_gen_buffer_size; -/* threshold to flush the translated code buffer */ -static size_t code_gen_buffer_max_size; -static uint8_t *code_gen_ptr; - -#if !defined(CONFIG_USER_ONLY) -int phys_ram_fd; static int in_migration; -RAMList ram_list = { .blocks = QLIST_HEAD_INITIALIZER(ram_list.blocks) }; +RAMList ram_list = { .blocks = QTAILQ_HEAD_INITIALIZER(ram_list.blocks) }; static MemoryRegion *system_memory; static MemoryRegion *system_io; AddressSpace address_space_io; AddressSpace address_space_memory; -DMAContext dma_context_memory; -MemoryRegion io_mem_ram, io_mem_rom, io_mem_unassigned, io_mem_notdirty; -static MemoryRegion io_mem_subpage_ram; +MemoryRegion io_mem_rom, io_mem_notdirty; +static MemoryRegion io_mem_unassigned; #endif -CPUArchState *first_cpu; +CPUState *first_cpu; /* current CPU in the current thread. It is only valid inside cpu_exec() */ -DEFINE_TLS(CPUArchState *,cpu_single_env); +DEFINE_TLS(CPUState *, current_cpu); /* 0 = Do not count executed instructions. 1 = Precise instruction counting. 2 = Adaptive rate instruction counting. */ -int use_icount = 0; - -typedef struct PageDesc { - /* 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 */ - unsigned int code_write_count; - uint8_t *code_bitmap; -#if defined(CONFIG_USER_ONLY) - unsigned long flags; -#endif -} PageDesc; +int use_icount; -/* In system mode we want L1_MAP to be based on ram offsets, - while in user mode we want it to be based on virtual addresses. */ #if !defined(CONFIG_USER_ONLY) -#if HOST_LONG_BITS < TARGET_PHYS_ADDR_SPACE_BITS -# define L1_MAP_ADDR_SPACE_BITS HOST_LONG_BITS -#else -# define L1_MAP_ADDR_SPACE_BITS TARGET_PHYS_ADDR_SPACE_BITS -#endif -#else -# define L1_MAP_ADDR_SPACE_BITS TARGET_VIRT_ADDR_SPACE_BITS -#endif - -/* Size of the L2 (and L3, etc) page tables. */ -#define L2_BITS 10 -#define L2_SIZE (1 << L2_BITS) - -#define P_L2_LEVELS \ - (((TARGET_PHYS_ADDR_SPACE_BITS - TARGET_PAGE_BITS - 1) / L2_BITS) + 1) -/* The bits remaining after N lower levels of page tables. */ -#define V_L1_BITS_REM \ - ((L1_MAP_ADDR_SPACE_BITS - TARGET_PAGE_BITS) % L2_BITS) - -#if V_L1_BITS_REM < 4 -#define V_L1_BITS (V_L1_BITS_REM + L2_BITS) -#else -#define V_L1_BITS V_L1_BITS_REM -#endif +typedef struct PhysPageEntry PhysPageEntry; -#define V_L1_SIZE ((target_ulong)1 << V_L1_BITS) +struct PhysPageEntry { + uint16_t is_leaf : 1; + /* index into phys_sections (is_leaf) or phys_map_nodes (!is_leaf) */ + uint16_t ptr : 15; +}; -#define V_L1_SHIFT (L1_MAP_ADDR_SPACE_BITS - TARGET_PAGE_BITS - V_L1_BITS) +typedef PhysPageEntry Node[L2_SIZE]; -uintptr_t qemu_real_host_page_size; -uintptr_t qemu_host_page_size; -uintptr_t qemu_host_page_mask; +struct AddressSpaceDispatch { + /* This is a multi-level map on the physical address space. + * The bottom level has pointers to MemoryRegionSections. + */ + PhysPageEntry phys_map; + Node *nodes; + MemoryRegionSection *sections; + AddressSpace *as; +}; -/* This is a multi-level map on the virtual address space. - The bottom level has pointers to PageDesc. */ -static void *l1_map[V_L1_SIZE]; +#define SUBPAGE_IDX(addr) ((addr) & ~TARGET_PAGE_MASK) +typedef struct subpage_t { + MemoryRegion iomem; + AddressSpace *as; + hwaddr base; + uint16_t sub_section[TARGET_PAGE_SIZE]; +} subpage_t; -#if !defined(CONFIG_USER_ONLY) +#define PHYS_SECTION_UNASSIGNED 0 +#define PHYS_SECTION_NOTDIRTY 1 +#define PHYS_SECTION_ROM 2 +#define PHYS_SECTION_WATCH 3 -static MemoryRegionSection *phys_sections; -static unsigned phys_sections_nb, phys_sections_nb_alloc; -static uint16_t phys_section_unassigned; -static uint16_t phys_section_notdirty; -static uint16_t phys_section_rom; -static uint16_t phys_section_watch; +typedef struct PhysPageMap { + unsigned sections_nb; + unsigned sections_nb_alloc; + unsigned nodes_nb; + unsigned nodes_nb_alloc; + Node *nodes; + MemoryRegionSection *sections; +} PhysPageMap; -/* Simple allocator for PhysPageEntry nodes */ -static PhysPageEntry (*phys_map_nodes)[L2_SIZE]; -static unsigned phys_map_nodes_nb, phys_map_nodes_nb_alloc; +static PhysPageMap *prev_map; +static PhysPageMap next_map; #define PHYS_MAP_NODE_NIL (((uint16_t)~0) >> 1) @@ -194,191 +133,18 @@ static void *qemu_safe_ram_ptr(ram_addr_t addr); static MemoryRegion io_mem_watch; #endif -static void tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc, - tb_page_addr_t phys_page2); - -/* statistics */ -static int tb_flush_count; -static int tb_phys_invalidate_count; - -#ifdef _WIN32 -static inline void map_exec(void *addr, long size) -{ - DWORD old_protect; - VirtualProtect(addr, size, - PAGE_EXECUTE_READWRITE, &old_protect); - -} -#else -static inline void map_exec(void *addr, long size) -{ - unsigned long start, end, page_size; - - page_size = getpagesize(); - start = (unsigned long)addr; - start &= ~(page_size - 1); - - end = (unsigned long)addr + size; - end += page_size - 1; - end &= ~(page_size - 1); - - mprotect((void *)start, end - start, - PROT_READ | PROT_WRITE | PROT_EXEC); -} -#endif - -static void page_init(void) -{ - /* NOTE: we can always suppose that qemu_host_page_size >= - TARGET_PAGE_SIZE */ -#ifdef _WIN32 - { - SYSTEM_INFO system_info; - - GetSystemInfo(&system_info); - qemu_real_host_page_size = system_info.dwPageSize; - } -#else - qemu_real_host_page_size = getpagesize(); -#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_mask = ~(qemu_host_page_size - 1); - -#if defined(CONFIG_BSD) && defined(CONFIG_USER_ONLY) - { -#ifdef HAVE_KINFO_GETVMMAP - struct kinfo_vmentry *freep; - int i, cnt; - - freep = kinfo_getvmmap(getpid(), &cnt); - if (freep) { - mmap_lock(); - for (i = 0; i < cnt; i++) { - unsigned long startaddr, endaddr; - - startaddr = freep[i].kve_start; - endaddr = freep[i].kve_end; - if (h2g_valid(startaddr)) { - startaddr = h2g(startaddr) & TARGET_PAGE_MASK; - - if (h2g_valid(endaddr)) { - endaddr = h2g(endaddr); - page_set_flags(startaddr, endaddr, PAGE_RESERVED); - } else { -#if TARGET_ABI_BITS <= L1_MAP_ADDR_SPACE_BITS - endaddr = ~0ul; - page_set_flags(startaddr, endaddr, PAGE_RESERVED); -#endif - } - } - } - free(freep); - mmap_unlock(); - } -#else - FILE *f; - - last_brk = (unsigned long)sbrk(0); - - f = fopen("/compat/linux/proc/self/maps", "r"); - if (f) { - mmap_lock(); - - do { - unsigned long startaddr, endaddr; - int n; - - n = fscanf (f, "%lx-%lx %*[^\n]\n", &startaddr, &endaddr); - - if (n == 2 && h2g_valid(startaddr)) { - startaddr = h2g(startaddr) & TARGET_PAGE_MASK; - - if (h2g_valid(endaddr)) { - endaddr = h2g(endaddr); - } else { - endaddr = ~0ul; - } - page_set_flags(startaddr, endaddr, PAGE_RESERVED); - } - } while (!feof(f)); - - fclose(f); - mmap_unlock(); - } -#endif - } -#endif -} - -static PageDesc *page_find_alloc(tb_page_addr_t index, int alloc) -{ - PageDesc *pd; - void **lp; - int i; - -#if defined(CONFIG_USER_ONLY) - /* We can't use g_malloc because it may recurse into a locked mutex. */ -# define ALLOC(P, SIZE) \ - do { \ - P = mmap(NULL, SIZE, PROT_READ | PROT_WRITE, \ - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); \ - } while (0) -#else -# define ALLOC(P, SIZE) \ - do { P = g_malloc0(SIZE); } while (0) -#endif - - /* Level 1. Always allocated. */ - lp = l1_map + ((index >> V_L1_SHIFT) & (V_L1_SIZE - 1)); - - /* Level 2..N-1. */ - for (i = V_L1_SHIFT / L2_BITS - 1; i > 0; i--) { - void **p = *lp; - - if (p == NULL) { - if (!alloc) { - return NULL; - } - ALLOC(p, sizeof(void *) * L2_SIZE); - *lp = p; - } - - lp = p + ((index >> (i * L2_BITS)) & (L2_SIZE - 1)); - } - - pd = *lp; - if (pd == NULL) { - if (!alloc) { - return NULL; - } - ALLOC(pd, sizeof(PageDesc) * L2_SIZE); - *lp = pd; - } - -#undef ALLOC - - return pd + (index & (L2_SIZE - 1)); -} - -static inline PageDesc *page_find(tb_page_addr_t index) -{ - return page_find_alloc(index, 0); -} #if !defined(CONFIG_USER_ONLY) static void phys_map_node_reserve(unsigned nodes) { - if (phys_map_nodes_nb + nodes > phys_map_nodes_nb_alloc) { - typedef PhysPageEntry Node[L2_SIZE]; - phys_map_nodes_nb_alloc = MAX(phys_map_nodes_nb_alloc * 2, 16); - phys_map_nodes_nb_alloc = MAX(phys_map_nodes_nb_alloc, - phys_map_nodes_nb + nodes); - phys_map_nodes = g_renew(Node, phys_map_nodes, - phys_map_nodes_nb_alloc); + if (next_map.nodes_nb + nodes > next_map.nodes_nb_alloc) { + next_map.nodes_nb_alloc = MAX(next_map.nodes_nb_alloc * 2, + 16); + next_map.nodes_nb_alloc = MAX(next_map.nodes_nb_alloc, + next_map.nodes_nb + nodes); + next_map.nodes = g_renew(Node, next_map.nodes, + next_map.nodes_nb_alloc); } } @@ -387,22 +153,16 @@ static uint16_t phys_map_node_alloc(void) unsigned i; uint16_t ret; - ret = phys_map_nodes_nb++; + ret = next_map.nodes_nb++; assert(ret != PHYS_MAP_NODE_NIL); - assert(ret != phys_map_nodes_nb_alloc); + assert(ret != next_map.nodes_nb_alloc); for (i = 0; i < L2_SIZE; ++i) { - phys_map_nodes[ret][i].is_leaf = 0; - phys_map_nodes[ret][i].ptr = PHYS_MAP_NODE_NIL; + next_map.nodes[ret][i].is_leaf = 0; + next_map.nodes[ret][i].ptr = PHYS_MAP_NODE_NIL; } return ret; } -static void phys_map_nodes_reset(void) -{ - phys_map_nodes_nb = 0; -} - - static void phys_page_set_level(PhysPageEntry *lp, hwaddr *index, hwaddr *nb, uint16_t leaf, int level) @@ -413,15 +173,15 @@ static void phys_page_set_level(PhysPageEntry *lp, hwaddr *index, if (!lp->is_leaf && lp->ptr == PHYS_MAP_NODE_NIL) { lp->ptr = phys_map_node_alloc(); - p = phys_map_nodes[lp->ptr]; + p = next_map.nodes[lp->ptr]; if (level == 0) { for (i = 0; i < L2_SIZE; i++) { p[i].is_leaf = 1; - p[i].ptr = phys_section_unassigned; + p[i].ptr = PHYS_SECTION_UNASSIGNED; } } } else { - p = phys_map_nodes[lp->ptr]; + p = next_map.nodes[lp->ptr]; } lp = &p[(*index >> (level * L2_BITS)) & (L2_SIZE - 1)]; @@ -448,1068 +208,224 @@ static void phys_page_set(AddressSpaceDispatch *d, phys_page_set_level(&d->phys_map, &index, &nb, leaf, P_L2_LEVELS - 1); } -MemoryRegionSection *phys_page_find(AddressSpaceDispatch *d, hwaddr index) +static MemoryRegionSection *phys_page_find(PhysPageEntry lp, hwaddr index, + Node *nodes, MemoryRegionSection *sections) { - PhysPageEntry lp = d->phys_map; PhysPageEntry *p; int i; - uint16_t s_index = phys_section_unassigned; for (i = P_L2_LEVELS - 1; i >= 0 && !lp.is_leaf; i--) { if (lp.ptr == PHYS_MAP_NODE_NIL) { - goto not_found; + return §ions[PHYS_SECTION_UNASSIGNED]; } - p = phys_map_nodes[lp.ptr]; + p = nodes[lp.ptr]; lp = p[(index >> (i * L2_BITS)) & (L2_SIZE - 1)]; } - - s_index = lp.ptr; -not_found: - return &phys_sections[s_index]; + return §ions[lp.ptr]; } bool memory_region_is_unassigned(MemoryRegion *mr) { - return mr != &io_mem_ram && mr != &io_mem_rom - && mr != &io_mem_notdirty && !mr->rom_device + return mr != &io_mem_rom && mr != &io_mem_notdirty && !mr->rom_device && mr != &io_mem_watch; } -#define mmap_lock() do { } while(0) -#define mmap_unlock() do { } while(0) -#endif +static MemoryRegionSection *address_space_lookup_region(AddressSpaceDispatch *d, + hwaddr addr, + bool resolve_subpage) +{ + MemoryRegionSection *section; + subpage_t *subpage; -#if defined(CONFIG_USER_ONLY) -/* Currently it is not recommended to allocate big chunks of data in - user mode. It will change when a dedicated libc will be used. */ -/* ??? 64-bit hosts ought to have no problem mmaping data outside the - region in which the guest needs to run. Revisit this. */ -#define USE_STATIC_CODE_GEN_BUFFER -#endif + section = phys_page_find(d->phys_map, addr >> TARGET_PAGE_BITS, + d->nodes, d->sections); + if (resolve_subpage && section->mr->subpage) { + subpage = container_of(section->mr, subpage_t, iomem); + section = &d->sections[subpage->sub_section[SUBPAGE_IDX(addr)]]; + } + return section; +} -/* ??? Should configure for this, not list operating systems here. */ -#if (defined(__linux__) \ - || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) \ - || defined(__DragonFly__) || defined(__OpenBSD__) \ - || defined(__NetBSD__)) -# define USE_MMAP -#endif +static MemoryRegionSection * +address_space_translate_internal(AddressSpaceDispatch *d, hwaddr addr, hwaddr *xlat, + hwaddr *plen, bool resolve_subpage) +{ + MemoryRegionSection *section; + Int128 diff; -/* Minimum size of the code gen buffer. This number is randomly chosen, - but not so small that we can't have a fair number of TB's live. */ -#define MIN_CODE_GEN_BUFFER_SIZE (1024u * 1024) - -/* Maximum size of the code gen buffer we'd like to use. Unless otherwise - indicated, this is constrained by the range of direct branches on the - host cpu, as used by the TCG implementation of goto_tb. */ -#if defined(__x86_64__) -# define MAX_CODE_GEN_BUFFER_SIZE (2ul * 1024 * 1024 * 1024) -#elif defined(__sparc__) -# define MAX_CODE_GEN_BUFFER_SIZE (2ul * 1024 * 1024 * 1024) -#elif defined(__arm__) -# define MAX_CODE_GEN_BUFFER_SIZE (16u * 1024 * 1024) -#elif defined(__s390x__) - /* We have a +- 4GB range on the branches; leave some slop. */ -# define MAX_CODE_GEN_BUFFER_SIZE (3ul * 1024 * 1024 * 1024) -#else -# define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1) -#endif + section = address_space_lookup_region(d, addr, resolve_subpage); + /* Compute offset within MemoryRegionSection */ + addr -= section->offset_within_address_space; -#define DEFAULT_CODE_GEN_BUFFER_SIZE_1 (32u * 1024 * 1024) + /* Compute offset within MemoryRegion */ + *xlat = addr + section->offset_within_region; -#define DEFAULT_CODE_GEN_BUFFER_SIZE \ - (DEFAULT_CODE_GEN_BUFFER_SIZE_1 < MAX_CODE_GEN_BUFFER_SIZE \ - ? DEFAULT_CODE_GEN_BUFFER_SIZE_1 : MAX_CODE_GEN_BUFFER_SIZE) + diff = int128_sub(section->mr->size, int128_make64(addr)); + *plen = int128_get64(int128_min(diff, int128_make64(*plen))); + return section; +} -static inline size_t size_code_gen_buffer(size_t tb_size) +MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr, + hwaddr *xlat, hwaddr *plen, + bool is_write) { - /* Size the buffer. */ - if (tb_size == 0) { -#ifdef USE_STATIC_CODE_GEN_BUFFER - tb_size = DEFAULT_CODE_GEN_BUFFER_SIZE; -#else - /* ??? Needs adjustments. */ - /* ??? If we relax the requirement that CONFIG_USER_ONLY use the - static buffer, we could size this on RESERVED_VA, on the text - segment size of the executable, or continue to use the default. */ - tb_size = (unsigned long)(ram_size / 4); -#endif - } - if (tb_size < MIN_CODE_GEN_BUFFER_SIZE) { - tb_size = MIN_CODE_GEN_BUFFER_SIZE; - } - if (tb_size > MAX_CODE_GEN_BUFFER_SIZE) { - tb_size = MAX_CODE_GEN_BUFFER_SIZE; - } - code_gen_buffer_size = tb_size; - return tb_size; -} - -#ifdef USE_STATIC_CODE_GEN_BUFFER -static uint8_t static_code_gen_buffer[DEFAULT_CODE_GEN_BUFFER_SIZE] - __attribute__((aligned(CODE_GEN_ALIGN))); - -static inline void *alloc_code_gen_buffer(void) -{ - map_exec(static_code_gen_buffer, code_gen_buffer_size); - return static_code_gen_buffer; -} -#elif defined(USE_MMAP) -static inline void *alloc_code_gen_buffer(void) -{ - int flags = MAP_PRIVATE | MAP_ANONYMOUS; - uintptr_t start = 0; - void *buf; - - /* Constrain the position of the buffer based on the host cpu. - Note that these addresses are chosen in concert with the - addresses assigned in the relevant linker script file. */ -# if defined(__PIE__) || defined(__PIC__) - /* Don't bother setting a preferred location if we're building - a position-independent executable. We're more likely to get - an address near the main executable if we let the kernel - choose the address. */ -# elif defined(__x86_64__) && defined(MAP_32BIT) - /* Force the memory down into low memory with the executable. - Leave the choice of exact location with the kernel. */ - flags |= MAP_32BIT; - /* Cannot expect to map more than 800MB in low memory. */ - if (code_gen_buffer_size > 800u * 1024 * 1024) { - code_gen_buffer_size = 800u * 1024 * 1024; - } -# elif defined(__sparc__) - start = 0x40000000ul; -# elif defined(__s390x__) - start = 0x90000000ul; -# endif + IOMMUTLBEntry iotlb; + MemoryRegionSection *section; + MemoryRegion *mr; + hwaddr len = *plen; - buf = mmap((void *)start, code_gen_buffer_size, - PROT_WRITE | PROT_READ | PROT_EXEC, flags, -1, 0); - return buf == MAP_FAILED ? NULL : buf; -} -#else -static inline void *alloc_code_gen_buffer(void) -{ - void *buf = g_malloc(code_gen_buffer_size); - if (buf) { - map_exec(buf, code_gen_buffer_size); - } - return buf; -} -#endif /* USE_STATIC_CODE_GEN_BUFFER, USE_MMAP */ + for (;;) { + section = address_space_translate_internal(as->dispatch, addr, &addr, plen, true); + mr = section->mr; -static inline void code_gen_alloc(size_t tb_size) -{ - code_gen_buffer_size = size_code_gen_buffer(tb_size); - code_gen_buffer = alloc_code_gen_buffer(); - if (code_gen_buffer == NULL) { - fprintf(stderr, "Could not allocate dynamic translator buffer\n"); - exit(1); + if (!mr->iommu_ops) { + break; + } + + iotlb = mr->iommu_ops->translate(mr, addr); + addr = ((iotlb.translated_addr & ~iotlb.addr_mask) + | (addr & iotlb.addr_mask)); + len = MIN(len, (addr | iotlb.addr_mask) - addr + 1); + if (!(iotlb.perm & (1 << is_write))) { + mr = &io_mem_unassigned; + break; + } + + as = iotlb.target_as; } - /* Steal room for the prologue at the end of the buffer. This ensures - (via the MAX_CODE_GEN_BUFFER_SIZE limits above) that direct branches - from TB's to the prologue are going to be in range. It also means - that we don't need to mark (additional) portions of the data segment - as executable. */ - code_gen_prologue = code_gen_buffer + code_gen_buffer_size - 1024; - code_gen_buffer_size -= 1024; - - code_gen_buffer_max_size = code_gen_buffer_size - - (TCG_MAX_OP_SIZE * OPC_BUF_SIZE); - code_gen_max_blocks = code_gen_buffer_size / CODE_GEN_AVG_BLOCK_SIZE; - tbs = g_malloc(code_gen_max_blocks * sizeof(TranslationBlock)); -} - -/* Must be called before using the QEMU cpus. 'tb_size' is the size - (in bytes) allocated to the translation buffer. Zero means default - size. */ -void tcg_exec_init(unsigned long tb_size) -{ - cpu_gen_init(); - code_gen_alloc(tb_size); - code_gen_ptr = code_gen_buffer; - tcg_register_jit(code_gen_buffer, code_gen_buffer_size); - page_init(); -#if !defined(CONFIG_USER_ONLY) || !defined(CONFIG_USE_GUEST_BASE) - /* There's no guest base to take into account, so go ahead and - initialize the prologue now. */ - tcg_prologue_init(&tcg_ctx); -#endif + *plen = len; + *xlat = addr; + return mr; } -bool tcg_enabled(void) +MemoryRegionSection * +address_space_translate_for_iotlb(AddressSpace *as, hwaddr addr, hwaddr *xlat, + hwaddr *plen) { - return code_gen_buffer != NULL; + MemoryRegionSection *section; + section = address_space_translate_internal(as->dispatch, addr, xlat, plen, false); + + assert(!section->mr->iommu_ops); + return section; } +#endif void cpu_exec_init_all(void) { #if !defined(CONFIG_USER_ONLY) + qemu_mutex_init(&ram_list.mutex); memory_map_init(); io_mem_init(); #endif } -#if defined(CPU_SAVE_VERSION) && !defined(CONFIG_USER_ONLY) +#if !defined(CONFIG_USER_ONLY) static int cpu_common_post_load(void *opaque, int version_id) { - CPUArchState *env = opaque; + CPUState *cpu = opaque; /* 0x01 was CPU_INTERRUPT_EXIT. This line can be removed when the version_id is increased. */ - env->interrupt_request &= ~0x01; - tlb_flush(env, 1); + cpu->interrupt_request &= ~0x01; + tlb_flush(cpu->env_ptr, 1); return 0; } -static const VMStateDescription vmstate_cpu_common = { +const VMStateDescription vmstate_cpu_common = { .name = "cpu_common", .version_id = 1, .minimum_version_id = 1, .minimum_version_id_old = 1, .post_load = cpu_common_post_load, .fields = (VMStateField []) { - VMSTATE_UINT32(halted, CPUArchState), - VMSTATE_UINT32(interrupt_request, CPUArchState), + VMSTATE_UINT32(halted, CPUState), + VMSTATE_UINT32(interrupt_request, CPUState), VMSTATE_END_OF_LIST() } }; + #endif -CPUArchState *qemu_get_cpu(int cpu) +CPUState *qemu_get_cpu(int index) { - CPUArchState *env = first_cpu; + CPUState *cpu = first_cpu; - while (env) { - if (env->cpu_index == cpu) + while (cpu) { + if (cpu->cpu_index == index) { break; - env = env->next_cpu; + } + cpu = cpu->next_cpu; } - return env; + return cpu; +} + +void qemu_for_each_cpu(void (*func)(CPUState *cpu, void *data), void *data) +{ + CPUState *cpu; + + cpu = first_cpu; + while (cpu) { + func(cpu, data); + cpu = cpu->next_cpu; + } } void cpu_exec_init(CPUArchState *env) { -#ifndef CONFIG_USER_ONLY CPUState *cpu = ENV_GET_CPU(env); -#endif - CPUArchState **penv; + CPUClass *cc = CPU_GET_CLASS(cpu); + CPUState **pcpu; int cpu_index; #if defined(CONFIG_USER_ONLY) cpu_list_lock(); #endif - env->next_cpu = NULL; - penv = &first_cpu; + cpu->next_cpu = NULL; + pcpu = &first_cpu; cpu_index = 0; - while (*penv != NULL) { - penv = &(*penv)->next_cpu; + while (*pcpu != NULL) { + pcpu = &(*pcpu)->next_cpu; cpu_index++; } - env->cpu_index = cpu_index; - env->numa_node = 0; + cpu->cpu_index = cpu_index; + cpu->numa_node = 0; QTAILQ_INIT(&env->breakpoints); QTAILQ_INIT(&env->watchpoints); #ifndef CONFIG_USER_ONLY cpu->thread_id = qemu_get_thread_id(); #endif - *penv = env; + *pcpu = cpu; #if defined(CONFIG_USER_ONLY) cpu_list_unlock(); #endif + if (qdev_get_vmsd(DEVICE(cpu)) == NULL) { + vmstate_register(NULL, cpu_index, &vmstate_cpu_common, cpu); + } #if defined(CPU_SAVE_VERSION) && !defined(CONFIG_USER_ONLY) - vmstate_register(NULL, cpu_index, &vmstate_cpu_common, env); register_savevm(NULL, "cpu", cpu_index, CPU_SAVE_VERSION, cpu_save, cpu_load, env); + assert(cc->vmsd == NULL); + assert(qdev_get_vmsd(DEVICE(cpu)) == NULL); #endif -} - -/* Allocate a new translation block. Flush the translation buffer if - too many translation blocks or too much generated code. */ -static TranslationBlock *tb_alloc(target_ulong pc) -{ - TranslationBlock *tb; - - if (nb_tbs >= code_gen_max_blocks || - (code_gen_ptr - code_gen_buffer) >= code_gen_buffer_max_size) - return NULL; - tb = &tbs[nb_tbs++]; - tb->pc = pc; - tb->cflags = 0; - return tb; -} - -void tb_free(TranslationBlock *tb) -{ - /* In practice this is mostly used for single use temporary TB - Ignore the hard cases and just back up if this TB happens to - be the last one generated. */ - if (nb_tbs > 0 && tb == &tbs[nb_tbs - 1]) { - code_gen_ptr = tb->tc_ptr; - nb_tbs--; - } -} - -static inline void invalidate_page_bitmap(PageDesc *p) -{ - if (p->code_bitmap) { - g_free(p->code_bitmap); - p->code_bitmap = NULL; - } - p->code_write_count = 0; -} - -/* Set to NULL all the 'first_tb' fields in all PageDescs. */ - -static void page_flush_tb_1 (int level, void **lp) -{ - int i; - - if (*lp == NULL) { - return; - } - if (level == 0) { - PageDesc *pd = *lp; - for (i = 0; i < L2_SIZE; ++i) { - pd[i].first_tb = NULL; - invalidate_page_bitmap(pd + i); - } - } else { - void **pp = *lp; - for (i = 0; i < L2_SIZE; ++i) { - page_flush_tb_1 (level - 1, pp + i); - } - } -} - -static void page_flush_tb(void) -{ - int i; - for (i = 0; i < V_L1_SIZE; i++) { - page_flush_tb_1(V_L1_SHIFT / L2_BITS - 1, l1_map + i); - } -} - -/* flush all the translation blocks */ -/* XXX: tb_flush is currently not thread safe */ -void tb_flush(CPUArchState *env1) -{ - CPUArchState *env; -#if defined(DEBUG_FLUSH) - printf("qemu: flush code_size=%ld nb_tbs=%d avg_tb_size=%ld\n", - (unsigned long)(code_gen_ptr - code_gen_buffer), - nb_tbs, nb_tbs > 0 ? - ((unsigned long)(code_gen_ptr - code_gen_buffer)) / nb_tbs : 0); -#endif - if ((unsigned long)(code_gen_ptr - code_gen_buffer) > code_gen_buffer_size) - cpu_abort(env1, "Internal error: code buffer overflow\n"); - - nb_tbs = 0; - - 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(target_ulong address) -{ - TranslationBlock *tb; - int i; - address &= TARGET_PAGE_MASK; - 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=" TARGET_FMT_lx - " PC=%08lx size=%04x\n", - address, (long)tb->pc, tb->size); - } - } - } -} - -/* verify that all the pages have correct rights for code */ -static void tb_page_check(void) -{ - TranslationBlock *tb; - int i, flags1, flags2; - - 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", - (long)tb->pc, tb->size, flags1, flags2); - } - } - } -} - -#endif - -/* invalidate one TB */ -static inline void tb_remove(TranslationBlock **ptb, TranslationBlock *tb, - int next_offset) -{ - TranslationBlock *tb1; - for(;;) { - tb1 = *ptb; - if (tb1 == tb) { - *ptb = *(TranslationBlock **)((char *)tb1 + next_offset); - break; - } - ptb = (TranslationBlock **)((char *)tb1 + next_offset); - } -} - -static inline void tb_page_remove(TranslationBlock **ptb, TranslationBlock *tb) -{ - TranslationBlock *tb1; - unsigned int n1; - - for(;;) { - tb1 = *ptb; - n1 = (uintptr_t)tb1 & 3; - tb1 = (TranslationBlock *)((uintptr_t)tb1 & ~3); - if (tb1 == tb) { - *ptb = tb1->page_next[n1]; - break; - } - ptb = &tb1->page_next[n1]; - } -} - -static inline void tb_jmp_remove(TranslationBlock *tb, int n) -{ - TranslationBlock *tb1, **ptb; - unsigned int n1; - - ptb = &tb->jmp_next[n]; - tb1 = *ptb; - if (tb1) { - /* find tb(n) in circular list */ - for(;;) { - tb1 = *ptb; - n1 = (uintptr_t)tb1 & 3; - tb1 = (TranslationBlock *)((uintptr_t)tb1 & ~3); - if (n1 == n && tb1 == tb) - break; - if (n1 == 2) { - ptb = &tb1->jmp_first; - } else { - ptb = &tb1->jmp_next[n1]; - } - } - /* now we can suppress tb(n) from the list */ - *ptb = tb->jmp_next[n]; - - tb->jmp_next[n] = NULL; - } -} - -/* reset the jump entry 'n' of a TB so that it is not chained to - another TB */ -static inline void tb_reset_jump(TranslationBlock *tb, int n) -{ - tb_set_jmp_target(tb, n, (uintptr_t)(tb->tc_ptr + tb->tb_next_offset[n])); -} - -void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr) -{ - CPUArchState *env; - PageDesc *p; - unsigned int h, n1; - tb_page_addr_t 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_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 */ - tb_jmp_remove(tb, 0); - tb_jmp_remove(tb, 1); - - /* suppress any remaining jumps to this TB */ - tb1 = tb->jmp_first; - for(;;) { - n1 = (uintptr_t)tb1 & 3; - if (n1 == 2) - break; - tb1 = (TranslationBlock *)((uintptr_t)tb1 & ~3); - tb2 = tb1->jmp_next[n1]; - tb_reset_jump(tb1, n1); - tb1->jmp_next[n1] = NULL; - tb1 = tb2; - } - tb->jmp_first = (TranslationBlock *)((uintptr_t)tb | 2); /* fail safe */ - - tb_phys_invalidate_count++; -} - -static inline void set_bits(uint8_t *tab, int start, int len) -{ - int end, mask, end1; - - end = start + len; - tab += start >> 3; - mask = 0xff << (start & 7); - if ((start & ~7) == (end & ~7)) { - if (start < end) { - mask &= ~(0xff << (end & 7)); - *tab |= mask; - } - } else { - *tab++ |= mask; - start = (start + 8) & ~7; - end1 = end & ~7; - while (start < end1) { - *tab++ = 0xff; - start += 8; - } - if (start < end) { - mask = ~(0xff << (end & 7)); - *tab |= mask; - } - } -} - -static void build_page_bitmap(PageDesc *p) -{ - int n, tb_start, tb_end; - TranslationBlock *tb; - - p->code_bitmap = g_malloc0(TARGET_PAGE_SIZE / 8); - - tb = p->first_tb; - while (tb != NULL) { - n = (uintptr_t)tb & 3; - tb = (TranslationBlock *)((uintptr_t)tb & ~3); - /* NOTE: this is subtle as a TB may span two physical pages */ - if (n == 0) { - /* NOTE: tb_end may be after the end of the page, but - it is not a problem */ - tb_start = tb->pc & ~TARGET_PAGE_MASK; - tb_end = tb_start + tb->size; - if (tb_end > TARGET_PAGE_SIZE) - tb_end = TARGET_PAGE_SIZE; - } else { - tb_start = 0; - tb_end = ((tb->pc + tb->size) & ~TARGET_PAGE_MASK); - } - set_bits(p->code_bitmap, tb_start, tb_end - tb_start); - tb = tb->page_next[n]; - } -} - -TranslationBlock *tb_gen_code(CPUArchState *env, - target_ulong pc, target_ulong cs_base, - int flags, int cflags) -{ - TranslationBlock *tb; - uint8_t *tc_ptr; - tb_page_addr_t phys_pc, phys_page2; - target_ulong virt_page2; - int code_gen_size; - - phys_pc = get_page_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(pc); - /* Don't forget to invalidate previous TB info. */ - tb_invalidated_flag = 1; - } - tc_ptr = code_gen_ptr; - tb->tc_ptr = tc_ptr; - tb->cs_base = cs_base; - tb->flags = flags; - tb->cflags = cflags; - cpu_gen_code(env, tb, &code_gen_size); - code_gen_ptr = (void *)(((uintptr_t)code_gen_ptr + code_gen_size + - CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1)); - - /* check next page if needed */ - virt_page2 = (pc + tb->size - 1) & TARGET_PAGE_MASK; - phys_page2 = -1; - if ((pc & TARGET_PAGE_MASK) != virt_page2) { - phys_page2 = get_page_addr_code(env, virt_page2); - } - tb_link_page(tb, phys_pc, phys_page2); - return tb; -} - -/* - * Invalidate all TBs which intersect with the target physical address range - * [start;end[. NOTE: start and end may refer to *different* physical pages. - * '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_range(tb_page_addr_t start, tb_page_addr_t end, - int is_cpu_write_access) -{ - while (start < end) { - tb_invalidate_phys_page_range(start, end, is_cpu_write_access); - start &= TARGET_PAGE_MASK; - start += TARGET_PAGE_SIZE; - } -} - -/* - * Invalidate all TBs which intersect with the target physical address 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(tb_page_addr_t start, tb_page_addr_t end, - int is_cpu_write_access) -{ - TranslationBlock *tb, *tb_next, *saved_tb; - CPUArchState *env = cpu_single_env; - tb_page_addr_t tb_start, tb_end; - PageDesc *p; - int n; -#ifdef TARGET_HAS_PRECISE_SMC - int current_tb_not_found = is_cpu_write_access; - TranslationBlock *current_tb = NULL; - int current_tb_modified = 0; - target_ulong current_pc = 0; - target_ulong current_cs_base = 0; - int current_flags = 0; -#endif /* TARGET_HAS_PRECISE_SMC */ - - p = page_find(start >> TARGET_PAGE_BITS); - if (!p) - return; - if (!p->code_bitmap && - ++p->code_write_count >= SMC_BITMAP_USE_THRESHOLD && - is_cpu_write_access) { - /* build code bitmap */ - build_page_bitmap(p); - } - - /* we remove all the TBs in the range [start, end[ */ - /* XXX: see if in some cases it could be faster to invalidate all the code */ - tb = p->first_tb; - while (tb != NULL) { - n = (uintptr_t)tb & 3; - tb = (TranslationBlock *)((uintptr_t)tb & ~3); - tb_next = tb->page_next[n]; - /* NOTE: this is subtle as a TB may span two physical pages */ - if (n == 0) { - /* NOTE: tb_end may be after the end of the page, but - it is not a problem */ - tb_start = tb->page_addr[0] + (tb->pc & ~TARGET_PAGE_MASK); - tb_end = tb_start + tb->size; - } else { - tb_start = tb->page_addr[1]; - tb_end = tb_start + ((tb->pc + tb->size) & ~TARGET_PAGE_MASK); - } - if (!(tb_end <= start || tb_start >= end)) { -#ifdef TARGET_HAS_PRECISE_SMC - if (current_tb_not_found) { - current_tb_not_found = 0; - current_tb = NULL; - if (env->mem_io_pc) { - /* now we have a real cpu fault */ - current_tb = tb_find_pc(env->mem_io_pc); - } - } - if (current_tb == tb && - (current_tb->cflags & CF_COUNT_MASK) != 1) { - /* If we are modifying the current TB, we must stop - its execution. We could be more precise by checking - that the modification is after the current PC, but it - would require a specialized function to partially - restore the CPU state */ - - current_tb_modified = 1; - cpu_restore_state(current_tb, env, env->mem_io_pc); - cpu_get_tb_cpu_state(env, ¤t_pc, ¤t_cs_base, - ¤t_flags); - } -#endif /* TARGET_HAS_PRECISE_SMC */ - /* we need to do that to handle the case where a signal - 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; - } -#if !defined(CONFIG_USER_ONLY) - /* if no code remaining, no need to continue to use slow writes */ - if (!p->first_tb) { - invalidate_page_bitmap(p); - if (is_cpu_write_access) { - tlb_unprotect_code_phys(env, start, env->mem_io_vaddr); - } - } -#endif -#ifdef TARGET_HAS_PRECISE_SMC - if (current_tb_modified) { - /* we generate a block containing just the instruction - modifying the memory. It will ensure that it cannot modify - itself */ - env->current_tb = NULL; - tb_gen_code(env, current_pc, current_cs_base, current_flags, 1); - cpu_resume_from_signal(env, NULL); - } -#endif -} - -/* len must be <= 8 and start must be a multiple of len */ -static inline void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len) -{ - PageDesc *p; - int offset, b; -#if 0 - if (1) { - qemu_log("modifying code at 0x%x size=%d EIP=%x PC=%08x\n", - cpu_single_env->mem_io_vaddr, len, - cpu_single_env->eip, - cpu_single_env->eip + - (intptr_t)cpu_single_env->segs[R_CS].base); - } -#endif - p = page_find(start >> TARGET_PAGE_BITS); - if (!p) - return; - if (p->code_bitmap) { - offset = start & ~TARGET_PAGE_MASK; - b = p->code_bitmap[offset >> 3] >> (offset & 7); - if (b & ((1 << len) - 1)) - goto do_invalidate; - } else { - do_invalidate: - tb_invalidate_phys_page_range(start, start + len, 1); + if (cc->vmsd != NULL) { + vmstate_register(NULL, cpu_index, cc->vmsd, cpu); } } -#if !defined(CONFIG_SOFTMMU) -static void tb_invalidate_phys_page(tb_page_addr_t addr, - uintptr_t pc, void *puc) -{ - TranslationBlock *tb; - PageDesc *p; - int n; -#ifdef TARGET_HAS_PRECISE_SMC - TranslationBlock *current_tb = NULL; - CPUArchState *env = cpu_single_env; - int current_tb_modified = 0; - target_ulong current_pc = 0; - target_ulong current_cs_base = 0; - int current_flags = 0; -#endif - - addr &= TARGET_PAGE_MASK; - p = page_find(addr >> TARGET_PAGE_BITS); - if (!p) - return; - tb = p->first_tb; -#ifdef TARGET_HAS_PRECISE_SMC - if (tb && pc != 0) { - current_tb = tb_find_pc(pc); - } -#endif - while (tb != NULL) { - n = (uintptr_t)tb & 3; - tb = (TranslationBlock *)((uintptr_t)tb & ~3); -#ifdef TARGET_HAS_PRECISE_SMC - if (current_tb == tb && - (current_tb->cflags & CF_COUNT_MASK) != 1) { - /* If we are modifying the current TB, we must stop - its execution. We could be more precise by checking - that the modification is after the current PC, but it - would require a specialized function to partially - restore the CPU state */ - - current_tb_modified = 1; - cpu_restore_state(current_tb, env, pc); - cpu_get_tb_cpu_state(env, ¤t_pc, ¤t_cs_base, - ¤t_flags); - } -#endif /* TARGET_HAS_PRECISE_SMC */ - tb_phys_invalidate(tb, addr); - tb = tb->page_next[n]; - } - p->first_tb = NULL; -#ifdef TARGET_HAS_PRECISE_SMC - if (current_tb_modified) { - /* we generate a block containing just the instruction - modifying the memory. It will ensure that it cannot modify - itself */ - env->current_tb = NULL; - tb_gen_code(env, current_pc, current_cs_base, current_flags, 1); - cpu_resume_from_signal(env, puc); - } -#endif -} -#endif - -/* add the tb in the target page and protect it if necessary */ -static inline void tb_alloc_page(TranslationBlock *tb, - unsigned int n, tb_page_addr_t page_addr) -{ - PageDesc *p; -#ifndef CONFIG_USER_ONLY - bool page_already_protected; -#endif - - tb->page_addr[n] = page_addr; - p = page_find_alloc(page_addr >> TARGET_PAGE_BITS, 1); - tb->page_next[n] = p->first_tb; -#ifndef CONFIG_USER_ONLY - page_already_protected = p->first_tb != NULL; -#endif - p->first_tb = (TranslationBlock *)((uintptr_t)tb | n); - invalidate_page_bitmap(p); - -#if defined(TARGET_HAS_SMC) || 1 - -#if defined(CONFIG_USER_ONLY) - if (p->flags & PAGE_WRITE) { - target_ulong addr; - PageDesc *p2; - int prot; - - /* force the host page as non writable (writes will have a - page fault + mprotect overhead) */ - page_addr &= qemu_host_page_mask; - prot = 0; - 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; - } - mprotect(g2h(page_addr), qemu_host_page_size, - (prot & PAGE_BITS) & ~PAGE_WRITE); -#ifdef DEBUG_TB_INVALIDATE - printf("protecting code page: 0x" TARGET_FMT_lx "\n", - page_addr); -#endif - } -#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 (!page_already_protected) { - tlb_protect_code(page_addr); - } -#endif - -#endif /* TARGET_HAS_SMC */ -} - -/* 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. */ -static void tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc, - tb_page_addr_t phys_page2) -{ - unsigned int h; - TranslationBlock **ptb; - - /* Grab the mmap lock to stop another thread invalidating this TB - before we are done. */ - mmap_lock(); - /* add in the physical hash table */ - h = tb_phys_hash_func(phys_pc); - ptb = &tb_phys_hash[h]; - tb->phys_hash_next = *ptb; - *ptb = tb; - - /* add in the page list */ - tb_alloc_page(tb, 0, phys_pc & TARGET_PAGE_MASK); - if (phys_page2 != -1) - tb_alloc_page(tb, 1, phys_page2); - else - tb->page_addr[1] = -1; - - tb->jmp_first = (TranslationBlock *)((uintptr_t)tb | 2); - tb->jmp_next[0] = NULL; - tb->jmp_next[1] = NULL; - - /* init original jump addresses */ - if (tb->tb_next_offset[0] != 0xffff) - 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 - mmap_unlock(); -} - -#if defined(CONFIG_QEMU_LDST_OPTIMIZATION) && defined(CONFIG_SOFTMMU) -/* check whether the given addr is in TCG generated code buffer or not */ -bool is_tcg_gen_code(uintptr_t tc_ptr) -{ - /* This can be called during code generation, code_gen_buffer_max_size - is used instead of code_gen_ptr for upper boundary checking */ - return (tc_ptr >= (uintptr_t)code_gen_buffer && - tc_ptr < (uintptr_t)(code_gen_buffer + code_gen_buffer_max_size)); -} -#endif - -/* find the TB 'tb' such that tb[0].tc_ptr <= tc_ptr < - tb[1].tc_ptr. Return NULL if not found */ -TranslationBlock *tb_find_pc(uintptr_t tc_ptr) -{ - int m_min, m_max, m; - uintptr_t v; - TranslationBlock *tb; - - if (nb_tbs <= 0) - return NULL; - if (tc_ptr < (uintptr_t)code_gen_buffer || - tc_ptr >= (uintptr_t)code_gen_ptr) { - return NULL; - } - /* binary search (cf Knuth) */ - m_min = 0; - m_max = nb_tbs - 1; - while (m_min <= m_max) { - m = (m_min + m_max) >> 1; - tb = &tbs[m]; - v = (uintptr_t)tb->tc_ptr; - if (v == tc_ptr) - return tb; - else if (tc_ptr < v) { - m_max = m - 1; - } else { - m_min = m + 1; - } - } - return &tbs[m_max]; -} - -static void tb_reset_jump_recursive(TranslationBlock *tb); - -static inline void tb_reset_jump_recursive2(TranslationBlock *tb, int n) -{ - TranslationBlock *tb1, *tb_next, **ptb; - unsigned int n1; - - tb1 = tb->jmp_next[n]; - if (tb1 != NULL) { - /* find head of list */ - for(;;) { - n1 = (uintptr_t)tb1 & 3; - tb1 = (TranslationBlock *)((uintptr_t)tb1 & ~3); - if (n1 == 2) - break; - tb1 = tb1->jmp_next[n1]; - } - /* we are now sure now that tb jumps to tb1 */ - tb_next = tb1; - - /* remove tb from the jmp_first list */ - ptb = &tb_next->jmp_first; - for(;;) { - tb1 = *ptb; - n1 = (uintptr_t)tb1 & 3; - tb1 = (TranslationBlock *)((uintptr_t)tb1 & ~3); - if (n1 == n && tb1 == tb) - break; - ptb = &tb1->jmp_next[n1]; - } - *ptb = tb->jmp_next[n]; - tb->jmp_next[n] = NULL; - - /* suppress the jump to next tb in generated code */ - tb_reset_jump(tb, n); - - /* suppress jumps in the tb on which we could have jumped */ - tb_reset_jump_recursive(tb_next); - } -} - -static void tb_reset_jump_recursive(TranslationBlock *tb) -{ - tb_reset_jump_recursive2(tb, 0); - tb_reset_jump_recursive2(tb, 1); -} - #if defined(TARGET_HAS_ICE) #if defined(CONFIG_USER_ONLY) -static void breakpoint_invalidate(CPUArchState *env, target_ulong pc) +static void breakpoint_invalidate(CPUState *cpu, target_ulong pc) { tb_invalidate_phys_page_range(pc, pc + 1, 0); } #else -void tb_invalidate_phys_addr(hwaddr addr) +static void breakpoint_invalidate(CPUState *cpu, target_ulong pc) { - ram_addr_t ram_addr; - MemoryRegionSection *section; - - section = phys_page_find(address_space_memory.dispatch, addr >> TARGET_PAGE_BITS); - if (!(memory_region_is_ram(section->mr) - || (section->mr->rom_device && section->mr->readable))) { - return; - } - ram_addr = (memory_region_get_ram_addr(section->mr) & TARGET_PAGE_MASK) - + memory_region_section_addr(section, addr); - tb_invalidate_phys_page_range(ram_addr, ram_addr + 1, 0); -} - -static void breakpoint_invalidate(CPUArchState *env, target_ulong pc) -{ - tb_invalidate_phys_addr(cpu_get_phys_page_debug(env, pc) | + tb_invalidate_phys_addr(cpu_get_phys_page_debug(cpu, pc) | (pc & ~TARGET_PAGE_MASK)); } #endif @@ -1612,15 +528,17 @@ int cpu_breakpoint_insert(CPUArchState *env, target_ulong pc, int flags, bp->flags = flags; /* keep all GDB-injected breakpoints in front */ - if (flags & BP_GDB) + if (flags & BP_GDB) { QTAILQ_INSERT_HEAD(&env->breakpoints, bp, entry); - else + } else { QTAILQ_INSERT_TAIL(&env->breakpoints, bp, entry); + } - breakpoint_invalidate(env, pc); + breakpoint_invalidate(ENV_GET_CPU(env), pc); - if (breakpoint) + if (breakpoint) { *breakpoint = bp; + } return 0; #else return -ENOSYS; @@ -1651,7 +569,7 @@ void cpu_breakpoint_remove_by_ref(CPUArchState *env, CPUBreakpoint *breakpoint) #if defined(TARGET_HAS_ICE) QTAILQ_REMOVE(&env->breakpoints, breakpoint, entry); - breakpoint_invalidate(env, breakpoint->pc); + breakpoint_invalidate(ENV_GET_CPU(env), breakpoint->pc); g_free(breakpoint); #endif @@ -1672,96 +590,26 @@ void cpu_breakpoint_remove_all(CPUArchState *env, int mask) /* enable or disable single step mode. EXCP_DEBUG is returned by the CPU loop after each instruction */ -void cpu_single_step(CPUArchState *env, int enabled) +void cpu_single_step(CPUState *cpu, int enabled) { #if defined(TARGET_HAS_ICE) - if (env->singlestep_enabled != enabled) { - env->singlestep_enabled = enabled; - if (kvm_enabled()) - kvm_update_guest_debug(env, 0); - else { + if (cpu->singlestep_enabled != enabled) { + cpu->singlestep_enabled = enabled; + if (kvm_enabled()) { + kvm_update_guest_debug(cpu, 0); + } else { /* must flush all the translated code to avoid inconsistencies */ /* XXX: only flush what is necessary */ + CPUArchState *env = cpu->env_ptr; tb_flush(env); } } #endif } -static void cpu_unlink_tb(CPUArchState *env) -{ - /* FIXME: TB unchaining isn't SMP safe. For now just ignore the - problem and hope the cpu will stop of its own accord. For userspace - emulation this often isn't actually as bad as it sounds. Often - signals are used primarily to interrupt blocking syscalls. */ - TranslationBlock *tb; - static spinlock_t interrupt_lock = SPIN_LOCK_UNLOCKED; - - spin_lock(&interrupt_lock); - tb = env->current_tb; - /* if the cpu is currently executing code, we must unlink it and - all the potentially executing TB */ - if (tb) { - env->current_tb = NULL; - tb_reset_jump_recursive(tb); - } - spin_unlock(&interrupt_lock); -} - -#ifndef CONFIG_USER_ONLY -/* mask must never be zero, except for A20 change call */ -static void tcg_handle_interrupt(CPUArchState *env, int mask) -{ - CPUState *cpu = ENV_GET_CPU(env); - int old_mask; - - old_mask = env->interrupt_request; - env->interrupt_request |= mask; - - /* - * If called from iothread context, wake the target cpu in - * case its halted. - */ - if (!qemu_cpu_is_self(cpu)) { - qemu_cpu_kick(cpu); - return; - } - - if (use_icount) { - env->icount_decr.u16.high = 0xffff; - if (!can_do_io(env) - && (mask & ~old_mask) != 0) { - cpu_abort(env, "Raised interrupt while not in I/O function"); - } - } else { - cpu_unlink_tb(env); - } -} - -CPUInterruptHandler cpu_interrupt_handler = tcg_handle_interrupt; - -#else /* CONFIG_USER_ONLY */ - -void cpu_interrupt(CPUArchState *env, int mask) -{ - env->interrupt_request |= mask; - cpu_unlink_tb(env); -} -#endif /* CONFIG_USER_ONLY */ - -void cpu_reset_interrupt(CPUArchState *env, int mask) -{ - env->interrupt_request &= ~mask; -} - -void cpu_exit(CPUArchState *env) -{ - env->exit_request = 1; - cpu_unlink_tb(env); -} - void cpu_abort(CPUArchState *env, const char *fmt, ...) { + CPUState *cpu = ENV_GET_CPU(env); va_list ap; va_list ap2; @@ -1770,12 +618,12 @@ void cpu_abort(CPUArchState *env, const char *fmt, ...) fprintf(stderr, "qemu: fatal: "); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); - cpu_dump_state(env, stderr, fprintf, CPU_DUMP_FPU | CPU_DUMP_CCOP); + cpu_dump_state(cpu, stderr, fprintf, CPU_DUMP_FPU | CPU_DUMP_CCOP); if (qemu_log_enabled()) { qemu_log("qemu: fatal: "); qemu_log_vprintf(fmt, ap2); qemu_log("\n"); - log_cpu_state(env, CPU_DUMP_FPU | CPU_DUMP_CCOP); + log_cpu_state(cpu, CPU_DUMP_FPU | CPU_DUMP_CCOP); qemu_log_flush(); qemu_log_close(); } @@ -1795,18 +643,16 @@ void cpu_abort(CPUArchState *env, const char *fmt, ...) CPUArchState *cpu_copy(CPUArchState *env) { CPUArchState *new_env = cpu_init(env->cpu_model_str); - CPUArchState *next_cpu = new_env->next_cpu; - int cpu_index = new_env->cpu_index; #if defined(TARGET_HAS_ICE) CPUBreakpoint *bp; CPUWatchpoint *wp; #endif - memcpy(new_env, env, sizeof(CPUArchState)); + /* Reset non arch specific state */ + cpu_reset(ENV_GET_CPU(new_env)); - /* Preserve chaining and index. */ - new_env->next_cpu = next_cpu; - new_env->cpu_index = cpu_index; + /* Copy arch specific state into the new CPU */ + memcpy(new_env, env, sizeof(CPUArchState)); /* Clone all break/watchpoints. Note: Once we support ptrace with hw-debug register access, make sure @@ -1827,21 +673,6 @@ CPUArchState *cpu_copy(CPUArchState *env) } #if !defined(CONFIG_USER_ONLY) -void tb_flush_jmp_cache(CPUArchState *env, target_ulong addr) -{ - unsigned int i; - - /* Discard jump cache entries for any tb which might potentially - overlap the flushed page. */ - i = tb_jmp_cache_hash_page(addr - TARGET_PAGE_SIZE); - memset (&env->tb_jmp_cache[i], 0, - TB_JMP_PAGE_SIZE * sizeof(TranslationBlock *)); - - i = tb_jmp_cache_hash_page(addr); - memset (&env->tb_jmp_cache[i], 0, - TB_JMP_PAGE_SIZE * sizeof(TranslationBlock *)); -} - static void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t end, uintptr_t length) { @@ -1887,11 +718,11 @@ static int cpu_physical_memory_set_dirty_tracking(int enable) } hwaddr memory_region_section_get_iotlb(CPUArchState *env, - MemoryRegionSection *section, - target_ulong vaddr, - hwaddr paddr, - int prot, - target_ulong *address) + MemoryRegionSection *section, + target_ulong vaddr, + hwaddr paddr, hwaddr xlat, + int prot, + target_ulong *address) { hwaddr iotlb; CPUWatchpoint *wp; @@ -1899,21 +730,15 @@ hwaddr memory_region_section_get_iotlb(CPUArchState *env, if (memory_region_is_ram(section->mr)) { /* Normal RAM. */ iotlb = (memory_region_get_ram_addr(section->mr) & TARGET_PAGE_MASK) - + memory_region_section_addr(section, paddr); + + xlat; if (!section->readonly) { - iotlb |= phys_section_notdirty; + iotlb |= PHYS_SECTION_NOTDIRTY; } else { - iotlb |= phys_section_rom; + iotlb |= PHYS_SECTION_ROM; } } else { - /* IO handlers are currently passed a physical address. - It would be nice to pass an offset from the base address - of that region. This would avoid having to special case RAM, - and avoid full address decoding in every device. - We can't use the high bits of pd for this because - IO_MEM_ROMD uses these as a ram address. */ - iotlb = section - phys_sections; - iotlb += memory_region_section_addr(section, paddr); + iotlb = section - address_space_memory.dispatch->sections; + iotlb += xlat; } /* Make accesses to pages with watchpoints go via the @@ -1922,7 +747,7 @@ hwaddr memory_region_section_get_iotlb(CPUArchState *env, if (vaddr == (wp->vaddr & TARGET_PAGE_MASK)) { /* Avoid trapping reads of pages with a write breakpoint. */ if ((prot & PAGE_WRITE) || (wp->flags & BP_MEM_READ)) { - iotlb = phys_section_watch + paddr; + iotlb = PHYS_SECTION_WATCH + paddr; *address |= TLB_MMIO; break; } @@ -1931,282 +756,36 @@ hwaddr memory_region_section_get_iotlb(CPUArchState *env, return iotlb; } +#endif /* defined(CONFIG_USER_ONLY) */ -#else -/* - * Walks guest process memory "regions" one by one - * and calls callback function 'fn' for each region. - */ - -struct walk_memory_regions_data -{ - walk_memory_regions_fn fn; - void *priv; - uintptr_t start; - int prot; -}; - -static int walk_memory_regions_end(struct walk_memory_regions_data *data, - abi_ulong end, int new_prot) -{ - if (data->start != -1ul) { - int rc = data->fn(data->priv, data->start, end, data->prot); - if (rc != 0) { - return rc; - } - } - - data->start = (new_prot ? end : -1ul); - data->prot = new_prot; - - return 0; -} - -static int walk_memory_regions_1(struct walk_memory_regions_data *data, - abi_ulong base, int level, void **lp) -{ - abi_ulong pa; - int i, rc; - - if (*lp == NULL) { - return walk_memory_regions_end(data, base, 0); - } - - if (level == 0) { - PageDesc *pd = *lp; - for (i = 0; i < L2_SIZE; ++i) { - int prot = pd[i].flags; - - pa = base | (i << TARGET_PAGE_BITS); - if (prot != data->prot) { - rc = walk_memory_regions_end(data, pa, prot); - if (rc != 0) { - return rc; - } - } - } - } else { - void **pp = *lp; - for (i = 0; i < L2_SIZE; ++i) { - pa = base | ((abi_ulong)i << - (TARGET_PAGE_BITS + L2_BITS * level)); - rc = walk_memory_regions_1(data, pa, level - 1, pp + i); - if (rc != 0) { - return rc; - } - } - } - - return 0; -} - -int walk_memory_regions(void *priv, walk_memory_regions_fn fn) -{ - struct walk_memory_regions_data data; - uintptr_t i; - - data.fn = fn; - data.priv = priv; - data.start = -1ul; - data.prot = 0; - - for (i = 0; i < V_L1_SIZE; i++) { - int rc = walk_memory_regions_1(&data, (abi_ulong)i << V_L1_SHIFT, - V_L1_SHIFT / L2_BITS - 1, l1_map + i); - if (rc != 0) { - return rc; - } - } - - return walk_memory_regions_end(&data, 0, 0); -} - -static int dump_region(void *priv, abi_ulong start, - abi_ulong end, unsigned long prot) -{ - FILE *f = (FILE *)priv; - - (void) fprintf(f, TARGET_ABI_FMT_lx"-"TARGET_ABI_FMT_lx - " "TARGET_ABI_FMT_lx" %c%c%c\n", - start, end, end - start, - ((prot & PAGE_READ) ? 'r' : '-'), - ((prot & PAGE_WRITE) ? 'w' : '-'), - ((prot & PAGE_EXEC) ? 'x' : '-')); - - return (0); -} - -/* dump memory mappings */ -void page_dump(FILE *f) -{ - (void) fprintf(f, "%-8s %-8s %-8s %s\n", - "start", "end", "size", "prot"); - walk_memory_regions(f, dump_region); -} - -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 positioned automatically depending - on PAGE_WRITE. The mmap_lock should already be held. */ -void page_set_flags(target_ulong start, target_ulong end, int flags) -{ - target_ulong addr, len; - - /* This function should never be called with addresses outside the - guest address space. If this assert fires, it probably indicates - a missing call to h2g_valid. */ -#if TARGET_ABI_BITS > L1_MAP_ADDR_SPACE_BITS - assert(end < ((abi_ulong)1 << L1_MAP_ADDR_SPACE_BITS)); -#endif - assert(start < end); - - start = start & TARGET_PAGE_MASK; - end = TARGET_PAGE_ALIGN(end); - - if (flags & PAGE_WRITE) { - flags |= PAGE_WRITE_ORG; - } - - for (addr = start, len = end - start; - len != 0; - len -= TARGET_PAGE_SIZE, addr += TARGET_PAGE_SIZE) { - PageDesc *p = page_find_alloc(addr >> TARGET_PAGE_BITS, 1); - - /* If the write protection bit 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; - } -} - -int page_check_range(target_ulong start, target_ulong len, int flags) -{ - PageDesc *p; - target_ulong end; - target_ulong addr; - - /* This function should never be called with addresses outside the - guest address space. If this assert fires, it probably indicates - a missing call to h2g_valid. */ -#if TARGET_ABI_BITS > L1_MAP_ADDR_SPACE_BITS - assert(start < ((abi_ulong)1 << L1_MAP_ADDR_SPACE_BITS)); -#endif - - if (len == 0) { - return 0; - } - if (start + len - 1 < start) { - /* We've wrapped around. */ - return -1; - } - - end = TARGET_PAGE_ALIGN(start+len); /* must do before we loose bits in the next step */ - start = start & TARGET_PAGE_MASK; - - for (addr = start, len = end - start; - len != 0; - len -= TARGET_PAGE_SIZE, addr += TARGET_PAGE_SIZE) { - p = page_find(addr >> TARGET_PAGE_BITS); - if( !p ) - return -1; - if( !(p->flags & PAGE_VALID) ) - return -1; +#if !defined(CONFIG_USER_ONLY) - if ((flags & PAGE_READ) && !(p->flags & PAGE_READ)) - return -1; - if (flags & PAGE_WRITE) { - if (!(p->flags & PAGE_WRITE_ORG)) - return -1; - /* unprotect the page if it was put read-only because it - contains translated code */ - if (!(p->flags & PAGE_WRITE)) { - if (!page_unprotect(addr, 0, NULL)) - return -1; - } - return 0; - } - } - return 0; -} +static int subpage_register (subpage_t *mmio, uint32_t start, uint32_t end, + uint16_t section); +static subpage_t *subpage_init(AddressSpace *as, hwaddr base); -/* called from signal handler: invalidate the code and unprotect the - page. Return TRUE if the fault was successfully handled. */ -int page_unprotect(target_ulong address, uintptr_t pc, void *puc) +static uint16_t phys_section_add(MemoryRegionSection *section) { - unsigned int prot; - PageDesc *p; - target_ulong host_start, host_end, addr; - - /* Technically this isn't safe inside a signal handler. However we - know this only ever happens in a synchronous SEGV handler, so in - practice it seems to be ok. */ - mmap_lock(); - - p = page_find(address >> TARGET_PAGE_BITS); - if (!p) { - mmap_unlock(); - return 0; - } - - /* if the page was really writable, then we change its - protection back to writable */ - if ((p->flags & PAGE_WRITE_ORG) && !(p->flags & PAGE_WRITE)) { - host_start = address & qemu_host_page_mask; - host_end = host_start + qemu_host_page_size; - - prot = 0; - for (addr = host_start ; addr < host_end ; addr += TARGET_PAGE_SIZE) { - p = page_find(addr >> TARGET_PAGE_BITS); - p->flags |= PAGE_WRITE; - prot |= p->flags; - - /* and since the content will be modified, we must invalidate - the corresponding translated code. */ - tb_invalidate_phys_page(addr, pc, puc); -#ifdef DEBUG_TB_CHECK - tb_invalidate_check(addr); -#endif - } - mprotect((void *)g2h(host_start), qemu_host_page_size, - prot & PAGE_BITS); + /* The physical section number is ORed with a page-aligned + * pointer to produce the iotlb entries. Thus it should + * never overflow into the page-aligned value. + */ + assert(next_map.sections_nb < TARGET_PAGE_SIZE); - mmap_unlock(); - return 1; + if (next_map.sections_nb == next_map.sections_nb_alloc) { + next_map.sections_nb_alloc = MAX(next_map.sections_nb_alloc * 2, + 16); + next_map.sections = g_renew(MemoryRegionSection, next_map.sections, + next_map.sections_nb_alloc); } - mmap_unlock(); - return 0; + next_map.sections[next_map.sections_nb] = *section; + memory_region_ref(section->mr); + return next_map.sections_nb++; } -#endif /* defined(CONFIG_USER_ONLY) */ - -#if !defined(CONFIG_USER_ONLY) -#define SUBPAGE_IDX(addr) ((addr) & ~TARGET_PAGE_MASK) -typedef struct subpage_t { - MemoryRegion iomem; - hwaddr base; - uint16_t sub_section[TARGET_PAGE_SIZE]; -} subpage_t; - -static int subpage_register (subpage_t *mmio, uint32_t start, uint32_t end, - uint16_t section); -static subpage_t *subpage_init(hwaddr base); -static void destroy_page_desc(uint16_t section_index) +static void phys_section_destroy(MemoryRegion *mr) { - MemoryRegionSection *section = &phys_sections[section_index]; - MemoryRegion *mr = section->mr; + memory_region_unref(mr); if (mr->subpage) { subpage_t *subpage = container_of(mr, subpage_t, iomem); @@ -2215,47 +794,15 @@ static void destroy_page_desc(uint16_t section_index) } } -static void destroy_l2_mapping(PhysPageEntry *lp, unsigned level) +static void phys_sections_free(PhysPageMap *map) { - unsigned i; - PhysPageEntry *p; - - if (lp->ptr == PHYS_MAP_NODE_NIL) { - return; + while (map->sections_nb > 0) { + MemoryRegionSection *section = &map->sections[--map->sections_nb]; + phys_section_destroy(section->mr); } - - p = phys_map_nodes[lp->ptr]; - for (i = 0; i < L2_SIZE; ++i) { - if (!p[i].is_leaf) { - destroy_l2_mapping(&p[i], level - 1); - } else { - destroy_page_desc(p[i].ptr); - } - } - lp->is_leaf = 0; - lp->ptr = PHYS_MAP_NODE_NIL; -} - -static void destroy_all_mappings(AddressSpaceDispatch *d) -{ - destroy_l2_mapping(&d->phys_map, P_L2_LEVELS - 1); - phys_map_nodes_reset(); -} - -static uint16_t phys_section_add(MemoryRegionSection *section) -{ - if (phys_sections_nb == phys_sections_nb_alloc) { - phys_sections_nb_alloc = MAX(phys_sections_nb_alloc * 2, 16); - phys_sections = g_renew(MemoryRegionSection, phys_sections, - phys_sections_nb_alloc); - } - phys_sections[phys_sections_nb] = *section; - return phys_sections_nb++; -} - -static void phys_sections_clear(void) -{ - phys_sections_nb = 0; + g_free(map->sections); + g_free(map->nodes); + g_free(map); } static void register_subpage(AddressSpaceDispatch *d, MemoryRegionSection *section) @@ -2263,17 +810,18 @@ static void register_subpage(AddressSpaceDispatch *d, MemoryRegionSection *secti subpage_t *subpage; hwaddr base = section->offset_within_address_space & TARGET_PAGE_MASK; - MemoryRegionSection *existing = phys_page_find(d, base >> TARGET_PAGE_BITS); + MemoryRegionSection *existing = phys_page_find(d->phys_map, base >> TARGET_PAGE_BITS, + next_map.nodes, next_map.sections); MemoryRegionSection subsection = { .offset_within_address_space = base, - .size = TARGET_PAGE_SIZE, + .size = int128_make64(TARGET_PAGE_SIZE), }; hwaddr start, end; assert(existing->mr->subpage || existing->mr == &io_mem_unassigned); if (!(existing->mr->subpage)) { - subpage = subpage_init(base); + subpage = subpage_init(d->as, base); subsection.mr = &subpage->iomem; phys_page_set(d, base >> TARGET_PAGE_BITS, 1, phys_section_add(&subsection)); @@ -2281,56 +829,53 @@ static void register_subpage(AddressSpaceDispatch *d, MemoryRegionSection *secti subpage = container_of(existing->mr, subpage_t, iomem); } start = section->offset_within_address_space & ~TARGET_PAGE_MASK; - end = start + section->size - 1; + end = start + int128_get64(section->size) - 1; subpage_register(subpage, start, end, phys_section_add(section)); } -static void register_multipage(AddressSpaceDispatch *d, MemoryRegionSection *section) +static void register_multipage(AddressSpaceDispatch *d, + MemoryRegionSection *section) { hwaddr start_addr = section->offset_within_address_space; - ram_addr_t size = section->size; - hwaddr addr; uint16_t section_index = phys_section_add(section); + uint64_t num_pages = int128_get64(int128_rshift(section->size, + TARGET_PAGE_BITS)); - assert(size); - - addr = start_addr; - phys_page_set(d, addr >> TARGET_PAGE_BITS, size >> TARGET_PAGE_BITS, - section_index); + assert(num_pages); + phys_page_set(d, start_addr >> TARGET_PAGE_BITS, num_pages, section_index); } static void mem_add(MemoryListener *listener, MemoryRegionSection *section) { - AddressSpaceDispatch *d = container_of(listener, AddressSpaceDispatch, listener); + AddressSpace *as = container_of(listener, AddressSpace, dispatch_listener); + AddressSpaceDispatch *d = as->next_dispatch; MemoryRegionSection now = *section, remain = *section; + Int128 page_size = int128_make64(TARGET_PAGE_SIZE); - if ((now.offset_within_address_space & ~TARGET_PAGE_MASK) - || (now.size < TARGET_PAGE_SIZE)) { - now.size = MIN(TARGET_PAGE_ALIGN(now.offset_within_address_space) - - now.offset_within_address_space, - now.size); + if (now.offset_within_address_space & ~TARGET_PAGE_MASK) { + uint64_t left = TARGET_PAGE_ALIGN(now.offset_within_address_space) + - now.offset_within_address_space; + + now.size = int128_min(int128_make64(left), now.size); register_subpage(d, &now); - remain.size -= now.size; - remain.offset_within_address_space += now.size; - remain.offset_within_region += now.size; + } else { + now.size = int128_zero(); } - while (remain.size >= TARGET_PAGE_SIZE) { + while (int128_ne(remain.size, now.size)) { + remain.size = int128_sub(remain.size, now.size); + remain.offset_within_address_space += int128_get64(now.size); + remain.offset_within_region += int128_get64(now.size); now = remain; - if (remain.offset_within_region & ~TARGET_PAGE_MASK) { - now.size = TARGET_PAGE_SIZE; + if (int128_lt(remain.size, page_size)) { + register_subpage(d, &now); + } else if (remain.offset_within_region & ~TARGET_PAGE_MASK) { + now.size = page_size; register_subpage(d, &now); } else { - now.size &= TARGET_PAGE_MASK; + now.size = int128_and(now.size, int128_neg(page_size)); register_multipage(d, &now); } - remain.size -= now.size; - remain.offset_within_address_space += now.size; - remain.offset_within_region += now.size; - } - now = remain; - if (now.size) { - register_subpage(d, &now); } } @@ -2340,6 +885,16 @@ void qemu_flush_coalesced_mmio_buffer(void) kvm_flush_coalesced_mmio_buffer(); } +void qemu_mutex_lock_ramlist(void) +{ + qemu_mutex_lock(&ram_list.mutex); +} + +void qemu_mutex_unlock_ramlist(void) +{ + qemu_mutex_unlock(&ram_list.mutex); +} + #if defined(__linux__) && !defined(TARGET_S390X) #include <sys/vfs.h> @@ -2371,6 +926,8 @@ static void *file_ram_alloc(RAMBlock *block, const char *path) { char *filename; + char *sanitized_name; + char *c; void *area; int fd; #ifdef MAP_POPULATE @@ -2392,18 +949,25 @@ static void *file_ram_alloc(RAMBlock *block, return NULL; } - if (asprintf(&filename, "%s/qemu_back_mem.XXXXXX", path) == -1) { - return NULL; + /* Make name safe to use with mkstemp by replacing '/' with '_'. */ + sanitized_name = g_strdup(block->mr->name); + for (c = sanitized_name; *c != '\0'; c++) { + if (*c == '/') + *c = '_'; } + filename = g_strdup_printf("%s/qemu_back_mem.%s.XXXXXX", path, + sanitized_name); + g_free(sanitized_name); + fd = mkstemp(filename); if (fd < 0) { perror("unable to create backing store for hugepages"); - free(filename); + g_free(filename); return NULL; } unlink(filename); - free(filename); + g_free(filename); memory = (memory+hpagesize-1) & ~(hpagesize-1); @@ -2441,15 +1005,17 @@ static ram_addr_t find_ram_offset(ram_addr_t size) RAMBlock *block, *next_block; ram_addr_t offset = RAM_ADDR_MAX, mingap = RAM_ADDR_MAX; - if (QLIST_EMPTY(&ram_list.blocks)) + assert(size != 0); /* it would hand out same offset multiple times */ + + if (QTAILQ_EMPTY(&ram_list.blocks)) return 0; - QLIST_FOREACH(block, &ram_list.blocks, next) { + QTAILQ_FOREACH(block, &ram_list.blocks, next) { ram_addr_t end, next = RAM_ADDR_MAX; end = block->offset + block->length; - QLIST_FOREACH(next_block, &ram_list.blocks, next) { + QTAILQ_FOREACH(next_block, &ram_list.blocks, next) { if (next_block->offset >= end) { next = MIN(next, next_block->offset); } @@ -2474,7 +1040,7 @@ ram_addr_t last_ram_offset(void) RAMBlock *block; ram_addr_t last = 0; - QLIST_FOREACH(block, &ram_list.blocks, next) + QTAILQ_FOREACH(block, &ram_list.blocks, next) last = MAX(last, block->offset + block->length); return last; @@ -2483,12 +1049,10 @@ ram_addr_t last_ram_offset(void) static void qemu_ram_setup_dump(void *addr, ram_addr_t size) { int ret; - QemuOpts *machine_opts; /* Use MADV_DONTDUMP, if user doesn't want the guest memory in the core */ - machine_opts = qemu_opts_find(qemu_find_opts("machine"), 0); - if (machine_opts && - !qemu_opt_get_bool(machine_opts, "dump-guest-core", true)) { + if (!qemu_opt_get_bool(qemu_get_machine_opts(), + "dump-guest-core", true)) { ret = qemu_madvise(addr, size, QEMU_MADV_DONTDUMP); if (ret) { perror("qemu_madvise"); @@ -2503,7 +1067,7 @@ void qemu_ram_set_idstr(ram_addr_t addr, const char *name, DeviceState *dev) RAMBlock *new_block, *block; new_block = NULL; - QLIST_FOREACH(block, &ram_list.blocks, next) { + QTAILQ_FOREACH(block, &ram_list.blocks, next) { if (block->offset == addr) { new_block = block; break; @@ -2521,21 +1085,21 @@ void qemu_ram_set_idstr(ram_addr_t addr, const char *name, DeviceState *dev) } pstrcat(new_block->idstr, sizeof(new_block->idstr), name); - QLIST_FOREACH(block, &ram_list.blocks, next) { + /* This assumes the iothread lock is taken here too. */ + qemu_mutex_lock_ramlist(); + QTAILQ_FOREACH(block, &ram_list.blocks, next) { if (block != new_block && !strcmp(block->idstr, new_block->idstr)) { fprintf(stderr, "RAMBlock \"%s\" already registered, abort!\n", new_block->idstr); abort(); } } + qemu_mutex_unlock_ramlist(); } static int memory_try_enable_merging(void *addr, size_t len) { - QemuOpts *opts; - - opts = qemu_opts_find(qemu_find_opts("machine"), 0); - if (opts && !qemu_opt_get_bool(opts, "mem-merge", true)) { + if (!qemu_opt_get_bool(qemu_get_machine_opts(), "mem-merge", true)) { /* disabled by the user */ return 0; } @@ -2546,11 +1110,13 @@ static int memory_try_enable_merging(void *addr, size_t len) ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, MemoryRegion *mr) { - RAMBlock *new_block; + RAMBlock *block, *new_block; size = TARGET_PAGE_ALIGN(size); new_block = g_malloc0(sizeof(*new_block)); + /* This assumes the iothread lock is taken here too. */ + qemu_mutex_lock_ramlist(); new_block->mr = mr; new_block->offset = find_ram_offset(size); if (host) { @@ -2561,7 +1127,7 @@ ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, #if defined (__linux__) && !defined(TARGET_S390X) new_block->host = file_ram_alloc(new_block, size, mem_path); if (!new_block->host) { - new_block->host = qemu_vmalloc(size); + new_block->host = qemu_anon_ram_alloc(size); memory_try_enable_merging(new_block->host, size); } #else @@ -2573,16 +1139,30 @@ ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, xen_ram_alloc(new_block->offset, size, mr); } else if (kvm_enabled()) { /* some s390/kvm configurations have special constraints */ - new_block->host = kvm_vmalloc(size); + new_block->host = kvm_ram_alloc(size); } else { - new_block->host = qemu_vmalloc(size); + new_block->host = qemu_anon_ram_alloc(size); } memory_try_enable_merging(new_block->host, size); } } new_block->length = size; - QLIST_INSERT_HEAD(&ram_list.blocks, new_block, next); + /* Keep the list sorted from biggest to smallest block. */ + QTAILQ_FOREACH(block, &ram_list.blocks, next) { + if (block->length < new_block->length) { + break; + } + } + if (block) { + QTAILQ_INSERT_BEFORE(block, new_block, next); + } else { + QTAILQ_INSERT_TAIL(&ram_list.blocks, new_block, next); + } + ram_list.mru_block = NULL; + + ram_list.version++; + qemu_mutex_unlock_ramlist(); ram_list.phys_dirty = g_realloc(ram_list.phys_dirty, last_ram_offset() >> TARGET_PAGE_BITS); @@ -2608,22 +1188,31 @@ void qemu_ram_free_from_ptr(ram_addr_t addr) { RAMBlock *block; - QLIST_FOREACH(block, &ram_list.blocks, next) { + /* This assumes the iothread lock is taken here too. */ + qemu_mutex_lock_ramlist(); + QTAILQ_FOREACH(block, &ram_list.blocks, next) { if (addr == block->offset) { - QLIST_REMOVE(block, next); + QTAILQ_REMOVE(&ram_list.blocks, block, next); + ram_list.mru_block = NULL; + ram_list.version++; g_free(block); - return; + break; } } + qemu_mutex_unlock_ramlist(); } void qemu_ram_free(ram_addr_t addr) { RAMBlock *block; - QLIST_FOREACH(block, &ram_list.blocks, next) { + /* This assumes the iothread lock is taken here too. */ + qemu_mutex_lock_ramlist(); + QTAILQ_FOREACH(block, &ram_list.blocks, next) { if (addr == block->offset) { - QLIST_REMOVE(block, next); + QTAILQ_REMOVE(&ram_list.blocks, block, next); + ram_list.mru_block = NULL; + ram_list.version++; if (block->flags & RAM_PREALLOC_MASK) { ; } else if (mem_path) { @@ -2632,26 +1221,23 @@ void qemu_ram_free(ram_addr_t addr) munmap(block->host, block->length); close(block->fd); } else { - qemu_vfree(block->host); + qemu_anon_ram_free(block->host, block->length); } #else abort(); #endif } else { -#if defined(TARGET_S390X) && defined(CONFIG_KVM) - munmap(block->host, block->length); -#else if (xen_enabled()) { xen_invalidate_map_cache_entry(block->host); } else { - qemu_vfree(block->host); + qemu_anon_ram_free(block->host, block->length); } -#endif } g_free(block); - return; + break; } } + qemu_mutex_unlock_ramlist(); } @@ -2663,7 +1249,7 @@ void qemu_ram_remap(ram_addr_t addr, ram_addr_t length) int flags; void *area, *vaddr; - QLIST_FOREACH(block, &ram_list.blocks, next) { + QTAILQ_FOREACH(block, &ram_list.blocks, next) { offset = addr - block->offset; if (offset < block->length) { vaddr = block->host + offset; @@ -2717,6 +1303,29 @@ void qemu_ram_remap(ram_addr_t addr, ram_addr_t length) } #endif /* !_WIN32 */ +static RAMBlock *qemu_get_ram_block(ram_addr_t addr) +{ + RAMBlock *block; + + /* The list is protected by the iothread lock here. */ + block = ram_list.mru_block; + if (block && addr - block->offset < block->length) { + goto found; + } + QTAILQ_FOREACH(block, &ram_list.blocks, next) { + if (addr - block->offset < block->length) { + goto found; + } + } + + fprintf(stderr, "Bad ram offset %" PRIx64 "\n", (uint64_t)addr); + abort(); + +found: + ram_list.mru_block = block; + return block; +} + /* Return a host pointer to ram allocated with qemu_ram_alloc. With the exception of the softmmu code in this file, this should only be used for local memory (e.g. video ram) that the device owns, @@ -2727,45 +1336,34 @@ void qemu_ram_remap(ram_addr_t addr, ram_addr_t length) */ void *qemu_get_ram_ptr(ram_addr_t addr) { - RAMBlock *block; + RAMBlock *block = qemu_get_ram_block(addr); - QLIST_FOREACH(block, &ram_list.blocks, next) { - if (addr - block->offset < block->length) { - /* Move this entry to to start of the list. */ - if (block != QLIST_FIRST(&ram_list.blocks)) { - QLIST_REMOVE(block, next); - QLIST_INSERT_HEAD(&ram_list.blocks, block, next); - } - if (xen_enabled()) { - /* We need to check if the requested address is in the RAM - * because we don't want to map the entire memory in QEMU. - * In that case just map until the end of the page. - */ - if (block->offset == 0) { - return xen_map_cache(addr, 0, 0); - } else if (block->host == NULL) { - block->host = - xen_map_cache(block->offset, block->length, 1); - } - } - return block->host + (addr - block->offset); + if (xen_enabled()) { + /* We need to check if the requested address is in the RAM + * because we don't want to map the entire memory in QEMU. + * In that case just map until the end of the page. + */ + if (block->offset == 0) { + return xen_map_cache(addr, 0, 0); + } else if (block->host == NULL) { + block->host = + xen_map_cache(block->offset, block->length, 1); } } - - fprintf(stderr, "Bad ram offset %" PRIx64 "\n", (uint64_t)addr); - abort(); - - return NULL; + return block->host + (addr - block->offset); } -/* Return a host pointer to ram allocated with qemu_ram_alloc. - * Same as qemu_get_ram_ptr but avoid reordering ramblocks. +/* Return a host pointer to ram allocated with qemu_ram_alloc. Same as + * qemu_get_ram_ptr but do not touch ram_list.mru_block. + * + * ??? Is this still necessary? */ static void *qemu_safe_ram_ptr(ram_addr_t addr) { RAMBlock *block; - QLIST_FOREACH(block, &ram_list.blocks, next) { + /* The list is protected by the iothread lock here. */ + QTAILQ_FOREACH(block, &ram_list.blocks, next) { if (addr - block->offset < block->length) { if (xen_enabled()) { /* We need to check if the requested address is in the RAM @@ -2791,7 +1389,7 @@ static void *qemu_safe_ram_ptr(ram_addr_t addr) /* Return a host pointer to guest's ram. Similar to qemu_get_ram_ptr * but takes a size argument */ -static void *qemu_ram_ptr_length(ram_addr_t addr, ram_addr_t *size) +static void *qemu_ram_ptr_length(ram_addr_t addr, hwaddr *size) { if (*size == 0) { return NULL; @@ -2801,7 +1399,7 @@ static void *qemu_ram_ptr_length(ram_addr_t addr, ram_addr_t *size) } else { RAMBlock *block; - QLIST_FOREACH(block, &ram_list.blocks, next) { + QTAILQ_FOREACH(block, &ram_list.blocks, next) { if (addr - block->offset < block->length) { if (addr - block->offset + *size > block->length) *size = block->length - addr + block->offset; @@ -2814,111 +1412,48 @@ static void *qemu_ram_ptr_length(ram_addr_t addr, ram_addr_t *size) } } -void qemu_put_ram_ptr(void *addr) -{ - trace_qemu_put_ram_ptr(addr); -} - -int qemu_ram_addr_from_host(void *ptr, ram_addr_t *ram_addr) +/* Some of the softmmu routines need to translate from a host pointer + (typically a TLB entry) back to a ram offset. */ +MemoryRegion *qemu_ram_addr_from_host(void *ptr, ram_addr_t *ram_addr) { RAMBlock *block; uint8_t *host = ptr; if (xen_enabled()) { *ram_addr = xen_ram_addr_from_mapcache(ptr); - return 0; + return qemu_get_ram_block(*ram_addr)->mr; + } + + block = ram_list.mru_block; + if (block && block->host && host - block->host < block->length) { + goto found; } - QLIST_FOREACH(block, &ram_list.blocks, next) { + QTAILQ_FOREACH(block, &ram_list.blocks, next) { /* This case append when the block is not mapped. */ if (block->host == NULL) { continue; } if (host - block->host < block->length) { - *ram_addr = block->offset + (host - block->host); - return 0; + goto found; } } - return -1; -} - -/* Some of the softmmu routines need to translate from a host pointer - (typically a TLB entry) back to a ram offset. */ -ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr) -{ - ram_addr_t ram_addr; - - if (qemu_ram_addr_from_host(ptr, &ram_addr)) { - fprintf(stderr, "Bad ram pointer %p\n", ptr); - abort(); - } - return ram_addr; -} - -static uint64_t unassigned_mem_read(void *opaque, hwaddr addr, - unsigned size) -{ -#ifdef DEBUG_UNASSIGNED - printf("Unassigned mem read " TARGET_FMT_plx "\n", addr); -#endif -#if defined(TARGET_ALPHA) || defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE) - cpu_unassigned_access(cpu_single_env, addr, 0, 0, 0, size); -#endif - return 0; -} - -static void unassigned_mem_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ -#ifdef DEBUG_UNASSIGNED - printf("Unassigned mem write " TARGET_FMT_plx " = 0x%"PRIx64"\n", addr, val); -#endif -#if defined(TARGET_ALPHA) || defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE) - cpu_unassigned_access(cpu_single_env, addr, 1, 0, 0, size); -#endif -} - -static const MemoryRegionOps unassigned_mem_ops = { - .read = unassigned_mem_read, - .write = unassigned_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static uint64_t error_mem_read(void *opaque, hwaddr addr, - unsigned size) -{ - abort(); -} + return NULL; -static void error_mem_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - abort(); +found: + *ram_addr = block->offset + (host - block->host); + return block->mr; } -static const MemoryRegionOps error_mem_ops = { - .read = error_mem_read, - .write = error_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const MemoryRegionOps rom_mem_ops = { - .read = error_mem_read, - .write = unassigned_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - static void notdirty_mem_write(void *opaque, hwaddr ram_addr, uint64_t val, unsigned size) { int dirty_flags; dirty_flags = cpu_physical_memory_get_dirty_flags(ram_addr); if (!(dirty_flags & CODE_DIRTY_FLAG)) { -#if !defined(CONFIG_USER_ONLY) tb_invalidate_phys_page_fast(ram_addr, size); dirty_flags = cpu_physical_memory_get_dirty_flags(ram_addr); -#endif } switch (size) { case 1: @@ -2937,22 +1472,29 @@ static void notdirty_mem_write(void *opaque, hwaddr ram_addr, cpu_physical_memory_set_dirty_flags(ram_addr, dirty_flags); /* we remove the notdirty callback only if the code has been flushed */ - if (dirty_flags == 0xff) - tlb_set_dirty(cpu_single_env, cpu_single_env->mem_io_vaddr); + if (dirty_flags == 0xff) { + CPUArchState *env = current_cpu->env_ptr; + tlb_set_dirty(env, env->mem_io_vaddr); + } +} + +static bool notdirty_mem_accepts(void *opaque, hwaddr addr, + unsigned size, bool is_write) +{ + return is_write; } static const MemoryRegionOps notdirty_mem_ops = { - .read = error_mem_read, .write = notdirty_mem_write, + .valid.accepts = notdirty_mem_accepts, .endianness = DEVICE_NATIVE_ENDIAN, }; /* Generate a debug exception if a watchpoint has been hit. */ static void check_watchpoint(int offset, int len_mask, int flags) { - CPUArchState *env = cpu_single_env; + CPUArchState *env = current_cpu->env_ptr; target_ulong pc, cs_base; - TranslationBlock *tb; target_ulong vaddr; CPUWatchpoint *wp; int cpu_flags; @@ -2961,7 +1503,7 @@ static void check_watchpoint(int offset, int len_mask, int flags) /* We re-entered the check after replacing the TB. Now raise * the debug interrupt so that is will trigger after the * current instruction. */ - cpu_interrupt(env, CPU_INTERRUPT_DEBUG); + cpu_interrupt(ENV_GET_CPU(env), CPU_INTERRUPT_DEBUG); return; } vaddr = (env->mem_io_vaddr & TARGET_PAGE_MASK) + offset; @@ -2971,13 +1513,7 @@ static void check_watchpoint(int offset, int len_mask, int flags) wp->flags |= BP_WATCHPOINT_HIT; if (!env->watchpoint_hit) { env->watchpoint_hit = wp; - tb = tb_find_pc(env->mem_io_pc); - if (!tb) { - cpu_abort(env, "check_watchpoint: could not find TB for " - "pc=%p", (void *)env->mem_io_pc); - } - cpu_restore_state(tb, env, env->mem_io_pc); - tb_phys_invalidate(tb, -1); + tb_check_watchpoint(env); if (wp->flags & BP_STOP_BEFORE_ACCESS) { env->exception_index = EXCP_DEBUG; cpu_loop_exit(env); @@ -3035,75 +1571,70 @@ static const MemoryRegionOps watch_mem_ops = { static uint64_t subpage_read(void *opaque, hwaddr addr, unsigned len) { - subpage_t *mmio = opaque; - unsigned int idx = SUBPAGE_IDX(addr); - MemoryRegionSection *section; + subpage_t *subpage = opaque; + uint8_t buf[4]; + #if defined(DEBUG_SUBPAGE) - printf("%s: subpage %p len %d addr " TARGET_FMT_plx " idx %d\n", __func__, - mmio, len, addr, idx); + printf("%s: subpage %p len %d addr " TARGET_FMT_plx "\n", __func__, + subpage, len, addr); #endif - - section = &phys_sections[mmio->sub_section[idx]]; - addr += mmio->base; - addr -= section->offset_within_address_space; - addr += section->offset_within_region; - return io_mem_read(section->mr, addr, len); + address_space_read(subpage->as, addr + subpage->base, buf, len); + switch (len) { + case 1: + return ldub_p(buf); + case 2: + return lduw_p(buf); + case 4: + return ldl_p(buf); + default: + abort(); + } } static void subpage_write(void *opaque, hwaddr addr, uint64_t value, unsigned len) { - subpage_t *mmio = opaque; - unsigned int idx = SUBPAGE_IDX(addr); - MemoryRegionSection *section; + subpage_t *subpage = opaque; + uint8_t buf[4]; + #if defined(DEBUG_SUBPAGE) printf("%s: subpage %p len %d addr " TARGET_FMT_plx - " idx %d value %"PRIx64"\n", - __func__, mmio, len, addr, idx, value); + " value %"PRIx64"\n", + __func__, subpage, len, addr, value); #endif - - section = &phys_sections[mmio->sub_section[idx]]; - addr += mmio->base; - addr -= section->offset_within_address_space; - addr += section->offset_within_region; - io_mem_write(section->mr, addr, value, len); -} - -static const MemoryRegionOps subpage_ops = { - .read = subpage_read, - .write = subpage_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static uint64_t subpage_ram_read(void *opaque, hwaddr addr, - unsigned size) -{ - ram_addr_t raddr = addr; - void *ptr = qemu_get_ram_ptr(raddr); - switch (size) { - case 1: return ldub_p(ptr); - case 2: return lduw_p(ptr); - case 4: return ldl_p(ptr); - default: abort(); + switch (len) { + case 1: + stb_p(buf, value); + break; + case 2: + stw_p(buf, value); + break; + case 4: + stl_p(buf, value); + break; + default: + abort(); } + address_space_write(subpage->as, addr + subpage->base, buf, len); } -static void subpage_ram_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) +static bool subpage_accepts(void *opaque, hwaddr addr, + unsigned size, bool is_write) { - ram_addr_t raddr = addr; - void *ptr = qemu_get_ram_ptr(raddr); - switch (size) { - case 1: return stb_p(ptr, value); - case 2: return stw_p(ptr, value); - case 4: return stl_p(ptr, value); - default: abort(); - } + subpage_t *subpage = opaque; +#if defined(DEBUG_SUBPAGE) + printf("%s: subpage %p %c len %d addr " TARGET_FMT_plx "\n", + __func__, subpage, is_write ? 'w' : 'r', len, addr); +#endif + + return address_space_access_valid(subpage->as, addr + subpage->base, + size, is_write); } -static const MemoryRegionOps subpage_ram_ops = { - .read = subpage_ram_read, - .write = subpage_ram_write, +static const MemoryRegionOps subpage_ops = { + .read = subpage_read, + .write = subpage_write, + .valid.accepts = subpage_accepts, .endianness = DEVICE_NATIVE_ENDIAN, }; @@ -3120,11 +1651,6 @@ static int subpage_register (subpage_t *mmio, uint32_t start, uint32_t end, printf("%s: %p start %08x end %08x idx %08x eidx %08x mem %ld\n", __func__, mmio, start, end, idx, eidx, memory); #endif - if (memory_region_is_ram(phys_sections[section].mr)) { - MemoryRegionSection new_section = phys_sections[section]; - new_section.mr = &io_mem_subpage_ram; - section = phys_section_add(&new_section); - } for (; idx <= eidx; idx++) { mmio->sub_section[idx] = section; } @@ -3132,21 +1658,22 @@ static int subpage_register (subpage_t *mmio, uint32_t start, uint32_t end, return 0; } -static subpage_t *subpage_init(hwaddr base) +static subpage_t *subpage_init(AddressSpace *as, hwaddr base) { subpage_t *mmio; mmio = g_malloc0(sizeof(subpage_t)); + mmio->as = as; mmio->base = base; - memory_region_init_io(&mmio->iomem, &subpage_ops, mmio, + memory_region_init_io(&mmio->iomem, NULL, &subpage_ops, mmio, "subpage", TARGET_PAGE_SIZE); mmio->iomem.subpage = true; #if defined(DEBUG_SUBPAGE) printf("%s: %p base " TARGET_FMT_plx " len %08x %d\n", __func__, mmio, base, TARGET_PAGE_SIZE, subpage_memory); #endif - subpage_register(mmio, 0, TARGET_PAGE_SIZE-1, phys_section_unassigned); + subpage_register(mmio, 0, TARGET_PAGE_SIZE-1, PHYS_SECTION_UNASSIGNED); return mmio; } @@ -3157,7 +1684,7 @@ static uint16_t dummy_section(MemoryRegion *mr) .mr = mr, .offset_within_address_space = 0, .offset_within_region = 0, - .size = UINT64_MAX, + .size = int128_2_64(), }; return phys_section_add(§ion); @@ -3165,48 +1692,79 @@ static uint16_t dummy_section(MemoryRegion *mr) MemoryRegion *iotlb_to_region(hwaddr index) { - return phys_sections[index & ~TARGET_PAGE_MASK].mr; + return address_space_memory.dispatch->sections[index & ~TARGET_PAGE_MASK].mr; } static void io_mem_init(void) { - memory_region_init_io(&io_mem_ram, &error_mem_ops, NULL, "ram", UINT64_MAX); - memory_region_init_io(&io_mem_rom, &rom_mem_ops, NULL, "rom", UINT64_MAX); - memory_region_init_io(&io_mem_unassigned, &unassigned_mem_ops, NULL, + memory_region_init_io(&io_mem_rom, NULL, &unassigned_mem_ops, NULL, "rom", UINT64_MAX); + memory_region_init_io(&io_mem_unassigned, NULL, &unassigned_mem_ops, NULL, "unassigned", UINT64_MAX); - memory_region_init_io(&io_mem_notdirty, ¬dirty_mem_ops, NULL, + memory_region_init_io(&io_mem_notdirty, NULL, ¬dirty_mem_ops, NULL, "notdirty", UINT64_MAX); - memory_region_init_io(&io_mem_subpage_ram, &subpage_ram_ops, NULL, - "subpage-ram", UINT64_MAX); - memory_region_init_io(&io_mem_watch, &watch_mem_ops, NULL, + memory_region_init_io(&io_mem_watch, NULL, &watch_mem_ops, NULL, "watch", UINT64_MAX); } static void mem_begin(MemoryListener *listener) { - AddressSpaceDispatch *d = container_of(listener, AddressSpaceDispatch, listener); + AddressSpace *as = container_of(listener, AddressSpace, dispatch_listener); + AddressSpaceDispatch *d = g_new(AddressSpaceDispatch, 1); + + d->phys_map = (PhysPageEntry) { .ptr = PHYS_MAP_NODE_NIL, .is_leaf = 0 }; + d->as = as; + as->next_dispatch = d; +} + +static void mem_commit(MemoryListener *listener) +{ + AddressSpace *as = container_of(listener, AddressSpace, dispatch_listener); + AddressSpaceDispatch *cur = as->dispatch; + AddressSpaceDispatch *next = as->next_dispatch; + + next->nodes = next_map.nodes; + next->sections = next_map.sections; - destroy_all_mappings(d); - d->phys_map.ptr = PHYS_MAP_NODE_NIL; + as->dispatch = next; + g_free(cur); } static void core_begin(MemoryListener *listener) { - phys_sections_clear(); - phys_section_unassigned = dummy_section(&io_mem_unassigned); - phys_section_notdirty = dummy_section(&io_mem_notdirty); - phys_section_rom = dummy_section(&io_mem_rom); - phys_section_watch = dummy_section(&io_mem_watch); + uint16_t n; + + prev_map = g_new(PhysPageMap, 1); + *prev_map = next_map; + + memset(&next_map, 0, sizeof(next_map)); + n = dummy_section(&io_mem_unassigned); + assert(n == PHYS_SECTION_UNASSIGNED); + n = dummy_section(&io_mem_notdirty); + assert(n == PHYS_SECTION_NOTDIRTY); + n = dummy_section(&io_mem_rom); + assert(n == PHYS_SECTION_ROM); + n = dummy_section(&io_mem_watch); + assert(n == PHYS_SECTION_WATCH); +} + +/* This listener's commit run after the other AddressSpaceDispatch listeners'. + * All AddressSpaceDispatch instances have switched to the next map. + */ +static void core_commit(MemoryListener *listener) +{ + phys_sections_free(prev_map); } static void tcg_commit(MemoryListener *listener) { - CPUArchState *env; + CPUState *cpu; /* 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) { + for (cpu = first_cpu; cpu != NULL; cpu = cpu->next_cpu) { + CPUArchState *env = cpu->env_ptr; + tlb_flush(env, 1); } } @@ -3221,62 +1779,36 @@ static void core_log_global_stop(MemoryListener *listener) cpu_physical_memory_set_dirty_tracking(0); } -static void io_region_add(MemoryListener *listener, - MemoryRegionSection *section) -{ - MemoryRegionIORange *mrio = g_new(MemoryRegionIORange, 1); - - mrio->mr = section->mr; - mrio->offset = section->offset_within_region; - iorange_init(&mrio->iorange, &memory_region_iorange_ops, - section->offset_within_address_space, section->size); - ioport_register(&mrio->iorange); -} - -static void io_region_del(MemoryListener *listener, - MemoryRegionSection *section) -{ - isa_unassign_ioport(section->offset_within_address_space, section->size); -} - static MemoryListener core_memory_listener = { .begin = core_begin, + .commit = core_commit, .log_global_start = core_log_global_start, .log_global_stop = core_log_global_stop, .priority = 1, }; -static MemoryListener io_memory_listener = { - .region_add = io_region_add, - .region_del = io_region_del, - .priority = 0, -}; - static MemoryListener tcg_memory_listener = { .commit = tcg_commit, }; void address_space_init_dispatch(AddressSpace *as) { - AddressSpaceDispatch *d = g_new(AddressSpaceDispatch, 1); - - d->phys_map = (PhysPageEntry) { .ptr = PHYS_MAP_NODE_NIL, .is_leaf = 0 }; - d->listener = (MemoryListener) { + as->dispatch = NULL; + as->dispatch_listener = (MemoryListener) { .begin = mem_begin, + .commit = mem_commit, .region_add = mem_add, .region_nop = mem_add, .priority = 0, }; - as->dispatch = d; - memory_listener_register(&d->listener, as); + memory_listener_register(&as->dispatch_listener, as); } void address_space_destroy_dispatch(AddressSpace *as) { AddressSpaceDispatch *d = as->dispatch; - memory_listener_unregister(&d->listener); - destroy_l2_mapping(&d->phys_map, P_L2_LEVELS - 1); + memory_listener_unregister(&as->dispatch_listener); g_free(d); as->dispatch = NULL; } @@ -3284,21 +1816,15 @@ void address_space_destroy_dispatch(AddressSpace *as) static void memory_map_init(void) { system_memory = g_malloc(sizeof(*system_memory)); - memory_region_init(system_memory, "system", INT64_MAX); - address_space_init(&address_space_memory, system_memory); - address_space_memory.name = "memory"; + memory_region_init(system_memory, NULL, "system", INT64_MAX); + address_space_init(&address_space_memory, system_memory, "memory"); system_io = g_malloc(sizeof(*system_io)); - memory_region_init(system_io, "io", 65536); - address_space_init(&address_space_io, system_io); - address_space_io.name = "I/O"; + memory_region_init(system_io, NULL, "io", 65536); + address_space_init(&address_space_io, system_io, "I/O"); memory_listener_register(&core_memory_listener, &address_space_memory); - memory_listener_register(&io_memory_listener, &address_space_io); memory_listener_register(&tcg_memory_listener, &address_space_memory); - - dma_context_init(&dma_context_memory, &address_space_memory, - NULL, NULL, NULL); } MemoryRegion *get_system_memory(void) @@ -3315,7 +1841,7 @@ MemoryRegion *get_system_io(void) /* physical memory access (slow version, mainly for debug) */ #if defined(CONFIG_USER_ONLY) -int cpu_memory_rw_debug(CPUArchState *env, target_ulong addr, +int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr, uint8_t *buf, int len, int is_write) { int l, flags; @@ -3368,146 +1894,177 @@ static void invalidate_and_set_dirty(hwaddr addr, xen_modified_memory(addr, length); } -void address_space_rw(AddressSpace *as, hwaddr addr, uint8_t *buf, +static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write) +{ + if (memory_region_is_ram(mr)) { + return !(is_write && mr->readonly); + } + if (memory_region_is_romd(mr)) { + return !is_write; + } + + return false; +} + +static int memory_access_size(MemoryRegion *mr, unsigned l, hwaddr addr) +{ + unsigned access_size_max = mr->ops->valid.max_access_size; + + /* Regions are assumed to support 1-4 byte accesses unless + otherwise specified. */ + if (access_size_max == 0) { + access_size_max = 4; + } + + /* Bound the maximum access by the alignment of the address. */ + if (!mr->ops->impl.unaligned) { + unsigned align_size_max = addr & -addr; + if (align_size_max != 0 && align_size_max < access_size_max) { + access_size_max = align_size_max; + } + } + + /* Don't attempt accesses larger than the maximum. */ + if (l > access_size_max) { + l = access_size_max; + } + + return l; +} + +bool address_space_rw(AddressSpace *as, hwaddr addr, uint8_t *buf, int len, bool is_write) { - AddressSpaceDispatch *d = as->dispatch; - int l; + hwaddr l; uint8_t *ptr; - uint32_t val; - hwaddr page; - MemoryRegionSection *section; + uint64_t val; + hwaddr addr1; + MemoryRegion *mr; + bool error = false; while (len > 0) { - page = addr & TARGET_PAGE_MASK; - l = (page + TARGET_PAGE_SIZE) - addr; - if (l > len) - l = len; - section = phys_page_find(d, page >> TARGET_PAGE_BITS); + l = len; + mr = address_space_translate(as, addr, &addr1, &l, is_write); if (is_write) { - if (!memory_region_is_ram(section->mr)) { - hwaddr addr1; - addr1 = memory_region_section_addr(section, addr); - /* XXX: could force cpu_single_env to NULL to avoid + if (!memory_access_is_direct(mr, is_write)) { + l = memory_access_size(mr, l, addr1); + /* XXX: could force current_cpu to NULL to avoid potential bugs */ - if (l >= 4 && ((addr1 & 3) == 0)) { + switch (l) { + case 8: + /* 64 bit write access */ + val = ldq_p(buf); + error |= io_mem_write(mr, addr1, val, 8); + break; + case 4: /* 32 bit write access */ val = ldl_p(buf); - io_mem_write(section->mr, addr1, val, 4); - l = 4; - } else if (l >= 2 && ((addr1 & 1) == 0)) { + error |= io_mem_write(mr, addr1, val, 4); + break; + case 2: /* 16 bit write access */ val = lduw_p(buf); - io_mem_write(section->mr, addr1, val, 2); - l = 2; - } else { + error |= io_mem_write(mr, addr1, val, 2); + break; + case 1: /* 8 bit write access */ val = ldub_p(buf); - io_mem_write(section->mr, addr1, val, 1); - l = 1; + error |= io_mem_write(mr, addr1, val, 1); + break; + default: + abort(); } - } else if (!section->readonly) { - ram_addr_t addr1; - addr1 = memory_region_get_ram_addr(section->mr) - + memory_region_section_addr(section, addr); + } else { + addr1 += memory_region_get_ram_addr(mr); /* RAM case */ ptr = qemu_get_ram_ptr(addr1); memcpy(ptr, buf, l); invalidate_and_set_dirty(addr1, l); - qemu_put_ram_ptr(ptr); } } else { - if (!(memory_region_is_ram(section->mr) || - memory_region_is_romd(section->mr))) { - hwaddr addr1; + if (!memory_access_is_direct(mr, is_write)) { /* I/O case */ - addr1 = memory_region_section_addr(section, addr); - if (l >= 4 && ((addr1 & 3) == 0)) { + l = memory_access_size(mr, l, addr1); + switch (l) { + case 8: + /* 64 bit read access */ + error |= io_mem_read(mr, addr1, &val, 8); + stq_p(buf, val); + break; + case 4: /* 32 bit read access */ - val = io_mem_read(section->mr, addr1, 4); + error |= io_mem_read(mr, addr1, &val, 4); stl_p(buf, val); - l = 4; - } else if (l >= 2 && ((addr1 & 1) == 0)) { + break; + case 2: /* 16 bit read access */ - val = io_mem_read(section->mr, addr1, 2); + error |= io_mem_read(mr, addr1, &val, 2); stw_p(buf, val); - l = 2; - } else { + break; + case 1: /* 8 bit read access */ - val = io_mem_read(section->mr, addr1, 1); + error |= io_mem_read(mr, addr1, &val, 1); stb_p(buf, val); - l = 1; + break; + default: + abort(); } } else { /* RAM case */ - ptr = qemu_get_ram_ptr(section->mr->ram_addr - + memory_region_section_addr(section, - addr)); + ptr = qemu_get_ram_ptr(mr->ram_addr + addr1); memcpy(buf, ptr, l); - qemu_put_ram_ptr(ptr); } } len -= l; buf += l; addr += l; } + + return error; } -void address_space_write(AddressSpace *as, hwaddr addr, +bool address_space_write(AddressSpace *as, hwaddr addr, const uint8_t *buf, int len) { - address_space_rw(as, addr, (uint8_t *)buf, len, true); + return address_space_rw(as, addr, (uint8_t *)buf, len, true); } -/** - * address_space_read: read from an address space. - * - * @as: #AddressSpace to be accessed - * @addr: address within that address space - * @buf: buffer with the data transferred - */ -void address_space_read(AddressSpace *as, hwaddr addr, uint8_t *buf, int len) +bool address_space_read(AddressSpace *as, hwaddr addr, uint8_t *buf, int len) { - address_space_rw(as, addr, buf, len, false); + return address_space_rw(as, addr, buf, len, false); } void cpu_physical_memory_rw(hwaddr addr, uint8_t *buf, int len, int is_write) { - return address_space_rw(&address_space_memory, addr, buf, len, is_write); + address_space_rw(&address_space_memory, addr, buf, len, is_write); } /* used for ROM loading : can write in RAM and ROM */ void cpu_physical_memory_write_rom(hwaddr addr, const uint8_t *buf, int len) { - AddressSpaceDispatch *d = address_space_memory.dispatch; - int l; + hwaddr l; uint8_t *ptr; - hwaddr page; - MemoryRegionSection *section; + hwaddr addr1; + MemoryRegion *mr; while (len > 0) { - page = addr & TARGET_PAGE_MASK; - l = (page + TARGET_PAGE_SIZE) - addr; - if (l > len) - l = len; - section = phys_page_find(d, page >> TARGET_PAGE_BITS); + l = len; + mr = address_space_translate(&address_space_memory, + addr, &addr1, &l, true); - if (!(memory_region_is_ram(section->mr) || - memory_region_is_romd(section->mr))) { + if (!(memory_region_is_ram(mr) || + memory_region_is_romd(mr))) { /* do nothing */ } else { - unsigned long addr1; - addr1 = memory_region_get_ram_addr(section->mr) - + memory_region_section_addr(section, addr); + addr1 += memory_region_get_ram_addr(mr); /* ROM/RAM case */ ptr = qemu_get_ram_ptr(addr1); memcpy(ptr, buf, l); invalidate_and_set_dirty(addr1, l); - qemu_put_ram_ptr(ptr); } len -= l; buf += l; @@ -3516,6 +2073,7 @@ void cpu_physical_memory_write_rom(hwaddr addr, } typedef struct { + MemoryRegion *mr; void *buffer; hwaddr addr; hwaddr len; @@ -3561,6 +2119,27 @@ static void cpu_notify_map_clients(void) } } +bool address_space_access_valid(AddressSpace *as, hwaddr addr, int len, bool is_write) +{ + MemoryRegion *mr; + hwaddr l, xlat; + + while (len > 0) { + l = len; + mr = address_space_translate(as, addr, &xlat, &l, is_write); + if (!memory_access_is_direct(mr, is_write)) { + l = memory_access_size(mr, l, addr); + if (!memory_region_access_valid(mr, xlat, l, is_write)) { + return false; + } + } + + len -= l; + addr += l; + } + return true; +} + /* Map a physical memory region into a host virtual address. * May map a subset of the requested range, given by and returned in *plen. * May return NULL if resources needed to perform the mapping are exhausted. @@ -3573,50 +2152,57 @@ void *address_space_map(AddressSpace *as, hwaddr *plen, bool is_write) { - AddressSpaceDispatch *d = as->dispatch; hwaddr len = *plen; - hwaddr todo = 0; - int l; - hwaddr page; - MemoryRegionSection *section; - ram_addr_t raddr = RAM_ADDR_MAX; - ram_addr_t rlen; - void *ret; + hwaddr done = 0; + hwaddr l, xlat, base; + MemoryRegion *mr, *this_mr; + ram_addr_t raddr; - while (len > 0) { - page = addr & TARGET_PAGE_MASK; - l = (page + TARGET_PAGE_SIZE) - addr; - if (l > len) - l = len; - section = phys_page_find(d, page >> TARGET_PAGE_BITS); - - if (!(memory_region_is_ram(section->mr) && !section->readonly)) { - if (todo || bounce.buffer) { - break; - } - bounce.buffer = qemu_memalign(TARGET_PAGE_SIZE, TARGET_PAGE_SIZE); - bounce.addr = addr; - bounce.len = l; - if (!is_write) { - address_space_read(as, addr, bounce.buffer, l); - } + if (len == 0) { + return NULL; + } - *plen = l; - return bounce.buffer; + l = len; + mr = address_space_translate(as, addr, &xlat, &l, is_write); + if (!memory_access_is_direct(mr, is_write)) { + if (bounce.buffer) { + return NULL; } - if (!todo) { - raddr = memory_region_get_ram_addr(section->mr) - + memory_region_section_addr(section, addr); + bounce.buffer = qemu_memalign(TARGET_PAGE_SIZE, TARGET_PAGE_SIZE); + bounce.addr = addr; + bounce.len = l; + + memory_region_ref(mr); + bounce.mr = mr; + if (!is_write) { + address_space_read(as, addr, bounce.buffer, l); } + *plen = l; + return bounce.buffer; + } + + base = xlat; + raddr = memory_region_get_ram_addr(mr); + + for (;;) { len -= l; addr += l; - todo += l; + done += l; + if (len == 0) { + break; + } + + l = len; + this_mr = address_space_translate(as, addr, &xlat, &l, is_write); + if (this_mr != mr || xlat != base + done) { + break; + } } - rlen = todo; - ret = qemu_ram_ptr_length(raddr, &rlen); - *plen = rlen; - return ret; + + memory_region_ref(mr); + *plen = done; + return qemu_ram_ptr_length(raddr + base, plen); } /* Unmaps a memory region previously mapped by address_space_map(). @@ -3627,8 +2213,12 @@ void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len, int is_write, hwaddr access_len) { if (buffer != bounce.buffer) { + MemoryRegion *mr; + ram_addr_t addr1; + + mr = qemu_ram_addr_from_host(buffer, &addr1); + assert(mr != NULL); if (is_write) { - ram_addr_t addr1 = qemu_ram_addr_from_host_nofail(buffer); while (access_len) { unsigned l; l = TARGET_PAGE_SIZE; @@ -3642,6 +2232,7 @@ void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len, if (xen_enabled()) { xen_invalidate_map_cache_entry(buffer); } + memory_region_unref(mr); return; } if (is_write) { @@ -3649,6 +2240,7 @@ void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len, } qemu_vfree(bounce.buffer); bounce.buffer = NULL; + memory_region_unref(bounce.mr); cpu_notify_map_clients(); } @@ -3670,16 +2262,16 @@ static inline uint32_t ldl_phys_internal(hwaddr addr, enum device_endian endian) { uint8_t *ptr; - uint32_t val; - MemoryRegionSection *section; - - section = phys_page_find(address_space_memory.dispatch, addr >> TARGET_PAGE_BITS); + uint64_t val; + MemoryRegion *mr; + hwaddr l = 4; + hwaddr addr1; - if (!(memory_region_is_ram(section->mr) || - memory_region_is_romd(section->mr))) { + mr = address_space_translate(&address_space_memory, addr, &addr1, &l, + false); + if (l < 4 || !memory_access_is_direct(mr, false)) { /* I/O case */ - addr = memory_region_section_addr(section, addr); - val = io_mem_read(section->mr, addr, 4); + io_mem_read(mr, addr1, &val, 4); #if defined(TARGET_WORDS_BIGENDIAN) if (endian == DEVICE_LITTLE_ENDIAN) { val = bswap32(val); @@ -3691,9 +2283,9 @@ static inline uint32_t ldl_phys_internal(hwaddr addr, #endif } else { /* RAM case */ - ptr = qemu_get_ram_ptr((memory_region_get_ram_addr(section->mr) + ptr = qemu_get_ram_ptr((memory_region_get_ram_addr(mr) & TARGET_PAGE_MASK) - + memory_region_section_addr(section, addr)); + + addr1); switch (endian) { case DEVICE_LITTLE_ENDIAN: val = ldl_le_p(ptr); @@ -3730,29 +2322,29 @@ static inline uint64_t ldq_phys_internal(hwaddr addr, { uint8_t *ptr; uint64_t val; - MemoryRegionSection *section; + MemoryRegion *mr; + hwaddr l = 8; + hwaddr addr1; - section = phys_page_find(address_space_memory.dispatch, addr >> TARGET_PAGE_BITS); - - if (!(memory_region_is_ram(section->mr) || - memory_region_is_romd(section->mr))) { + mr = address_space_translate(&address_space_memory, addr, &addr1, &l, + false); + if (l < 8 || !memory_access_is_direct(mr, false)) { /* I/O case */ - addr = memory_region_section_addr(section, addr); - - /* XXX This is broken when device endian != cpu endian. - Fix and add "endian" variable check */ -#ifdef TARGET_WORDS_BIGENDIAN - val = io_mem_read(section->mr, addr, 4) << 32; - val |= io_mem_read(section->mr, addr + 4, 4); + io_mem_read(mr, addr1, &val, 8); +#if defined(TARGET_WORDS_BIGENDIAN) + if (endian == DEVICE_LITTLE_ENDIAN) { + val = bswap64(val); + } #else - val = io_mem_read(section->mr, addr, 4); - val |= io_mem_read(section->mr, addr + 4, 4) << 32; + if (endian == DEVICE_BIG_ENDIAN) { + val = bswap64(val); + } #endif } else { /* RAM case */ - ptr = qemu_get_ram_ptr((memory_region_get_ram_addr(section->mr) + ptr = qemu_get_ram_ptr((memory_region_get_ram_addr(mr) & TARGET_PAGE_MASK) - + memory_region_section_addr(section, addr)); + + addr1); switch (endian) { case DEVICE_LITTLE_ENDIAN: val = ldq_le_p(ptr); @@ -3797,15 +2389,15 @@ static inline uint32_t lduw_phys_internal(hwaddr addr, { uint8_t *ptr; uint64_t val; - MemoryRegionSection *section; - - section = phys_page_find(address_space_memory.dispatch, addr >> TARGET_PAGE_BITS); + MemoryRegion *mr; + hwaddr l = 2; + hwaddr addr1; - if (!(memory_region_is_ram(section->mr) || - memory_region_is_romd(section->mr))) { + mr = address_space_translate(&address_space_memory, addr, &addr1, &l, + false); + if (l < 2 || !memory_access_is_direct(mr, false)) { /* I/O case */ - addr = memory_region_section_addr(section, addr); - val = io_mem_read(section->mr, addr, 2); + io_mem_read(mr, addr1, &val, 2); #if defined(TARGET_WORDS_BIGENDIAN) if (endian == DEVICE_LITTLE_ENDIAN) { val = bswap16(val); @@ -3817,9 +2409,9 @@ static inline uint32_t lduw_phys_internal(hwaddr addr, #endif } else { /* RAM case */ - ptr = qemu_get_ram_ptr((memory_region_get_ram_addr(section->mr) + ptr = qemu_get_ram_ptr((memory_region_get_ram_addr(mr) & TARGET_PAGE_MASK) - + memory_region_section_addr(section, addr)); + + addr1); switch (endian) { case DEVICE_LITTLE_ENDIAN: val = lduw_le_p(ptr); @@ -3856,20 +2448,16 @@ uint32_t lduw_be_phys(hwaddr addr) void stl_phys_notdirty(hwaddr addr, uint32_t val) { uint8_t *ptr; - MemoryRegionSection *section; - - section = phys_page_find(address_space_memory.dispatch, addr >> TARGET_PAGE_BITS); - - if (!memory_region_is_ram(section->mr) || section->readonly) { - addr = memory_region_section_addr(section, addr); - if (memory_region_is_ram(section->mr)) { - section = &phys_sections[phys_section_rom]; - } - io_mem_write(section->mr, addr, val, 4); + MemoryRegion *mr; + hwaddr l = 4; + hwaddr addr1; + + mr = address_space_translate(&address_space_memory, addr, &addr1, &l, + true); + if (l < 4 || !memory_access_is_direct(mr, true)) { + io_mem_write(mr, addr1, val, 4); } else { - unsigned long addr1 = (memory_region_get_ram_addr(section->mr) - & TARGET_PAGE_MASK) - + memory_region_section_addr(section, addr); + addr1 += memory_region_get_ram_addr(mr) & TARGET_PAGE_MASK; ptr = qemu_get_ram_ptr(addr1); stl_p(ptr, val); @@ -3885,47 +2473,18 @@ void stl_phys_notdirty(hwaddr addr, uint32_t val) } } -void stq_phys_notdirty(hwaddr addr, uint64_t val) -{ - uint8_t *ptr; - MemoryRegionSection *section; - - section = phys_page_find(address_space_memory.dispatch, addr >> TARGET_PAGE_BITS); - - if (!memory_region_is_ram(section->mr) || section->readonly) { - addr = memory_region_section_addr(section, addr); - if (memory_region_is_ram(section->mr)) { - section = &phys_sections[phys_section_rom]; - } -#ifdef TARGET_WORDS_BIGENDIAN - io_mem_write(section->mr, addr, val >> 32, 4); - io_mem_write(section->mr, addr + 4, (uint32_t)val, 4); -#else - io_mem_write(section->mr, addr, (uint32_t)val, 4); - io_mem_write(section->mr, addr + 4, val >> 32, 4); -#endif - } else { - ptr = qemu_get_ram_ptr((memory_region_get_ram_addr(section->mr) - & TARGET_PAGE_MASK) - + memory_region_section_addr(section, addr)); - stq_p(ptr, val); - } -} - /* warning: addr must be aligned */ static inline void stl_phys_internal(hwaddr addr, uint32_t val, enum device_endian endian) { uint8_t *ptr; - MemoryRegionSection *section; - - section = phys_page_find(address_space_memory.dispatch, addr >> TARGET_PAGE_BITS); + MemoryRegion *mr; + hwaddr l = 4; + hwaddr addr1; - if (!memory_region_is_ram(section->mr) || section->readonly) { - addr = memory_region_section_addr(section, addr); - if (memory_region_is_ram(section->mr)) { - section = &phys_sections[phys_section_rom]; - } + mr = address_space_translate(&address_space_memory, addr, &addr1, &l, + true); + if (l < 4 || !memory_access_is_direct(mr, true)) { #if defined(TARGET_WORDS_BIGENDIAN) if (endian == DEVICE_LITTLE_ENDIAN) { val = bswap32(val); @@ -3935,12 +2494,10 @@ static inline void stl_phys_internal(hwaddr addr, uint32_t val, val = bswap32(val); } #endif - io_mem_write(section->mr, addr, val, 4); + io_mem_write(mr, addr1, val, 4); } else { - unsigned long addr1; - addr1 = (memory_region_get_ram_addr(section->mr) & TARGET_PAGE_MASK) - + memory_region_section_addr(section, addr); /* RAM case */ + addr1 += memory_region_get_ram_addr(mr) & TARGET_PAGE_MASK; ptr = qemu_get_ram_ptr(addr1); switch (endian) { case DEVICE_LITTLE_ENDIAN: @@ -3984,15 +2541,13 @@ static inline void stw_phys_internal(hwaddr addr, uint32_t val, enum device_endian endian) { uint8_t *ptr; - MemoryRegionSection *section; + MemoryRegion *mr; + hwaddr l = 2; + hwaddr addr1; - section = phys_page_find(address_space_memory.dispatch, addr >> TARGET_PAGE_BITS); - - if (!memory_region_is_ram(section->mr) || section->readonly) { - addr = memory_region_section_addr(section, addr); - if (memory_region_is_ram(section->mr)) { - section = &phys_sections[phys_section_rom]; - } + mr = address_space_translate(&address_space_memory, addr, &addr1, &l, + true); + if (l < 2 || !memory_access_is_direct(mr, true)) { #if defined(TARGET_WORDS_BIGENDIAN) if (endian == DEVICE_LITTLE_ENDIAN) { val = bswap16(val); @@ -4002,12 +2557,10 @@ static inline void stw_phys_internal(hwaddr addr, uint32_t val, val = bswap16(val); } #endif - io_mem_write(section->mr, addr, val, 2); + io_mem_write(mr, addr1, val, 2); } else { - unsigned long addr1; - addr1 = (memory_region_get_ram_addr(section->mr) & TARGET_PAGE_MASK) - + memory_region_section_addr(section, addr); /* RAM case */ + addr1 += memory_region_get_ram_addr(mr) & TARGET_PAGE_MASK; ptr = qemu_get_ram_ptr(addr1); switch (endian) { case DEVICE_LITTLE_ENDIAN: @@ -4059,7 +2612,7 @@ void stq_be_phys(hwaddr addr, uint64_t val) } /* virtual memory access for debug (includes writing to ROM) */ -int cpu_memory_rw_debug(CPUArchState *env, target_ulong addr, +int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr, uint8_t *buf, int len, int is_write) { int l; @@ -4068,7 +2621,7 @@ int cpu_memory_rw_debug(CPUArchState *env, target_ulong addr, while (len > 0) { page = addr & TARGET_PAGE_MASK; - phys_addr = cpu_get_phys_page_debug(env, page); + phys_addr = cpu_get_phys_page_debug(cpu, page); /* if no physical page mapped, return an error */ if (phys_addr == -1) return -1; @@ -4088,119 +2641,8 @@ int cpu_memory_rw_debug(CPUArchState *env, target_ulong addr, } #endif -/* in deterministic execution mode, instructions doing device I/Os - must be at the end of the TB */ -void cpu_io_recompile(CPUArchState *env, uintptr_t retaddr) -{ - TranslationBlock *tb; - uint32_t n, cflags; - target_ulong pc, cs_base; - uint64_t flags; - - tb = tb_find_pc(retaddr); - if (!tb) { - cpu_abort(env, "cpu_io_recompile: could not find TB for pc=%p", - (void *)retaddr); - } - n = env->icount_decr.u16.low + tb->icount; - cpu_restore_state(tb, env, retaddr); - /* Calculate how many instructions had been executed before the fault - occurred. */ - n = n - env->icount_decr.u16.low; - /* Generate a new TB ending on the I/O insn. */ - n++; - /* On MIPS and SH, delay slot instructions can only be restarted if - they were already the first instruction in the TB. If this is not - the first instruction in a TB then re-execute the preceding - branch. */ -#if defined(TARGET_MIPS) - if ((env->hflags & MIPS_HFLAG_BMASK) != 0 && n > 1) { - env->active_tc.PC -= 4; - env->icount_decr.u16.low++; - env->hflags &= ~MIPS_HFLAG_BMASK; - } -#elif defined(TARGET_SH4) - if ((env->flags & ((DELAY_SLOT | DELAY_SLOT_CONDITIONAL))) != 0 - && n > 1) { - env->pc -= 2; - env->icount_decr.u16.low++; - env->flags &= ~(DELAY_SLOT | DELAY_SLOT_CONDITIONAL); - } -#endif - /* This should never happen. */ - if (n > CF_COUNT_MASK) - cpu_abort(env, "TB too big during recompile"); - - cflags = n | CF_LAST_IO; - pc = tb->pc; - cs_base = tb->cs_base; - flags = tb->flags; - tb_phys_invalidate(tb, -1); - /* FIXME: In theory this could raise an exception. In practice - we have already translated the block once so it's probably ok. */ - tb_gen_code(env, pc, cs_base, flags, cflags); - /* TODO: If env->pc != tb->pc (i.e. the faulting instruction was not - the first in the TB) then we end up generating a whole new TB and - repeating the fault, which is horribly inefficient. - Better would be to execute just this insn uncached, or generate a - second new TB. */ - cpu_resume_from_signal(env, NULL); -} - #if !defined(CONFIG_USER_ONLY) -void dump_exec_info(FILE *f, fprintf_function cpu_fprintf) -{ - 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, "Translation buffer state:\n"); - cpu_fprintf(f, "gen code size %td/%zd\n", - code_gen_ptr - code_gen_buffer, code_gen_buffer_max_size); - cpu_fprintf(f, "TB count %d/%d\n", - nb_tbs, code_gen_max_blocks); - cpu_fprintf(f, "TB avg target size %d max=%d bytes\n", - nb_tbs ? target_code_size / nb_tbs : 0, - max_target_code_size); - cpu_fprintf(f, "TB avg host size %td 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, "\nStatistics:\n"); - 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); - tcg_dump_info(f, cpu_fprintf); -} - /* * A helper function for the _utterly broken_ virtio device model to find out if * it's running on a big endian machine. Don't do this at home kids! @@ -4220,12 +2662,22 @@ bool virtio_is_big_endian(void) #ifndef CONFIG_USER_ONLY bool cpu_physical_memory_is_io(hwaddr phys_addr) { - MemoryRegionSection *section; + MemoryRegion*mr; + hwaddr l = 1; - section = phys_page_find(address_space_memory.dispatch, - phys_addr >> TARGET_PAGE_BITS); + mr = address_space_translate(&address_space_memory, + phys_addr, &phys_addr, &l, false); - return !(memory_region_is_ram(section->mr) || - memory_region_is_romd(section->mr)); + return !(memory_region_is_ram(mr) || + memory_region_is_romd(mr)); +} + +void qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque) +{ + RAMBlock *block; + + QTAILQ_FOREACH(block, &ram_list.blocks, next) { + func(block->host, block->offset, block->length, opaque); + } } #endif |