diff options
author | bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162> | 2007-11-14 11:29:07 +0000 |
---|---|---|
committer | bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162> | 2007-11-14 11:29:07 +0000 |
commit | a03e2d421e7f33316750d6b7396d1a7e14b18d53 (patch) | |
tree | 371bef5eed6f6feb63ead1c4ce813b51c24683fb /linux-user | |
parent | dae3270c6e25062b9085da0756b68fafedc9bb2c (diff) |
fixed target_mmap() if host page size < TARGET_PAGE_SIZE
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3642 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'linux-user')
-rw-r--r-- | linux-user/mmap.c | 264 |
1 files changed, 126 insertions, 138 deletions
diff --git a/linux-user/mmap.c b/linux-user/mmap.c index f87303402d..3f2647f304 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -151,19 +151,53 @@ static int mmap_frag(abi_ulong real_start, return 0; } +#if defined(__CYGWIN__) +/* Cygwin doesn't have a whole lot of address space. */ +static abi_ulong mmap_next_start = 0x18000000; +#else +static abi_ulong mmap_next_start = 0x40000000; +#endif + +/* find a free memory area of size 'size'. The search starts at + 'start'. If 'start' == 0, then a default start address is used. + Return -1 if error. +*/ +/* XXX: should mark pages used by the host as reserved to be sure not + to use them. */ +static abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size) +{ + abi_ulong addr, addr1, addr_start; + int prot; + + size = HOST_PAGE_ALIGN(size); + start = start & qemu_host_page_mask; + addr = start; + if (addr == 0) + addr = mmap_next_start; + addr_start = addr; + for(;;) { + prot = 0; + for(addr1 = addr; addr1 < (addr + size); addr1 += TARGET_PAGE_SIZE) { + prot |= page_get_flags(addr1); + } + if (prot == 0) + break; + addr += qemu_host_page_size; + /* we found nothing */ + if (addr == addr_start) + return (abi_ulong)-1; + } + if (start == 0) + mmap_next_start = addr + size; + return addr; +} + /* NOTE: all the constants are the HOST ones */ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, int flags, int fd, abi_ulong offset) { abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len; unsigned long host_start; -#if defined(__alpha__) || defined(__sparc__) || defined(__x86_64__) || \ - defined(__ia64) || defined(__mips__) - static abi_ulong last_start = 0x40000000; -#elif defined(__CYGWIN__) - /* Cygwin doesn't have a whole lot of address space. */ - static abi_ulong last_start = 0x18000000; -#endif #ifdef DEBUG_MMAP { @@ -203,147 +237,101 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, real_start = start & qemu_host_page_mask; if (!(flags & MAP_FIXED)) { -#if defined(__alpha__) || defined(__sparc__) || defined(__x86_64__) || \ - defined(__ia64) || defined(__mips__) || defined(__CYGWIN__) - /* tell the kernel to search at the same place as i386 */ - if (real_start == 0) { - real_start = last_start; - last_start += HOST_PAGE_ALIGN(len); - } -#endif - host_offset = offset & qemu_host_page_mask; - host_len = len + offset - host_offset; - - if (qemu_host_page_size > qemu_real_host_page_size) { - /* - * The guest expects to see mmapped areas aligned to it's pagesize. - * If the host's real page size is smaller than the guest's, we need - * to fixup the maps. It is done by allocating a larger area, - * displacing the map (if needed) and finally chopping off the spare - * room at the edges. - */ - - /* - * We assume qemu_host_page_size is always the same as - * TARGET_PAGE_SIZE, see exec.c. qemu_real_host_page_size is the - * hosts real page size. - */ - abi_ulong host_end; - unsigned long host_aligned_start; - void *p; - - host_len = HOST_PAGE_ALIGN(host_len + qemu_host_page_size - - qemu_real_host_page_size); - p = mmap(real_start ? g2h(real_start) : NULL, - host_len, prot, flags, fd, host_offset); - if (p == MAP_FAILED) - return -1; - - host_start = (unsigned long)p; - host_end = host_start + host_len; - - /* Find start and end, aligned to the targets pagesize with-in the - large mmaped area. */ - host_aligned_start = TARGET_PAGE_ALIGN(host_start); - if (!(flags & MAP_ANONYMOUS)) - host_aligned_start += offset - host_offset; - - start = h2g(host_aligned_start); - end = start + TARGET_PAGE_ALIGN(len); - - /* Chop off the leftovers, if any. */ - if (host_aligned_start > host_start) - munmap((void *)host_start, host_aligned_start - host_start); - if (end < host_end) - munmap((void *)g2h(end), host_end - end); - - goto the_end1; - } else { - /* if not fixed, no need to do anything */ - void *p = mmap(real_start ? g2h(real_start) : NULL, - host_len, prot, flags, fd, host_offset); - if (p == MAP_FAILED) - return -1; - /* update start so that it points to the file position at 'offset' */ - host_start = (unsigned long)p; - if (!(flags & MAP_ANONYMOUS)) - host_start += offset - host_offset; - start = h2g(host_start); - goto the_end1; + abi_ulong mmap_start; + void *p; + host_offset = offset & qemu_host_page_mask; + host_len = len + offset - host_offset; + host_len = HOST_PAGE_ALIGN(host_len); + mmap_start = mmap_find_vma(real_start, host_len); + if (mmap_start == (abi_ulong)-1) { + errno = ENOMEM; + return -1; } - } - - if (start & ~TARGET_PAGE_MASK) { - errno = EINVAL; - return -1; - } - end = start + len; - real_end = HOST_PAGE_ALIGN(end); - - /* worst case: we cannot map the file because the offset is not - aligned, so we read it */ - if (!(flags & MAP_ANONYMOUS) && - (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) { - /* msync() won't work here, so we return an error if write is - possible while it is a shared mapping */ - if ((flags & MAP_TYPE) == MAP_SHARED && - (prot & PROT_WRITE)) { + /* Note: we prefer to control the mapping address. It is + especially important if qemu_host_page_size > + qemu_real_host_page_size */ + p = mmap(g2h(mmap_start), + host_len, prot, flags | MAP_FIXED, fd, host_offset); + if (p == MAP_FAILED) + return -1; + /* update start so that it points to the file position at 'offset' */ + host_start = (unsigned long)p; + if (!(flags & MAP_ANONYMOUS)) + host_start += offset - host_offset; + start = h2g(host_start); + } else { + if (start & ~TARGET_PAGE_MASK) { errno = EINVAL; return -1; } - retaddr = target_mmap(start, len, prot | PROT_WRITE, - MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, - -1, 0); - if (retaddr == -1) - return -1; - pread(fd, g2h(start), len, offset); - if (!(prot & PROT_WRITE)) { - ret = target_mprotect(start, len, prot); - if (ret != 0) - return ret; + end = start + len; + real_end = HOST_PAGE_ALIGN(end); + + /* worst case: we cannot map the file because the offset is not + aligned, so we read it */ + if (!(flags & MAP_ANONYMOUS) && + (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) { + /* msync() won't work here, so we return an error if write is + possible while it is a shared mapping */ + if ((flags & MAP_TYPE) == MAP_SHARED && + (prot & PROT_WRITE)) { + errno = EINVAL; + return -1; + } + retaddr = target_mmap(start, len, prot | PROT_WRITE, + MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, + -1, 0); + if (retaddr == -1) + return -1; + pread(fd, g2h(start), len, offset); + if (!(prot & PROT_WRITE)) { + ret = target_mprotect(start, len, prot); + if (ret != 0) + return ret; + } + goto the_end; } - goto the_end; - } - - /* handle the start of the mapping */ - if (start > real_start) { - if (real_end == real_start + qemu_host_page_size) { - /* one single host page */ - ret = mmap_frag(real_start, start, end, + + /* handle the start of the mapping */ + if (start > real_start) { + if (real_end == real_start + qemu_host_page_size) { + /* one single host page */ + ret = mmap_frag(real_start, start, end, + prot, flags, fd, offset); + if (ret == -1) + return ret; + goto the_end1; + } + ret = mmap_frag(real_start, start, real_start + qemu_host_page_size, prot, flags, fd, offset); if (ret == -1) return ret; - goto the_end1; + real_start += qemu_host_page_size; + } + /* handle the end of the mapping */ + if (end < real_end) { + ret = mmap_frag(real_end - qemu_host_page_size, + real_end - qemu_host_page_size, real_end, + prot, flags, fd, + offset + real_end - qemu_host_page_size - start); + if (ret == -1) + return -1; + real_end -= qemu_host_page_size; } - ret = mmap_frag(real_start, start, real_start + qemu_host_page_size, - prot, flags, fd, offset); - if (ret == -1) - return ret; - real_start += qemu_host_page_size; - } - /* handle the end of the mapping */ - if (end < real_end) { - ret = mmap_frag(real_end - qemu_host_page_size, - real_end - qemu_host_page_size, real_end, - prot, flags, fd, - offset + real_end - qemu_host_page_size - start); - if (ret == -1) - return -1; - real_end -= qemu_host_page_size; - } - /* map the middle (easier) */ - if (real_start < real_end) { - void *p; - unsigned long offset1; - if (flags & MAP_ANONYMOUS) - offset1 = 0; - else - offset1 = offset + real_start - start; - p = mmap(g2h(real_start), real_end - real_start, - prot, flags, fd, offset1); - if (p == MAP_FAILED) - return -1; + /* map the middle (easier) */ + if (real_start < real_end) { + void *p; + unsigned long offset1; + if (flags & MAP_ANONYMOUS) + offset1 = 0; + else + offset1 = offset + real_start - start; + p = mmap(g2h(real_start), real_end - real_start, + prot, flags, fd, offset1); + if (p == MAP_FAILED) + return -1; + } } the_end1: page_set_flags(start, start + len, prot | PAGE_VALID); |