xref: /openbmc/qemu/linux-user/mmap.c (revision 6e0abc25)
1 /*
2  *  mmap support for qemu
3  *
4  *  Copyright (c) 2003 Fabrice Bellard
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <stdarg.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <sys/mman.h>
28 #include <linux/mman.h>
29 #include <linux/unistd.h>
30 
31 #include "qemu.h"
32 #include "qemu-common.h"
33 #include "translate-all.h"
34 
35 //#define DEBUG_MMAP
36 
37 static pthread_mutex_t mmap_mutex = PTHREAD_MUTEX_INITIALIZER;
38 static __thread int mmap_lock_count;
39 
40 void mmap_lock(void)
41 {
42     if (mmap_lock_count++ == 0) {
43         pthread_mutex_lock(&mmap_mutex);
44     }
45 }
46 
47 void mmap_unlock(void)
48 {
49     if (--mmap_lock_count == 0) {
50         pthread_mutex_unlock(&mmap_mutex);
51     }
52 }
53 
54 /* Grab lock to make sure things are in a consistent state after fork().  */
55 void mmap_fork_start(void)
56 {
57     if (mmap_lock_count)
58         abort();
59     pthread_mutex_lock(&mmap_mutex);
60 }
61 
62 void mmap_fork_end(int child)
63 {
64     if (child)
65         pthread_mutex_init(&mmap_mutex, NULL);
66     else
67         pthread_mutex_unlock(&mmap_mutex);
68 }
69 
70 /* NOTE: all the constants are the HOST ones, but addresses are target. */
71 int target_mprotect(abi_ulong start, abi_ulong len, int prot)
72 {
73     abi_ulong end, host_start, host_end, addr;
74     int prot1, ret;
75 
76 #ifdef DEBUG_MMAP
77     printf("mprotect: start=0x" TARGET_ABI_FMT_lx
78            "len=0x" TARGET_ABI_FMT_lx " prot=%c%c%c\n", start, len,
79            prot & PROT_READ ? 'r' : '-',
80            prot & PROT_WRITE ? 'w' : '-',
81            prot & PROT_EXEC ? 'x' : '-');
82 #endif
83 
84     if ((start & ~TARGET_PAGE_MASK) != 0)
85         return -EINVAL;
86     len = TARGET_PAGE_ALIGN(len);
87     end = start + len;
88     if (end < start)
89         return -EINVAL;
90     prot &= PROT_READ | PROT_WRITE | PROT_EXEC;
91     if (len == 0)
92         return 0;
93 
94     mmap_lock();
95     host_start = start & qemu_host_page_mask;
96     host_end = HOST_PAGE_ALIGN(end);
97     if (start > host_start) {
98         /* handle host page containing start */
99         prot1 = prot;
100         for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
101             prot1 |= page_get_flags(addr);
102         }
103         if (host_end == host_start + qemu_host_page_size) {
104             for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
105                 prot1 |= page_get_flags(addr);
106             }
107             end = host_end;
108         }
109         ret = mprotect(g2h(host_start), qemu_host_page_size, prot1 & PAGE_BITS);
110         if (ret != 0)
111             goto error;
112         host_start += qemu_host_page_size;
113     }
114     if (end < host_end) {
115         prot1 = prot;
116         for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
117             prot1 |= page_get_flags(addr);
118         }
119         ret = mprotect(g2h(host_end - qemu_host_page_size), qemu_host_page_size,
120                        prot1 & PAGE_BITS);
121         if (ret != 0)
122             goto error;
123         host_end -= qemu_host_page_size;
124     }
125 
126     /* handle the pages in the middle */
127     if (host_start < host_end) {
128         ret = mprotect(g2h(host_start), host_end - host_start, prot);
129         if (ret != 0)
130             goto error;
131     }
132     page_set_flags(start, start + len, prot | PAGE_VALID);
133     mmap_unlock();
134     return 0;
135 error:
136     mmap_unlock();
137     return ret;
138 }
139 
140 /* map an incomplete host page */
141 static int mmap_frag(abi_ulong real_start,
142                      abi_ulong start, abi_ulong end,
143                      int prot, int flags, int fd, abi_ulong offset)
144 {
145     abi_ulong real_end, addr;
146     void *host_start;
147     int prot1, prot_new;
148 
149     real_end = real_start + qemu_host_page_size;
150     host_start = g2h(real_start);
151 
152     /* get the protection of the target pages outside the mapping */
153     prot1 = 0;
154     for(addr = real_start; addr < real_end; addr++) {
155         if (addr < start || addr >= end)
156             prot1 |= page_get_flags(addr);
157     }
158 
159     if (prot1 == 0) {
160         /* no page was there, so we allocate one */
161         void *p = mmap(host_start, qemu_host_page_size, prot,
162                        flags | MAP_ANONYMOUS, -1, 0);
163         if (p == MAP_FAILED)
164             return -1;
165         prot1 = prot;
166     }
167     prot1 &= PAGE_BITS;
168 
169     prot_new = prot | prot1;
170     if (!(flags & MAP_ANONYMOUS)) {
171         /* msync() won't work here, so we return an error if write is
172            possible while it is a shared mapping */
173         if ((flags & MAP_TYPE) == MAP_SHARED &&
174             (prot & PROT_WRITE))
175             return -1;
176 
177         /* adjust protection to be able to read */
178         if (!(prot1 & PROT_WRITE))
179             mprotect(host_start, qemu_host_page_size, prot1 | PROT_WRITE);
180 
181         /* read the corresponding file data */
182         if (pread(fd, g2h(start), end - start, offset) == -1)
183             return -1;
184 
185         /* put final protection */
186         if (prot_new != (prot1 | PROT_WRITE))
187             mprotect(host_start, qemu_host_page_size, prot_new);
188     } else {
189         /* just update the protection */
190         if (prot_new != prot1) {
191             mprotect(host_start, qemu_host_page_size, prot_new);
192         }
193     }
194     return 0;
195 }
196 
197 #if HOST_LONG_BITS == 64 && TARGET_ABI_BITS == 64
198 # define TASK_UNMAPPED_BASE  (1ul << 38)
199 #elif defined(__CYGWIN__)
200 /* Cygwin doesn't have a whole lot of address space.  */
201 # define TASK_UNMAPPED_BASE  0x18000000
202 #else
203 # define TASK_UNMAPPED_BASE  0x40000000
204 #endif
205 abi_ulong mmap_next_start = TASK_UNMAPPED_BASE;
206 
207 unsigned long last_brk;
208 
209 /* Subroutine of mmap_find_vma, used when we have pre-allocated a chunk
210    of guest address space.  */
211 static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size)
212 {
213     abi_ulong addr;
214     abi_ulong end_addr;
215     int prot;
216     int looped = 0;
217 
218     if (size > reserved_va) {
219         return (abi_ulong)-1;
220     }
221 
222     size = HOST_PAGE_ALIGN(size);
223     end_addr = start + size;
224     if (end_addr > reserved_va) {
225         end_addr = reserved_va;
226     }
227     addr = end_addr - qemu_host_page_size;
228 
229     while (1) {
230         if (addr > end_addr) {
231             if (looped) {
232                 return (abi_ulong)-1;
233             }
234             end_addr = reserved_va;
235             addr = end_addr - qemu_host_page_size;
236             looped = 1;
237             continue;
238         }
239         prot = page_get_flags(addr);
240         if (prot) {
241             end_addr = addr;
242         }
243         if (addr + size == end_addr) {
244             break;
245         }
246         addr -= qemu_host_page_size;
247     }
248 
249     if (start == mmap_next_start) {
250         mmap_next_start = addr;
251     }
252 
253     return addr;
254 }
255 
256 /*
257  * Find and reserve a free memory area of size 'size'. The search
258  * starts at 'start'.
259  * It must be called with mmap_lock() held.
260  * Return -1 if error.
261  */
262 abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
263 {
264     void *ptr, *prev;
265     abi_ulong addr;
266     int wrapped, repeat;
267 
268     /* If 'start' == 0, then a default start address is used. */
269     if (start == 0) {
270         start = mmap_next_start;
271     } else {
272         start &= qemu_host_page_mask;
273     }
274 
275     size = HOST_PAGE_ALIGN(size);
276 
277     if (reserved_va) {
278         return mmap_find_vma_reserved(start, size);
279     }
280 
281     addr = start;
282     wrapped = repeat = 0;
283     prev = 0;
284 
285     for (;; prev = ptr) {
286         /*
287          * Reserve needed memory area to avoid a race.
288          * It should be discarded using:
289          *  - mmap() with MAP_FIXED flag
290          *  - mremap() with MREMAP_FIXED flag
291          *  - shmat() with SHM_REMAP flag
292          */
293         ptr = mmap(g2h(addr), size, PROT_NONE,
294                    MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE, -1, 0);
295 
296         /* ENOMEM, if host address space has no memory */
297         if (ptr == MAP_FAILED) {
298             return (abi_ulong)-1;
299         }
300 
301         /* Count the number of sequential returns of the same address.
302            This is used to modify the search algorithm below.  */
303         repeat = (ptr == prev ? repeat + 1 : 0);
304 
305         if (h2g_valid(ptr + size - 1)) {
306             addr = h2g(ptr);
307 
308             if ((addr & ~TARGET_PAGE_MASK) == 0) {
309                 /* Success.  */
310                 if (start == mmap_next_start && addr >= TASK_UNMAPPED_BASE) {
311                     mmap_next_start = addr + size;
312                 }
313                 return addr;
314             }
315 
316             /* The address is not properly aligned for the target.  */
317             switch (repeat) {
318             case 0:
319                 /* Assume the result that the kernel gave us is the
320                    first with enough free space, so start again at the
321                    next higher target page.  */
322                 addr = TARGET_PAGE_ALIGN(addr);
323                 break;
324             case 1:
325                 /* Sometimes the kernel decides to perform the allocation
326                    at the top end of memory instead.  */
327                 addr &= TARGET_PAGE_MASK;
328                 break;
329             case 2:
330                 /* Start over at low memory.  */
331                 addr = 0;
332                 break;
333             default:
334                 /* Fail.  This unaligned block must the last.  */
335                 addr = -1;
336                 break;
337             }
338         } else {
339             /* Since the result the kernel gave didn't fit, start
340                again at low memory.  If any repetition, fail.  */
341             addr = (repeat ? -1 : 0);
342         }
343 
344         /* Unmap and try again.  */
345         munmap(ptr, size);
346 
347         /* ENOMEM if we checked the whole of the target address space.  */
348         if (addr == (abi_ulong)-1) {
349             return (abi_ulong)-1;
350         } else if (addr == 0) {
351             if (wrapped) {
352                 return (abi_ulong)-1;
353             }
354             wrapped = 1;
355             /* Don't actually use 0 when wrapping, instead indicate
356                that we'd truly like an allocation in low memory.  */
357             addr = (mmap_min_addr > TARGET_PAGE_SIZE
358                      ? TARGET_PAGE_ALIGN(mmap_min_addr)
359                      : TARGET_PAGE_SIZE);
360         } else if (wrapped && addr >= start) {
361             return (abi_ulong)-1;
362         }
363     }
364 }
365 
366 /* NOTE: all the constants are the HOST ones */
367 abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
368                      int flags, int fd, abi_ulong offset)
369 {
370     abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len;
371 
372     mmap_lock();
373 #ifdef DEBUG_MMAP
374     {
375         printf("mmap: start=0x" TARGET_ABI_FMT_lx
376                " len=0x" TARGET_ABI_FMT_lx " prot=%c%c%c flags=",
377                start, len,
378                prot & PROT_READ ? 'r' : '-',
379                prot & PROT_WRITE ? 'w' : '-',
380                prot & PROT_EXEC ? 'x' : '-');
381         if (flags & MAP_FIXED)
382             printf("MAP_FIXED ");
383         if (flags & MAP_ANONYMOUS)
384             printf("MAP_ANON ");
385         switch(flags & MAP_TYPE) {
386         case MAP_PRIVATE:
387             printf("MAP_PRIVATE ");
388             break;
389         case MAP_SHARED:
390             printf("MAP_SHARED ");
391             break;
392         default:
393             printf("[MAP_TYPE=0x%x] ", flags & MAP_TYPE);
394             break;
395         }
396         printf("fd=%d offset=" TARGET_ABI_FMT_lx "\n", fd, offset);
397     }
398 #endif
399 
400     if (offset & ~TARGET_PAGE_MASK) {
401         errno = EINVAL;
402         goto fail;
403     }
404 
405     len = TARGET_PAGE_ALIGN(len);
406     if (len == 0)
407         goto the_end;
408     real_start = start & qemu_host_page_mask;
409     host_offset = offset & qemu_host_page_mask;
410 
411     /* If the user is asking for the kernel to find a location, do that
412        before we truncate the length for mapping files below.  */
413     if (!(flags & MAP_FIXED)) {
414         host_len = len + offset - host_offset;
415         host_len = HOST_PAGE_ALIGN(host_len);
416         start = mmap_find_vma(real_start, host_len);
417         if (start == (abi_ulong)-1) {
418             errno = ENOMEM;
419             goto fail;
420         }
421     }
422 
423     /* When mapping files into a memory area larger than the file, accesses
424        to pages beyond the file size will cause a SIGBUS.
425 
426        For example, if mmaping a file of 100 bytes on a host with 4K pages
427        emulating a target with 8K pages, the target expects to be able to
428        access the first 8K. But the host will trap us on any access beyond
429        4K.
430 
431        When emulating a target with a larger page-size than the hosts, we
432        may need to truncate file maps at EOF and add extra anonymous pages
433        up to the targets page boundary.  */
434 
435     if ((qemu_real_host_page_size < TARGET_PAGE_SIZE)
436         && !(flags & MAP_ANONYMOUS)) {
437        struct stat sb;
438 
439        if (fstat (fd, &sb) == -1)
440            goto fail;
441 
442        /* Are we trying to create a map beyond EOF?.  */
443        if (offset + len > sb.st_size) {
444            /* If so, truncate the file map at eof aligned with
445               the hosts real pagesize. Additional anonymous maps
446               will be created beyond EOF.  */
447            len = REAL_HOST_PAGE_ALIGN(sb.st_size - offset);
448        }
449     }
450 
451     if (!(flags & MAP_FIXED)) {
452         unsigned long host_start;
453         void *p;
454 
455         host_len = len + offset - host_offset;
456         host_len = HOST_PAGE_ALIGN(host_len);
457 
458         /* Note: we prefer to control the mapping address. It is
459            especially important if qemu_host_page_size >
460            qemu_real_host_page_size */
461         p = mmap(g2h(start), host_len, prot,
462                  flags | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
463         if (p == MAP_FAILED)
464             goto fail;
465         /* update start so that it points to the file position at 'offset' */
466         host_start = (unsigned long)p;
467         if (!(flags & MAP_ANONYMOUS)) {
468             p = mmap(g2h(start), len, prot,
469                      flags | MAP_FIXED, fd, host_offset);
470             if (p == MAP_FAILED) {
471                 munmap(g2h(start), host_len);
472                 goto fail;
473             }
474             host_start += offset - host_offset;
475         }
476         start = h2g(host_start);
477     } else {
478         if (start & ~TARGET_PAGE_MASK) {
479             errno = EINVAL;
480             goto fail;
481         }
482         end = start + len;
483         real_end = HOST_PAGE_ALIGN(end);
484 
485 	/*
486 	 * Test if requested memory area fits target address space
487 	 * It can fail only on 64-bit host with 32-bit target.
488 	 * On any other target/host host mmap() handles this error correctly.
489 	 */
490         if ((unsigned long)start + len - 1 > (abi_ulong) -1) {
491             errno = EINVAL;
492             goto fail;
493         }
494 
495         /* worst case: we cannot map the file because the offset is not
496            aligned, so we read it */
497         if (!(flags & MAP_ANONYMOUS) &&
498             (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) {
499             /* msync() won't work here, so we return an error if write is
500                possible while it is a shared mapping */
501             if ((flags & MAP_TYPE) == MAP_SHARED &&
502                 (prot & PROT_WRITE)) {
503                 errno = EINVAL;
504                 goto fail;
505             }
506             retaddr = target_mmap(start, len, prot | PROT_WRITE,
507                                   MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
508                                   -1, 0);
509             if (retaddr == -1)
510                 goto fail;
511             if (pread(fd, g2h(start), len, offset) == -1)
512                 goto fail;
513             if (!(prot & PROT_WRITE)) {
514                 ret = target_mprotect(start, len, prot);
515                 assert(ret == 0);
516             }
517             goto the_end;
518         }
519 
520         /* handle the start of the mapping */
521         if (start > real_start) {
522             if (real_end == real_start + qemu_host_page_size) {
523                 /* one single host page */
524                 ret = mmap_frag(real_start, start, end,
525                                 prot, flags, fd, offset);
526                 if (ret == -1)
527                     goto fail;
528                 goto the_end1;
529             }
530             ret = mmap_frag(real_start, start, real_start + qemu_host_page_size,
531                             prot, flags, fd, offset);
532             if (ret == -1)
533                 goto fail;
534             real_start += qemu_host_page_size;
535         }
536         /* handle the end of the mapping */
537         if (end < real_end) {
538             ret = mmap_frag(real_end - qemu_host_page_size,
539                             real_end - qemu_host_page_size, real_end,
540                             prot, flags, fd,
541                             offset + real_end - qemu_host_page_size - start);
542             if (ret == -1)
543                 goto fail;
544             real_end -= qemu_host_page_size;
545         }
546 
547         /* map the middle (easier) */
548         if (real_start < real_end) {
549             void *p;
550             unsigned long offset1;
551             if (flags & MAP_ANONYMOUS)
552                 offset1 = 0;
553             else
554                 offset1 = offset + real_start - start;
555             p = mmap(g2h(real_start), real_end - real_start,
556                      prot, flags, fd, offset1);
557             if (p == MAP_FAILED)
558                 goto fail;
559         }
560     }
561  the_end1:
562     page_set_flags(start, start + len, prot | PAGE_VALID);
563  the_end:
564 #ifdef DEBUG_MMAP
565     printf("ret=0x" TARGET_ABI_FMT_lx "\n", start);
566     page_dump(stdout);
567     printf("\n");
568 #endif
569     tb_invalidate_phys_range(start, start + len);
570     mmap_unlock();
571     return start;
572 fail:
573     mmap_unlock();
574     return -1;
575 }
576 
577 static void mmap_reserve(abi_ulong start, abi_ulong size)
578 {
579     abi_ulong real_start;
580     abi_ulong real_end;
581     abi_ulong addr;
582     abi_ulong end;
583     int prot;
584 
585     real_start = start & qemu_host_page_mask;
586     real_end = HOST_PAGE_ALIGN(start + size);
587     end = start + size;
588     if (start > real_start) {
589         /* handle host page containing start */
590         prot = 0;
591         for (addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
592             prot |= page_get_flags(addr);
593         }
594         if (real_end == real_start + qemu_host_page_size) {
595             for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
596                 prot |= page_get_flags(addr);
597             }
598             end = real_end;
599         }
600         if (prot != 0)
601             real_start += qemu_host_page_size;
602     }
603     if (end < real_end) {
604         prot = 0;
605         for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
606             prot |= page_get_flags(addr);
607         }
608         if (prot != 0)
609             real_end -= qemu_host_page_size;
610     }
611     if (real_start != real_end) {
612         mmap(g2h(real_start), real_end - real_start, PROT_NONE,
613                  MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE,
614                  -1, 0);
615     }
616 }
617 
618 int target_munmap(abi_ulong start, abi_ulong len)
619 {
620     abi_ulong end, real_start, real_end, addr;
621     int prot, ret;
622 
623 #ifdef DEBUG_MMAP
624     printf("munmap: start=0x" TARGET_ABI_FMT_lx " len=0x"
625            TARGET_ABI_FMT_lx "\n",
626            start, len);
627 #endif
628     if (start & ~TARGET_PAGE_MASK)
629         return -EINVAL;
630     len = TARGET_PAGE_ALIGN(len);
631     if (len == 0)
632         return -EINVAL;
633     mmap_lock();
634     end = start + len;
635     real_start = start & qemu_host_page_mask;
636     real_end = HOST_PAGE_ALIGN(end);
637 
638     if (start > real_start) {
639         /* handle host page containing start */
640         prot = 0;
641         for(addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
642             prot |= page_get_flags(addr);
643         }
644         if (real_end == real_start + qemu_host_page_size) {
645             for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
646                 prot |= page_get_flags(addr);
647             }
648             end = real_end;
649         }
650         if (prot != 0)
651             real_start += qemu_host_page_size;
652     }
653     if (end < real_end) {
654         prot = 0;
655         for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
656             prot |= page_get_flags(addr);
657         }
658         if (prot != 0)
659             real_end -= qemu_host_page_size;
660     }
661 
662     ret = 0;
663     /* unmap what we can */
664     if (real_start < real_end) {
665         if (reserved_va) {
666             mmap_reserve(real_start, real_end - real_start);
667         } else {
668             ret = munmap(g2h(real_start), real_end - real_start);
669         }
670     }
671 
672     if (ret == 0) {
673         page_set_flags(start, start + len, 0);
674         tb_invalidate_phys_range(start, start + len);
675     }
676     mmap_unlock();
677     return ret;
678 }
679 
680 abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
681                        abi_ulong new_size, unsigned long flags,
682                        abi_ulong new_addr)
683 {
684     int prot;
685     void *host_addr;
686 
687     mmap_lock();
688 
689     if (flags & MREMAP_FIXED) {
690         host_addr = (void *) syscall(__NR_mremap, g2h(old_addr),
691                                      old_size, new_size,
692                                      flags,
693                                      g2h(new_addr));
694 
695         if (reserved_va && host_addr != MAP_FAILED) {
696             /* If new and old addresses overlap then the above mremap will
697                already have failed with EINVAL.  */
698             mmap_reserve(old_addr, old_size);
699         }
700     } else if (flags & MREMAP_MAYMOVE) {
701         abi_ulong mmap_start;
702 
703         mmap_start = mmap_find_vma(0, new_size);
704 
705         if (mmap_start == -1) {
706             errno = ENOMEM;
707             host_addr = MAP_FAILED;
708         } else {
709             host_addr = (void *) syscall(__NR_mremap, g2h(old_addr),
710                                          old_size, new_size,
711                                          flags | MREMAP_FIXED,
712                                          g2h(mmap_start));
713             if (reserved_va) {
714                 mmap_reserve(old_addr, old_size);
715             }
716         }
717     } else {
718         int prot = 0;
719         if (reserved_va && old_size < new_size) {
720             abi_ulong addr;
721             for (addr = old_addr + old_size;
722                  addr < old_addr + new_size;
723                  addr++) {
724                 prot |= page_get_flags(addr);
725             }
726         }
727         if (prot == 0) {
728             host_addr = mremap(g2h(old_addr), old_size, new_size, flags);
729             if (host_addr != MAP_FAILED && reserved_va && old_size > new_size) {
730                 mmap_reserve(old_addr + old_size, new_size - old_size);
731             }
732         } else {
733             errno = ENOMEM;
734             host_addr = MAP_FAILED;
735         }
736         /* Check if address fits target address space */
737         if ((unsigned long)host_addr + new_size > (abi_ulong)-1) {
738             /* Revert mremap() changes */
739             host_addr = mremap(g2h(old_addr), new_size, old_size, flags);
740             errno = ENOMEM;
741             host_addr = MAP_FAILED;
742         }
743     }
744 
745     if (host_addr == MAP_FAILED) {
746         new_addr = -1;
747     } else {
748         new_addr = h2g(host_addr);
749         prot = page_get_flags(old_addr);
750         page_set_flags(old_addr, old_addr + old_size, 0);
751         page_set_flags(new_addr, new_addr + new_size, prot | PAGE_VALID);
752     }
753     tb_invalidate_phys_range(new_addr, new_addr + new_size);
754     mmap_unlock();
755     return new_addr;
756 }
757 
758 int target_msync(abi_ulong start, abi_ulong len, int flags)
759 {
760     abi_ulong end;
761 
762     if (start & ~TARGET_PAGE_MASK)
763         return -EINVAL;
764     len = TARGET_PAGE_ALIGN(len);
765     end = start + len;
766     if (end < start)
767         return -EINVAL;
768     if (end == start)
769         return 0;
770 
771     start &= qemu_host_page_mask;
772     return msync(g2h(start), end - start, flags);
773 }
774