]> git.proxmox.com Git - mirror_qemu.git/blobdiff - exec.c
Fix bsd-user qemu_vmalloc() host page protection code
[mirror_qemu.git] / exec.c
diff --git a/exec.c b/exec.c
index 246e9701fd3a2a5913066ebde3b3bd573d0d0b80..8274ec2d0fa9e0e995fb0bbdc8cdf327213951ab 100644 (file)
--- a/exec.c
+++ b/exec.c
@@ -38,6 +38,7 @@
 #include "hw/hw.h"
 #include "osdep.h"
 #include "kvm.h"
+#include "qemu-timer.h"
 #if defined(CONFIG_USER_ONLY)
 #include <qemu.h>
 #include <signal.h>
@@ -135,16 +136,14 @@ typedef struct PageDesc {
 #endif
 } PageDesc;
 
-typedef struct PhysPageDesc {
-    /* offset in host memory of the page + io_index in the low bits */
-    ram_addr_t phys_offset;
-    ram_addr_t region_offset;
-} PhysPageDesc;
-
-/* In system mode we want L1_MAP to be based on physical addresses,
+/* 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
@@ -188,6 +187,12 @@ unsigned long qemu_host_page_mask;
 static void *l1_map[V_L1_SIZE];
 
 #if !defined(CONFIG_USER_ONLY)
+typedef struct PhysPageDesc {
+    /* offset in host memory of the page + io_index in the low bits */
+    ram_addr_t phys_offset;
+    ram_addr_t region_offset;
+} PhysPageDesc;
+
 /* This is a multi-level map on the physical address space.
    The bottom level has pointers to PhysPageDesc.  */
 static void *l1_phys_map[P_L1_SIZE];
@@ -213,7 +218,9 @@ int loglevel;
 static int log_append = 0;
 
 /* statistics */
+#if !defined(CONFIG_USER_ONLY)
 static int tlb_flush_count;
+#endif
 static int tb_flush_count;
 static int tb_phys_invalidate_count;
 
@@ -301,8 +308,12 @@ static void page_init(void)
 #endif
 }
 
-static PageDesc *page_find_alloc(target_ulong index, int alloc)
+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 qemu_malloc because it may recurse into a locked mutex.
        Neither can we record the new pages we reserve while allocating a
@@ -328,10 +339,6 @@ static PageDesc *page_find_alloc(target_ulong index, int alloc)
     do { P = qemu_mallocz(SIZE); } while (0)
 #endif
 
-    PageDesc *pd;
-    void **lp;
-    int i;
-
     /* Level 1.  Always allocated.  */
     lp = l1_map + ((index >> V_L1_SHIFT) & (V_L1_SIZE - 1));
 
@@ -374,7 +381,7 @@ static PageDesc *page_find_alloc(target_ulong index, int alloc)
     return pd + (index & (L2_SIZE - 1));
 }
 
-static inline PageDesc *page_find(target_ulong index)
+static inline PageDesc *page_find(tb_page_addr_t index)
 {
     return page_find_alloc(index, 0);
 }
@@ -636,13 +643,13 @@ static void page_flush_tb_1 (int level, void **lp)
     }
     if (level == 0) {
         PageDesc *pd = *lp;
-        for (i = 0; i < L2_BITS; ++i) {
+        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_BITS; ++i) {
+        for (i = 0; i < L2_SIZE; ++i) {
             page_flush_tb_1 (level - 1, pp + i);
         }
     }
@@ -791,12 +798,12 @@ static inline void tb_reset_jump(TranslationBlock *tb, int n)
     tb_set_jmp_target(tb, n, (unsigned long)(tb->tc_ptr + tb->tb_next_offset[n]));
 }
 
