xref: /qemu/linux-user/mmap.c (revision 530c0032)
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
178167ee88SBlue Swirl  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
1854936004Sbellard  */
1954936004Sbellard #include <stdlib.h>
2054936004Sbellard #include <stdio.h>
2154936004Sbellard #include <stdarg.h>
2254936004Sbellard #include <string.h>
2354936004Sbellard #include <unistd.h>
2454936004Sbellard #include <errno.h>
2554c5a2aeSedgar_igl #include <sys/types.h>
2654c5a2aeSedgar_igl #include <sys/stat.h>
2754936004Sbellard #include <sys/mman.h>
283af72a4dSblueswir1 #include <linux/mman.h>
293af72a4dSblueswir1 #include <linux/unistd.h>
3054936004Sbellard 
3154936004Sbellard #include "qemu.h"
3278f5bf1eSblueswir1 #include "qemu-common.h"
331652b974SPaolo Bonzini #include "translate-all.h"
3454936004Sbellard 
3554936004Sbellard //#define DEBUG_MMAP
3654936004Sbellard 
371e6eec8bSBlue Swirl static pthread_mutex_t mmap_mutex = PTHREAD_MUTEX_INITIALIZER;
38dfd3f85cSJuan Quintela static __thread int mmap_lock_count;
39c8a706feSpbrook 
40c8a706feSpbrook void mmap_lock(void)
41c8a706feSpbrook {
42c8a706feSpbrook     if (mmap_lock_count++ == 0) {
43c8a706feSpbrook         pthread_mutex_lock(&mmap_mutex);
44c8a706feSpbrook     }
45c8a706feSpbrook }
46c8a706feSpbrook 
47c8a706feSpbrook void mmap_unlock(void)
48c8a706feSpbrook {
49c8a706feSpbrook     if (--mmap_lock_count == 0) {
50c8a706feSpbrook         pthread_mutex_unlock(&mmap_mutex);
51c8a706feSpbrook     }
52c8a706feSpbrook }
53d5975363Spbrook 
54d5975363Spbrook /* Grab lock to make sure things are in a consistent state after fork().  */
55d5975363Spbrook void mmap_fork_start(void)
56d5975363Spbrook {
57d5975363Spbrook     if (mmap_lock_count)
58d5975363Spbrook         abort();
59d5975363Spbrook     pthread_mutex_lock(&mmap_mutex);
60d5975363Spbrook }
61d5975363Spbrook 
62d5975363Spbrook void mmap_fork_end(int child)
63d5975363Spbrook {
64d5975363Spbrook     if (child)
65d5975363Spbrook         pthread_mutex_init(&mmap_mutex, NULL);
66d5975363Spbrook     else
67d5975363Spbrook         pthread_mutex_unlock(&mmap_mutex);
68d5975363Spbrook }
69c8a706feSpbrook 
7053a5960aSpbrook /* NOTE: all the constants are the HOST ones, but addresses are target. */
71992f48a0Sblueswir1 int target_mprotect(abi_ulong start, abi_ulong len, int prot)
7254936004Sbellard {
73992f48a0Sblueswir1     abi_ulong end, host_start, host_end, addr;
7454936004Sbellard     int prot1, ret;
7554936004Sbellard 
7654936004Sbellard #ifdef DEBUG_MMAP
770bf9e31aSBlue Swirl     printf("mprotect: start=0x" TARGET_ABI_FMT_lx
780bf9e31aSBlue Swirl            "len=0x" TARGET_ABI_FMT_lx " prot=%c%c%c\n", start, len,
7954936004Sbellard            prot & PROT_READ ? 'r' : '-',
8054936004Sbellard            prot & PROT_WRITE ? 'w' : '-',
8154936004Sbellard            prot & PROT_EXEC ? 'x' : '-');
8254936004Sbellard #endif
8354936004Sbellard 
8454936004Sbellard     if ((start & ~TARGET_PAGE_MASK) != 0)
8554936004Sbellard         return -EINVAL;
8654936004Sbellard     len = TARGET_PAGE_ALIGN(len);
8754936004Sbellard     end = start + len;
8854936004Sbellard     if (end < start)
8954936004Sbellard         return -EINVAL;
90171cd1cdSbalrog     prot &= PROT_READ | PROT_WRITE | PROT_EXEC;
9154936004Sbellard     if (len == 0)
9254936004Sbellard         return 0;
9354936004Sbellard 
94c8a706feSpbrook     mmap_lock();
9583fb7adfSbellard     host_start = start & qemu_host_page_mask;
9654936004Sbellard     host_end = HOST_PAGE_ALIGN(end);
9754936004Sbellard     if (start > host_start) {
9854936004Sbellard         /* handle host page containing start */
9954936004Sbellard         prot1 = prot;
10054936004Sbellard         for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
10154936004Sbellard             prot1 |= page_get_flags(addr);
10254936004Sbellard         }
10383fb7adfSbellard         if (host_end == host_start + qemu_host_page_size) {
104d418c81eSbellard             for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
105d418c81eSbellard                 prot1 |= page_get_flags(addr);
106d418c81eSbellard             }
107d418c81eSbellard             end = host_end;
108d418c81eSbellard         }
10953a5960aSpbrook         ret = mprotect(g2h(host_start), qemu_host_page_size, prot1 & PAGE_BITS);
11054936004Sbellard         if (ret != 0)
111c8a706feSpbrook             goto error;
11283fb7adfSbellard         host_start += qemu_host_page_size;
11354936004Sbellard     }
11454936004Sbellard     if (end < host_end) {
11554936004Sbellard         prot1 = prot;
11654936004Sbellard         for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
11754936004Sbellard             prot1 |= page_get_flags(addr);
11854936004Sbellard         }
11953a5960aSpbrook         ret = mprotect(g2h(host_end - qemu_host_page_size), qemu_host_page_size,
12054936004Sbellard                        prot1 & PAGE_BITS);
12154936004Sbellard         if (ret != 0)
122c8a706feSpbrook             goto error;
12383fb7adfSbellard         host_end -= qemu_host_page_size;
12454936004Sbellard     }
12554936004Sbellard 
12654936004Sbellard     /* handle the pages in the middle */
12754936004Sbellard     if (host_start < host_end) {
12853a5960aSpbrook         ret = mprotect(g2h(host_start), host_end - host_start, prot);
12954936004Sbellard         if (ret != 0)
130c8a706feSpbrook             goto error;
13154936004Sbellard     }
13254936004Sbellard     page_set_flags(start, start + len, prot | PAGE_VALID);
133c8a706feSpbrook     mmap_unlock();
13454936004Sbellard     return 0;
135c8a706feSpbrook error:
136c8a706feSpbrook     mmap_unlock();
137c8a706feSpbrook     return ret;
13854936004Sbellard }
13954936004Sbellard 
14054936004Sbellard /* map an incomplete host page */
141992f48a0Sblueswir1 static int mmap_frag(abi_ulong real_start,
142992f48a0Sblueswir1                      abi_ulong start, abi_ulong end,
143992f48a0Sblueswir1                      int prot, int flags, int fd, abi_ulong offset)
14454936004Sbellard {
14580210bcdSths     abi_ulong real_end, addr;
14653a5960aSpbrook     void *host_start;
14754936004Sbellard     int prot1, prot_new;
14854936004Sbellard 
14953a5960aSpbrook     real_end = real_start + qemu_host_page_size;
15053a5960aSpbrook     host_start = g2h(real_start);
15154936004Sbellard 
15254936004Sbellard     /* get the protection of the target pages outside the mapping */
15354936004Sbellard     prot1 = 0;
15453a5960aSpbrook     for(addr = real_start; addr < real_end; addr++) {
15554936004Sbellard         if (addr < start || addr >= end)
15654936004Sbellard             prot1 |= page_get_flags(addr);
15754936004Sbellard     }
15854936004Sbellard 
15954936004Sbellard     if (prot1 == 0) {
16054936004Sbellard         /* no page was there, so we allocate one */
16180210bcdSths         void *p = mmap(host_start, qemu_host_page_size, prot,
16254936004Sbellard                        flags | MAP_ANONYMOUS, -1, 0);
16380210bcdSths         if (p == MAP_FAILED)
16480210bcdSths             return -1;
16553a5960aSpbrook         prot1 = prot;
16654936004Sbellard     }
16754936004Sbellard     prot1 &= PAGE_BITS;
16854936004Sbellard 
16954936004Sbellard     prot_new = prot | prot1;
17054936004Sbellard     if (!(flags & MAP_ANONYMOUS)) {
17154936004Sbellard         /* msync() won't work here, so we return an error if write is
17254936004Sbellard            possible while it is a shared mapping */
17354936004Sbellard         if ((flags & MAP_TYPE) == MAP_SHARED &&
17454936004Sbellard             (prot & PROT_WRITE))
175ee636500SJuan Quintela             return -1;
17654936004Sbellard 
17754936004Sbellard         /* adjust protection to be able to read */
17854936004Sbellard         if (!(prot1 & PROT_WRITE))
17953a5960aSpbrook             mprotect(host_start, qemu_host_page_size, prot1 | PROT_WRITE);
18054936004Sbellard 
18154936004Sbellard         /* read the corresponding file data */
182fb7e378cSKirill A. Shutemov         if (pread(fd, g2h(start), end - start, offset) == -1)
183fb7e378cSKirill A. Shutemov             return -1;
18454936004Sbellard 
18554936004Sbellard         /* put final protection */
18654936004Sbellard         if (prot_new != (prot1 | PROT_WRITE))
18753a5960aSpbrook             mprotect(host_start, qemu_host_page_size, prot_new);
18854936004Sbellard     } else {
18954936004Sbellard         if (prot_new != prot1) {
19053a5960aSpbrook             mprotect(host_start, qemu_host_page_size, prot_new);
19154936004Sbellard         }
192e6deac9cSChen Gang         if (prot_new & PROT_WRITE) {
193e6deac9cSChen Gang             memset(g2h(start), 0, end - start);
194e6deac9cSChen Gang         }
19554936004Sbellard     }
19654936004Sbellard     return 0;
19754936004Sbellard }
19854936004Sbellard 
19914f24e14SRichard Henderson #if HOST_LONG_BITS == 64 && TARGET_ABI_BITS == 64
20014f24e14SRichard Henderson # define TASK_UNMAPPED_BASE  (1ul << 38)
20114f24e14SRichard Henderson #elif defined(__CYGWIN__)
202a03e2d42Sbellard /* Cygwin doesn't have a whole lot of address space.  */
20314f24e14SRichard Henderson # define TASK_UNMAPPED_BASE  0x18000000
204a03e2d42Sbellard #else
20514f24e14SRichard Henderson # define TASK_UNMAPPED_BASE  0x40000000
206a03e2d42Sbellard #endif
20759e9d91cSPeter Maydell abi_ulong mmap_next_start = TASK_UNMAPPED_BASE;
208a03e2d42Sbellard 
2090776590dSpbrook unsigned long last_brk;
2100776590dSpbrook 
21168a1c816SPaul Brook /* Subroutine of mmap_find_vma, used when we have pre-allocated a chunk
21268a1c816SPaul Brook    of guest address space.  */
21368a1c816SPaul Brook static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size)
21468a1c816SPaul Brook {
21568a1c816SPaul Brook     abi_ulong addr;
21659e9d91cSPeter Maydell     abi_ulong end_addr;
21768a1c816SPaul Brook     int prot;
21868a1c816SPaul Brook     int looped = 0;
21968a1c816SPaul Brook 
220b76f21a7SLaurent Vivier     if (size > reserved_va) {
22168a1c816SPaul Brook         return (abi_ulong)-1;
22268a1c816SPaul Brook     }
22368a1c816SPaul Brook 
22459e9d91cSPeter Maydell     size = HOST_PAGE_ALIGN(size);
22559e9d91cSPeter Maydell     end_addr = start + size;
226b76f21a7SLaurent Vivier     if (end_addr > reserved_va) {
227b76f21a7SLaurent Vivier         end_addr = reserved_va;
22859e9d91cSPeter Maydell     }
22959e9d91cSPeter Maydell     addr = end_addr - qemu_host_page_size;
23059e9d91cSPeter Maydell 
23159e9d91cSPeter Maydell     while (1) {
23259e9d91cSPeter Maydell         if (addr > end_addr) {
23368a1c816SPaul Brook             if (looped) {
23468a1c816SPaul Brook                 return (abi_ulong)-1;
23568a1c816SPaul Brook             }
236b76f21a7SLaurent Vivier             end_addr = reserved_va;
23759e9d91cSPeter Maydell             addr = end_addr - qemu_host_page_size;
23868a1c816SPaul Brook             looped = 1;
23968a1c816SPaul Brook             continue;
24068a1c816SPaul Brook         }
24168a1c816SPaul Brook         prot = page_get_flags(addr);
24268a1c816SPaul Brook         if (prot) {
24359e9d91cSPeter Maydell             end_addr = addr;
24468a1c816SPaul Brook         }
24559e9d91cSPeter Maydell         if (addr + size == end_addr) {
24659e9d91cSPeter Maydell             break;
24768a1c816SPaul Brook         }
24859e9d91cSPeter Maydell         addr -= qemu_host_page_size;
24959e9d91cSPeter Maydell     }
25059e9d91cSPeter Maydell 
25159e9d91cSPeter Maydell     if (start == mmap_next_start) {
25268a1c816SPaul Brook         mmap_next_start = addr;
25359e9d91cSPeter Maydell     }
25459e9d91cSPeter Maydell 
25559e9d91cSPeter Maydell     return addr;
25668a1c816SPaul Brook }
25768a1c816SPaul Brook 
258fe3b4152SKirill A. Shutemov /*
259fe3b4152SKirill A. Shutemov  * Find and reserve a free memory area of size 'size'. The search
260fe3b4152SKirill A. Shutemov  * starts at 'start'.
261fe3b4152SKirill A. Shutemov  * It must be called with mmap_lock() held.
262fe3b4152SKirill A. Shutemov  * Return -1 if error.
263a03e2d42Sbellard  */
2649ad197d9SRiku Voipio abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
265a03e2d42Sbellard {
26614f24e14SRichard Henderson     void *ptr, *prev;
267fe3b4152SKirill A. Shutemov     abi_ulong addr;
26814f24e14SRichard Henderson     int wrapped, repeat;
269fe3b4152SKirill A. Shutemov 
270fe3b4152SKirill A. Shutemov     /* If 'start' == 0, then a default start address is used. */
27114f24e14SRichard Henderson     if (start == 0) {
272fe3b4152SKirill A. Shutemov         start = mmap_next_start;
27314f24e14SRichard Henderson     } else {
27414f24e14SRichard Henderson         start &= qemu_host_page_mask;
27514f24e14SRichard Henderson     }
27614f24e14SRichard Henderson 
27714f24e14SRichard Henderson     size = HOST_PAGE_ALIGN(size);
278fe3b4152SKirill A. Shutemov 
279b76f21a7SLaurent Vivier     if (reserved_va) {
28068a1c816SPaul Brook         return mmap_find_vma_reserved(start, size);
28168a1c816SPaul Brook     }
28268a1c816SPaul Brook 
283a03e2d42Sbellard     addr = start;
28414f24e14SRichard Henderson     wrapped = repeat = 0;
28514f24e14SRichard Henderson     prev = 0;
286fe3b4152SKirill A. Shutemov 
28714f24e14SRichard Henderson     for (;; prev = ptr) {
288fe3b4152SKirill A. Shutemov         /*
289fe3b4152SKirill A. Shutemov          * Reserve needed memory area to avoid a race.
290fe3b4152SKirill A. Shutemov          * It should be discarded using:
291fe3b4152SKirill A. Shutemov          *  - mmap() with MAP_FIXED flag
292fe3b4152SKirill A. Shutemov          *  - mremap() with MREMAP_FIXED flag
293fe3b4152SKirill A. Shutemov          *  - shmat() with SHM_REMAP flag
294fe3b4152SKirill A. Shutemov          */
29514f24e14SRichard Henderson         ptr = mmap(g2h(addr), size, PROT_NONE,
296fe3b4152SKirill A. Shutemov                    MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE, -1, 0);
297fe3b4152SKirill A. Shutemov 
298fe3b4152SKirill A. Shutemov         /* ENOMEM, if host address space has no memory */
29914f24e14SRichard Henderson         if (ptr == MAP_FAILED) {
300a03e2d42Sbellard             return (abi_ulong)-1;
301a03e2d42Sbellard         }
302fe3b4152SKirill A. Shutemov 
30314f24e14SRichard Henderson         /* Count the number of sequential returns of the same address.
30414f24e14SRichard Henderson            This is used to modify the search algorithm below.  */
30514f24e14SRichard Henderson         repeat = (ptr == prev ? repeat + 1 : 0);
306fe3b4152SKirill A. Shutemov 
30714f24e14SRichard Henderson         if (h2g_valid(ptr + size - 1)) {
30814f24e14SRichard Henderson             addr = h2g(ptr);
30914f24e14SRichard Henderson 
31014f24e14SRichard Henderson             if ((addr & ~TARGET_PAGE_MASK) == 0) {
31114f24e14SRichard Henderson                 /* Success.  */
31214f24e14SRichard Henderson                 if (start == mmap_next_start && addr >= TASK_UNMAPPED_BASE) {
31314f24e14SRichard Henderson                     mmap_next_start = addr + size;
31414f24e14SRichard Henderson                 }
31514f24e14SRichard Henderson                 return addr;
31614f24e14SRichard Henderson             }
31714f24e14SRichard Henderson 
31814f24e14SRichard Henderson             /* The address is not properly aligned for the target.  */
31914f24e14SRichard Henderson             switch (repeat) {
32014f24e14SRichard Henderson             case 0:
32114f24e14SRichard Henderson                 /* Assume the result that the kernel gave us is the
32214f24e14SRichard Henderson                    first with enough free space, so start again at the
32314f24e14SRichard Henderson                    next higher target page.  */
32414f24e14SRichard Henderson                 addr = TARGET_PAGE_ALIGN(addr);
32514f24e14SRichard Henderson                 break;
32614f24e14SRichard Henderson             case 1:
32714f24e14SRichard Henderson                 /* Sometimes the kernel decides to perform the allocation
32814f24e14SRichard Henderson                    at the top end of memory instead.  */
32914f24e14SRichard Henderson                 addr &= TARGET_PAGE_MASK;
33014f24e14SRichard Henderson                 break;
33114f24e14SRichard Henderson             case 2:
33214f24e14SRichard Henderson                 /* Start over at low memory.  */
33314f24e14SRichard Henderson                 addr = 0;
33414f24e14SRichard Henderson                 break;
33514f24e14SRichard Henderson             default:
33614f24e14SRichard Henderson                 /* Fail.  This unaligned block must the last.  */
33714f24e14SRichard Henderson                 addr = -1;
33814f24e14SRichard Henderson                 break;
33914f24e14SRichard Henderson             }
34014f24e14SRichard Henderson         } else {
34114f24e14SRichard Henderson             /* Since the result the kernel gave didn't fit, start
34214f24e14SRichard Henderson                again at low memory.  If any repetition, fail.  */
34314f24e14SRichard Henderson             addr = (repeat ? -1 : 0);
34414f24e14SRichard Henderson         }
34514f24e14SRichard Henderson 
34614f24e14SRichard Henderson         /* Unmap and try again.  */
34714f24e14SRichard Henderson         munmap(ptr, size);
34814f24e14SRichard Henderson 
34914f24e14SRichard Henderson         /* ENOMEM if we checked the whole of the target address space.  */
350d0b3e4f5SBlue Swirl         if (addr == (abi_ulong)-1) {
35114f24e14SRichard Henderson             return (abi_ulong)-1;
35214f24e14SRichard Henderson         } else if (addr == 0) {
35314f24e14SRichard Henderson             if (wrapped) {
35414f24e14SRichard Henderson                 return (abi_ulong)-1;
35514f24e14SRichard Henderson             }
35614f24e14SRichard Henderson             wrapped = 1;
35714f24e14SRichard Henderson             /* Don't actually use 0 when wrapping, instead indicate
3588186e783SStefan Weil                that we'd truly like an allocation in low memory.  */
35914f24e14SRichard Henderson             addr = (mmap_min_addr > TARGET_PAGE_SIZE
36014f24e14SRichard Henderson                      ? TARGET_PAGE_ALIGN(mmap_min_addr)
36114f24e14SRichard Henderson                      : TARGET_PAGE_SIZE);
36214f24e14SRichard Henderson         } else if (wrapped && addr >= start) {
36314f24e14SRichard Henderson             return (abi_ulong)-1;
36414f24e14SRichard Henderson         }
36514f24e14SRichard Henderson     }
366a03e2d42Sbellard }
367a03e2d42Sbellard 
36854936004Sbellard /* NOTE: all the constants are the HOST ones */
369992f48a0Sblueswir1 abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
370992f48a0Sblueswir1                      int flags, int fd, abi_ulong offset)
37154936004Sbellard {
372992f48a0Sblueswir1     abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len;
37354936004Sbellard 
374c8a706feSpbrook     mmap_lock();
37554936004Sbellard #ifdef DEBUG_MMAP
37654936004Sbellard     {
3770bf9e31aSBlue Swirl         printf("mmap: start=0x" TARGET_ABI_FMT_lx
3780bf9e31aSBlue Swirl                " len=0x" TARGET_ABI_FMT_lx " prot=%c%c%c flags=",
37954936004Sbellard                start, len,
38054936004Sbellard                prot & PROT_READ ? 'r' : '-',
38154936004Sbellard                prot & PROT_WRITE ? 'w' : '-',
38254936004Sbellard                prot & PROT_EXEC ? 'x' : '-');
38354936004Sbellard         if (flags & MAP_FIXED)
38454936004Sbellard             printf("MAP_FIXED ");
38554936004Sbellard         if (flags & MAP_ANONYMOUS)
38654936004Sbellard             printf("MAP_ANON ");
38754936004Sbellard         switch(flags & MAP_TYPE) {
38854936004Sbellard         case MAP_PRIVATE:
38954936004Sbellard             printf("MAP_PRIVATE ");
39054936004Sbellard             break;
39154936004Sbellard         case MAP_SHARED:
39254936004Sbellard             printf("MAP_SHARED ");
39354936004Sbellard             break;
39454936004Sbellard         default:
39554936004Sbellard             printf("[MAP_TYPE=0x%x] ", flags & MAP_TYPE);
39654936004Sbellard             break;
39754936004Sbellard         }
3980bf9e31aSBlue Swirl         printf("fd=%d offset=" TARGET_ABI_FMT_lx "\n", fd, offset);
39954936004Sbellard     }
40054936004Sbellard #endif
40154936004Sbellard 
402e89f07d3Spbrook     if (offset & ~TARGET_PAGE_MASK) {
403e89f07d3Spbrook         errno = EINVAL;
404c8a706feSpbrook         goto fail;
405e89f07d3Spbrook     }
40654936004Sbellard 
40754936004Sbellard     len = TARGET_PAGE_ALIGN(len);
40854936004Sbellard     if (len == 0)
409c8a706feSpbrook         goto the_end;
41053a5960aSpbrook     real_start = start & qemu_host_page_mask;
411a5e7ee46SRichard Henderson     host_offset = offset & qemu_host_page_mask;
412a5e7ee46SRichard Henderson 
413a5e7ee46SRichard Henderson     /* If the user is asking for the kernel to find a location, do that
414a5e7ee46SRichard Henderson        before we truncate the length for mapping files below.  */
415a5e7ee46SRichard Henderson     if (!(flags & MAP_FIXED)) {
416a5e7ee46SRichard Henderson         host_len = len + offset - host_offset;
417a5e7ee46SRichard Henderson         host_len = HOST_PAGE_ALIGN(host_len);
418a5e7ee46SRichard Henderson         start = mmap_find_vma(real_start, host_len);
419a5e7ee46SRichard Henderson         if (start == (abi_ulong)-1) {
420a5e7ee46SRichard Henderson             errno = ENOMEM;
421a5e7ee46SRichard Henderson             goto fail;
422a5e7ee46SRichard Henderson         }
423a5e7ee46SRichard Henderson     }
42454936004Sbellard 
42554c5a2aeSedgar_igl     /* When mapping files into a memory area larger than the file, accesses
42654c5a2aeSedgar_igl        to pages beyond the file size will cause a SIGBUS.
42754c5a2aeSedgar_igl 
42854c5a2aeSedgar_igl        For example, if mmaping a file of 100 bytes on a host with 4K pages
42954c5a2aeSedgar_igl        emulating a target with 8K pages, the target expects to be able to
43054c5a2aeSedgar_igl        access the first 8K. But the host will trap us on any access beyond
43154c5a2aeSedgar_igl        4K.
43254c5a2aeSedgar_igl 
43354c5a2aeSedgar_igl        When emulating a target with a larger page-size than the hosts, we
43454c5a2aeSedgar_igl        may need to truncate file maps at EOF and add extra anonymous pages
43554c5a2aeSedgar_igl        up to the targets page boundary.  */
43654c5a2aeSedgar_igl 
43754c5a2aeSedgar_igl     if ((qemu_real_host_page_size < TARGET_PAGE_SIZE)
43854c5a2aeSedgar_igl         && !(flags & MAP_ANONYMOUS)) {
43954c5a2aeSedgar_igl        struct stat sb;
44054c5a2aeSedgar_igl 
44154c5a2aeSedgar_igl        if (fstat (fd, &sb) == -1)
44254c5a2aeSedgar_igl            goto fail;
44354c5a2aeSedgar_igl 
44454c5a2aeSedgar_igl        /* Are we trying to create a map beyond EOF?.  */
44554c5a2aeSedgar_igl        if (offset + len > sb.st_size) {
44654c5a2aeSedgar_igl            /* If so, truncate the file map at eof aligned with
44754c5a2aeSedgar_igl               the hosts real pagesize. Additional anonymous maps
44854c5a2aeSedgar_igl               will be created beyond EOF.  */
4490c2d70c4SPaolo Bonzini            len = REAL_HOST_PAGE_ALIGN(sb.st_size - offset);
45054c5a2aeSedgar_igl        }
45154c5a2aeSedgar_igl     }
45254c5a2aeSedgar_igl 
45354936004Sbellard     if (!(flags & MAP_FIXED)) {
454a5e7ee46SRichard Henderson         unsigned long host_start;
455a03e2d42Sbellard         void *p;
456a5e7ee46SRichard Henderson 
45754936004Sbellard         host_len = len + offset - host_offset;
458a03e2d42Sbellard         host_len = HOST_PAGE_ALIGN(host_len);
459a5e7ee46SRichard Henderson 
460a03e2d42Sbellard         /* Note: we prefer to control the mapping address. It is
461a03e2d42Sbellard            especially important if qemu_host_page_size >
462a03e2d42Sbellard            qemu_real_host_page_size */
463a5e7ee46SRichard Henderson         p = mmap(g2h(start), host_len, prot,
464a5e7ee46SRichard Henderson                  flags | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
46580210bcdSths         if (p == MAP_FAILED)
466c8a706feSpbrook             goto fail;
46754936004Sbellard         /* update start so that it points to the file position at 'offset' */
46880210bcdSths         host_start = (unsigned long)p;
46954c5a2aeSedgar_igl         if (!(flags & MAP_ANONYMOUS)) {
470a5e7ee46SRichard Henderson             p = mmap(g2h(start), len, prot,
47154c5a2aeSedgar_igl                      flags | MAP_FIXED, fd, host_offset);
4728384274eSJürg Billeter             if (p == MAP_FAILED) {
4738384274eSJürg Billeter                 munmap(g2h(start), host_len);
4748384274eSJürg Billeter                 goto fail;
4758384274eSJürg Billeter             }
47653a5960aSpbrook             host_start += offset - host_offset;
47754c5a2aeSedgar_igl         }
47853a5960aSpbrook         start = h2g(host_start);
479a03e2d42Sbellard     } else {
480e89f07d3Spbrook         if (start & ~TARGET_PAGE_MASK) {
481e89f07d3Spbrook             errno = EINVAL;
482c8a706feSpbrook             goto fail;
483e89f07d3Spbrook         }
48454936004Sbellard         end = start + len;
48553a5960aSpbrook         real_end = HOST_PAGE_ALIGN(end);
48654936004Sbellard 
48745bc1f52Saurel32 	/*
48845bc1f52Saurel32 	 * Test if requested memory area fits target address space
48945bc1f52Saurel32 	 * It can fail only on 64-bit host with 32-bit target.
49045bc1f52Saurel32 	 * On any other target/host host mmap() handles this error correctly.
49145bc1f52Saurel32 	 */
49245bc1f52Saurel32         if ((unsigned long)start + len - 1 > (abi_ulong) -1) {
49345bc1f52Saurel32             errno = EINVAL;
49445bc1f52Saurel32             goto fail;
49545bc1f52Saurel32         }
49645bc1f52Saurel32 
49754936004Sbellard         /* worst case: we cannot map the file because the offset is not
49854936004Sbellard            aligned, so we read it */
49954936004Sbellard         if (!(flags & MAP_ANONYMOUS) &&
50083fb7adfSbellard             (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) {
50154936004Sbellard             /* msync() won't work here, so we return an error if write is
50254936004Sbellard                possible while it is a shared mapping */
50354936004Sbellard             if ((flags & MAP_TYPE) == MAP_SHARED &&
504e89f07d3Spbrook                 (prot & PROT_WRITE)) {
505e89f07d3Spbrook                 errno = EINVAL;
506c8a706feSpbrook                 goto fail;
507e89f07d3Spbrook             }
50854936004Sbellard             retaddr = target_mmap(start, len, prot | PROT_WRITE,
50954936004Sbellard                                   MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
51054936004Sbellard                                   -1, 0);
51154936004Sbellard             if (retaddr == -1)
512c8a706feSpbrook                 goto fail;
513fb7e378cSKirill A. Shutemov             if (pread(fd, g2h(start), len, offset) == -1)
514fb7e378cSKirill A. Shutemov                 goto fail;
51554936004Sbellard             if (!(prot & PROT_WRITE)) {
51654936004Sbellard                 ret = target_mprotect(start, len, prot);
51786abac06SPaolo Bonzini                 assert(ret == 0);
51854936004Sbellard             }
51954936004Sbellard             goto the_end;
52054936004Sbellard         }
52154936004Sbellard 
52254936004Sbellard         /* handle the start of the mapping */
52353a5960aSpbrook         if (start > real_start) {
52453a5960aSpbrook             if (real_end == real_start + qemu_host_page_size) {
52554936004Sbellard                 /* one single host page */
52653a5960aSpbrook                 ret = mmap_frag(real_start, start, end,
52754936004Sbellard                                 prot, flags, fd, offset);
52854936004Sbellard                 if (ret == -1)
529c8a706feSpbrook                     goto fail;
53054936004Sbellard                 goto the_end1;
53154936004Sbellard             }
53253a5960aSpbrook             ret = mmap_frag(real_start, start, real_start + qemu_host_page_size,
53354936004Sbellard                             prot, flags, fd, offset);
53454936004Sbellard             if (ret == -1)
535c8a706feSpbrook                 goto fail;
53653a5960aSpbrook             real_start += qemu_host_page_size;
53754936004Sbellard         }
53854936004Sbellard         /* handle the end of the mapping */
53953a5960aSpbrook         if (end < real_end) {
54053a5960aSpbrook             ret = mmap_frag(real_end - qemu_host_page_size,
541*530c0032SChen Gang                             real_end - qemu_host_page_size, end,
54254936004Sbellard                             prot, flags, fd,
54353a5960aSpbrook                             offset + real_end - qemu_host_page_size - start);
54454936004Sbellard             if (ret == -1)
545c8a706feSpbrook                 goto fail;
54653a5960aSpbrook             real_end -= qemu_host_page_size;
54754936004Sbellard         }
54854936004Sbellard 
54954936004Sbellard         /* map the middle (easier) */
55053a5960aSpbrook         if (real_start < real_end) {
55180210bcdSths             void *p;
5524a585ccbSbellard             unsigned long offset1;
5534a585ccbSbellard             if (flags & MAP_ANONYMOUS)
5544a585ccbSbellard                 offset1 = 0;
5554a585ccbSbellard             else
55653a5960aSpbrook                 offset1 = offset + real_start - start;
55780210bcdSths             p = mmap(g2h(real_start), real_end - real_start,
5584a585ccbSbellard                      prot, flags, fd, offset1);
55980210bcdSths             if (p == MAP_FAILED)
560c8a706feSpbrook                 goto fail;
56154936004Sbellard         }
562a03e2d42Sbellard     }
56354936004Sbellard  the_end1:
56454936004Sbellard     page_set_flags(start, start + len, prot | PAGE_VALID);
56554936004Sbellard  the_end:
56654936004Sbellard #ifdef DEBUG_MMAP
5670bf9e31aSBlue Swirl     printf("ret=0x" TARGET_ABI_FMT_lx "\n", start);
56854936004Sbellard     page_dump(stdout);
56954936004Sbellard     printf("\n");
57054936004Sbellard #endif
57135865339SPaolo Bonzini     tb_invalidate_phys_range(start, start + len);
572c8a706feSpbrook     mmap_unlock();
57354936004Sbellard     return start;
574c8a706feSpbrook fail:
575c8a706feSpbrook     mmap_unlock();
576c8a706feSpbrook     return -1;
57754936004Sbellard }
57854936004Sbellard 
57968a1c816SPaul Brook static void mmap_reserve(abi_ulong start, abi_ulong size)
58068a1c816SPaul Brook {
58168a1c816SPaul Brook     abi_ulong real_start;
58268a1c816SPaul Brook     abi_ulong real_end;
58368a1c816SPaul Brook     abi_ulong addr;
58468a1c816SPaul Brook     abi_ulong end;
58568a1c816SPaul Brook     int prot;
58668a1c816SPaul Brook 
58768a1c816SPaul Brook     real_start = start & qemu_host_page_mask;
58868a1c816SPaul Brook     real_end = HOST_PAGE_ALIGN(start + size);
58968a1c816SPaul Brook     end = start + size;
59068a1c816SPaul Brook     if (start > real_start) {
59168a1c816SPaul Brook         /* handle host page containing start */
59268a1c816SPaul Brook         prot = 0;
59368a1c816SPaul Brook         for (addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
59468a1c816SPaul Brook             prot |= page_get_flags(addr);
59568a1c816SPaul Brook         }
59668a1c816SPaul Brook         if (real_end == real_start + qemu_host_page_size) {
59768a1c816SPaul Brook             for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
59868a1c816SPaul Brook                 prot |= page_get_flags(addr);
59968a1c816SPaul Brook             }
60068a1c816SPaul Brook             end = real_end;
60168a1c816SPaul Brook         }
60268a1c816SPaul Brook         if (prot != 0)
60368a1c816SPaul Brook             real_start += qemu_host_page_size;
60468a1c816SPaul Brook     }
60568a1c816SPaul Brook     if (end < real_end) {
60668a1c816SPaul Brook         prot = 0;
60768a1c816SPaul Brook         for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
60868a1c816SPaul Brook             prot |= page_get_flags(addr);
60968a1c816SPaul Brook         }
61068a1c816SPaul Brook         if (prot != 0)
61168a1c816SPaul Brook             real_end -= qemu_host_page_size;
61268a1c816SPaul Brook     }
61368a1c816SPaul Brook     if (real_start != real_end) {
61468a1c816SPaul Brook         mmap(g2h(real_start), real_end - real_start, PROT_NONE,
61568a1c816SPaul Brook                  MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE,
61668a1c816SPaul Brook                  -1, 0);
61768a1c816SPaul Brook     }
61868a1c816SPaul Brook }
61968a1c816SPaul Brook 
620992f48a0Sblueswir1 int target_munmap(abi_ulong start, abi_ulong len)
62154936004Sbellard {
622992f48a0Sblueswir1     abi_ulong end, real_start, real_end, addr;
62354936004Sbellard     int prot, ret;
62454936004Sbellard 
62554936004Sbellard #ifdef DEBUG_MMAP
6260bf9e31aSBlue Swirl     printf("munmap: start=0x" TARGET_ABI_FMT_lx " len=0x"
6270bf9e31aSBlue Swirl            TARGET_ABI_FMT_lx "\n",
6280bf9e31aSBlue Swirl            start, len);
62954936004Sbellard #endif
63054936004Sbellard     if (start & ~TARGET_PAGE_MASK)
63154936004Sbellard         return -EINVAL;
63254936004Sbellard     len = TARGET_PAGE_ALIGN(len);
63354936004Sbellard     if (len == 0)
63454936004Sbellard         return -EINVAL;
635c8a706feSpbrook     mmap_lock();
63654936004Sbellard     end = start + len;
63753a5960aSpbrook     real_start = start & qemu_host_page_mask;
63853a5960aSpbrook     real_end = HOST_PAGE_ALIGN(end);
63954936004Sbellard 
64053a5960aSpbrook     if (start > real_start) {
64154936004Sbellard         /* handle host page containing start */
64254936004Sbellard         prot = 0;
64353a5960aSpbrook         for(addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
64454936004Sbellard             prot |= page_get_flags(addr);
64554936004Sbellard         }
64653a5960aSpbrook         if (real_end == real_start + qemu_host_page_size) {
64753a5960aSpbrook             for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
648d418c81eSbellard                 prot |= page_get_flags(addr);
649d418c81eSbellard             }
65053a5960aSpbrook             end = real_end;
651d418c81eSbellard         }
65254936004Sbellard         if (prot != 0)
65353a5960aSpbrook             real_start += qemu_host_page_size;
65454936004Sbellard     }
65553a5960aSpbrook     if (end < real_end) {
65654936004Sbellard         prot = 0;
65753a5960aSpbrook         for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
65854936004Sbellard             prot |= page_get_flags(addr);
65954936004Sbellard         }
66054936004Sbellard         if (prot != 0)
66153a5960aSpbrook             real_end -= qemu_host_page_size;
66254936004Sbellard     }
66354936004Sbellard 
664c8a706feSpbrook     ret = 0;
66554936004Sbellard     /* unmap what we can */
66653a5960aSpbrook     if (real_start < real_end) {
667b76f21a7SLaurent Vivier         if (reserved_va) {
66868a1c816SPaul Brook             mmap_reserve(real_start, real_end - real_start);
66968a1c816SPaul Brook         } else {
6704118a970Sj_mayer             ret = munmap(g2h(real_start), real_end - real_start);
67154936004Sbellard         }
67268a1c816SPaul Brook     }
67354936004Sbellard 
67477a8f1a5SAlexander Graf     if (ret == 0) {
67554936004Sbellard         page_set_flags(start, start + len, 0);
67635865339SPaolo Bonzini         tb_invalidate_phys_range(start, start + len);
67777a8f1a5SAlexander Graf     }
678c8a706feSpbrook     mmap_unlock();
679c8a706feSpbrook     return ret;
68054936004Sbellard }
68154936004Sbellard 
682992f48a0Sblueswir1 abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
683992f48a0Sblueswir1                        abi_ulong new_size, unsigned long flags,
684992f48a0Sblueswir1                        abi_ulong new_addr)
68554936004Sbellard {
68654936004Sbellard     int prot;
687f19412a2Saurel32     void *host_addr;
68854936004Sbellard 
689c8a706feSpbrook     mmap_lock();
690f19412a2Saurel32 
69168a1c816SPaul Brook     if (flags & MREMAP_FIXED) {
6923af72a4dSblueswir1         host_addr = (void *) syscall(__NR_mremap, g2h(old_addr),
6933af72a4dSblueswir1                                      old_size, new_size,
6943af72a4dSblueswir1                                      flags,
69568a1c816SPaul Brook                                      g2h(new_addr));
69668a1c816SPaul Brook 
697b76f21a7SLaurent Vivier         if (reserved_va && host_addr != MAP_FAILED) {
69868a1c816SPaul Brook             /* If new and old addresses overlap then the above mremap will
69968a1c816SPaul Brook                already have failed with EINVAL.  */
70068a1c816SPaul Brook             mmap_reserve(old_addr, old_size);
70168a1c816SPaul Brook         }
70268a1c816SPaul Brook     } else if (flags & MREMAP_MAYMOVE) {
703f19412a2Saurel32         abi_ulong mmap_start;
704f19412a2Saurel32 
705f19412a2Saurel32         mmap_start = mmap_find_vma(0, new_size);
706f19412a2Saurel32 
707f19412a2Saurel32         if (mmap_start == -1) {
708f19412a2Saurel32             errno = ENOMEM;
709f19412a2Saurel32             host_addr = MAP_FAILED;
71068a1c816SPaul Brook         } else {
7113af72a4dSblueswir1             host_addr = (void *) syscall(__NR_mremap, g2h(old_addr),
7123af72a4dSblueswir1                                          old_size, new_size,
7133af72a4dSblueswir1                                          flags | MREMAP_FIXED,
7143af72a4dSblueswir1                                          g2h(mmap_start));
715b76f21a7SLaurent Vivier             if (reserved_va) {
71668a1c816SPaul Brook                 mmap_reserve(old_addr, old_size);
71768a1c816SPaul Brook             }
718c65ffe6dSamateur         }
7193af72a4dSblueswir1     } else {
72068a1c816SPaul Brook         int prot = 0;
721b76f21a7SLaurent Vivier         if (reserved_va && old_size < new_size) {
72268a1c816SPaul Brook             abi_ulong addr;
72368a1c816SPaul Brook             for (addr = old_addr + old_size;
72468a1c816SPaul Brook                  addr < old_addr + new_size;
72568a1c816SPaul Brook                  addr++) {
72668a1c816SPaul Brook                 prot |= page_get_flags(addr);
72768a1c816SPaul Brook             }
72868a1c816SPaul Brook         }
72968a1c816SPaul Brook         if (prot == 0) {
730f19412a2Saurel32             host_addr = mremap(g2h(old_addr), old_size, new_size, flags);
731b76f21a7SLaurent Vivier             if (host_addr != MAP_FAILED && reserved_va && old_size > new_size) {
73268a1c816SPaul Brook                 mmap_reserve(old_addr + old_size, new_size - old_size);
73368a1c816SPaul Brook             }
73468a1c816SPaul Brook         } else {
73568a1c816SPaul Brook             errno = ENOMEM;
73668a1c816SPaul Brook             host_addr = MAP_FAILED;
73768a1c816SPaul Brook         }
738f19412a2Saurel32         /* Check if address fits target address space */
739f19412a2Saurel32         if ((unsigned long)host_addr + new_size > (abi_ulong)-1) {
740f19412a2Saurel32             /* Revert mremap() changes */
741f19412a2Saurel32             host_addr = mremap(g2h(old_addr), new_size, old_size, flags);
742f19412a2Saurel32             errno = ENOMEM;
743f19412a2Saurel32             host_addr = MAP_FAILED;
744f19412a2Saurel32         }
745f19412a2Saurel32     }
746f19412a2Saurel32 
747f19412a2Saurel32     if (host_addr == MAP_FAILED) {
748c8a706feSpbrook         new_addr = -1;
749c8a706feSpbrook     } else {
750a5b85f79Sths         new_addr = h2g(host_addr);
75154936004Sbellard         prot = page_get_flags(old_addr);
75254936004Sbellard         page_set_flags(old_addr, old_addr + old_size, 0);
75354936004Sbellard         page_set_flags(new_addr, new_addr + new_size, prot | PAGE_VALID);
754c8a706feSpbrook     }
75535865339SPaolo Bonzini     tb_invalidate_phys_range(new_addr, new_addr + new_size);
756c8a706feSpbrook     mmap_unlock();
75754936004Sbellard     return new_addr;
75854936004Sbellard }
75954936004Sbellard 
760992f48a0Sblueswir1 int target_msync(abi_ulong start, abi_ulong len, int flags)
76154936004Sbellard {
762992f48a0Sblueswir1     abi_ulong end;
76354936004Sbellard 
76454936004Sbellard     if (start & ~TARGET_PAGE_MASK)
76554936004Sbellard         return -EINVAL;
76654936004Sbellard     len = TARGET_PAGE_ALIGN(len);
76754936004Sbellard     end = start + len;
768d418c81eSbellard     if (end < start)
769d418c81eSbellard         return -EINVAL;
770d418c81eSbellard     if (end == start)
771d418c81eSbellard         return 0;
77254936004Sbellard 
77383fb7adfSbellard     start &= qemu_host_page_mask;
77453a5960aSpbrook     return msync(g2h(start), end - start, flags);
77554936004Sbellard }
776