diff options
-rw-r--r-- | linux-user/mmap.c | 68 |
1 files changed, 42 insertions, 26 deletions
diff --git a/linux-user/mmap.c b/linux-user/mmap.c index bb9cbe5..6308787 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -722,47 +722,63 @@ fail: return -1; } -static void mmap_reserve(abi_ulong start, abi_ulong size) +static void mmap_reserve(abi_ulong start, abi_ulong len) { abi_ulong real_start; - abi_ulong real_end; - abi_ulong addr; - abi_ulong end; + abi_ulong real_last; + abi_ulong real_len; + abi_ulong last; + abi_ulong a; + void *host_start, *ptr; int prot; + last = start + len - 1; real_start = start & qemu_host_page_mask; - real_end = HOST_PAGE_ALIGN(start + size); - end = start + size; - if (start > real_start) { - /* handle host page containing start */ + real_last = HOST_PAGE_ALIGN(last) - 1; + + /* + * If guest pages remain on the first or last host pages, + * adjust the deallocation to retain those guest pages. + * The single page special case is required for the last page, + * lest real_start overflow to zero. + */ + if (real_last - real_start < qemu_host_page_size) { prot = 0; - for (addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) { - prot |= page_get_flags(addr); + for (a = real_start; a < start; a += TARGET_PAGE_SIZE) { + prot |= page_get_flags(a); } - if (real_end == real_start + qemu_host_page_size) { - for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) { - prot |= page_get_flags(addr); - } - end = real_end; + for (a = last; a < real_last; a += TARGET_PAGE_SIZE) { + prot |= page_get_flags(a + 1); + } + if (prot != 0) { + return; + } + } else { + for (prot = 0, a = real_start; a < start; a += TARGET_PAGE_SIZE) { + prot |= page_get_flags(a); } if (prot != 0) { real_start += qemu_host_page_size; } - } - if (end < real_end) { - prot = 0; - for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) { - prot |= page_get_flags(addr); + + for (prot = 0, a = last; a < real_last; a += TARGET_PAGE_SIZE) { + prot |= page_get_flags(a + 1); } if (prot != 0) { - real_end -= qemu_host_page_size; + real_last -= qemu_host_page_size; + } + + if (real_last < real_start) { + return; } } - if (real_start != real_end) { - mmap(g2h_untagged(real_start), real_end - real_start, PROT_NONE, - MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE, - -1, 0); - } + + real_len = real_last - real_start + 1; + host_start = g2h_untagged(real_start); + + ptr = mmap(host_start, real_len, PROT_NONE, + MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE, -1, 0); + assert(ptr == host_start); } int target_munmap(abi_ulong start, abi_ulong len) |