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