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