qemu/linux-user/mmap.c
<<
>>
Prefs
   1/*
   2 *  mmap support for qemu
   3 *
   4 *  Copyright (c) 2003 Fabrice Bellard
   5 *
   6 *  This program is free software; you can redistribute it and/or modify
   7 *  it under the terms of the GNU General Public License as published by
   8 *  the Free Software Foundation; either version 2 of the License, or
   9 *  (at your option) any later version.
  10 *
  11 *  This program is distributed in the hope that it will be useful,
  12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 *  GNU General Public License for more details.
  15 *
  16 *  You should have received a copy of the GNU General Public License
  17 *  along with this program; if not, see <http://www.gnu.org/licenses/>.
  18 */
  19#include "qemu/osdep.h"
  20
  21#include "qemu.h"
  22#include "qemu-common.h"
  23
  24//#define DEBUG_MMAP
  25
  26static pthread_mutex_t mmap_mutex = PTHREAD_MUTEX_INITIALIZER;
  27static __thread int mmap_lock_count;
  28
  29void mmap_lock(void)
  30{
  31    if (mmap_lock_count++ == 0) {
  32        pthread_mutex_lock(&mmap_mutex);
  33    }
  34}
  35
  36void mmap_unlock(void)
  37{
  38    if (--mmap_lock_count == 0) {
  39        pthread_mutex_unlock(&mmap_mutex);
  40    }
  41}
  42
  43bool have_mmap_lock(void)
  44{
  45    return mmap_lock_count > 0 ? true : false;
  46}
  47
  48/* Grab lock to make sure things are in a consistent state after fork().  */
  49void mmap_fork_start(void)
  50{
  51    if (mmap_lock_count)
  52        abort();
  53    pthread_mutex_lock(&mmap_mutex);
  54}
  55
  56void mmap_fork_end(int child)
  57{
  58    if (child)
  59        pthread_mutex_init(&mmap_mutex, NULL);
  60    else
  61        pthread_mutex_unlock(&mmap_mutex);
  62}
  63
  64/* NOTE: all the constants are the HOST ones, but addresses are target. */
  65int target_mprotect(abi_ulong start, abi_ulong len, int prot)
  66{
  67    abi_ulong end, host_start, host_end, addr;
  68    int prot1, ret;
  69
  70#ifdef DEBUG_MMAP
  71    printf("mprotect: start=0x" TARGET_ABI_FMT_lx
  72           "len=0x" TARGET_ABI_FMT_lx " prot=%c%c%c\n", start, len,
  73           prot & PROT_READ ? 'r' : '-',
  74           prot & PROT_WRITE ? 'w' : '-',
  75           prot & PROT_EXEC ? 'x' : '-');
  76#endif
  77
  78    if ((start & ~TARGET_PAGE_MASK) != 0)
  79        return -TARGET_EINVAL;
  80    len = TARGET_PAGE_ALIGN(len);
  81    end = start + len;
  82    if (!guest_range_valid(start, len)) {
  83        return -TARGET_ENOMEM;
  84    }
  85    prot &= PROT_READ | PROT_WRITE | PROT_EXEC;
  86    if (len == 0)
  87        return 0;
  88
  89    mmap_lock();
  90    host_start = start & qemu_host_page_mask;
  91    host_end = HOST_PAGE_ALIGN(end);
  92    if (start > host_start) {
  93        /* handle host page containing start */
  94        prot1 = prot;
  95        for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
  96            prot1 |= page_get_flags(addr);
  97        }
  98        if (host_end == host_start + qemu_host_page_size) {
  99            for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
 100                prot1 |= page_get_flags(addr);
 101            }
 102            end = host_end;
 103        }
 104        ret = mprotect(g2h(host_start), qemu_host_page_size, prot1 & PAGE_BITS);
 105        if (ret != 0)
 106            goto error;
 107        host_start += qemu_host_page_size;
 108    }
 109    if (end < host_end) {
 110        prot1 = prot;
 111        for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
 112            prot1 |= page_get_flags(addr);
 113        }
 114        ret = mprotect(g2h(host_end - qemu_host_page_size), qemu_host_page_size,
 115                       prot1 & PAGE_BITS);
 116        if (ret != 0)
 117            goto error;
 118        host_end -= qemu_host_page_size;
 119    }
 120
 121    /* handle the pages in the middle */
 122    if (host_start < host_end) {
 123        ret = mprotect(g2h(host_start), host_end - host_start, prot);
 124        if (ret != 0)
 125            goto error;
 126    }
 127    page_set_flags(start, start + len, prot | PAGE_VALID);
 128    mmap_unlock();
 129    return 0;
 130error:
 131    mmap_unlock();
 132    return ret;
 133}
 134
 135/* map an incomplete host page */
 136static int mmap_frag(abi_ulong real_start,
 137                     abi_ulong start, abi_ulong end,
 138                     int prot, int flags, int fd, abi_ulong offset)
 139{
 140    abi_ulong real_end, addr;
 141    void *host_start;
 142    int prot1, prot_new;
 143
 144    real_end = real_start + qemu_host_page_size;
 145    host_start = g2h(real_start);
 146
 147    /* get the protection of the target pages outside the mapping */
 148    prot1 = 0;
 149    for(addr = real_start; addr < real_end; addr++) {
 150        if (addr < start || addr >= end)
 151            prot1 |= page_get_flags(addr);
 152    }
 153
 154    if (prot1 == 0) {
 155        /* no page was there, so we allocate one */
 156        void *p = mmap(host_start, qemu_host_page_size, prot,
 157                       flags | MAP_ANONYMOUS, -1, 0);
 158        if (p == MAP_FAILED)
 159            return -1;
 160        prot1 = prot;
 161    }
 162    prot1 &= PAGE_BITS;
 163
 164    prot_new = prot | prot1;
 165    if (!(flags & MAP_ANONYMOUS)) {
 166        /* msync() won't work here, so we return an error if write is
 167           possible while it is a shared mapping */
 168        if ((flags & MAP_TYPE) == MAP_SHARED &&
 169            (prot & PROT_WRITE))
 170            return -1;
 171
 172        /* adjust protection to be able to read */
 173        if (!(prot1 & PROT_WRITE))
 174            mprotect(host_start, qemu_host_page_size, prot1 | PROT_WRITE);
 175
 176        /* read the corresponding file data */
 177        if (pread(fd, g2h(start), end - start, offset) == -1)
 178            return -1;
 179
 180        /* put final protection */
 181        if (prot_new != (prot1 | PROT_WRITE))
 182            mprotect(host_start, qemu_host_page_size, prot_new);
 183    } else {
 184        if (prot_new != prot1) {
 185            mprotect(host_start, qemu_host_page_size, prot_new);
 186        }
 187        if (prot_new & PROT_WRITE) {
 188            memset(g2h(start), 0, end - start);
 189        }
 190    }
 191    return 0;
 192}
 193
 194#if HOST_LONG_BITS == 64 && TARGET_ABI_BITS == 64
 195# define TASK_UNMAPPED_BASE  (1ul << 38)
 196#else
 197# define TASK_UNMAPPED_BASE  0x40000000
 198#endif
 199abi_ulong mmap_next_start = TASK_UNMAPPED_BASE;
 200
 201unsigned long last_brk;
 202
 203/* Subroutine of mmap_find_vma, used when we have pre-allocated a chunk
 204   of guest address space.  */
 205static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size)
 206{
 207    abi_ulong addr;
 208    abi_ulong end_addr;
 209    int prot;
 210    int looped = 0;
 211
 212    if (size > reserved_va) {
 213        return (abi_ulong)-1;
 214    }
 215
 216    size = HOST_PAGE_ALIGN(size);
 217    end_addr = start + size;
 218    if (end_addr > reserved_va) {
 219        end_addr = reserved_va;
 220    }
 221    addr = end_addr - qemu_host_page_size;
 222
 223    while (1) {
 224        if (addr > end_addr) {
 225            if (looped) {
 226                return (abi_ulong)-1;
 227            }
 228            end_addr = reserved_va;
 229            addr = end_addr - qemu_host_page_size;
 230            looped = 1;
 231            continue;
 232        }
 233        prot = page_get_flags(addr);
 234        if (prot) {
 235            end_addr = addr;
 236        }
 237        if (addr && addr + size == end_addr) {
 238            break;
 239        }
 240        addr -= qemu_host_page_size;
 241    }
 242
 243    if (start == mmap_next_start) {
 244        mmap_next_start = addr;
 245    }
 246
 247    return addr;
 248}
 249
 250/*
 251 * Find and reserve a free memory area of size 'size'. The search
 252 * starts at 'start'.
 253 * It must be called with mmap_lock() held.
 254 * Return -1 if error.
 255 */
 256abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
 257{
 258    void *ptr, *prev;
 259    abi_ulong addr;
 260    int wrapped, repeat;
 261
 262    /* If 'start' == 0, then a default start address is used. */
 263    if (start == 0) {
 264        start = mmap_next_start;
 265    } else {
 266        start &= qemu_host_page_mask;
 267    }
 268
 269    size = HOST_PAGE_ALIGN(size);
 270
 271    if (reserved_va) {
 272        return mmap_find_vma_reserved(start, size);
 273    }
 274
 275    addr = start;
 276    wrapped = repeat = 0;
 277    prev = 0;
 278
 279    for (;; prev = ptr) {
 280        /*
 281         * Reserve needed memory area to avoid a race.
 282         * It should be discarded using:
 283         *  - mmap() with MAP_FIXED flag
 284         *  - mremap() with MREMAP_FIXED flag
 285         *  - shmat() with SHM_REMAP flag
 286         */
 287        ptr = mmap(g2h(addr), size, PROT_NONE,
 288                   MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE, -1, 0);
 289
 290        /* ENOMEM, if host address space has no memory */
 291        if (ptr == MAP_FAILED) {
 292            return (abi_ulong)-1;
 293        }
 294
 295        /* Count the number of sequential returns of the same address.
 296           This is used to modify the search algorithm below.  */
 297        repeat = (ptr == prev ? repeat + 1 : 0);
 298
 299        if (h2g_valid(ptr + size - 1)) {
 300            addr = h2g(ptr);
 301
 302            if ((addr & ~TARGET_PAGE_MASK) == 0) {
 303                /* Success.  */
 304                if (start == mmap_next_start && addr >= TASK_UNMAPPED_BASE) {
 305                    mmap_next_start = addr + size;
 306                }
 307                return addr;
 308            }
 309
 310            /* The address is not properly aligned for the target.  */
 311            switch (repeat) {
 312            case 0:
 313                /* Assume the result that the kernel gave us is the
 314                   first with enough free space, so start again at the
 315                   next higher target page.  */
 316                addr = TARGET_PAGE_ALIGN(addr);
 317                break;
 318            case 1:
 319                /* Sometimes the kernel decides to perform the allocation
 320                   at the top end of memory instead.  */
 321                addr &= TARGET_PAGE_MASK;
 322                break;
 323            case 2:
 324                /* Start over at low memory.  */
 325                addr = 0;
 326                break;
 327            default:
 328                /* Fail.  This unaligned block must the last.  */
 329                addr = -1;
 330                break;
 331            }
 332        } else {
 333            /* Since the result the kernel gave didn't fit, start
 334               again at low memory.  If any repetition, fail.  */
 335            addr = (repeat ? -1 : 0);
 336        }
 337
 338        /* Unmap and try again.  */
 339        munmap(ptr, size);
 340
 341        /* ENOMEM if we checked the whole of the target address space.  */
 342        if (addr == (abi_ulong)-1) {
 343            return (abi_ulong)-1;
 344        } else if (addr == 0) {
 345            if (wrapped) {
 346                return (abi_ulong)-1;
 347            }
 348            wrapped = 1;
 349            /* Don't actually use 0 when wrapping, instead indicate
 350               that we'd truly like an allocation in low memory.  */
 351            addr = (mmap_min_addr > TARGET_PAGE_SIZE
 352                     ? TARGET_PAGE_ALIGN(mmap_min_addr)
 353                     : TARGET_PAGE_SIZE);
 354        } else if (wrapped && addr >= start) {
 355            return (abi_ulong)-1;
 356        }
 357    }
 358}
 359
 360/* NOTE: all the constants are the HOST ones */
 361abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
 362                     int flags, int fd, abi_ulong offset)
 363{
 364    abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len;
 365
 366    mmap_lock();
 367#ifdef DEBUG_MMAP
 368    {
 369        printf("mmap: start=0x" TARGET_ABI_FMT_lx
 370               " len=0x" TARGET_ABI_FMT_lx " prot=%c%c%c flags=",
 371               start, len,
 372               prot & PROT_READ ? 'r' : '-',
 373               prot & PROT_WRITE ? 'w' : '-',
 374               prot & PROT_EXEC ? 'x' : '-');
 375        if (flags & MAP_FIXED)
 376            printf("MAP_FIXED ");
 377        if (flags & MAP_ANONYMOUS)
 378            printf("MAP_ANON ");
 379        switch(flags & MAP_TYPE) {
 380        case MAP_PRIVATE:
 381            printf("MAP_PRIVATE ");
 382            break;
 383        case MAP_SHARED:
 384            printf("MAP_SHARED ");
 385            break;
 386        default:
 387            printf("[MAP_TYPE=0x%x] ", flags & MAP_TYPE);
 388            break;
 389        }
 390        printf("fd=%d offset=" TARGET_ABI_FMT_lx "\n", fd, offset);
 391    }
 392#endif
 393
 394    if (!len) {
 395        errno = EINVAL;
 396        goto fail;
 397    }
 398
 399    /* Also check for overflows... */
 400    len = TARGET_PAGE_ALIGN(len);
 401    if (!len) {
 402        errno = ENOMEM;
 403        goto fail;
 404    }
 405
 406    if (offset & ~TARGET_PAGE_MASK) {
 407        errno = EINVAL;
 408        goto fail;
 409    }
 410
 411    real_start = start & qemu_host_page_mask;
 412    host_offset = offset & qemu_host_page_mask;
 413
 414    /* If the user is asking for the kernel to find a location, do that
 415       before we truncate the length for mapping files below.  */
 416    if (!(flags & MAP_FIXED)) {
 417        host_len = len + offset - host_offset;
 418        host_len = HOST_PAGE_ALIGN(host_len);
 419        start = mmap_find_vma(real_start, host_len);
 420        if (start == (abi_ulong)-1) {
 421            errno = ENOMEM;
 422            goto fail;
 423        }
 424    }
 425
 426    /* When mapping files into a memory area larger than the file, accesses
 427       to pages beyond the file size will cause a SIGBUS. 
 428
 429       For example, if mmaping a file of 100 bytes on a host with 4K pages
 430       emulating a target with 8K pages, the target expects to be able to
 431       access the first 8K. But the host will trap us on any access beyond
 432       4K.  
 433
 434       When emulating a target with a larger page-size than the hosts, we
 435       may need to truncate file maps at EOF and add extra anonymous pages
 436       up to the targets page boundary.  */
 437
 438    if ((qemu_real_host_page_size < qemu_host_page_size) &&
 439        !(flags & MAP_ANONYMOUS)) {
 440        struct stat sb;
 441
 442       if (fstat (fd, &sb) == -1)
 443           goto fail;
 444
 445       /* Are we trying to create a map beyond EOF?.  */
 446       if (offset + len > sb.st_size) {
 447           /* If so, truncate the file map at eof aligned with 
 448              the hosts real pagesize. Additional anonymous maps
 449              will be created beyond EOF.  */
 450           len = REAL_HOST_PAGE_ALIGN(sb.st_size - offset);
 451       }
 452    }
 453
 454    if (!(flags & MAP_FIXED)) {
 455        unsigned long host_start;
 456        void *p;
 457
 458        host_len = len + offset - host_offset;
 459        host_len = HOST_PAGE_ALIGN(host_len);
 460
 461        /* Note: we prefer to control the mapping address. It is
 462           especially important if qemu_host_page_size >
 463           qemu_real_host_page_size */
 464        p = mmap(g2h(start), host_len, prot,
 465                 flags | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
 466        if (p == MAP_FAILED)
 467            goto fail;
 468        /* update start so that it points to the file position at 'offset' */
 469        host_start = (unsigned long)p;
 470        if (!(flags & MAP_ANONYMOUS)) {
 471            p = mmap(g2h(start), len, prot,
 472                     flags | MAP_FIXED, fd, host_offset);
 473            if (p == MAP_FAILED) {
 474                munmap(g2h(start), host_len);
 475                goto fail;
 476            }
 477            host_start += offset - host_offset;
 478        }
 479        start = h2g(host_start);
 480    } else {
 481        if (start & ~TARGET_PAGE_MASK) {
 482            errno = EINVAL;
 483            goto fail;
 484        }
 485        end = start + len;
 486        real_end = HOST_PAGE_ALIGN(end);
 487
 488        /*
 489         * Test if requested memory area fits target address space
 490         * It can fail only on 64-bit host with 32-bit target.
 491         * On any other target/host host mmap() handles this error correctly.
 492         */
 493        if (!guest_range_valid(start, len)) {
 494            errno = ENOMEM;
 495            goto fail;
 496        }
 497
 498        /* worst case: we cannot map the file because the offset is not
 499           aligned, so we read it */
 500        if (!(flags & MAP_ANONYMOUS) &&
 501            (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) {
 502            /* msync() won't work here, so we return an error if write is
 503               possible while it is a shared mapping */
 504            if ((flags & MAP_TYPE) == MAP_SHARED &&
 505                (prot & PROT_WRITE)) {
 506                errno = EINVAL;
 507                goto fail;
 508            }
 509            retaddr = target_mmap(start, len, prot | PROT_WRITE,
 510                                  MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
 511                                  -1, 0);
 512            if (retaddr == -1)
 513                goto fail;
 514            if (pread(fd, g2h(start), len, offset) == -1)
 515                goto fail;
 516            if (!(prot & PROT_WRITE)) {
 517                ret = target_mprotect(start, len, prot);
 518                assert(ret == 0);
 519            }
 520            goto the_end;
 521        }
 522        
 523        /* handle the start of the mapping */
 524        if (start > real_start) {
 525            if (real_end == real_start + qemu_host_page_size) {
 526                /* one single host page */
 527                ret = mmap_frag(real_start, start, end,
 528                                prot, flags, fd, offset);
 529                if (ret == -1)
 530                    goto fail;
 531                goto the_end1;
 532            }
 533            ret = mmap_frag(real_start, start, real_start + qemu_host_page_size,
 534                            prot, flags, fd, offset);
 535            if (ret == -1)
 536                goto fail;
 537            real_start += qemu_host_page_size;
 538        }
 539        /* handle the end of the mapping */
 540        if (end < real_end) {
 541            ret = mmap_frag(real_end - qemu_host_page_size,
 542                            real_end - qemu_host_page_size, end,
 543                            prot, flags, fd,
 544                            offset + real_end - qemu_host_page_size - start);
 545            if (ret == -1)
 546                goto fail;
 547            real_end -= qemu_host_page_size;
 548        }
 549
 550        /* map the middle (easier) */
 551        if (real_start < real_end) {
 552            void *p;
 553            unsigned long offset1;
 554            if (flags & MAP_ANONYMOUS)
 555                offset1 = 0;
 556            else
 557                offset1 = offset + real_start - start;
 558            p = mmap(g2h(real_start), real_end - real_start,
 559                     prot, flags, fd, offset1);
 560            if (p == MAP_FAILED)
 561                goto fail;
 562        }
 563    }
 564 the_end1:
 565    page_set_flags(start, start + len, prot | PAGE_VALID);
 566 the_end:
 567#ifdef DEBUG_MMAP
 568    printf("ret=0x" TARGET_ABI_FMT_lx "\n", start);
 569    page_dump(stdout);
 570    printf("\n");
 571#endif
 572    tb_invalidate_phys_range(start, start + len);
 573    mmap_unlock();
 574    return start;
 575fail:
 576    mmap_unlock();
 577    return -1;
 578}
 579
 580static void mmap_reserve(abi_ulong start, abi_ulong size)
 581{
 582    abi_ulong real_start;
 583    abi_ulong real_end;
 584    abi_ulong addr;
 585    abi_ulong end;
 586    int prot;
 587
 588    real_start = start & qemu_host_page_mask;
 589    real_end = HOST_PAGE_ALIGN(start + size);
 590    end = start + size;
 591    if (start > real_start) {
 592        /* handle host page containing start */
 593        prot = 0;
 594        for (addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
 595            prot |= page_get_flags(addr);
 596        }
 597        if (real_end == real_start + qemu_host_page_size) {
 598            for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
 599                prot |= page_get_flags(addr);
 600            }
 601            end = real_end;
 602        }
 603        if (prot != 0)
 604            real_start += qemu_host_page_size;
 605    }
 606    if (end < real_end) {
 607        prot = 0;
 608        for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
 609            prot |= page_get_flags(addr);
 610        }
 611        if (prot != 0)
 612            real_end -= qemu_host_page_size;
 613    }
 614    if (real_start != real_end) {
 615        mmap(g2h(real_start), real_end - real_start, PROT_NONE,
 616                 MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE,
 617                 -1, 0);
 618    }
 619}
 620
 621int target_munmap(abi_ulong start, abi_ulong len)
 622{
 623    abi_ulong end, real_start, real_end, addr;
 624    int prot, ret;
 625
 626#ifdef DEBUG_MMAP
 627    printf("munmap: start=0x" TARGET_ABI_FMT_lx " len=0x"
 628           TARGET_ABI_FMT_lx "\n",
 629           start, len);
 630#endif
 631    if (start & ~TARGET_PAGE_MASK)
 632        return -TARGET_EINVAL;
 633    len = TARGET_PAGE_ALIGN(len);
 634    if (len == 0 || !guest_range_valid(start, len)) {
 635        return -TARGET_EINVAL;
 636    }
 637
 638    mmap_lock();
 639    end = start + len;
 640    real_start = start & qemu_host_page_mask;
 641    real_end = HOST_PAGE_ALIGN(end);
 642
 643    if (start > real_start) {
 644        /* handle host page containing start */
 645        prot = 0;
 646        for(addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
 647            prot |= page_get_flags(addr);
 648        }
 649        if (real_end == real_start + qemu_host_page_size) {
 650            for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
 651                prot |= page_get_flags(addr);
 652            }
 653            end = real_end;
 654        }
 655        if (prot != 0)
 656            real_start += qemu_host_page_size;
 657    }
 658    if (end < real_end) {
 659        prot = 0;
 660        for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
 661            prot |= page_get_flags(addr);
 662        }
 663        if (prot != 0)
 664            real_end -= qemu_host_page_size;
 665    }
 666
 667    ret = 0;
 668    /* unmap what we can */
 669    if (real_start < real_end) {
 670        if (reserved_va) {
 671            mmap_reserve(real_start, real_end - real_start);
 672        } else {
 673            ret = munmap(g2h(real_start), real_end - real_start);
 674        }
 675    }
 676
 677    if (ret == 0) {
 678        page_set_flags(start, start + len, 0);
 679        tb_invalidate_phys_range(start, start + len);
 680    }
 681    mmap_unlock();
 682    return ret;
 683}
 684
 685abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
 686                       abi_ulong new_size, unsigned long flags,
 687                       abi_ulong new_addr)
 688{
 689    int prot;
 690    void *host_addr;
 691
 692    if (!guest_range_valid(old_addr, old_size) ||
 693        ((flags & MREMAP_FIXED) &&
 694         !guest_range_valid(new_addr, new_size))) {
 695        errno = ENOMEM;
 696        return -1;
 697    }
 698
 699    mmap_lock();
 700
 701    if (flags & MREMAP_FIXED) {
 702        host_addr = mremap(g2h(old_addr), old_size, new_size,
 703                           flags, g2h(new_addr));
 704
 705        if (reserved_va && host_addr != MAP_FAILED) {
 706            /* If new and old addresses overlap then the above mremap will
 707               already have failed with EINVAL.  */
 708            mmap_reserve(old_addr, old_size);
 709        }
 710    } else if (flags & MREMAP_MAYMOVE) {
 711        abi_ulong mmap_start;
 712
 713        mmap_start = mmap_find_vma(0, new_size);
 714
 715        if (mmap_start == -1) {
 716            errno = ENOMEM;
 717            host_addr = MAP_FAILED;
 718        } else {
 719            host_addr = mremap(g2h(old_addr), old_size, new_size,
 720                               flags | MREMAP_FIXED, g2h(mmap_start));
 721            if (reserved_va) {
 722                mmap_reserve(old_addr, old_size);
 723            }
 724        }
 725    } else {
 726        int prot = 0;
 727        if (reserved_va && old_size < new_size) {
 728            abi_ulong addr;
 729            for (addr = old_addr + old_size;
 730                 addr < old_addr + new_size;
 731                 addr++) {
 732                prot |= page_get_flags(addr);
 733            }
 734        }
 735        if (prot == 0) {
 736            host_addr = mremap(g2h(old_addr), old_size, new_size, flags);
 737            if (host_addr != MAP_FAILED && reserved_va && old_size > new_size) {
 738                mmap_reserve(old_addr + old_size, new_size - old_size);
 739            }
 740        } else {
 741            errno = ENOMEM;
 742            host_addr = MAP_FAILED;
 743        }
 744        /* Check if address fits target address space */
 745        if ((unsigned long)host_addr + new_size > (abi_ulong)-1) {
 746            /* Revert mremap() changes */
 747            host_addr = mremap(g2h(old_addr), new_size, old_size, flags);
 748            errno = ENOMEM;
 749            host_addr = MAP_FAILED;
 750        }
 751    }
 752
 753    if (host_addr == MAP_FAILED) {
 754        new_addr = -1;
 755    } else {
 756        new_addr = h2g(host_addr);
 757        prot = page_get_flags(old_addr);
 758        page_set_flags(old_addr, old_addr + old_size, 0);
 759        page_set_flags(new_addr, new_addr + new_size, prot | PAGE_VALID);
 760    }
 761    tb_invalidate_phys_range(new_addr, new_addr + new_size);
 762    mmap_unlock();
 763    return new_addr;
 764}
 765