diff --git a/linux-user/elfload.c b/linux-user/elfload.c index cbc7617765..819fdd515a 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -332,9 +332,17 @@ enum ARM_HWCAP_ARM_VFPv3D16 = 1 << 13, }; -#define TARGET_HAS_GUEST_VALIDATE_BASE -/* We want the opportunity to check the suggested base */ -bool guest_validate_base(unsigned long guest_base) +#define TARGET_HAS_VALIDATE_GUEST_SPACE +/* Return 1 if the proposed guest space is suitable for the guest. + * Return 0 if the proposed guest space isn't suitable, but another + * address space should be tried. + * Return -1 if there is no way the proposed guest space can be + * valid regardless of the base. + * The guest code may leave a page mapped and populate it if the + * address is suitable. + */ +static int validate_guest_space(unsigned long guest_base, + unsigned long guest_size) { unsigned long real_start, test_page_addr; @@ -342,6 +350,15 @@ bool guest_validate_base(unsigned long guest_base) * commpage at 0xffff0fxx */ test_page_addr = guest_base + (0xffff0f00 & qemu_host_page_mask); + + /* If the commpage lies within the already allocated guest space, + * then there is no way we can allocate it. + */ + if (test_page_addr >= guest_base + && test_page_addr <= (guest_base + guest_size)) { + return -1; + } + /* Note it needs to be writeable to let us initialise it */ real_start = (unsigned long) mmap((void *)test_page_addr, qemu_host_page_size, @@ -1418,9 +1435,10 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, return sp; } -#ifndef TARGET_HAS_GUEST_VALIDATE_BASE +#ifndef TARGET_HAS_VALIDATE_GUEST_SPACE /* If the guest doesn't have a validation function just agree */ -bool guest_validate_base(unsigned long guest_base) +static int validate_guest_space(unsigned long guest_base, + unsigned long guest_size) { return 1; } @@ -1439,7 +1457,7 @@ unsigned long init_guest_space(unsigned long host_start, /* If just a starting address is given, then just verify that * address. */ if (host_start && !host_size) { - if (guest_validate_base(host_start)) { + if (validate_guest_space(host_start, host_size) == 1) { return host_start; } else { return (unsigned long)-1; @@ -1456,6 +1474,8 @@ unsigned long init_guest_space(unsigned long host_start, /* Otherwise, a non-zero size region of memory needs to be mapped * and validated. */ while (1) { + unsigned long real_size = host_size; + /* Do not use mmap_find_vma here because that is limited to the * guest address space. We are going to make the * guest address space fit whatever we're given. @@ -1466,9 +1486,28 @@ unsigned long init_guest_space(unsigned long host_start, return (unsigned long)-1; } - if ((real_start == current_start) - && guest_validate_base(real_start - guest_start)) { - break; + /* Ensure the address is properly aligned. */ + if (real_start & ~qemu_host_page_mask) { + munmap((void *)real_start, host_size); + real_size = host_size + qemu_host_page_size; + real_start = (unsigned long) + mmap((void *)real_start, real_size, PROT_NONE, flags, -1, 0); + if (real_start == (unsigned long)-1) { + return (unsigned long)-1; + } + real_start = HOST_PAGE_ALIGN(real_start); + } + + /* Check to see if the address is valid. */ + if (!host_start || real_start == current_start) { + int valid = validate_guest_space(real_start - guest_start, + real_size); + if (valid == 1) { + break; + } else if (valid == -1) { + return (unsigned long)-1; + } + /* valid == 0, so try again. */ } /* That address didn't work. Unmap and try a different one. @@ -1490,6 +1529,8 @@ unsigned long init_guest_space(unsigned long host_start, } } + qemu_log("Reserved 0x%lx bytes of guest address space\n", host_size); + return real_start; } diff --git a/linux-user/main.c b/linux-user/main.c index 9d921aa4f0..63c1249576 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -3516,39 +3516,16 @@ int main(int argc, char **argv, char **envp) */ 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) { + if (reserved_va || have_guest_base) { + guest_base = init_guest_space(guest_base, reserved_va, 0, + have_guest_base); + if (guest_base == (unsigned long)-1) { 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); - mmap_next_start = reserved_va; - } - if (reserved_va || have_guest_base) { - if (!guest_validate_base(guest_base)) { - fprintf(stderr, "Guest base/Reserved VA rejected by guest code\n"); - exit(1); + if (reserved_va) { + mmap_next_start = reserved_va; } } #endif /* CONFIG_USE_GUEST_BASE */ diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 7d4e23e4c1..69b27d7146 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -204,12 +204,6 @@ int get_osversion(void); void fork_start(void); void fork_end(int child); -/* Return true if the proposed guest_base is suitable for the guest. - * The guest code may leave a page mapped and populate it if the - * address is suitable. - */ -bool guest_validate_base(unsigned long guest_base); - /* Creates the initial guest address space in the host memory space using * the given host start address hint and size. The guest_start parameter * specifies the start address of the guest space. guest_base will be the