]> Git Repo - qemu.git/commitdiff
Pre-allocate guest address space
authorPaul Brook <[email protected]>
Sat, 29 May 2010 01:27:35 +0000 (02:27 +0100)
committerPaul Brook <[email protected]>
Sat, 29 May 2010 01:27:35 +0000 (02:27 +0100)
Allow pre-allocation of the guest virtual address space in usermode emulation.

Signed-off-by: Paul Brook <[email protected]>
cpu-all.h
linux-user/elfload.c
linux-user/main.c
linux-user/mmap.c
qemu-doc.texi

index 47a5722a1d9281492d8abffcce299a83e5b9f7bf..77eaf8573525ff7a6efd9bbd0e9ae2f113a3d5e9 100644 (file)
--- a/cpu-all.h
+++ b/cpu-all.h
@@ -627,6 +627,7 @@ static inline void stfq_be_p(void *ptr, float64 v)
 #if defined(CONFIG_USE_GUEST_BASE)
 extern unsigned long guest_base;
 extern int have_guest_base;
+extern unsigned long reserved_va;
 #define GUEST_BASE guest_base
 #else
 #define GUEST_BASE 0ul
index 1f27918beb6674c5e0fd573342517123580c04da..2d920f2017e3150dafab403b7c68f0cc8afdcace 100644 (file)
@@ -1682,7 +1682,7 @@ int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
      * In case where user has not explicitly set the guest_base, we
      * probe here that should we set it automatically.
      */
