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#include "trace.h"
  21#include "exec/log.h"
  22#include "qemu.h"
  23#include "user-internals.h"
  24#include "user-mmap.h"
  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/*
  65 * Validate target prot bitmask.
  66 * Return the prot bitmask for the host in *HOST_PROT.
  67 * Return 0 if the target prot bitmask is invalid, otherwise
  68 * the internal qemu page_flags (which will include PAGE_VALID).
  69 */
  70static int validate_prot_to_pageflags(int *host_prot, int prot)
  71{
  72    int valid = PROT_READ | PROT_WRITE | PROT_EXEC | TARGET_PROT_SEM;
  73    int page_flags = (prot & PAGE_BITS) | PAGE_VALID;
  74
  75    /*
  76     * For the host, we need not pass anything except read/write/exec.
  77     * While PROT_SEM is allowed by all hosts, it is also ignored, so
  78     * don't bother transforming guest bit to host bit.  Any other
  79     * target-specific prot bits will not be understood by the host
  80     * and will need to be encoded into page_flags for qemu emulation.
  81     *
  82     * Pages that are executable by the guest will never be executed
  83     * by the host, but the host will need to be able to read them.
  84     */
  85    *host_prot = (prot & (PROT_READ | PROT_WRITE))
  86               | (prot & PROT_EXEC ? PROT_READ : 0);
  87
  88#ifdef TARGET_AARCH64
  89    {
  90        ARMCPU *cpu = ARM_CPU(thread_cpu);
  91
  92        /*
  93         * The PROT_BTI bit is only accepted if the cpu supports the feature.
  94         * Since this is the unusual case, don't bother checking unless
  95         * the bit has been requested.  If set and valid, record the bit
  96         * within QEMU's page_flags.
  97         */
  98        if ((prot & TARGET_PROT_BTI) && cpu_isar_feature(aa64_bti, cpu)) {
  99            valid |= TARGET_PROT_BTI;
 100            page_flags |= PAGE_BTI;
 101        }
 102        /* Similarly for the PROT_MTE bit. */
 103        if ((prot & TARGET_PROT_MTE) && cpu_isar_feature(aa64_mte, cpu)) {
 104            valid |= TARGET_PROT_MTE;
 105            page_flags |= PAGE_MTE;
 106        }
 107    }
 108#endif
 109
 110    return prot & ~valid ? 0 : page_flags;
 111}
 112
 113/* NOTE: all the constants are the HOST ones, but addresses are target. */
 114int target_mprotect(abi_ulong start, abi_ulong len, int target_prot)
 115{
 116    abi_ulong end, host_start, host_end, addr;
 117    int prot1, ret, page_flags, host_prot;
 118
 119    trace_target_mprotect(start, len, target_prot);
 120
 121    if ((start & ~TARGET_PAGE_MASK) != 0) {
 122        return -TARGET_EINVAL;
 123    }
 124    page_flags = validate_prot_to_pageflags(&host_prot, target_prot);
 125    if (!page_flags) {
 126        return -TARGET_EINVAL;
 127    }
 128    len = TARGET_PAGE_ALIGN(len);
 129    end = start + len;
 130    if (!guest_range_valid_untagged(start, len)) {
 131        return -TARGET_ENOMEM;
 132    }
 133    if (len == 0) {
 134        return 0;
 135    }
 136
 137    mmap_lock();
 138    host_start = start & qemu_host_page_mask;
 139    host_end = HOST_PAGE_ALIGN(end);
 140    if (start > host_start) {
 141        /* handle host page containing start */
 142        prot1 = host_prot;
 143        for (addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
 144            prot1 |= page_get_flags(addr);
 145        }
 146        if (host_end == host_start + qemu_host_page_size) {
 147            for (addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
 148                prot1 |= page_get_flags(addr);
 149            }
 150            end = host_end;
 151        }
 152        ret = mprotect(g2h_untagged(host_start), qemu_host_page_size,
 153                       prot1 & PAGE_BITS);
 154        if (ret != 0) {
 155            goto error;
 156        }
 157        host_start += qemu_host_page_size;
 158    }
 159    if (end < host_end) {
 160        prot1 = host_prot;
 161        for (addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
 162            prot1 |= page_get_flags(addr);
 163        }
 164        ret = mprotect(g2h_untagged(host_end - qemu_host_page_size),
 165                       qemu_host_page_size, prot1 & PAGE_BITS);
 166        if (ret != 0) {
 167            goto error;
 168        }
 169        host_end -= qemu_host_page_size;
 170    }
 171
 172    /* handle the pages in the middle */
 173    if (host_start < host_end) {
 174        ret = mprotect(g2h_untagged(host_start),
 175                       host_end - host_start, host_prot);
 176        if (ret != 0) {
 177            goto error;
 178        }
 179    }
 180    page_set_flags(start, start + len, page_flags);
 181    mmap_unlock();
 182    return 0;
 183error:
 184    mmap_unlock();
 185    return ret;
 186}
 187
 188/* map an incomplete host page */
 189static int mmap_frag(abi_ulong real_start,
 190                     abi_ulong start, abi_ulong end,
 191                     int prot, int flags, int fd, abi_ulong offset)
 192{
 193    abi_ulong real_end, addr;
 194    void *host_start;
 195    int prot1, prot_new;
 196
 197    real_end = real_start + qemu_host_page_size;
 198    host_start = g2h_untagged(real_start);
 199
 200    /* get the protection of the target pages outside the mapping */
 201    prot1 = 0;
 202    for(addr = real_start; addr < real_end; addr++) {
 203        if (addr < start || addr >= end)
 204            prot1 |= page_get_flags(addr);
 205    }
 206
 207    if (prot1 == 0) {
 208        /* no page was there, so we allocate one */
 209        void *p = mmap(host_start, qemu_host_page_size, prot,
 210                       flags | MAP_ANONYMOUS, -1, 0);
 211        if (p == MAP_FAILED)
 212            return -1;
 213        prot1 = prot;
 214    }
 215    prot1 &= PAGE_BITS;
 216
 217    prot_new = prot | prot1;
 218    if (!(flags & MAP_ANONYMOUS)) {
 219        /* msync() won't work here, so we return an error if write is
 220           possible while it is a shared mapping */
 221        if ((flags & MAP_TYPE) == MAP_SHARED &&
 222            (prot & PROT_WRITE))
 223            return -1;
 224
 225        /* adjust protection to be able to read */
 226        if (!(prot1 & PROT_WRITE))
 227            mprotect(host_start, qemu_host_page_size, prot1 | PROT_WRITE);
 228
 229        /* read the corresponding file data */
 230        if (pread(fd, g2h_untagged(start), end - start, offset) == -1)
 231            return -1;
 232
 233        /* put final protection */
 234        if (prot_new != (prot1 | PROT_WRITE))
 235            mprotect(host_start, qemu_host_page_size, prot_new);
 236    } else {
 237        if (prot_new != prot1) {
 238            mprotect(host_start, qemu_host_page_size, prot_new);
 239        }
 240        if (prot_new & PROT_WRITE) {
 241            memset(g2h_untagged(start), 0, end - start);
 242        }
 243    }
 244    return 0;
 245}
 246
 247#if HOST_LONG_BITS == 64 && TARGET_ABI_BITS == 64
 248#ifdef TARGET_AARCH64
 249# define TASK_UNMAPPED_BASE  0x5500000000
 250#else
 251# define TASK_UNMAPPED_BASE  (1ul << 38)
 252#endif
 253#else
 254# define TASK_UNMAPPED_BASE  0x40000000
 255#endif
 256abi_ulong mmap_next_start = TASK_UNMAPPED_BASE;
 257
 258unsigned long last_brk;
 259
 260/* Subroutine of mmap_find_vma, used when we have pre-allocated a chunk
 261   of guest address space.  */
 262static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size,
 263                                        abi_ulong align)
 264{
 265    abi_ulong addr, end_addr, incr = qemu_host_page_size;
 266    int prot;
 267    bool looped = false;
 268
 269    if (size > reserved_va) {
 270        return (abi_ulong)-1;
 271    }
 272
 273    /* Note that start and size have already been aligned by mmap_find_vma. */
 274
 275    end_addr = start + size;
 276    if (start > reserved_va - size) {
 277        /* Start at the top of the address space.  */
 278        end_addr = ((reserved_va - size) & -align) + size;
 279        looped = true;
 280    }
 281
 282    /* Search downward from END_ADDR, checking to see if a page is in use.  */
 283    addr = end_addr;
 284    while (1) {
 285        addr -= incr;
 286        if (addr > end_addr) {
 287            if (looped) {
 288                /* Failure.  The entire address space has been searched.  */
 289                return (abi_ulong)-1;
 290            }
 291            /* Re-start at the top of the address space.  */
 292            addr = end_addr = ((reserved_va - size) & -align) + size;
 293            looped = true;
 294        } else {
 295            prot = page_get_flags(addr);
 296            if (prot) {
 297                /* Page in use.  Restart below this page.  */
 298                addr = end_addr = ((addr - size) & -align) + size;
 299            } else if (addr && addr + size == end_addr) {
 300                /* Success!  All pages between ADDR and END_ADDR are free.  */
 301                if (start == mmap_next_start) {
 302                    mmap_next_start = addr;
 303                }
 304                return addr;
 305            }
 306        }
 307    }
 308}
 309
 310/*
 311 * Find and reserve a free memory area of size 'size'. The search
 312 * starts at 'start'.
 313 * It must be called with mmap_lock() held.
 314 * Return -1 if error.
 315 */
 316abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size, abi_ulong align)
 317{
 318    void *ptr, *prev;
 319    abi_ulong addr;
 320    int wrapped, repeat;
 321
 322    align = MAX(align, qemu_host_page_size);
 323
 324    /* If 'start' == 0, then a default start address is used. */
 325    if (start == 0) {
 326        start = mmap_next_start;
 327    } else {
 328        start &= qemu_host_page_mask;
 329    }
 330    start = ROUND_UP(start, align);
 331
 332    size = HOST_PAGE_ALIGN(size);
 333
 334    if (reserved_va) {
 335        return mmap_find_vma_reserved(start, size, align);
 336    }
 337
 338    addr = start;
 339    wrapped = repeat = 0;
 340    prev = 0;
 341
 342    for (;; prev = ptr) {
 343        /*
 344         * Reserve needed memory area to avoid a race.
 345         * It should be discarded using:
 346         *  - mmap() with MAP_FIXED flag
 347         *  - mremap() with MREMAP_FIXED flag
 348         *  - shmat() with SHM_REMAP flag
 349         */
 350        ptr = mmap(g2h_untagged(addr), size, PROT_NONE,
 351                   MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE, -1, 0);
 352
 353        /* ENOMEM, if host address space has no memory */
 354        if (ptr == MAP_FAILED) {
 355            return (abi_ulong)-1;
 356        }
 357
 358        /* Count the number of sequential returns of the same address.
 359           This is used to modify the search algorithm below.  */
 360        repeat = (ptr == prev ? repeat + 1 : 0);
 361
 362        if (h2g_valid(ptr + size - 1)) {
 363            addr = h2g(ptr);
 364
 365            if ((addr & (align - 1)) == 0) {
 366                /* Success.  */
 367                if (start == mmap_next_start && addr >= TASK_UNMAPPED_BASE) {
 368                    mmap_next_start = addr + size;
 369                }
 370                return addr;
 371            }
 372
 373            /* The address is not properly aligned for the target.  */
 374            switch (repeat) {
 375            case 0:
 376                /* Assume the result that the kernel gave us is the
 377                   first with enough free space, so start again at the
 378                   next higher target page.  */
 379                addr = ROUND_UP(addr, align);
 380                break;
 381            case 1:
 382                /* Sometimes the kernel decides to perform the allocation
 383                   at the top end of memory instead.  */
 384                addr &= -align;
 385                break;
 386            case 2:
 387                /* Start over at low memory.  */
 388                addr = 0;
 389                break;
 390            default:
 391                /* Fail.  This unaligned block must the last.  */
 392                addr = -1;
 393                break;
 394            }
 395        } else {
 396            /* Since the result the kernel gave didn't fit, start
 397               again at low memory.  If any repetition, fail.  */
 398            addr = (repeat ? -1 : 0);
 399        }
 400
 401        /* Unmap and try again.  */
 402        munmap(ptr, size);
 403
 404        /* ENOMEM if we checked the whole of the target address space.  */
 405        if (addr == (abi_ulong)-1) {
 406            return (abi_ulong)-1;
 407        } else if (addr == 0) {
 408            if (wrapped) {
 409                return (abi_ulong)-1;
 410            }
 411            wrapped = 1;
 412            /* Don't actually use 0 when wrapping, instead indicate
 413               that we'd truly like an allocation in low memory.  */
 414            addr = (mmap_min_addr > TARGET_PAGE_SIZE
 415                     ? TARGET_PAGE_ALIGN(mmap_min_addr)
 416                     : TARGET_PAGE_SIZE);
 417        } else if (wrapped && addr >= start) {
 418            return (abi_ulong)-1;
 419        }
 420    }
 421}
 422
 423/* NOTE: all the constants are the HOST ones */
 424abi_long target_mmap(abi_ulong start, abi_ulong len, int target_prot,
 425                     int flags, int fd, abi_ulong offset)
 426{
 427    abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len;
 428    int page_flags, host_prot;
 429
 430    mmap_lock();
 431    trace_target_mmap(start, len, target_prot, flags, fd, offset);
 432
 433    if (!len) {
 434        errno = EINVAL;
 435        goto fail;
 436    }
 437
 438    page_flags = validate_prot_to_pageflags(&host_prot, target_prot);
 439    if (!page_flags) {
 440        errno = EINVAL;
 441        goto fail;
 442    }
 443
 444    /* Also check for overflows... */
 445    len = TARGET_PAGE_ALIGN(len);
 446    if (!len) {
 447        errno = ENOMEM;
 448        goto fail;
 449    }
 450
 451    if (offset & ~TARGET_PAGE_MASK) {
 452        errno = EINVAL;
 453        goto fail;
 454    }
 455
 456    /*
 457     * If we're mapping shared memory, ensure we generate code for parallel
 458     * execution and flush old translations.  This will work up to the level
 459     * supported by the host -- anything that requires EXCP_ATOMIC will not
 460     * be atomic with respect to an external process.
 461     */
 462    if (flags & MAP_SHARED) {
 463        CPUState *cpu = thread_cpu;
 464        if (!(cpu->tcg_cflags & CF_PARALLEL)) {
 465            cpu->tcg_cflags |= CF_PARALLEL;
 466            tb_flush(cpu);
 467        }
 468    }
 469
 470    real_start = start & qemu_host_page_mask;
 471    host_offset = offset & qemu_host_page_mask;
 472
 473    /* If the user is asking for the kernel to find a location, do that
 474       before we truncate the length for mapping files below.  */
 475    if (!(flags & MAP_FIXED)) {
 476        host_len = len + offset - host_offset;
 477        host_len = HOST_PAGE_ALIGN(host_len);
 478        start = mmap_find_vma(real_start, host_len, TARGET_PAGE_SIZE);
 479        if (start == (abi_ulong)-1) {
 480            errno = ENOMEM;
 481            goto fail;
 482        }
 483    }
 484
 485    /* When mapping files into a memory area larger than the file, accesses
 486       to pages beyond the file size will cause a SIGBUS. 
 487
 488       For example, if mmaping a file of 100 bytes on a host with 4K pages
 489       emulating a target with 8K pages, the target expects to be able to
 490       access the first 8K. But the host will trap us on any access beyond
 491       4K.  
 492
 493       When emulating a target with a larger page-size than the hosts, we
 494       may need to truncate file maps at EOF and add extra anonymous pages
 495       up to the targets page boundary.  */
 496
 497    if ((qemu_real_host_page_size < qemu_host_page_size) &&
 498        !(flags & MAP_ANONYMOUS)) {
 499        struct stat sb;
 500
 501       if (fstat (fd, &sb) == -1)
 502           goto fail;
 503
 504       /* Are we trying to create a map beyond EOF?.  */
 505       if (offset + len > sb.st_size) {
 506           /* If so, truncate the file map at eof aligned with 
 507              the hosts real pagesize. Additional anonymous maps
 508              will be created beyond EOF.  */
 509           len = REAL_HOST_PAGE_ALIGN(sb.st_size - offset);
 510       }
 511    }
 512
 513    if (!(flags & MAP_FIXED)) {
 514        unsigned long host_start;
 515        void *p;
 516
 517        host_len = len + offset - host_offset;
 518        host_len = HOST_PAGE_ALIGN(host_len);
 519
 520        /* Note: we prefer to control the mapping address. It is
 521           especially important if qemu_host_page_size >
 522           qemu_real_host_page_size */
 523        p = mmap(g2h_untagged(start), host_len, host_prot,
 524                 flags | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
 525        if (p == MAP_FAILED) {
 526            goto fail;
 527        }
 528        /* update start so that it points to the file position at 'offset' */
 529        host_start = (unsigned long)p;
 530        if (!(flags & MAP_ANONYMOUS)) {
 531            p = mmap(g2h_untagged(start), len, host_prot,
 532                     flags | MAP_FIXED, fd, host_offset);
 533            if (p == MAP_FAILED) {
 534                munmap(g2h_untagged(start), host_len);
 535                goto fail;
 536            }
 537            host_start += offset - host_offset;
 538        }
 539        start = h2g(host_start);
 540    } else {
 541        if (start & ~TARGET_PAGE_MASK) {
 542            errno = EINVAL;
 543            goto fail;
 544        }
 545        end = start + len;
 546        real_end = HOST_PAGE_ALIGN(end);
 547
 548        /*
 549         * Test if requested memory area fits target address space
 550         * It can fail only on 64-bit host with 32-bit target.
 551         * On any other target/host host mmap() handles this error correctly.
 552         */
 553        if (end < start || !guest_range_valid_untagged(start, len)) {
 554            errno = ENOMEM;
 555            goto fail;
 556        }
 557
 558        /* worst case: we cannot map the file because the offset is not
 559           aligned, so we read it */
 560        if (!(flags & MAP_ANONYMOUS) &&
 561            (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) {
 562            /* msync() won't work here, so we return an error if write is
 563               possible while it is a shared mapping */
 564            if ((flags & MAP_TYPE) == MAP_SHARED &&
 565                (host_prot & PROT_WRITE)) {
 566                errno = EINVAL;
 567                goto fail;
 568            }
 569            retaddr = target_mmap(start, len, target_prot | PROT_WRITE,
 570                                  MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
 571                                  -1, 0);
 572            if (retaddr == -1)
 573                goto fail;
 574            if (pread(fd, g2h_untagged(start), len, offset) == -1)
 575                goto fail;
 576            if (!(host_prot & PROT_WRITE)) {
 577                ret = target_mprotect(start, len, target_prot);
 578                assert(ret == 0);
 579            }
 580            goto the_end;
 581        }
 582        
 583        /* handle the start of the mapping */
 584        if (start > real_start) {
 585            if (real_end == real_start + qemu_host_page_size) {
 586                /* one single host page */
 587                ret = mmap_frag(real_start, start, end,
 588                                host_prot, flags, fd, offset);
 589                if (ret == -1)
 590                    goto fail;
 591                goto the_end1;
 592            }
 593            ret = mmap_frag(real_start, start, real_start + qemu_host_page_size,
 594                            host_prot, flags, fd, offset);
 595            if (ret == -1)
 596                goto fail;
 597            real_start += qemu_host_page_size;
 598        }
 599        /* handle the end of the mapping */
 600        if (end < real_end) {
 601            ret = mmap_frag(real_end - qemu_host_page_size,
 602                            real_end - qemu_host_page_size, end,
 603                            host_prot, flags, fd,
 604                            offset + real_end - qemu_host_page_size - start);
 605            if (ret == -1)
 606                goto fail;
 607            real_end -= qemu_host_page_size;
 608        }
 609
 610        /* map the middle (easier) */
 611        if (real_start < real_end) {
 612            void *p;
 613            unsigned long offset1;
 614            if (flags & MAP_ANONYMOUS)
 615                offset1 = 0;
 616            else
 617                offset1 = offset + real_start - start;
 618            p = mmap(g2h_untagged(real_start), real_end - real_start,
 619                     host_prot, flags, fd, offset1);
 620            if (p == MAP_FAILED)
 621                goto fail;
 622        }
 623    }
 624 the_end1:
 625    if (flags & MAP_ANONYMOUS) {
 626        page_flags |= PAGE_ANON;
 627    }
 628    page_flags |= PAGE_RESET;
 629    page_set_flags(start, start + len, page_flags);
 630 the_end:
 631    trace_target_mmap_complete(start);
 632    if (qemu_loglevel_mask(CPU_LOG_PAGE)) {
 633        log_page_dump(__func__);
 634    }
 635    tb_invalidate_phys_range(start, start + len);
 636    mmap_unlock();
 637    return start;
 638fail:
 639    mmap_unlock();
 640    return -1;
 641}
 642
 643static void mmap_reserve(abi_ulong start, abi_ulong size)
 644{
 645    abi_ulong real_start;
 646    abi_ulong real_end;
 647    abi_ulong addr;
 648    abi_ulong end;
 649    int prot;
 650
 651    real_start = start & qemu_host_page_mask;
 652    real_end = HOST_PAGE_ALIGN(start + size);
 653    end = start + size;
 654    if (start > real_start) {
 655        /* handle host page containing start */
 656        prot = 0;
 657        for (addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
 658            prot |= page_get_flags(addr);
 659        }
 660        if (real_end == real_start + qemu_host_page_size) {
 661            for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
 662                prot |= page_get_flags(addr);
 663            }
 664            end = real_end;
 665        }
 666        if (prot != 0)
 667            real_start += qemu_host_page_size;
 668    }
 669    if (end < real_end) {
 670        prot = 0;
 671        for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
 672            prot |= page_get_flags(addr);
 673        }
 674        if (prot != 0)
 675            real_end -= qemu_host_page_size;
 676    }
 677    if (real_start != real_end) {
 678        mmap(g2h_untagged(real_start), real_end - real_start, PROT_NONE,
 679                 MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE,
 680                 -1, 0);
 681    }
 682}
 683
 684int target_munmap(abi_ulong start, abi_ulong len)
 685{
 686    abi_ulong end, real_start, real_end, addr;
 687    int prot, ret;
 688
 689    trace_target_munmap(start, len);
 690
 691    if (start & ~TARGET_PAGE_MASK)
 692        return -TARGET_EINVAL;
 693    len = TARGET_PAGE_ALIGN(len);
 694    if (len == 0 || !guest_range_valid_untagged(start, len)) {
 695        return -TARGET_EINVAL;
 696    }
 697
 698    mmap_lock();
 699    end = start + len;
 700    real_start = start & qemu_host_page_mask;
 701    real_end = HOST_PAGE_ALIGN(end);
 702
 703    if (start > real_start) {
 704        /* handle host page containing start */
 705        prot = 0;
 706        for(addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
 707            prot |= page_get_flags(addr);
 708        }
 709        if (real_end == real_start + qemu_host_page_size) {
 710            for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
 711                prot |= page_get_flags(addr);
 712            }
 713            end = real_end;
 714        }
 715        if (prot != 0)
 716            real_start += qemu_host_page_size;
 717    }
 718    if (end < real_end) {
 719        prot = 0;
 720        for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
 721            prot |= page_get_flags(addr);
 722        }
 723        if (prot != 0)
 724            real_end -= qemu_host_page_size;
 725    }
 726
 727    ret = 0;
 728    /* unmap what we can */
 729    if (real_start < real_end) {
 730        if (reserved_va) {
 731            mmap_reserve(real_start, real_end - real_start);
 732        } else {
 733            ret = munmap(g2h_untagged(real_start), real_end - real_start);
 734        }
 735    }
 736
 737    if (ret == 0) {
 738        page_set_flags(start, start + len, 0);
 739        tb_invalidate_phys_range(start, start + len);
 740    }
 741    mmap_unlock();
 742    return ret;
 743}
 744
 745abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
 746                       abi_ulong new_size, unsigned long flags,
 747                       abi_ulong new_addr)
 748{
 749    int prot;
 750    void *host_addr;
 751
 752    if (!guest_range_valid_untagged(old_addr, old_size) ||
 753        ((flags & MREMAP_FIXED) &&
 754         !guest_range_valid_untagged(new_addr, new_size)) ||
 755        ((flags & MREMAP_MAYMOVE) == 0 &&
 756         !guest_range_valid_untagged(old_addr, new_size))) {
 757        errno = ENOMEM;
 758        return -1;
 759    }
 760
 761    mmap_lock();
 762
 763    if (flags & MREMAP_FIXED) {
 764        host_addr = mremap(g2h_untagged(old_addr), old_size, new_size,
 765                           flags, g2h_untagged(new_addr));
 766
 767        if (reserved_va && host_addr != MAP_FAILED) {
 768            /* If new and old addresses overlap then the above mremap will
 769               already have failed with EINVAL.  */
 770            mmap_reserve(old_addr, old_size);
 771        }
 772    } else if (flags & MREMAP_MAYMOVE) {
 773        abi_ulong mmap_start;
 774
 775        mmap_start = mmap_find_vma(0, new_size, TARGET_PAGE_SIZE);
 776
 777        if (mmap_start == -1) {
 778            errno = ENOMEM;
 779            host_addr = MAP_FAILED;
 780        } else {
 781            host_addr = mremap(g2h_untagged(old_addr), old_size, new_size,
 782                               flags | MREMAP_FIXED,
 783                               g2h_untagged(mmap_start));
 784            if (reserved_va) {
 785                mmap_reserve(old_addr, old_size);
 786            }
 787        }
 788    } else {
 789        int prot = 0;
 790        if (reserved_va && old_size < new_size) {
 791            abi_ulong addr;
 792            for (addr = old_addr + old_size;
 793                 addr < old_addr + new_size;
 794                 addr++) {
 795                prot |= page_get_flags(addr);
 796            }
 797        }
 798        if (prot == 0) {
 799            host_addr = mremap(g2h_untagged(old_addr),
 800                               old_size, new_size, flags);
 801
 802            if (host_addr != MAP_FAILED) {
 803                /* Check if address fits target address space */
 804                if (!guest_range_valid_untagged(h2g(host_addr), new_size)) {
 805                    /* Revert mremap() changes */
 806                    host_addr = mremap(g2h_untagged(old_addr),
 807                                       new_size, old_size, flags);
 808                    errno = ENOMEM;
 809                    host_addr = MAP_FAILED;
 810                } else if (reserved_va && old_size > new_size) {
 811                    mmap_reserve(old_addr + old_size, old_size - new_size);
 812                }
 813            }
 814        } else {
 815            errno = ENOMEM;
 816            host_addr = MAP_FAILED;
 817        }
 818    }
 819
 820    if (host_addr == MAP_FAILED) {
 821        new_addr = -1;
 822    } else {
 823        new_addr = h2g(host_addr);
 824        prot = page_get_flags(old_addr);
 825        page_set_flags(old_addr, old_addr + old_size, 0);
 826        page_set_flags(new_addr, new_addr + new_size,
 827                       prot | PAGE_VALID | PAGE_RESET);
 828    }
 829    tb_invalidate_phys_range(new_addr, new_addr + new_size);
 830    mmap_unlock();
 831    return new_addr;
 832}
 833