-void tb_phys_invalidate(TranslationBlock *tb, target_ulong page_addr)
+void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr)
 {
     CPUState *env;
     PageDesc *p;
     unsigned int h, n1;
-    target_phys_addr_t phys_pc;
+    tb_page_addr_t phys_pc;
     TranslationBlock *tb1, *tb2;
 
     /* remove the TB from the hash list */
@@ -908,10 +915,11 @@ TranslationBlock *tb_gen_code(CPUState *env,
 {
     TranslationBlock *tb;
     uint8_t *tc_ptr;
-    target_ulong phys_pc, phys_page2, virt_page2;
+    tb_page_addr_t phys_pc, phys_page2;
+    target_ulong virt_page2;
     int code_gen_size;
 
-    phys_pc = get_phys_addr_code(env, pc);
+    phys_pc = get_page_addr_code(env, pc);
     tb = tb_alloc(pc);
     if (!tb) {
         /* flush must be done */
@@ -933,9 +941,9 @@ TranslationBlock *tb_gen_code(CPUState *env,
     virt_page2 = (pc + tb->size - 1) & TARGET_PAGE_MASK;
     phys_page2 = -1;
     if ((pc & TARGET_PAGE_MASK) != virt_page2) {
-        phys_page2 = get_phys_addr_code(env, virt_page2);
+        phys_page2 = get_page_addr_code(env, virt_page2);
     }
-    tb_link_phys(tb, phys_pc, phys_page2);
+    tb_link_page(tb, phys_pc, phys_page2);
     return tb;
 }
 
@@ -944,12 +952,12 @@ TranslationBlock *tb_gen_code(CPUState *env,
    the same physical page. 'is_cpu_write_access' should be true if called
    from a real cpu write access: the virtual CPU will exit the current
    TB if code is modified inside this TB. */
-void tb_invalidate_phys_page_range(target_phys_addr_t start, target_phys_addr_t end,
+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;
     CPUState *env = cpu_single_env;
-    target_ulong tb_start, tb_end;
+    tb_page_addr_t tb_start, tb_end;
     PageDesc *p;
     int n;
 #ifdef TARGET_HAS_PRECISE_SMC
@@ -1051,7 +1059,7 @@ void tb_invalidate_phys_page_range(target_phys_addr_t start, target_phys_addr_t
 }
 
 /* len must be <= 8 and start must be a multiple of len */
-static inline void tb_invalidate_phys_page_fast(target_phys_addr_t start, int len)
+static inline void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len)
 {
     PageDesc *p;
     int offset, b;
@@ -1078,7 +1086,7 @@ static inline void tb_invalidate_phys_page_fast(target_phys_addr_t start, int le
 }
 
 #if !defined(CONFIG_SOFTMMU)
-static void tb_invalidate_phys_page(target_phys_addr_t addr,
+static void tb_invalidate_phys_page(tb_page_addr_t addr,
                                     unsigned long pc, void *puc)
 {
     TranslationBlock *tb;
@@ -1140,7 +1148,7 @@ static void tb_invalidate_phys_page(target_phys_addr_t addr,
 
 /* add the tb in the target page and protect it if necessary */
 static inline void tb_alloc_page(TranslationBlock *tb,
-                                 unsigned int n, target_ulong page_addr)
+                                 unsigned int n, tb_page_addr_t page_addr)
 {
     PageDesc *p;
     TranslationBlock *last_first_tb;
@@ -1172,7 +1180,6 @@ static inline void tb_alloc_page(TranslationBlock *tb,
                 continue;
             prot |= p2->flags;
             p2->flags &= ~PAGE_WRITE;
-            page_get_flags(addr);
           }
         mprotect(g2h(page_addr), qemu_host_page_size,
                  (prot & PAGE_BITS) & ~PAGE_WRITE);
@@ -1221,8 +1228,8 @@ void tb_free(TranslationBlock *tb)
 
 /* add a new TB and link it to the physical page tables. phys_page2 is
    (-1) to indicate that only one page contains the TB. */
-void tb_link_phys(TranslationBlock *tb,
-                  target_ulong phys_pc, target_ulong phys_page2)
+void tb_link_page(TranslationBlock *tb,
+                  tb_page_addr_t phys_pc, tb_page_addr_t phys_page2)
 {
     unsigned int h;
     TranslationBlock **ptb;
@@ -1716,7 +1723,7 @@ static void phys_page_for_each_1(CPUPhysMemoryClient *client,
     }
     if (level == 0) {
         PhysPageDesc *pd = *lp;
-        for (i = 0; i < L2_BITS; ++i) {
+        for (i = 0; i < L2_SIZE; ++i) {
             if (pd[i].phys_offset != IO_MEM_UNASSIGNED) {
                 client->set_memory(client, pd[i].region_offset,
                                    TARGET_PAGE_SIZE, pd[i].phys_offset);
@@ -1724,7 +1731,7 @@ static void phys_page_for_each_1(CPUPhysMemoryClient *client,
         }
     } else {
         void **pp = *lp;
-        for (i = 0; i < L2_BITS; ++i) {
+        for (i = 0; i < L2_SIZE; ++i) {
             phys_page_for_each_1(client, level - 1, pp + i);
         }
     }
@@ -1911,6 +1918,8 @@ void tlb_flush(CPUState *env, int flush_global)
 
     memset (env->tb_jmp_cache, 0, TB_JMP_CACHE_SIZE * sizeof (void *));
 
+    env->tlb_flush_addr = -1;
+    env->tlb_flush_mask = 0;
     tlb_flush_count++;
 }
 
@@ -1934,6 +1943,16 @@ void tlb_flush_page(CPUState *env, target_ulong addr)
 #if defined(DEBUG_TLB)
     printf("tlb_flush_page: " TARGET_FMT_lx "\n", addr);
 #endif
+    /* Check if we need to flush due to large pages.  */
+    if ((addr & env->tlb_flush_mask) == env->tlb_flush_addr) {
+#if defined(DEBUG_TLB)
+        printf("tlb_flush_page: forced full flush ("
+               TARGET_FMT_lx "/" TARGET_FMT_lx ")\n",
+               env->tlb_flush_addr, env->tlb_flush_mask);
+#endif
+        tlb_flush(env, 1);
+        return;
+    }
     /* must reset current TB so that interrupts cannot modify the
        links while we are modifying them */
     env->current_tb = NULL;
@@ -2083,13 +2102,35 @@ static inline void tlb_set_dirty(CPUState *env, target_ulong vaddr)
         tlb_set_dirty1(&env->tlb_table[mmu_idx][i], vaddr);
 }
 
-/* add a new TLB entry. At most one entry for a given virtual address
-   is permitted. Return 0 if OK or 2 if the page could not be mapped
-   (can only happen in non SOFTMMU mode for I/O pages or pages
-   conflicting with the host address space). */
-int tlb_set_page_exec(CPUState *env, target_ulong vaddr,
-                      target_phys_addr_t paddr, int prot,
-                      int mmu_idx, int is_softmmu)
+/* Our TLB does not support large pages, so remember the area covered by
+   large pages and trigger a full TLB flush if these are invalidated.  */
+static void tlb_add_large_page(CPUState *env, target_ulong vaddr,
+                               target_ulong size)
+{
+    target_ulong mask = ~(size - 1);
+
+    if (env->tlb_flush_addr == (target_ulong)-1) {
+        env->tlb_flush_addr = vaddr & mask;
+        env->tlb_flush_mask = mask;
+        return;
+    }
+    /* Extend the existing region to include the new page.
+       This is a compromise between unnecessary flushes and the cost
+       of maintaining a full variable size TLB.  */
+    mask &= env->tlb_flush_mask;
+    while (((env->tlb_flush_addr ^ vaddr) & mask) != 0) {
+        mask <<= 1;
+    }
+    env->tlb_flush_addr &= mask;
+    env->tlb_flush_mask = mask;
+}
+
+/* Add a new TLB entry. At most one entry for a given virtual address
+   is permitted. Only a single TARGET_PAGE_SIZE region is mapped, the
+   supplied size is only used by tlb_flush_page.  */
+void tlb_set_page(CPUState *env, target_ulong vaddr,
+                  target_phys_addr_t paddr, int prot,
+                  int mmu_idx, target_ulong size)
 {
     PhysPageDesc *p;
     unsigned long pd;
@@ -2097,11 +2138,14 @@ int tlb_set_page_exec(CPUState *env, target_ulong vaddr,
     target_ulong address;
     target_ulong code_address;
     target_phys_addr_t addend;
-    int ret;
     CPUTLBEntry *te;
     CPUWatchpoint *wp;
     target_phys_addr_t iotlb;
 
+    assert(size >= TARGET_PAGE_SIZE);
+    if (size != TARGET_PAGE_SIZE) {
+        tlb_add_large_page(env, vaddr, size);
+    }
     p = phys_page_find(paddr >> TARGET_PAGE_BITS);
     if (!p) {
         pd = IO_MEM_UNASSIGNED;
@@ -2113,7 +2157,6 @@ int tlb_set_page_exec(CPUState *env, target_ulong vaddr,
            vaddr, (int)paddr, prot, mmu_idx, is_softmmu, pd);
 #endif
 
-    ret = 0;
     address = vaddr;
     if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM && !(pd & IO_MEM_ROMD)) {
         /* IO memory case (romd handled later) */
@@ -2183,7 +2226,6 @@ int tlb_set_page_exec(CPUState *env, target_ulong vaddr,
     } else {
         te->addr_write = -1;
     }
-    return ret;
 }
 
 #else
@@ -2210,7 +2252,7 @@ struct walk_memory_regions_data
 };
 
 static int walk_memory_regions_end(struct walk_memory_regions_data *data,
-                                   unsigned long end, int new_prot)
+                                   abi_ulong end, int new_prot)
 {
     if (data->start != -1ul) {
         int rc = data->fn(data->priv, data->start, end, data->prot);
@@ -2226,9 +2268,9 @@ static int walk_memory_regions_end(struct walk_memory_regions_data *data,
 }
 
 static int walk_memory_regions_1(struct walk_memory_regions_data *data,
-                                 unsigned long base, int level, void **lp)
+                                 abi_ulong base, int level, void **lp)
 {
-    unsigned long pa;
+    abi_ulong pa;
     int i, rc;
 
     if (*lp == NULL) {
@@ -2237,7 +2279,7 @@ static int walk_memory_regions_1(struct walk_memory_regions_data *data,
 
     if (level == 0) {
         PageDesc *pd = *lp;
-        for (i = 0; i < L2_BITS; ++i) {
+        for (i = 0; i < L2_SIZE; ++i) {
             int prot = pd[i].flags;
 
             pa = base | (i << TARGET_PAGE_BITS);
@@ -2250,8 +2292,9 @@ static int walk_memory_regions_1(struct walk_memory_regions_data *data,
         }
     } else {
         void **pp = *lp;
-        for (i = 0; i < L2_BITS; ++i) {
-            pa = base | (i << (TARGET_PAGE_BITS + L2_BITS * level));
+        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;
@@ -2273,7 +2316,7 @@ int walk_memory_regions(void *priv, walk_memory_regions_fn fn)
     data.prot = 0;
 
     for (i = 0; i < V_L1_SIZE; i++) {
-        int rc = walk_memory_regions_1(&data, i << V_L1_SHIFT,
+        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;
@@ -2283,12 +2326,13 @@ int walk_memory_regions(void *priv, walk_memory_regions_fn fn)
     return walk_memory_regions_end(&data, 0, 0);
 }
 
-static int dump_region(void *priv, unsigned long start,
-    unsigned long end, unsigned long prot)
+static int dump_region(void *priv, abi_ulong start,
+    abi_ulong end, unsigned long prot)
 {
     FILE *f = (FILE *)priv;
 
-    (void) fprintf(f, "%08lx-%08lx %08lx %c%c%c\n",
+    (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' : '-'),
@@ -2325,8 +2369,8 @@ void page_set_flags(target_ulong start, target_ulong end, int flags)
     /* 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 HOST_LONG_BITS > L1_MAP_ADDR_SPACE_BITS
-    assert(end < (1ul << L1_MAP_ADDR_SPACE_BITS));
+#if TARGET_ABI_BITS > L1_MAP_ADDR_SPACE_BITS
+    assert(end < ((abi_ulong)1 << L1_MAP_ADDR_SPACE_BITS));
 #endif
     assert(start < end);
 
@@ -2362,8 +2406,8 @@ int page_check_range(target_ulong start, target_ulong len, int flags)
     /* 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 HOST_LONG_BITS > L1_MAP_ADDR_SPACE_BITS
-    assert(start < (1ul << L1_MAP_ADDR_SPACE_BITS));
+#if TARGET_ABI_BITS > L1_MAP_ADDR_SPACE_BITS
+    assert(start < ((abi_ulong)1 << L1_MAP_ADDR_SPACE_BITS));
 #endif
 
     if (start + len - 1 < start) {
@@ -2624,7 +2668,7 @@ static long gethugepagesize(const char *path)
     } while (ret != 0 && errno == EINTR);
 
     if (ret != 0) {
-           perror("statfs");
+           perror(path);
            return 0;
     }
 
@@ -2664,7 +2708,7 @@ static void *file_ram_alloc(ram_addr_t memory, const char *path)
 
     fd = mkstemp(filename);
     if (fd < 0) {
-       perror("mkstemp");
+       perror("unable to create backing store for hugepages");
        free(filename);
        return NULL;
     }
@@ -3919,6 +3963,8 @@ void cpu_io_recompile(CPUState *env, void *retaddr)
     cpu_resume_from_signal(env, NULL);
 }
 
+#if !defined(CONFIG_USER_ONLY)
+
 void dump_exec_info(FILE *f,
                     int (*cpu_fprintf)(FILE *f, const char *fmt, ...))
 {
@@ -3972,8 +4018,6 @@ void dump_exec_info(FILE *f,
     tcg_dump_info(f, cpu_fprintf);
 }
 
-#if !defined(CONFIG_USER_ONLY)
-
 #define MMUSUFFIX _cmmu
 #define GETPC() NULL
 #define env cpu_single_env