-    if (!have_guest_base) {
+    if (!(have_guest_base || reserved_va)) {
         /*
          * Go through ELF program header table and find the address
          * range used by loadable segments.  Check that this is available on
index de1076b0afeebb069a5f3feb9652d12f8dea7fbe..0f23fc9cd9c1d6c1bd70ac6af4c72d62f7abb9a0 100644 (file)
@@ -44,6 +44,7 @@ unsigned long mmap_min_addr;
 #if defined(CONFIG_USE_GUEST_BASE)
 unsigned long guest_base;
 int have_guest_base;
+unsigned long reserved_va;
 #endif
 
 static const char *interp_prefix = CONFIG_QEMU_PREFIX;
@@ -2610,6 +2611,7 @@ static void usage(void)
            "-0 argv0          forces target process argv[0] to be argv0\n"
 #if defined(CONFIG_USE_GUEST_BASE)
            "-B address        set guest_base address to address\n"
+           "-R size           reserve size bytes for guest virtual address space\n"
 #endif
            "\n"
            "Debug options:\n"
@@ -2805,6 +2807,39 @@ int main(int argc, char **argv, char **envp)
         } else if (!strcmp(r, "B")) {
            guest_base = strtol(argv[optind++], NULL, 0);
            have_guest_base = 1;
+        } else if (!strcmp(r, "R")) {
+            char *p;
+            int shift = 0;
+            reserved_va = strtoul(argv[optind++], &p, 0);
+            switch (*p) {
+            case 'k':
+            case 'K':
+                shift = 10;
+                break;
+            case 'M':
+                shift = 20;
+                break;
+            case 'G':
+                shift = 30;
+                break;
+            }
+            if (shift) {
+                unsigned long unshifted = reserved_va;
+                p++;
+                reserved_va <<= shift;
+                if (((reserved_va >> shift) != unshifted)
+#if HOST_LONG_BITS > TARGET_VIRT_ADDR_SPACE_BITS
+                    || (reserved_va > (1ul << TARGET_VIRT_ADDR_SPACE_BITS))
+#endif
+                    ) {
+                    fprintf(stderr, "Reserved virtual address too big\n");
+                    exit(1);
+                }
+            }
+            if (*p) {
+                fprintf(stderr, "Unrecognised -R size suffix '%s'\n", p);
+                exit(1);
+            }
 #endif
         } else if (!strcmp(r, "drop-ld-preload")) {
             (void) envlist_unsetenv(envlist, "LD_PRELOAD");
@@ -2893,6 +2928,34 @@ int main(int argc, char **argv, char **envp)
      * proper page alignment for guest_base.
      */
     guest_base = HOST_PAGE_ALIGN(guest_base);
+
+    if (reserved_va) {
+        void *p;
+        int flags;
+
+        flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE;
+        if (have_guest_base) {
+            flags |= MAP_FIXED;
+        }
+        p = mmap((void *)guest_base, reserved_va, PROT_NONE, flags, -1, 0);
+        if (p == MAP_FAILED) {
+            fprintf(stderr, "Unable to reserve guest address space\n");
+            exit(1);
+        }
+        guest_base = (unsigned long)p;
+        /* Make sure the address is properly aligned.  */
+        if (guest_base & ~qemu_host_page_mask) {
+            munmap(p, reserved_va);
+            p = mmap((void *)guest_base, reserved_va + qemu_host_page_size,
+                     PROT_NONE, flags, -1, 0);
+            if (p == MAP_FAILED) {
+                fprintf(stderr, "Unable to reserve guest address space\n");
+                exit(1);
+            }
+            guest_base = HOST_PAGE_ALIGN((unsigned long)p);
+        }
+        qemu_log("Reserved 0x%lx bytes of guest address space\n", reserved_va);
+    }
 #endif /* CONFIG_USE_GUEST_BASE */
 
     /*
index fd315aaabba92b8f766d4ae8a4e4122c157bb709..39da6dfb402307f6efc6c8f48425e7e120557b92 100644 (file)
@@ -216,6 +216,40 @@ static abi_ulong mmap_next_start = TASK_UNMAPPED_BASE;
 
 unsigned long last_brk;
 
+/* Subroutine of mmap_find_vma, used when we have pre-allocated a chunk
+   of guest address space.  */
+static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size)
+{
+    abi_ulong addr;
+    abi_ulong last_addr;
+    int prot;
+    int looped = 0;
+
+    if (size > reserved_va) {
+        return (abi_ulong)-1;
+    }
+
+    last_addr = start;
+    for (addr = start; last_addr + size != addr; addr += qemu_host_page_size) {
+        if (last_addr + size >= reserved_va
+            || (abi_ulong)(last_addr + size) < last_addr) {
+            if (looped) {
+                return (abi_ulong)-1;
+            }
+            last_addr = qemu_host_page_size;
+            addr = 0;
+            looped = 1;
+            continue;
+        }
+        prot = page_get_flags(addr);
+        if (prot) {
+            last_addr = addr + qemu_host_page_size;
+        }
+    }
+    mmap_next_start = addr;
+    return last_addr;
+}
+
 /*
  * Find and reserve a free memory area of size 'size'. The search
  * starts at 'start'.
@@ -237,6 +271,10 @@ abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
 
     size = HOST_PAGE_ALIGN(size);
 
+    if (reserved_va) {
+        return mmap_find_vma_reserved(start, size);
+    }
+
     addr = start;
     wrapped = repeat = 0;
     prev = 0;
@@ -525,6 +563,47 @@ fail:
     return -1;
 }
 
+static void mmap_reserve(abi_ulong start, abi_ulong size)
+{
+    abi_ulong real_start;
+    abi_ulong real_end;
+    abi_ulong addr;
+    abi_ulong end;
+    int prot;
+
+    real_start = start & qemu_host_page_mask;
+    real_end = HOST_PAGE_ALIGN(start + size);
+    end = start + size;
+    if (start > real_start) {
+        /* handle host page containing start */
+        prot = 0;
+        for (addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
+            prot |= page_get_flags(addr);
+        }
+        if (real_end == real_start + qemu_host_page_size) {
+            for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
+                prot |= page_get_flags(addr);
+            }
+            end = real_end;
+        }
+        if (prot != 0)
+            real_start += qemu_host_page_size;
+    }
+    if (end < real_end) {
+        prot = 0;
+        for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
+            prot |= page_get_flags(addr);
+        }
+        if (prot != 0)
+            real_end -= qemu_host_page_size;
+    }
+    if (real_start != real_end) {
+        mmap(g2h(real_start), real_end - real_start, PROT_NONE,
+                 MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE,
+                 -1, 0);
+    }
+}
+
 int target_munmap(abi_ulong start, abi_ulong len)
 {
     abi_ulong end, real_start, real_end, addr;
@@ -572,7 +651,11 @@ int target_munmap(abi_ulong start, abi_ulong len)
     ret = 0;
     /* unmap what we can */
     if (real_start < real_end) {
-        ret = munmap(g2h(real_start), real_end - real_start);
+        if (reserved_va) {
+            mmap_reserve(real_start, real_end - real_start);
+        } else {
+            ret = munmap(g2h(real_start), real_end - real_start);
+        }
     }
 
     if (ret == 0)
@@ -590,12 +673,18 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
 
     mmap_lock();
 
-    if (flags & MREMAP_FIXED)
+    if (flags & MREMAP_FIXED) {
         host_addr = (void *) syscall(__NR_mremap, g2h(old_addr),
                                      old_size, new_size,
                                      flags,
-                                     new_addr);
-    else if (flags & MREMAP_MAYMOVE) {
+                                     g2h(new_addr));
+
+        if (reserved_va && host_addr != MAP_FAILED) {
+            /* If new and old addresses overlap then the above mremap will
+               already have failed with EINVAL.  */
+            mmap_reserve(old_addr, old_size);
+        }
+    } else if (flags & MREMAP_MAYMOVE) {
         abi_ulong mmap_start;
 
         mmap_start = mmap_find_vma(0, new_size);
@@ -603,13 +692,32 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
         if (mmap_start == -1) {
             errno = ENOMEM;
             host_addr = MAP_FAILED;
-        } else
+        } else {
             host_addr = (void *) syscall(__NR_mremap, g2h(old_addr),
                                          old_size, new_size,
                                          flags | MREMAP_FIXED,
                                          g2h(mmap_start));
+            mmap_reserve(old_addr, old_size);
+        }
     } else {
-        host_addr = mremap(g2h(old_addr), old_size, new_size, flags);
+        int prot = 0;
+        if (reserved_va && old_size < new_size) {
+            abi_ulong addr;
+            for (addr = old_addr + old_size;
+                 addr < old_addr + new_size;
+                 addr++) {
+                prot |= page_get_flags(addr);
+            }
+        }
+        if (prot == 0) {
+            host_addr = mremap(g2h(old_addr), old_size, new_size, flags);
+            if (host_addr != MAP_FAILED && reserved_va && old_size > new_size) {
+                mmap_reserve(old_addr + old_size, new_size - old_size);
+            }
+        } else {
+            errno = ENOMEM;
+            host_addr = MAP_FAILED;
+        }
         /* Check if address fits target address space */
         if ((unsigned long)host_addr + new_size > (abi_ulong)-1) {
             /* Revert mremap() changes */
index 6647b7b2e4ad5f99a354a67702e8393fbf9f27e5..e2c8e5638079a9e59bdf464ad901d02e8b5e92b1 100644 (file)
@@ -2124,7 +2124,7 @@ qemu-i386 /usr/local/qemu-i386/wine/bin/wine \
 @subsection Command line options
 
 @example
-usage: qemu-i386 [-h] [-d] [-L path] [-s size] [-cpu model] [-g port] [-B offset] program [arguments...]
+usage: qemu-i386 [-h] [-d] [-L path] [-s size] [-cpu model] [-g port] [-B offset] [-R size] program [arguments...]
 @end example
 
 @table @option
@@ -2140,6 +2140,9 @@ Select CPU model (-cpu ? for list and additional feature selection)
 Offset guest address by the specified number of bytes.  This is useful when
 the address region rewuired by guest applications is reserved on the host.
 Ths option is currently only supported on some hosts.
+@item -R size
+Pre-allocate a guest virtual address space of the given size (in bytes).
+"G", "M", and "k" suffixes may be used when specifying the size.  
 @end table
 
 Debug options:
This page took 0.044842 seconds and 4 git commands to generate.