Revision 68a1c816 linux-user/mmap.c

b/linux-user/mmap.c
216 216

  
217 217
unsigned long last_brk;
218 218

  
219
/* Subroutine of mmap_find_vma, used when we have pre-allocated a chunk
220
   of guest address space.  */
221
static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size)
222
{
223
    abi_ulong addr;
224
    abi_ulong last_addr;
225
    int prot;
226
    int looped = 0;
227

  
228
    if (size > reserved_va) {
229
        return (abi_ulong)-1;
230
    }
231

  
232
    last_addr = start;
233
    for (addr = start; last_addr + size != addr; addr += qemu_host_page_size) {
234
        if (last_addr + size >= reserved_va
235
            || (abi_ulong)(last_addr + size) < last_addr) {
236
            if (looped) {
237
                return (abi_ulong)-1;
238
            }
239
            last_addr = qemu_host_page_size;
240
            addr = 0;
241
            looped = 1;
242
            continue;
243
        }
244
        prot = page_get_flags(addr);
245
        if (prot) {
246
            last_addr = addr + qemu_host_page_size;
247
        }
248
    }
249
    mmap_next_start = addr;
250
    return last_addr;
251
}
252

  
219 253
/*
220 254
 * Find and reserve a free memory area of size 'size'. The search
221 255
 * starts at 'start'.
......
237 271

  
238 272
    size = HOST_PAGE_ALIGN(size);
239 273

  
274
    if (reserved_va) {
275
        return mmap_find_vma_reserved(start, size);
276
    }
277

  
240 278
    addr = start;
241 279
    wrapped = repeat = 0;
242 280
    prev = 0;
......
525 563
    return -1;
526 564
}
527 565

  
566
static void mmap_reserve(abi_ulong start, abi_ulong size)
567
{
568
    abi_ulong real_start;
569
    abi_ulong real_end;
570
    abi_ulong addr;
571
    abi_ulong end;
572
    int prot;
573

  
574
    real_start = start & qemu_host_page_mask;
575
    real_end = HOST_PAGE_ALIGN(start + size);
576
    end = start + size;
577
    if (start > real_start) {
578
        /* handle host page containing start */
579
        prot = 0;
580
        for (addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
581
            prot |= page_get_flags(addr);
582
        }
583
        if (real_end == real_start + qemu_host_page_size) {
584
            for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
585
                prot |= page_get_flags(addr);
586
            }
587
            end = real_end;
588
        }
589
        if (prot != 0)
590
            real_start += qemu_host_page_size;
591
    }
592
    if (end < real_end) {
593
        prot = 0;
594
        for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
595
            prot |= page_get_flags(addr);
596
        }
597
        if (prot != 0)
598
            real_end -= qemu_host_page_size;
599
    }
600
    if (real_start != real_end) {
601
        mmap(g2h(real_start), real_end - real_start, PROT_NONE,
602
                 MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE,
603
                 -1, 0);
604
    }
605
}
606

  
528 607
int target_munmap(abi_ulong start, abi_ulong len)
529 608
{
530 609
    abi_ulong end, real_start, real_end, addr;
......
572 651
    ret = 0;
573 652
    /* unmap what we can */
574 653
    if (real_start < real_end) {
575
        ret = munmap(g2h(real_start), real_end - real_start);
654
        if (reserved_va) {
655
            mmap_reserve(real_start, real_end - real_start);
656
        } else {
657
            ret = munmap(g2h(real_start), real_end - real_start);
658
        }
576 659
    }
577 660

  
578 661
    if (ret == 0)
......
590 673

  
591 674
    mmap_lock();
592 675

  
593
    if (flags & MREMAP_FIXED)
676
    if (flags & MREMAP_FIXED) {
594 677
        host_addr = (void *) syscall(__NR_mremap, g2h(old_addr),
595 678
                                     old_size, new_size,
596 679
                                     flags,
597
                                     new_addr);
598
    else if (flags & MREMAP_MAYMOVE) {
680
                                     g2h(new_addr));
681

  
682
        if (reserved_va && host_addr != MAP_FAILED) {
683
            /* If new and old addresses overlap then the above mremap will
684
               already have failed with EINVAL.  */
685
            mmap_reserve(old_addr, old_size);
686
        }
687
    } else if (flags & MREMAP_MAYMOVE) {
599 688
        abi_ulong mmap_start;
600 689

  
601 690
        mmap_start = mmap_find_vma(0, new_size);
......
603 692
        if (mmap_start == -1) {
604 693
            errno = ENOMEM;
605 694
            host_addr = MAP_FAILED;
606
        } else
695
        } else {
607 696
            host_addr = (void *) syscall(__NR_mremap, g2h(old_addr),
608 697
                                         old_size, new_size,
609 698
                                         flags | MREMAP_FIXED,
610 699
                                         g2h(mmap_start));
700
            mmap_reserve(old_addr, old_size);
701
        }
611 702
    } else {
612
        host_addr = mremap(g2h(old_addr), old_size, new_size, flags);
703
        int prot = 0;
704
        if (reserved_va && old_size < new_size) {
705
            abi_ulong addr;
706
            for (addr = old_addr + old_size;
707
                 addr < old_addr + new_size;
708
                 addr++) {
709
                prot |= page_get_flags(addr);
710
            }
711
        }
712
        if (prot == 0) {
713
            host_addr = mremap(g2h(old_addr), old_size, new_size, flags);
714
            if (host_addr != MAP_FAILED && reserved_va && old_size > new_size) {
715
                mmap_reserve(old_addr + old_size, new_size - old_size);
716
            }
717
        } else {
718
            errno = ENOMEM;
719
            host_addr = MAP_FAILED;
720
        }
613 721
        /* Check if address fits target address space */
614 722
        if ((unsigned long)host_addr + new_size > (abi_ulong)-1) {
615 723
            /* Revert mremap() changes */

Also available in: Unified diff