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>
This commit is contained in:
parent
0be1d07c0e
commit
68a1c81686
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
||||
/*
|
||||
|
@ -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 */
|
||||
|
@ -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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user