xref: /qemu/linux-user/mmap.c (revision 45bc1f52)
154936004Sbellard /*
254936004Sbellard  *  mmap support for qemu
354936004Sbellard  *
454936004Sbellard  *  Copyright (c) 2003 Fabrice Bellard
554936004Sbellard  *
654936004Sbellard  *  This program is free software; you can redistribute it and/or modify
754936004Sbellard  *  it under the terms of the GNU General Public License as published by
854936004Sbellard  *  the Free Software Foundation; either version 2 of the License, or
954936004Sbellard  *  (at your option) any later version.
1054936004Sbellard  *
1154936004Sbellard  *  This program is distributed in the hope that it will be useful,
1254936004Sbellard  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
1354936004Sbellard  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1454936004Sbellard  *  GNU General Public License for more details.
1554936004Sbellard  *
1654936004Sbellard  *  You should have received a copy of the GNU General Public License
1754936004Sbellard  *  along with this program; if not, write to the Free Software
1854936004Sbellard  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
1954936004Sbellard  */
2054936004Sbellard #include <stdlib.h>
2154936004Sbellard #include <stdio.h>
2254936004Sbellard #include <stdarg.h>
2354936004Sbellard #include <string.h>
2454936004Sbellard #include <unistd.h>
2554936004Sbellard #include <errno.h>
2654936004Sbellard #include <sys/mman.h>
2754936004Sbellard 
2854936004Sbellard #include "qemu.h"
2978f5bf1eSblueswir1 #include "qemu-common.h"
3054936004Sbellard 
3154936004Sbellard //#define DEBUG_MMAP
3254936004Sbellard 
33c8a706feSpbrook #if defined(USE_NPTL)
34c8a706feSpbrook pthread_mutex_t mmap_mutex;
35c8a706feSpbrook static int __thread mmap_lock_count;
36c8a706feSpbrook 
37c8a706feSpbrook void mmap_lock(void)
38c8a706feSpbrook {
39c8a706feSpbrook     if (mmap_lock_count++ == 0) {
40c8a706feSpbrook         pthread_mutex_lock(&mmap_mutex);
41c8a706feSpbrook     }
42c8a706feSpbrook }
43c8a706feSpbrook 
44c8a706feSpbrook void mmap_unlock(void)
45c8a706feSpbrook {
46c8a706feSpbrook     if (--mmap_lock_count == 0) {
47c8a706feSpbrook         pthread_mutex_unlock(&mmap_mutex);
48c8a706feSpbrook     }
49c8a706feSpbrook }
50d5975363Spbrook 
51d5975363Spbrook /* Grab lock to make sure things are in a consistent state after fork().  */
52d5975363Spbrook void mmap_fork_start(void)
53d5975363Spbrook {
54d5975363Spbrook     if (mmap_lock_count)
55d5975363Spbrook         abort();
56d5975363Spbrook     pthread_mutex_lock(&mmap_mutex);
57d5975363Spbrook }
58d5975363Spbrook 
59d5975363Spbrook void mmap_fork_end(int child)
60d5975363Spbrook {
61d5975363Spbrook     if (child)
62d5975363Spbrook         pthread_mutex_init(&mmap_mutex, NULL);
63d5975363Spbrook     else
64d5975363Spbrook         pthread_mutex_unlock(&mmap_mutex);
65d5975363Spbrook }
66c8a706feSpbrook #else
67c8a706feSpbrook /* We aren't threadsafe to start with, so no need to worry about locking.  */
68c8a706feSpbrook void mmap_lock(void)
69c8a706feSpbrook {
70c8a706feSpbrook }
71c8a706feSpbrook 
72c8a706feSpbrook void mmap_unlock(void)
73c8a706feSpbrook {
74c8a706feSpbrook }
75c8a706feSpbrook #endif
76c8a706feSpbrook 
7717e2377aSpbrook void *qemu_vmalloc(size_t size)
7817e2377aSpbrook {
7917e2377aSpbrook     void *p;
8017e2377aSpbrook     unsigned long addr;
8117e2377aSpbrook     mmap_lock();
8217e2377aSpbrook     /* Use map and mark the pages as used.  */
8317e2377aSpbrook     p = mmap(NULL, size, PROT_READ | PROT_WRITE,
8417e2377aSpbrook              MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
8517e2377aSpbrook 
8617e2377aSpbrook     addr = (unsigned long)p;
8717e2377aSpbrook     if (addr == (target_ulong) addr) {
8817e2377aSpbrook         /* Allocated region overlaps guest address space.
8917e2377aSpbrook            This may recurse.  */
9017e2377aSpbrook         page_set_flags(addr & TARGET_PAGE_MASK, TARGET_PAGE_ALIGN(addr + size),
9117e2377aSpbrook                        PAGE_RESERVED);
9217e2377aSpbrook     }
9317e2377aSpbrook 
9417e2377aSpbrook     mmap_unlock();
9517e2377aSpbrook     return p;
9617e2377aSpbrook }
9717e2377aSpbrook 
9817e2377aSpbrook void *qemu_malloc(size_t size)
9917e2377aSpbrook {
10017e2377aSpbrook     char * p;
10117e2377aSpbrook     size += 16;
10217e2377aSpbrook     p = qemu_vmalloc(size);
10317e2377aSpbrook     *(size_t *)p = size;
10417e2377aSpbrook     return p + 16;
10517e2377aSpbrook }
10617e2377aSpbrook 
10717e2377aSpbrook /* We use map, which is always zero initialized.  */
10817e2377aSpbrook void * qemu_mallocz(size_t size)
10917e2377aSpbrook {
11017e2377aSpbrook     return qemu_malloc(size);
11117e2377aSpbrook }
11217e2377aSpbrook 
11317e2377aSpbrook void qemu_free(void *ptr)
11417e2377aSpbrook {
11517e2377aSpbrook     /* FIXME: We should unmark the reserved pages here.  However this gets
11617e2377aSpbrook        complicated when one target page spans multiple host pages, so we
11717e2377aSpbrook        don't bother.  */
11817e2377aSpbrook     size_t *p;
11917e2377aSpbrook     p = (size_t *)((char *)ptr - 16);
12017e2377aSpbrook     munmap(p, *p);
12117e2377aSpbrook }
12217e2377aSpbrook 
12353a5960aSpbrook /* NOTE: all the constants are the HOST ones, but addresses are target. */
124992f48a0Sblueswir1 int target_mprotect(abi_ulong start, abi_ulong len, int prot)
12554936004Sbellard {
126992f48a0Sblueswir1     abi_ulong end, host_start, host_end, addr;
12754936004Sbellard     int prot1, ret;
12854936004Sbellard 
12954936004Sbellard #ifdef DEBUG_MMAP
130a5b85f79Sths     printf("mprotect: start=0x" TARGET_FMT_lx
131a5b85f79Sths            "len=0x" TARGET_FMT_lx " prot=%c%c%c\n", start, len,
13254936004Sbellard            prot & PROT_READ ? 'r' : '-',
13354936004Sbellard            prot & PROT_WRITE ? 'w' : '-',
13454936004Sbellard            prot & PROT_EXEC ? 'x' : '-');
13554936004Sbellard #endif
13654936004Sbellard 
13754936004Sbellard     if ((start & ~TARGET_PAGE_MASK) != 0)
13854936004Sbellard         return -EINVAL;
13954936004Sbellard     len = TARGET_PAGE_ALIGN(len);
14054936004Sbellard     end = start + len;
14154936004Sbellard     if (end < start)
14254936004Sbellard         return -EINVAL;
143171cd1cdSbalrog     prot &= PROT_READ | PROT_WRITE | PROT_EXEC;
14454936004Sbellard     if (len == 0)
14554936004Sbellard         return 0;
14654936004Sbellard 
147c8a706feSpbrook     mmap_lock();
14883fb7adfSbellard     host_start = start & qemu_host_page_mask;
14954936004Sbellard     host_end = HOST_PAGE_ALIGN(end);
15054936004Sbellard     if (start > host_start) {
15154936004Sbellard         /* handle host page containing start */
15254936004Sbellard         prot1 = prot;
15354936004Sbellard         for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
15454936004Sbellard             prot1 |= page_get_flags(addr);
15554936004Sbellard         }
15683fb7adfSbellard         if (host_end == host_start + qemu_host_page_size) {
157d418c81eSbellard             for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
158d418c81eSbellard                 prot1 |= page_get_flags(addr);
159d418c81eSbellard             }
160d418c81eSbellard             end = host_end;
161d418c81eSbellard         }
16253a5960aSpbrook         ret = mprotect(g2h(host_start), qemu_host_page_size, prot1 & PAGE_BITS);
16354936004Sbellard         if (ret != 0)
164c8a706feSpbrook             goto error;
16583fb7adfSbellard         host_start += qemu_host_page_size;
16654936004Sbellard     }
16754936004Sbellard     if (end < host_end) {
16854936004Sbellard         prot1 = prot;
16954936004Sbellard         for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
17054936004Sbellard             prot1 |= page_get_flags(addr);
17154936004Sbellard         }
17253a5960aSpbrook         ret = mprotect(g2h(host_end - qemu_host_page_size), qemu_host_page_size,
17354936004Sbellard                        prot1 & PAGE_BITS);
17454936004Sbellard         if (ret != 0)
175c8a706feSpbrook             goto error;
17683fb7adfSbellard         host_end -= qemu_host_page_size;
17754936004Sbellard     }
17854936004Sbellard 
17954936004Sbellard     /* handle the pages in the middle */
18054936004Sbellard     if (host_start < host_end) {
18153a5960aSpbrook         ret = mprotect(g2h(host_start), host_end - host_start, prot);
18254936004Sbellard         if (ret != 0)
183c8a706feSpbrook             goto error;
18454936004Sbellard     }
18554936004Sbellard     page_set_flags(start, start + len, prot | PAGE_VALID);
186c8a706feSpbrook     mmap_unlock();
18754936004Sbellard     return 0;
188c8a706feSpbrook error:
189c8a706feSpbrook     mmap_unlock();
190c8a706feSpbrook     return ret;
19154936004Sbellard }
19254936004Sbellard 
19354936004Sbellard /* map an incomplete host page */
194992f48a0Sblueswir1 static int mmap_frag(abi_ulong real_start,
195992f48a0Sblueswir1                      abi_ulong start, abi_ulong end,
196992f48a0Sblueswir1                      int prot, int flags, int fd, abi_ulong offset)
19754936004Sbellard {
19880210bcdSths     abi_ulong real_end, addr;
19953a5960aSpbrook     void *host_start;
20054936004Sbellard     int prot1, prot_new;
20154936004Sbellard 
20253a5960aSpbrook     real_end = real_start + qemu_host_page_size;
20353a5960aSpbrook     host_start = g2h(real_start);
20454936004Sbellard 
20554936004Sbellard     /* get the protection of the target pages outside the mapping */
20654936004Sbellard     prot1 = 0;
20753a5960aSpbrook     for(addr = real_start; addr < real_end; addr++) {
20854936004Sbellard         if (addr < start || addr >= end)
20954936004Sbellard             prot1 |= page_get_flags(addr);
21054936004Sbellard     }
21154936004Sbellard 
21254936004Sbellard     if (prot1 == 0) {
21354936004Sbellard         /* no page was there, so we allocate one */
21480210bcdSths         void *p = mmap(host_start, qemu_host_page_size, prot,
21554936004Sbellard                        flags | MAP_ANONYMOUS, -1, 0);
21680210bcdSths         if (p == MAP_FAILED)
21780210bcdSths             return -1;
21853a5960aSpbrook         prot1 = prot;
21954936004Sbellard     }
22054936004Sbellard     prot1 &= PAGE_BITS;
22154936004Sbellard 
22254936004Sbellard     prot_new = prot | prot1;
22354936004Sbellard     if (!(flags & MAP_ANONYMOUS)) {
22454936004Sbellard         /* msync() won't work here, so we return an error if write is
22554936004Sbellard            possible while it is a shared mapping */
22654936004Sbellard         if ((flags & MAP_TYPE) == MAP_SHARED &&
22754936004Sbellard             (prot & PROT_WRITE))
22854936004Sbellard             return -EINVAL;
22954936004Sbellard 
23054936004Sbellard         /* adjust protection to be able to read */
23154936004Sbellard         if (!(prot1 & PROT_WRITE))
23253a5960aSpbrook             mprotect(host_start, qemu_host_page_size, prot1 | PROT_WRITE);
23354936004Sbellard 
23454936004Sbellard         /* read the corresponding file data */
23553a5960aSpbrook         pread(fd, g2h(start), end - start, offset);
23654936004Sbellard 
23754936004Sbellard         /* put final protection */
23854936004Sbellard         if (prot_new != (prot1 | PROT_WRITE))
23953a5960aSpbrook             mprotect(host_start, qemu_host_page_size, prot_new);
24054936004Sbellard     } else {
24154936004Sbellard         /* just update the protection */
24254936004Sbellard         if (prot_new != prot1) {
24353a5960aSpbrook             mprotect(host_start, qemu_host_page_size, prot_new);
24454936004Sbellard         }
24554936004Sbellard     }
24654936004Sbellard     return 0;
24754936004Sbellard }
24854936004Sbellard 
249a03e2d42Sbellard #if defined(__CYGWIN__)
250a03e2d42Sbellard /* Cygwin doesn't have a whole lot of address space.  */
251a03e2d42Sbellard static abi_ulong mmap_next_start = 0x18000000;
252a03e2d42Sbellard #else
253a03e2d42Sbellard static abi_ulong mmap_next_start = 0x40000000;
254a03e2d42Sbellard #endif
255a03e2d42Sbellard 
2560776590dSpbrook unsigned long last_brk;
2570776590dSpbrook 
258a03e2d42Sbellard /* find a free memory area of size 'size'. The search starts at
259a03e2d42Sbellard    'start'. If 'start' == 0, then a default start address is used.
260a03e2d42Sbellard    Return -1 if error.
261a03e2d42Sbellard */
26250a9569bSbalrog /* page_init() marks pages used by the host as reserved to be sure not
263a03e2d42Sbellard    to use them. */
264a03e2d42Sbellard static abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
265a03e2d42Sbellard {
266a03e2d42Sbellard     abi_ulong addr, addr1, addr_start;
267a03e2d42Sbellard     int prot;
2680776590dSpbrook     unsigned long new_brk;
2690776590dSpbrook 
2700776590dSpbrook     new_brk = (unsigned long)sbrk(0);
2710776590dSpbrook     if (last_brk && last_brk < new_brk && last_brk == (target_ulong)last_brk) {
2720776590dSpbrook         /* This is a hack to catch the host allocating memory with brk().
2730776590dSpbrook            If it uses mmap then we loose.
2740776590dSpbrook            FIXME: We really want to avoid the host allocating memory in
2750776590dSpbrook            the first place, and maybe leave some slack to avoid switching
2760776590dSpbrook            to mmap.  */
2770776590dSpbrook         page_set_flags(last_brk & TARGET_PAGE_MASK,
2780776590dSpbrook                        TARGET_PAGE_ALIGN(new_brk),
2790776590dSpbrook                        PAGE_RESERVED);
2800776590dSpbrook     }
2810776590dSpbrook     last_brk = new_brk;
282a03e2d42Sbellard 
283a03e2d42Sbellard     size = HOST_PAGE_ALIGN(size);
284a03e2d42Sbellard     start = start & qemu_host_page_mask;
285a03e2d42Sbellard     addr = start;
286a03e2d42Sbellard     if (addr == 0)
287a03e2d42Sbellard         addr = mmap_next_start;
288a03e2d42Sbellard     addr_start = addr;
289a03e2d42Sbellard     for(;;) {
290a03e2d42Sbellard         prot = 0;
291a03e2d42Sbellard         for(addr1 = addr; addr1 < (addr + size); addr1 += TARGET_PAGE_SIZE) {
292a03e2d42Sbellard             prot |= page_get_flags(addr1);
293a03e2d42Sbellard         }
294a03e2d42Sbellard         if (prot == 0)
295a03e2d42Sbellard             break;
296a03e2d42Sbellard         addr += qemu_host_page_size;
297a03e2d42Sbellard         /* we found nothing */
298a03e2d42Sbellard         if (addr == addr_start)
299a03e2d42Sbellard             return (abi_ulong)-1;
300a03e2d42Sbellard     }
301a03e2d42Sbellard     if (start == 0)
302a03e2d42Sbellard         mmap_next_start = addr + size;
303a03e2d42Sbellard     return addr;
304a03e2d42Sbellard }
305a03e2d42Sbellard 
30654936004Sbellard /* NOTE: all the constants are the HOST ones */
307992f48a0Sblueswir1 abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
308992f48a0Sblueswir1                      int flags, int fd, abi_ulong offset)
30954936004Sbellard {
310992f48a0Sblueswir1     abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len;
311a5b85f79Sths     unsigned long host_start;
31254936004Sbellard 
313c8a706feSpbrook     mmap_lock();
31454936004Sbellard #ifdef DEBUG_MMAP
31554936004Sbellard     {
316a5b85f79Sths         printf("mmap: start=0x" TARGET_FMT_lx
317a5b85f79Sths                " len=0x" TARGET_FMT_lx " prot=%c%c%c flags=",
31854936004Sbellard                start, len,
31954936004Sbellard                prot & PROT_READ ? 'r' : '-',
32054936004Sbellard                prot & PROT_WRITE ? 'w' : '-',
32154936004Sbellard                prot & PROT_EXEC ? 'x' : '-');
32254936004Sbellard         if (flags & MAP_FIXED)
32354936004Sbellard             printf("MAP_FIXED ");
32454936004Sbellard         if (flags & MAP_ANONYMOUS)
32554936004Sbellard             printf("MAP_ANON ");
32654936004Sbellard         switch(flags & MAP_TYPE) {
32754936004Sbellard         case MAP_PRIVATE:
32854936004Sbellard             printf("MAP_PRIVATE ");
32954936004Sbellard             break;
33054936004Sbellard         case MAP_SHARED:
33154936004Sbellard             printf("MAP_SHARED ");
33254936004Sbellard             break;
33354936004Sbellard         default:
33454936004Sbellard             printf("[MAP_TYPE=0x%x] ", flags & MAP_TYPE);
33554936004Sbellard             break;
33654936004Sbellard         }
337a5b85f79Sths         printf("fd=%d offset=" TARGET_FMT_lx "\n", fd, offset);
33854936004Sbellard     }
33954936004Sbellard #endif
34054936004Sbellard 
341e89f07d3Spbrook     if (offset & ~TARGET_PAGE_MASK) {
342e89f07d3Spbrook         errno = EINVAL;
343c8a706feSpbrook         goto fail;
344e89f07d3Spbrook     }
34554936004Sbellard 
34654936004Sbellard     len = TARGET_PAGE_ALIGN(len);
34754936004Sbellard     if (len == 0)
348c8a706feSpbrook         goto the_end;
34953a5960aSpbrook     real_start = start & qemu_host_page_mask;
35054936004Sbellard 
35154936004Sbellard     if (!(flags & MAP_FIXED)) {
352a03e2d42Sbellard         abi_ulong mmap_start;
353a03e2d42Sbellard         void *p;
35483fb7adfSbellard         host_offset = offset & qemu_host_page_mask;
35554936004Sbellard         host_len = len + offset - host_offset;
356a03e2d42Sbellard         host_len = HOST_PAGE_ALIGN(host_len);
357a03e2d42Sbellard         mmap_start = mmap_find_vma(real_start, host_len);
358a03e2d42Sbellard         if (mmap_start == (abi_ulong)-1) {
359a03e2d42Sbellard             errno = ENOMEM;
360c8a706feSpbrook             goto fail;
361a03e2d42Sbellard         }
362a03e2d42Sbellard         /* Note: we prefer to control the mapping address. It is
363a03e2d42Sbellard            especially important if qemu_host_page_size >
364a03e2d42Sbellard            qemu_real_host_page_size */
365a03e2d42Sbellard         p = mmap(g2h(mmap_start),
366a03e2d42Sbellard                  host_len, prot, flags | MAP_FIXED, fd, host_offset);
36780210bcdSths         if (p == MAP_FAILED)
368c8a706feSpbrook             goto fail;
36954936004Sbellard         /* update start so that it points to the file position at 'offset' */
37080210bcdSths         host_start = (unsigned long)p;
37154936004Sbellard         if (!(flags & MAP_ANONYMOUS))
37253a5960aSpbrook             host_start += offset - host_offset;
37353a5960aSpbrook         start = h2g(host_start);
374a03e2d42Sbellard     } else {
3757ab240adSbalrog         int flg;
3767ab240adSbalrog         target_ulong addr;
3777ab240adSbalrog 
378e89f07d3Spbrook         if (start & ~TARGET_PAGE_MASK) {
379e89f07d3Spbrook             errno = EINVAL;
380c8a706feSpbrook             goto fail;
381e89f07d3Spbrook         }
38254936004Sbellard         end = start + len;
38353a5960aSpbrook         real_end = HOST_PAGE_ALIGN(end);
38454936004Sbellard 
385*45bc1f52Saurel32 	/*
386*45bc1f52Saurel32 	 * Test if requested memory area fits target address space
387*45bc1f52Saurel32 	 * It can fail only on 64-bit host with 32-bit target.
388*45bc1f52Saurel32 	 * On any other target/host host mmap() handles this error correctly.
389*45bc1f52Saurel32 	 */
390*45bc1f52Saurel32         if ((unsigned long)start + len - 1 > (abi_ulong) -1) {
391*45bc1f52Saurel32             errno = EINVAL;
392*45bc1f52Saurel32             goto fail;
393*45bc1f52Saurel32         }
394*45bc1f52Saurel32 
3957ab240adSbalrog         for(addr = real_start; addr < real_end; addr += TARGET_PAGE_SIZE) {
3967ab240adSbalrog             flg = page_get_flags(addr);
3977ab240adSbalrog             if (flg & PAGE_RESERVED) {
3987ab240adSbalrog                 errno = ENXIO;
399c8a706feSpbrook                 goto fail;
4007ab240adSbalrog             }
4017ab240adSbalrog         }
4027ab240adSbalrog 
40354936004Sbellard         /* worst case: we cannot map the file because the offset is not
40454936004Sbellard            aligned, so we read it */
40554936004Sbellard         if (!(flags & MAP_ANONYMOUS) &&
40683fb7adfSbellard             (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) {
40754936004Sbellard             /* msync() won't work here, so we return an error if write is
40854936004Sbellard                possible while it is a shared mapping */
40954936004Sbellard             if ((flags & MAP_TYPE) == MAP_SHARED &&
410e89f07d3Spbrook                 (prot & PROT_WRITE)) {
411e89f07d3Spbrook                 errno = EINVAL;
412c8a706feSpbrook                 goto fail;
413e89f07d3Spbrook             }
41454936004Sbellard             retaddr = target_mmap(start, len, prot | PROT_WRITE,
41554936004Sbellard                                   MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
41654936004Sbellard                                   -1, 0);
41754936004Sbellard             if (retaddr == -1)
418c8a706feSpbrook                 goto fail;
41953a5960aSpbrook             pread(fd, g2h(start), len, offset);
42054936004Sbellard             if (!(prot & PROT_WRITE)) {
42154936004Sbellard                 ret = target_mprotect(start, len, prot);
422c8a706feSpbrook                 if (ret != 0) {
423c8a706feSpbrook                     start = ret;
424c8a706feSpbrook                     goto the_end;
425c8a706feSpbrook                 }
42654936004Sbellard             }
42754936004Sbellard             goto the_end;
42854936004Sbellard         }
42954936004Sbellard 
43054936004Sbellard         /* handle the start of the mapping */
43153a5960aSpbrook         if (start > real_start) {
43253a5960aSpbrook             if (real_end == real_start + qemu_host_page_size) {
43354936004Sbellard                 /* one single host page */
43453a5960aSpbrook                 ret = mmap_frag(real_start, start, end,
43554936004Sbellard                                 prot, flags, fd, offset);
43654936004Sbellard                 if (ret == -1)
437c8a706feSpbrook                     goto fail;
43854936004Sbellard                 goto the_end1;
43954936004Sbellard             }
44053a5960aSpbrook             ret = mmap_frag(real_start, start, real_start + qemu_host_page_size,
44154936004Sbellard                             prot, flags, fd, offset);
44254936004Sbellard             if (ret == -1)
443c8a706feSpbrook                 goto fail;
44453a5960aSpbrook             real_start += qemu_host_page_size;
44554936004Sbellard         }
44654936004Sbellard         /* handle the end of the mapping */
44753a5960aSpbrook         if (end < real_end) {
44853a5960aSpbrook             ret = mmap_frag(real_end - qemu_host_page_size,
44953a5960aSpbrook                             real_end - qemu_host_page_size, real_end,
45054936004Sbellard                             prot, flags, fd,
45153a5960aSpbrook                             offset + real_end - qemu_host_page_size - start);
45254936004Sbellard             if (ret == -1)
453c8a706feSpbrook                 goto fail;
45453a5960aSpbrook             real_end -= qemu_host_page_size;
45554936004Sbellard         }
45654936004Sbellard 
45754936004Sbellard         /* map the middle (easier) */
45853a5960aSpbrook         if (real_start < real_end) {
45980210bcdSths             void *p;
4604a585ccbSbellard             unsigned long offset1;
4614a585ccbSbellard             if (flags & MAP_ANONYMOUS)
4624a585ccbSbellard                 offset1 = 0;
4634a585ccbSbellard             else
46453a5960aSpbrook                 offset1 = offset + real_start - start;
46580210bcdSths             p = mmap(g2h(real_start), real_end - real_start,
4664a585ccbSbellard                      prot, flags, fd, offset1);
46780210bcdSths             if (p == MAP_FAILED)
468c8a706feSpbrook                 goto fail;
46954936004Sbellard         }
470a03e2d42Sbellard     }
47154936004Sbellard  the_end1:
47254936004Sbellard     page_set_flags(start, start + len, prot | PAGE_VALID);
47354936004Sbellard  the_end:
47454936004Sbellard #ifdef DEBUG_MMAP
4752e0ded9cSedgar_igl     printf("ret=0x" TARGET_FMT_lx "\n", start);
47654936004Sbellard     page_dump(stdout);
47754936004Sbellard     printf("\n");
47854936004Sbellard #endif
479c8a706feSpbrook     mmap_unlock();
48054936004Sbellard     return start;
481c8a706feSpbrook fail:
482c8a706feSpbrook     mmap_unlock();
483c8a706feSpbrook     return -1;
48454936004Sbellard }
48554936004Sbellard 
486992f48a0Sblueswir1 int target_munmap(abi_ulong start, abi_ulong len)
48754936004Sbellard {
488992f48a0Sblueswir1     abi_ulong end, real_start, real_end, addr;
48954936004Sbellard     int prot, ret;
49054936004Sbellard 
49154936004Sbellard #ifdef DEBUG_MMAP
49254936004Sbellard     printf("munmap: start=0x%lx len=0x%lx\n", start, len);
49354936004Sbellard #endif
49454936004Sbellard     if (start & ~TARGET_PAGE_MASK)
49554936004Sbellard         return -EINVAL;
49654936004Sbellard     len = TARGET_PAGE_ALIGN(len);
49754936004Sbellard     if (len == 0)
49854936004Sbellard         return -EINVAL;
499c8a706feSpbrook     mmap_lock();
50054936004Sbellard     end = start + len;
50153a5960aSpbrook     real_start = start & qemu_host_page_mask;
50253a5960aSpbrook     real_end = HOST_PAGE_ALIGN(end);
50354936004Sbellard 
50453a5960aSpbrook     if (start > real_start) {
50554936004Sbellard         /* handle host page containing start */
50654936004Sbellard         prot = 0;
50753a5960aSpbrook         for(addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
50854936004Sbellard             prot |= page_get_flags(addr);
50954936004Sbellard         }
51053a5960aSpbrook         if (real_end == real_start + qemu_host_page_size) {
51153a5960aSpbrook             for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
512d418c81eSbellard                 prot |= page_get_flags(addr);
513d418c81eSbellard             }
51453a5960aSpbrook             end = real_end;
515d418c81eSbellard         }
51654936004Sbellard         if (prot != 0)
51753a5960aSpbrook             real_start += qemu_host_page_size;
51854936004Sbellard     }
51953a5960aSpbrook     if (end < real_end) {
52054936004Sbellard         prot = 0;
52153a5960aSpbrook         for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
52254936004Sbellard             prot |= page_get_flags(addr);
52354936004Sbellard         }
52454936004Sbellard         if (prot != 0)
52553a5960aSpbrook             real_end -= qemu_host_page_size;
52654936004Sbellard     }
52754936004Sbellard 
528c8a706feSpbrook     ret = 0;
52954936004Sbellard     /* unmap what we can */
53053a5960aSpbrook     if (real_start < real_end) {
5314118a970Sj_mayer         ret = munmap(g2h(real_start), real_end - real_start);
53254936004Sbellard     }
53354936004Sbellard 
534c8a706feSpbrook     if (ret == 0)
53554936004Sbellard         page_set_flags(start, start + len, 0);
536c8a706feSpbrook     mmap_unlock();
537c8a706feSpbrook     return ret;
53854936004Sbellard }
53954936004Sbellard 
54054936004Sbellard /* XXX: currently, we only handle MAP_ANONYMOUS and not MAP_FIXED
54154936004Sbellard    blocks which have been allocated starting on a host page */
542992f48a0Sblueswir1 abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
543992f48a0Sblueswir1                        abi_ulong new_size, unsigned long flags,
544992f48a0Sblueswir1                        abi_ulong new_addr)
54554936004Sbellard {
54654936004Sbellard     int prot;
547a5b85f79Sths     unsigned long host_addr;
54854936004Sbellard 
549c8a706feSpbrook     mmap_lock();
55054936004Sbellard     /* XXX: use 5 args syscall */
551a5b85f79Sths     host_addr = (long)mremap(g2h(old_addr), old_size, new_size, flags);
552c8a706feSpbrook     if (host_addr == -1) {
553c8a706feSpbrook         new_addr = -1;
554c8a706feSpbrook     } else {
555a5b85f79Sths         new_addr = h2g(host_addr);
55654936004Sbellard         prot = page_get_flags(old_addr);
55754936004Sbellard         page_set_flags(old_addr, old_addr + old_size, 0);
55854936004Sbellard         page_set_flags(new_addr, new_addr + new_size, prot | PAGE_VALID);
559c8a706feSpbrook     }
560c8a706feSpbrook     mmap_unlock();
56154936004Sbellard     return new_addr;
56254936004Sbellard }
56354936004Sbellard 
564992f48a0Sblueswir1 int target_msync(abi_ulong start, abi_ulong len, int flags)
56554936004Sbellard {
566992f48a0Sblueswir1     abi_ulong end;
56754936004Sbellard 
56854936004Sbellard     if (start & ~TARGET_PAGE_MASK)
56954936004Sbellard         return -EINVAL;
57054936004Sbellard     len = TARGET_PAGE_ALIGN(len);
57154936004Sbellard     end = start + len;
572d418c81eSbellard     if (end < start)
573d418c81eSbellard         return -EINVAL;
574d418c81eSbellard     if (end == start)
575d418c81eSbellard         return 0;
57654936004Sbellard 
57783fb7adfSbellard     start &= qemu_host_page_mask;
57853a5960aSpbrook     return msync(g2h(start), end - start, flags);
57954936004Sbellard }
580