Pre-allocate guest address space

Allow pre-allocation of the guest virtual address space in usermode emulation.

Signed-off-by: Paul Brook <paul@codesourcery.com>
master
Paul Brook 2010-05-29 02:27:35 +01:00
parent 0be1d07c0e
commit 68a1c81686
5 changed files with 183 additions and 8 deletions

View File

@ -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

View 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

View 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 */
/*

View 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 */

View 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: