xref: /qemu/linux-user/mmap.c (revision 0776590d)
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"
2954936004Sbellard 
3054936004Sbellard //#define DEBUG_MMAP
3154936004Sbellard 
3253a5960aSpbrook /* NOTE: all the constants are the HOST ones, but addresses are target. */
33992f48a0Sblueswir1 int target_mprotect(abi_ulong start, abi_ulong len, int prot)
3454936004Sbellard {
35992f48a0Sblueswir1     abi_ulong end, host_start, host_end, addr;
3654936004Sbellard     int prot1, ret;
3754936004Sbellard 
3854936004Sbellard #ifdef DEBUG_MMAP
39a5b85f79Sths     printf("mprotect: start=0x" TARGET_FMT_lx
40a5b85f79Sths            "len=0x" TARGET_FMT_lx " prot=%c%c%c\n", start, len,
4154936004Sbellard            prot & PROT_READ ? 'r' : '-',
4254936004Sbellard            prot & PROT_WRITE ? 'w' : '-',
4354936004Sbellard            prot & PROT_EXEC ? 'x' : '-');
4454936004Sbellard #endif
4554936004Sbellard 
4654936004Sbellard     if ((start & ~TARGET_PAGE_MASK) != 0)
4754936004Sbellard         return -EINVAL;
4854936004Sbellard     len = TARGET_PAGE_ALIGN(len);
4954936004Sbellard     end = start + len;
5054936004Sbellard     if (end < start)
5154936004Sbellard         return -EINVAL;
52171cd1cdSbalrog     prot &= PROT_READ | PROT_WRITE | PROT_EXEC;
5354936004Sbellard     if (len == 0)
5454936004Sbellard         return 0;
5554936004Sbellard 
5683fb7adfSbellard     host_start = start & qemu_host_page_mask;
5754936004Sbellard     host_end = HOST_PAGE_ALIGN(end);
5854936004Sbellard     if (start > host_start) {
5954936004Sbellard         /* handle host page containing start */
6054936004Sbellard         prot1 = prot;
6154936004Sbellard         for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
6254936004Sbellard             prot1 |= page_get_flags(addr);
6354936004Sbellard         }
6483fb7adfSbellard         if (host_end == host_start + qemu_host_page_size) {
65d418c81eSbellard             for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
66d418c81eSbellard                 prot1 |= page_get_flags(addr);
67d418c81eSbellard             }
68d418c81eSbellard             end = host_end;
69d418c81eSbellard         }
7053a5960aSpbrook         ret = mprotect(g2h(host_start), qemu_host_page_size, prot1 & PAGE_BITS);
7154936004Sbellard         if (ret != 0)
7254936004Sbellard             return ret;
7383fb7adfSbellard         host_start += qemu_host_page_size;
7454936004Sbellard     }
7554936004Sbellard     if (end < host_end) {
7654936004Sbellard         prot1 = prot;
7754936004Sbellard         for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
7854936004Sbellard             prot1 |= page_get_flags(addr);
7954936004Sbellard         }
8053a5960aSpbrook         ret = mprotect(g2h(host_end - qemu_host_page_size), qemu_host_page_size,
8154936004Sbellard                        prot1 & PAGE_BITS);
8254936004Sbellard         if (ret != 0)
8354936004Sbellard             return ret;
8483fb7adfSbellard         host_end -= qemu_host_page_size;
8554936004Sbellard     }
8654936004Sbellard 
8754936004Sbellard     /* handle the pages in the middle */
8854936004Sbellard     if (host_start < host_end) {
8953a5960aSpbrook         ret = mprotect(g2h(host_start), host_end - host_start, prot);
9054936004Sbellard         if (ret != 0)
9154936004Sbellard             return ret;
9254936004Sbellard     }
9354936004Sbellard     page_set_flags(start, start + len, prot | PAGE_VALID);
9454936004Sbellard     return 0;
9554936004Sbellard }
9654936004Sbellard 
9754936004Sbellard /* map an incomplete host page */
98992f48a0Sblueswir1 static int mmap_frag(abi_ulong real_start,
99992f48a0Sblueswir1                      abi_ulong start, abi_ulong end,
100992f48a0Sblueswir1                      int prot, int flags, int fd, abi_ulong offset)
10154936004Sbellard {
10280210bcdSths     abi_ulong real_end, addr;
10353a5960aSpbrook     void *host_start;
10454936004Sbellard     int prot1, prot_new;
10554936004Sbellard 
10653a5960aSpbrook     real_end = real_start + qemu_host_page_size;
10753a5960aSpbrook     host_start = g2h(real_start);
10854936004Sbellard 
10954936004Sbellard     /* get the protection of the target pages outside the mapping */
11054936004Sbellard     prot1 = 0;
11153a5960aSpbrook     for(addr = real_start; addr < real_end; addr++) {
11254936004Sbellard         if (addr < start || addr >= end)
11354936004Sbellard             prot1 |= page_get_flags(addr);
11454936004Sbellard     }
11554936004Sbellard 
11654936004Sbellard     if (prot1 == 0) {
11754936004Sbellard         /* no page was there, so we allocate one */
11880210bcdSths         void *p = mmap(host_start, qemu_host_page_size, prot,
11954936004Sbellard                        flags | MAP_ANONYMOUS, -1, 0);
12080210bcdSths         if (p == MAP_FAILED)
12180210bcdSths             return -1;
12253a5960aSpbrook         prot1 = prot;
12354936004Sbellard     }
12454936004Sbellard     prot1 &= PAGE_BITS;
12554936004Sbellard 
12654936004Sbellard     prot_new = prot | prot1;
12754936004Sbellard     if (!(flags & MAP_ANONYMOUS)) {
12854936004Sbellard         /* msync() won't work here, so we return an error if write is
12954936004Sbellard            possible while it is a shared mapping */
13054936004Sbellard         if ((flags & MAP_TYPE) == MAP_SHARED &&
13154936004Sbellard             (prot & PROT_WRITE))
13254936004Sbellard             return -EINVAL;
13354936004Sbellard 
13454936004Sbellard         /* adjust protection to be able to read */
13554936004Sbellard         if (!(prot1 & PROT_WRITE))
13653a5960aSpbrook             mprotect(host_start, qemu_host_page_size, prot1 | PROT_WRITE);
13754936004Sbellard 
13854936004Sbellard         /* read the corresponding file data */
13953a5960aSpbrook         pread(fd, g2h(start), end - start, offset);
14054936004Sbellard 
14154936004Sbellard         /* put final protection */
14254936004Sbellard         if (prot_new != (prot1 | PROT_WRITE))
14353a5960aSpbrook             mprotect(host_start, qemu_host_page_size, prot_new);
14454936004Sbellard     } else {
14554936004Sbellard         /* just update the protection */
14654936004Sbellard         if (prot_new != prot1) {
14753a5960aSpbrook             mprotect(host_start, qemu_host_page_size, prot_new);
14854936004Sbellard         }
14954936004Sbellard     }
15054936004Sbellard     return 0;
15154936004Sbellard }
15254936004Sbellard 
153a03e2d42Sbellard #if defined(__CYGWIN__)
154a03e2d42Sbellard /* Cygwin doesn't have a whole lot of address space.  */
155a03e2d42Sbellard static abi_ulong mmap_next_start = 0x18000000;
156a03e2d42Sbellard #else
157a03e2d42Sbellard static abi_ulong mmap_next_start = 0x40000000;
158a03e2d42Sbellard #endif
159a03e2d42Sbellard 
160*0776590dSpbrook unsigned long last_brk;
161*0776590dSpbrook 
162a03e2d42Sbellard /* find a free memory area of size 'size'. The search starts at
163a03e2d42Sbellard    'start'. If 'start' == 0, then a default start address is used.
164a03e2d42Sbellard    Return -1 if error.
165a03e2d42Sbellard */
16650a9569bSbalrog /* page_init() marks pages used by the host as reserved to be sure not
167a03e2d42Sbellard    to use them. */
168a03e2d42Sbellard static abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
169a03e2d42Sbellard {
170a03e2d42Sbellard     abi_ulong addr, addr1, addr_start;
171a03e2d42Sbellard     int prot;
172*0776590dSpbrook     unsigned long new_brk;
173*0776590dSpbrook 
174*0776590dSpbrook     new_brk = (unsigned long)sbrk(0);
175*0776590dSpbrook     if (last_brk && last_brk < new_brk && last_brk == (target_ulong)last_brk) {
176*0776590dSpbrook         /* This is a hack to catch the host allocating memory with brk().
177*0776590dSpbrook            If it uses mmap then we loose.
178*0776590dSpbrook            FIXME: We really want to avoid the host allocating memory in
179*0776590dSpbrook            the first place, and maybe leave some slack to avoid switching
180*0776590dSpbrook            to mmap.  */
181*0776590dSpbrook         page_set_flags(last_brk & TARGET_PAGE_MASK,
182*0776590dSpbrook                        TARGET_PAGE_ALIGN(new_brk),
183*0776590dSpbrook                        PAGE_RESERVED);
184*0776590dSpbrook     }
185*0776590dSpbrook     last_brk = new_brk;
186a03e2d42Sbellard 
187a03e2d42Sbellard     size = HOST_PAGE_ALIGN(size);
188a03e2d42Sbellard     start = start & qemu_host_page_mask;
189a03e2d42Sbellard     addr = start;
190a03e2d42Sbellard     if (addr == 0)
191a03e2d42Sbellard         addr = mmap_next_start;
192a03e2d42Sbellard     addr_start = addr;
193a03e2d42Sbellard     for(;;) {
194a03e2d42Sbellard         prot = 0;
195a03e2d42Sbellard         for(addr1 = addr; addr1 < (addr + size); addr1 += TARGET_PAGE_SIZE) {
196a03e2d42Sbellard             prot |= page_get_flags(addr1);
197a03e2d42Sbellard         }
198a03e2d42Sbellard         if (prot == 0)
199a03e2d42Sbellard             break;
200a03e2d42Sbellard         addr += qemu_host_page_size;
201a03e2d42Sbellard         /* we found nothing */
202a03e2d42Sbellard         if (addr == addr_start)
203a03e2d42Sbellard             return (abi_ulong)-1;
204a03e2d42Sbellard     }
205a03e2d42Sbellard     if (start == 0)
206a03e2d42Sbellard         mmap_next_start = addr + size;
207a03e2d42Sbellard     return addr;
208a03e2d42Sbellard }
209a03e2d42Sbellard 
21054936004Sbellard /* NOTE: all the constants are the HOST ones */
211992f48a0Sblueswir1 abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
212992f48a0Sblueswir1                      int flags, int fd, abi_ulong offset)
21354936004Sbellard {
214992f48a0Sblueswir1     abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len;
215a5b85f79Sths     unsigned long host_start;
21654936004Sbellard 
21754936004Sbellard #ifdef DEBUG_MMAP
21854936004Sbellard     {
219a5b85f79Sths         printf("mmap: start=0x" TARGET_FMT_lx
220a5b85f79Sths                " len=0x" TARGET_FMT_lx " prot=%c%c%c flags=",
22154936004Sbellard                start, len,
22254936004Sbellard                prot & PROT_READ ? 'r' : '-',
22354936004Sbellard                prot & PROT_WRITE ? 'w' : '-',
22454936004Sbellard                prot & PROT_EXEC ? 'x' : '-');
22554936004Sbellard         if (flags & MAP_FIXED)
22654936004Sbellard             printf("MAP_FIXED ");
22754936004Sbellard         if (flags & MAP_ANONYMOUS)
22854936004Sbellard             printf("MAP_ANON ");
22954936004Sbellard         switch(flags & MAP_TYPE) {
23054936004Sbellard         case MAP_PRIVATE:
23154936004Sbellard             printf("MAP_PRIVATE ");
23254936004Sbellard             break;
23354936004Sbellard         case MAP_SHARED:
23454936004Sbellard             printf("MAP_SHARED ");
23554936004Sbellard             break;
23654936004Sbellard         default:
23754936004Sbellard             printf("[MAP_TYPE=0x%x] ", flags & MAP_TYPE);
23854936004Sbellard             break;
23954936004Sbellard         }
240a5b85f79Sths         printf("fd=%d offset=" TARGET_FMT_lx "\n", fd, offset);
24154936004Sbellard     }
24254936004Sbellard #endif
24354936004Sbellard 
244e89f07d3Spbrook     if (offset & ~TARGET_PAGE_MASK) {
245e89f07d3Spbrook         errno = EINVAL;
246e89f07d3Spbrook         return -1;
247e89f07d3Spbrook     }
24854936004Sbellard 
24954936004Sbellard     len = TARGET_PAGE_ALIGN(len);
25054936004Sbellard     if (len == 0)
25154936004Sbellard         return start;
25253a5960aSpbrook     real_start = start & qemu_host_page_mask;
25354936004Sbellard 
25454936004Sbellard     if (!(flags & MAP_FIXED)) {
255a03e2d42Sbellard         abi_ulong mmap_start;
256a03e2d42Sbellard         void *p;
25783fb7adfSbellard         host_offset = offset & qemu_host_page_mask;
25854936004Sbellard         host_len = len + offset - host_offset;
259a03e2d42Sbellard         host_len = HOST_PAGE_ALIGN(host_len);
260a03e2d42Sbellard         mmap_start = mmap_find_vma(real_start, host_len);
261a03e2d42Sbellard         if (mmap_start == (abi_ulong)-1) {
262a03e2d42Sbellard             errno = ENOMEM;
263a5b85f79Sths             return -1;
264a03e2d42Sbellard         }
265a03e2d42Sbellard         /* Note: we prefer to control the mapping address. It is
266a03e2d42Sbellard            especially important if qemu_host_page_size >
267a03e2d42Sbellard            qemu_real_host_page_size */
268a03e2d42Sbellard         p = mmap(g2h(mmap_start),
269a03e2d42Sbellard                  host_len, prot, flags | MAP_FIXED, fd, host_offset);
27080210bcdSths         if (p == MAP_FAILED)
271a5b85f79Sths             return -1;
27254936004Sbellard         /* update start so that it points to the file position at 'offset' */
27380210bcdSths         host_start = (unsigned long)p;
27454936004Sbellard         if (!(flags & MAP_ANONYMOUS))
27553a5960aSpbrook             host_start += offset - host_offset;
27653a5960aSpbrook         start = h2g(host_start);
277a03e2d42Sbellard     } else {
2787ab240adSbalrog         int flg;
2797ab240adSbalrog         target_ulong addr;
2807ab240adSbalrog 
281e89f07d3Spbrook         if (start & ~TARGET_PAGE_MASK) {
282e89f07d3Spbrook             errno = EINVAL;
283e89f07d3Spbrook             return -1;
284e89f07d3Spbrook         }
28554936004Sbellard         end = start + len;
28653a5960aSpbrook         real_end = HOST_PAGE_ALIGN(end);
28754936004Sbellard 
2887ab240adSbalrog         for(addr = real_start; addr < real_end; addr += TARGET_PAGE_SIZE) {
2897ab240adSbalrog             flg = page_get_flags(addr);
2907ab240adSbalrog             if (flg & PAGE_RESERVED) {
2917ab240adSbalrog                 errno = ENXIO;
2927ab240adSbalrog                 return -1;
2937ab240adSbalrog             }
2947ab240adSbalrog         }
2957ab240adSbalrog 
29654936004Sbellard         /* worst case: we cannot map the file because the offset is not
29754936004Sbellard            aligned, so we read it */
29854936004Sbellard         if (!(flags & MAP_ANONYMOUS) &&
29983fb7adfSbellard             (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) {
30054936004Sbellard             /* msync() won't work here, so we return an error if write is
30154936004Sbellard                possible while it is a shared mapping */
30254936004Sbellard             if ((flags & MAP_TYPE) == MAP_SHARED &&
303e89f07d3Spbrook                 (prot & PROT_WRITE)) {
304e89f07d3Spbrook                 errno = EINVAL;
305e89f07d3Spbrook                 return -1;
306e89f07d3Spbrook             }
30754936004Sbellard             retaddr = target_mmap(start, len, prot | PROT_WRITE,
30854936004Sbellard                                   MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
30954936004Sbellard                                   -1, 0);
31054936004Sbellard             if (retaddr == -1)
311a5b85f79Sths                 return -1;
31253a5960aSpbrook             pread(fd, g2h(start), len, offset);
31354936004Sbellard             if (!(prot & PROT_WRITE)) {
31454936004Sbellard                 ret = target_mprotect(start, len, prot);
31554936004Sbellard                 if (ret != 0)
31654936004Sbellard                     return ret;
31754936004Sbellard             }
31854936004Sbellard             goto the_end;
31954936004Sbellard         }
32054936004Sbellard 
32154936004Sbellard         /* handle the start of the mapping */
32253a5960aSpbrook         if (start > real_start) {
32353a5960aSpbrook             if (real_end == real_start + qemu_host_page_size) {
32454936004Sbellard                 /* one single host page */
32553a5960aSpbrook                 ret = mmap_frag(real_start, start, end,
32654936004Sbellard                                 prot, flags, fd, offset);
32754936004Sbellard                 if (ret == -1)
32854936004Sbellard                     return ret;
32954936004Sbellard                 goto the_end1;
33054936004Sbellard             }
33153a5960aSpbrook             ret = mmap_frag(real_start, start, real_start + qemu_host_page_size,
33254936004Sbellard                             prot, flags, fd, offset);
33354936004Sbellard             if (ret == -1)
33454936004Sbellard                 return ret;
33553a5960aSpbrook             real_start += qemu_host_page_size;
33654936004Sbellard         }
33754936004Sbellard         /* handle the end of the mapping */
33853a5960aSpbrook         if (end < real_end) {
33953a5960aSpbrook             ret = mmap_frag(real_end - qemu_host_page_size,
34053a5960aSpbrook                             real_end - qemu_host_page_size, real_end,
34154936004Sbellard                             prot, flags, fd,
34253a5960aSpbrook                             offset + real_end - qemu_host_page_size - start);
34354936004Sbellard             if (ret == -1)
344a5b85f79Sths                 return -1;
34553a5960aSpbrook             real_end -= qemu_host_page_size;
34654936004Sbellard         }
34754936004Sbellard 
34854936004Sbellard         /* map the middle (easier) */
34953a5960aSpbrook         if (real_start < real_end) {
35080210bcdSths             void *p;
3514a585ccbSbellard             unsigned long offset1;
3524a585ccbSbellard             if (flags & MAP_ANONYMOUS)
3534a585ccbSbellard                 offset1 = 0;
3544a585ccbSbellard             else
35553a5960aSpbrook                 offset1 = offset + real_start - start;
35680210bcdSths             p = mmap(g2h(real_start), real_end - real_start,
3574a585ccbSbellard                      prot, flags, fd, offset1);
35880210bcdSths             if (p == MAP_FAILED)
359a5b85f79Sths                 return -1;
36054936004Sbellard         }
361a03e2d42Sbellard     }
36254936004Sbellard  the_end1:
36354936004Sbellard     page_set_flags(start, start + len, prot | PAGE_VALID);
36454936004Sbellard  the_end:
36554936004Sbellard #ifdef DEBUG_MMAP
3662e0ded9cSedgar_igl     printf("ret=0x" TARGET_FMT_lx "\n", start);
36754936004Sbellard     page_dump(stdout);
36854936004Sbellard     printf("\n");
36954936004Sbellard #endif
37054936004Sbellard     return start;
37154936004Sbellard }
37254936004Sbellard 
373992f48a0Sblueswir1 int target_munmap(abi_ulong start, abi_ulong len)
37454936004Sbellard {
375992f48a0Sblueswir1     abi_ulong end, real_start, real_end, addr;
37654936004Sbellard     int prot, ret;
37754936004Sbellard 
37854936004Sbellard #ifdef DEBUG_MMAP
37954936004Sbellard     printf("munmap: start=0x%lx len=0x%lx\n", start, len);
38054936004Sbellard #endif
38154936004Sbellard     if (start & ~TARGET_PAGE_MASK)
38254936004Sbellard         return -EINVAL;
38354936004Sbellard     len = TARGET_PAGE_ALIGN(len);
38454936004Sbellard     if (len == 0)
38554936004Sbellard         return -EINVAL;
38654936004Sbellard     end = start + len;
38753a5960aSpbrook     real_start = start & qemu_host_page_mask;
38853a5960aSpbrook     real_end = HOST_PAGE_ALIGN(end);
38954936004Sbellard 
39053a5960aSpbrook     if (start > real_start) {
39154936004Sbellard         /* handle host page containing start */
39254936004Sbellard         prot = 0;
39353a5960aSpbrook         for(addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
39454936004Sbellard             prot |= page_get_flags(addr);
39554936004Sbellard         }
39653a5960aSpbrook         if (real_end == real_start + qemu_host_page_size) {
39753a5960aSpbrook             for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
398d418c81eSbellard                 prot |= page_get_flags(addr);
399d418c81eSbellard             }
40053a5960aSpbrook             end = real_end;
401d418c81eSbellard         }
40254936004Sbellard         if (prot != 0)
40353a5960aSpbrook             real_start += qemu_host_page_size;
40454936004Sbellard     }
40553a5960aSpbrook     if (end < real_end) {
40654936004Sbellard         prot = 0;
40753a5960aSpbrook         for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
40854936004Sbellard             prot |= page_get_flags(addr);
40954936004Sbellard         }
41054936004Sbellard         if (prot != 0)
41153a5960aSpbrook             real_end -= qemu_host_page_size;
41254936004Sbellard     }
41354936004Sbellard 
41454936004Sbellard     /* unmap what we can */
41553a5960aSpbrook     if (real_start < real_end) {
4164118a970Sj_mayer         ret = munmap(g2h(real_start), real_end - real_start);
41754936004Sbellard         if (ret != 0)
41854936004Sbellard             return ret;
41954936004Sbellard     }
42054936004Sbellard 
42154936004Sbellard     page_set_flags(start, start + len, 0);
42254936004Sbellard     return 0;
42354936004Sbellard }
42454936004Sbellard 
42554936004Sbellard /* XXX: currently, we only handle MAP_ANONYMOUS and not MAP_FIXED
42654936004Sbellard    blocks which have been allocated starting on a host page */
427992f48a0Sblueswir1 abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
428992f48a0Sblueswir1                        abi_ulong new_size, unsigned long flags,
429992f48a0Sblueswir1                        abi_ulong new_addr)
43054936004Sbellard {
43154936004Sbellard     int prot;
432a5b85f79Sths     unsigned long host_addr;
43354936004Sbellard 
43454936004Sbellard     /* XXX: use 5 args syscall */
435a5b85f79Sths     host_addr = (long)mremap(g2h(old_addr), old_size, new_size, flags);
436a5b85f79Sths     if (host_addr == -1)
437a5b85f79Sths         return -1;
438a5b85f79Sths     new_addr = h2g(host_addr);
43954936004Sbellard     prot = page_get_flags(old_addr);
44054936004Sbellard     page_set_flags(old_addr, old_addr + old_size, 0);
44154936004Sbellard     page_set_flags(new_addr, new_addr + new_size, prot | PAGE_VALID);
44254936004Sbellard     return new_addr;
44354936004Sbellard }
44454936004Sbellard 
445992f48a0Sblueswir1 int target_msync(abi_ulong start, abi_ulong len, int flags)
44654936004Sbellard {
447992f48a0Sblueswir1     abi_ulong end;
44854936004Sbellard 
44954936004Sbellard     if (start & ~TARGET_PAGE_MASK)
45054936004Sbellard         return -EINVAL;
45154936004Sbellard     len = TARGET_PAGE_ALIGN(len);
45254936004Sbellard     end = start + len;
453d418c81eSbellard     if (end < start)
454d418c81eSbellard         return -EINVAL;
455d418c81eSbellard     if (end == start)
456d418c81eSbellard         return 0;
45754936004Sbellard 
45883fb7adfSbellard     start &= qemu_host_page_mask;
45953a5960aSpbrook     return msync(g2h(start), end - start, flags);
46054936004Sbellard }
461