[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH v1 04/10] linux-user: completely re-write init_guest_space
From: |
Thomas Huth |
Subject: |
Re: [PATCH v1 04/10] linux-user: completely re-write init_guest_space |
Date: |
Thu, 21 May 2020 06:43:16 +0200 |
User-agent: |
Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.9.0 |
On 13/05/2020 19.51, Alex Bennée wrote:
> First we ensure all guest space initialisation logic comes through
> probe_guest_base once we understand the nature of the binary we are
> loading. The convoluted init_guest_space routine is removed and
> replaced with a number of pgb_* helpers which are called depending on
> what requirements we have when loading the binary.
>
> We first try to do what is requested by the host. Failing that we try
> and satisfy the guest requested base address. If all those options
> fail we fall back to finding a space in the memory map using our
> recently written read_self_maps() helper.
>
> There are some additional complications we try and take into account
> when looking for holes in the address space. We try not to go directly
> after the system brk() space so there is space for a little growth. We
> also don't want to have to use negative offsets which would result in
> slightly less efficient code on x86 when it's unable to use the
> segment offset register.
>
> Less mind-binding gotos and hopefully clearer logic throughout.
>
> Signed-off-by: Alex Bennée <address@hidden>
> Acked-by: Laurent Vivier <address@hidden>
[...]
> diff --git a/linux-user/elfload.c b/linux-user/elfload.c
> index 619c054cc48..01a9323a637 100644
> --- a/linux-user/elfload.c
> +++ b/linux-user/elfload.c
> @@ -11,6 +11,7 @@
> #include "qemu/queue.h"
> #include "qemu/guest-random.h"
> #include "qemu/units.h"
> +#include "qemu/selfmap.h"
>
> #ifdef _ARCH_PPC64
> #undef ARCH_DLINFO
> @@ -382,68 +383,30 @@ enum {
>
> /* The commpage only exists for 32 bit kernels */
>
> -/* 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 init_guest_commpage(unsigned long guest_base,
> - unsigned long guest_size)
> -{
> - unsigned long real_start, test_page_addr;
> -
> - /* We need to check that we can force a fault on access to the
> - * 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.
> - *
> - * You may be thinking that that this check is redundant because
> - * we already validated the guest size against MAX_RESERVED_VA;
> - * but if qemu_host_page_mask is unusually large, then
> - * test_page_addr may be lower.
> - */
> - if (test_page_addr >= guest_base
> - && test_page_addr < (guest_base + guest_size)) {
> - return -1;
> - }
> +#define ARM_COMMPAGE (intptr_t)0xffff0f00u
>
> - /* Note it needs to be writeable to let us initialise it */
> - real_start = (unsigned long)
> - mmap((void *)test_page_addr, qemu_host_page_size,
> - PROT_READ | PROT_WRITE,
> - MAP_ANONYMOUS | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
> +static bool init_guest_commpage(void)
> +{
> + void *want = g2h(ARM_COMMPAGE & -qemu_host_page_size);
> + void *addr = mmap(want, qemu_host_page_size, PROT_READ | PROT_WRITE,
> + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
>
> - /* If we can't map it then try another address */
> - if (real_start == -1ul) {
> - return 0;
> + if (addr == MAP_FAILED) {
> + perror("Allocating guest commpage");
> + exit(EXIT_FAILURE);
> }
> -
> - if (real_start != test_page_addr) {
> - /* OS didn't put the page where we asked - unmap and reject */
> - munmap((void *)real_start, qemu_host_page_size);
> - return 0;
> + if (addr != want) {
> + return false;
> }
>
> - /* Leave the page mapped
> - * Populate it (mmap should have left it all 0'd)
> - */
> -
> - /* Kernel helper versions */
> - __put_user(5, (uint32_t *)g2h(0xffff0ffcul));
> + /* Set kernel helper versions; rest of page is 0. */
> + __put_user(5, (uint32_t *)g2h(0xffff0ffcu));
>
> - /* Now it's populated make it RO */
> - if (mprotect((void *)test_page_addr, qemu_host_page_size, PROT_READ)) {
> + if (mprotect(addr, qemu_host_page_size, PROT_READ)) {
> perror("Protecting guest commpage");
> - exit(-1);
> + exit(EXIT_FAILURE);
> }
> -
> - return 1; /* All good */
> + return true;
> }
>
> #define ELF_HWCAP get_elf_hwcap()
> @@ -2075,239 +2038,267 @@ static abi_ulong create_elf_tables(abi_ulong p, int
> argc, int envc,
> return sp;
> }
>
> -unsigned long init_guest_space(unsigned long host_start,
> - unsigned long host_size,
> - unsigned long guest_start,
> - bool fixed)
> -{
> - /* In order to use host shmat, we must be able to honor SHMLBA. */
> - unsigned long align = MAX(SHMLBA, qemu_host_page_size);
> - unsigned long current_start, aligned_start;
> - int flags;
> -
> - assert(host_start || host_size);
> -
> - /* If just a starting address is given, then just verify that
> - * address. */
> - if (host_start && !host_size) {
> -#if defined(TARGET_ARM) && !defined(TARGET_AARCH64)
> - if (init_guest_commpage(host_start, host_size) != 1) {
> - return (unsigned long)-1;
> - }
> +#ifndef ARM_COMMPAGE
> +#define ARM_COMMPAGE 0
> +#define init_guest_commpage() true
> #endif
> - return host_start;
> - }
>
> - /* Setup the initial flags and start address. */
> - current_start = host_start & -align;
> - flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE;
> - if (fixed) {
> - flags |= MAP_FIXED;
> - }
> +static void pgb_fail_in_use(const char *image_name)
> +{
> + error_report("%s: requires virtual address space that is in use "
> + "(omit the -B option or choose a different value)",
> + image_name);
> + exit(EXIT_FAILURE);
> +}
>
> - /* Otherwise, a non-zero size region of memory needs to be mapped
> - * and validated. */
> +static void pgb_have_guest_base(const char *image_name, abi_ulong
> guest_loaddr,
> + abi_ulong guest_hiaddr, long align)
> +{
> + const int flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE;
> + void *addr, *test;
>
> -#if defined(TARGET_ARM) && !defined(TARGET_AARCH64)
> - /* On 32-bit ARM, we need to map not just the usable memory, but
> - * also the commpage. Try to find a suitable place by allocating
> - * a big chunk for all of it. If host_start, then the naive
> - * strategy probably does good enough.
> - */
> - if (!host_start) {
> - unsigned long guest_full_size, host_full_size, real_start;
> -
> - guest_full_size =
> - (0xffff0f00 & qemu_host_page_mask) + qemu_host_page_size;
> - host_full_size = guest_full_size - guest_start;
> - real_start = (unsigned long)
> - mmap(NULL, host_full_size, PROT_NONE, flags, -1, 0);
> - if (real_start == (unsigned long)-1) {
> - if (host_size < host_full_size - qemu_host_page_size) {
> - /* We failed to map a continous segment, but we're
> - * allowed to have a gap between the usable memory and
> - * the commpage where other things can be mapped.
> - * This sparseness gives us more flexibility to find
> - * an address range.
> - */
> - goto naive;
> - }
> - return (unsigned long)-1;
> + if (!QEMU_IS_ALIGNED(guest_base, align)) {
> + fprintf(stderr, "Requested guest base 0x%lx does not satisfy "
> + "host minimum alignment (0x%lx)\n",
> + guest_base, align);
> + exit(EXIT_FAILURE);
> + }
> +
> + /* Sanity check the guest binary. */
> + if (reserved_va) {
> + if (guest_hiaddr > reserved_va) {
> + error_report("%s: requires more than reserved virtual "
> + "address space (0x%" PRIx64 " > 0x%lx)",
> + image_name, (uint64_t)guest_hiaddr, reserved_va);
> + exit(EXIT_FAILURE);
> }
> - munmap((void *)real_start, host_full_size);
> - if (real_start & (align - 1)) {
> - /* The same thing again, but with extra
> - * so that we can shift around alignment.
> - */
> - unsigned long real_size = host_full_size + qemu_host_page_size;
> - real_start = (unsigned long)
> - mmap(NULL, real_size, PROT_NONE, flags, -1, 0);
> - if (real_start == (unsigned long)-1) {
> - if (host_size < host_full_size - qemu_host_page_size) {
> - goto naive;
> - }
> - return (unsigned long)-1;
> - }
> - munmap((void *)real_start, real_size);
> - real_start = ROUND_UP(real_start, align);
> + } else {
> + if ((guest_hiaddr - guest_base) > ~(uintptr_t)0) {
> + error_report("%s: requires more virtual address space "
> + "than the host can provide (0x%" PRIx64 ")",
> + image_name, (uint64_t)guest_hiaddr - guest_base);
> + exit(EXIT_FAILURE);
> }
Hi Alex,
this causes an error with newer versions of Clang:
linux-user/elfload.c:2076:41: error: result of comparison 'unsigned
long' > 18446744073709551615 is always false
[-Werror,-Wtautological-type-limit-compare]
4685 if ((guest_hiaddr - guest_base) > ~(uintptr_t)0) {
4686 ~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~
4687 1 error generated.
Any ideas how to fix this?
Thomas
- [PATCH v1 00/10] testing and tcg tweaks, Alex Bennée, 2020/05/13
- [PATCH v1 02/10] travis.yml: Improve the --disable-tcg test on s390x, Alex Bennée, 2020/05/13
- [PATCH v1 01/10] tests/guest-debug: catch hanging guests, Alex Bennée, 2020/05/13
- [PATCH v1 03/10] tests/docker: Kludge <linux/swab.h> breakage by pinning linux-libc-dev, Alex Bennée, 2020/05/13
- [PATCH v1 05/10] exec/cpu-all: Use bool for have_guest_base, Alex Bennée, 2020/05/13
- [PATCH v1 07/10] accel/tcg: don't disable exec_tb trace events, Alex Bennée, 2020/05/13
- [PATCH v1 04/10] linux-user: completely re-write init_guest_space, Alex Bennée, 2020/05/13
- Re: [PATCH v1 04/10] linux-user: completely re-write init_guest_space,
Thomas Huth <=
[PATCH v1 06/10] accel/tcg: Relax va restrictions on 64-bit guests, Alex Bennée, 2020/05/13
[PATCH v1 09/10] disas: add optional note support to cap_disas, Alex Bennée, 2020/05/13
[PATCH v1 10/10] translate-all: include guest address in out_asm output, Alex Bennée, 2020/05/13
[PATCH v1 08/10] disas: include an optional note for the start of disassembly, Alex Bennée, 2020/05/13