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